forked from mirrors/gecko-dev
NOTE: The '__SSi' and '__SS_lastSessionWindowID' properties on windows are kept,
because they are expected to stick around longer during application shutdown.
The benefits is are:
1. Cleaner code - Sessionstore implementation details are not leaked outside its
module.
2. Observing the lifetime of objects becomes unnecessary, because the WeakMaps are
cleaned up when objects are GC'd, making leakage of their references impossible
and Sessionstore can't hold objects hostage anymore.
3. Simplification - all state is now maintained in SessionStore.jsm, which allows
for additional refactoring later on to simplify the implementation further.
MozReview-Commit-ID: C1II8qHkQ6F
--HG--
extra : rebase_source : e5fc6984558bd455a33e275f7060d42c93c21720
112 lines
3.6 KiB
JavaScript
112 lines
3.6 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
var windowState = {windows: [{tabs: [
|
|
{entries: [{url: "http://example.com#1", triggeringPrincipal_base64}]},
|
|
{entries: [{url: "http://example.com#2", triggeringPrincipal_base64}]},
|
|
{entries: [{url: "http://example.com#3", triggeringPrincipal_base64}]},
|
|
{entries: [{url: "http://example.com#4", triggeringPrincipal_base64}]},
|
|
{entries: [{url: "http://example.com#5", triggeringPrincipal_base64}], hidden: true},
|
|
{entries: [{url: "http://example.com#6", triggeringPrincipal_base64}], hidden: true},
|
|
{entries: [{url: "http://example.com#7", triggeringPrincipal_base64}], hidden: true},
|
|
{entries: [{url: "http://example.com#8", triggeringPrincipal_base64}], hidden: true}
|
|
]}]};
|
|
|
|
function test() {
|
|
waitForExplicitFinish();
|
|
requestLongerTimeout(2);
|
|
|
|
registerCleanupFunction(function() {
|
|
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
|
|
});
|
|
|
|
// First stage: restoreHiddenTabs = true
|
|
// Second stage: restoreHiddenTabs = false
|
|
test_loadTabs(true, function() {
|
|
test_loadTabs(false, finish);
|
|
});
|
|
}
|
|
|
|
function test_loadTabs(restoreHiddenTabs, callback) {
|
|
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", restoreHiddenTabs);
|
|
|
|
let expectedTabs = restoreHiddenTabs ? 8 : 4;
|
|
let firstProgress = true;
|
|
|
|
newWindowWithState(windowState, function(win, needsRestore, isRestoring) {
|
|
if (firstProgress) {
|
|
firstProgress = false;
|
|
is(isRestoring, 3, "restoring 3 tabs concurrently");
|
|
} else {
|
|
ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
|
|
}
|
|
|
|
// We're explicity checking for (isRestoring == 1) here because the test
|
|
// progress listener is called before the session store one. So when we're
|
|
// called with one tab left to restore we know that the last tab has
|
|
// finished restoring and will soon be handled by the SS listener.
|
|
let tabsNeedingRestore = win.gBrowser.tabs.length - needsRestore;
|
|
if (isRestoring == 1 && tabsNeedingRestore == expectedTabs) {
|
|
is(win.gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
|
|
|
|
TabsProgressListener.uninit();
|
|
executeSoon(callback);
|
|
}
|
|
});
|
|
}
|
|
|
|
var TabsProgressListener = {
|
|
init(win) {
|
|
this.window = win;
|
|
Services.obs.addObserver(this, "sessionstore-debug-tab-restored");
|
|
},
|
|
|
|
uninit() {
|
|
Services.obs.removeObserver(this, "sessionstore-debug-tab-restored");
|
|
|
|
delete this.window;
|
|
delete this.callback;
|
|
},
|
|
|
|
setCallback(callback) {
|
|
this.callback = callback;
|
|
},
|
|
|
|
observe(browser) {
|
|
TabsProgressListener.onRestored(browser);
|
|
},
|
|
|
|
onRestored(browser) {
|
|
if (this.callback && ss.getInternalObjectState(browser) == TAB_STATE_RESTORING)
|
|
this.callback.apply(null, [this.window].concat(this.countTabs()));
|
|
},
|
|
|
|
countTabs() {
|
|
let needsRestore = 0, isRestoring = 0;
|
|
|
|
for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
|
|
let state = ss.getInternalObjectState(this.window.gBrowser.tabs[i].linkedBrowser);
|
|
if (state == TAB_STATE_RESTORING)
|
|
isRestoring++;
|
|
else if (state == TAB_STATE_NEEDS_RESTORE)
|
|
needsRestore++;
|
|
}
|
|
|
|
return [needsRestore, isRestoring];
|
|
}
|
|
};
|
|
|
|
// ----------
|
|
function newWindowWithState(state, callback) {
|
|
let opts = "chrome,all,dialog=no,height=800,width=800";
|
|
let win = window.openDialog(getBrowserURL(), "_blank", opts);
|
|
|
|
registerCleanupFunction(() => BrowserTestUtils.closeWindow(win));
|
|
|
|
whenWindowLoaded(win, function onWindowLoaded(aWin) {
|
|
TabsProgressListener.init(aWin);
|
|
TabsProgressListener.setCallback(callback);
|
|
|
|
ss.setWindowState(aWin, JSON.stringify(state), true);
|
|
});
|
|
}
|