Backed out 2 changesets (bug 1625513) for bc failures in browser/base/content/test/siteIdentity/browser_identity_UI.js on a CLOSED TREE

Backed out changeset 876b33334577 (bug 1625513)
Backed out changeset 69f75d37ae8e (bug 1625513)

--HG--
extra : rebase_source : e8752080577e762777eff9cda7a695bfa2ccd4cf
This commit is contained in:
Oana Pop Rus 2020-04-09 00:57:32 +03:00
Родитель 87d90e34dd
Коммит 90abf0bc61
15 изменённых файлов: 497 добавлений и 317 удалений

Просмотреть файл

@ -5671,51 +5671,6 @@
}
}
},
async performProcessSwitch(
aBrowser,
aRemoteType,
aSwitchId,
aReplaceBrowsingContext
) {
E10SUtils.log().info(
`performing switch from ${aBrowser.remoteType} to ${aRemoteType}`
);
// Don't try to switch tabs before delayed startup is completed.
await window.delayedStartupPromise;
// Perform a navigateAndRestore to trigger the process switch.
let tab = this.getTabForBrowser(aBrowser);
let loadArguments = {
newFrameloader: true, // Switch even if remoteType hasn't changed.
remoteType: aRemoteType, // Don't derive remoteType to switch to.
// Information about which channel should be performing the load.
redirectLoadSwitchId: aSwitchId,
// True if this is a process switch due to a policy mismatch, means we
// shouldn't preserve our browsing context.
replaceBrowsingContext: aReplaceBrowsingContext,
};
await SessionStore.navigateAndRestore(tab, loadArguments, -1);
// If the process switch seems to have failed, send an error over to our
// caller, to give it a chance to kill our channel.
if (
aBrowser.remoteType != aRemoteType ||
!aBrowser.frameLoader ||
!aBrowser.frameLoader.remoteTab
) {
throw Cr.NS_ERROR_FAILURE;
}
// Tell our caller to redirect the load into this newly created process.
let remoteTab = aBrowser.frameLoader.remoteTab;
E10SUtils.log().debug(`new tabID: ${remoteTab.tabId}`);
return remoteTab.contentProcessId;
},
};
/**

Просмотреть файл

@ -54,6 +54,7 @@ const OBSERVING = [
"browser:purge-session-history-for-domain",
"idle-daily",
"clear-origin-attributes-data",
"channel-on-may-change-process",
];
// XUL Window properties to (re)store
@ -518,6 +519,9 @@ var SessionStoreInternal = {
// A counter to be used to generate a unique ID for each closed tab or window.
_nextClosedId: 0,
// A monotonic value used to generate a unique ID for each process switch.
_switchIdMonotonic: 0,
// During the initial restore and setBrowserState calls tracks the number of
// windows yet to be restored
_restoreCount: -1,
@ -943,6 +947,9 @@ var SessionStoreInternal = {
this._forgetTabsWithUserContextId(userContextId);
}
break;
case "channel-on-may-change-process":
this.onMayChangeProcess(aSubject);
break;
}
},
@ -2912,6 +2919,237 @@ var SessionStoreInternal = {
}
},
async _doTabProcessSwitch(
aBrowser,
aRemoteType,
aChannel,
aSwitchId,
aReplaceBrowsingContext
) {
E10SUtils.log().info(
`performing switch from ${aBrowser.remoteType} to ${aRemoteType}`
);
// Don't try to switch tabs before delayed startup is completed.
await aBrowser.ownerGlobal.delayedStartupPromise;
// Perform a navigateAndRestore to trigger the process switch.
let tab = aBrowser.ownerGlobal.gBrowser.getTabForBrowser(aBrowser);
let loadArguments = {
newFrameloader: true, // Switch even if remoteType hasn't changed.
remoteType: aRemoteType, // Don't derive remoteType to switch to.
// Information about which channel should be performing the load.
redirectLoadSwitchId: aSwitchId,
// True if this is a process switch due to a policy mismatch, means we
// shouldn't preserve our browsing context.
replaceBrowsingContext: aReplaceBrowsingContext,
};
await SessionStore.navigateAndRestore(tab, loadArguments, -1);
// If the process switch seems to have failed, send an error over to our
// caller, to give it a chance to kill our channel.
if (
aBrowser.remoteType != aRemoteType ||
!aBrowser.frameLoader ||
!aBrowser.frameLoader.remoteTab
) {
throw Cr.NS_ERROR_FAILURE;
}
// Tell our caller to redirect the load into this newly created process.
let remoteTab = aBrowser.frameLoader.remoteTab;
E10SUtils.log().debug(`new tabID: ${remoteTab.tabId}`);
return remoteTab.contentProcessId;
},
/**
* Perform a destructive process switch into a distinct process.
* This method is asynchronous, as it requires multiple calls into content
* processes.
*/
async _doProcessSwitch(
aBrowsingContext,
aRemoteType,
aChannel,
aSwitchId,
aReplaceBrowsingContext
) {
// There are two relevant cases when performing a process switch for a
// browsing context: in-process and out-of-process embedders.
// If our embedder is in-process (e.g. we're a xul:browser element embedded
// within <tabbrowser>), then we can perform a process switch using the
// traditional mechanism.
if (aBrowsingContext.embedderElement) {
return this._doTabProcessSwitch(
aBrowsingContext.embedderElement,
aRemoteType,
aChannel,
aSwitchId,
aReplaceBrowsingContext
);
}
return aBrowsingContext.changeFrameRemoteness(aRemoteType, aSwitchId);
},
// Examine the channel response to see if we should change the process
// performing the given load. aRequestor implements nsIProcessSwitchRequestor
onMayChangeProcess(aRequestor) {
if (!E10SUtils.documentChannel()) {
throw new Error("This code is only used by document channel");
}
let switchRequestor;
try {
switchRequestor = aRequestor.QueryInterface(Ci.nsIProcessSwitchRequestor);
} catch (e) {
E10SUtils.log().warn(`object not compatible with process switching `);
return;
}
const channel = switchRequestor.channel;
if (!channel.isDocument || !channel.loadInfo) {
return; // Not a document load.
}
// Check that the document has a corresponding BrowsingContext.
let browsingContext = channel.loadInfo.targetBrowsingContext;
let isSubframe =
channel.loadInfo.externalContentPolicyType !=
Ci.nsIContentPolicy.TYPE_DOCUMENT;
if (!browsingContext) {
E10SUtils.log().debug(`no BrowsingContext - ignoring`);
return;
}
// Determine if remote subframes should be used for this load.
let topBC = browsingContext.top;
if (!topBC.embedderElement) {
E10SUtils.log().debug(`no embedder for top - ignoring`);
return;
}
let topDocShell = topBC.embedderElement.ownerGlobal.docShell;
let { useRemoteSubframes } = topDocShell.QueryInterface(Ci.nsILoadContext);
if (!useRemoteSubframes && isSubframe) {
E10SUtils.log().debug(`remote subframes disabled - ignoring`);
return;
}
// Get principal for a document already loaded in the BrowsingContext.
let currentPrincipal = null;
if (browsingContext.currentWindowGlobal) {
currentPrincipal = browsingContext.currentWindowGlobal.documentPrincipal;
}
// We can only perform a process switch on in-process frames if they are
// embedded within a normal tab. We can't do one of these swaps for a
// cross-origin frame.
if (browsingContext.embedderElement) {
let tabbrowser = browsingContext.embedderElement.getTabBrowser();
if (!tabbrowser) {
E10SUtils.log().debug(
`cannot find tabbrowser for loading tab - ignoring`
);
return;
}
let tab = tabbrowser.getTabForBrowser(browsingContext.embedderElement);
if (!tab) {
E10SUtils.log().debug(
`not a normal tab, so cannot swap processes - ignoring`
);
return;
}
} else if (!browsingContext.parent) {
E10SUtils.log().debug(
`no parent or in-process embedder element - ignoring`
);
return;
}
// Get the current remote type for the BrowsingContext.
let currentRemoteType = browsingContext.currentRemoteType;
if (currentRemoteType == E10SUtils.NOT_REMOTE) {
E10SUtils.log().debug(`currently not remote - ignoring`);
return;
}
// Determine the process type the load should be performed in.
let resultPrincipal = Services.scriptSecurityManager.getChannelResultPrincipal(
channel
);
const isCOOPSwitch =
E10SUtils.useCrossOriginOpenerPolicy() &&
switchRequestor.hasCrossOriginOpenerPolicyMismatch();
let preferredRemoteType = currentRemoteType;
if (
E10SUtils.useCrossOriginOpenerPolicy() &&
switchRequestor.crossOriginOpenerPolicy ==
Ci.nsILoadInfo.OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP
) {
// We want documents with a SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP
// COOP policy to be loaded in a separate process for which we can enable
// high resolution timers.
preferredRemoteType =
E10SUtils.WEB_REMOTE_COOP_COEP_TYPE_PREFIX + resultPrincipal.siteOrigin;
} else if (isCOOPSwitch) {
// If it is a coop switch, but doesn't have this flag, we want to switch
// to a default remoteType
preferredRemoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
}
E10SUtils.log().info(
`currentRemoteType (${currentRemoteType}) preferredRemoteType: ${preferredRemoteType}`
);
let remoteType = E10SUtils.getRemoteTypeForPrincipal(
resultPrincipal,
true,
useRemoteSubframes,
preferredRemoteType,
currentPrincipal,
isSubframe
);
E10SUtils.log().debug(
`${currentRemoteType}, ${remoteType}, ${isCOOPSwitch}`
);
if (currentRemoteType == remoteType && !isCOOPSwitch) {
E10SUtils.log().debug(`type (${remoteType}) is compatible - ignoring`);
return;
}
if (
remoteType == E10SUtils.NOT_REMOTE ||
currentRemoteType == E10SUtils.NOT_REMOTE
) {
E10SUtils.log().debug(`non-remote source/target - ignoring`);
return;
}
// ------------------------------------------------------------------------
// DANGER ZONE: Perform a process switch into the new process. This is
// destructive.
// ------------------------------------------------------------------------
let identifier = ++this._switchIdMonotonic;
let tabPromise = this._doProcessSwitch(
browsingContext,
remoteType,
channel,
identifier,
isCOOPSwitch
);
switchRequestor.switchProcessTo(tabPromise, identifier);
},
/* ........ nsISessionStore API .............. */
getBrowserState: function ssi_getBrowserState() {

Просмотреть файл

@ -501,6 +501,25 @@ CanonicalBrowsingContext::ChangeFrameRemoteness(const nsAString& aRemoteType,
return promise.forget();
}
already_AddRefed<Promise> CanonicalBrowsingContext::ChangeFrameRemoteness(
const nsAString& aRemoteType, uint64_t aPendingSwitchId, ErrorResult& aRv) {
nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
ChangeFrameRemoteness(aRemoteType, aPendingSwitchId)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[promise](BrowserParent* aBrowserParent) {
promise->MaybeResolve(aBrowserParent->Manager()->ChildID());
},
[promise](nsresult aRv) { promise->MaybeReject(aRv); });
return promise.forget();
}
MediaController* CanonicalBrowsingContext::GetMediaController() {
// We would only create one media controller per tab, so accessing the
// controller via the top-level browsing context.

Просмотреть файл

@ -102,6 +102,12 @@ class CanonicalBrowsingContext final : public BrowsingContext {
RefPtr<RemotenessPromise> ChangeFrameRemoteness(const nsAString& aRemoteType,
uint64_t aPendingSwitchId);
// Helper version for WebIDL - resolves to the PID where the load is being
// resumed.
already_AddRefed<Promise> ChangeFrameRemoteness(const nsAString& aRemoteType,
uint64_t aPendingSwitchId,
ErrorResult& aRv);
// Return a media controller from the top-level browsing context that can
// control all media belonging to this browsing context tree. Return nullptr
// if the top-level browsing context has been discarded.

Просмотреть файл

@ -98,6 +98,10 @@ interface CanonicalBrowsingContext : BrowsingContext {
[Throws]
void loadURI(DOMString aURI, optional LoadURIOptions aOptions = {});
[Throws]
Promise<unsigned long long> changeFrameRemoteness(
DOMString remoteType, unsigned long long pendingSwitchId);
readonly attribute nsISHistory? sessionHistory;
};

Просмотреть файл

@ -173,40 +173,4 @@ interface nsIBrowser : nsISupports
void updateSecurityUIForSecurityChange(in nsITransportSecurityInfo aSecurityInfo,
in uint32_t aState,
in boolean aIsSecureContext);
/**
* Called by Gecko when it wants to change the process which is currently
* being used to render content in this tab.
*
* Returns a promise which will be resolved with the ChildID of the new
* process.
*
* @param aRemoteType the new remote type to switch this browser into
* @param aPendingSwitchId unique identifier for the ongoing
* process-switching load
* @param aReplaceBrowsingContext if true, do not preserve the
* BrowsingContext loaded inside this
* browser after changing processes.
*/
Promise performProcessSwitch(in AString aRemoteType,
in uint64_t aPendingSwitchId,
in boolean aReplaceBrowsingContext);
/** If `true`, this browser supports the `performProcessSwitch` method */
readonly attribute bool canPerformProcessSwitch;
/**
* Helper method for invoking `E10SUtils.getRemoteTypeForPrincipal` from
* `DocumentLoadListener`.
*
* FIXME: DocumentLoadListener should make process selection decisions without
* an `nsIBrowser`, as the implementation does not depend on which browser
* it's being called on.
*/
AString getRemoteTypeForPrincipal(in nsIPrincipal aPrincipal,
in boolean aRemoteTabs,
in boolean aRemoteSubframes,
in AString aPreferredRemoteType,
in nsIPrincipal aCurrentPrincipal,
in boolean aIsSubframe);
};

Просмотреть файл

@ -75,6 +75,7 @@ XPIDL_SOURCES += [
'nsIPermission.idl',
'nsIPermissionManager.idl',
'nsIPrivateBrowsingChannel.idl',
'nsIProcessSwitchRequestor.idl',
'nsIProgressEventSink.idl',
'nsIPrompt.idl',
'nsIProtocolHandler.idl',

Просмотреть файл

@ -0,0 +1,52 @@
/* -*- 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
* channel-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();
/**
* Returns a cached CrossOriginOpenerPolicy that is computed just before we
* determine if there is a policy mismatch.
* @throws NS_ERROR_NOT_AVAILABLE if called before onStartRequest
*/
[must_use, binaryname(CachedCrossOriginOpenerPolicy)] readonly attribute nsILoadInfo_CrossOriginOpenerPolicy crossOriginOpenerPolicy;
};

Просмотреть файл

@ -39,7 +39,6 @@
#include "nsIOService.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/StaticPrefs_security.h"
#include "nsIBrowser.h"
mozilla::LazyLogModule gDocumentChannelLog("DocumentChannel");
#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
@ -233,6 +232,7 @@ NS_INTERFACE_MAP_BEGIN(DocumentLoadListener)
NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
NS_INTERFACE_MAP_ENTRY(nsIProcessSwitchRequestor)
NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentLoadListener)
NS_INTERFACE_MAP_END
@ -826,187 +826,26 @@ void DocumentLoadListener::SerializeRedirectData(
aArgs.loadStateLoadType() = mLoadStateLoadType;
}
bool DocumentLoadListener::MaybeTriggerProcessSwitch() {
MOZ_DIAGNOSTIC_ASSERT(!mDoingProcessSwitch,
"Already in the middle of switching?");
MOZ_DIAGNOSTIC_ASSERT(mChannel);
MOZ_DIAGNOSTIC_ASSERT(mParentChannelListener);
void DocumentLoadListener::TriggerCrossProcessSwitch() {
MOZ_ASSERT(mRedirectContentProcessIdPromise);
MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
MOZ_ASSERT(NS_IsMainThread());
LOG(("DocumentLoadListener MaybeTriggerProcessSwitch [this=%p]", this));
LOG(("DocumentLoadListener TriggerCrossProcessSwitch [this=%p]", this));
// Get the BrowsingContext which will be switching processes.
RefPtr<CanonicalBrowsingContext> browsingContext =
mParentChannelListener->GetBrowsingContext();
if (NS_WARN_IF(!browsingContext)) {
LOG(("Process Switch Abort: no browsing context"));
return false;
}
if (!browsingContext->IsContent()) {
LOG(("Process Switch Abort: non-content browsing context"));
return false;
}
if (browsingContext->GetParent() && !browsingContext->UseRemoteSubframes()) {
LOG(("Process Switch Abort: remote subframes disabled"));
return false;
}
// We currently can't switch processes for toplevel loads unless they're
// loaded within a browser tab. We also enforce this for non-toplevel tabs, as
// otherwise cross-origin subframes could get out of sync.
// FIXME: Ideally we won't do this in the future.
Element* browserElement = browsingContext->Top()->GetEmbedderElement();
if (!browserElement) {
LOG(("Process Switch Abort: cannot get browser element"));
return false;
}
nsCOMPtr<nsIBrowser> browser = browserElement->AsBrowser();
if (!browser) {
LOG(("Process Switch Abort: not loaded within nsIBrowser"));
return false;
}
bool loadedInTab = false;
if (NS_FAILED(browser->GetCanPerformProcessSwitch(&loadedInTab)) ||
!loadedInTab) {
LOG(("Process Switch Abort: browser is not loaded in a tab"));
return false;
}
// Get information about the current document loaded in our BrowsingContext.
nsCOMPtr<nsIPrincipal> currentPrincipal;
if (RefPtr<WindowGlobalParent> wgp =
browsingContext->GetCurrentWindowGlobal()) {
currentPrincipal = wgp->DocumentPrincipal();
}
RefPtr<ContentParent> currentProcess = browsingContext->GetContentParent();
if (!currentProcess) {
LOG(("Process Switch Abort: frame currently not remote"));
return false;
}
// Get the final principal, used to select which process to load into.
nsCOMPtr<nsIPrincipal> resultPrincipal;
nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
mChannel, getter_AddRefs(resultPrincipal));
if (NS_FAILED(rv)) {
LOG(("Process Switch Abort: failed to get channel result principal"));
return false;
}
if (resultPrincipal->IsSystemPrincipal()) {
LOG(("Process Switch Abort: cannot switch process for system principal"));
return false;
}
// Determine our COOP status, which will be used to determine our preferred
// remote type.
bool isCOOPSwitch = HasCrossOriginOpenerPolicyMismatch();
nsILoadInfo::CrossOriginOpenerPolicy coop =
nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
if (RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel)) {
MOZ_ALWAYS_SUCCEEDS(httpChannel->GetCrossOriginOpenerPolicy(&coop));
}
nsAutoString preferredRemoteType(currentProcess->GetRemoteType());
if (coop ==
nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) {
// We want documents with SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP COOP
// policy to be loaded in a separate process in which we can enable
// high-resolution timers.
nsAutoCString siteOrigin;
resultPrincipal->GetSiteOrigin(siteOrigin);
preferredRemoteType.Assign(
NS_LITERAL_STRING(WITH_COOP_COEP_REMOTE_TYPE_PREFIX));
preferredRemoteType.Append(NS_ConvertUTF8toUTF16(siteOrigin));
} else if (isCOOPSwitch) {
// If we're doing a COOP switch, we do not need any affinity to the current
// remote type. Clear it back to the default value.
preferredRemoteType.Assign(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
}
MOZ_DIAGNOSTIC_ASSERT(!preferredRemoteType.IsEmpty(),
"Unexpected empty remote type!");
LOG(
("DocumentLoadListener GetRemoteTypeForPrincipal "
"[this=%p, currentProcess=%s, preferredRemoteType=%s]",
this, NS_ConvertUTF16toUTF8(currentProcess->GetRemoteType()).get(),
NS_ConvertUTF16toUTF8(preferredRemoteType).get()));
nsAutoString remoteType;
rv = browser->GetRemoteTypeForPrincipal(
resultPrincipal, browsingContext->UseRemoteTabs(),
browsingContext->UseRemoteSubframes(), preferredRemoteType,
currentPrincipal, browsingContext->GetParent(), remoteType);
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("Process Switch Abort: getRemoteTypeForPrincipal threw an exception"));
return false;
}
// Check if a process switch is needed.
if (currentProcess->GetRemoteType() == remoteType && !isCOOPSwitch) {
LOG(("Process Switch Abort: type (%s) is compatible",
NS_ConvertUTF16toUTF8(remoteType).get()));
return false;
}
if (NS_WARN_IF(remoteType.IsEmpty())) {
LOG(("Process Switch Abort: non-remote target process"));
return false;
}
LOG(("Process Switch: Changing Remoteness from '%s' to '%s'",
NS_ConvertUTF16toUTF8(currentProcess->GetRemoteType()).get(),
NS_ConvertUTF16toUTF8(remoteType).get()));
// XXX: This is super hacky, and we should be able to do something better.
static uint64_t sNextCrossProcessRedirectIdentifier = 0;
mCrossProcessRedirectIdentifier = ++sNextCrossProcessRedirectIdentifier;
mDoingProcessSwitch = true;
RefPtr<DocumentLoadListener> self = this;
// At this point, we're actually going to perform a process switch, which
// involves calling into other logic.
if (browsingContext->GetParent()) {
LOG(("Process Switch: Calling ChangeFrameRemoteness"));
// If we're switching a subframe, ask BrowsingContext to do it for us.
MOZ_ASSERT(!isCOOPSwitch);
browsingContext
->ChangeFrameRemoteness(remoteType, mCrossProcessRedirectIdentifier)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self](BrowserParent* aBrowserParent) {
MOZ_ASSERT(self->mChannel,
"Something went wrong, channel got cancelled");
self->TriggerRedirectToRealChannel(
Some(aBrowserParent->Manager()->ChildID()));
},
[self](nsresult aStatusCode) {
MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
self->RedirectToRealChannelFinished(aStatusCode);
});
return true;
}
LOG(("Process Switch: Calling nsIBrowser::PerformProcessSwitch"));
// We're switching a toplevel BrowsingContext's process. This has to be done
// using nsIBrowser.
RefPtr<Promise> domPromise;
browser->PerformProcessSwitch(remoteType, mCrossProcessRedirectIdentifier,
isCOOPSwitch, getter_AddRefs(domPromise));
MOZ_DIAGNOSTIC_ASSERT(domPromise,
"PerformProcessSwitch didn't return a promise");
MozPromise<uint64_t, nsresult, true>::FromDomPromise(domPromise)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self](uint64_t aCpId) {
MOZ_ASSERT(self->mChannel,
"Something went wrong, channel got cancelled");
self->TriggerRedirectToRealChannel(Some(aCpId));
},
[self](nsresult aStatusCode) {
MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
self->RedirectToRealChannelFinished(aStatusCode);
});
return true;
mRedirectContentProcessIdPromise->Then(
GetMainThreadSerialEventTarget(), __func__,
[self, this](uint64_t aCpId) {
MOZ_ASSERT(mChannel, "Something went wrong, channel got cancelled");
TriggerRedirectToRealChannel(Some(aCpId));
},
[self](nsresult aStatusCode) {
MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
self->RedirectToRealChannelFinished(aStatusCode);
});
}
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
@ -1145,10 +984,18 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
httpChannel->SetApplyConversion(false);
}
// Determine if a new process needs to be spawned. If it does, this will
// trigger a cross process switch, and we should hold off on redirecting to
// the real channel.
if (status != NS_BINDING_ABORTED && MaybeTriggerProcessSwitch()) {
// notify "channel-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.
if (status != NS_BINDING_ABORTED) {
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
obsService->NotifyObservers(ToSupports(this),
"channel-on-may-change-process", nullptr);
}
if (mRedirectContentProcessIdPromise) {
TriggerCrossProcessSwitch();
return NS_OK;
}
@ -1308,8 +1155,10 @@ DocumentLoadListener::AsyncOnChannelRedirect(
// include the state of all channels we redirected through.
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(aOldChannel);
if (httpChannel) {
mHasCrossOriginOpenerPolicyMismatch |=
httpChannel->HasCrossOriginOpenerPolicyMismatch();
bool mismatch = false;
MOZ_ALWAYS_SUCCEEDS(
httpChannel->HasCrossOriginOpenerPolicyMismatch(&mismatch));
mHasCrossOriginOpenerPolicyMismatch |= mismatch;
}
// We don't need to confirm internal redirects or record any
@ -1394,22 +1243,70 @@ DocumentLoadListener::AsyncOnChannelRedirect(
return NS_OK;
}
//-----------------------------------------------------------------------------
// DocumentLoadListener::nsIProcessSwitchRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP DocumentLoadListener::GetChannel(nsIChannel** aChannel) {
MOZ_ASSERT(mChannel);
nsCOMPtr<nsIChannel> channel(mChannel);
channel.forget(aChannel);
return NS_OK;
}
NS_IMETHODIMP DocumentLoadListener::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
bool DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch() {
NS_IMETHODIMP
DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
// If we found a COOP mismatch on an earlier channel and then
// redirected away from that, we should use that result.
if (mHasCrossOriginOpenerPolicyMismatch) {
return true;
*aMismatch = true;
return NS_OK;
}
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
if (!httpChannel) {
// Not an nsHttpChannel assume it's okay to switch.
return false;
*aMismatch = false;
return NS_OK;
}
return httpChannel->HasCrossOriginOpenerPolicyMismatch();
return httpChannel->HasCrossOriginOpenerPolicyMismatch(aMismatch);
}
NS_IMETHODIMP
DocumentLoadListener::GetCachedCrossOriginOpenerPolicy(
nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) {
MOZ_ASSERT(aPolicy);
if (!aPolicy) {
return NS_ERROR_INVALID_ARG;
}
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
if (!httpChannel) {
*aPolicy = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
return NS_OK;
}
return httpChannel->GetCrossOriginOpenerPolicy(aPolicy);
}
} // namespace net

Просмотреть файл

@ -20,6 +20,7 @@
#include "nsIObserver.h"
#include "nsIParentChannel.h"
#include "nsIParentRedirectingChannel.h"
#include "nsIProcessSwitchRequestor.h"
#include "nsIRedirectResultListener.h"
#include "nsIMultiPartChannel.h"
@ -59,6 +60,7 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
public nsIParentChannel,
public nsIChannelEventSink,
public HttpChannelSecurityWarningReporter,
public nsIProcessSwitchRequestor,
public nsIMultiPartChannelListener {
public:
explicit DocumentLoadListener(dom::CanonicalBrowsingContext* aBrowsingContext,
@ -79,6 +81,7 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIPROCESSSWITCHREQUESTOR
NS_DECL_NSIMULTIPARTCHANNELLISTENER
// We suspend the underlying channel when replacing ourselves with
@ -172,10 +175,10 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// by us, and resumes the underlying source channel.
void FinishReplacementChannelSetup(bool aSucceeded);
// Called from `OnStartRequest` to make the decision about whether or not to
// change process. This method will return `nullptr` if the current target
// process is appropriate.
bool MaybeTriggerProcessSwitch();
// Called when we have a cross-process switch promise. Waits on the
// promise, and then call TriggerRedirectToRealChannel with the
// provided content process id.
void TriggerCrossProcessSwitch();
// A helper for TriggerRedirectToRealChannel that abstracts over
// the same-process and cross-process switch cases and returns
@ -191,8 +194,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
dom::CanonicalBrowsingContext* aBrowsingContext,
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId);
bool HasCrossOriginOpenerPolicyMismatch();
// This defines a variant that describes all the attribute setters (and their
// parameters) from nsIParentChannel
//
@ -345,8 +346,16 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// channel.
bool mIsFinished = false;
// This identifier is set by MaybeTriggerProcessSwitch, and is later
// passed to the childChannel in order to identify it in the new process.
typedef MozPromise<uint64_t, nsresult, true /* exclusive */>
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<ContentProcessIdPromise> 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;
};

Просмотреть файл

@ -6215,6 +6215,7 @@ 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)
@ -7394,6 +7395,59 @@ nsHttpChannel::GetRequestMethod(nsACString& aMethod) {
return HttpBaseChannel::GetRequestMethod(aMethod);
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIProcessSwitchRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP nsHttpChannel::GetChannel(nsIChannel** aChannel) {
*aChannel = do_AddRef(this).take();
return NS_OK;
}
NS_IMETHODIMP nsHttpChannel::SwitchProcessTo(
dom::Promise* aContentProcessIdPromise, uint64_t aIdentifier) {
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aContentProcessIdPromise);
LOG(("nsHttpChannel::SwitchProcessTo [this=%p]", this));
LogCallingScriptLocation(this);
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(this, parentChannel);
RefPtr<DocumentLoadListener> 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);
}
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
nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = mHasCrossOriginOpenerPolicyMismatch;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetCachedCrossOriginOpenerPolicy(
nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) {
return HttpBaseChannel::GetCrossOriginOpenerPolicy(aPolicy);
}
// See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
// This method runs steps 1-4 of the algorithm to compare
// cross-origin-opener policies

Просмотреть файл

@ -32,6 +32,7 @@
#include "mozilla/Atomics.h"
#include "mozilla/extensions/PStreamFilterParent.h"
#include "mozilla/Mutex.h"
#include "nsIProcessSwitchRequestor.h"
class nsDNSPrefetch;
class nsICancelable;
@ -78,7 +79,8 @@ class nsHttpChannel final : public HttpBaseChannel,
public nsIChannelWithDivertableParentListener,
public nsIRaceCacheWithNetwork,
public nsIRequestTailUnblockCallback,
public nsITimerCallback {
public nsITimerCallback,
public nsIProcessSwitchRequestor {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIREQUESTOBSERVER
@ -100,6 +102,7 @@ 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
@ -274,6 +277,16 @@ class nsHttpChannel final : public HttpBaseChannel,
}
TransactionObserver* GetTransactionObserver() { return mTransactionObserver; }
typedef MozPromise<uint64_t, nsresult, true /* exclusive */>
ContentProcessIdPromise;
already_AddRefed<ContentProcessIdPromise>
TakeRedirectContentProcessIdPromise() {
return mRedirectContentProcessIdPromise.forget();
}
uint64_t CrossProcessRedirectIdentifier() {
return mCrossProcessRedirectIdentifier;
}
CacheDisposition mCacheDisposition;
protected:
@ -482,11 +495,6 @@ class nsHttpChannel final : public HttpBaseChannel,
nsresult ProcessCrossOriginResourcePolicyHeader();
nsresult ComputeCrossOriginOpenerPolicyMismatch();
// This method returns the cached result of running the Cross-Origin-Opener
// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
bool HasCrossOriginOpenerPolicyMismatch() {
return mHasCrossOriginOpenerPolicyMismatch;
}
/**
* A function to process a single security header (STS or PKP), assumes
@ -574,6 +582,12 @@ class nsHttpChannel final : public HttpBaseChannel,
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCOMPtr<nsIChannel> mPreflightChannel;
// The associated childChannel is getting relocated to another process.
// This promise will be resolved when that process is set up.
RefPtr<ContentProcessIdPromise> mRedirectContentProcessIdPromise;
// This identifier is passed to the childChannel in order to identify it.
uint64_t mCrossProcessRedirectIdentifier = 0;
// nsChannelClassifier checks this channel's URI against
// the URI classifier service.
// nsChannelClassifier will be invoked twice in InitLocalBlockList() and

Просмотреть файл

@ -18,6 +18,7 @@
#include "nsStandardURL.h"
#include "LoadContextInfo.h"
#include "nsCategoryManagerUtils.h"
#include "nsIProcessSwitchRequestor.h"
#include "nsSocketProviderService.h"
#include "nsISocketProvider.h"
#include "nsPrintfCString.h"

Просмотреть файл

@ -35,6 +35,7 @@ class nsIHttpUpgradeListener;
class nsIPrefBranch;
class nsICancelable;
class nsICookieService;
class nsIProcessSwitchRequestor;
class nsIIOService;
class nsIRequestContextService;
class nsISiteSecurityService;

Просмотреть файл

@ -2034,41 +2034,6 @@
}
return origins;
}
get canPerformProcessSwitch() {
return this.getTabBrowser() != null;
}
performProcessSwitch(
remoteType,
redirectLoadSwitchId,
replaceBrowsingContext
) {
return this.getTabBrowser().performProcessSwitch(
this,
remoteType,
redirectLoadSwitchId,
replaceBrowsingContext
);
}
getRemoteTypeForPrincipal(
principal,
remoteTabs,
remoteSubframes,
preferredRemoteType,
currentPrincipal,
isSubframe
) {
return E10SUtils.getRemoteTypeForPrincipal(
principal,
remoteTabs,
remoteSubframes,
preferredRemoteType,
currentPrincipal,
isSubframe
);
}
}
MozXULElement.implementCustomInterface(MozBrowser, [Ci.nsIBrowser]);