summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/ContentRestore.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/sessionstore/ContentRestore.sys.mjs')
-rw-r--r--browser/components/sessionstore/ContentRestore.sys.mjs435
1 files changed, 0 insertions, 435 deletions
diff --git a/browser/components/sessionstore/ContentRestore.sys.mjs b/browser/components/sessionstore/ContentRestore.sys.mjs
deleted file mode 100644
index e55772cab3..0000000000
--- a/browser/components/sessionstore/ContentRestore.sys.mjs
+++ /dev/null
@@ -1,435 +0,0 @@
-/* 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/. */
-
-const lazy = {};
-
-ChromeUtils.defineESModuleGetters(lazy, {
- SessionHistory: "resource://gre/modules/sessionstore/SessionHistory.sys.mjs",
- Utils: "resource://gre/modules/sessionstore/Utils.sys.mjs",
-});
-
-/**
- * This module implements the content side of session restoration. The chrome
- * side is handled by SessionStore.sys.mjs. The functions in this module are called
- * by content-sessionStore.js based on messages received from SessionStore.sys.mjs
- * (or, in one case, based on a "load" event). Each tab has its own
- * ContentRestore instance, constructed by content-sessionStore.js.
- *
- * In a typical restore, content-sessionStore.js will call the following based
- * on messages and events it receives:
- *
- * restoreHistory(tabData, loadArguments, callbacks)
- * Restores the tab's history and session cookies.
- * restoreTabContent(loadArguments, finishCallback)
- * Starts loading the data for the current page to restore.
- * restoreDocument()
- * Restore form and scroll data.
- *
- * When the page has been loaded from the network, we call finishCallback. It
- * should send a message to SessionStore.sys.mjs, which may cause other tabs to be
- * restored.
- *
- * When the page has finished loading, a "load" event will trigger in
- * content-sessionStore.js, which will call restoreDocument. At that point,
- * form data is restored and the restore is complete.
- *
- * At any time, SessionStore.sys.mjs can cancel the ongoing restore by sending a
- * reset message, which causes resetRestore to be called. At that point it's
- * legal to begin another restore.
- */
-export function ContentRestore(chromeGlobal) {
- let internal = new ContentRestoreInternal(chromeGlobal);
- let external = {};
-
- let EXPORTED_METHODS = [
- "restoreHistory",
- "restoreTabContent",
- "restoreDocument",
- "resetRestore",
- ];
-
- for (let method of EXPORTED_METHODS) {
- external[method] = internal[method].bind(internal);
- }
-
- return Object.freeze(external);
-}
-
-function ContentRestoreInternal(chromeGlobal) {
- this.chromeGlobal = chromeGlobal;
-
- // The following fields are only valid during certain phases of the restore
- // process.
-
- // The tabData for the restore. Set in restoreHistory and removed in
- // restoreTabContent.
- this._tabData = null;
-
- // Contains {entry, scrollPositions, formdata}, where entry is a
- // single entry from the tabData.entries array. Set in
- // restoreTabContent and removed in restoreDocument.
- this._restoringDocument = null;
-
- // This listener is used to detect reloads on restoring tabs. Set in
- // restoreHistory and removed in restoreTabContent.
- this._historyListener = null;
-
- // This listener detects when a pending tab starts loading (when not
- // initiated by sessionstore) and when a restoring tab has finished loading
- // data from the network. Set in restoreHistory() and restoreTabContent(),
- // removed in resetRestore().
- this._progressListener = null;
-}
-
-/**
- * The API for the ContentRestore module. Methods listed in EXPORTED_METHODS are
- * public.
- */
-ContentRestoreInternal.prototype = {
- get docShell() {
- return this.chromeGlobal.docShell;
- },
-
- /**
- * Starts the process of restoring a tab. The tabData to be restored is passed
- * in here and used throughout the restoration. The epoch (which must be
- * non-zero) is passed through to all the callbacks. If a load in the tab
- * is started while it is pending, the appropriate callbacks are called.
- */
- restoreHistory(tabData, loadArguments, callbacks) {
- this._tabData = tabData;
-
- // In case about:blank isn't done yet.
- let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
- webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL);
-
- // Make sure currentURI is set so that switch-to-tab works before the tab is
- // restored. We'll reset this to about:blank when we try to restore the tab
- // to ensure that docshell doeesn't get confused. Don't bother doing this if
- // we're restoring immediately due to a process switch. It just causes the
- // URL bar to be temporarily blank.
- let activeIndex = tabData.index - 1;
- let activePageData = tabData.entries[activeIndex] || {};
- let uri = activePageData.url || null;
- if (uri && !loadArguments) {
- webNavigation.setCurrentURIForSessionStore(Services.io.newURI(uri));
- }
-
- lazy.SessionHistory.restore(this.docShell, tabData);
-
- // Add a listener to watch for reloads.
- let listener = new HistoryListener(this.docShell, () => {
- // On reload, restore tab contents.
- this.restoreTabContent(null, false, callbacks.onLoadFinished);
- });
-
- webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener);
- this._historyListener = listener;
-
- // Make sure to reset the capabilities and attributes in case this tab gets
- // reused.
- SessionStoreUtils.restoreDocShellCapabilities(
- this.docShell,
- tabData.disallow
- );
-
- // Add a progress listener to correctly handle browser.loadURI()
- // calls from foreign code.
- this._progressListener = new ProgressListener(this.docShell, {
- onStartRequest: () => {
- // Some code called browser.loadURI() on a pending tab. It's safe to
- // assume we don't care about restoring scroll or form data.
- this._tabData = null;
-
- // Listen for the tab to finish loading.
- this.restoreTabContentStarted(callbacks.onLoadFinished);
-
- // Notify the parent.
- callbacks.onLoadStarted();
- },
- });
- },
-
- /**
- * Start loading the current page. When the data has finished loading from the
- * network, finishCallback is called. Returns true if the load was successful.
- */
- restoreTabContent(loadArguments, isRemotenessUpdate, finishCallback) {
- let tabData = this._tabData;
- this._tabData = null;
-
- let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
-
- // Listen for the tab to finish loading.
- this.restoreTabContentStarted(finishCallback);
-
- // Reset the current URI to about:blank. We changed it above for
- // switch-to-tab, but now it must go back to the correct value before the
- // load happens. Don't bother doing this if we're restoring immediately
- // due to a process switch.
- if (!isRemotenessUpdate) {
- webNavigation.setCurrentURIForSessionStore(
- Services.io.newURI("about:blank")
- );
- }
-
- try {
- 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.
- //
- // NOTE: In this case `isRemotenessUpdate` must be true.
- webNavigation.resumeRedirectedLoad(
- loadArguments.redirectLoadSwitchId,
- loadArguments.redirectHistoryIndex
- );
- } 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
- // userTypedClear value means that the load had started.
- // Load userTypedValue and fix up the URL if it's partial/broken.
- let loadURIOptions = {
- triggeringPrincipal:
- Services.scriptSecurityManager.getSystemPrincipal(),
- loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
- };
- webNavigation.fixupAndLoadURIString(
- tabData.userTypedValue,
- loadURIOptions
- );
- } else if (tabData.entries.length) {
- // Stash away the data we need for restoreDocument.
- this._restoringDocument = {
- formdata: tabData.formdata || {},
- scrollPositions: tabData.scroll || {},
- };
-
- // In order to work around certain issues in session history, we need to
- // force session history to update its internal index and call reload
- // instead of gotoIndex. See bug 597315.
- let history = webNavigation.sessionHistory.legacySHistory;
- history.reloadCurrentEntry();
- } else {
- // If there's nothing to restore, we should still blank the page.
- let loadURIOptions = {
- triggeringPrincipal:
- Services.scriptSecurityManager.getSystemPrincipal(),
- loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
- // Specify an override to force the load to finish in the current
- // process, as tests rely on this behaviour for non-fission session
- // restore.
- remoteTypeOverride: Services.appinfo.remoteType,
- };
- webNavigation.loadURI(
- Services.io.newURI("about:blank"),
- loadURIOptions
- );
- }
-
- return true;
- } catch (ex) {
- if (ex instanceof Ci.nsIException) {
- // Ignore page load errors, but return false to signal that the load never
- // happened.
- return false;
- }
- }
- return null;
- },
-
- /**
- * To be called after restoreHistory(). Removes all listeners needed for
- * pending tabs and makes sure to notify when the tab finished loading.
- */
- restoreTabContentStarted(finishCallback) {
- // The reload listener is no longer needed.
- this._historyListener.uninstall();
- this._historyListener = null;
-
- // Remove the old progress listener.
- this._progressListener.uninstall();
-
- // We're about to start a load. This listener will be called when the load
- // has finished getting everything from the network.
- this._progressListener = new ProgressListener(this.docShell, {
- onStopRequest: () => {
- // Call resetRestore() to reset the state back to normal. The data
- // needed for restoreDocument() (which hasn't happened yet) will
- // remain in _restoringDocument.
- this.resetRestore();
-
- finishCallback();
- },
- });
- },
-
- /**
- * Finish restoring the tab by filling in form data and setting the scroll
- * position. The restore is complete when this function exits. It should be
- * called when the "load" event fires for the restoring tab. Returns true
- * if we're restoring a document.
- */
- restoreDocument() {
- if (!this._restoringDocument) {
- return;
- }
-
- let { formdata, scrollPositions } = this._restoringDocument;
- this._restoringDocument = null;
-
- let window = this.docShell.domWindow;
-
- // Restore form data.
- lazy.Utils.restoreFrameTreeData(window, formdata, (frame, data) => {
- // restore() will return false, and thus abort restoration for the
- // current |frame| and its descendants, if |data.url| is given but
- // doesn't match the loaded document's URL.
- return SessionStoreUtils.restoreFormData(frame.document, data);
- });
-
- // Restore scroll data.
- lazy.Utils.restoreFrameTreeData(window, scrollPositions, (frame, data) => {
- if (data.scroll) {
- SessionStoreUtils.restoreScrollPosition(frame, data);
- }
- });
- },
-
- /**
- * Cancel an ongoing restore. This function can be called any time between
- * restoreHistory and restoreDocument.
- *
- * This function is called externally (if a restore is canceled) and
- * internally (when the loads for a restore have finished). In the latter
- * case, it's called before restoreDocument, so it cannot clear
- * _restoringDocument.
- */
- resetRestore() {
- this._tabData = null;
-
- if (this._historyListener) {
- this._historyListener.uninstall();
- }
- this._historyListener = null;
-
- if (this._progressListener) {
- this._progressListener.uninstall();
- }
- this._progressListener = null;
- },
-};
-
-/*
- * This listener detects when a page being restored is reloaded. It triggers a
- * callback and cancels the reload. The callback will send a message to
- * SessionStore.sys.mjs so that it can restore the content immediately.
- */
-function HistoryListener(docShell, callback) {
- let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
- webNavigation.sessionHistory.legacySHistory.addSHistoryListener(this);
-
- this.webNavigation = webNavigation;
- this.callback = callback;
-}
-HistoryListener.prototype = {
- QueryInterface: ChromeUtils.generateQI([
- "nsISHistoryListener",
- "nsISupportsWeakReference",
- ]),
-
- uninstall() {
- let shistory = this.webNavigation.sessionHistory.legacySHistory;
- if (shistory) {
- shistory.removeSHistoryListener(this);
- }
- },
-
- OnHistoryGotoIndex() {},
- OnHistoryPurge() {},
- OnHistoryReplaceEntry() {},
-
- // This will be called for a pending tab when loadURI(uri) is called where
- // the given |uri| only differs in the fragment.
- OnHistoryNewEntry(newURI) {
- let currentURI = this.webNavigation.currentURI;
-
- // Ignore new SHistory entries with the same URI as those do not indicate
- // a navigation inside a document by changing the #hash part of the URL.
- // We usually hit this when purging session history for browsers.
- if (currentURI && currentURI.spec == newURI.spec) {
- return;
- }
-
- // Reset the tab's URL to what it's actually showing. Without this loadURI()
- // would use the current document and change the displayed URL only.
- this.webNavigation.setCurrentURIForSessionStore(
- Services.io.newURI("about:blank")
- );
-
- // Kick off a new load so that we navigate away from about:blank to the
- // new URL that was passed to loadURI(). The new load will cause a
- // STATE_START notification to be sent and the ProgressListener will then
- // notify the parent and do the rest.
- let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
- let loadURIOptions = {
- triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
- loadFlags,
- };
- this.webNavigation.loadURI(newURI, loadURIOptions);
- },
-
- OnHistoryReload() {
- this.callback();
-
- // Cancel the load.
- return false;
- },
-};
-
-/**
- * This class informs SessionStore.sys.mjs whenever the network requests for a
- * restoring page have completely finished. We only restore three tabs
- * simultaneously, so this is the signal for SessionStore.sys.mjs to kick off
- * another restore (if there are more to do).
- *
- * The progress listener is also used to be notified when a load not initiated
- * by sessionstore starts. Pending tabs will then need to be marked as no
- * longer pending.
- */
-function ProgressListener(docShell, callbacks) {
- let webProgress = docShell
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
- webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
-
- this.webProgress = webProgress;
- this.callbacks = callbacks;
-}
-
-ProgressListener.prototype = {
- QueryInterface: ChromeUtils.generateQI([
- "nsIWebProgressListener",
- "nsISupportsWeakReference",
- ]),
-
- uninstall() {
- this.webProgress.removeProgressListener(this);
- },
-
- onStateChange(webProgress, request, stateFlags, status) {
- let { STATE_IS_WINDOW, STATE_STOP, STATE_START } =
- Ci.nsIWebProgressListener;
- if (!webProgress.isTopLevel || !(stateFlags & STATE_IS_WINDOW)) {
- return;
- }
-
- if (stateFlags & STATE_START && this.callbacks.onStartRequest) {
- this.callbacks.onStartRequest();
- }
-
- if (stateFlags & STATE_STOP && this.callbacks.onStopRequest) {
- this.callbacks.onStopRequest();
- }
- },
-};