summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/browser_354894_perwindowpb.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/sessionstore/test/browser_354894_perwindowpb.js')
-rw-r--r--browser/components/sessionstore/test/browser_354894_perwindowpb.js489
1 files changed, 489 insertions, 0 deletions
diff --git a/browser/components/sessionstore/test/browser_354894_perwindowpb.js b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
new file mode 100644
index 0000000000..90368536dc
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -0,0 +1,489 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Checks that restoring the last browser window in session is actually
+ * working.
+ *
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894
+ * @note It is implicitly tested that restoring the last window works when
+ * non-browser windows are around. The "Run Tests" window as well as the main
+ * browser window (wherein the test code gets executed) won't be considered
+ * browser windows. To achiveve this said main browser window has its windowtype
+ * attribute modified so that it's not considered a browser window any longer.
+ * This is crucial, because otherwise there would be two browser windows around,
+ * said main test window and the one opened by the tests, and hence the new
+ * logic wouldn't be executed at all.
+ * @note Mac only tests the new notifications, as restoring the last window is
+ * not enabled on that platform (platform shim; the application is kept running
+ * although there are no windows left)
+ * @note There is a difference when closing a browser window with
+ * BrowserTryToCloseWindow() as opposed to close(). The former will make
+ * nsSessionStore restore a window next time it gets a chance and will post
+ * notifications. The latter won't.
+ */
+
+// The rejection "BrowserWindowTracker.getTopWindow(...) is null" is left
+// unhandled in some cases. This bug should be fixed, but for the moment this
+// file allows a class of rejections.
+//
+// NOTE: Allowing a whole class of rejections should be avoided. Normally you
+// should use "expectUncaughtRejection" to flag individual failures.
+const { PromiseTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromiseTestUtils.sys.mjs"
+);
+PromiseTestUtils.allowMatchingRejectionsGlobally(/getTopWindow/);
+
+// Some urls that might be opened in tabs and/or popups
+// Do not use about:blank:
+// That one is reserved for special purposes in the tests
+const TEST_URLS = ["about:mozilla", "about:buildconfig"];
+
+// Number of -request notifications to except
+// remember to adjust when adding new tests
+const NOTIFICATIONS_EXPECTED = 6;
+
+// Window features of popup windows
+const POPUP_FEATURES = "toolbar=no,resizable=no,status=no";
+
+// Window features of browser windows
+const CHROME_FEATURES = "chrome,all,dialog=no";
+
+const IS_MAC = navigator.platform.match(/Mac/);
+
+/**
+ * Returns an Object with two properties:
+ * open (int):
+ * A count of how many non-closed navigator:browser windows there are.
+ * winstates (int):
+ * A count of how many windows there are in the SessionStore state.
+ */
+function getBrowserWindowsCount() {
+ let open = 0;
+ for (let win of Services.wm.getEnumerator("navigator:browser")) {
+ if (!win.closed) {
+ ++open;
+ }
+ }
+
+ let winstates = JSON.parse(ss.getBrowserState()).windows.length;
+
+ return { open, winstates };
+}
+
+add_setup(async function () {
+ // Make sure we've only got one browser window to start with
+ let { open, winstates } = getBrowserWindowsCount();
+ is(open, 1, "Should only be one open window");
+ is(winstates, 1, "Should only be one window state in SessionStore");
+
+ // This test takes some time to run, and it could timeout randomly.
+ // So we require a longer timeout. See bug 528219.
+ requestLongerTimeout(3);
+
+ // Make the main test window not count as a browser window any longer
+ let oldWinType = document.documentElement.getAttribute("windowtype");
+ document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+
+ registerCleanupFunction(() => {
+ document.documentElement.setAttribute("windowtype", oldWinType);
+ });
+});
+
+/**
+ * Sets up one of our tests by setting the right preferences, and
+ * then opening up a browser window preloaded with some tabs.
+ *
+ * @param options (Object)
+ * An object that can contain the following properties:
+ *
+ * private:
+ * Whether or not the opened window should be private.
+ *
+ * denyFirst:
+ * Whether or not the first window that attempts to close
+ * via closeWindowForRestoration should be denied.
+ *
+ * @param testFunction (Function*)
+ * A generator function that yields Promises to be run
+ * once the test has been set up.
+ *
+ * @returns Promise
+ * Resolves once the test has been cleaned up.
+ */
+let setupTest = async function (options, testFunction) {
+ await pushPrefs(
+ ["browser.startup.page", 3],
+ ["browser.tabs.warnOnClose", false]
+ );
+ // SessionStartup caches pref values, but as this test tries to simulate a
+ // startup scenario, we'll reset them here.
+ SessionStartup.resetForTest();
+
+ // Observe these, and also use to count the number of hits
+ let observing = {
+ "browser-lastwindow-close-requested": 0,
+ "browser-lastwindow-close-granted": 0,
+ };
+
+ /**
+ * Helper: Will observe and handle the notifications for us
+ */
+ let hitCount = 0;
+ function observer(aCancel, aTopic, aData) {
+ // count so that we later may compare
+ observing[aTopic]++;
+
+ // handle some tests
+ if (options.denyFirst && ++hitCount == 1) {
+ aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true;
+ }
+ }
+
+ for (let o in observing) {
+ Services.obs.addObserver(observer, o);
+ }
+
+ let newWin = await promiseNewWindowLoaded({
+ private: options.private || false,
+ });
+
+ await injectTestTabs(newWin);
+
+ await testFunction(newWin, observing);
+
+ let count = getBrowserWindowsCount();
+ is(count.open, 0, "Got right number of open windows");
+ is(count.winstates, 1, "Got right number of stored window states");
+
+ for (let o in observing) {
+ Services.obs.removeObserver(observer, o);
+ }
+
+ await popPrefs();
+ // Act like nothing ever happened.
+ SessionStartup.resetForTest();
+};
+
+/**
+ * Loads a TEST_URLS into a browser window.
+ *
+ * @param win (Window)
+ * The browser window to load the tabs in
+ */
+function injectTestTabs(win) {
+ let promises = TEST_URLS.map(url =>
+ BrowserTestUtils.addTab(win.gBrowser, url)
+ ).map(tab => BrowserTestUtils.browserLoaded(tab.linkedBrowser));
+ return Promise.all(promises);
+}
+
+/**
+ * Attempts to close a window via BrowserTryToCloseWindow so that
+ * we get the browser-lastwindow-close-requested and
+ * browser-lastwindow-close-granted observer notifications.
+ *
+ * @param win (Window)
+ * The window to try to close
+ * @returns Promise
+ * Resolves to true if the window closed, or false if the window
+ * was denied the ability to close.
+ */
+function closeWindowForRestoration(win) {
+ return new Promise(resolve => {
+ let closePromise = BrowserTestUtils.windowClosed(win);
+ win.BrowserTryToCloseWindow();
+ if (!win.closed) {
+ resolve(false);
+ return;
+ }
+
+ closePromise.then(() => {
+ resolve(true);
+ });
+ });
+}
+
+/**
+ * Normal in-session restore
+ *
+ * @note: Non-Mac only
+ *
+ * Should do the following:
+ * 1. Open a new browser window
+ * 2. Add some tabs
+ * 3. Close that window
+ * 4. Opening another window
+ * 5. Checks that state is restored
+ */
+add_task(async function test_open_close_normal() {
+ if (IS_MAC) {
+ return;
+ }
+
+ await setupTest({ denyFirst: true }, async function (newWin, obs) {
+ let closed = await closeWindowForRestoration(newWin);
+ ok(!closed, "First close request should have been denied");
+
+ closed = await closeWindowForRestoration(newWin);
+ ok(closed, "Second close request should be accepted");
+
+ newWin = await promiseNewWindowLoaded();
+ is(
+ newWin.gBrowser.browsers.length,
+ TEST_URLS.length + 2,
+ "Restored window in-session with otherpopup windows around"
+ );
+
+ // Note that this will not result in the the browser-lastwindow-close
+ // notifications firing for this other newWin.
+ await BrowserTestUtils.closeWindow(newWin);
+
+ // setupTest gave us a window which was denied for closing once, and then
+ // closed.
+ is(
+ obs["browser-lastwindow-close-requested"],
+ 2,
+ "Got expected browser-lastwindow-close-requested notifications"
+ );
+ is(
+ obs["browser-lastwindow-close-granted"],
+ 1,
+ "Got expected browser-lastwindow-close-granted notifications"
+ );
+ });
+});
+
+/**
+ * PrivateBrowsing in-session restore
+ *
+ * @note: Non-Mac only
+ *
+ * Should do the following:
+ * 1. Open a new browser window A
+ * 2. Add some tabs
+ * 3. Close the window A as the last window
+ * 4. Open a private browsing window B
+ * 5. Make sure that B didn't restore the tabs from A
+ * 6. Close private browsing window B
+ * 7. Open a new window C
+ * 8. Make sure that new window C has restored tabs from A
+ */
+add_task(async function test_open_close_private_browsing() {
+ if (IS_MAC) {
+ return;
+ }
+
+ await setupTest({}, async function (newWin, obs) {
+ let closed = await closeWindowForRestoration(newWin);
+ ok(closed, "Should be able to close the window");
+
+ newWin = await promiseNewWindowLoaded({ private: true });
+ is(
+ newWin.gBrowser.browsers.length,
+ 1,
+ "Did not restore in private browsing mode"
+ );
+
+ closed = await closeWindowForRestoration(newWin);
+ ok(closed, "Should be able to close the window");
+
+ newWin = await promiseNewWindowLoaded();
+ is(
+ newWin.gBrowser.browsers.length,
+ TEST_URLS.length + 2,
+ "Restored tabs in a new non-private window"
+ );
+
+ // Note that this will not result in the the browser-lastwindow-close
+ // notifications firing for this other newWin.
+ await BrowserTestUtils.closeWindow(newWin);
+
+ // We closed two windows with closeWindowForRestoration, and both
+ // should have been successful.
+ is(
+ obs["browser-lastwindow-close-requested"],
+ 2,
+ "Got expected browser-lastwindow-close-requested notifications"
+ );
+ is(
+ obs["browser-lastwindow-close-granted"],
+ 2,
+ "Got expected browser-lastwindow-close-granted notifications"
+ );
+ });
+});
+
+/**
+ * Open some popup window to check it isn't restored. Instead nothing at all
+ * should be restored
+ *
+ * @note: Non-Mac only
+ *
+ * Should do the following:
+ * 1. Open a popup
+ * 2. Add another tab to the popup (so that it gets stored) and close it again
+ * 3. Open a window
+ * 4. Check that nothing at all is restored
+ * 5. Open two browser windows and close them again
+ * 6. undoCloseWindow() one
+ * 7. Open another browser window
+ * 8. Check that nothing at all is restored
+ */
+add_task(async function test_open_close_only_popup() {
+ if (IS_MAC) {
+ return;
+ }
+
+ await setupTest({}, async function (newWin, obs) {
+ // We actually don't care about the initial window in this test.
+ await BrowserTestUtils.closeWindow(newWin);
+
+ // This will cause nsSessionStore to restore a window the next time it
+ // gets a chance.
+ let popupPromise = BrowserTestUtils.waitForNewWindow();
+ openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
+ let popup = await popupPromise;
+
+ is(
+ popup.gBrowser.browsers.length,
+ 1,
+ "Did not restore the popup window (1)"
+ );
+
+ let closed = await closeWindowForRestoration(popup);
+ ok(closed, "Should be able to close the window");
+
+ popupPromise = BrowserTestUtils.waitForNewWindow();
+ openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
+ popup = await popupPromise;
+
+ BrowserTestUtils.addTab(popup.gBrowser, TEST_URLS[0]);
+ is(
+ popup.gBrowser.browsers.length,
+ 2,
+ "Did not restore to the popup window (2)"
+ );
+
+ await BrowserTestUtils.closeWindow(popup);
+
+ newWin = await promiseNewWindowLoaded();
+ isnot(
+ newWin.gBrowser.browsers.length,
+ 2,
+ "Did not restore the popup window"
+ );
+ is(
+ TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec),
+ -1,
+ "Did not restore the popup window (2)"
+ );
+ await BrowserTestUtils.closeWindow(newWin);
+
+ // We closed one popup window with closeWindowForRestoration, and popup
+ // windows should never fire the browser-lastwindow notifications.
+ is(
+ obs["browser-lastwindow-close-requested"],
+ 0,
+ "Got expected browser-lastwindow-close-requested notifications"
+ );
+ is(
+ obs["browser-lastwindow-close-granted"],
+ 0,
+ "Got expected browser-lastwindow-close-granted notifications"
+ );
+ });
+});
+
+/**
+ * Open some windows and do undoCloseWindow. This should prevent any
+ * restoring later in the test
+ *
+ * @note: Non-Mac only
+ *
+ * Should do the following:
+ * 1. Open two browser windows and close them again
+ * 2. undoCloseWindow() one
+ * 3. Open another browser window
+ * 4. Make sure nothing at all is restored
+ */
+add_task(async function test_open_close_restore_from_popup() {
+ if (IS_MAC) {
+ return;
+ }
+
+ await setupTest({}, async function (newWin, obs) {
+ let newWin2 = await promiseNewWindowLoaded();
+ await injectTestTabs(newWin2);
+
+ let closed = await closeWindowForRestoration(newWin);
+ ok(closed, "Should be able to close the window");
+ closed = await closeWindowForRestoration(newWin2);
+ ok(closed, "Should be able to close the window");
+
+ let counts = getBrowserWindowsCount();
+ is(counts.open, 0, "Got right number of open windows");
+ is(counts.winstates, 1, "Got right number of window states");
+
+ newWin = undoCloseWindow(0);
+ await BrowserTestUtils.waitForEvent(newWin, "load");
+
+ // Make sure we wait until this window is restored.
+ await BrowserTestUtils.waitForEvent(
+ newWin.gBrowser.tabContainer,
+ "SSTabRestored"
+ );
+
+ newWin2 = await promiseNewWindowLoaded();
+
+ is(
+ TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec),
+ -1,
+ "Did not restore, as undoCloseWindow() was last called (2)"
+ );
+
+ counts = getBrowserWindowsCount();
+ is(counts.open, 2, "Got right number of open windows");
+ is(counts.winstates, 3, "Got right number of window states");
+
+ await BrowserTestUtils.closeWindow(newWin);
+ await BrowserTestUtils.closeWindow(newWin2);
+
+ counts = getBrowserWindowsCount();
+ is(counts.open, 0, "Got right number of open windows");
+ is(counts.winstates, 1, "Got right number of window states");
+ });
+});
+
+/**
+ * Test if closing can be denied on Mac.
+ * @note: Mac only
+ */
+add_task(async function test_mac_notifications() {
+ if (!IS_MAC) {
+ return;
+ }
+
+ await setupTest({ denyFirst: true }, async function (newWin, obs) {
+ let closed = await closeWindowForRestoration(newWin);
+ ok(!closed, "First close attempt should be denied");
+ closed = await closeWindowForRestoration(newWin);
+ ok(closed, "Second close attempt should be granted");
+
+ // We tried closing once, and got denied. Then we tried again and
+ // succeeded. That means 2 close requests, and 1 close granted.
+ is(
+ obs["browser-lastwindow-close-requested"],
+ 2,
+ "Got expected browser-lastwindow-close-requested notifications"
+ );
+ is(
+ obs["browser-lastwindow-close-granted"],
+ 1,
+ "Got expected browser-lastwindow-close-granted notifications"
+ );
+ });
+});