summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/browser_589246.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/sessionstore/test/browser_589246.js')
-rw-r--r--browser/components/sessionstore/test/browser_589246.js286
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();
+ });
+ });
+}