From bee28f01d7b308eaf8975dc311cb48c79f36e042 Mon Sep 17 00:00:00 2001 From: Bogdan Tara Date: Fri, 20 Sep 2019 06:58:44 +0300 Subject: [PATCH] Backed out 8 changesets (bug 1575744) for HttpChannelParent related assertion failures Backed out changeset af61675dd488 (bug 1575744) Backed out changeset bf794b9373c8 (bug 1575744) Backed out changeset 39ffb74d2e12 (bug 1575744) Backed out changeset c1547b3df672 (bug 1575744) Backed out changeset 382ee8672027 (bug 1575744) Backed out changeset 5abb38484f11 (bug 1575744) Backed out changeset d5244c1bbfe8 (bug 1575744) Backed out changeset c74b81debf73 (bug 1575744) --HG-- rename : netwerk/base/nsIProcessSwitchRequestor.idl => netwerk/base/nsICrossProcessSwitchChannel.idl --- .../components/sessionstore/SessionStore.jsm | 31 ++-- dom/ipc/ContentChild.cpp | 13 +- dom/ipc/ContentChild.h | 2 +- dom/ipc/PContent.ipdl | 6 +- dom/promise/PromiseNativeHandler.cpp | 54 ------- dom/promise/PromiseNativeHandler.h | 28 +--- dom/promise/moz.build | 1 - netwerk/base/moz.build | 2 +- netwerk/base/nsICrossProcessSwitchChannel.idl | 39 +++++ netwerk/base/nsIProcessSwitchRequestor.idl | 45 ------ netwerk/ipc/DocumentChannelParent.cpp | 137 ++++++----------- netwerk/ipc/DocumentChannelParent.h | 23 +-- netwerk/protocol/http/HttpBaseChannel.cpp | 11 ++ netwerk/protocol/http/HttpBaseChannel.h | 3 + netwerk/protocol/http/HttpChannelChild.cpp | 21 +++ netwerk/protocol/http/HttpChannelChild.h | 2 + netwerk/protocol/http/HttpChannelParent.cpp | 56 ++++--- netwerk/protocol/http/HttpChannelParent.h | 20 +-- netwerk/protocol/http/NullHttpChannel.cpp | 11 ++ netwerk/protocol/http/PHttpChannel.ipdl | 6 + netwerk/protocol/http/moz.build | 1 - netwerk/protocol/http/nsHttpChannel.cpp | 145 ++++++++++-------- netwerk/protocol/http/nsHttpChannel.h | 11 +- netwerk/protocol/http/nsHttpHandler.cpp | 9 -- netwerk/protocol/http/nsHttpHandler.h | 6 +- netwerk/protocol/http/nsIHttpChannel.idl | 20 +++ .../protocol/http/nsIHttpProtocolHandler.idl | 6 +- netwerk/protocol/viewsource/moz.build | 2 - .../viewsource/nsViewSourceChannel.cpp | 33 +++- .../protocol/viewsource/nsViewSourceChannel.h | 18 +-- .../browser/browser_cross_process_redirect.js | 18 +-- xpcom/threads/MozPromise.h | 12 +- xpcom/threads/MozPromiseInlines.h | 53 ------- xpcom/threads/moz.build | 1 - 34 files changed, 355 insertions(+), 491 deletions(-) delete mode 100644 dom/promise/PromiseNativeHandler.cpp create mode 100644 netwerk/base/nsICrossProcessSwitchChannel.idl delete mode 100644 netwerk/base/nsIProcessSwitchRequestor.idl delete mode 100644 xpcom/threads/MozPromiseInlines.h diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 69d666861a8b..4057f5be9bc3 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -2606,8 +2606,8 @@ var SessionStoreInternal = { }, // Examine the channel response to see if we should change the process - // performing the given load. aRequestor implements nsIProcessSwitchRequestor - onMayChangeProcess(aRequestor) { + // performing the given load. + onMayChangeProcess(aChannel) { if ( !E10SUtils.useHttpResponseProcessSelection() && !E10SUtils.useCrossOriginOpenerPolicy() @@ -2615,26 +2615,19 @@ var SessionStoreInternal = { return; } - let switchRequestor; - try { - switchRequestor = aRequestor.QueryInterface(Ci.nsIProcessSwitchRequestor); - } catch (e) { - debug(`[process-switch]: object not compatible with process switching `); - return; - } + aChannel.QueryInterface(Ci.nsIHttpChannel); - const channel = switchRequestor.channel; - if (!channel.isDocument || !channel.loadInfo) { + if (!aChannel.isDocument || !aChannel.loadInfo) { return; // Not a document load. } // Check that the document has a corresponding BrowsingContext. let browsingContext; - let cp = channel.loadInfo.externalContentPolicyType; + let cp = aChannel.loadInfo.externalContentPolicyType; if (cp == Ci.nsIContentPolicy.TYPE_DOCUMENT) { - browsingContext = channel.loadInfo.browsingContext; + browsingContext = aChannel.loadInfo.browsingContext; } else { - browsingContext = channel.loadInfo.frameBrowsingContext; + browsingContext = aChannel.loadInfo.frameBrowsingContext; } if (!browsingContext) { @@ -2698,7 +2691,7 @@ var SessionStoreInternal = { // Determine the process type the load should be performed in. let resultPrincipal = Services.scriptSecurityManager.getChannelResultPrincipal( - channel + aChannel ); let remoteType = E10SUtils.getRemoteTypeForPrincipal( resultPrincipal, @@ -2710,7 +2703,7 @@ var SessionStoreInternal = { if ( currentRemoteType == remoteType && (!E10SUtils.useCrossOriginOpenerPolicy() || - !switchRequestor.hasCrossOriginOpenerPolicyMismatch()) + !aChannel.hasCrossOriginOpenerPolicyMismatch()) ) { debug(`[process-switch]: type (${remoteType}) is compatible - ignoring`); return; @@ -2726,7 +2719,7 @@ var SessionStoreInternal = { const isCOOPSwitch = E10SUtils.useCrossOriginOpenerPolicy() && - switchRequestor.hasCrossOriginOpenerPolicyMismatch(); + aChannel.hasCrossOriginOpenerPolicyMismatch(); // ------------------------------------------------------------------------ // DANGER ZONE: Perform a process switch into the new process. This is @@ -2736,11 +2729,11 @@ var SessionStoreInternal = { let tabPromise = this._doProcessSwitch( browsingContext, remoteType, - channel, + aChannel, identifier, isCOOPSwitch ); - switchRequestor.switchProcessTo(tabPromise, identifier); + aChannel.switchProcessTo(tabPromise, identifier); }, /* ........ nsISessionStore API .............. */ diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 269feff50a76..427e01483c92 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -3633,7 +3633,7 @@ mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect( const ReplacementChannelConfigInit& aConfig, const Maybe& aLoadInfo, const uint64_t& aChannelId, nsIURI* aOriginalURI, const uint64_t& aIdentifier, - const uint32_t& aRedirectMode, CrossProcessRedirectResolver&& aResolve) { + const uint32_t& aRedirectMode) { nsCOMPtr loadInfo; nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(loadInfo)); @@ -3655,15 +3655,8 @@ mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect( // This is used to report any errors back to the parent by calling // CrossProcessRedirectFinished. - auto scopeExit = MakeScopeExit([&]() { - nsCOMPtr loadInfo; - MOZ_ALWAYS_SUCCEEDS(newChannel->GetLoadInfo(getter_AddRefs(loadInfo))); - Maybe loadInfoArgs; - MOZ_ALWAYS_SUCCEEDS( - mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs)); - aResolve( - Tuple&>(rv, loadInfoArgs)); - }); + auto scopeExit = + MakeScopeExit([&]() { httpChild->CrossProcessRedirectFinished(rv); }); rv = httpChild->SetChannelId(aChannelId); if (NS_FAILED(rv)) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index d21ea7f969b2..46d008959f88 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -662,7 +662,7 @@ class ContentChild final : public PContentChild, const ReplacementChannelConfigInit& aConfig, const Maybe& aLoadInfoForwarder, const uint64_t& aChannelId, nsIURI* aOriginalURI, const uint64_t& aIdentifier, - const uint32_t& aRedirectMode, CrossProcessRedirectResolver&& aResolve); + const uint32_t& aRedirectMode); mozilla::ipc::IPCResult RecvStartDelayedAutoplayMediaComponents( BrowsingContext* aContext); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 7cdb8c84d235..32984b383a9c 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -791,9 +791,6 @@ child: // new HttpChannelChild that will be connected to the parent channel // represented by registrarId. // This is on PContent not PNecko, as PNecko may not be initialized yet. - // The returned loadInfo needs to be set on the channel - since the channel - // moved to a new process it now has different properties. - async CrossProcessRedirect(uint32_t aRegistrarId, nsIURI aURI, ReplacementChannelConfigInit config, @@ -801,8 +798,7 @@ child: uint64_t aChannelId, nsIURI aOriginalURI, uint64_t aIdentifier, - uint32_t aRedirectMode) - returns (nsresult rv, LoadInfoArgs? arg); + uint32_t aRedirectMode); /** * This method is used to notifty content process to start delayed autoplay diff --git a/dom/promise/PromiseNativeHandler.cpp b/dom/promise/PromiseNativeHandler.cpp deleted file mode 100644 index b7bcf695eb92..000000000000 --- a/dom/promise/PromiseNativeHandler.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 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 "PromiseNativeHandler.h" -#include "mozilla/dom/Promise.h" -#include "nsISupportsImpl.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS0(DomPromiseListener) - -DomPromiseListener::DomPromiseListener(dom::Promise* aDOMPromise) { - aDOMPromise->AppendNativeHandler(this); -} - -DomPromiseListener::DomPromiseListener(dom::Promise* aDOMPromise, - CallbackType&& aResolve, - CallbackType&& aReject) - : mResolve(Some(std::move(aResolve))), mReject(Some(std::move(aReject))) { - aDOMPromise->AppendNativeHandler(this); -} - -void DomPromiseListener::ResolvedCallback(JSContext* aCx, - JS::Handle aValue) { - if (mResolve) { - (*mResolve)(aCx, aValue); - } - // Let's clear the lambdas in case we have a cycle to ourselves. - mResolve.reset(); - mReject.reset(); -} - -void DomPromiseListener::RejectedCallback(JSContext* aCx, - JS::Handle aValue) { - if (mReject) { - (*mReject)(aCx, aValue); - } - // Let's clear the lambdas in case we have a cycle to ourselves. - mResolve.reset(); - mReject.reset(); -} - -void DomPromiseListener::SetResolvers(CallbackType&& aResolve, - CallbackType&& aReject) { - mResolve = Some(std::move(aResolve)); - mReject = Some(std::move(aReject)); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/promise/PromiseNativeHandler.h b/dom/promise/PromiseNativeHandler.h index 73f27c73275b..ba714f7b008f 100644 --- a/dom/promise/PromiseNativeHandler.h +++ b/dom/promise/PromiseNativeHandler.h @@ -7,15 +7,12 @@ #ifndef mozilla_dom_PromiseNativeHandler_h #define mozilla_dom_PromiseNativeHandler_h -#include -#include "js/TypeDecls.h" #include "nsISupports.h" +#include "js/TypeDecls.h" namespace mozilla { namespace dom { -class Promise; - /* * PromiseNativeHandler allows C++ to react to a Promise being * rejected/resolved. A PromiseNativeHandler can be appended to a Promise using @@ -23,7 +20,7 @@ class Promise; */ class PromiseNativeHandler : public nsISupports { protected: - virtual ~PromiseNativeHandler() = default; + virtual ~PromiseNativeHandler() {} public: MOZ_CAN_RUN_SCRIPT @@ -35,27 +32,6 @@ class PromiseNativeHandler : public nsISupports { JS::Handle aValue) = 0; }; -// This class is used to set C++ callbacks once a dom Promise a resolved or -// rejected. -class DomPromiseListener final : public PromiseNativeHandler { - NS_DECL_ISUPPORTS - - public: - using CallbackType = std::function)>; - - explicit DomPromiseListener(Promise* aDOMPromise); - DomPromiseListener(Promise* aDOMPromise, CallbackType&& aResolve, - CallbackType&& aReject); - void SetResolvers(CallbackType&& aResolve, CallbackType&& aReject); - void ResolvedCallback(JSContext* aCx, JS::Handle aValue) override; - void RejectedCallback(JSContext* aCx, JS::Handle aValue) override; - - private: - ~DomPromiseListener() = default; - Maybe mResolve; - Maybe mReject; -}; - } // namespace dom } // namespace mozilla diff --git a/dom/promise/moz.build b/dom/promise/moz.build index 3758801f9890..a6cf61dfda73 100644 --- a/dom/promise/moz.build +++ b/dom/promise/moz.build @@ -18,7 +18,6 @@ EXPORTS.mozilla.dom += [ UNIFIED_SOURCES += [ 'Promise.cpp', 'PromiseDebugging.cpp', - 'PromiseNativeHandler.cpp', ] LOCAL_INCLUDES += [ diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 2c3f3dc50c5d..9631512ab98c 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -35,6 +35,7 @@ XPIDL_SOURCES += [ 'nsIClassifiedChannel.idl', 'nsIClassOfService.idl', 'nsIContentSniffer.idl', + 'nsICrossProcessSwitchChannel.idl', 'nsIDashboard.idl', 'nsIDashboardEventNotifier.idl', 'nsIDeprecationWarner.idl', @@ -75,7 +76,6 @@ XPIDL_SOURCES += [ 'nsIPermission.idl', 'nsIPermissionManager.idl', 'nsIPrivateBrowsingChannel.idl', - 'nsIProcessSwitchRequestor.idl', 'nsIProgressEventSink.idl', 'nsIPrompt.idl', 'nsIProtocolHandler.idl', diff --git a/netwerk/base/nsICrossProcessSwitchChannel.idl b/netwerk/base/nsICrossProcessSwitchChannel.idl new file mode 100644 index 000000000000..9fac14b22348 --- /dev/null +++ b/netwerk/base/nsICrossProcessSwitchChannel.idl @@ -0,0 +1,39 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * 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 "nsISupports.idl" +#include "nsIAsyncVerifyRedirectCallback.idl" +#include "nsIHttpChannel.idl" + +[scriptable, uuid(d2471b64-0292-4cb5-b801-914e34b31af0)] +interface nsICrossProcessSwitchChannel : nsISupports +{ + /** + * When a process switch has been processed (either successfully or not), + * HttpChannelParent will call this method on the DocumentChannelParent or + * HttpChannelParent. + * + * @param callback + * Object to inform about the async result of this method. + * @param status + * The error that caused the process switch to fail. + * NS_OK means the process switch was processed successfully. + */ + void finishCrossProcessSwitch(in nsIAsyncVerifyRedirectCallback callback, in nsresult status); + + /** + * When a process switch is about to start, + * nsHttpChannel will call this method on DocumentChannelParent or + * HttpChannelParent + * + * @param channel + * nsIHttpChannel caller. + * @param identifier + * identifier from SessionStore to be passed to the childChannel in + * order to identify it. + */ + void triggerCrossProcessSwitch(in nsIHttpChannel channel, in uint64_t identifier); +}; diff --git a/netwerk/base/nsIProcessSwitchRequestor.idl b/netwerk/base/nsIProcessSwitchRequestor.idl deleted file mode 100644 index 7e1973b07c71..000000000000 --- a/netwerk/base/nsIProcessSwitchRequestor.idl +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * 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 "nsIChannel.idl" - -/** - * The nsIProcessSwitchRequestor interface allows clients to instruct - * SessionStore.jsm that a channel setup has completed and a process switch - * may be required. This works alongside the on-may-change-process observer - * notification. - * This interface must be used only from the XPCOM main thread. - */ -[scriptable, uuid(fce8497b-c57c-4557-b360-3efefc83eff5)] -interface nsIProcessSwitchRequestor : nsISupports -{ - /** - * The underlying channel object that was intercepted and that could trigger - * a process. - */ - readonly attribute nsIChannel channel; - - /** - * Instructs the callee to be loaded in a new process. Like 'redirectTo' - * this can only be used on channels that have not yet called their - * listener's OnStartRequest(). Can only be called during the - * http-on-may-change-process observer notification. - * - * @param aTabPromise a promise which resolves to a nsIRemotTab object - * which the load will proceed in. - * @param aIdentifier a 64-bit ID which will be provided to the - * ChildProcessChannelListener. - */ - [must_use] void switchProcessTo(in Promise aTabPromise, - in unsigned long long aIdentifier); - - /** - * Used to determine if there is a Cross-Origin-Opener-Policy mismatch - * that would require switching the channel to another process. - * @throws NS_ERROR_NOT_AVAILABLE if we don't have a responseHead - */ - [must_use] boolean hasCrossOriginOpenerPolicyMismatch(); -}; diff --git a/netwerk/ipc/DocumentChannelParent.cpp b/netwerk/ipc/DocumentChannelParent.cpp index b6b7092a4b69..0ba2cfe6e02b 100644 --- a/netwerk/ipc/DocumentChannelParent.cpp +++ b/netwerk/ipc/DocumentChannelParent.cpp @@ -8,7 +8,6 @@ #include "DocumentChannelParent.h" #include "mozilla/DebugOnly.h" #include "mozilla/LoadInfo.h" -#include "mozilla/MozPromiseInlines.h" // For MozPromise::FromDomPromise #include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/ClientChannelHelper.h" #include "mozilla/dom/ContentParent.h" @@ -39,8 +38,8 @@ NS_INTERFACE_MAP_BEGIN(DocumentChannelParent) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIParentChannel) NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback) + NS_INTERFACE_MAP_ENTRY(nsICrossProcessSwitchChannel) NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) - NS_INTERFACE_MAP_ENTRY(nsIProcessSwitchRequestor) NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannelParent) NS_INTERFACE_MAP_END @@ -240,7 +239,7 @@ void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) { newChannel->Cancel(NS_BINDING_ABORTED); } } - // Release all previously registered channels, they are no longer needed to be + // Release all previously registered channels, they are no longer need to be // kept in the registrar from this moment. registrar->DeregisterChannels(mRedirectChannelId); @@ -358,21 +357,46 @@ void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) { } } -void DocumentChannelParent::TriggerCrossProcessSwitch() { - MOZ_ASSERT(mRedirectContentProcessIdPromise); +NS_IMETHODIMP +DocumentChannelParent::FinishCrossProcessSwitch( + nsIAsyncVerifyRedirectCallback* aCallback, nsresult aStatusCode) { + // We only manually Suspend mChannel when we initiate the redirect + // from OnStartRequest, which is currently only in the same-process + // case. + MOZ_ASSERT(!mSuspendedChannel); + + if (NS_SUCCEEDED(aStatusCode)) { + // This updates ParentChannelListener to point to this parent and at + // the same time cancels the old channel. + FinishReplacementChannelSetup(true); + } + + aCallback->OnRedirectVerifyCallback(aStatusCode); + return NS_OK; +} + +nsresult DocumentChannelParent::TriggerCrossProcessSwitch( + nsIHttpChannel* aChannel, uint64_t aIdentifier) { CancelChildForProcessSwitch(); - RefPtr self = this; - mRedirectContentProcessIdPromise->Then( + + RefPtr httpChannel = do_QueryObject(aChannel); + MOZ_DIAGNOSTIC_ASSERT(httpChannel, + "Must be called with nsHttpChannel object"); + RefPtr p = + httpChannel->TakeRedirectContentProcessIdPromise(); + + p->Then( GetMainThreadSerialEventTarget(), __func__, - [self, this](uint64_t aCpId) { - MOZ_ASSERT(mChannel, "Something went wrong, channel got cancelled"); - TriggerRedirectToRealChannel(mChannel, Some(aCpId), - mCrossProcessRedirectIdentifier); + [self = RefPtr(this), + channel = nsCOMPtr(aChannel), aIdentifier](uint64_t aCpId) { + self->TriggerRedirectToRealChannel(channel, Some(aCpId), aIdentifier); }, - [self](nsresult aStatusCode) { + [httpChannel](nsresult aStatusCode) { MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error"); - self->RedirectToRealChannelFinished(aStatusCode); + httpChannel->OnRedirectVerifyCallback(aStatusCode); }); + + return NS_OK; } void DocumentChannelParent::TriggerRedirectToRealChannel( @@ -515,7 +539,6 @@ void DocumentChannelParent::TriggerRedirectToRealChannel( contentDispositionFilename = Some(contentDispositionFilenameTemp); } - RefPtr self = this; if (aDestinationProcess) { dom::ContentParent* cp = dom::ContentProcessManager::GetSingleton()->GetContentProcessById( @@ -526,36 +549,24 @@ void DocumentChannelParent::TriggerRedirectToRealChannel( MOZ_ASSERT(config); - cp->SendCrossProcessRedirect(mRedirectChannelId, uri, *config, loadInfoArgs, - channelId, originalURI, aIdentifier, - redirectMode) - ->Then( - GetCurrentThreadSerialEventTarget(), __func__, - [self](Tuple>&& aResponse) { - if (NS_SUCCEEDED(Get<0>(aResponse))) { - nsCOMPtr newLoadInfo; - MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo( - Get<1>(aResponse), getter_AddRefs(newLoadInfo))); - - if (newLoadInfo) { - self->mChannel->SetLoadInfo(newLoadInfo); - } - } - self->RedirectToRealChannelFinished(Get<0>(aResponse)); - }, - [self](const mozilla::ipc::ResponseRejectReason) { - self->RedirectToRealChannelFinished(NS_ERROR_FAILURE); - }); + auto result = cp->SendCrossProcessRedirect( + mRedirectChannelId, uri, *config, loadInfoArgs, channelId, originalURI, + aIdentifier, redirectMode); + MOZ_ASSERT(result, "SendCrossProcessRedirect failed"); + Unused << result; } else { + RefPtr channel = this; SendRedirectToRealChannel(mRedirectChannelId, uri, newLoadFlags, config, loadInfoArgs, mRedirects, channelId, originalURI, redirectMode, redirectFlags, contentDisposition, contentDispositionFilename) ->Then( GetCurrentThreadSerialEventTarget(), __func__, - [self](nsresult aRv) { self->RedirectToRealChannelFinished(aRv); }, - [self](const mozilla::ipc::ResponseRejectReason) { - self->RedirectToRealChannelFinished(NS_ERROR_FAILURE); + [channel](nsresult aRv) { + channel->RedirectToRealChannelFinished(aRv); + }, + [channel](const mozilla::ipc::ResponseRejectReason) { + channel->RedirectToRealChannelFinished(NS_ERROR_FAILURE); }); } } @@ -595,16 +606,6 @@ DocumentChannelParent::OnStartRequest(nsIRequest* aRequest) { if (channel) { Unused << channel->GetApplyConversion(&mOldApplyConversion); channel->SetApplyConversion(false); - - // notify "http-on-may-change-process" observers which is typically - // SessionStore.jsm. This will determine if a new process needs to be - // spawned and if so SwitchProcessTo() will be called which will set a - // ContentProcessIdPromise. - gHttpHandler->OnMayChangeProcess(this); - if (mRedirectContentProcessIdPromise) { - TriggerCrossProcessSwitch(); - return NS_OK; - } } TriggerRedirectToRealChannel(mChannel); @@ -828,47 +829,5 @@ DocumentChannelParent::AsyncOnChannelRedirect( return NS_OK; } -//----------------------------------------------------------------------------- -// DocumentChannelParent::nsIProcessSwitchRequestor -//----------------------------------------------------------------------------- - -NS_IMETHODIMP DocumentChannelParent::GetChannel(nsIChannel** aChannel) { - MOZ_ASSERT(mChannel); - nsCOMPtr channel(mChannel); - channel.forget(aChannel); - return NS_OK; -} - -NS_IMETHODIMP DocumentChannelParent::SwitchProcessTo( - dom::Promise* aContentProcessIdPromise, uint64_t aIdentifier) { - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_ARG(aContentProcessIdPromise); - - mRedirectContentProcessIdPromise = - ContentProcessIdPromise::FromDomPromise(aContentProcessIdPromise); - mCrossProcessRedirectIdentifier = aIdentifier; - return NS_OK; -} - -// This method returns the cached result of running the Cross-Origin-Opener -// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch -NS_IMETHODIMP -DocumentChannelParent::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) { - MOZ_ASSERT(aMismatch); - - if (!aMismatch) { - return NS_ERROR_INVALID_ARG; - } - - nsCOMPtr channel = do_QueryInterface(mChannel); - if (!channel) { - // Not an nsHttpChannel assume it's okay to switch. - *aMismatch = false; - return NS_OK; - } - - return channel->HasCrossOriginOpenerPolicyMismatch(aMismatch); -} - } // namespace net } // namespace mozilla diff --git a/netwerk/ipc/DocumentChannelParent.h b/netwerk/ipc/DocumentChannelParent.h index cf7844a0afbf..00094f7a8b49 100644 --- a/netwerk/ipc/DocumentChannelParent.h +++ b/netwerk/ipc/DocumentChannelParent.h @@ -7,17 +7,16 @@ #ifndef mozilla_net_DocumentChannelParent_h #define mozilla_net_DocumentChannelParent_h -#include "mozilla/MozPromise.h" #include "mozilla/Variant.h" #include "mozilla/net/NeckoCommon.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/net/PDocumentChannelParent.h" #include "mozilla/net/ParentChannelListener.h" +#include "nsICrossProcessSwitchChannel.h" #include "nsIInterfaceRequestor.h" #include "nsIObserver.h" #include "nsIParentChannel.h" #include "nsIParentRedirectingChannel.h" -#include "nsIProcessSwitchRequestor.h" #include "nsIRedirectResultListener.h" #define DOCUMENT_CHANNEL_PARENT_IID \ @@ -37,8 +36,8 @@ class DocumentChannelParent : public nsIInterfaceRequestor, public nsIAsyncVerifyRedirectReadyCallback, public nsIParentChannel, public nsIChannelEventSink, - public HttpChannelSecurityWarningReporter, - public nsIProcessSwitchRequestor { + public nsICrossProcessSwitchChannel, + public HttpChannelSecurityWarningReporter { public: explicit DocumentChannelParent(const dom::PBrowserOrId& iframeEmbedding, nsILoadContext* aLoadContext, @@ -53,7 +52,7 @@ class DocumentChannelParent : public nsIInterfaceRequestor, NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK NS_DECL_NSICHANNELEVENTSINK - NS_DECL_NSIPROCESSSWITCHREQUESTOR + NS_DECL_NSICROSSPROCESSSWITCHCHANNEL NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_CHANNEL_PARENT_IID) @@ -111,8 +110,6 @@ class DocumentChannelParent : public nsIInterfaceRequestor, void FinishReplacementChannelSetup(bool aSucceeded); - void TriggerCrossProcessSwitch(); - // This defines a variant that describes all the attribute setters (and their // parameters) from nsIParentChannel // @@ -212,18 +209,6 @@ class DocumentChannelParent : public nsIInterfaceRequestor, // helper from being installed, but we need to restore the value // later. bool mOldApplyConversion = false; - - typedef MozPromise - ContentProcessIdPromise; - // This promise is set following a on-may-change-process observer - // notification when the associated channel is getting relocated to another - // process. It will be resolved when that process is set up. - RefPtr mRedirectContentProcessIdPromise; - // This identifier is set at the same time as the - // mRedirectContentProcessIdPromise. - // This identifier is later passed to the childChannel in order to identify it - // once the promise is resolved. - uint64_t mCrossProcessRedirectIdentifier = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(DocumentChannelParent, diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 3f3f2c3737a8..26a2855eb667 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1926,6 +1926,17 @@ HttpBaseChannel::RedirectTo(nsIURI* targetURI) { return NS_OK; } +NS_IMETHODIMP +HttpBaseChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent, + uint64_t aIdentifier) { + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) { + return NS_ERROR_NOT_AVAILABLE; +} + NS_IMETHODIMP HttpBaseChannel::UpgradeToSecure() { // Upgrades are handled internally between http-on-modify-request and diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index b7061e22ea36..fba1b72b5e8c 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -219,6 +219,9 @@ class HttpBaseChannel : public nsHashPropertyBag, NS_IMETHOD GetResponseStatusText(nsACString& aValue) override; NS_IMETHOD GetRequestSucceeded(bool* aValue) override; NS_IMETHOD RedirectTo(nsIURI* newURI) override; + NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise* aBrowserParent, + uint64_t aIdentifier) override; + NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) override; NS_IMETHOD UpgradeToSecure() override; NS_IMETHOD GetRequestContextID(uint64_t* aRCID) override; NS_IMETHOD GetTransferSize(uint64_t* aTransferSize) override; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 23e02cffab6d..99143599568e 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -4076,5 +4076,26 @@ void HttpChannelChild::MaybeCallSynthesizedCallback() { mSynthesizedCallback = nullptr; } +nsresult HttpChannelChild::CrossProcessRedirectFinished(nsresult aStatus) { + if (!CanSend()) { + return NS_BINDING_FAILED; + } + + if (!mCanceled && NS_SUCCEEDED(mStatus)) { + mStatus = aStatus; + } + + // The loadInfo is updated in nsDocShell::OpenInitializedChannel to have the + // correct attributes (such as browsingContextID). + // We need to send it to the parent channel so the two match, which is done + nsCOMPtr loadInfo; + MOZ_ALWAYS_SUCCEEDS(GetLoadInfo(getter_AddRefs(loadInfo))); + Maybe loadInfoArgs; + MOZ_ALWAYS_SUCCEEDS( + mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs)); + Unused << SendCrossProcessRedirectDone(mStatus, loadInfoArgs); + return NS_OK; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 5e4bfa898472..0beb9cf63587 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -125,6 +125,8 @@ class HttpChannelChild final : public PHttpChannelChild, // Callback while background channel is destroyed. void OnBackgroundChildDestroyed(HttpBackgroundChannelChild* aBgChild); + nsresult CrossProcessRedirectFinished(nsresult aStatus); + protected: mozilla::ipc::IPCResult RecvOnStartRequest( const nsresult& channelStatus, const nsHttpResponseHead& responseHead, diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 02fb9aa1725a..6717cfa13903 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -289,6 +289,7 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelParent) NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback) NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener) + NS_INTERFACE_MAP_ENTRY(nsICrossProcessSwitchChannel) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel) NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelParent) NS_INTERFACE_MAP_END @@ -1256,32 +1257,44 @@ void HttpChannelParent::MaybeFlushPendingDiversion() { } } -void HttpChannelParent::FinishCrossProcessSwitch(nsHttpChannel* aChannel, - nsresult aStatus) { +static void FinishCrossProcessSwitchHelper(nsHttpChannel* aChannel, + nsresult aStatus) { + nsCOMPtr switchListener; + NS_QueryNotificationCallbacks(aChannel, switchListener); + MOZ_ASSERT(switchListener); + + switchListener->FinishCrossProcessSwitch(aChannel, aStatus); +} + +NS_IMETHODIMP +HttpChannelParent::FinishCrossProcessSwitch( + nsIAsyncVerifyRedirectCallback* aCallback, nsresult aStatus) { if (NS_SUCCEEDED(aStatus)) { // This updates ParentChannelListener to point to this parent and at // the same time cancels the old channel. OnRedirectResult(true); } - aChannel->OnRedirectVerifyCallback(aStatus); + aCallback->OnRedirectVerifyCallback(aStatus); + return NS_OK; } -void HttpChannelParent::CrossProcessRedirectDone( +mozilla::ipc::IPCResult HttpChannelParent::RecvCrossProcessRedirectDone( const nsresult& aResult, const mozilla::Maybe& aLoadInfoArgs) { RefPtr chan = do_QueryObject(mChannel); nsresult rv = aResult; - auto sendReply = MakeScopeExit([&]() { FinishCrossProcessSwitch(chan, rv); }); + auto sendReply = + MakeScopeExit([&]() { FinishCrossProcessSwitchHelper(chan, rv); }); if (NS_FAILED(rv)) { - return; + return IPC_OK(); } nsCOMPtr newLoadInfo; rv = LoadInfoArgsToLoadInfo(aLoadInfoArgs, getter_AddRefs(newLoadInfo)); if (NS_FAILED(rv)) { - return; + return IPC_OK(); } if (newLoadInfo) { @@ -1294,13 +1307,15 @@ void HttpChannelParent::CrossProcessRedirectDone( WaitForBgParent()->Then( GetMainThreadSerialEventTarget(), __func__, [self, chan, aResult]() { - self->FinishCrossProcessSwitch(chan, aResult); + FinishCrossProcessSwitchHelper(chan, aResult); }, [self, chan](const nsresult& aRejectionRv) { MOZ_ASSERT(NS_FAILED(aRejectionRv), "This should be an error code"); - self->FinishCrossProcessSwitch(chan, aRejectionRv); + FinishCrossProcessSwitchHelper(chan, aRejectionRv); }); } + + return IPC_OK(); } void HttpChannelParent::ResponseSynthesized() { @@ -2664,7 +2679,7 @@ nsresult HttpChannelParent::TriggerCrossProcessSwitch(nsIHttpChannel* aChannel, RedirectChannelRegistrar::GetOrCreate(); MOZ_ASSERT(registrar); rv = registrar->RegisterChannel(channel, &self->mRedirectChannelId); - NS_ENSURE_SUCCESS_VOID(rv); + NS_ENSURE_SUCCESS(rv, rv); LOG(("Registered %p channel under id=%d", channel.get(), self->mRedirectChannelId)); @@ -2695,20 +2710,15 @@ nsresult HttpChannelParent::TriggerCrossProcessSwitch(nsIHttpChannel* aChannel, dom::ContentProcessManager::GetSingleton()->GetContentProcessById( ContentParentId{cpId}); if (!cp) { - return; + return NS_ERROR_UNEXPECTED; } - cp->SendCrossProcessRedirect(self->mRedirectChannelId, uri, config, - loadInfoArgs, channelId, originalURI, - aIdentifier, redirectMode) - ->Then( - GetCurrentThreadSerialEventTarget(), __func__, - [self](Tuple>&& aResponse) { - self->CrossProcessRedirectDone(Get<0>(aResponse), - Get<1>(aResponse)); - }, - [self](const mozilla::ipc::ResponseRejectReason) { - self->CrossProcessRedirectDone(NS_ERROR_FAILURE, Nothing()); - }); + auto result = cp->SendCrossProcessRedirect( + self->mRedirectChannelId, uri, config, loadInfoArgs, channelId, + originalURI, aIdentifier, redirectMode); + + MOZ_ASSERT(result, "SendCrossProcessRedirect failed"); + + return result ? NS_OK : NS_ERROR_UNEXPECTED; }, [httpChannel](nsresult aStatus) { MOZ_ASSERT(NS_FAILED(aStatus), "Status should be error"); diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index 926ed07e4f84..4a67f59e5b71 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -14,6 +14,7 @@ #include "mozilla/net/NeckoCommon.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/MozPromise.h" +#include "nsICrossProcessSwitchChannel.h" #include "nsIObserver.h" #include "nsIParentRedirectingChannel.h" #include "nsIProgressEventSink.h" @@ -60,7 +61,8 @@ class HttpChannelParent final : public nsIInterfaceRequestor, public HttpChannelSecurityWarningReporter, public nsIAsyncVerifyRedirectReadyCallback, public nsIChannelEventSink, - public nsIRedirectResultListener { + public nsIRedirectResultListener, + public nsICrossProcessSwitchChannel { virtual ~HttpChannelParent(); public: @@ -76,6 +78,7 @@ class HttpChannelParent final : public nsIInterfaceRequestor, NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIREDIRECTRESULTLISTENER + NS_DECL_NSICROSSPROCESSSWITCHCHANNEL NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID) @@ -129,13 +132,6 @@ class HttpChannelParent final : public nsIInterfaceRequestor, base::ProcessId OtherPid() const; - // Called by nsHttpChannel when a process switch is about to start. - // aChannel: nsIHttpChannel caller. - // aIdentifier: identifier from SessionStore to be passed to the childChannel - // in order to identify it. - nsresult TriggerCrossProcessSwitch(nsIHttpChannel* aChannel, - uint64_t aIdentifier); - // Calling this method will cancel the HttpChannelChild because the consumer // needs to be relocated to another process. // Any OnStart/Stop/DataAvailable calls that follow will not be sent to the @@ -213,6 +209,9 @@ class HttpChannelParent final : public nsIInterfaceRequestor, virtual mozilla::ipc::IPCResult RecvDivertOnStopRequest( const nsresult& statusCode) override; virtual mozilla::ipc::IPCResult RecvDivertComplete() override; + virtual mozilla::ipc::IPCResult RecvCrossProcessRedirectDone( + const nsresult& aResult, + const mozilla::Maybe& aLoadInfoArgs) override; virtual mozilla::ipc::IPCResult RecvRemoveCorsPreflightCacheEntry( const URIParams& uri, const mozilla::ipc::PrincipalInfo& requestingPrincipal) override; @@ -256,11 +255,6 @@ class HttpChannelParent final : public nsIInterfaceRequestor, void MaybeFlushPendingDiversion(); void ResponseSynthesized(); - void CrossProcessRedirectDone( - const nsresult& aResult, - const mozilla::Maybe& aLoadInfoArgs); - void FinishCrossProcessSwitch(nsHttpChannel* aChannel, nsresult aStatus); - // final step for Redirect2Verify procedure, will be invoked while both // redirecting and redirected channel are ready or any error happened. // OnRedirectVerifyCallback will be invoked for finishing the async diff --git a/netwerk/protocol/http/NullHttpChannel.cpp b/netwerk/protocol/http/NullHttpChannel.cpp index 1127659c45d1..1e53dfcd4b06 100644 --- a/netwerk/protocol/http/NullHttpChannel.cpp +++ b/netwerk/protocol/http/NullHttpChannel.cpp @@ -273,6 +273,17 @@ NullHttpChannel::RedirectTo(nsIURI* aNewURI) { return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +NullHttpChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent, + uint64_t aIdentifier) { + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +NullHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) { + return NS_ERROR_NOT_AVAILABLE; +} + NS_IMETHODIMP NullHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index d48eb5c490dc..3ecc1daf7822 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -50,6 +50,12 @@ parent: CorsPreflightArgs? corsPreflightArgs, bool chooseAppcache); + // Sent to the parent in order signal that the child side listeners have been + // set up and the parent side of the channel can be opened. + // The passed loadInfo needs to be set on the channel - since the channel + // moved to a new process it now has different properties. + async CrossProcessRedirectDone(nsresult result, LoadInfoArgs? loadInfo); + // For document loads we keep this protocol open after child's // OnStopRequest, and send this msg (instead of __delete__) to allow // partial cleanup on parent. diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build index 64e21578775b..37c54377e159 100644 --- a/netwerk/protocol/http/moz.build +++ b/netwerk/protocol/http/moz.build @@ -132,7 +132,6 @@ LOCAL_INCLUDES += [ '/extensions/auth', '/netwerk/base', '/netwerk/cookie', - '/netwerk/ipc', '/netwerk/url-classifier', ] diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 761389194b55..c9f812aef651 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -9,11 +9,9 @@ #include -#include "DocumentChannelParent.h" -#include "mozilla/MozPromiseInlines.h" // For MozPromise::FromDomPromise +#include "mozilla/dom/nsCSPContext.h" #include "mozilla/ScopeExit.h" #include "mozilla/Sprintf.h" -#include "mozilla/dom/nsCSPContext.h" #include "nsHttp.h" #include "nsHttpChannel.h" @@ -125,6 +123,7 @@ #include "../../cache2/CacheFileUtils.h" #include "../../cache2/CacheHashUtils.h" #include "nsINetworkLinkService.h" +#include "mozilla/dom/PromiseNativeHandler.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ServiceWorkerUtils.h" #include "mozilla/net/AsyncUrlChannelClassifier.h" @@ -2580,26 +2579,18 @@ nsresult nsHttpChannel::ContinueProcessResponse1() { return NS_OK; } - nsCOMPtr parentChannel; - NS_QueryNotificationCallbacks(this, parentChannel); + // notify "http-on-may-change-process" observers + gHttpHandler->OnMayChangeProcess(this); - RefPtr documentChannelParent = - do_QueryObject(parentChannel); + if (mRedirectContentProcessIdPromise) { + MOZ_ASSERT(!mOnStartRequestCalled); - if (!documentChannelParent) { - // notify "http-on-may-change-process" observers - gHttpHandler->OnMayChangeProcess(this); - - if (mRedirectContentProcessIdPromise) { - MOZ_ASSERT(!mOnStartRequestCalled); - - PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2); - rv = StartCrossProcessRedirect(); - if (NS_SUCCEEDED(rv)) { - return NS_OK; - } - PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2); + PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2); + rv = StartCrossProcessRedirect(); + if (NS_SUCCEEDED(rv)) { + return NS_OK; } + PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2); } } @@ -6139,7 +6130,6 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel) NS_INTERFACE_MAP_ENTRY(nsITimerCallback) NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener) NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback) - NS_INTERFACE_MAP_ENTRY(nsIProcessSwitchRequestor) NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpChannel) NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) @@ -7242,13 +7232,49 @@ nsHttpChannel::GetRequestMethod(nsACString& aMethod) { } //----------------------------------------------------------------------------- -// nsHttpChannel::nsIProcessSwitchRequestor +// nsHttpChannel::nsIRequestObserver //----------------------------------------------------------------------------- -NS_IMETHODIMP nsHttpChannel::GetChannel(nsIChannel** aChannel) { - *aChannel = do_AddRef(this).take(); - return NS_OK; -} +// This class is used to convert from a DOM promise to a MozPromise. +class DomPromiseListener final : dom::PromiseNativeHandler { + NS_DECL_ISUPPORTS + + static RefPtr Create( + dom::Promise* aDOMPromise) { + MOZ_ASSERT(aDOMPromise); + RefPtr handler = new DomPromiseListener(); + RefPtr promise = + handler->mPromiseHolder.Ensure(__func__); + aDOMPromise->AppendNativeHandler(handler); + return promise; + } + + virtual void ResolvedCallback(JSContext* aCx, + JS::Handle aValue) override { + uint64_t cpId; + if (!JS::ToUint64(aCx, aValue, &cpId)) { + mPromiseHolder.Reject(NS_ERROR_FAILURE, __func__); + return; + } + mPromiseHolder.Resolve(cpId, __func__); + } + + virtual void RejectedCallback(JSContext* aCx, + JS::Handle aValue) override { + if (!aValue.isInt32()) { + mPromiseHolder.Reject(NS_ERROR_DOM_NOT_NUMBER_ERR, __func__); + return; + } + mPromiseHolder.Reject((nsresult)aValue.toInt32(), __func__); + } + + private: + DomPromiseListener() = default; + ~DomPromiseListener() = default; + MozPromiseHolder mPromiseHolder; +}; + +NS_IMPL_ISUPPORTS0(DomPromiseListener) NS_IMETHODIMP nsHttpChannel::SwitchProcessTo( dom::Promise* aContentProcessIdPromise, uint64_t aIdentifier) { @@ -7258,36 +7284,15 @@ NS_IMETHODIMP nsHttpChannel::SwitchProcessTo( LOG(("nsHttpChannel::SwitchProcessTo [this=%p]", this)); LogCallingScriptLocation(this); - nsCOMPtr parentChannel; - NS_QueryNotificationCallbacks(this, parentChannel); - RefPtr documentChannelParent = - do_QueryObject(parentChannel); - // This is a temporary change as the DocumentChannelParent currently must go - // through the nsHttpChannel to perform a process switch via SessionStore. - if (!documentChannelParent) { - // We cannot do this after OnStartRequest of the listener has been called. - NS_ENSURE_FALSE(mOnStartRequestCalled, NS_ERROR_NOT_AVAILABLE); - } + // We cannot do this after OnStartRequest of the listener has been called. + NS_ENSURE_FALSE(mOnStartRequestCalled, NS_ERROR_NOT_AVAILABLE); mRedirectContentProcessIdPromise = - ContentProcessIdPromise::FromDomPromise(aContentProcessIdPromise); + DomPromiseListener::Create(aContentProcessIdPromise); mCrossProcessRedirectIdentifier = aIdentifier; return NS_OK; } -// This method returns the cached result of running the Cross-Origin-Opener -// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch -NS_IMETHODIMP -nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) { - MOZ_ASSERT(aMismatch); - if (!aMismatch) { - return NS_ERROR_INVALID_ARG; - } - - *aMismatch = mHasCrossOriginOpenerPolicyMismatch; - return NS_OK; -} - nsresult nsHttpChannel::StartCrossProcessRedirect() { nsresult rv; @@ -7298,7 +7303,8 @@ nsresult nsHttpChannel::StartCrossProcessRedirect() { nsCOMPtr parentChannel; NS_QueryNotificationCallbacks(this, parentChannel); - RefPtr httpParent = do_QueryObject(parentChannel); + nsCOMPtr httpParent = + do_QueryInterface(parentChannel); MOZ_ASSERT(httpParent); NS_ENSURE_TRUE(httpParent, NS_ERROR_UNEXPECTED); @@ -7350,6 +7356,19 @@ static bool CompareCrossOriginOpenerPolicies( return false; } +// This method returns the cached result of running the Cross-Origin-Opener +// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch +NS_IMETHODIMP +nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) { + MOZ_ASSERT(aMismatch); + if (!aMismatch) { + return NS_ERROR_INVALID_ARG; + } + + *aMismatch = mHasCrossOriginOpenerPolicyMismatch; + return NS_OK; +} + // This runs steps 1-5 of the algorithm when navigating a top level document. // See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e nsresult nsHttpChannel::ComputeCrossOriginOpenerPolicyMismatch() { @@ -7572,10 +7591,6 @@ nsresult nsHttpChannel::ProcessCrossOriginResourcePolicyHeader() { return NS_OK; } -//----------------------------------------------------------------------------- -// nsHttpChannel::nsIRequestObserver -//----------------------------------------------------------------------------- - NS_IMETHODIMP nsHttpChannel::OnStartRequest(nsIRequest* request) { nsresult rv; @@ -7716,21 +7731,15 @@ nsHttpChannel::OnStartRequest(nsIRequest* request) { return NS_OK; } - nsCOMPtr parentChannel; - NS_QueryNotificationCallbacks(this, parentChannel); - RefPtr documentChannelParent = - do_QueryObject(parentChannel); - if (!documentChannelParent) { - gHttpHandler->OnMayChangeProcess(this); + gHttpHandler->OnMayChangeProcess(this); - if (mRedirectContentProcessIdPromise) { - PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1); - rv = StartCrossProcessRedirect(); - if (NS_SUCCEEDED(rv)) { - return NS_OK; - } - PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1); + if (mRedirectContentProcessIdPromise) { + PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1); + rv = StartCrossProcessRedirect(); + if (NS_SUCCEEDED(rv)) { + return NS_OK; } + PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1); } } diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 948bdc1d5a0d..bf78184df48b 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -33,7 +33,6 @@ #include "nsIRaceCacheWithNetwork.h" #include "mozilla/extensions/PStreamFilterParent.h" #include "mozilla/Mutex.h" -#include "nsIProcessSwitchRequestor.h" #include "nsIRemoteTab.h" class nsDNSPrefetch; @@ -79,8 +78,7 @@ class nsHttpChannel final : public HttpBaseChannel, public nsIChannelWithDivertableParentListener, public nsIRaceCacheWithNetwork, public nsIRequestTailUnblockCallback, - public nsITimerCallback, - public nsIProcessSwitchRequestor { + public nsITimerCallback { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIREQUESTOBSERVER @@ -102,7 +100,6 @@ class nsHttpChannel final : public HttpBaseChannel, NS_DECL_NSIRACECACHEWITHNETWORK NS_DECL_NSITIMERCALLBACK NS_DECL_NSIREQUESTTAILUNBLOCKCALLBACK - NS_DECL_NSIPROCESSSWITCHREQUESTOR // nsIHttpAuthenticableChannel. We can't use // NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and @@ -153,6 +150,9 @@ class nsHttpChannel final : public HttpBaseChannel, NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override; // nsIHttpChannel NS_IMETHOD GetEncodedBodySize(uint64_t* aEncodedBodySize) override; + NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise* aBrowserParent, + uint64_t aIdentifier) override; + NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) override; // nsIHttpChannelInternal NS_IMETHOD SetupFallbackChannel(const char* aFallbackKey) override; NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override; @@ -276,8 +276,7 @@ class nsHttpChannel final : public HttpBaseChannel, } TransactionObserver* GetTransactionObserver() { return mTransactionObserver; } - typedef MozPromise - ContentProcessIdPromise; + typedef MozPromise ContentProcessIdPromise; already_AddRefed TakeRedirectContentProcessIdPromise() { return mRedirectContentProcessIdPromise.forget(); diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index b813aea0931d..6b7f0d532da5 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -23,7 +23,6 @@ #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIPrefLocalizedString.h" -#include "nsIProcessSwitchRequestor.h" #include "nsSocketProviderService.h" #include "nsISocketProvider.h" #include "nsPrintfCString.h" @@ -807,14 +806,6 @@ void nsHttpHandler::NotifyObservers(nsIChannel* chan, const char* event) { if (obsService) obsService->NotifyObservers(chan, event, nullptr); } -void nsHttpHandler::NotifyObservers(nsIProcessSwitchRequestor* request, - const char* event) { - LOG(("nsHttpHandler::NotifyObservers [request=%p event=\"%s\"]\n", request, - event)); - nsCOMPtr obsService = services::GetObserverService(); - if (obsService) obsService->NotifyObservers(request, event, nullptr); -} - nsresult nsHttpHandler::AsyncOnChannelRedirect( nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags, nsIEventTarget* mainThreadEventTarget) { diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 06f27f473bbc..3b2a229467bb 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -29,7 +29,6 @@ class nsIHttpChannel; class nsIPrefBranch; class nsICancelable; class nsICookieService; -class nsIProcessSwitchRequestor; class nsIIOService; class nsIRequestContextService; class nsISiteSecurityService; @@ -408,8 +407,8 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, NotifyObservers(chan, NS_HTTP_ON_EXAMINE_CACHED_RESPONSE_TOPIC); } - void OnMayChangeProcess(nsIProcessSwitchRequestor* request) { - NotifyObservers(request, NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC); + void OnMayChangeProcess(nsIHttpChannel* chan) { + NotifyObservers(chan, NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC); } // Generates the host:port string for use in the Host: header as well as the @@ -480,7 +479,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, MOZ_MUST_USE nsresult InitConnectionMgr(); void NotifyObservers(nsIChannel* chan, const char* event); - void NotifyObservers(nsIProcessSwitchRequestor* request, const char* event); void SetFastOpenOSSupport(); diff --git a/netwerk/protocol/http/nsIHttpChannel.idl b/netwerk/protocol/http/nsIHttpChannel.idl index 1f6f3359179c..01e39d995a35 100644 --- a/netwerk/protocol/http/nsIHttpChannel.idl +++ b/netwerk/protocol/http/nsIHttpChannel.idl @@ -415,6 +415,26 @@ interface nsIHttpChannel : nsIIdentChannel */ [must_use] void redirectTo(in nsIURI aTargetURI); + /** + * Instructs the channel to be loaded in a new process. Like 'redirectTo' + * this can only be used on channels that have not yet called their + * listener's OnStartRequest(). + * + * @param aTabPromise a promise which resolves to a nsIRemotTab object + * which the load will proceed in. + * @param aIdentifier a 64-bit ID which will be provided to the + * ChildProcessChannelListener. + */ + [must_use] void switchProcessTo(in Promise aTabPromise, + in unsigned long long aIdentifier); + + /** + * Used to determine if there is a Cross-Origin-Opener-Policy mismatch + * that would require switching the channel to another process. + * @throws NS_ERROR_NOT_AVAILABLE if we don't have a responseHead + */ + [must_use] boolean hasCrossOriginOpenerPolicyMismatch(); + /** * Flags a channel to be upgraded to HTTPS. * diff --git a/netwerk/protocol/http/nsIHttpProtocolHandler.idl b/netwerk/protocol/http/nsIHttpProtocolHandler.idl index abe113e98079..d8e022740583 100644 --- a/netwerk/protocol/http/nsIHttpProtocolHandler.idl +++ b/netwerk/protocol/http/nsIHttpProtocolHandler.idl @@ -175,9 +175,9 @@ interface nsIHttpProtocolHandler : nsIProxiedProtocolHandler /** * The observer of this topic is notified before before the response for a HTTP - * load is available. The "subject" of the notification is the - * nsIProcessSwitchRequestor instance. Observers may call "switchProcessTo" to - * perform a process switch while this is being run. + * load is available. The "subject" of the notification is the nsIHttpChannel + * instance. Observers may call "switchProcessTo" to perform a process switch + * while this is being run. */ #define NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC "http-on-may-change-process" diff --git a/netwerk/protocol/viewsource/moz.build b/netwerk/protocol/viewsource/moz.build index 6976fde4e23f..71a8d34717c0 100644 --- a/netwerk/protocol/viewsource/moz.build +++ b/netwerk/protocol/viewsource/moz.build @@ -22,8 +22,6 @@ EXPORTS += [ FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/netwerk/base', - # For nsHttpChannel.h - '/netwerk/protocol/http', ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp index c53c5744811a..7085395f3697 100644 --- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp @@ -5,18 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsViewSourceChannel.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/NullPrincipal.h" -#include "nsContentSecurityManager.h" -#include "nsContentUtils.h" -#include "nsHttpChannel.h" -#include "nsIHttpHeaderVisitor.h" #include "nsIIOService.h" -#include "nsIInputStreamChannel.h" -#include "nsIReferrerInfo.h" #include "nsMimeTypes.h" #include "nsNetUtil.h" +#include "nsContentUtils.h" +#include "nsIHttpHeaderVisitor.h" +#include "nsContentSecurityManager.h" #include "nsServiceManagerUtils.h" +#include "nsIInputStreamChannel.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/NullPrincipal.h" +#include "nsIReferrerInfo.h" NS_IMPL_ADDREF(nsViewSourceChannel) NS_IMPL_RELEASE(nsViewSourceChannel) @@ -943,6 +942,24 @@ nsViewSourceChannel::RedirectTo(nsIURI* uri) { return !mHttpChannel ? NS_ERROR_NULL_POINTER : mHttpChannel->RedirectTo(uri); } +NS_IMETHODIMP +nsViewSourceChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent, + uint64_t aIdentifier) { + return !mHttpChannel + ? NS_ERROR_NULL_POINTER + : mHttpChannel->SwitchProcessTo(aBrowserParent, aIdentifier); +} + +NS_IMETHODIMP +nsViewSourceChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) { + MOZ_ASSERT(aMismatch); + if (!aMismatch) { + return NS_ERROR_INVALID_ARG; + } + *aMismatch = false; + return NS_OK; +} + NS_IMETHODIMP nsViewSourceChannel::UpgradeToSecure() { return !mHttpChannel ? NS_ERROR_NULL_POINTER diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.h b/netwerk/protocol/viewsource/nsViewSourceChannel.h index 7290b6e893ab..365b380410e3 100644 --- a/netwerk/protocol/viewsource/nsViewSourceChannel.h +++ b/netwerk/protocol/viewsource/nsViewSourceChannel.h @@ -6,18 +6,18 @@ #ifndef nsViewSourceChannel_h___ #define nsViewSourceChannel_h___ -#include "mozilla/Attributes.h" -#include "mozilla/net/NeckoChannelParams.h" +#include "nsString.h" #include "nsCOMPtr.h" -#include "nsIApplicationCacheChannel.h" -#include "nsICachingChannel.h" -#include "nsIFormPOSTActionChannel.h" +#include "nsIViewSourceChannel.h" +#include "nsIURI.h" +#include "nsIStreamListener.h" #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" -#include "nsIStreamListener.h" -#include "nsIURI.h" -#include "nsIViewSourceChannel.h" -#include "nsString.h" +#include "nsICachingChannel.h" +#include "nsIApplicationCacheChannel.h" +#include "nsIFormPOSTActionChannel.h" +#include "mozilla/Attributes.h" +#include "mozilla/net/NeckoChannelParams.h" class nsViewSourceChannel final : public nsIViewSourceChannel, public nsIStreamListener, diff --git a/netwerk/test/browser/browser_cross_process_redirect.js b/netwerk/test/browser/browser_cross_process_redirect.js index 993d9314996d..e35ac470cc98 100644 --- a/netwerk/test/browser/browser_cross_process_redirect.js +++ b/netwerk/test/browser/browser_cross_process_redirect.js @@ -40,28 +40,26 @@ ProcessChooser.prototype = { Services.obs.removeObserver(this, "http-on-may-change-process"); }, - examine(aRequestor) { - const channel = aRequestor.channel; - - if (this.channel && this.channel != channel) { + examine(aChannel) { + if (this.channel && this.channel != aChannel) { // Hack: this is just so we don't get redirected multiple times. info("same channel. give null"); return; } - if (channel.URI.host != this.toDomain) { - info("wrong host for channel " + channel.URI.host); + if (aChannel.URI.host != this.toDomain) { + info("wrong host for channel " + aChannel.URI.host); return; } - let redirects = channel.loadInfo.redirectChain; + let redirects = aChannel.loadInfo.redirectChain; if (redirects[redirects.length - 1].principal.URI.host != this.fromDomain) { info("didn't find redirect"); return; } info("setting channel"); - this.channel = channel; + this.channel = aChannel; let self = this; info("unregistering"); @@ -81,13 +79,13 @@ ProcessChooser.prototype = { }); info("calling switchprocessto"); - aRequestor.switchProcessTo(tabPromise, identifier); + aChannel.switchProcessTo(tabPromise, identifier); }, observe(aSubject, aTopic, aData) { switch (aTopic) { case "http-on-may-change-process": - this.examine(aSubject.QueryInterface(Ci.nsIProcessSwitchRequestor)); + this.examine(aSubject.QueryInterface(Ci.nsIHttpChannel)); break; default: ok(false, "Unexpected topic observed!"); diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h index 8b9b2767937a..5233fab52105 100644 --- a/xpcom/threads/MozPromise.h +++ b/xpcom/threads/MozPromise.h @@ -9,8 +9,8 @@ # include "mozilla/Logging.h" # include "mozilla/Maybe.h" -# include "mozilla/Monitor.h" # include "mozilla/Mutex.h" +# include "mozilla/Monitor.h" # include "mozilla/RefPtr.h" # include "mozilla/Tuple.h" # include "mozilla/TypeTraits.h" @@ -38,10 +38,6 @@ namespace mozilla { -namespace dom { -class Promise; -} - extern LazyLogModule gMozPromiseLog; # define PROMISE_LOG(x, ...) \ @@ -958,12 +954,6 @@ class MozPromise : public MozPromiseBase { } # endif - // Creates a C++ MozPromise from its JS counterpart, dom::Promise. - // FromDomPromise currently only supports primitive types (int8/16/32, float, - // double) And the reject value type must be a nsresult. - // To use, please include MozPromiseInlines.h - static RefPtr FromDomPromise(dom::Promise* aDOMPromise); - // Note we expose the function AssertIsDead() instead of IsDead() since // checking IsDead() is a data race in the situation where the request is not // dead. Therefore we enforce the form |Assert(IsDead())| by exposing diff --git a/xpcom/threads/MozPromiseInlines.h b/xpcom/threads/MozPromiseInlines.h deleted file mode 100644 index dec8828c5745..000000000000 --- a/xpcom/threads/MozPromiseInlines.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 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/. */ - -#if !defined(MozPromiseInlines_h_) -# define MozPromiseInlines_h_ - -# include "mozilla/MozPromise.h" -# include "mozilla/dom/PrimitiveConversions.h" -# include "mozilla/dom/PromiseNativeHandler.h" - -namespace mozilla { - -// Creates a C++ MozPromise from its JS counterpart, dom::Promise. -// FromDomPromise currently only supports primitive types (int8/16/32, float, -// double) And the reject value type must be a nsresult. -template -RefPtr> -MozPromise::FromDomPromise( - dom::Promise* aDOMPromise) { - static_assert(IsSame::value, - "Reject type must be nsresult"); - RefPtr p = new Private(__func__); - RefPtr listener = new dom::DomPromiseListener( - aDOMPromise, - [p](JSContext* aCx, JS::Handle aValue) { - ResolveValueT value; - bool ok = dom::ValueToPrimitive( - aCx, aValue, &value); - if (!ok) { - p->Reject(NS_ERROR_FAILURE, __func__); - return; - } - p->Resolve(value, __func__); - }, - [p](JSContext* aCx, JS::Handle aValue) { - if (!aValue.isInt32()) { - p->Reject(NS_ERROR_DOM_NOT_NUMBER_ERR, __func__); - return; - } - nsresult rv = nsresult(aValue.toInt32()); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - p->Reject(rv, __func__); - }); - return p; -} - -} // namespace mozilla - -#endif diff --git a/xpcom/threads/moz.build b/xpcom/threads/moz.build index f871776ec6f4..7220d0710dca 100644 --- a/xpcom/threads/moz.build +++ b/xpcom/threads/moz.build @@ -53,7 +53,6 @@ EXPORTS.mozilla += [ 'MainThreadIdlePeriod.h', 'Monitor.h', 'MozPromise.h', - 'MozPromiseInlines.h', 'Mutex.h', 'PerformanceCounter.h', 'Queue.h',