1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/sessionstore/test/browser_589246.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,241 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// Mirrors WINDOW_ATTRIBUTES IN nsSessionStore.js 1.9 +const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"]; 1.10 + 1.11 +let stateBackup = ss.getBrowserState(); 1.12 + 1.13 +let originalWarnOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose"); 1.14 +let originalStartupPage = gPrefService.getIntPref("browser.startup.page"); 1.15 +let originalWindowType = document.documentElement.getAttribute("windowtype"); 1.16 + 1.17 +let gotLastWindowClosedTopic = false; 1.18 +let shouldPinTab = false; 1.19 +let shouldOpenTabs = false; 1.20 +let shouldCloseTab = false; 1.21 +let testNum = 0; 1.22 +let afterTestCallback; 1.23 + 1.24 +// Set state so we know the closed windows content 1.25 +let testState = { 1.26 + windows: [ 1.27 + { tabs: [{ entries: [{ url: "http://example.org" }] }] } 1.28 + ], 1.29 + _closedWindows: [] 1.30 +}; 1.31 + 1.32 +// We'll push a set of conditions and callbacks into this array 1.33 +// Ideally we would also test win/linux under a complete set of conditions, but 1.34 +// the tests for osx mirror the other set of conditions possible on win/linux. 1.35 +let tests = []; 1.36 + 1.37 +// the third & fourth test share a condition check, keep it DRY 1.38 +function checkOSX34Generator(num) { 1.39 + return function(aPreviousState, aCurState) { 1.40 + // In here, we should have restored the pinned tab, so only the unpinned tab 1.41 + // should be in aCurState. So let's shape our expectations. 1.42 + let expectedState = JSON.parse(aPreviousState); 1.43 + expectedState[0].tabs.shift(); 1.44 + // size attributes are stripped out in _prepDataForDeferredRestore in nsSessionStore. 1.45 + // This isn't the best approach, but neither is comparing JSON strings 1.46 + WINDOW_ATTRIBUTES.forEach(function (attr) delete expectedState[0][attr]); 1.47 + 1.48 + is(aCurState, JSON.stringify(expectedState), 1.49 + "test #" + num + ": closedWindowState is as expected"); 1.50 + }; 1.51 +} 1.52 +function checkNoWindowsGenerator(num) { 1.53 + return function(aPreviousState, aCurState) { 1.54 + is(aCurState, "[]", "test #" + num + ": there should be no closedWindowsLeft"); 1.55 + }; 1.56 +} 1.57 + 1.58 +// The first test has 0 pinned tabs and 1 unpinned tab 1.59 +tests.push({ 1.60 + pinned: false, 1.61 + extra: false, 1.62 + close: false, 1.63 + checkWinLin: checkNoWindowsGenerator(1), 1.64 + checkOSX: function(aPreviousState, aCurState) { 1.65 + is(aCurState, aPreviousState, "test #1: closed window state is unchanged"); 1.66 + } 1.67 +}); 1.68 + 1.69 +// The second test has 1 pinned tab and 0 unpinned tabs. 1.70 +tests.push({ 1.71 + pinned: true, 1.72 + extra: false, 1.73 + close: false, 1.74 + checkWinLin: checkNoWindowsGenerator(2), 1.75 + checkOSX: checkNoWindowsGenerator(2) 1.76 +}); 1.77 + 1.78 +// The third test has 1 pinned tab and 2 unpinned tabs. 1.79 +tests.push({ 1.80 + pinned: true, 1.81 + extra: true, 1.82 + close: false, 1.83 + checkWinLin: checkNoWindowsGenerator(3), 1.84 + checkOSX: checkOSX34Generator(3) 1.85 +}); 1.86 + 1.87 +// The fourth test has 1 pinned tab, 2 unpinned tabs, and closes one unpinned tab. 1.88 +tests.push({ 1.89 + pinned: true, 1.90 + extra: true, 1.91 + close: "one", 1.92 + checkWinLin: checkNoWindowsGenerator(4), 1.93 + checkOSX: checkOSX34Generator(4) 1.94 +}); 1.95 + 1.96 +// The fifth test has 1 pinned tab, 2 unpinned tabs, and closes both unpinned tabs. 1.97 +tests.push({ 1.98 + pinned: true, 1.99 + extra: true, 1.100 + close: "both", 1.101 + checkWinLin: checkNoWindowsGenerator(5), 1.102 + checkOSX: checkNoWindowsGenerator(5) 1.103 +}); 1.104 + 1.105 + 1.106 +function test() { 1.107 + /** Test for Bug 589246 - Closed window state getting corrupted when closing 1.108 + and reopening last browser window without exiting browser **/ 1.109 + waitForExplicitFinish(); 1.110 + // windows opening & closing, so extending the timeout 1.111 + requestLongerTimeout(2); 1.112 + 1.113 + // We don't want the quit dialog pref 1.114 + gPrefService.setBoolPref("browser.tabs.warnOnClose", false); 1.115 + // Ensure that we would restore the session (important for Windows) 1.116 + gPrefService.setIntPref("browser.startup.page", 3); 1.117 + 1.118 + runNextTestOrFinish(); 1.119 +} 1.120 + 1.121 +function runNextTestOrFinish() { 1.122 + if (tests.length) { 1.123 + setupForTest(tests.shift()) 1.124 + } 1.125 + else { 1.126 + // some state is cleaned up at the end of each test, but not all 1.127 + ["browser.tabs.warnOnClose", "browser.startup.page"].forEach(function(p) { 1.128 + if (gPrefService.prefHasUserValue(p)) 1.129 + gPrefService.clearUserPref(p); 1.130 + }); 1.131 + 1.132 + ss.setBrowserState(stateBackup); 1.133 + executeSoon(finish); 1.134 + } 1.135 +} 1.136 + 1.137 +function setupForTest(aConditions) { 1.138 + // reset some checks 1.139 + gotLastWindowClosedTopic = false; 1.140 + shouldPinTab = aConditions.pinned; 1.141 + shouldOpenTabs = aConditions.extra; 1.142 + shouldCloseTab = aConditions.close; 1.143 + testNum++; 1.144 + 1.145 + // set our test callback 1.146 + afterTestCallback = /Mac/.test(navigator.platform) ? aConditions.checkOSX 1.147 + : aConditions.checkWinLin; 1.148 + 1.149 + // Add observers 1.150 + Services.obs.addObserver(onLastWindowClosed, "browser-lastwindow-close-granted", false); 1.151 + 1.152 + // Set the state 1.153 + Services.obs.addObserver(onStateRestored, "sessionstore-browser-state-restored", false); 1.154 + ss.setBrowserState(JSON.stringify(testState)); 1.155 +} 1.156 + 1.157 +function onStateRestored(aSubject, aTopic, aData) { 1.158 + info("test #" + testNum + ": onStateRestored"); 1.159 + Services.obs.removeObserver(onStateRestored, "sessionstore-browser-state-restored"); 1.160 + 1.161 + // change this window's windowtype so that closing a new window will trigger 1.162 + // browser-lastwindow-close-granted. 1.163 + document.documentElement.setAttribute("windowtype", "navigator:testrunner"); 1.164 + 1.165 + let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com"); 1.166 + newWin.addEventListener("load", function(aEvent) { 1.167 + newWin.removeEventListener("load", arguments.callee, false); 1.168 + 1.169 + whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() { 1.170 + // pin this tab 1.171 + if (shouldPinTab) 1.172 + newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab); 1.173 + 1.174 + newWin.addEventListener("unload", function () { 1.175 + newWin.removeEventListener("unload", arguments.callee, false); 1.176 + onWindowUnloaded(); 1.177 + }, false); 1.178 + // Open a new tab as well. On Windows/Linux this will be restored when the 1.179 + // new window is opened below (in onWindowUnloaded). On OS X we'll just 1.180 + // restore the pinned tabs, leaving the unpinned tab in the closedWindowsData. 1.181 + if (shouldOpenTabs) { 1.182 + let newTab = newWin.gBrowser.addTab("about:config"); 1.183 + let newTab2 = newWin.gBrowser.addTab("about:buildconfig"); 1.184 + 1.185 + newTab.linkedBrowser.addEventListener("load", function() { 1.186 + newTab.linkedBrowser.removeEventListener("load", arguments.callee, true); 1.187 + 1.188 + if (shouldCloseTab == "one") { 1.189 + newWin.gBrowser.removeTab(newTab2); 1.190 + } 1.191 + else if (shouldCloseTab == "both") { 1.192 + newWin.gBrowser.removeTab(newTab); 1.193 + newWin.gBrowser.removeTab(newTab2); 1.194 + } 1.195 + newWin.BrowserTryToCloseWindow(); 1.196 + }, true); 1.197 + } 1.198 + else { 1.199 + newWin.BrowserTryToCloseWindow(); 1.200 + } 1.201 + }); 1.202 + }, false); 1.203 +} 1.204 + 1.205 +// This will be called before the window is actually closed 1.206 +function onLastWindowClosed(aSubject, aTopic, aData) { 1.207 + info("test #" + testNum + ": onLastWindowClosed"); 1.208 + Services.obs.removeObserver(onLastWindowClosed, "browser-lastwindow-close-granted"); 1.209 + gotLastWindowClosedTopic = true; 1.210 +} 1.211 + 1.212 +// This is the unload event listener on the new window (from onStateRestored). 1.213 +// Unload is fired after the window is closed, so sessionstore has already 1.214 +// updated _closedWindows (which is important). We'll open a new window here 1.215 +// which should actually trigger the bug. 1.216 +function onWindowUnloaded() { 1.217 + info("test #" + testNum + ": onWindowClosed"); 1.218 + ok(gotLastWindowClosedTopic, "test #" + testNum + ": browser-lastwindow-close-granted was notified prior"); 1.219 + 1.220 + let previousClosedWindowData = ss.getClosedWindowData(); 1.221 + 1.222 + // Now we want to open a new window 1.223 + let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:mozilla"); 1.224 + newWin.addEventListener("load", function(aEvent) { 1.225 + newWin.removeEventListener("load", arguments.callee, false); 1.226 + 1.227 + newWin.gBrowser.selectedBrowser.addEventListener("load", function () { 1.228 + newWin.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); 1.229 + 1.230 + // Good enough for checking the state 1.231 + afterTestCallback(previousClosedWindowData, ss.getClosedWindowData()); 1.232 + afterTestCleanup(newWin); 1.233 + }, true); 1.234 + 1.235 + }, false); 1.236 +} 1.237 + 1.238 +function afterTestCleanup(aNewWin) { 1.239 + executeSoon(function() { 1.240 + aNewWin.close(); 1.241 + document.documentElement.setAttribute("windowtype", originalWindowType); 1.242 + runNextTestOrFinish(); 1.243 + }); 1.244 +}