|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 // Mirrors WINDOW_ATTRIBUTES IN nsSessionStore.js |
|
6 const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"]; |
|
7 |
|
8 let stateBackup = ss.getBrowserState(); |
|
9 |
|
10 let originalWarnOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose"); |
|
11 let originalStartupPage = gPrefService.getIntPref("browser.startup.page"); |
|
12 let originalWindowType = document.documentElement.getAttribute("windowtype"); |
|
13 |
|
14 let gotLastWindowClosedTopic = false; |
|
15 let shouldPinTab = false; |
|
16 let shouldOpenTabs = false; |
|
17 let shouldCloseTab = false; |
|
18 let testNum = 0; |
|
19 let afterTestCallback; |
|
20 |
|
21 // Set state so we know the closed windows content |
|
22 let testState = { |
|
23 windows: [ |
|
24 { tabs: [{ entries: [{ url: "http://example.org" }] }] } |
|
25 ], |
|
26 _closedWindows: [] |
|
27 }; |
|
28 |
|
29 // We'll push a set of conditions and callbacks into this array |
|
30 // Ideally we would also test win/linux under a complete set of conditions, but |
|
31 // the tests for osx mirror the other set of conditions possible on win/linux. |
|
32 let tests = []; |
|
33 |
|
34 // the third & fourth test share a condition check, keep it DRY |
|
35 function checkOSX34Generator(num) { |
|
36 return function(aPreviousState, aCurState) { |
|
37 // In here, we should have restored the pinned tab, so only the unpinned tab |
|
38 // should be in aCurState. So let's shape our expectations. |
|
39 let expectedState = JSON.parse(aPreviousState); |
|
40 expectedState[0].tabs.shift(); |
|
41 // size attributes are stripped out in _prepDataForDeferredRestore in nsSessionStore. |
|
42 // This isn't the best approach, but neither is comparing JSON strings |
|
43 WINDOW_ATTRIBUTES.forEach(function (attr) delete expectedState[0][attr]); |
|
44 |
|
45 is(aCurState, JSON.stringify(expectedState), |
|
46 "test #" + num + ": closedWindowState is as expected"); |
|
47 }; |
|
48 } |
|
49 function checkNoWindowsGenerator(num) { |
|
50 return function(aPreviousState, aCurState) { |
|
51 is(aCurState, "[]", "test #" + num + ": there should be no closedWindowsLeft"); |
|
52 }; |
|
53 } |
|
54 |
|
55 // The first test has 0 pinned tabs and 1 unpinned tab |
|
56 tests.push({ |
|
57 pinned: false, |
|
58 extra: false, |
|
59 close: false, |
|
60 checkWinLin: checkNoWindowsGenerator(1), |
|
61 checkOSX: function(aPreviousState, aCurState) { |
|
62 is(aCurState, aPreviousState, "test #1: closed window state is unchanged"); |
|
63 } |
|
64 }); |
|
65 |
|
66 // The second test has 1 pinned tab and 0 unpinned tabs. |
|
67 tests.push({ |
|
68 pinned: true, |
|
69 extra: false, |
|
70 close: false, |
|
71 checkWinLin: checkNoWindowsGenerator(2), |
|
72 checkOSX: checkNoWindowsGenerator(2) |
|
73 }); |
|
74 |
|
75 // The third test has 1 pinned tab and 2 unpinned tabs. |
|
76 tests.push({ |
|
77 pinned: true, |
|
78 extra: true, |
|
79 close: false, |
|
80 checkWinLin: checkNoWindowsGenerator(3), |
|
81 checkOSX: checkOSX34Generator(3) |
|
82 }); |
|
83 |
|
84 // The fourth test has 1 pinned tab, 2 unpinned tabs, and closes one unpinned tab. |
|
85 tests.push({ |
|
86 pinned: true, |
|
87 extra: true, |
|
88 close: "one", |
|
89 checkWinLin: checkNoWindowsGenerator(4), |
|
90 checkOSX: checkOSX34Generator(4) |
|
91 }); |
|
92 |
|
93 // The fifth test has 1 pinned tab, 2 unpinned tabs, and closes both unpinned tabs. |
|
94 tests.push({ |
|
95 pinned: true, |
|
96 extra: true, |
|
97 close: "both", |
|
98 checkWinLin: checkNoWindowsGenerator(5), |
|
99 checkOSX: checkNoWindowsGenerator(5) |
|
100 }); |
|
101 |
|
102 |
|
103 function test() { |
|
104 /** Test for Bug 589246 - Closed window state getting corrupted when closing |
|
105 and reopening last browser window without exiting browser **/ |
|
106 waitForExplicitFinish(); |
|
107 // windows opening & closing, so extending the timeout |
|
108 requestLongerTimeout(2); |
|
109 |
|
110 // We don't want the quit dialog pref |
|
111 gPrefService.setBoolPref("browser.tabs.warnOnClose", false); |
|
112 // Ensure that we would restore the session (important for Windows) |
|
113 gPrefService.setIntPref("browser.startup.page", 3); |
|
114 |
|
115 runNextTestOrFinish(); |
|
116 } |
|
117 |
|
118 function runNextTestOrFinish() { |
|
119 if (tests.length) { |
|
120 setupForTest(tests.shift()) |
|
121 } |
|
122 else { |
|
123 // some state is cleaned up at the end of each test, but not all |
|
124 ["browser.tabs.warnOnClose", "browser.startup.page"].forEach(function(p) { |
|
125 if (gPrefService.prefHasUserValue(p)) |
|
126 gPrefService.clearUserPref(p); |
|
127 }); |
|
128 |
|
129 ss.setBrowserState(stateBackup); |
|
130 executeSoon(finish); |
|
131 } |
|
132 } |
|
133 |
|
134 function setupForTest(aConditions) { |
|
135 // reset some checks |
|
136 gotLastWindowClosedTopic = false; |
|
137 shouldPinTab = aConditions.pinned; |
|
138 shouldOpenTabs = aConditions.extra; |
|
139 shouldCloseTab = aConditions.close; |
|
140 testNum++; |
|
141 |
|
142 // set our test callback |
|
143 afterTestCallback = /Mac/.test(navigator.platform) ? aConditions.checkOSX |
|
144 : aConditions.checkWinLin; |
|
145 |
|
146 // Add observers |
|
147 Services.obs.addObserver(onLastWindowClosed, "browser-lastwindow-close-granted", false); |
|
148 |
|
149 // Set the state |
|
150 Services.obs.addObserver(onStateRestored, "sessionstore-browser-state-restored", false); |
|
151 ss.setBrowserState(JSON.stringify(testState)); |
|
152 } |
|
153 |
|
154 function onStateRestored(aSubject, aTopic, aData) { |
|
155 info("test #" + testNum + ": onStateRestored"); |
|
156 Services.obs.removeObserver(onStateRestored, "sessionstore-browser-state-restored"); |
|
157 |
|
158 // change this window's windowtype so that closing a new window will trigger |
|
159 // browser-lastwindow-close-granted. |
|
160 document.documentElement.setAttribute("windowtype", "navigator:testrunner"); |
|
161 |
|
162 let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com"); |
|
163 newWin.addEventListener("load", function(aEvent) { |
|
164 newWin.removeEventListener("load", arguments.callee, false); |
|
165 |
|
166 whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() { |
|
167 // pin this tab |
|
168 if (shouldPinTab) |
|
169 newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab); |
|
170 |
|
171 newWin.addEventListener("unload", function () { |
|
172 newWin.removeEventListener("unload", arguments.callee, false); |
|
173 onWindowUnloaded(); |
|
174 }, false); |
|
175 // Open a new tab as well. On Windows/Linux this will be restored when the |
|
176 // new window is opened below (in onWindowUnloaded). On OS X we'll just |
|
177 // restore the pinned tabs, leaving the unpinned tab in the closedWindowsData. |
|
178 if (shouldOpenTabs) { |
|
179 let newTab = newWin.gBrowser.addTab("about:config"); |
|
180 let newTab2 = newWin.gBrowser.addTab("about:buildconfig"); |
|
181 |
|
182 newTab.linkedBrowser.addEventListener("load", function() { |
|
183 newTab.linkedBrowser.removeEventListener("load", arguments.callee, true); |
|
184 |
|
185 if (shouldCloseTab == "one") { |
|
186 newWin.gBrowser.removeTab(newTab2); |
|
187 } |
|
188 else if (shouldCloseTab == "both") { |
|
189 newWin.gBrowser.removeTab(newTab); |
|
190 newWin.gBrowser.removeTab(newTab2); |
|
191 } |
|
192 newWin.BrowserTryToCloseWindow(); |
|
193 }, true); |
|
194 } |
|
195 else { |
|
196 newWin.BrowserTryToCloseWindow(); |
|
197 } |
|
198 }); |
|
199 }, false); |
|
200 } |
|
201 |
|
202 // This will be called before the window is actually closed |
|
203 function onLastWindowClosed(aSubject, aTopic, aData) { |
|
204 info("test #" + testNum + ": onLastWindowClosed"); |
|
205 Services.obs.removeObserver(onLastWindowClosed, "browser-lastwindow-close-granted"); |
|
206 gotLastWindowClosedTopic = true; |
|
207 } |
|
208 |
|
209 // This is the unload event listener on the new window (from onStateRestored). |
|
210 // Unload is fired after the window is closed, so sessionstore has already |
|
211 // updated _closedWindows (which is important). We'll open a new window here |
|
212 // which should actually trigger the bug. |
|
213 function onWindowUnloaded() { |
|
214 info("test #" + testNum + ": onWindowClosed"); |
|
215 ok(gotLastWindowClosedTopic, "test #" + testNum + ": browser-lastwindow-close-granted was notified prior"); |
|
216 |
|
217 let previousClosedWindowData = ss.getClosedWindowData(); |
|
218 |
|
219 // Now we want to open a new window |
|
220 let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:mozilla"); |
|
221 newWin.addEventListener("load", function(aEvent) { |
|
222 newWin.removeEventListener("load", arguments.callee, false); |
|
223 |
|
224 newWin.gBrowser.selectedBrowser.addEventListener("load", function () { |
|
225 newWin.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); |
|
226 |
|
227 // Good enough for checking the state |
|
228 afterTestCallback(previousClosedWindowData, ss.getClosedWindowData()); |
|
229 afterTestCleanup(newWin); |
|
230 }, true); |
|
231 |
|
232 }, false); |
|
233 } |
|
234 |
|
235 function afterTestCleanup(aNewWin) { |
|
236 executeSoon(function() { |
|
237 aNewWin.close(); |
|
238 document.documentElement.setAttribute("windowtype", originalWindowType); |
|
239 runNextTestOrFinish(); |
|
240 }); |
|
241 } |