forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			489 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			489 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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"
 | |
|     );
 | |
|   });
 | |
| });
 | 
