From 86e16129193e81db28cbc89c26e1dbf17bd863f2 Mon Sep 17 00:00:00 2001 From: "M. Sirringhaus" Date: Tue, 9 Feb 2021 12:52:22 +0000 Subject: [PATCH] Bug 1666725 - Make gvfs e10s compatible r=valentin,necko-reviewers Split channel into parent/child to be able to use dbus in the content sandbox Differential Revision: https://phabricator.services.mozilla.com/D92206 --- ipc/ipdl/ipdl/direct_call.py | 2 + netwerk/ipc/NeckoChannelParams.ipdlh | 24 + netwerk/ipc/NeckoChild.cpp | 17 + netwerk/ipc/NeckoChild.h | 8 +- netwerk/ipc/NeckoParent.cpp | 51 ++ netwerk/ipc/NeckoParent.h | 16 +- netwerk/ipc/PNecko.ipdl | 3 + netwerk/protocol/gio/GIOChannelChild.cpp | 468 ++++++++++++++++++ netwerk/protocol/gio/GIOChannelChild.h | 109 ++++ netwerk/protocol/gio/GIOChannelParent.cpp | 323 ++++++++++++ netwerk/protocol/gio/GIOChannelParent.h | 80 +++ netwerk/protocol/gio/PGIOChannel.ipdl | 50 ++ netwerk/protocol/gio/moz.build | 25 +- netwerk/protocol/gio/nsGIOProtocolHandler.cpp | 127 +++-- netwerk/protocol/gio/nsGIOProtocolHandler.h | 9 +- 15 files changed, 1257 insertions(+), 55 deletions(-) create mode 100644 netwerk/protocol/gio/GIOChannelChild.cpp create mode 100644 netwerk/protocol/gio/GIOChannelChild.h create mode 100644 netwerk/protocol/gio/GIOChannelParent.cpp create mode 100644 netwerk/protocol/gio/GIOChannelParent.h create mode 100644 netwerk/protocol/gio/PGIOChannel.ipdl diff --git a/ipc/ipdl/ipdl/direct_call.py b/ipc/ipdl/ipdl/direct_call.py index 42d89df1b093..413e91275b0e 100644 --- a/ipc/ipdl/ipdl/direct_call.py +++ b/ipc/ipdl/ipdl/direct_call.py @@ -666,6 +666,8 @@ VIRTUAL_CALL_CLASSES = set( ("PColorPicker", "child"), ("PColorPicker", "parent"), ("PDataChannel", "child"), + ("PGIOChannel", "child"), + ("PGIOChannel", "parent"), ("PFileChannel", "child"), ("PFilePicker", "child"), ("PFunctionBroker", "child"), diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh index ba16cc55dc4d..6ede62bf53cf 100644 --- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -543,5 +543,29 @@ struct SpeculativeConnectionOverriderArgs { bool allow1918; }; +//----------------------------------------------------------------------------- +// GIO IPDL structs +//----------------------------------------------------------------------------- + +struct GIOChannelOpenArgs +{ + URIParams uri; + uint64_t startPos; + nsCString entityID; + IPCStream? uploadStream; + LoadInfoArgs? loadInfo; + uint32_t loadFlags; +}; + +struct GIOChannelConnectArgs +{ + uint32_t channelId; +}; + +union GIOChannelCreationArgs +{ + GIOChannelOpenArgs; // For AsyncOpen: the common case. + GIOChannelConnectArgs; // Used for redirected-to channels +}; } // namespace net } // namespace mozilla diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp index 66b1569a1006..8c0d4104a7e4 100644 --- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -13,6 +13,7 @@ #include "mozilla/net/CookieServiceChild.h" #include "mozilla/net/FTPChannelChild.h" #include "mozilla/net/DataChannelChild.h" +#include "mozilla/net/GIOChannelChild.h" #include "mozilla/net/FileChannelChild.h" #include "mozilla/net/WebSocketChannelChild.h" #include "mozilla/net/WebSocketEventListenerChild.h" @@ -141,6 +142,22 @@ bool NeckoChild::DeallocPFTPChannelChild(PFTPChannelChild* channel) { return true; } +PGIOChannelChild* NeckoChild::AllocPGIOChannelChild( + PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized, + const GIOChannelCreationArgs& aOpenArgs) { + // We don't allocate here: see GIOChannelChild::AsyncOpen() + MOZ_CRASH("AllocPGIOChannelChild should not be called"); + return nullptr; +} + +bool NeckoChild::DeallocPGIOChannelChild(PGIOChannelChild* channel) { + MOZ_ASSERT(IsNeckoChild(), "DeallocPGIOChannelChild called by non-child!"); + + GIOChannelChild* child = static_cast(channel); + child->ReleaseIPDLReference(); + return true; +} + PCookieServiceChild* NeckoChild::AllocPCookieServiceChild() { // We don't allocate here: see CookieService::GetSingleton() MOZ_ASSERT_UNREACHABLE("AllocPCookieServiceChild should not be called"); diff --git a/netwerk/ipc/NeckoChild.h b/netwerk/ipc/NeckoChild.h index efc98e95b902..f853432d1373 100644 --- a/netwerk/ipc/NeckoChild.h +++ b/netwerk/ipc/NeckoChild.h @@ -42,6 +42,12 @@ class NeckoChild : public PNeckoChild { PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized, const FTPChannelCreationArgs& aOpenArgs); bool DeallocPFTPChannelChild(PFTPChannelChild*); + + PGIOChannelChild* AllocPGIOChannelChild( + PBrowserChild* aBrowser, const SerializedLoadContext& aSerialized, + const GIOChannelCreationArgs& aOpenArgs); + bool DeallocPGIOChannelChild(PGIOChannelChild*); + PWebSocketChild* AllocPWebSocketChild(PBrowserChild*, const SerializedLoadContext&, const uint32_t&); @@ -81,7 +87,7 @@ class NeckoChild : public PNeckoChild { const Maybe& aLoadInfo); bool DeallocPClassifierDummyChannelChild( - PClassifierDummyChannelChild* aChannel); + PClassifierDummyChannelChild* aActor); }; /** diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 6eea0b82059a..dcead3707850 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -18,6 +18,7 @@ #include "mozilla/net/WebSocketChannelParent.h" #include "mozilla/net/WebSocketEventListenerParent.h" #include "mozilla/net/DataChannelParent.h" +#include "mozilla/net/GIOChannelParent.h" #include "mozilla/net/DocumentChannelParent.h" #include "mozilla/net/SimpleChannelParent.h" #include "mozilla/net/AltDataOutputStreamParent.h" @@ -408,6 +409,56 @@ mozilla::ipc::IPCResult NeckoParent::RecvPDataChannelConstructor( return IPC_OK(); } +static already_AddRefed GetRequestingPrincipal( + const GIOChannelCreationArgs& aArgs) { + if (aArgs.type() != GIOChannelCreationArgs::TGIOChannelOpenArgs) { + return nullptr; + } + + const GIOChannelOpenArgs& args = aArgs.get_GIOChannelOpenArgs(); + return GetRequestingPrincipal(args.loadInfo()); +} + +PGIOChannelParent* NeckoParent::AllocPGIOChannelParent( + PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized, + const GIOChannelCreationArgs& aOpenArgs) { + nsCOMPtr requestingPrincipal = + GetRequestingPrincipal(aOpenArgs); + + nsCOMPtr loadContext; + const char* error = CreateChannelLoadContext( + aBrowser, Manager(), aSerialized, requestingPrincipal, loadContext); + if (error) { + printf_stderr( + "NeckoParent::AllocPGIOChannelParent: " + "FATAL error: %s: KILLING CHILD PROCESS\n", + error); + return nullptr; + } + PBOverrideStatus overrideStatus = + PBOverrideStatusFromLoadContext(aSerialized); + GIOChannelParent* p = new GIOChannelParent(BrowserParent::GetFrom(aBrowser), + loadContext, overrideStatus); + p->AddRef(); + return p; +} + +bool NeckoParent::DeallocPGIOChannelParent(PGIOChannelParent* channel) { + GIOChannelParent* p = static_cast(channel); + p->Release(); + return true; +} + +mozilla::ipc::IPCResult NeckoParent::RecvPGIOChannelConstructor( + PGIOChannelParent* actor, PBrowserParent* aBrowser, + const SerializedLoadContext& aSerialized, + const GIOChannelCreationArgs& aOpenArgs) { + GIOChannelParent* p = static_cast(actor); + DebugOnly rv = p->Init(aOpenArgs); + MOZ_ASSERT(rv); + return IPC_OK(); +} + PSimpleChannelParent* NeckoParent::AllocPSimpleChannelParent( const uint32_t& channelId) { RefPtr p = new SimpleChannelParent(); diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 661ebac8365d..791a04a725b7 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -153,7 +153,7 @@ class NeckoParent : public PNeckoParent { const uint16_t& aType, const OriginAttributes& aOriginAttributes, const uint32_t& aFlags); virtual mozilla::ipc::IPCResult RecvPDNSRequestConstructor( - PDNSRequestParent* actor, const nsCString& hostName, + PDNSRequestParent* actor, const nsCString& aHost, const nsCString& trrServer, const uint16_t& type, const OriginAttributes& aOriginAttributes, const uint32_t& flags) override; @@ -177,8 +177,18 @@ class NeckoParent : public PNeckoParent { virtual mozilla::ipc::IPCResult RecvPDataChannelConstructor( PDataChannelParent* aActor, const uint32_t& channelId) override; + PGIOChannelParent* AllocPGIOChannelParent( + PBrowserParent* aBrowser, const SerializedLoadContext& aSerialized, + const GIOChannelCreationArgs& aOpenArgs); + bool DeallocPGIOChannelParent(PGIOChannelParent* channel); + + virtual mozilla::ipc::IPCResult RecvPGIOChannelConstructor( + PGIOChannelParent* aActor, PBrowserParent* aBrowser, + const SerializedLoadContext& aSerialized, + const GIOChannelCreationArgs& aOpenArgs) override; + PSimpleChannelParent* AllocPSimpleChannelParent(const uint32_t& channelId); - bool DeallocPSimpleChannelParent(PSimpleChannelParent* parent); + bool DeallocPSimpleChannelParent(PSimpleChannelParent* actor); virtual mozilla::ipc::IPCResult RecvPSimpleChannelConstructor( PSimpleChannelParent* aActor, const uint32_t& channelId) override; @@ -232,7 +242,7 @@ class NeckoParent : public PNeckoParent { const Maybe& aLoadInfo); bool DeallocPClassifierDummyChannelParent( - PClassifierDummyChannelParent* aParent); + PClassifierDummyChannelParent* aActor); virtual mozilla::ipc::IPCResult RecvPClassifierDummyChannelConstructor( PClassifierDummyChannelParent* aActor, nsIURI* aURI, diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index 6add80f40ecd..c6c10621e030 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -10,6 +10,7 @@ include protocol PHttpChannel; include protocol PCookieService; include protocol PBrowser; include protocol PFTPChannel; +include protocol PGIOChannel; include protocol PWebSocket; include protocol PWebSocketEventListener; include protocol PTCPSocket; @@ -60,6 +61,7 @@ nested(upto inside_cpow) sync protocol PNecko manages PUDPSocket; manages PDNSRequest; manages PDataChannel; + manages PGIOChannel; manages PSimpleChannel; manages PFileChannel; manages PTransportProvider; @@ -112,6 +114,7 @@ parent: * the parent and the child when we're redirecting to a data: URI. */ async PDataChannel(uint32_t channelId); + async PGIOChannel(nullable PBrowser browser, SerializedLoadContext loadContext, GIOChannelCreationArgs args); async PSimpleChannel(uint32_t channelId); async PFileChannel(uint32_t channelId); diff --git a/netwerk/protocol/gio/GIOChannelChild.cpp b/netwerk/protocol/gio/GIOChannelChild.cpp new file mode 100644 index 000000000000..9becbebbb2e3 --- /dev/null +++ b/netwerk/protocol/gio/GIOChannelChild.cpp @@ -0,0 +1,468 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/NeckoChild.h" +#include "GIOChannelChild.h" +#include "nsGIOProtocolHandler.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/BrowserChild.h" +#include "nsContentUtils.h" +#include "nsIBrowserChild.h" +#include "nsStringStream.h" +#include "nsNetUtil.h" +#include "base/compiler_specific.h" +#include "mozilla/ipc/IPCStreamUtils.h" +#include "mozilla/ipc/URIUtils.h" +#include "SerializedLoadContext.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "nsIURIMutator.h" +#include "nsContentSecurityManager.h" +#include "SerializedLoadContext.h" +#include "mozilla/Logging.h" + +using mozilla::dom::ContentChild; + +namespace mozilla { +#undef LOG +#define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args) +namespace net { + +GIOChannelChild::GIOChannelChild(nsIURI* aUri) + : mIPCOpen(false), + mEventQ(new ChannelEventQueue(static_cast(this))), + mCanceled(false), + mSuspendCount(0), + mIsPending(false), + mStartPos(0), + mSuspendSent(false) { + SetURI(aUri); + // We could support thread retargeting, but as long as we're being driven by + // IPDL on the main thread it doesn't buy us anything. + DisallowThreadRetargeting(); +} + +void GIOChannelChild::AddIPDLReference() { + MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference"); + mIPCOpen = true; + AddRef(); +} + +void GIOChannelChild::ReleaseIPDLReference() { + MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference"); + mIPCOpen = false; + Release(); +} + +//----------------------------------------------------------------------------- +// GIOChannelChild::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS_INHERITED(GIOChannelChild, nsBaseChannel, nsIChildChannel) + +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelChild::AsyncOpen(nsIStreamListener* aListener) { + nsCOMPtr listener = aListener; + nsresult rv = + nsContentSecurityManager::doContentSecurityCheck(this, listener); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("GIOChannelChild::AsyncOpen [this=%p]\n", this)); + + NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); + NS_ENSURE_TRUE( + !static_cast(gNeckoChild->Manager())->IsShuttingDown(), + NS_ERROR_FAILURE); + NS_ENSURE_ARG_POINTER(listener); + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + + // Port checked in parent, but duplicate here so we can return with error + // immediately, as we've done since before e10s. + rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate, + // because in the child ipdl, + // a typedef URI is defined... + if (NS_FAILED(rv)) { + return rv; + } + + mozilla::dom::BrowserChild* browserChild = nullptr; + nsCOMPtr iBrowserChild; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, + NS_GET_IID(nsIBrowserChild), + getter_AddRefs(iBrowserChild)); + GetCallback(iBrowserChild); + if (iBrowserChild) { + browserChild = + static_cast(iBrowserChild.get()); + } + + mListener = listener; + + // add ourselves to the load group. + if (mLoadGroup) { + mLoadGroup->AddRequest(this, nullptr); + } + + mozilla::ipc::AutoIPCStream autoStream; + autoStream.Serialize(mUploadStream, + static_cast(gNeckoChild->Manager())); + + uint32_t loadFlags = 0; + GetLoadFlags(&loadFlags); + + GIOChannelOpenArgs openArgs; + SerializeURI(nsBaseChannel::URI(), openArgs.uri()); + openArgs.startPos() = mStartPos; + openArgs.entityID() = mEntityID; + openArgs.uploadStream() = autoStream.TakeOptionalValue(); + openArgs.loadFlags() = loadFlags; + + nsCOMPtr loadInfo = LoadInfo(); + rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo()); + NS_ENSURE_SUCCESS(rv, rv); + + // This must happen before the constructor message is sent. + SetupNeckoTarget(); + + gNeckoChild->SendPGIOChannelConstructor( + this, browserChild, IPC::SerializedLoadContext(this), openArgs); + + // The socket transport layer in the chrome process now has a logical ref to + // us until OnStopRequest is called. + AddIPDLReference(); + + mIsPending = true; + mWasOpened = true; + + return rv; +} + +NS_IMETHODIMP +GIOChannelChild::IsPending(bool* aResult) { + *aResult = mIsPending; + return NS_OK; +} + +nsresult GIOChannelChild::OpenContentStream(bool aAsync, + nsIInputStream** aStream, + nsIChannel** aChannel) { + MOZ_CRASH("GIOChannel*Child* should never have OpenContentStream called!"); + return NS_OK; +} + +mozilla::ipc::IPCResult GIOChannelChild::RecvOnStartRequest( + const nsresult& aChannelStatus, const int64_t& aContentLength, + const nsCString& aContentType, const nsCString& aEntityID, + const URIParams& aURI) { + LOG(("GIOChannelChild::RecvOnStartRequest [this=%p]\n", this)); + mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( + this, [self = UnsafePtr(this), aChannelStatus, + aContentLength, aContentType, aEntityID, aURI]() { + self->DoOnStartRequest(aChannelStatus, aContentLength, aContentType, + aEntityID, aURI); + })); + return IPC_OK(); +} + +void GIOChannelChild::DoOnStartRequest(const nsresult& aChannelStatus, + const int64_t& aContentLength, + const nsCString& aContentType, + const nsCString& aEntityID, + const URIParams& aURI) { + LOG(("GIOChannelChild::DoOnStartRequest [this=%p]\n", this)); + if (!mCanceled && NS_SUCCEEDED(mStatus)) { + mStatus = aChannelStatus; + } + + mContentLength = aContentLength; + SetContentType(aContentType); + mEntityID = aEntityID; + + nsCString spec; + nsCOMPtr uri = DeserializeURI(aURI); + nsresult rv = uri->GetSpec(spec); + if (NS_SUCCEEDED(rv)) { + // Changes nsBaseChannel::URI() + rv = NS_MutateURI(mURI).SetSpec(spec).Finalize(mURI); + } + + if (NS_FAILED(rv)) { + Cancel(rv); + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + rv = mListener->OnStartRequest(this); + if (NS_FAILED(rv)) { + Cancel(rv); + } +} + +mozilla::ipc::IPCResult GIOChannelChild::RecvOnDataAvailable( + const nsresult& aChannelStatus, const nsCString& aData, + const uint64_t& aOffset, const uint32_t& aCount) { + LOG(("GIOChannelChild::RecvOnDataAvailable [this=%p]\n", this)); + mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( + this, [self = UnsafePtr(this), aChannelStatus, aData, + aOffset, aCount]() { + self->DoOnDataAvailable(aChannelStatus, aData, aOffset, aCount); + })); + + return IPC_OK(); +} + +void GIOChannelChild::DoOnDataAvailable(const nsresult& aChannelStatus, + const nsCString& aData, + const uint64_t& aOffset, + const uint32_t& aCount) { + LOG(("GIOChannelChild::DoOnDataAvailable [this=%p]\n", this)); + + if (!mCanceled && NS_SUCCEEDED(mStatus)) { + mStatus = aChannelStatus; + } + + if (mCanceled) { + return; + } + + // NOTE: the OnDataAvailable contract requires the client to read all the data + // in the inputstream. This code relies on that ('data' will go away after + // this function). Apparently the previous, non-e10s behavior was to actually + // support only reading part of the data, allowing later calls to read the + // rest. + nsCOMPtr stringStream; + nsresult rv = + NS_NewByteInputStream(getter_AddRefs(stringStream), + Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND); + if (NS_FAILED(rv)) { + Cancel(rv); + return; + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + rv = mListener->OnDataAvailable(this, stringStream, aOffset, aCount); + if (NS_FAILED(rv)) { + Cancel(rv); + } + stringStream->Close(); +} + +mozilla::ipc::IPCResult GIOChannelChild::RecvOnStopRequest( + const nsresult& aChannelStatus) { + LOG(("GIOChannelChild::RecvOnStopRequest [this=%p status=%" PRIx32 "]\n", + this, static_cast(aChannelStatus))); + + mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( + this, [self = UnsafePtr(this), aChannelStatus]() { + self->DoOnStopRequest(aChannelStatus); + })); + return IPC_OK(); +} + +void GIOChannelChild::DoOnStopRequest(const nsresult& aChannelStatus) { + LOG(("GIOChannelChild::DoOnStopRequest [this=%p status=%" PRIx32 "]\n", this, + static_cast(aChannelStatus))); + + if (!mCanceled) { + mStatus = aChannelStatus; + } + + { // Ensure that all queued ipdl events are dispatched before + // we initiate protocol deletion below. + mIsPending = false; + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + (void)mListener->OnStopRequest(this, aChannelStatus); + + mListener = nullptr; + + if (mLoadGroup) { + mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus); + } + } + + // This calls NeckoChild::DeallocPGIOChannelChild(), which deletes |this| if + // IPDL holds the last reference. Don't rely on |this| existing after here! + Send__delete__(this); +} + +mozilla::ipc::IPCResult GIOChannelChild::RecvFailedAsyncOpen( + const nsresult& aStatusCode) { + LOG(("GIOChannelChild::RecvFailedAsyncOpen [this=%p status=%" PRIx32 "]\n", + this, static_cast(aStatusCode))); + mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( + this, [self = UnsafePtr(this), aStatusCode]() { + self->DoFailedAsyncOpen(aStatusCode); + })); + return IPC_OK(); +} + +void GIOChannelChild::DoFailedAsyncOpen(const nsresult& aStatusCode) { + LOG(("GIOChannelChild::DoFailedAsyncOpen [this=%p status=%" PRIx32 "]\n", + this, static_cast(aStatusCode))); + mStatus = aStatusCode; + + if (mLoadGroup) { + mLoadGroup->RemoveRequest(this, nullptr, aStatusCode); + } + + if (mListener) { + mListener->OnStartRequest(this); + mIsPending = false; + mListener->OnStopRequest(this, aStatusCode); + } else { + mIsPending = false; + } + + mListener = nullptr; + + if (mIPCOpen) { + Send__delete__(this); + } +} + +mozilla::ipc::IPCResult GIOChannelChild::RecvDeleteSelf() { + mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( + this, + [self = UnsafePtr(this)]() { self->DoDeleteSelf(); })); + return IPC_OK(); +} + +void GIOChannelChild::DoDeleteSelf() { + if (mIPCOpen) { + Send__delete__(this); + } +} + +//----------------------------------------------------------------------------- +// GIOChannelChild::nsIResumableChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelChild::Cancel(nsresult aStatus) { + LOG(("GIOChannelChild::Cancel [this=%p]\n", this)); + + if (mCanceled) { + return NS_OK; + } + + mCanceled = true; + mStatus = aStatus; + if (mIPCOpen) { + SendCancel(aStatus); + } + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelChild::Suspend() { + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); + + LOG(("GIOChannelChild::Suspend [this=%p]\n", this)); + + // SendSuspend only once, when suspend goes from 0 to 1. + if (!mSuspendCount++) { + SendSuspend(); + mSuspendSent = true; + } + mEventQ->Suspend(); + + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelChild::Resume() { + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); + + LOG(("GIOChannelChild::Resume [this=%p]\n", this)); + + // SendResume only once, when suspend count drops to 0. + if (!--mSuspendCount && mSuspendSent) { + SendResume(); + } + mEventQ->Resume(); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// GIOChannelChild::nsIChildChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelChild::ConnectParent(uint32_t aId) { + NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); + NS_ENSURE_TRUE( + !static_cast(gNeckoChild->Manager())->IsShuttingDown(), + NS_ERROR_FAILURE); + + LOG(("GIOChannelChild::ConnectParent [this=%p]\n", this)); + + mozilla::dom::BrowserChild* browserChild = nullptr; + nsCOMPtr iBrowserChild; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, + NS_GET_IID(nsIBrowserChild), + getter_AddRefs(iBrowserChild)); + GetCallback(iBrowserChild); + if (iBrowserChild) { + browserChild = + static_cast(iBrowserChild.get()); + } + + // This must happen before the constructor message is sent. + SetupNeckoTarget(); + + // The socket transport in the chrome process now holds a logical ref to us + // until OnStopRequest, or we do a redirect, or we hit an IPDL error. + AddIPDLReference(); + + GIOChannelConnectArgs connectArgs(aId); + + if (!gNeckoChild->SendPGIOChannelConstructor( + this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) { + LOG(("GIOChannelChild::CompleteRedirectSetup [this=%p]\n", this)); + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + + mIsPending = true; + mWasOpened = true; + mListener = aListener; + + // add ourselves to the load group. + if (mLoadGroup) { + mLoadGroup->AddRequest(this, nullptr); + } + + // We already have an open IPDL connection to the parent. If on-modify-request + // listeners or load group observers canceled us, let the parent handle it + // and send it back to us naturally. + return NS_OK; +} + +void GIOChannelChild::SetupNeckoTarget() { + if (mNeckoTarget) { + return; + } + nsCOMPtr loadInfo = LoadInfo(); + mNeckoTarget = + nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Network); + if (!mNeckoTarget) { + return; + } + + gNeckoChild->SetEventTargetForActor(this, mNeckoTarget); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/gio/GIOChannelChild.h b/netwerk/protocol/gio/GIOChannelChild.h new file mode 100644 index 000000000000..87eb214cb4d8 --- /dev/null +++ b/netwerk/protocol/gio/GIOChannelChild.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NS_GIOCHANNELCHILD_H +#define NS_GIOCHANNELCHILD_H + +#include "mozilla/net/PGIOChannelChild.h" +#include "mozilla/net/ChannelEventQueue.h" +#include "nsBaseChannel.h" +#include "nsIUploadChannel.h" +#include "nsIProxiedChannel.h" +#include "nsIResumableChannel.h" +#include "nsIChildChannel.h" +#include "nsIEventTarget.h" +#include "nsIStreamListener.h" + +class nsIEventTarget; + +namespace mozilla { +namespace net { + +class GIOChannelChild final : public PGIOChannelChild, + public nsBaseChannel, + public nsIChildChannel { + public: + using nsIStreamListener = ::nsIStreamListener; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSICHILDCHANNEL + + NS_IMETHOD Cancel(nsresult aStatus) override; + NS_IMETHOD Suspend() override; + NS_IMETHOD Resume() override; + + explicit GIOChannelChild(nsIURI* uri); + + void AddIPDLReference(); + void ReleaseIPDLReference(); + + NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override; + + // Note that we handle this ourselves, overriding the nsBaseChannel + // default behavior, in order to be e10s-friendly. + NS_IMETHOD IsPending(bool* aResult) override; + + nsresult OpenContentStream(bool aAsync, nsIInputStream** aStream, + nsIChannel** aChannel) override; + + bool IsSuspended() const; + + protected: + virtual ~GIOChannelChild() = default; + + mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& aChannelStatus, + const int64_t& aContentLength, + const nsCString& aContentType, + const nsCString& aEntityID, + const URIParams& aURI) override; + mozilla::ipc::IPCResult RecvOnDataAvailable(const nsresult& aChannelStatus, + const nsCString& aData, + const uint64_t& aOffset, + const uint32_t& aCount) override; + mozilla::ipc::IPCResult RecvOnStopRequest( + const nsresult& aChannelStatus) override; + mozilla::ipc::IPCResult RecvFailedAsyncOpen( + const nsresult& aStatusCode) override; + mozilla::ipc::IPCResult RecvDeleteSelf() override; + + void DoOnStartRequest(const nsresult& aChannelStatus, + const int64_t& aContentLength, + const nsCString& aContentType, + const nsCString& aEntityID, const URIParams& aURI); + void DoOnDataAvailable(const nsresult& aChannelStatus, const nsCString& aData, + const uint64_t& aOffset, const uint32_t& aCount); + void DoOnStopRequest(const nsresult& aChannelStatus); + void DoFailedAsyncOpen(const nsresult& aStatusCode); + void DoDeleteSelf(); + + void SetupNeckoTarget() override; + + friend class NeckoTargetChannelFunctionEvent; + + private: + nsCOMPtr mUploadStream; + + bool mIPCOpen; + const RefPtr mEventQ; + + bool mCanceled = false; + uint32_t mSuspendCount; + bool mIsPending = false; + + uint64_t mStartPos; + nsCString mEntityID; + + // Set if SendSuspend is called. Determines if SendResume is needed when + // diverting callbacks to parent. + bool mSuspendSent; +}; + +inline bool GIOChannelChild::IsSuspended() const { return mSuspendCount != 0; } + +} // namespace net +} // namespace mozilla + +#endif /* NS_GIOCHANNELCHILD_H */ diff --git a/netwerk/protocol/gio/GIOChannelParent.cpp b/netwerk/protocol/gio/GIOChannelParent.cpp new file mode 100644 index 000000000000..c07195d31591 --- /dev/null +++ b/netwerk/protocol/gio/GIOChannelParent.cpp @@ -0,0 +1,323 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GIOChannelParent.h" +#include "nsGIOProtocolHandler.h" +#include "mozilla/Assertions.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/net/NeckoParent.h" +#include "nsNetUtil.h" +#include "nsIChannel.h" +#include "mozilla/net/NeckoChannelParams.h" +#include "nsIAuthPrompt.h" +#include "nsIAuthPromptProvider.h" +#include "nsISecureBrowserUI.h" +#include "nsQueryObject.h" +#include "mozilla/Logging.h" + +using namespace mozilla::dom; +using namespace mozilla::ipc; + +namespace mozilla { +#undef LOG +#define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args) +namespace net { + +GIOChannelParent::GIOChannelParent(dom::BrowserParent* aIframeEmbedding, + nsILoadContext* aLoadContext, + PBOverrideStatus aOverrideStatus) + : mLoadContext(aLoadContext), + mPBOverride(aOverrideStatus), + mBrowserParent(aIframeEmbedding) { + mEventQ = new ChannelEventQueue(static_cast(this)); +} + +void GIOChannelParent::ActorDestroy(ActorDestroyReason why) { + // We may still have refcount>0 if the channel hasn't called OnStopRequest + // yet, but we must not send any more msgs to child. + mIPCClosed = true; +} + +//----------------------------------------------------------------------------- +// GIOChannelParent::nsISupports +//----------------------------------------------------------------------------- +NS_IMPL_ISUPPORTS(GIOChannelParent, nsIStreamListener, nsIParentChannel, + nsIInterfaceRequestor, nsIRequestObserver) + +//----------------------------------------------------------------------------- +// GIOChannelParent methods +//----------------------------------------------------------------------------- + +bool GIOChannelParent::Init(const GIOChannelCreationArgs& aOpenArgs) { + switch (aOpenArgs.type()) { + case GIOChannelCreationArgs::TGIOChannelOpenArgs: { + const GIOChannelOpenArgs& a = aOpenArgs.get_GIOChannelOpenArgs(); + return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(), + a.loadInfo(), a.loadFlags()); + } + case GIOChannelCreationArgs::TGIOChannelConnectArgs: { + const GIOChannelConnectArgs& cArgs = + aOpenArgs.get_GIOChannelConnectArgs(); + return ConnectChannel(cArgs.channelId()); + } + default: + MOZ_ASSERT_UNREACHABLE("unknown open type"); + return false; + } +} + +bool GIOChannelParent::DoAsyncOpen(const URIParams& aURI, + const uint64_t& aStartPos, + const nsCString& aEntityID, + const Maybe& aUploadStream, + const Maybe& aLoadInfoArgs, + const uint32_t& aLoadFlags) { + nsresult rv; + + nsCOMPtr uri = DeserializeURI(aURI); + if (!uri) { + return false; + } + +#ifdef DEBUG + LOG(("GIOChannelParent DoAsyncOpen [this=%p uri=%s]\n", this, + uri->GetSpecOrDefault().get())); +#endif + + nsCOMPtr ios(do_GetIOService(&rv)); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + nsCOMPtr loadInfo; + rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs, + getter_AddRefs(loadInfo)); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + OriginAttributes attrs; + rv = loadInfo->GetOriginAttributes(&attrs); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + nsCOMPtr chan; + rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo, nullptr, + nullptr, nullptr, aLoadFlags, ios); + + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + mChannel = chan; + + nsIChannel* gioChan = static_cast(mChannel.get()); + + rv = gioChan->AsyncOpen(this); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + return true; +} + +bool GIOChannelParent::ConnectChannel(const uint64_t& channelId) { + nsresult rv; + + LOG(("Looking for a registered channel [this=%p, id=%" PRIx64 "]", this, + channelId)); + + nsCOMPtr channel; + rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)); + if (NS_SUCCEEDED(rv)) { + mChannel = channel; + } + + LOG((" found channel %p, rv=%08" PRIx32, mChannel.get(), + static_cast(rv))); + + return true; +} + +mozilla::ipc::IPCResult GIOChannelParent::RecvCancel(const nsresult& status) { + if (mChannel) { + mChannel->Cancel(status); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GIOChannelParent::RecvSuspend() { + if (mChannel) { + mChannel->Suspend(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GIOChannelParent::RecvResume() { + if (mChannel) { + mChannel->Resume(); + } + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// GIOChannelParent::nsIRequestObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelParent::OnStartRequest(nsIRequest* aRequest) { + LOG(("GIOChannelParent::OnStartRequest [this=%p]\n", this)); + nsCOMPtr chan = do_QueryInterface(aRequest); + MOZ_ASSERT(chan); + NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); + + int64_t contentLength; + chan->GetContentLength(&contentLength); + nsCString contentType; + chan->GetContentType(contentType); + nsresult channelStatus = NS_OK; + chan->GetStatus(&channelStatus); + + nsCString entityID; + URIParams uriparam; + nsCOMPtr uri; + chan->GetURI(getter_AddRefs(uri)); + SerializeURI(uri, uriparam); + + if (mIPCClosed || !SendOnStartRequest(channelStatus, contentLength, + contentType, entityID, uriparam)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { + LOG(("GIOChannelParent::OnStopRequest: [this=%p status=%" PRIu32 "]\n", this, + static_cast(aStatusCode))); + + if (mIPCClosed || !SendOnStopRequest(aStatusCode)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// GIOChannelParent::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelParent::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, + uint64_t aOffset, uint32_t aCount) { + LOG(("GIOChannelParent::OnDataAvailable [this=%p]\n", this)); + + nsCString data; + nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount); + if (NS_FAILED(rv)) { + return rv; + } + + nsresult channelStatus = NS_OK; + mChannel->GetStatus(&channelStatus); + + if (mIPCClosed || + !SendOnDataAvailable(channelStatus, data, aOffset, aCount)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +//----------------------------------------------------------------------------- +// GIOChannelParent::nsIParentChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelParent::SetParentListener(ParentChannelListener* aListener) { + // Do not need ptr to ParentChannelListener. + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags, + bool aIsThirdParty) { + // Nothing to do. + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::NotifyFlashPluginStateChanged( + nsIHttpChannel::FlashPluginState aState) { + // Nothing to do. + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::SetClassifierMatchedInfo(const nsACString& aList, + const nsACString& aProvider, + const nsACString& aFullHash) { + // nothing to do + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::SetClassifierMatchedTrackingInfo( + const nsACString& aLists, const nsACString& aFullHashes) { + // nothing to do + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::Delete() { + if (mIPCClosed || !SendDeleteSelf()) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_IMETHODIMP +GIOChannelParent::GetRemoteType(nsACString& aRemoteType) { + if (!CanSend()) { + return NS_ERROR_UNEXPECTED; + } + + dom::PContentParent* pcp = Manager()->Manager(); + aRemoteType = static_cast(pcp)->GetRemoteType(); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// GIOChannelParent::nsIInterfaceRequestor +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +GIOChannelParent::GetInterface(const nsIID& uuid, void** result) { + if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) || + uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) { + if (mBrowserParent) { + return mBrowserParent->QueryInterface(uuid, result); + } + } + + // Only support nsILoadContext if child channel's callbacks did too + if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) { + nsCOMPtr copy = mLoadContext; + copy.forget(result); + return NS_OK; + } + + return QueryInterface(uuid, result); +} + +//--------------------- +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/gio/GIOChannelParent.h b/netwerk/protocol/gio/GIOChannelParent.h new file mode 100644 index 000000000000..ec75aace8c81 --- /dev/null +++ b/netwerk/protocol/gio/GIOChannelParent.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NS_GIOCHANNELPARENT_H +#define NS_GIOCHANNELPARENT_H + +#include "mozilla/net/PGIOChannelParent.h" +#include "mozilla/net/NeckoParent.h" +#include "nsIParentChannel.h" +#include "nsIInterfaceRequestor.h" + +class nsILoadContext; + +namespace mozilla { + +namespace dom { +class BrowserParent; +} // namespace dom + +namespace net { +class ChannelEventQueue; + +class GIOChannelParent final : public PGIOChannelParent, + public nsIParentChannel, + public nsIInterfaceRequestor { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIPARENTCHANNEL + NS_DECL_NSIINTERFACEREQUESTOR + + GIOChannelParent(dom::BrowserParent* aIframeEmbedding, + nsILoadContext* aLoadContext, + PBOverrideStatus aOverrideStatus); + + bool Init(const GIOChannelCreationArgs& aOpenArgs); + + protected: + virtual ~GIOChannelParent() = default; + + bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos, + const nsCString& aEntityID, + const Maybe& aUploadStream, + const Maybe& aLoadInfoArgs, + const uint32_t& aLoadFlags); + + // used to connect redirected-to channel in parent with just created + // ChildChannel. Used during HTTP->FTP redirects. + bool ConnectChannel(const uint64_t& channelId); + + virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& status) override; + virtual mozilla::ipc::IPCResult RecvSuspend() override; + virtual mozilla::ipc::IPCResult RecvResume() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + nsCOMPtr mChannel; + + bool mIPCClosed = false; + + nsCOMPtr mLoadContext; + + PBOverrideStatus mPBOverride; + + // Set to the canceled status value if the main channel was canceled. + nsresult mStatus = NS_OK; + + RefPtr mBrowserParent; + + RefPtr mEventQ; +}; + +} // namespace net +} // namespace mozilla + +#endif /* NS_GIOCHANNELPARENT_H */ diff --git a/netwerk/protocol/gio/PGIOChannel.ipdl b/netwerk/protocol/gio/PGIOChannel.ipdl new file mode 100644 index 000000000000..6b7bcc96a217 --- /dev/null +++ b/netwerk/protocol/gio/PGIOChannel.ipdl @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PNecko; +include URIParams; + +//FIXME: bug #792908 (NeckoChannelParams already included by PNecko) +include NeckoChannelParams; + +using PRTime from "prtime.h"; + +namespace mozilla { +namespace net { + +async protocol PGIOChannel +{ + manager PNecko; + +parent: + // Note: channels are opened during construction, so no open method here: + // see PNecko.ipdl + + async __delete__(); + + async Cancel(nsresult status); + async Suspend(); + async Resume(); + +child: + async OnStartRequest(nsresult aChannelStatus, + int64_t aContentLength, + nsCString aContentType, + nsCString aEntityID, + URIParams aURI); + async OnDataAvailable(nsresult channelStatus, + nsCString data, + uint64_t offset, + uint32_t count); + async OnStopRequest(nsresult channelStatus); + async FailedAsyncOpen(nsresult statusCode); + + async DeleteSelf(); +}; + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/gio/moz.build b/netwerk/protocol/gio/moz.build index cd659096bf25..7e28502558dd 100644 --- a/netwerk/protocol/gio/moz.build +++ b/netwerk/protocol/gio/moz.build @@ -4,10 +4,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ - "nsGIOProtocolHandler.cpp", -] - XPCOM_MANIFESTS += [ "components.conf", ] @@ -16,6 +12,27 @@ EXPORTS += [ "nsGIOProtocolHandler.h", ] +EXPORTS.mozilla.net += [ + "GIOChannelChild.h", + "GIOChannelParent.h", +] + +UNIFIED_SOURCES += [ + "GIOChannelChild.cpp", + "GIOChannelParent.cpp", + "nsGIOProtocolHandler.cpp", +] + +IPDL_SOURCES = [ + "PGIOChannel.ipdl", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +LOCAL_INCLUDES += [ + "/netwerk/base", +] + FINAL_LIBRARY = "xul" CXXFLAGS += CONFIG["TK_CFLAGS"] diff --git a/netwerk/protocol/gio/nsGIOProtocolHandler.cpp b/netwerk/protocol/gio/nsGIOProtocolHandler.cpp index 61c642d8f127..f1ea22f92f9f 100644 --- a/netwerk/protocol/gio/nsGIOProtocolHandler.cpp +++ b/netwerk/protocol/gio/nsGIOProtocolHandler.cpp @@ -8,9 +8,11 @@ * input stream provided by GVFS/GIO. */ #include "nsGIOProtocolHandler.h" +#include "GIOChannelChild.h" #include "mozilla/Components.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Logging.h" +#include "mozilla/net/NeckoChild.h" #include "mozilla/NullPrincipal.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" @@ -41,8 +43,9 @@ using namespace mozilla; //----------------------------------------------------------------------------- // NSPR_LOG_MODULES=gio:5 -static mozilla::LazyLogModule sGIOLog("gio"); -#define LOG(args) MOZ_LOG(sGIOLog, mozilla::LogLevel::Debug, args) +LazyLogModule gGIOLog("gio"); +#undef LOG +#define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args) //----------------------------------------------------------------------------- static nsresult MapGIOResult(gint code) { @@ -108,17 +111,21 @@ static nsresult MapGIOResult(gint code) { } static nsresult MapGIOResult(GError* result) { - if (!result) return NS_OK; + if (!result) { + return NS_OK; + } return MapGIOResult(result->code); } + /** Return values for mount operation. * These enums are used as mount operation return values. */ -typedef enum { +enum class MountOperationResult { MOUNT_OPERATION_IN_PROGRESS, /** \enum operation in progress */ MOUNT_OPERATION_SUCCESS, /** \enum operation successful */ MOUNT_OPERATION_FAILED /** \enum operation not successful */ -} MountOperationResult; +}; + //----------------------------------------------------------------------------- /** * Sort function compares according to file type (directory/file) @@ -132,11 +139,13 @@ static gint FileInfoComparator(gconstpointer a, gconstpointer b) { GFileInfo* ia = (GFileInfo*)a; GFileInfo* ib = (GFileInfo*)b; if (g_file_info_get_file_type(ia) == G_FILE_TYPE_DIRECTORY && - g_file_info_get_file_type(ib) != G_FILE_TYPE_DIRECTORY) + g_file_info_get_file_type(ib) != G_FILE_TYPE_DIRECTORY) { return -1; + } if (g_file_info_get_file_type(ib) == G_FILE_TYPE_DIRECTORY && - g_file_info_get_file_type(ia) != G_FILE_TYPE_DIRECTORY) + g_file_info_get_file_type(ia) != G_FILE_TYPE_DIRECTORY) { return 1; + } return strcasecmp(g_file_info_get_name(ia), g_file_info_get_name(ib)); } @@ -149,10 +158,7 @@ static void mount_operation_ask_password( GMountOperation* mount_op, const char* message, const char* default_user, const char* default_domain, GAskPasswordFlags flags, gpointer user_data); //----------------------------------------------------------------------------- - class nsGIOInputStream final : public nsIInputStream { - ~nsGIOInputStream() { Close(); } - public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIINPUTSTREAM @@ -189,6 +195,7 @@ class nsGIOInputStream final : public nsIInputStream { void SetMountResult(MountOperationResult result, gint error_code); private: + ~nsGIOInputStream() { Close(); } nsresult DoOpen(); nsresult DoRead(char* aBuf, uint32_t aCount, uint32_t* aCountRead); nsresult SetContentTypeOfChannel(const char* contentType); @@ -206,10 +213,12 @@ class nsGIOInputStream final : public nsIInputStream { nsCString mDirBuf; uint32_t mDirBufCursor; bool mDirOpen; - MountOperationResult mMountRes; + MountOperationResult mMountRes = + MountOperationResult::MOUNT_OPERATION_SUCCESS; mozilla::Monitor mMonitorMountInProgress; - gint mMountErrorCode; + gint mMountErrorCode{}; }; + /** * Set result of mount operation and notify monitor waiting for results. * This method is called in main thread as long as it is used only @@ -232,7 +241,7 @@ nsresult nsGIOInputStream::MountVolume() { GMountOperation* mount_op = g_mount_operation_new(); g_signal_connect(mount_op, "ask-password", G_CALLBACK(mount_operation_ask_password), mChannel); - mMountRes = MOUNT_OPERATION_IN_PROGRESS; + mMountRes = MountOperationResult::MOUNT_OPERATION_IN_PROGRESS; /* g_file_mount_enclosing_volume uses a dbus request to mount the volume. Callback mount_enclosing_volume_finished is called in main thread (not this thread on which this method is called). */ @@ -240,11 +249,13 @@ nsresult nsGIOInputStream::MountVolume() { mount_enclosing_volume_finished, this); mozilla::MonitorAutoLock mon(mMonitorMountInProgress); /* Waiting for finish of mount operation thread */ - while (mMountRes == MOUNT_OPERATION_IN_PROGRESS) mon.Wait(); + while (mMountRes == MountOperationResult::MOUNT_OPERATION_IN_PROGRESS) { + mon.Wait(); + } g_object_unref(mount_op); - if (mMountRes == MOUNT_OPERATION_FAILED) { + if (mMountRes == MountOperationResult::MOUNT_OPERATION_FAILED) { return MapGIOResult(mMountErrorCode); } return NS_OK; @@ -288,7 +299,9 @@ nsresult nsGIOInputStream::DoOpenDirectory() { // Write base URL (make sure it ends with a '/') mDirBuf.AppendLiteral("300: "); mDirBuf.Append(mSpec); - if (mSpec.get()[mSpec.Length() - 1] != '/') mDirBuf.Append('/'); + if (mSpec.get()[mSpec.Length() - 1] != '/') { + mDirBuf.Append('/'); + } mDirBuf.Append('\n'); // Write column names @@ -360,7 +373,9 @@ nsresult nsGIOInputStream::DoOpen() { if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) { // location is not yet mounted, try to mount g_error_free(error); - if (NS_IsMainThread()) return NS_ERROR_NOT_CONNECTED; + if (NS_IsMainThread()) { + return NS_ERROR_NOT_CONNECTED; + } error = nullptr; rv = MountVolume(); if (rv != NS_OK) { @@ -395,7 +410,9 @@ nsresult nsGIOInputStream::DoOpen() { g_warning("Unable to get file type."); rv = NS_ERROR_FILE_NOT_FOUND; } - if (info) g_object_unref(info); + if (info) { + g_object_unref(info); + } return rv; } @@ -590,7 +607,9 @@ nsGIOInputStream::Close() { mSpec.Truncate(); // free memory // Prevent future reads from re-opening the handle. - if (NS_SUCCEEDED(mStatus)) mStatus = NS_BASE_STREAM_CLOSED; + if (NS_SUCCEEDED(mStatus)) { + mStatus = NS_BASE_STREAM_CLOSED; + } return NS_OK; } @@ -601,7 +620,9 @@ nsGIOInputStream::Close() { */ NS_IMETHODIMP nsGIOInputStream::Available(uint64_t* aResult) { - if (NS_FAILED(mStatus)) return mStatus; + if (NS_FAILED(mStatus)) { + return mStatus; + } *aResult = mBytesRemaining; @@ -628,7 +649,9 @@ nsGIOInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aCountRead) { mStatus = DoRead(aBuf, aCount, aCountRead); // Check if all data has been read - if (mStatus == NS_BASE_STREAM_CLOSED) return NS_OK; + if (mStatus == NS_BASE_STREAM_CLOSED) { + return NS_OK; + } // Check whenever any error appears while reading return mStatus; @@ -671,10 +694,11 @@ static void mount_enclosing_volume_finished(GObject* source_object, if (error) { g_warning("Mount failed: %s %d", error->message, error->code); - istream->SetMountResult(MOUNT_OPERATION_FAILED, error->code); + istream->SetMountResult(MountOperationResult::MOUNT_OPERATION_FAILED, + error->code); g_error_free(error); } else { - istream->SetMountResult(MOUNT_OPERATION_SUCCESS, 0); + istream->SetMountResult(MountOperationResult::MOUNT_OPERATION_SUCCESS, 0); } } @@ -840,6 +864,9 @@ already_AddRefed nsGIOProtocolHandler::GetSingleton() { NS_IMPL_ISUPPORTS(nsGIOProtocolHandler, nsIProtocolHandler, nsIObserver) nsresult nsGIOProtocolHandler::Init() { + if (net::IsNeckoChild()) { + net::NeckoChild::InitNeckoChild(); + } nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefs) { InitSupportedProtocolsPref(prefs); @@ -865,29 +892,21 @@ void nsGIOProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch* prefs) { #ifdef MOZ_PROXY_BYPASS_PROTECTION "" // use none #else - "smb:,sftp:" // use defaults + "sftp:" // use defaults (comma separated list) #endif ); } LOG(("gio: supported protocols \"%s\"\n", mSupportedProtocols.get())); } -bool nsGIOProtocolHandler::IsSupportedProtocol(const nsCString& aSpec) { - const char* specString = aSpec.get(); - const char* colon = strchr(specString, ':'); - if (!colon) return false; - - uint32_t length = colon - specString + 1; - - // + ':' - nsCString scheme(specString, length); - - char* found = PL_strcasestr(mSupportedProtocols.get(), scheme.get()); - if (!found) return false; - - if (found[length] != ',' && found[length] != '\0') return false; - - return true; +bool nsGIOProtocolHandler::IsSupportedProtocol(const nsCString& aScheme) { + nsAutoCString schemeWithColon = aScheme + ":"_ns; + for (const auto& protocol : mSupportedProtocols.Split(',')) { + if (schemeWithColon.Equals(protocol, nsCaseInsensitiveCStringComparator)) { + return true; + } + } + return false; } NS_IMETHODIMP @@ -940,10 +959,8 @@ nsGIOProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsAutoCString spec; rv = aURI->GetSpec(spec); - if (NS_FAILED(rv)) return rv; - - if (!IsSupportedProtocol(spec)) { - return NS_ERROR_UNKNOWN_PROTOCOL; + if (NS_FAILED(rv)) { + return rv; } nsAutoCString scheme; @@ -952,10 +969,29 @@ nsGIOProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, return rv; } - if (!IsValidGIOScheme(scheme)) { + if (!IsSupportedProtocol(scheme)) { return NS_ERROR_UNKNOWN_PROTOCOL; } + // g_vfs_get_supported_uri_schemes() returns a very limited list in the + // child due to the sandbox, so we only check if its valid for the parent. + if (XRE_IsParentProcess() && !IsValidGIOScheme(scheme)) { + return NS_ERROR_UNKNOWN_PROTOCOL; + } + + RefPtr channel; + if (net::IsNeckoChild()) { + channel = new mozilla::net::GIOChannelChild(aURI); + // set the loadInfo on the new channel + channel->SetLoadInfo(aLoadInfo); + + rv = channel->SetContentType(nsLiteralCString(UNKNOWN_CONTENT_TYPE)); + NS_ENSURE_SUCCESS(rv, rv); + + channel.forget(aResult); + return NS_OK; + } + RefPtr stream = new nsGIOInputStream(spec); if (!stream) { return NS_ERROR_OUT_OF_MEMORY; @@ -969,6 +1005,7 @@ nsGIOProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, if (NS_SUCCEEDED(rv)) { stream->SetChannel(*aResult); } + return rv; } diff --git a/netwerk/protocol/gio/nsGIOProtocolHandler.h b/netwerk/protocol/gio/nsGIOProtocolHandler.h index 4c8d936402d8..3a41776459d5 100644 --- a/netwerk/protocol/gio/nsGIOProtocolHandler.h +++ b/netwerk/protocol/gio/nsGIOProtocolHandler.h @@ -10,6 +10,9 @@ #include "nsIPrefBranch.h" #include "nsStringFwd.h" +#include "mozilla/Logging.h" +extern mozilla::LazyLogModule gGIOLog; + class nsGIOProtocolHandler final : public nsIProtocolHandler, public nsIObserver { public: @@ -18,11 +21,13 @@ class nsGIOProtocolHandler final : public nsIProtocolHandler, NS_DECL_NSIOBSERVER static already_AddRefed GetSingleton(); - bool IsSupportedProtocol(const nsCString& spec); + bool IsSupportedProtocol(const nsCString& aScheme); + + protected: + ~nsGIOProtocolHandler() = default; private: nsresult Init(); - ~nsGIOProtocolHandler() = default; void InitSupportedProtocolsPref(nsIPrefBranch* prefs);