diff --git a/browser/components/sessionstore/ContentRestore.jsm b/browser/components/sessionstore/ContentRestore.jsm index fafb7dc2e4c9..af8b01283b1a 100644 --- a/browser/components/sessionstore/ContentRestore.jsm +++ b/browser/components/sessionstore/ContentRestore.jsm @@ -53,10 +53,11 @@ function ContentRestore(chromeGlobal) { let EXPORTED_METHODS = [ "restoreHistory", + "finishRestoreHistory", + "restoreOnNewEntry", "restoreTabContent", "restoreDocument", "resetRestore", - "setRestoringDocument", ]; for (let method of EXPORTED_METHODS) { @@ -76,6 +77,11 @@ function ContentRestoreInternal(chromeGlobal) { // restoreTabContent. this._tabData = null; + // To make the sessionRestore work with session history living in the parent process, + // we divided the restoreHistory() into two parts in bug 1507287. + // This is used for the second part to prevent tabData is removed case. + this._tabDataForFinishRestoreHistory = null; + // Contains {entry, scrollPositions, formdata}, where entry is a // single entry from the tabData.entries array. Set in // restoreTabContent and removed in restoreDocument. @@ -90,6 +96,25 @@ function ContentRestoreInternal(chromeGlobal) { // data from the network. Set in restoreHistory() and restoreTabContent(), // removed in resetRestore(). this._progressListener = null; + + this._shistoryInParent = false; +} + +function kickOffNewLoadFromBlankPage(webNavigation, newURI) { + // 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. + webNavigation.setCurrentURI(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, + }; + webNavigation.loadURI(newURI, loadURIOptions); } /** @@ -101,24 +126,13 @@ ContentRestoreInternal.prototype = { return this.chromeGlobal.docShell; }, - setRestoringDocument(data) { - if (!Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should only be used with SHIP"); - } - this._restoringDocument = data; - }, - /** * 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) { - if (Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should be unused with SHIP"); - } - + restoreHistory(tabData, loadArguments, callbacks, shistoryInParent) { this._tabData = tabData; // In case about:blank isn't done yet. @@ -137,16 +151,35 @@ ContentRestoreInternal.prototype = { webNavigation.setCurrentURI(Services.io.newURI(uri)); } - SessionHistory.restore(this.docShell, tabData); + this._shistoryInParent = shistoryInParent; - // Add a listener to watch for reloads. - let listener = new HistoryListener(this.docShell, () => { - // On reload, restore tab contents. - this.restoreTabContent(null, false, callbacks.onLoadFinished); - }); + this._tabDataForFinishRestoreHistory = tabData; + if (this._shistoryInParent) { + callbacks.requestRestoreSHistory(); + } else { + SessionHistory.restore(this.docShell, tabData); - webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener); - this._historyListener = listener; + // Add a listener to watch for reloads. + let listener = new HistoryListener(this.docShell, () => { + // On reload, restore tab contents. + this.restoreTabContent( + null, + false, + callbacks.onLoadFinished, + null, + null + ); + }); + + webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener); + this._historyListener = listener; + this.finishRestoreHistory(callbacks); + } + }, + + finishRestoreHistory(callbacks) { + let tabData = this._tabDataForFinishRestoreHistory; + this._tabDataForFinishRestoreHistory = null; // Make sure to reset the capabilities and attributes in case this tab gets // reused. @@ -169,7 +202,10 @@ ContentRestoreInternal.prototype = { this._tabData = null; // Listen for the tab to finish loading. - this.restoreTabContentStarted(callbacks.onLoadFinished); + this.restoreTabContentStarted( + callbacks.onLoadFinished, + callbacks.removeRestoreListener + ); // Notify the parent. callbacks.onLoadStarted(); @@ -177,22 +213,29 @@ ContentRestoreInternal.prototype = { }); }, + restoreOnNewEntry(newURI) { + let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation); + kickOffNewLoadFromBlankPage(webNavigation, newURI); + }, + /** * 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) { - if (Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should be unused with SHIP"); - } - + restoreTabContent( + loadArguments, + isRemotenessUpdate, + finishCallback, + removeListenerCallback, + reloadSHistoryCallback + ) { let tabData = this._tabData; this._tabData = null; let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation); // Listen for the tab to finish loading. - this.restoreTabContentStarted(finishCallback); + this.restoreTabContentStarted(finishCallback, removeListenerCallback); // 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 @@ -234,8 +277,12 @@ ContentRestoreInternal.prototype = { // 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(); + if (this._shistoryInParent) { + reloadSHistoryCallback(); + } else { + let history = webNavigation.sessionHistory.legacySHistory; + history.reloadCurrentEntry(); + } } else { // If there's nothing to restore, we should still blank the page. let loadURIOptions = { @@ -260,14 +307,14 @@ ContentRestoreInternal.prototype = { * To be called after restoreHistory(). Removes all listeners needed for * pending tabs and makes sure to notify when the tab finished loading. */ - restoreTabContentStarted(finishCallback) { - if (Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should be unused with SHIP"); - } - + restoreTabContentStarted(finishCallback, removeListenerCallback) { // The reload listener is no longer needed. - this._historyListener.uninstall(); - this._historyListener = null; + if (this._shistoryInParent) { + removeListenerCallback(); + } else if (this._historyListener) { + this._historyListener.uninstall(); + this._historyListener = null; + } // Remove the old progress listener. this._progressListener.uninstall(); @@ -289,14 +336,12 @@ ContentRestoreInternal.prototype = { /** * 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. + * called when the "load" event fires for the restoring tab. */ restoreDocument() { if (!this._restoringDocument) { - return false; + return; } - let { formdata, scrollPositions } = this._restoringDocument; this._restoringDocument = null; @@ -316,7 +361,6 @@ ContentRestoreInternal.prototype = { SessionStoreUtils.restoreScrollPosition(frame, data); } }); - return true; }, /** @@ -384,20 +428,7 @@ HistoryListener.prototype = { 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(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.spec, loadURIOptions); + kickOffNewLoadFromBlankPage(this.webNavigation, newURI); }, OnHistoryReload() { diff --git a/browser/components/sessionstore/ContentSessionStore.jsm b/browser/components/sessionstore/ContentSessionStore.jsm index 7adc3a3a3d3c..f7ced766a5de 100644 --- a/browser/components/sessionstore/ContentSessionStore.jsm +++ b/browser/components/sessionstore/ContentSessionStore.jsm @@ -95,16 +95,9 @@ class EventListener extends Handler { } if (this.contentRestoreInitialized) { - // Restore the form data and scroll position (if we're restoring a - // document). - if ( - this.contentRestore.restoreDocument() && - Services.appinfo.sessionHistoryInParent - ) { - this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", { - epoch: this.store.epoch, - }); - } + // Restore the form data and scroll position. If we're not currently + // restoring a tab state then this call will simply be a noop. + this.contentRestore.restoreDocument(); } } } @@ -510,13 +503,14 @@ class MessageQueue extends Handler { */ const MESSAGES = [ "SessionStore:restoreHistory", - "SessionStore:restoreDocShellState", + "SessionStore:finishRestoreHistory", + "SessionStore:OnHistoryReload", + "SessionStore:OnHistoryNewEntry", "SessionStore:restoreTabContent", "SessionStore:resetRestore", "SessionStore:flush", "SessionStore:becomeActiveProcess", "SessionStore:prepareForProcessChange", - "SessionStore:setRestoringDocument", ]; class ContentSessionStore { @@ -528,13 +522,18 @@ class ContentSessionStore { this.contentRestoreInitialized = false; + this.waitRestoreSHistoryInParent = false; + this.restoreTabContentData = null; + XPCOMUtils.defineLazyGetter(this, "contentRestore", () => { this.contentRestoreInitialized = true; return new ContentRestore(mm); }); this.handlers = [new EventListener(this), this.messageQueue]; - if (Services.appinfo.sessionHistoryInParent) { + + this._shistoryInParent = Services.appinfo.sessionHistoryInParent; + if (this._shistoryInParent) { this.mm.sendAsyncMessage("SessionStore:addSHistoryListener"); } else { this.handlers.push(new SessionHistoryListener(this)); @@ -567,11 +566,45 @@ class ContentSessionStore { case "SessionStore:restoreHistory": this.restoreHistory(data); break; - case "SessionStore:restoreDocShellState": - this.restoreDocShellState(data); + case "SessionStore:finishRestoreHistory": + this.finishRestoreHistory(); + break; + case "SessionStore:OnHistoryNewEntry": + this.contentRestore.restoreOnNewEntry(data.uri); + break; + case "SessionStore:OnHistoryReload": + // On reload, restore tab contents. + this.contentRestore.restoreTabContent( + null, + false, + () => { + // Tell SessionStore.jsm that it may want to restore some more tabs, + // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time. + this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", { + epoch: this.epoch, + }); + }, + () => { + // Tell SessionStore.jsm to remove restoreListener. + this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", { + epoch: this.epoch, + }); + }, + () => { + // Tell SessionStore.jsm to reload currentEntry. + this.mm.sendAsyncMessage("SessionStore:reloadCurrentEntry", { + epoch: this.epoch, + }); + } + ); break; case "SessionStore:restoreTabContent": - this.restoreTabContent(data); + if (this.waitRestoreSHistoryInParent) { + // Queue the TabContentData if we haven't finished sHistoryRestore yet. + this.restoreTabContentData = data; + } else { + this.restoreTabContent(data); + } break; case "SessionStore:resetRestore": this.contentRestore.resetRestore(); @@ -580,7 +613,7 @@ class ContentSessionStore { this.flush(data); break; case "SessionStore:becomeActiveProcess": - if (!Services.appinfo.sessionHistoryInParent) { + if (!this._shistoryInParent) { SessionHistoryListener.collect(); } break; @@ -593,40 +626,62 @@ class ContentSessionStore { // parent process. this.mm.docShell.persistLayoutHistoryState(); break; - case "SessionStore:setRestoringDocument": - this.contentRestore.setRestoringDocument(data); - break; default: debug("received unknown message '" + name + "'"); break; } } - // non-SHIP only - restoreHistory(data) { - let { epoch, tabData, loadArguments, isRemotenessUpdate } = data; + restoreHistory({ epoch, tabData, loadArguments, isRemotenessUpdate }) { + this.contentRestore.restoreHistory( + tabData, + loadArguments, + { + // Note: The callbacks passed here will only be used when a load starts + // that was not initiated by sessionstore itself. This can happen when + // some code calls browser.loadURI() or browser.reload() on a pending + // browser/tab. - this.contentRestore.restoreHistory(tabData, loadArguments, { - // Note: The callbacks passed here will only be used when a load starts - // that was not initiated by sessionstore itself. This can happen when - // some code calls browser.loadURI() or browser.reload() on a pending - // browser/tab. + onLoadStarted: () => { + // Notify the parent that the tab is no longer pending. + this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", { + epoch, + }); + }, - onLoadStarted: () => { - // Notify the parent that the tab is no longer pending. - this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", { - epoch, - }); + onLoadFinished: () => { + // Tell SessionStore.jsm that it may want to restore some more tabs, + // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time. + this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", { + epoch, + }); + }, + + removeRestoreListener: () => { + if (!this._shistoryInParent) { + return; + } + + // Notify the parent that the tab is no longer pending. + this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", { + epoch, + }); + }, + + requestRestoreSHistory: () => { + if (!this._shistoryInParent) { + return; + } + + this.waitRestoreSHistoryInParent = true; + // Send tabData to the parent process. + this.mm.sendAsyncMessage("SessionStore:restoreSHistoryInParent", { + epoch, + }); + }, }, - - onLoadFinished: () => { - // Tell SessionStore.jsm that it may want to restore some more tabs, - // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time. - this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", { - epoch, - }); - }, - }); + this._shistoryInParent + ); if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { // For non-remote tabs, when restoreHistory finishes, we send a synchronous @@ -643,7 +698,7 @@ class ContentSessionStore { epoch, isRemotenessUpdate, }); - } else { + } else if (!this._shistoryInParent) { this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", { epoch, isRemotenessUpdate, @@ -651,35 +706,50 @@ class ContentSessionStore { } } - // SHIP only - restoreDocShellState(data) { - let { epoch, tabData } = data; + finishRestoreHistory() { + this.contentRestore.finishRestoreHistory({ + // Note: The callbacks passed here will only be used when a load starts + // that was not initiated by sessionstore itself. This can happen when + // some code calls browser.loadURI() or browser.reload() on a pending + // browser/tab. + onLoadStarted: () => { + // Notify the parent that the tab is no longer pending. + this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", { + epoch: this.epoch, + }); + }, - if (!Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should only be used with SHIP"); - } - let { docShell } = this.mm; + onLoadFinished: () => { + // Tell SessionStore.jsm that it may want to restore some more tabs, + // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time. + this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", { + epoch: this.epoch, + }); + }, - if (tabData.uri) { - docShell.setCurrentURI(Services.io.newURI(tabData.uri)); - } + removeRestoreListener: () => { + if (!this._shistoryInParent) { + return; + } - if (tabData.disallow) { - SessionStoreUtils.restoreDocShellCapabilities(docShell, tabData.disallow); - } + // Notify the parent that the tab is no longer pending. + this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", { + epoch: this.epoch, + }); + }, + }); - if (tabData.storage) { - SessionStoreUtils.restoreSessionStorage(docShell, tabData.storage); + this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", { + epoch: this.epoch, + }); + if (this.restoreTabContentData) { + this.restoreTabContent(this.restoreTabContentData); + this.restoreTabContentData = null; } - // Since we don't send restoreHistory, we need to tell the parent when - // to call SSTabRestoring (via restoreHistoryComplete) - this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", { epoch }); + this.waitRestoreSHistoryInParent = false; } restoreTabContent({ loadArguments, isRemotenessUpdate, reason }) { - if (Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should be unused with SHIP"); - } let epoch = this.epoch; // We need to pass the value of didStartLoad back to SessionStore.jsm. @@ -693,6 +763,17 @@ class ContentSessionStore { epoch, isRemotenessUpdate, }); + }, + () => { + // Tell SessionStore.jsm to remove restore listener. + this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", { + epoch, + }); + }, + () => { + this.mm.sendAsyncMessage("SessionStore:reloadCurrentEntry", { + epoch, + }); } ); diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 16e5f374aed3..e9f4ba52cd95 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -99,6 +99,18 @@ const MESSAGES = [ // The content script asks us to add the session history listener in the // parent process when sessionHistory is in the parent process. "SessionStore:addSHistoryListener", + + // The content script asks us to remove the session history listener which + // is added in the restore process when sessionHistory is in the parent process. + "SessionStore:removeRestoreListener", + + // The content script asks us to restore session history in the parent process + // when sessionHistory is in the parent process. + "SessionStore:restoreSHistoryInParent", + + // The content script asks us to reload the current session history entry when + // sessionHistory is in the parent process. + "SessionStore:reloadCurrentEntry", ]; // The list of messages we accept from s that have no tab @@ -543,10 +555,6 @@ var SessionStoreInternal = { // For each element, records the SHistoryListener. _browserSHistoryListenerForRestore: new WeakMap(), - // For each element that's being restored, holds a web progress - // listener that watches for STATE_START and STATE_STOP events. - _browserProgressListenerForRestore: new WeakMap(), - // The history data needed to be restored in the parent. _shistoryToRestore: new WeakMap(), @@ -987,9 +995,12 @@ var SessionStoreInternal = { // We also need to save the SHistoryLister into this._browserSHistoryListener. addSHistoryListener(aBrowser) { function SHistoryListener(browser) { - browser.browsingContext.sessionHistory.addSHistoryListener(this); + browser.frameLoader.browsingContext.sessionHistory.addSHistoryListener( + this + ); this.browser = browser; + this.frameLoader = browser.frameLoader; this._fromIdx = kNoIndex; this._sHistoryChanges = false; if (this.browser.currentURI && this.browser.ownerGlobal) { @@ -1016,9 +1027,7 @@ var SessionStoreInternal = { } if (!this._sHistoryChanges) { - this.browser.frameLoader.requestSHistoryUpdate( - /*aImmediately*/ false - ); + this.frameLoader.requestSHistoryUpdate(/*aImmediately*/ false); this._sHistoryChanges = true; } this._fromIdx = index; @@ -1030,13 +1039,11 @@ var SessionStoreInternal = { }, uninstall() { - if (this.browser.browsingContext?.sessionHistory) { - this.browser.browsingContext.sessionHistory.removeSHistoryListener( - this - ); - SessionStoreInternal._browserSHistoryListener.delete( - this.browser.permanentKey - ); + if (this.frameLoader.browsingContext) { + let shistory = this.frameLoader.browsingContext.sessionHistory; + if (shistory) { + shistory.removeSHistoryListener(this); + } } }, @@ -1063,16 +1070,34 @@ var SessionStoreInternal = { }, }; - // Don't bother registering another listener if we already have one for this - // browser. We would've already updated the listener's state in response to - // the history event that was triggered by the navigation. - if (this._browserSHistoryListener.has(aBrowser.permanentKey)) { - return; + let spec = null; + if (aBrowser.currentURI) { + spec = aBrowser.currentURI.displaySpec; } - // XXX: When can this happen? - if (!aBrowser.browsingContext?.sessionHistory) { - throw new Error("no SessionHistory object"); + if (!aBrowser.frameLoader) { + debug( + "addSHistoryListener(), aBrowser.frameLoader doesn't exist" + + ",browser.currentURI.displaySpec=" + + spec + ); + return; + } + if (!aBrowser.frameLoader.browsingContext) { + debug( + "addSHistoryListener(), aBrowser.fl.browsingContext doesn't exists" + + ",browser.currentURI.displaySpec=" + + spec + ); + return; + } + if (!aBrowser.frameLoader.browsingContext.sessionHistory) { + debug( + "addSHistoryListener(), aBrowser.fl.bc.sessionHistory doesn't exists" + + ",browser.currentURI.displaySpec=" + + spec + ); + return; } let listener = new SHistoryListener(aBrowser); @@ -1091,14 +1116,12 @@ var SessionStoreInternal = { * callback and cancels the reload. The callback will send a message to * SessionStore.jsm so that it can restore the content immediately. */ - addSHistoryListenerForRestore(aBrowser, aCallbacks) { - if (!Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should only be used with SHIP"); - } - function SHistoryListener(browser, callbacks) { - browser.browsingContext.sessionHistory.addSHistoryListener(this); + addSHistoryListenerForRestore(aBrowser) { + function SHistoryListener(browser) { + browser.frameLoader.browsingContext.sessionHistory.addSHistoryListener( + this + ); this.browser = browser; - this.callbacks = callbacks; } SHistoryListener.prototype = { QueryInterface: ChromeUtils.generateQI([ @@ -1107,13 +1130,10 @@ var SessionStoreInternal = { ]), uninstall() { - let shistory = this.browser.browsingContext?.sessionHistory; + let shistory = this.browser.frameLoader.browsingContext.sessionHistory; if (shistory) { shistory.removeSHistoryListener(this); } - SessionStoreInternal._browserSHistoryListenerForRestore.delete( - this.browser.permanentKey - ); }, OnHistoryGotoIndex() {}, @@ -1132,97 +1152,38 @@ var SessionStoreInternal = { return; } - if (this.callbacks.onHistoryNewEntry) { - this.callbacks.onHistoryNewEntry(newURI); - } + // Notify ContentSessionStore.jsm to restore on new entry. + this.browser.messageManager.sendAsyncMessage( + "SessionStore:OnHistoryNewEntry", + { uri: newURI.spec } + ); }, OnHistoryReload() { - if (this.callbacks.onHistoryReload) { - return this.callbacks.onHistoryReload(); - } + // Notify ContentSessionStore.jsm to restore tab contents. + this.browser.messageManager.sendAsyncMessage( + "SessionStore:OnHistoryReload" + ); + // Cancel the load. return false; }, }; - // XXX: When can this happen? - if (!aBrowser.browsingContext?.sessionHistory) { - throw new Error("no SessionHistory object"); + if ( + !aBrowser.frameLoader || + !aBrowser.frameLoader.browsingContext || + !aBrowser.frameLoader.browsingContext.sessionHistory + ) { + return; } - // Ensure we only have 1 active listener per browser. - if (this._browserSHistoryListenerForRestore.has(aBrowser.permanentKey)) { - this._browserSHistoryListenerForRestore - .get(aBrowser.permanentKey) - .uninstall(); - } - - let listener = new SHistoryListener(aBrowser, aCallbacks); + let listener = new SHistoryListener(aBrowser); this._browserSHistoryListenerForRestore.set( aBrowser.permanentKey, listener ); }, - addProgressListenerForRestore(browser, callbacks) { - if (!Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should only be used with SHIP"); - } - class ProgressListener { - constructor() { - browser.addProgressListener( - this, - Ci.nsIWebProgress.NOTIFY_STATE_WINDOW - ); - } - uninstall() { - browser.removeProgressListener(this); - SessionStoreInternal._browserProgressListenerForRestore.delete( - browser.permanentKey - ); - } - onStateChange(webProgress, request, stateFlags, status) { - if ( - !webProgress.isTopLevel || - !(stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) - ) { - return; - } - - if ( - callbacks.onStartRequest && - stateFlags & Ci.nsIWebProgressListener.STATE_START - ) { - callbacks.onStartRequest(); - this.uninstall(); - } - - if ( - callbacks.onStopRequest && - stateFlags & Ci.nsIWebProgressListener.STATE_STOP - ) { - callbacks.onStopRequest(); - this.uninstall(); - } - } - } - - ProgressListener.prototype.QueryInterface = ChromeUtils.generateQI([ - "nsIWebProgressListener", - "nsISupportsWeakReference", - ]); - - // Ensure we only have 1 listener per browser. - if (this._browserProgressListenerForRestore.has(browser.permanentKey)) { - this._browserProgressListenerForRestore - .get(browser.permanentKey) - .uninstall(); - } - - let listener = new ProgressListener(); - this._browserProgressListenerForRestore.set(browser.permanentKey, listener); - }, - updateSessionStoreFromTablistener(aBrowser, aBrowsingContext, aData) { if (aBrowser.permanentKey == undefined) { return; @@ -1347,6 +1308,31 @@ var SessionStoreInternal = { case "SessionStore:addSHistoryListener": this.addSHistoryListener(browser); break; + case "SessionStore:restoreSHistoryInParent": + if ( + browser.frameLoader && + browser.frameLoader.browsingContext && + browser.frameLoader.browsingContext.sessionHistory + ) { + let tabData = this._shistoryToRestore.get(browser.permanentKey); + if (tabData) { + this._shistoryToRestore.delete(browser.permanentKey); + SessionHistory.restoreFromParent( + browser.frameLoader.browsingContext.sessionHistory, + tabData + ); + } + this.addSHistoryListenerForRestore(browser); + } else { + debug( + "receive SessionStore:restoreSHistoryInParent: but cannot find sessionHistory from bc." + ); + } + browser.messageManager.sendAsyncMessage( + "SessionStore:finishRestoreHistory" + ); + break; + case "SessionStore:update": // |browser.frameLoader| might be empty if the browser was already // destroyed and its tab removed. In that case we still have the last @@ -1360,6 +1346,26 @@ var SessionStoreInternal = { return; } + if (aMessage.data.isFinal) { + // If this the final message we need to resolve all pending flush + // requests for the given browser as they might have been sent too + // late and will never respond. If they have been sent shortly after + // switching a browser's remoteness there isn't too much data to skip. + TabStateFlusher.resolveAll(browser); + let listener = this._browserSHistoryListener.get( + browser.permanentKey + ); + if (listener) { + listener.uninstall(); + this._browserSHistoryListener.delete(browser.permanentKey); + } + } else if (aMessage.data.flushID) { + // This is an update kicked off by an async flush request. Notify the + // TabStateFlusher so that it can finish the request and notify its + // consumer that's waiting for the flush to be done. + TabStateFlusher.resolve(browser, aMessage.data.flushID); + } + // Ignore messages from elements that have crashed // and not yet been revived. if (this._crashedBrowsers.has(browser.permanentKey)) { @@ -1408,39 +1414,169 @@ var SessionStoreInternal = { } } } - if (aMessage.data.isFinal) { - // If this the final message we need to resolve all pending flush - // requests for the given browser as they might have been sent too - // late and will never respond. If they have been sent shortly after - // switching a browser's remoteness there isn't too much data to skip. - TabStateFlusher.resolveAll(browser); + break; + case "SessionStore:restoreHistoryComplete": { + // Notify the tabbrowser that the tab chrome has been restored. + let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab)); - for (let wm of [ - this._browserSHistoryListener, - this._browserSHistoryListenerForRestore, - this._browserProgressListenerForRestore, - ]) { - let listener = wm.get(browser.permanentKey); - if (listener) { - listener.uninstall(); - } - } - } else if (aMessage.data.flushID) { - // This is an update kicked off by an async flush request. Notify the - // TabStateFlusher so that it can finish the request and notify its - // consumer that's waiting for the flush to be done. - TabStateFlusher.resolve(browser, aMessage.data.flushID); + // wall-paper fix for bug 439675: make sure that the URL to be loaded + // is always visible in the address bar if no other value is present + let activePageData = tabData.entries[tabData.index - 1] || null; + let uri = activePageData ? activePageData.url || null : null; + // NB: we won't set initial URIs (about:home, about:newtab, etc.) here + // because their load will not normally trigger a location bar clearing + // when they finish loading (to avoid race conditions where we then + // clear user input instead), so we shouldn't set them here either. + // They also don't fall under the issues in bug 439675 where user input + // needs to be preserved if the load doesn't succeed. + // We also don't do this for remoteness updates, where it should not + // be necessary. + if ( + !browser.userTypedValue && + uri && + !data.isRemotenessUpdate && + !win.gInitialPages.includes(uri) + ) { + browser.userTypedValue = uri; + } + + // Update tab label and icon again after the tab history was updated. + this.updateTabLabelAndIcon(tab, tabData); + + let event = win.document.createEvent("Events"); + event.initEvent("SSTabRestoring", true, false); + tab.dispatchEvent(event); + break; + } + case "SessionStore:removeRestoreListener": + let listener = this._browserSHistoryListenerForRestore.get( + browser.permanentKey + ); + if (listener) { + listener.uninstall(); + this._browserSHistoryListenerForRestore.delete(browser.permanentKey); } break; - case "SessionStore:restoreHistoryComplete": - this._restoreHistoryComplete(browser, data); + case "SessionStore:reloadCurrentEntry": + let fL = + browser.frameLoader || + this._lastKnownFrameLoader.get(browser.permanentKey); + if (fL) { + if (fL.browsingContext) { + if (fL.browsingContext.sessionHistory) { + fL.browsingContext.sessionHistory.reloadCurrentEntry(); + } else { + debug( + "receive SessionStore:reloadCurrentEntry browser.fL.bC.sessionHistory is null." + ); + } + } else { + debug( + "receive SessionStore:reloadCurrentEntry browser.fL.browsingContext is null." + ); + } + } else { + debug( + "receive SessionStore:reloadCurrentEntry browser.frameLoader is null." + ); + } break; - case "SessionStore:restoreTabContentStarted": - this._restoreTabContentStarted(browser, data); + case "SessionStore:restoreTabContentStarted": { + let initiatedBySessionStore = + TAB_STATE_FOR_BROWSER.get(browser) != TAB_STATE_NEEDS_RESTORE; + let isNavigateAndRestore = + data.reason == RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE; + + // We need to be careful when restoring the urlbar's search mode because + // we race a call to gURLBar.setURI due to the location change. setURI + // will exit search mode and set gURLBar.value to the restored URL, + // clobbering any search mode and userTypedValue we restore here. If + // this is a typical restore -- restoring on startup or restoring a + // closed tab for example -- then we need to restore search mode after + // that setURI call, and so we wait until restoreTabContentComplete, at + // which point setURI will have been called. If this is not a typical + // restore -- it was not initiated by session store or it's due to a + // remoteness change -- then we do not want to restore search mode at + // all, and so we remove it from the tab state cache. In particular, if + // the restore is due to a remoteness change, then the user is loading a + // new URL and the current search mode should not be carried over to it. + let cacheState = TabStateCache.get(browser); + if (cacheState.searchMode) { + if (!initiatedBySessionStore || isNavigateAndRestore) { + TabStateCache.update(browser, { + searchMode: null, + userTypedValue: null, + }); + } + break; + } + + if (!initiatedBySessionStore) { + // If a load not initiated by sessionstore was started in a + // previously pending tab. Mark the tab as no longer pending. + this.markTabAsRestoring(tab); + } else if (!isNavigateAndRestore) { + // If the user was typing into the URL bar when we crashed, but hadn't hit + // enter yet, then we just need to write that value to the URL bar without + // loading anything. This must happen after the load, as the load will clear + // userTypedValue. + // + // Note that we only want to do that if we're restoring state for reasons + // _other_ than a navigateAndRestore remoteness-flip, as such a flip + // implies that the user was navigating. + let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab)); + if ( + tabData.userTypedValue && + !tabData.userTypedClear && + !browser.userTypedValue + ) { + browser.userTypedValue = tabData.userTypedValue; + if (tab.selected) { + win.gURLBar.setURI(); + } + } + + // Remove state we don't need any longer. + TabStateCache.update(browser, { + userTypedValue: null, + userTypedClear: null, + }); + } break; - case "SessionStore:restoreTabContentComplete": - this._restoreTabContentComplete(browser, data); + } + case "SessionStore:restoreTabContentComplete": { + // Restore search mode and its search string in userTypedValue, if + // appropriate. + let cacheState = TabStateCache.get(browser); + if (cacheState.searchMode) { + win.gURLBar.setSearchMode(cacheState.searchMode, browser); + browser.userTypedValue = cacheState.userTypedValue; + if (tab.selected) { + win.gURLBar.setURI(); + } + TabStateCache.update(browser, { + searchMode: null, + userTypedValue: null, + }); + } + + // This callback is used exclusively by tests that want to + // monitor the progress of network loads. + if (gDebuggingEnabled) { + Services.obs.notifyObservers(browser, NOTIFY_TAB_RESTORED); + } + + SessionStoreInternal._resetLocalTabRestoringState(tab); + SessionStoreInternal.restoreNextTab(); + + this._sendTabRestoredNotification(tab, data.isRemotenessUpdate); + + Services.obs.notifyObservers( + null, + "sessionstore-one-or-no-tab-restored" + ); break; + } case "SessionStore:crashedTabRevived": // The browser was revived by navigating to a different page // manually, so we remove it from the ignored browser set. @@ -2800,9 +2936,7 @@ var SessionStoreInternal = { // when the user revives the tab from the crash. if (TAB_STATE_FOR_BROWSER.has(browser)) { let tab = win.gBrowser.getTabForBrowser(browser); - if (tab) { - this._resetLocalTabRestoringState(tab); - } + this._resetLocalTabRestoringState(tab); } }, @@ -4678,7 +4812,7 @@ var SessionStoreInternal = { } } - this._sendRestoreTabContent(browser, { + browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent", { loadArguments, isRemotenessUpdate, reason: @@ -5752,287 +5886,6 @@ var SessionStoreInternal = { return deferred; }, - /** - * This mirrors ContentRestore.restoreHistory() for parent process session - * history restores, but we're not actually restoring history here. - */ - async _restoreTabState(browser, data) { - if (!Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should only be used with SHIP"); - } - - // In case about:blank isn't done yet. - browser.stop(); - - let win = browser.ownerGlobal; - let tab = win?.gBrowser.getTabForBrowser(browser); - - browser.messageManager.sendAsyncMessage( - "SessionStore:restoreDocShellState", - { - epoch: data.epoch, - tabData: { - uri: data.tabData?.entries[data.tabData.index - 1]?.url ?? null, - disallow: data.tabData?.disallow, - storage: data.tabData?.storage, - }, - } - ); - // For the non-remote case, the above will restore, but asynchronously. - // However, we need to restore DocShellState synchronously in the - // parent to avoid a test failure. - if (tab.linkedBrowser.docShell) { - SessionStoreUtils.restoreDocShellCapabilities( - tab.linkedBrowser.docShell, - data.tabData.disallow - ); - } - - if (data.tabData?.storage) { - delete data.tabData.storage; - } - - this._shistoryToRestore.set(browser.permanentKey, data); - - SessionHistory.restoreFromParent( - browser.browsingContext.sessionHistory, - data.tabData - ); - - this.addSHistoryListenerForRestore(browser, { - onHistoryReload: () => { - this._restoreTabContent(browser); - return false; - }, - }); - }, - - /** - * This mirrors ContentRestore.restoreTabContent() for parent process session - * history restores. - */ - _restoreTabContent(browser, options) { - if (!Services.appinfo.sessionHistoryInParent) { - throw new Error("This function should only be used with SHIP"); - } - - let listener = this._browserProgressListenerForRestore.get( - browser.permanentKey - ); - if (listener) { - listener.uninstall(); - } - - listener = this._browserSHistoryListenerForRestore.get( - browser.permanentKey - ); - if (listener) { - listener.uninstall(); - } - - let restoreData = { - ...this._shistoryToRestore.get(browser.permanentKey), - ...options, - }; - this._shistoryToRestore.delete(browser.permanentKey); - - this._restoreTabContentStarted(browser, restoreData); - - let { tabData } = restoreData; - - let uri = "about:blank"; - let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY; - - if (tabData?.userTypedValue && tabData?.userTypedClear) { - uri = tabData.userTypedValue; - loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; - } else if (tabData?.entries.length) { - uri = loadFlags = null; - browser.messageManager.sendAsyncMessage( - "SessionStore:setRestoringDocument", - { - entry: tabData.entries[tabData.index - 1] || {}, - formdata: tabData.formdata || {}, - scrollPositions: tabData.scroll || {}, - } - ); - browser.browsingContext.sessionHistory.reloadCurrentEntry(); - } - - if (uri && loadFlags) { - // We only create this listener if we're *not* expecting a reply from - // content. OTherwise we'll call _restoreTabContentComplete too early, and - // fire events before the restore has actually completed. - // - // XXX: If this causes problems, we may be able to just update tests that - // rely on the existing timing to wait for the load event instead. - this.addProgressListenerForRestore(browser, { - onStopRequest: () => { - this._restoreTabContentComplete(browser, restoreData); - }, - }); - - browser.browsingContext.loadURI(uri, { - loadFlags, - triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), - }); - } - }, - - _sendRestoreTabContent(browser, options) { - if (Services.appinfo.sessionHistoryInParent) { - this._restoreTabContent(browser, options); - } else { - browser.messageManager.sendAsyncMessage( - "SessionStore:restoreTabContent", - options - ); - } - }, - - _restoreHistoryComplete(browser, data) { - let win = browser.ownerGlobal; - let tab = win?.gBrowser.getTabForBrowser(browser); - if (!tab) { - return; - } - - // Notify the tabbrowser that the tab chrome has been restored. - let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab)); - - // wall-paper fix for bug 439675: make sure that the URL to be loaded - // is always visible in the address bar if no other value is present - let activePageData = tabData.entries[tabData.index - 1] || null; - let uri = activePageData ? activePageData.url || null : null; - // NB: we won't set initial URIs (about:home, about:newtab, etc.) here - // because their load will not normally trigger a location bar clearing - // when they finish loading (to avoid race conditions where we then - // clear user input instead), so we shouldn't set them here either. - // They also don't fall under the issues in bug 439675 where user input - // needs to be preserved if the load doesn't succeed. - // We also don't do this for remoteness updates, where it should not - // be necessary. - if ( - !browser.userTypedValue && - uri && - !data.isRemotenessUpdate && - !win.gInitialPages.includes(uri) - ) { - browser.userTypedValue = uri; - } - - // Update tab label and icon again after the tab history was updated. - this.updateTabLabelAndIcon(tab, tabData); - - let event = win.document.createEvent("Events"); - event.initEvent("SSTabRestoring", true, false); - tab.dispatchEvent(event); - }, - - _restoreTabContentStarted(browser, data) { - let win = browser.ownerGlobal; - let tab = win?.gBrowser.getTabForBrowser(browser); - if (!tab) { - return; - } - - let initiatedBySessionStore = - TAB_STATE_FOR_BROWSER.get(browser) != TAB_STATE_NEEDS_RESTORE; - let isNavigateAndRestore = - data.reason == RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE; - - // We need to be careful when restoring the urlbar's search mode because - // we race a call to gURLBar.setURI due to the location change. setURI - // will exit search mode and set gURLBar.value to the restored URL, - // clobbering any search mode and userTypedValue we restore here. If - // this is a typical restore -- restoring on startup or restoring a - // closed tab for example -- then we need to restore search mode after - // that setURI call, and so we wait until restoreTabContentComplete, at - // which point setURI will have been called. If this is not a typical - // restore -- it was not initiated by session store or it's due to a - // remoteness change -- then we do not want to restore search mode at - // all, and so we remove it from the tab state cache. In particular, if - // the restore is due to a remoteness change, then the user is loading a - // new URL and the current search mode should not be carried over to it. - let cacheState = TabStateCache.get(browser); - if (cacheState.searchMode) { - if (!initiatedBySessionStore || isNavigateAndRestore) { - TabStateCache.update(browser, { - searchMode: null, - userTypedValue: null, - }); - } - return; - } - - if (!initiatedBySessionStore) { - // If a load not initiated by sessionstore was started in a - // previously pending tab. Mark the tab as no longer pending. - this.markTabAsRestoring(tab); - } else if (!isNavigateAndRestore) { - // If the user was typing into the URL bar when we crashed, but hadn't hit - // enter yet, then we just need to write that value to the URL bar without - // loading anything. This must happen after the load, as the load will clear - // userTypedValue. - // - // Note that we only want to do that if we're restoring state for reasons - // _other_ than a navigateAndRestore remoteness-flip, as such a flip - // implies that the user was navigating. - let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab)); - if ( - tabData.userTypedValue && - !tabData.userTypedClear && - !browser.userTypedValue - ) { - browser.userTypedValue = tabData.userTypedValue; - if (tab.selected) { - win.gURLBar.setURI(); - } - } - - // Remove state we don't need any longer. - TabStateCache.update(browser, { - userTypedValue: null, - userTypedClear: null, - }); - } - }, - - _restoreTabContentComplete(browser, data) { - let win = browser.ownerGlobal; - let tab = browser.ownerGlobal?.gBrowser.getTabForBrowser(browser); - if (!tab) { - return; - } - // Restore search mode and its search string in userTypedValue, if - // appropriate. - let cacheState = TabStateCache.get(browser); - if (cacheState.searchMode) { - win.gURLBar.setSearchMode(cacheState.searchMode, browser); - browser.userTypedValue = cacheState.userTypedValue; - if (tab.selected) { - win.gURLBar.setURI(); - } - TabStateCache.update(browser, { - searchMode: null, - userTypedValue: null, - }); - } - - // This callback is used exclusively by tests that want to - // monitor the progress of network loads. - if (gDebuggingEnabled) { - Services.obs.notifyObservers(browser, NOTIFY_TAB_RESTORED); - } - - SessionStoreInternal._resetLocalTabRestoringState(tab); - SessionStoreInternal.restoreNextTab(); - - this._sendTabRestoredNotification(tab, data.isRemotenessUpdate); - - Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored"); - }, - /** * Send the "SessionStore:restoreHistory" message to content, triggering a * content restore. This method is intended to be used internally by @@ -6068,15 +5921,16 @@ var SessionStoreInternal = { } } - if (Services.appinfo.sessionHistoryInParent) { - this._restoreTabState(browser, options); - } else { - browser.messageManager.sendAsyncMessage( - "SessionStore:restoreHistory", - options - ); + if (this._shistoryInParent) { + // Save the history data for restoring in the parent process. + this._shistoryToRestore.set(browser.permanentKey, options.tabData); } + browser.messageManager.sendAsyncMessage( + "SessionStore:restoreHistory", + options + ); + if (browser && browser.frameLoader) { browser.frameLoader.requestEpochUpdate(options.epoch); } diff --git a/browser/components/sessionstore/test/browser.ini b/browser/components/sessionstore/test/browser.ini index dbdd474738e3..eb3f33f18d63 100644 --- a/browser/components/sessionstore/test/browser.ini +++ b/browser/components/sessionstore/test/browser.ini @@ -91,7 +91,7 @@ skip-if = (verify && debug && (os == 'linux')) [browser_capabilities.js] [browser_cleaner.js] [browser_crashedTabs.js] -skip-if = !e10s || !crashreporter || verify || fission # Bug 1679856 +skip-if = !e10s || !crashreporter || verify || sessionHistoryInParent [browser_pinned_tabs.js] skip-if = debug || ccov # Bug 1625525 [browser_unrestored_crashedTabs.js] @@ -102,10 +102,10 @@ skip-if = !e10s || !crashreporter skip-if = (os == 'win') # bug 1331853 [browser_dynamic_frames.js] [browser_formdata.js] -skip-if = (verify && debug) || fission # bug 1572084 +skip-if = fission || (verify && debug) [browser_formdata_cc.js] [browser_formdata_format.js] -skip-if = (!debug && (os == 'linux')) || fission # Bug 1535645, Bug 1572084 +skip-if = !debug && (os == 'linux') # Bug 1535645 [browser_formdata_password.js] support-files = file_formdata_password.html [browser_formdata_xpath.js] @@ -128,20 +128,22 @@ skip-if = true # Bug 1646894 skip-if = !fission # Bug 1665942, will enable this on non-fission mode in the remaining patches. [browser_restore_cookies_noOriginAttributes.js] [browser_scrollPositions.js] +fail-if = sessionHistoryInParent [browser_scrollPositionsReaderMode.js] [browser_sessionHistory.js] skip-if = tsan # Bug 1651907, highly frequent on TSan support-files = file_sessionHistory_hashchange.html [browser_sessionStorage.js] -skip-if = fission # Bug 1572084 +fail-if = fission [browser_sessionStorage_size.js] [browser_sizemodeBeforeMinimized.js] [browser_tab_label_during_restore.js] [browser_swapDocShells.js] -fail-if = sessionHistoryInParent # Bug 1602501 +fail-if = sessionHistoryInParent [browser_switch_remoteness.js] run-if = e10s +skip-if = sessionHistoryInParent [browser_upgrade_backup.js] skip-if = debug || asan || tsan || (verify && debug && os == 'mac') # Bug 1435394 disabled on Linux, OSX and Windows [browser_windowRestore_perwindowpb.js] @@ -204,7 +206,7 @@ skip-if = (verify && (os == 'mac' || os == 'win')) [browser_586068-cascade.js] [browser_586068-multi_window.js] [browser_586068-reload.js] -skip-if = sessionHistoryInParent # Bug 1668084 +skip-if = sessionHistoryInParent [browser_586068-select.js] [browser_586068-window_state.js] [browser_586068-window_state_override.js] @@ -290,6 +292,7 @@ skip-if = run-if = e10s [browser_background_tab_crash.js] run-if = e10s && crashreporter +skip-if = sessionHistoryInParent # Disabled on debug for frequent intermittent failures: [browser_undoCloseById.js] @@ -308,7 +311,7 @@ skip-if = !crashreporter || !e10s # Tabs can't crash without e10s [browser_cookies_privacy.js] [browser_speculative_connect.js] [browser_1446343-windowsize.js] -skip-if = os == 'linux' # Bug 1600180 +skip-if = (os == 'linux' && os_version == '18.04') # Bug 1600180 [browser_restore_reversed_z_order.js] skip-if = true #Bug 1455602 [browser_cookies_sameSite.js] diff --git a/browser/components/sessionstore/test/browser_394759_behavior.js b/browser/components/sessionstore/test/browser_394759_behavior.js index cf7900bdd78b..5fd83440a3a0 100644 --- a/browser/components/sessionstore/test/browser_394759_behavior.js +++ b/browser/components/sessionstore/test/browser_394759_behavior.js @@ -21,11 +21,9 @@ */ function testWindows(windowsToOpen, expectedResults) { return (async function() { - let num = 0; for (let winData of windowsToOpen) { let features = "chrome,dialog=no," + (winData.isPopup ? "all=no" : "all"); - let url = "http://example.com/?window=" + num; - num = num + 1; + let url = "http://example.com/?window=" + windowsToOpen.length; let openWindowPromise = BrowserTestUtils.waitForNewWindow({ url }); openDialog(AppConstants.BROWSER_CHROME_URL, "", features, url); diff --git a/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js b/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js index 1159f8ef9bcb..c051fc287232 100644 --- a/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js +++ b/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js @@ -168,12 +168,6 @@ add_task(async function run_test() { // Clear cookies. Services.cookies.removeAll(); - // In real usage, the event loop would get to spin between setWindowState - // uses. Without a spin, we can defer handling the STATE_STOP that - // removes the progress listener until after the mozbrowser has been - // destroyed, causing a window leak. - await new Promise(resolve => win.setTimeout(resolve, 0)); - // Restore window with session cookies that have originAttributes within. await setWindowState(win, SESSION_DATA_OA, true); diff --git a/browser/components/sessionstore/test/browser_switch_remoteness.js b/browser/components/sessionstore/test/browser_switch_remoteness.js index 7839033c6a1d..94cf42ad66f2 100644 --- a/browser/components/sessionstore/test/browser_switch_remoteness.js +++ b/browser/components/sessionstore/test/browser_switch_remoteness.js @@ -42,7 +42,7 @@ add_task(async function() { // Load a non-remote page. BrowserTestUtils.loadURI(browser, "about:robots"); - await promiseBrowserLoaded(browser); + await promiseTabRestored(tab); ok(!browser.isRemoteBrowser, "browser is not remote anymore"); // Check that we didn't lose any shistory entries. diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp index d300165b182f..1581cd55c279 100644 --- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -258,11 +258,15 @@ nsSHistory::nsSHistory(BrowsingContext* aRootBC) // Init mHistoryTracker on setting mRootBC so we can bind its event // target to the tabGroup. - mHistoryTracker = mozilla::MakeUnique( - this, - mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS, - CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT), - GetCurrentSerialEventTarget()); + nsPIDOMWindowOuter* win; + if (mRootBC && (win = mRootBC->GetDOMWindow())) { + nsCOMPtr global = do_QueryInterface(win); + mHistoryTracker = mozilla::MakeUnique( + this, + mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS, + CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT), + global->EventTargetFor(mozilla::TaskCategory::Other)); + } } nsSHistory::~nsSHistory() {} diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index cef2ca6d349e..37d7ef874098 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -2519,10 +2519,6 @@ bool nsFrameLoader::TryRemoteBrowserInternal() { return false; } - if (mPendingBrowsingContext->IsTop()) { - mPendingBrowsingContext->InitSessionHistory(); - } - //