/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); // Some tests here assume that all restored tabs are loaded without waiting for // the user to bring them to the foreground. We ensure this by resetting the // related preference (see the "firefox.js" defaults file for details). Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false); registerCleanupFunction(function () { Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); }); // This kicks off the search service used on about:home and allows the // session restore tests to be run standalone without triggering errors. Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs; function provideWindow(aCallback, aURL, aFeatures) { function callbackSoon(aWindow) { executeSoon(function executeCallbackSoon() { aCallback(aWindow); }); } let win = openDialog(getBrowserURL(), "", aFeatures || "chrome,all,dialog=no", aURL); whenWindowLoaded(win, function onWindowLoaded(aWin) { if (!aURL) { info("Loaded a blank window."); callbackSoon(aWin); return; } aWin.gBrowser.selectedBrowser.addEventListener("load", function selectedBrowserLoadListener() { aWin.gBrowser.selectedBrowser.removeEventListener("load", selectedBrowserLoadListener, true); callbackSoon(aWin); }, true); }); } // This assumes that tests will at least have some state/entries function waitForBrowserState(aState, aSetStateCallback) { let windows = [window]; let tabsRestored = 0; let expectedTabsRestored = 0; let expectedWindows = aState.windows.length; let windowsOpen = 1; let listening = false; let windowObserving = false; let restoreHiddenTabs = Services.prefs.getBoolPref( "browser.sessionstore.restore_hidden_tabs"); aState.windows.forEach(function (winState) { winState.tabs.forEach(function (tabState) { if (restoreHiddenTabs || !tabState.hidden) expectedTabsRestored++; }); }); // There must be only hidden tabs and restoreHiddenTabs = false. We still // expect one of them to be restored because it gets shown automatically. if (!expectedTabsRestored) expectedTabsRestored = 1; function onSSTabRestored(aEvent) { if (++tabsRestored == expectedTabsRestored) { // Remove the event listener from each window windows.forEach(function(win) { win.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true); }); listening = false; info("running " + aSetStateCallback.name); executeSoon(aSetStateCallback); } } // Used to add our listener to further windows so we can catch SSTabRestored // coming from them when creating a multi-window state. function windowObserver(aSubject, aTopic, aData) { if (aTopic == "domwindowopened") { let newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow); newWindow.addEventListener("load", function() { newWindow.removeEventListener("load", arguments.callee, false); if (++windowsOpen == expectedWindows) { Services.ww.unregisterNotification(windowObserver); windowObserving = false; } // Track this window so we can remove the progress listener later windows.push(newWindow); // Add the progress listener newWindow.gBrowser.tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true); }, false); } } // We only want to register the notification if we expect more than 1 window if (expectedWindows > 1) { registerCleanupFunction(function() { if (windowObserving) { Services.ww.unregisterNotification(windowObserver); } }); windowObserving = true; Services.ww.registerNotification(windowObserver); } registerCleanupFunction(function() { if (listening) { windows.forEach(function(win) { win.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true); }); } }); // Add the event listener for this window as well. listening = true; gBrowser.tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true); // Finally, call setBrowserState ss.setBrowserState(JSON.stringify(aState)); } // Doesn't assume that the tab needs to be closed in a cleanup function. // If that's the case, the test author should handle that in the test. function waitForTabState(aTab, aState, aCallback) { let listening = true; function onSSTabRestored() { aTab.removeEventListener("SSTabRestored", onSSTabRestored, false); listening = false; aCallback(); } aTab.addEventListener("SSTabRestored", onSSTabRestored, false); registerCleanupFunction(function() { if (listening) { aTab.removeEventListener("SSTabRestored", onSSTabRestored, false); } }); ss.setTabState(aTab, JSON.stringify(aState)); } // waitForSaveState waits for a state write but not necessarily for the state to // turn dirty. function waitForSaveState(aSaveStateCallback) { let observing = false; let topic = "sessionstore-state-write"; let sessionSaveTimeout = 1000 + Services.prefs.getIntPref("browser.sessionstore.interval"); function removeObserver() { if (!observing) return; Services.obs.removeObserver(observer, topic, false); observing = false; } let timeout = setTimeout(function () { removeObserver(); aSaveStateCallback(); }, sessionSaveTimeout); function observer(aSubject, aTopic, aData) { removeObserver(); timeout = clearTimeout(timeout); executeSoon(aSaveStateCallback); } registerCleanupFunction(function() { removeObserver(); if (timeout) { clearTimeout(timeout); } }); observing = true; Services.obs.addObserver(observer, topic, false); }; function whenBrowserLoaded(aBrowser, aCallback) { aBrowser.addEventListener("load", function onLoad() { aBrowser.removeEventListener("load", onLoad, true); executeSoon(aCallback); }, true); } function whenWindowLoaded(aWindow, aCallback) { aWindow.addEventListener("load", function windowLoadListener() { aWindow.removeEventListener("load", windowLoadListener, false); executeSoon(function executeWhenWindowLoaded() { aCallback(aWindow); }); }, false); } var gUniqueCounter = 0; function r() { return Date.now() + "-" + (++gUniqueCounter); }