diff options
Diffstat (limited to 'browser/components/sessionstore/ContentRestore.jsm')
-rw-r--r-- | browser/components/sessionstore/ContentRestore.jsm | 434 |
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() {}, -}; |