summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/ContentRestore.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/sessionstore/ContentRestore.jsm')
-rw-r--r--browser/components/sessionstore/ContentRestore.jsm434
1 files changed, 0 insertions, 434 deletions
diff --git a/browser/components/sessionstore/ContentRestore.jsm b/browser/components/sessionstore/ContentRestore.jsm
deleted file mode 100644
index d4972bcaf..000000000
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ /dev/null
@@ -1,434 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["ContentRestore"];
-
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
-
-XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
- "resource:///modules/sessionstore/DocShellCapabilities.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormData",
- "resource://gre/modules/FormData.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PageStyle",
- "resource:///modules/sessionstore/PageStyle.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ScrollPosition",
- "resource://gre/modules/ScrollPosition.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
- "resource:///modules/sessionstore/SessionHistory.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
- "resource:///modules/sessionstore/SessionStorage.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Utils",
- "resource://gre/modules/sessionstore/Utils.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
- * by content-sessionStore.js based on messages received from SessionStore.jsm
- * (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.jsm, 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.jsm 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.
- */
-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, pageStyle, 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.setCurrentURI(Utils.makeURI(uri));
- }
-
- 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.addSHistoryListener(listener);
- this._historyListener = listener;
-
- // Make sure to reset the capabilities and attributes in case this tab gets
- // reused.
- let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
- DocShellCapabilities.restore(this.docShell, disallow);
-
- if (tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
- SessionStorage.restore(this.docShell, tabData.storage);
- delete tabData.storage;
- }
-
- // 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: function (loadArguments, isRemotenessUpdate, finishCallback) {
- let tabData = this._tabData;
- this._tabData = null;
-
- let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
- let history = webNavigation.sessionHistory;
-
- // 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.setCurrentURI(Utils.makeURI("about:blank"));
- }
-
- try {
- if (loadArguments) {
- // 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.
- let referrer = loadArguments.referrer ?
- Utils.makeURI(loadArguments.referrer) : null;
- let referrerPolicy = ('referrerPolicy' in loadArguments
- ? loadArguments.referrerPolicy
- : Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT);
- let postData = loadArguments.postData ?
- Utils.makeInputStream(loadArguments.postData) : null;
- let triggeringPrincipal = loadArguments.triggeringPrincipal
- ? Utils.deserializePrincipal(loadArguments.triggeringPrincipal)
- : null;
-
- if (loadArguments.userContextId) {
- webNavigation.setOriginAttributesBeforeLoading({ userContextId: loadArguments.userContextId });
- }
-
- webNavigation.loadURIWithOptions(loadArguments.uri, loadArguments.flags,
- referrer, referrerPolicy, postData,
- null, null, triggeringPrincipal);
- } 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.
- webNavigation.loadURI(tabData.userTypedValue,
- Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
- null, null, null);
- } else if (tabData.entries.length) {
- // Stash away the data we need for restoreDocument.
- let activeIndex = tabData.index - 1;
- this._restoringDocument = {entry: tabData.entries[activeIndex] || {},
- formdata: tabData.formdata || {},
- pageStyle: tabData.pageStyle || {},
- 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.
- history.reloadCurrentEntry();
- } else {
- // If there's nothing to restore, we should still blank the page.
- webNavigation.loadURI("about:blank",
- Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
- null, null, null);
- }
-
- 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;
- }
- },
-
- /**
- * 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.
- */
- restoreDocument: function () {
- if (!this._restoringDocument) {
- return;
- }
- let {entry, pageStyle, formdata, scrollPositions} = this._restoringDocument;
- this._restoringDocument = null;
-
- let window = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow);
-
- PageStyle.restoreTree(this.docShell, pageStyle);
- FormData.restoreTree(window, formdata);
- ScrollPosition.restoreTree(window, scrollPositions);
- },
-
- /**
- * 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: function () {
- 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.jsm so that it can restore the content immediately.
- */
-function HistoryListener(docShell, callback) {
- let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
- webNavigation.sessionHistory.addSHistoryListener(this);
-
- this.webNavigation = webNavigation;
- this.callback = callback;
-}
-HistoryListener.prototype = {
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsISHistoryListener,
- Ci.nsISupportsWeakReference
- ]),
-
- uninstall: function () {
- let shistory = this.webNavigation.sessionHistory;
- if (shistory) {
- shistory.removeSHistoryListener(this);
- }
- },
-
- OnHistoryGoBack: function(backURI) { return true; },
- OnHistoryGoForward: function(forwardURI) { return true; },
- OnHistoryGotoIndex: function(index, gotoURI) { return true; },
- OnHistoryPurge: function(numEntries) { return true; },
- OnHistoryReplaceEntry: function(index) {},
-
- // 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.setCurrentURI(Utils.makeURI("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 flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
- this.webNavigation.loadURI(newURI.spec, flags, null, null, null);
- },
-
- OnHistoryReload(reloadURI, reloadFlags) {
- this.callback();
-
- // Cancel the load.
- return false;
- },
-}
-
-/**
- * This class informs SessionStore.jsm whenever the network requests for a
- * restoring page have completely finished. We only restore three tabs
- * simultaneously, so this is the signal for SessionStore.jsm 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: XPCOMUtils.generateQI([
- Ci.nsIWebProgressListener,
- Ci.nsISupportsWeakReference
- ]),
-
- uninstall: function() {
- this.webProgress.removeProgressListener(this);
- },
-
- onStateChange: function(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();
- }
- },
-
- onLocationChange: function() {},
- onProgressChange: function() {},
- onStatusChange: function() {},
- onSecurityChange: function() {},
-};