Bug 1671983 - Part 2: Remove SessionStore.navigateAndRestore, r=annyG,kashav

This formed the backbone of the previous process switching codepath, and
shouldn't be necessary anymore thanks to DocumentChannel's new codepath.

This also removes the eager process switching logic from frontend's _loadURI, as
it would rarely be taken, unless an invalid URI was entered, already.

Differential Revision: https://phabricator.services.mozilla.com/D94639
This commit is contained in:
Nika Layzell 2020-11-12 18:00:55 +00:00
parent 9ff27adca3
commit 6b824cc31d
5 changed files with 35 additions and 448 deletions

View File

@ -1537,32 +1537,31 @@ function _loadURI(browser, uri, params = {}) {
throw new Error("Cannot load with mismatched userContextId");
}
let {
uriObject,
requiredRemoteType,
mustChangeProcess,
newFrameloader,
} = E10SUtils.shouldLoadURIInBrowser(
browser,
uri,
gMultiProcessBrowser,
gFissionBrowser,
loadFlags
);
if (uriObject && handleUriInChrome(browser, uriObject)) {
// If we've handled the URI in Chrome then just return here.
return;
}
if (newFrameloader) {
// If a new frameloader is needed for process reselection because this used
// to be a preloaded browser, clear the preloaded state now.
browser.removeAttribute("preloadedState");
// Attempt to perform URI fixup to see if we can handle this URI in chrome.
try {
let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE;
if (loadFlags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
}
if (loadFlags & Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS;
}
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_PRIVATE_CONTEXT;
}
let uriObject = Services.uriFixup.getFixupURIInfo(uri, fixupFlags)
.preferredURI;
if (uriObject && handleUriInChrome(browser, uriObject)) {
// If we've handled the URI in Chrome, then just return here.
return;
}
} catch (e) {
// getFixupURIInfo may throw. Gracefully recover and try to load the URI normally.
}
// !requiredRemoteType means we're loading in the parent/this process.
if (!requiredRemoteType) {
browser.isNavigating = true;
}
// XXX(nika): Is `browser.isNavigating` necessary anymore?
browser.isNavigating = true;
let loadURIOptions = {
triggeringPrincipal,
csp,
@ -1572,73 +1571,12 @@ function _loadURI(browser, uri, params = {}) {
hasValidUserGestureActivation,
};
try {
if (!mustChangeProcess) {
browser.webNavigation.loadURI(uri, loadURIOptions);
} else {
// Check if the current browser is allowed to unload.
let { permitUnload } = browser.permitUnload();
if (!permitUnload) {
return;
}
if (postData) {
postData = serializeInputStream(postData);
}
let loadParams = {
uri,
triggeringPrincipal: triggeringPrincipal
? E10SUtils.serializePrincipal(triggeringPrincipal)
: null,
flags: loadFlags,
referrerInfo: E10SUtils.serializeReferrerInfo(referrerInfo),
remoteType: requiredRemoteType,
postData,
newFrameloader,
csp: csp ? gSerializationHelper.serializeToString(csp) : null,
};
if (userContextId) {
loadParams.userContextId = userContextId;
}
if (browser.webNavigation.maybeCancelContentJSExecution) {
let cancelContentJSEpoch = browser.webNavigation.maybeCancelContentJSExecution(
Ci.nsIRemoteTab.NAVIGATE_URL,
{ uri: uriObject }
);
loadParams.cancelContentJSEpoch = cancelContentJSEpoch;
}
LoadInOtherProcess(browser, loadParams);
}
} catch (e) {
// If anything goes wrong when switching remoteness, just switch remoteness
// manually and load the URI.
// We might lose history that way but at least the browser loaded a page.
// This might be necessary if SessionStore wasn't initialized yet i.e.
// when the homepage is a non-remote page.
if (mustChangeProcess) {
Cu.reportError(e);
gBrowser.updateBrowserRemotenessByURL(browser, uri);
browser.webNavigation.loadURI(uri, loadURIOptions);
} else {
throw e;
}
browser.webNavigation.loadURI(uri, loadURIOptions);
} finally {
if (!requiredRemoteType) {
browser.isNavigating = false;
}
browser.isNavigating = false;
}
}
// Starts a new load in the browser first switching the browser to the correct
// process
function LoadInOtherProcess(browser, loadOptions, historyIndex = -1) {
let tab = gBrowser.getTabForBrowser(browser);
SessionStore.navigateAndRestore(tab, loadOptions, historyIndex);
}
let _resolveDelayedStartup;
var delayedStartupPromise = new Promise(resolve => {
_resolveDelayedStartup = resolve;

View File

@ -18,11 +18,6 @@ ChromeUtils.defineModuleGetter(
"Utils",
"resource://gre/modules/sessionstore/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"E10SUtils",
"resource://gre/modules/E10SUtils.jsm"
);
/**
* This module implements the content side of session restoration. The chrome
* side is handled by SessionStore.jsm. The functions in this module are called
@ -254,56 +249,12 @@ ContentRestoreInternal.prototype = {
if (loadArguments) {
// If the load was started in another process, and the in-flight channel
// was redirected into this process, resume that load within our process.
if (loadArguments.redirectLoadSwitchId) {
webNavigation.resumeRedirectedLoad(
loadArguments.redirectLoadSwitchId,
loadArguments.redirectHistoryIndex
);
return true;
}
// A load has been redirected to a new process so get history into the
// same state it was before the load started then trigger the load.
// Referrer information is now stored as a referrerInfo property. We
// should also cope with the old format of passing `referrer` and
// `referrerPolicy` separately.
let referrerInfo = loadArguments.referrerInfo;
if (referrerInfo) {
referrerInfo = E10SUtils.deserializeReferrerInfo(referrerInfo);
} else {
let referrer = loadArguments.referrer
? Services.io.newURI(loadArguments.referrer)
: null;
let referrerPolicy =
"referrerPolicy" in loadArguments
? loadArguments.referrerPolicy
: Ci.nsIReferrerInfo.EMPTY;
let ReferrerInfo = Components.Constructor(
"@mozilla.org/referrer-info;1",
"nsIReferrerInfo",
"init"
);
referrerInfo = new ReferrerInfo(referrerPolicy, true, referrer);
}
let postData = loadArguments.postData
? E10SUtils.makeInputStream(loadArguments.postData)
: null;
let triggeringPrincipal = E10SUtils.deserializePrincipal(
loadArguments.triggeringPrincipal,
() => Services.scriptSecurityManager.createNullPrincipal({})
//
// NOTE: In this case `isRemotenessUpdate` must be true.
webNavigation.resumeRedirectedLoad(
loadArguments.redirectLoadSwitchId,
loadArguments.redirectHistoryIndex
);
let csp = loadArguments.csp
? E10SUtils.deserializeCSP(loadArguments.csp)
: null;
let loadURIOptions = {
triggeringPrincipal,
loadFlags: loadArguments.flags,
referrerInfo,
postData,
csp,
};
webNavigation.loadURI(loadArguments.uri, loadURIOptions);
} else if (tabData.userTypedValue && tabData.userTypedClear) {
// If the user typed a URL into the URL bar and hit enter right before
// we crashed, we want to start loading that page again. A non-zero

View File

@ -13,7 +13,6 @@ const TAB_CUSTOM_VALUES = new WeakMap();
const TAB_LAZY_STATES = new WeakMap();
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
const TAB_STATE_WILL_RESTORE = 3;
const TAB_STATE_FOR_BROWSER = new WeakMap();
const WINDOW_RESTORE_IDS = new WeakMap();
const WINDOW_RESTORE_ZINDICES = new WeakMap();
@ -430,14 +429,6 @@ var SessionStore = {
return SessionStoreInternal.reviveAllCrashedTabs();
},
navigateAndRestore(tab, loadArguments, historyIndex) {
return SessionStoreInternal.navigateAndRestore(
tab,
loadArguments,
historyIndex
);
},
updateSessionStoreFromTablistener(aBrowser, aBrowsingContext, aData) {
return SessionStoreInternal.updateSessionStoreFromTablistener(
aBrowser,
@ -604,11 +595,6 @@ var SessionStoreInternal = {
// reason about.
_saveableClosedWindowData: new WeakSet(),
// A map (xul:browser -> object) that maps a browser that is switching
// remoteness via navigateAndRestore, to the loadArguments that were
// most recently passed when calling navigateAndRestore.
_remotenessChangingBrowsers: new WeakMap(),
// whether a setBrowserState call is in progress
_browserSetState: false,
@ -3865,148 +3851,6 @@ var SessionStoreInternal = {
}
},
/**
* Navigate the given |tab| by first collecting its current state and then
* either changing only the index of the currently shown history entry,
* or restoring the exact same state again and passing the new URL to load
* in |loadArguments|. Use this method to seamlessly switch between pages
* loaded in the parent and pages loaded in the child process.
*
* This method might be called multiple times before it has finished
* flushing the browser tab. If that occurs, the loadArguments from
* the most recent call to navigateAndRestore will be used once the
* flush has finished.
*
* This method returns a promise which will be resolved when the browser
* element's process has been swapped. The load is not guaranteed to have
* been completed at this point.
*/
navigateAndRestore(tab, loadArguments, historyIndex) {
let window = tab.ownerGlobal;
if (!window.__SSi) {
Cu.reportError("Tab's window must be tracked.");
return Promise.reject();
}
let browser = tab.linkedBrowser;
// If we were alerady waiting for a flush from a previous call to
// navigateAndRestore on this tab, update the loadArguments stored, and
// asynchronously wait on the flush's promise.
if (this._remotenessChangingBrowsers.has(browser.permanentKey)) {
let opts = this._remotenessChangingBrowsers.get(browser.permanentKey);
// XXX(nika): In the existing logic, we always use the initial
// historyIndex value, and don't update it if multiple navigateAndRestore
// calls are made. Should we update it here?
opts.loadArguments = loadArguments;
return opts.promise;
}
// Begin the asynchronous NavigateAndRestore process, and store the current
// load arguments and promise in our _remotenessChangingBrowsers weakmap.
let promise = this._asyncNavigateAndRestore(tab);
this._remotenessChangingBrowsers.set(browser.permanentKey, {
loadArguments,
historyIndex,
promise,
});
// Set up the browser UI to look like we're doing something while waiting
// for a TabStateFlush from our frame scripts.
let uriObj;
try {
uriObj = Services.io.newURI(loadArguments.uri);
} catch (e) {}
// Start the throbber to pretend we're doing something while actually
// waiting for data from the frame script. This throbber is disabled
// if the URI is a local about: URI.
if (!uriObj || (uriObj && !uriObj.schemeIs("about"))) {
tab.setAttribute("busy", "true");
}
// Hack to ensure that the about:home, about:newtab, and about:welcome
// favicon is loaded instantaneously, to avoid flickering and improve
// perceived performance.
window.gBrowser.setDefaultIcon(tab, uriObj);
TAB_STATE_FOR_BROWSER.set(tab.linkedBrowser, TAB_STATE_WILL_RESTORE);
// Notify of changes to closed objects.
this._notifyOfClosedObjectsChange();
return promise;
},
/**
* Internal logic called by navigateAndRestore to flush tab state, and
* trigger a remoteness changing load with the most recent load arguments.
*
* This method's promise will resolve when the process for the given
* xul:browser element has successfully been swapped.
*
* @param tab to navigate and restore.
*/
async _asyncNavigateAndRestore(tab) {
let permanentKey = tab.linkedBrowser.permanentKey;
let browser = tab.linkedBrowser;
// NOTE: This is currently the only async operation used, but this is likely
// to change in the future.
await this.prepareToChangeRemoteness(browser);
// Now that we have flushed state, our loadArguments, etc. may have been
// overwritten by multiple calls to navigateAndRestore. Load the most
// recently stored one.
let { loadArguments, historyIndex } = this._remotenessChangingBrowsers.get(
permanentKey
);
this._remotenessChangingBrowsers.delete(permanentKey);
// The tab might have been closed/gone in the meantime.
if (tab.closing || !tab.linkedBrowser) {
return;
}
// The tab or its window might be gone.
let window = tab.ownerGlobal;
if (!window || !window.__SSi || window.closed) {
return;
}
let tabState = TabState.clone(tab, TAB_CUSTOM_VALUES.get(tab));
let options = {
restoreImmediately: true,
// We want to make sure that this information is passed to restoreTab
// whether or not a historyIndex is passed in. Thus, we extract it from
// the loadArguments.
newFrameloader: loadArguments.newFrameloader,
remoteType: loadArguments.remoteType,
// Make sure that SessionStore knows that this restoration is due
// to a navigation, as opposed to us restoring a closed window or tab.
restoreContentReason: RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE,
};
if (historyIndex >= 0) {
tabState.index = historyIndex + 1;
tabState.index = Math.max(
1,
Math.min(tabState.index, tabState.entries.length)
);
} else {
options.loadArguments = loadArguments;
}
// Need to reset restoring tabs.
if (TAB_STATE_FOR_BROWSER.has(tab.linkedBrowser)) {
this._resetLocalTabRestoringState(tab);
}
// Restore the state into the tab.
this.restoreTab(tab, tabState, options);
},
/**
* Retrieves the latest session history information for a tab. The cached data
* is returned immediately, but a callback may be provided that supplies
@ -4943,9 +4787,6 @@ var SessionStoreInternal = {
let activeIndex = tabData.index - 1;
let activePageData = tabData.entries[activeIndex] || null;
let uri = activePageData ? activePageData.url || null : null;
if (loadArguments) {
uri = loadArguments.uri;
}
this.markTabAsRestoring(aTab);
@ -4953,22 +4794,10 @@ var SessionStoreInternal = {
// necessary.
let isRemotenessUpdate = aOptions.isRemotenessUpdate;
if (!isRemotenessUpdate) {
let newFrameloader = aOptions.newFrameloader;
if (aOptions.remoteType !== undefined) {
// We already have a selected remote type so we update to that.
isRemotenessUpdate = tabbrowser.updateBrowserRemoteness(browser, {
remoteType: aOptions.remoteType,
newFrameloader,
});
} else {
isRemotenessUpdate = tabbrowser.updateBrowserRemotenessByURL(
browser,
uri,
{
newFrameloader,
}
);
}
isRemotenessUpdate = tabbrowser.updateBrowserRemotenessByURL(
browser,
uri
);
if (isRemotenessUpdate) {
// We updated the remoteness, so we need to send the history down again.

View File

@ -123,7 +123,7 @@ interface nsIURIFixup : nsISupports
/**
* Convert load flags from nsIWebNavigation to URI fixup flags for use in
* createFixupURI or getFixupURIInfo.
* getFixupURIInfo.
*
* @param aURIText Candidate URI; used for determining whether to
* allow keyword lookups.

View File

@ -11,12 +11,6 @@ const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"useSeparateFileUriProcess",
@ -122,17 +116,6 @@ const kSafeSchemes = [
"xmpp",
];
const kDocumentChannelDeniedSchemes = ["javascript"];
const kDocumentChannelDeniedURIs = ["about:crashcontent", "about:printpreview"];
// Changes here should also be made in URIUsesDocChannel in DocumentChannel.cpp.
function documentChannelPermittedForURI(aURI) {
return (
!kDocumentChannelDeniedSchemes.includes(aURI.scheme) &&
!kDocumentChannelDeniedURIs.includes(aURI.spec)
);
}
// Note that even if the scheme fits the criteria for a web-handled scheme
// (ie it is compatible with the checks registerProtocolHandler uses), it may
// not be web-handled - it could still be handled via the OS by another app.
@ -858,120 +841,6 @@ var E10SUtils = {
return deserialized;
},
/**
* Returns whether or not a URI is supposed to load in a particular
* browser given its current remote type.
*
* @param browser (<xul:browser>)
* The browser to check.
* @param uri (String)
* The URI that will be checked to see if it can load in the
* browser.
* @param multiProcess (boolean, optional)
* Whether or not multi-process tabs are enabled. Defaults to true.
* @param remoteSubframes (boolean, optional)
* Whether or not multi-process subframes are enabled. Defaults to
* false.
* @param flags (Number, optional)
* nsIWebNavigation flags used to clean up the URL in the event that
* it needs fixing ia the URI fixup service. Defaults to
* nsIWebNavigation.LOAD_FLAGS_NONE.
*
* @return (Object)
* An object with the following properties:
*
* uriObject (nsIURI)
* The fixed-up URI that was generated for the check.
*
* requiredRemoteType (String)
* The remoteType that was computed for the browser that
* is required to load the URI.
*
* mustChangeProcess (boolean)
* Whether or not the front-end will be required to flip
* the process in order to view the URI.
*
* NOTE:
* mustChangeProcess might be false even if a process
* flip will occur. In this case, DocumentChannel is taking
* care of the process flip for us rather than the front-end
* code.
*
* newFrameloader (boolean)
* Whether or not a new frameloader will need to be created
* in order to browse to this URI. For non-Fission, this is
* important if we're transition from a web content process
* to another web content process, but want to force the
* creation of a _new_ web content process.
*/
shouldLoadURIInBrowser(
browser,
uri,
multiProcess = true,
remoteSubframes = false,
flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE
) {
let currentRemoteType = browser.remoteType;
let requiredRemoteType;
let uriObject;
try {
let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE;
if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
}
if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS;
}
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_PRIVATE_CONTEXT;
}
uriObject = Services.uriFixup.getFixupURIInfo(uri, fixupFlags)
.preferredURI;
// Note that I had thought that we could set uri = uriObject.spec here, to
// save on fixup later on, but that changes behavior and breaks tests.
requiredRemoteType = this.getRemoteTypeForURIObject(
uriObject,
multiProcess,
remoteSubframes,
currentRemoteType,
browser.currentURI
);
} catch (e) {
// createFixupURI throws if it can't create a URI. If that's the case then
// we still need to pass down the uri because docshell handles this case.
requiredRemoteType = multiProcess ? DEFAULT_REMOTE_TYPE : NOT_REMOTE;
}
let mustChangeProcess = requiredRemoteType != currentRemoteType;
let newFrameloader = false;
if (
browser.getAttribute("preloadedState") === "consumed" &&
uri != "about:newtab"
) {
// Leaving about:newtab from a used to be preloaded browser should run the process
// selecting algorithm again.
mustChangeProcess = true;
newFrameloader = true;
}
// If we already have a content process, and the load will be
// handled using DocumentChannel, then we can skip switching
// for now, and let DocumentChannel do it during the response.
if (uriObject && documentChannelPermittedForURI(uriObject)) {
mustChangeProcess = false;
newFrameloader = false;
}
return {
uriObject,
requiredRemoteType,
mustChangeProcess,
newFrameloader,
};
},
wrapHandlingUserInput(aWindow, aIsHandling, aCallback) {
var handlingUserInput;
try {