diff options
Diffstat (limited to 'browser/components/sessionstore/test/browser_589246.js')
-rw-r--r-- | browser/components/sessionstore/test/browser_589246.js | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/browser/components/sessionstore/test/browser_589246.js b/browser/components/sessionstore/test/browser_589246.js new file mode 100644 index 0000000000..2fd92b2b82 --- /dev/null +++ b/browser/components/sessionstore/test/browser_589246.js @@ -0,0 +1,286 @@ +/* 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/. */ + +// Mirrors WINDOW_ATTRIBUTES IN SessionStore.sys.mjs +const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"]; + +var stateBackup = ss.getBrowserState(); + +var originalWarnOnClose = Services.prefs.getBoolPref( + "browser.tabs.warnOnClose" +); +var originalStartupPage = Services.prefs.getIntPref("browser.startup.page"); +var originalWindowType = document.documentElement.getAttribute("windowtype"); + +var gotLastWindowClosedTopic = false; +var shouldPinTab = false; +var shouldOpenTabs = false; +var shouldCloseTab = false; +var testNum = 0; +var afterTestCallback; + +// Set state so we know the closed windows content +var testState = { + windows: [{ tabs: [{ entries: [{ url: "http://example.org" }] }] }], + _closedWindows: [], +}; + +// We'll push a set of conditions and callbacks into this array +// Ideally we would also test win/linux under a complete set of conditions, but +// the tests for osx mirror the other set of conditions possible on win/linux. +var tests = []; + +// the third & fourth test share a condition check, keep it DRY +function checkOSX34Generator(num) { + return function (aPreviousState, aCurState) { + // In here, we should have restored the pinned tab, so only the unpinned tab + // should be in aCurState. So let's shape our expectations. + let expectedState = JSON.parse(aPreviousState); + expectedState[0].tabs.shift(); + // size attributes are stripped out in _prepDataForDeferredRestore in SessionStore.sys.mjs. + // This isn't the best approach, but neither is comparing JSON strings + WINDOW_ATTRIBUTES.forEach(attr => delete expectedState[0][attr]); + + is( + aCurState, + JSON.stringify(expectedState), + "test #" + num + ": closedWindowState is as expected" + ); + }; +} +function checkNoWindowsGenerator(num) { + return function (aPreviousState, aCurState) { + is( + aCurState, + "[]", + "test #" + num + ": there should be no closedWindowsLeft" + ); + }; +} + +// The first test has 0 pinned tabs and 1 unpinned tab +tests.push({ + pinned: false, + extra: false, + close: false, + checkWinLin: checkNoWindowsGenerator(1), + checkOSX(aPreviousState, aCurState) { + is(aCurState, aPreviousState, "test #1: closed window state is unchanged"); + }, +}); + +// The second test has 1 pinned tab and 0 unpinned tabs. +tests.push({ + pinned: true, + extra: false, + close: false, + checkWinLin: checkNoWindowsGenerator(2), + checkOSX: checkNoWindowsGenerator(2), +}); + +// The third test has 1 pinned tab and 2 unpinned tabs. +tests.push({ + pinned: true, + extra: true, + close: false, + checkWinLin: checkNoWindowsGenerator(3), + checkOSX: checkOSX34Generator(3), +}); + +// The fourth test has 1 pinned tab, 2 unpinned tabs, and closes one unpinned tab. +tests.push({ + pinned: true, + extra: true, + close: "one", + checkWinLin: checkNoWindowsGenerator(4), + checkOSX: checkOSX34Generator(4), +}); + +// The fifth test has 1 pinned tab, 2 unpinned tabs, and closes both unpinned tabs. +tests.push({ + pinned: true, + extra: true, + close: "both", + checkWinLin: checkNoWindowsGenerator(5), + checkOSX: checkNoWindowsGenerator(5), +}); + +function test() { + /** Test for Bug 589246 - Closed window state getting corrupted when closing + and reopening last browser window without exiting browser **/ + waitForExplicitFinish(); + // windows opening & closing, so extending the timeout + requestLongerTimeout(2); + + // We don't want the quit dialog pref + Services.prefs.setBoolPref("browser.tabs.warnOnClose", false); + // Ensure that we would restore the session (important for Windows) + Services.prefs.setIntPref("browser.startup.page", 3); + + runNextTestOrFinish(); +} + +function runNextTestOrFinish() { + if (tests.length) { + setupForTest(tests.shift()); + } else { + // some state is cleaned up at the end of each test, but not all + ["browser.tabs.warnOnClose", "browser.startup.page"].forEach(function (p) { + if (Services.prefs.prefHasUserValue(p)) { + Services.prefs.clearUserPref(p); + } + }); + + ss.setBrowserState(stateBackup); + executeSoon(finish); + } +} + +function setupForTest(aConditions) { + // reset some checks + gotLastWindowClosedTopic = false; + shouldPinTab = aConditions.pinned; + shouldOpenTabs = aConditions.extra; + shouldCloseTab = aConditions.close; + testNum++; + + // set our test callback + afterTestCallback = /Mac/.test(navigator.platform) + ? aConditions.checkOSX + : aConditions.checkWinLin; + + // Add observers + Services.obs.addObserver( + onLastWindowClosed, + "browser-lastwindow-close-granted" + ); + + // Set the state + Services.obs.addObserver( + onStateRestored, + "sessionstore-browser-state-restored" + ); + ss.setBrowserState(JSON.stringify(testState)); +} + +function onStateRestored(aSubject, aTopic, aData) { + info("test #" + testNum + ": onStateRestored"); + Services.obs.removeObserver( + onStateRestored, + "sessionstore-browser-state-restored" + ); + + // change this window's windowtype so that closing a new window will trigger + // browser-lastwindow-close-granted. + document.documentElement.setAttribute("windowtype", "navigator:testrunner"); + + let newWin = openDialog( + location, + "_blank", + "chrome,all,dialog=no", + "http://example.com" + ); + newWin.addEventListener( + "load", + function (aEvent) { + promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => { + // pin this tab + if (shouldPinTab) { + newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab); + } + + newWin.addEventListener( + "unload", + function () { + onWindowUnloaded(); + }, + { once: true } + ); + // Open a new tab as well. On Windows/Linux this will be restored when the + // new window is opened below (in onWindowUnloaded). On OS X we'll just + // restore the pinned tabs, leaving the unpinned tab in the closedWindowsData. + if (shouldOpenTabs) { + let newTab = BrowserTestUtils.addTab(newWin.gBrowser, "about:config"); + let newTab2 = BrowserTestUtils.addTab( + newWin.gBrowser, + "about:buildconfig" + ); + + newTab.linkedBrowser.addEventListener( + "load", + function () { + if (shouldCloseTab == "one") { + newWin.gBrowser.removeTab(newTab2); + } else if (shouldCloseTab == "both") { + newWin.gBrowser.removeTab(newTab); + newWin.gBrowser.removeTab(newTab2); + } + newWin.BrowserTryToCloseWindow(); + }, + { capture: true, once: true } + ); + } else { + newWin.BrowserTryToCloseWindow(); + } + }); + }, + { once: true } + ); +} + +// This will be called before the window is actually closed +function onLastWindowClosed(aSubject, aTopic, aData) { + info("test #" + testNum + ": onLastWindowClosed"); + Services.obs.removeObserver( + onLastWindowClosed, + "browser-lastwindow-close-granted" + ); + gotLastWindowClosedTopic = true; +} + +// This is the unload event listener on the new window (from onStateRestored). +// Unload is fired after the window is closed, so sessionstore has already +// updated _closedWindows (which is important). We'll open a new window here +// which should actually trigger the bug. +function onWindowUnloaded() { + info("test #" + testNum + ": onWindowClosed"); + ok( + gotLastWindowClosedTopic, + "test #" + testNum + ": browser-lastwindow-close-granted was notified prior" + ); + + let previousClosedWindowData = ss.getClosedWindowData(); + + // Now we want to open a new window + let newWin = openDialog( + location, + "_blank", + "chrome,all,dialog=no", + "about:mozilla" + ); + newWin.addEventListener( + "load", + function (aEvent) { + newWin.gBrowser.selectedBrowser.addEventListener( + "load", + function () { + // Good enough for checking the state + afterTestCallback(previousClosedWindowData, ss.getClosedWindowData()); + afterTestCleanup(newWin); + }, + { capture: true, once: true } + ); + }, + { once: true } + ); +} + +function afterTestCleanup(aNewWin) { + executeSoon(function () { + BrowserTestUtils.closeWindow(aNewWin).then(() => { + document.documentElement.setAttribute("windowtype", originalWindowType); + runNextTestOrFinish(); + }); + }); +} |