mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
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:
parent
140a416459
commit
013e325e7d
@ -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() {
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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() {}
|
||||
|
Loading…
Reference in New Issue
Block a user