michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Checks that restoring the last browser window in session is actually michael@0: * working: michael@0: * 1.1) Open a new browser window michael@0: * 1.2) Add some tabs michael@0: * 1.3) Close that window michael@0: * 1.4) Opening another window michael@0: * --> State is restored michael@0: * michael@0: * 2.1) Open a new browser window michael@0: * 2.2) Add some tabs michael@0: * 2.3) Enter private browsing mode michael@0: * 2.4) Close the window while still in private browsing mode michael@0: * 2.5) Opening a new window michael@0: * --> State is not restored, because private browsing mode is still active michael@0: * 2.6) Leaving private browsing mode michael@0: * 2.7) Open another window michael@0: * --> State (that was before entering PBM) is restored michael@0: * michael@0: * 3.1) Open a new browser window michael@0: * 3.2) Add some tabs michael@0: * 3.4) Open some popups michael@0: * 3.5) Add another tab to one popup (so that it gets stored) and close it again michael@0: * 3.5) Close the browser window michael@0: * 3.6) Open another browser window michael@0: * --> State of the closed browser window, but not of the popup, is restored michael@0: * michael@0: * 4.1) Open a popup michael@0: * 4.2) Add another tab to the popup (so that it gets stored) and close it again michael@0: * 4.3) Open a window michael@0: * --> Nothing at all should be restored michael@0: * michael@0: * 5.1) Open two browser windows and close them again michael@0: * 5.2) undoCloseWindow() one michael@0: * 5.3) Open another browser window michael@0: * --> Nothing at all should be restored michael@0: * michael@0: * Checks the new notifications are correctly posted and processed, that is michael@0: * for each successful -requested a -granted is received, but omitted if michael@0: * -requested was cnceled michael@0: * Said notifications are: michael@0: * - browser-lastwindow-close-requested michael@0: * - browser-lastwindow-close-granted michael@0: * Tests are: michael@0: * 6) Cancel closing when first observe a -requested michael@0: * --> Window is kept open michael@0: * 7) Count the number of notifications michael@0: * --> count(-requested) == count(-granted) + 1 michael@0: * --> (The first -requested was canceled, so off-by-one) michael@0: * 8) (Mac only) Mac version of Test 5 additionally preparing Test 6 michael@0: * michael@0: * @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894 michael@0: * @note It is implicitly tested that restoring the last window works when michael@0: * non-browser windows are around. The "Run Tests" window as well as the main michael@0: * browser window (wherein the test code gets executed) won't be considered michael@0: * browser windows. To achiveve this said main browser window has it's windowtype michael@0: * attribute modified so that it's not considered a browser window any longer. michael@0: * This is crucial, because otherwise there would be two browser windows around, michael@0: * said main test window and the one opened by the tests, and hence the new michael@0: * logic wouldn't be executed at all. michael@0: * @note Mac only tests the new notifications, as restoring the last window is michael@0: * not enabled on that platform (platform shim; the application is kept running michael@0: * although there are no windows left) michael@0: * @note There is a difference when closing a browser window with michael@0: * BrowserTryToCloseWindow() as opposed to close(). The former will make michael@0: * nsSessionStore restore a window next time it gets a chance and will post michael@0: * notifications. The latter won't. michael@0: */ michael@0: michael@0: function browserWindowsCount(expected, msg) { michael@0: if (typeof expected == "number") michael@0: expected = [expected, expected]; michael@0: let count = 0; michael@0: let e = Services.wm.getEnumerator("navigator:browser"); michael@0: while (e.hasMoreElements()) { michael@0: if (!e.getNext().closed) michael@0: ++count; michael@0: } michael@0: is(count, expected[0], msg + " (nsIWindowMediator)"); michael@0: let state = ss.getBrowserState(); michael@0: is(JSON.parse(state).windows.length, expected[1], msg + " (getBrowserState)"); michael@0: } michael@0: michael@0: function test() { michael@0: browserWindowsCount(1, "Only one browser window should be open initially"); michael@0: michael@0: waitForExplicitFinish(); michael@0: // This test takes some time to run, and it could timeout randomly. michael@0: // So we require a longer timeout. See bug 528219. michael@0: requestLongerTimeout(2); michael@0: michael@0: // Some urls that might be opened in tabs and/or popups michael@0: // Do not use about:blank: michael@0: // That one is reserved for special purposes in the tests michael@0: const TEST_URLS = ["about:mozilla", "about:buildconfig"]; michael@0: michael@0: // Number of -request notifications to except michael@0: // remember to adjust when adding new tests michael@0: const NOTIFICATIONS_EXPECTED = 6; michael@0: michael@0: // Window features of popup windows michael@0: const POPUP_FEATURES = "toolbar=no,resizable=no,status=no"; michael@0: michael@0: // Window features of browser windows michael@0: const CHROME_FEATURES = "chrome,all,dialog=no"; michael@0: michael@0: // Store the old window type for cleanup michael@0: let oldWinType = ""; michael@0: // Store the old tabs.warnOnClose pref so that we may reset it during michael@0: // cleanup michael@0: let oldWarnTabsOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose"); michael@0: michael@0: // Observe these, and also use to count the number of hits michael@0: let observing = { michael@0: "browser-lastwindow-close-requested": 0, michael@0: "browser-lastwindow-close-granted": 0 michael@0: }; michael@0: michael@0: /** michael@0: * Helper: Will observe and handle the notifications for us michael@0: */ michael@0: let hitCount = 0; michael@0: function observer(aCancel, aTopic, aData) { michael@0: // count so that we later may compare michael@0: observing[aTopic]++; michael@0: michael@0: // handle some tests michael@0: if (++hitCount == 1) { michael@0: // Test 6 michael@0: aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper: Sets prefs as the testsuite requires michael@0: * @note Will be reset in cleanTestSuite just before finishing the tests michael@0: */ michael@0: function setPrefs() { michael@0: gPrefService.setIntPref("browser.startup.page", 3); michael@0: gPrefService.setBoolPref( michael@0: "browser.privatebrowsing.keep_current_session", michael@0: true michael@0: ); michael@0: gPrefService.setBoolPref("browser.tabs.warnOnClose", false); michael@0: } michael@0: michael@0: /** michael@0: * Helper: Sets up this testsuite michael@0: */ michael@0: function setupTestsuite(testFn) { michael@0: // Register our observers michael@0: for (let o in observing) michael@0: Services.obs.addObserver(observer, o, false); michael@0: michael@0: // Make the main test window not count as a browser window any longer michael@0: oldWinType = document.documentElement.getAttribute("windowtype"); michael@0: document.documentElement.setAttribute("windowtype", "navigator:testrunner"); michael@0: } michael@0: michael@0: /** michael@0: * Helper: Cleans up behind the testsuite michael@0: */ michael@0: function cleanupTestsuite(callback) { michael@0: // Finally remove observers again michael@0: for (let o in observing) michael@0: Services.obs.removeObserver(observer, o); michael@0: michael@0: // Reset the prefs we touched michael@0: [ michael@0: "browser.startup.page", michael@0: "browser.privatebrowsing.keep_current_session" michael@0: ].forEach(function (pref) { michael@0: if (gPrefService.prefHasUserValue(pref)) michael@0: gPrefService.clearUserPref(pref); michael@0: }); michael@0: gPrefService.setBoolPref("browser.tabs.warnOnClose", oldWarnTabsOnClose); michael@0: michael@0: // Reset the window type michael@0: document.documentElement.setAttribute("windowtype", oldWinType); michael@0: } michael@0: michael@0: /** michael@0: * Helper: sets the prefs and a new window with our test tabs michael@0: */ michael@0: function setupTestAndRun(aIsPrivateWindow, testFn) { michael@0: // Prepare the prefs michael@0: setPrefs(); michael@0: michael@0: // Prepare a window; open it and add more tabs michael@0: let options = {}; michael@0: if (aIsPrivateWindow) { michael@0: options = {private: true}; michael@0: } michael@0: michael@0: whenNewWindowLoaded(options, function (newWin) { michael@0: TEST_URLS.forEach(function (url) { michael@0: newWin.gBrowser.addTab(url); michael@0: }); michael@0: michael@0: executeSoon(() => testFn(newWin)); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Test 1: Normal in-session restore michael@0: * @note: Non-Mac only michael@0: */ michael@0: function testOpenCloseNormal(nextFn) { michael@0: setupTestAndRun(false, function(newWin) { michael@0: // Close the window michael@0: // window.close doesn't push any close events, michael@0: // so use BrowserTryToCloseWindow michael@0: newWin.BrowserTryToCloseWindow(); michael@0: michael@0: // The first request to close is denied by our observer (Test 6) michael@0: ok(!newWin.closed, "First close request was denied"); michael@0: if (!newWin.closed) { michael@0: newWin.BrowserTryToCloseWindow(); michael@0: ok(newWin.closed, "Second close request was granted"); michael@0: } michael@0: michael@0: // Open a new window michael@0: // The previously closed window should be restored michael@0: whenNewWindowLoaded({}, function (newWin) { michael@0: is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1, michael@0: "Restored window in-session with otherpopup windows around"); michael@0: michael@0: // Cleanup michael@0: newWin.close(); michael@0: michael@0: // Next please michael@0: executeSoon(nextFn); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Test 2: PrivateBrowsing in-session restore michael@0: * @note: Non-Mac only michael@0: */ michael@0: function testOpenClosePrivateBrowsing(nextFn) { michael@0: setupTestAndRun(false, function(newWin) { michael@0: // Close the window michael@0: newWin.BrowserTryToCloseWindow(); michael@0: michael@0: // Enter private browsing mode michael@0: // Open a new window. michael@0: // The previously closed window should NOT be restored michael@0: whenNewWindowLoaded({private: true}, function (newWin) { michael@0: is(newWin.gBrowser.browsers.length, 1, michael@0: "Did not restore in private browing mode"); michael@0: michael@0: // Cleanup michael@0: newWin.BrowserTryToCloseWindow(); michael@0: michael@0: // Exit private browsing mode again michael@0: whenNewWindowLoaded({}, function (newWin) { michael@0: is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1, michael@0: "Restored after leaving private browsing again"); michael@0: michael@0: newWin.close(); michael@0: michael@0: // Next please michael@0: executeSoon(nextFn); michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Test 3: Open some popup windows to check those aren't restored, but michael@0: * the browser window is michael@0: * @note: Non-Mac only michael@0: */ michael@0: function testOpenCloseWindowAndPopup(nextFn) { michael@0: setupTestAndRun(false, function(newWin) { michael@0: // open some popups michael@0: let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]); michael@0: let popup2 = openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]); michael@0: popup2.addEventListener("load", function() { michael@0: popup2.removeEventListener("load", arguments.callee, false); michael@0: popup2.gBrowser.addEventListener("load", function() { michael@0: popup2.gBrowser.removeEventListener("load", arguments.callee, true); michael@0: popup2.gBrowser.addTab(TEST_URLS[0]); michael@0: // close the window michael@0: newWin.BrowserTryToCloseWindow(); michael@0: michael@0: // Close the popup window michael@0: // The test is successful when not this popup window is restored michael@0: // but instead newWin michael@0: popup2.close(); michael@0: michael@0: // open a new window the previously closed window should be restored to michael@0: whenNewWindowLoaded({}, function (newWin) { michael@0: is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1, michael@0: "Restored window and associated tabs in session"); michael@0: michael@0: // Cleanup michael@0: newWin.close(); michael@0: popup.close(); michael@0: michael@0: // Next please michael@0: executeSoon(nextFn); michael@0: }); michael@0: }, true); michael@0: }, false); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Test 4: Open some popup window to check it isn't restored. michael@0: * Instead nothing at all should be restored michael@0: * @note: Non-Mac only michael@0: */ michael@0: function testOpenCloseOnlyPopup(nextFn) { michael@0: // prepare the prefs michael@0: setPrefs(); michael@0: michael@0: // This will cause nsSessionStore to restore a window the next time it michael@0: // gets a chance. michael@0: let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]); michael@0: popup.addEventListener("load", function() { michael@0: this.removeEventListener("load", arguments.callee, true); michael@0: is(popup.gBrowser.browsers.length, 1, michael@0: "Did not restore the popup window (1)"); michael@0: popup.BrowserTryToCloseWindow(); michael@0: michael@0: // Real tests michael@0: popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]); michael@0: popup.addEventListener("load", function() { michael@0: popup.removeEventListener("load", arguments.callee, false); michael@0: popup.gBrowser.addEventListener("load", function() { michael@0: popup.gBrowser.removeEventListener("load", arguments.callee, true); michael@0: popup.gBrowser.addTab(TEST_URLS[0]); michael@0: michael@0: is(popup.gBrowser.browsers.length, 2, michael@0: "Did not restore to the popup window (2)"); michael@0: michael@0: // Close the popup window michael@0: // The test is successful when not this popup window is restored michael@0: // but instead a new window is opened without restoring anything michael@0: popup.close(); michael@0: michael@0: whenNewWindowLoaded({}, function (newWin) { michael@0: isnot(newWin.gBrowser.browsers.length, 2, michael@0: "Did not restore the popup window"); michael@0: is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1, michael@0: "Did not restore the popup window (2)"); michael@0: michael@0: // Cleanup michael@0: newWin.close(); michael@0: michael@0: // Next please michael@0: executeSoon(nextFn); michael@0: }); michael@0: }, true); michael@0: }, false); michael@0: }, true); michael@0: } michael@0: michael@0: /** michael@0: * Test 5: Open some windows and do undoCloseWindow. This should prevent any michael@0: * restoring later in the test michael@0: * @note: Non-Mac only michael@0: */ michael@0: function testOpenCloseRestoreFromPopup(nextFn) { michael@0: setupTestAndRun(false, function(newWin) { michael@0: setupTestAndRun(false, function(newWin2) { michael@0: newWin.BrowserTryToCloseWindow(); michael@0: newWin2.BrowserTryToCloseWindow(); michael@0: michael@0: browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup"); michael@0: michael@0: newWin = undoCloseWindow(0); michael@0: michael@0: whenNewWindowLoaded({}, function (newWin2) { michael@0: is(newWin2.gBrowser.browsers.length, 1, michael@0: "Did not restore, as undoCloseWindow() was last called"); michael@0: is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1, michael@0: "Did not restore, as undoCloseWindow() was last called (2)"); michael@0: michael@0: browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup"); michael@0: michael@0: // Cleanup michael@0: newWin.close(); michael@0: newWin2.close(); michael@0: michael@0: browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup"); michael@0: michael@0: // Next please michael@0: executeSoon(nextFn); michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Test 7: Check whether the right number of notifications was received during michael@0: * the tests michael@0: */ michael@0: function testNotificationCount(nextFn) { michael@0: is(observing["browser-lastwindow-close-requested"], NOTIFICATIONS_EXPECTED, michael@0: "browser-lastwindow-close-requested notifications observed"); michael@0: michael@0: // -request must be one more as we cancel the first one we hit, michael@0: // and hence won't produce a corresponding -grant michael@0: // @see observer.observe michael@0: is(observing["browser-lastwindow-close-requested"], michael@0: observing["browser-lastwindow-close-granted"] + 1, michael@0: "Notification count for -request and -grant matches"); michael@0: michael@0: executeSoon(nextFn); michael@0: } michael@0: michael@0: /** michael@0: * Test 8: Test if closing can be denied on Mac michael@0: * Futhermore prepares the testNotificationCount test (Test 7) michael@0: * @note: Mac only michael@0: */ michael@0: function testMacNotifications(nextFn, iteration) { michael@0: iteration = iteration || 1; michael@0: setupTestAndRun(false, function(newWin) { michael@0: // close the window michael@0: // window.close doesn't push any close events, michael@0: // so use BrowserTryToCloseWindow michael@0: newWin.BrowserTryToCloseWindow(); michael@0: if (iteration == 1) { michael@0: ok(!newWin.closed, "First close attempt denied"); michael@0: if (!newWin.closed) { michael@0: newWin.BrowserTryToCloseWindow(); michael@0: ok(newWin.closed, "Second close attempt granted"); michael@0: } michael@0: } michael@0: michael@0: if (iteration < NOTIFICATIONS_EXPECTED - 1) { michael@0: executeSoon(function() testMacNotifications(nextFn, ++iteration)); michael@0: } michael@0: else { michael@0: executeSoon(nextFn); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: // Execution starts here michael@0: michael@0: setupTestsuite(); michael@0: if (navigator.platform.match(/Mac/)) { michael@0: // Mac tests michael@0: testMacNotifications(function () { michael@0: testNotificationCount(function () { michael@0: cleanupTestsuite(); michael@0: browserWindowsCount(1, "Only one browser window should be open eventually"); michael@0: finish(); michael@0: }); michael@0: }); michael@0: } michael@0: else { michael@0: // Non-Mac Tests michael@0: testOpenCloseNormal(function () { michael@0: browserWindowsCount([0, 1], "browser windows after testOpenCloseNormal"); michael@0: testOpenClosePrivateBrowsing(function () { michael@0: browserWindowsCount([0, 1], "browser windows after testOpenClosePrivateBrowsing"); michael@0: testOpenCloseWindowAndPopup(function () { michael@0: browserWindowsCount([0, 1], "browser windows after testOpenCloseWindowAndPopup"); michael@0: testOpenCloseOnlyPopup(function () { michael@0: browserWindowsCount([0, 1], "browser windows after testOpenCloseOnlyPopup"); michael@0: testOpenCloseRestoreFromPopup(function () { michael@0: browserWindowsCount([0, 1], "browser windows after testOpenCloseRestoreFromPopup"); michael@0: testNotificationCount(function () { michael@0: cleanupTestsuite(); michael@0: browserWindowsCount(1, "browser windows after testNotificationCount"); michael@0: finish(); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: }