Bug 1673617 - Refactor how session restore works with SHIP, r=nika

With mods from kashav's last Try run

Differential Revision: https://phabricator.services.mozilla.com/D99461
This commit is contained in:
Randell Jesup 2021-02-01 22:57:14 +00:00
parent 140a416459
commit 013e325e7d
8 changed files with 597 additions and 540 deletions

View File

@ -53,11 +53,10 @@ function ContentRestore(chromeGlobal) {
let EXPORTED_METHODS = [
"restoreHistory",
"finishRestoreHistory",
"restoreOnNewEntry",
"restoreTabContent",
"restoreDocument",
"resetRestore",
"setRestoringDocument",
];
for (let method of EXPORTED_METHODS) {
@ -77,11 +76,6 @@ 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.
@ -96,25 +90,6 @@ 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);
}
/**
@ -126,13 +101,24 @@ 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, shistoryInParent) {
restoreHistory(tabData, loadArguments, callbacks) {
if (Services.appinfo.sessionHistoryInParent) {
throw new Error("This function should be unused with SHIP");
}
this._tabData = tabData;
// In case about:blank isn't done yet.
@ -151,35 +137,16 @@ ContentRestoreInternal.prototype = {
webNavigation.setCurrentURI(Services.io.newURI(uri));
}
this._shistoryInParent = shistoryInParent;
SessionHistory.restore(this.docShell, tabData);
this._tabDataForFinishRestoreHistory = tabData;
if (this._shistoryInParent) {
callbacks.requestRestoreSHistory();
} else {
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);
});
// 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;
webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener);
this._historyListener = listener;
// Make sure to reset the capabilities and attributes in case this tab gets
// reused.
@ -202,10 +169,7 @@ ContentRestoreInternal.prototype = {
this._tabData = null;
// Listen for the tab to finish loading.
this.restoreTabContentStarted(
callbacks.onLoadFinished,
callbacks.removeRestoreListener
);
this.restoreTabContentStarted(callbacks.onLoadFinished);
// Notify the parent.
callbacks.onLoadStarted();
@ -213,29 +177,22 @@ 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,
removeListenerCallback,
reloadSHistoryCallback
) {
restoreTabContent(loadArguments, isRemotenessUpdate, finishCallback) {
if (Services.appinfo.sessionHistoryInParent) {
throw new Error("This function should be unused with SHIP");
}
let tabData = this._tabData;
this._tabData = null;
let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
// Listen for the tab to finish loading.
this.restoreTabContentStarted(finishCallback, removeListenerCallback);
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
@ -277,12 +234,8 @@ 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.
if (this._shistoryInParent) {
reloadSHistoryCallback();
} else {
let history = webNavigation.sessionHistory.legacySHistory;
history.reloadCurrentEntry();
}
let history = webNavigation.sessionHistory.legacySHistory;
history.reloadCurrentEntry();
} else {
// If there's nothing to restore, we should still blank the page.
let loadURIOptions = {
@ -307,15 +260,15 @@ 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, removeListenerCallback) {
// The reload listener is no longer needed.
if (this._shistoryInParent) {
removeListenerCallback();
} else if (this._historyListener) {
this._historyListener.uninstall();
this._historyListener = null;
restoreTabContentStarted(finishCallback) {
if (Services.appinfo.sessionHistoryInParent) {
throw new Error("This function should be unused with SHIP");
}
// The reload listener is no longer needed.
this._historyListener.uninstall();
this._historyListener = null;
// Remove the old progress listener.
this._progressListener.uninstall();
@ -336,12 +289,14 @@ 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.
* called when the "load" event fires for the restoring tab. Returns true
* if we're restoring a document.
*/
restoreDocument() {
if (!this._restoringDocument) {
return;
return false;
}
let { formdata, scrollPositions } = this._restoringDocument;
this._restoringDocument = null;
@ -361,6 +316,7 @@ ContentRestoreInternal.prototype = {
SessionStoreUtils.restoreScrollPosition(frame, data);
}
});
return true;
},
/**
@ -428,7 +384,20 @@ HistoryListener.prototype = {
return;
}
kickOffNewLoadFromBlankPage(this.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.
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);
},
OnHistoryReload() {

View File

@ -95,9 +95,16 @@ class EventListener extends Handler {
}
if (this.contentRestoreInitialized) {
// 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();
// 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,
});
}
}
}
}
@ -501,14 +508,13 @@ class MessageQueue extends Handler {
*/
const MESSAGES = [
"SessionStore:restoreHistory",
"SessionStore:finishRestoreHistory",
"SessionStore:OnHistoryReload",
"SessionStore:OnHistoryNewEntry",
"SessionStore:restoreDocShellState",
"SessionStore:restoreTabContent",
"SessionStore:resetRestore",
"SessionStore:flush",
"SessionStore:becomeActiveProcess",
"SessionStore:prepareForProcessChange",
"SessionStore:setRestoringDocument",
];
class ContentSessionStore {
@ -520,18 +526,13 @@ 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];
this._shistoryInParent = Services.appinfo.sessionHistoryInParent;
if (this._shistoryInParent) {
if (Services.appinfo.sessionHistoryInParent) {
this.mm.sendAsyncMessage("SessionStore:addSHistoryListener");
} else {
this.handlers.push(new SessionHistoryListener(this));
@ -564,45 +565,11 @@ class ContentSessionStore {
case "SessionStore:restoreHistory":
this.restoreHistory(data);
break;
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,
});
}
);
case "SessionStore:restoreDocShellState":
this.restoreDocShellState(data);
break;
case "SessionStore:restoreTabContent":
if (this.waitRestoreSHistoryInParent) {
// Queue the TabContentData if we haven't finished sHistoryRestore yet.
this.restoreTabContentData = data;
} else {
this.restoreTabContent(data);
}
this.restoreTabContent(data);
break;
case "SessionStore:resetRestore":
this.contentRestore.resetRestore();
@ -611,7 +578,7 @@ class ContentSessionStore {
this.flush(data);
break;
case "SessionStore:becomeActiveProcess":
if (!this._shistoryInParent) {
if (!Services.appinfo.sessionHistoryInParent) {
SessionHistoryListener.collect();
}
break;
@ -624,62 +591,43 @@ class ContentSessionStore {
// parent process.
this.mm.docShell.persistLayoutHistoryState();
break;
case "SessionStore:setRestoringDocument":
this.contentRestore.setRestoringDocument(data);
break;
default:
debug("received unknown message '" + name + "'");
break;
}
}
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.
// non-SHIP only
restoreHistory(data) {
let { epoch, tabData, loadArguments, isRemotenessUpdate } = data;
onLoadStarted: () => {
// Notify the parent that the tab is no longer pending.
this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", {
epoch,
});
},
if (Services.appinfo.sessionHistoryInParent) {
throw new Error("This function should be unused with SHIP");
}
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.
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,
});
},
onLoadStarted: () => {
// Notify the parent that the tab is no longer pending.
this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", {
epoch,
});
},
this._shistoryInParent
);
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,
});
},
});
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
// For non-remote tabs, when restoreHistory finishes, we send a synchronous
@ -696,7 +644,7 @@ class ContentSessionStore {
epoch,
isRemotenessUpdate,
});
} else if (!this._shistoryInParent) {
} else {
this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", {
epoch,
isRemotenessUpdate,
@ -704,50 +652,35 @@ class ContentSessionStore {
}
}
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,
});
},
// SHIP only
restoreDocShellState(data) {
let { epoch, tabData } = data;
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,
});
},
removeRestoreListener: () => {
if (!this._shistoryInParent) {
return;
}
// Notify the parent that the tab is no longer pending.
this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", {
epoch: this.epoch,
});
},
});
this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", {
epoch: this.epoch,
});
if (this.restoreTabContentData) {
this.restoreTabContent(this.restoreTabContentData);
this.restoreTabContentData = null;
if (!Services.appinfo.sessionHistoryInParent) {
throw new Error("This function should only be used with SHIP");
}
this.waitRestoreSHistoryInParent = false;
let { docShell } = this.mm;
if (tabData.uri) {
docShell.setCurrentURI(Services.io.newURI(tabData.uri));
}
if (tabData.disallow) {
SessionStoreUtils.restoreDocShellCapabilities(docShell, tabData.disallow);
}
if (tabData.storage) {
SessionStoreUtils.restoreSessionStorage(docShell, tabData.storage);
}
// Since we don't send restoreHistory, we need to tell the parent when
// to call SSTabRestoring (via restoreHistoryComplete)
this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", { epoch });
}
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.
@ -761,17 +694,6 @@ class ContentSessionStore {
epoch,
isRemotenessUpdate,
});
},
() => {
// Tell SessionStore.jsm to remove restore listener.
this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", {
epoch,
});
},
() => {
this.mm.sendAsyncMessage("SessionStore:reloadCurrentEntry", {
epoch,
});
}
);

View File

@ -99,18 +99,6 @@ 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 <xul:browser>s that have no tab
@ -555,6 +543,10 @@ var SessionStoreInternal = {
// For each <browser> element, records the SHistoryListener.
_browserSHistoryListenerForRestore: new WeakMap(),
// For each <browser> 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(),
@ -1042,6 +1034,9 @@ var SessionStoreInternal = {
this.browser.browsingContext.sessionHistory.removeSHistoryListener(
this
);
SessionStoreInternal._browserSHistoryListener.delete(
this.browser.permanentKey
);
}
},
@ -1096,12 +1091,14 @@ 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) {
function SHistoryListener(browser) {
browser.frameLoader.browsingContext.sessionHistory.addSHistoryListener(
this
);
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);
this.browser = browser;
this.callbacks = callbacks;
}
SHistoryListener.prototype = {
QueryInterface: ChromeUtils.generateQI([
@ -1110,10 +1107,13 @@ var SessionStoreInternal = {
]),
uninstall() {
let shistory = this.browser.frameLoader.browsingContext.sessionHistory;
let shistory = this.browser.browsingContext?.sessionHistory;
if (shistory) {
shistory.removeSHistoryListener(this);
}
SessionStoreInternal._browserSHistoryListenerForRestore.delete(
this.browser.permanentKey
);
},
OnHistoryGotoIndex() {},
@ -1132,38 +1132,97 @@ var SessionStoreInternal = {
return;
}
// Notify ContentSessionStore.jsm to restore on new entry.
this.browser.messageManager.sendAsyncMessage(
"SessionStore:OnHistoryNewEntry",
{ uri: newURI.spec }
);
if (this.callbacks.onHistoryNewEntry) {
this.callbacks.onHistoryNewEntry(newURI);
}
},
OnHistoryReload() {
// Notify ContentSessionStore.jsm to restore tab contents.
this.browser.messageManager.sendAsyncMessage(
"SessionStore:OnHistoryReload"
);
// Cancel the load.
if (this.callbacks.onHistoryReload) {
return this.callbacks.onHistoryReload();
}
return false;
},
};
if (
!aBrowser.frameLoader ||
!aBrowser.frameLoader.browsingContext ||
!aBrowser.frameLoader.browsingContext.sessionHistory
) {
return;
// XXX: When can this happen?
if (!aBrowser.browsingContext?.sessionHistory) {
throw new Error("no SessionHistory object");
}
let listener = new SHistoryListener(aBrowser);
// 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);
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;
@ -1288,31 +1347,6 @@ 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
@ -1326,18 +1360,68 @@ var SessionStoreInternal = {
return;
}
// Ignore messages from <browser> elements that have crashed
// and not yet been revived.
if (!this._crashedBrowsers.has(browser.permanentKey)) {
// Update the tab's cached state.
// Mark the window as dirty and trigger a delayed write.
TabState.update(browser, aMessage.data);
this.saveStateDelayed(win);
// Handle any updates sent by the child after the tab was closed. This
// might be the final update as sent by the "unload" handler but also
// any async update message that was sent before the child unloaded.
if (this._closedTabs.has(browser.permanentKey)) {
let { closedTabs, tabData } = this._closedTabs.get(
browser.permanentKey
);
// Update the closed tab's state. This will be reflected in its
// window's list of closed tabs as that refers to the same object.
TabState.copyFromCache(browser, tabData.state);
// Is this the tab's final message?
if (aMessage.data.isFinal) {
// We expect no further updates.
this._closedTabs.delete(browser.permanentKey);
// The tab state no longer needs this reference.
delete tabData.permanentKey;
// Determine whether the tab state is worth saving.
let shouldSave = this._shouldSaveTabState(tabData.state);
let index = closedTabs.indexOf(tabData);
if (shouldSave && index == -1) {
// If the tab state is worth saving and we didn't push it onto
// the list of closed tabs when it was closed (because we deemed
// the state not worth saving) then add it to the window's list
// of closed tabs now.
this.saveClosedTabData(closedTabs, tabData);
} else if (!shouldSave && index > -1) {
// Remove from the list of closed tabs. The update messages sent
// after the tab was closed changed enough state so that we no
// longer consider its data interesting enough to keep around.
this.removeClosedTabData(closedTabs, index);
}
}
}
}
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);
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
@ -1345,218 +1429,16 @@ var SessionStoreInternal = {
// consumer that's waiting for the flush to be done.
TabStateFlusher.resolve(browser, aMessage.data.flushID);
}
// Ignore messages from <browser> elements that have crashed
// and not yet been revived.
if (this._crashedBrowsers.has(browser.permanentKey)) {
return;
}
// Update the tab's cached state.
// Mark the window as dirty and trigger a delayed write.
TabState.update(browser, aMessage.data);
this.saveStateDelayed(win);
// Handle any updates sent by the child after the tab was closed. This
// might be the final update as sent by the "unload" handler but also
// any async update message that was sent before the child unloaded.
if (this._closedTabs.has(browser.permanentKey)) {
let { closedTabs, tabData } = this._closedTabs.get(
browser.permanentKey
);
// Update the closed tab's state. This will be reflected in its
// window's list of closed tabs as that refers to the same object.
TabState.copyFromCache(browser, tabData.state);
// Is this the tab's final message?
if (aMessage.data.isFinal) {
// We expect no further updates.
this._closedTabs.delete(browser.permanentKey);
// The tab state no longer needs this reference.
delete tabData.permanentKey;
// Determine whether the tab state is worth saving.
let shouldSave = this._shouldSaveTabState(tabData.state);
let index = closedTabs.indexOf(tabData);
if (shouldSave && index == -1) {
// If the tab state is worth saving and we didn't push it onto
// the list of closed tabs when it was closed (because we deemed
// the state not worth saving) then add it to the window's list
// of closed tabs now.
this.saveClosedTabData(closedTabs, tabData);
} else if (!shouldSave && index > -1) {
// Remove from the list of closed tabs. The update messages sent
// after the tab was closed changed enough state so that we no
// longer consider its data interesting enough to keep around.
this.removeClosedTabData(closedTabs, index);
}
}
}
break;
case "SessionStore:restoreHistoryComplete": {
// 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);
case "SessionStore:restoreHistoryComplete":
this._restoreHistoryComplete(browser, data);
break;
}
case "SessionStore:removeRestoreListener":
let listener = this._browserSHistoryListenerForRestore.get(
browser.permanentKey
);
if (listener) {
listener.uninstall();
this._browserSHistoryListenerForRestore.delete(browser.permanentKey);
}
case "SessionStore:restoreTabContentStarted":
this._restoreTabContentStarted(browser, data);
break;
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."
);
}
case "SessionStore:restoreTabContentComplete":
this._restoreTabContentComplete(browser, data);
break;
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": {
// 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.
@ -2916,7 +2798,9 @@ var SessionStoreInternal = {
// when the user revives the tab from the crash.
if (TAB_STATE_FOR_BROWSER.has(browser)) {
let tab = win.gBrowser.getTabForBrowser(browser);
this._resetLocalTabRestoringState(tab);
if (tab) {
this._resetLocalTabRestoringState(tab);
}
}
},
@ -4792,7 +4676,7 @@ var SessionStoreInternal = {
}
}
browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent", {
this._sendRestoreTabContent(browser, {
loadArguments,
isRemotenessUpdate,
reason:
@ -5866,6 +5750,287 @@ 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
@ -5901,16 +6066,15 @@ var SessionStoreInternal = {
}
}
if (this._shistoryInParent) {
// Save the history data for restoring in the parent process.
this._shistoryToRestore.set(browser.permanentKey, options.tabData);
if (Services.appinfo.sessionHistoryInParent) {
this._restoreTabState(browser, options);
} else {
browser.messageManager.sendAsyncMessage(
"SessionStore:restoreHistory",
options
);
}
browser.messageManager.sendAsyncMessage(
"SessionStore:restoreHistory",
options
);
if (browser && browser.frameLoader) {
browser.frameLoader.requestEpochUpdate(options.epoch);
}

View File

@ -102,10 +102,10 @@ skip-if = !e10s || !crashreporter
skip-if = (os == 'win') # bug 1331853
[browser_dynamic_frames.js]
[browser_formdata.js]
skip-if = fission || (verify && debug)
skip-if = (verify && debug) || fission # bug 1572084
[browser_formdata_cc.js]
[browser_formdata_format.js]
skip-if = !debug && (os == 'linux') # Bug 1535645
skip-if = (!debug && (os == 'linux')) || fission # Bug 1535645, Bug 1572084
[browser_formdata_password.js]
support-files = file_formdata_password.html
[browser_formdata_xpath.js]
@ -113,6 +113,7 @@ support-files = file_formdata_password.html
[browser_frame_history.js]
[browser_global_store.js]
[browser_history_persist.js]
skip-if = fission # Bug 1690134
[browser_label_and_icon.js]
[browser_merge_closed_tabs.js]
[browser_old_favicon.js]
@ -128,22 +129,20 @@ 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]
fail-if = fission
skip-if = fission # Bug 1572084
[browser_sessionStorage_size.js]
[browser_sizemodeBeforeMinimized.js]
[browser_tab_label_during_restore.js]
[browser_swapDocShells.js]
fail-if = sessionHistoryInParent
fail-if = sessionHistoryInParent # Bug 1602501
[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]
@ -206,7 +205,7 @@ skip-if = (verify && (os == 'mac' || os == 'win'))
[browser_586068-cascade.js]
[browser_586068-multi_window.js]
[browser_586068-reload.js]
skip-if = sessionHistoryInParent
skip-if = sessionHistoryInParent # Bug 1668084
[browser_586068-select.js]
[browser_586068-window_state.js]
[browser_586068-window_state_override.js]
@ -292,7 +291,6 @@ 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]
@ -311,11 +309,11 @@ 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' && os_version == '18.04') # Bug 1600180
skip-if = os == 'linux' # Bug 1600180
[browser_restore_reversed_z_order.js]
skip-if = true #Bug 1455602
[browser_cookies_sameSite.js]
[browser_urlbarSearchMode.js]
[browser_restore_container_tabs_oa.js]
[browser_restore_private_tab_os.js]
[browser_restore_private_tab_os.js]

View File

@ -21,9 +21,11 @@
*/
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=" + windowsToOpen.length;
let url = "http://example.com/?window=" + num;
num = num + 1;
let openWindowPromise = BrowserTestUtils.waitForNewWindow({ url });
openDialog(AppConstants.BROWSER_CHROME_URL, "", features, url);

View File

@ -168,6 +168,12 @@ 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);

View File

@ -42,7 +42,7 @@ add_task(async function() {
// Load a non-remote page.
BrowserTestUtils.loadURI(browser, "about:robots");
await promiseTabRestored(tab);
await promiseBrowserLoaded(browser);
ok(!browser.isRemoteBrowser, "browser is not remote anymore");
// Check that we didn't lose any shistory entries.

View File

@ -258,15 +258,11 @@ nsSHistory::nsSHistory(BrowsingContext* aRootBC)
// Init mHistoryTracker on setting mRootBC so we can bind its event
// target to the tabGroup.
nsPIDOMWindowOuter* win;
if (mRootBC && (win = mRootBC->GetDOMWindow())) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(win);
mHistoryTracker = mozilla::MakeUnique<HistoryTracker>(
this,
mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS,
CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT),
global->EventTargetFor(mozilla::TaskCategory::Other));
}
mHistoryTracker = mozilla::MakeUnique<HistoryTracker>(
this,
mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS,
CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT),
GetCurrentSerialEventTarget());
}
nsSHistory::~nsSHistory() {}