|
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 /** |
|
6 * Checks that restoring the last browser window in session is actually |
|
7 * working: |
|
8 * 1.1) Open a new browser window |
|
9 * 1.2) Add some tabs |
|
10 * 1.3) Close that window |
|
11 * 1.4) Opening another window |
|
12 * --> State is restored |
|
13 * |
|
14 * 2.1) Open a new browser window |
|
15 * 2.2) Add some tabs |
|
16 * 2.3) Enter private browsing mode |
|
17 * 2.4) Close the window while still in private browsing mode |
|
18 * 2.5) Opening a new window |
|
19 * --> State is not restored, because private browsing mode is still active |
|
20 * 2.6) Leaving private browsing mode |
|
21 * 2.7) Open another window |
|
22 * --> State (that was before entering PBM) is restored |
|
23 * |
|
24 * 3.1) Open a new browser window |
|
25 * 3.2) Add some tabs |
|
26 * 3.4) Open some popups |
|
27 * 3.5) Add another tab to one popup (so that it gets stored) and close it again |
|
28 * 3.5) Close the browser window |
|
29 * 3.6) Open another browser window |
|
30 * --> State of the closed browser window, but not of the popup, is restored |
|
31 * |
|
32 * 4.1) Open a popup |
|
33 * 4.2) Add another tab to the popup (so that it gets stored) and close it again |
|
34 * 4.3) Open a window |
|
35 * --> Nothing at all should be restored |
|
36 * |
|
37 * 5.1) Open two browser windows and close them again |
|
38 * 5.2) undoCloseWindow() one |
|
39 * 5.3) Open another browser window |
|
40 * --> Nothing at all should be restored |
|
41 * |
|
42 * Checks the new notifications are correctly posted and processed, that is |
|
43 * for each successful -requested a -granted is received, but omitted if |
|
44 * -requested was cnceled |
|
45 * Said notifications are: |
|
46 * - browser-lastwindow-close-requested |
|
47 * - browser-lastwindow-close-granted |
|
48 * Tests are: |
|
49 * 6) Cancel closing when first observe a -requested |
|
50 * --> Window is kept open |
|
51 * 7) Count the number of notifications |
|
52 * --> count(-requested) == count(-granted) + 1 |
|
53 * --> (The first -requested was canceled, so off-by-one) |
|
54 * 8) (Mac only) Mac version of Test 5 additionally preparing Test 6 |
|
55 * |
|
56 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894 |
|
57 * @note It is implicitly tested that restoring the last window works when |
|
58 * non-browser windows are around. The "Run Tests" window as well as the main |
|
59 * browser window (wherein the test code gets executed) won't be considered |
|
60 * browser windows. To achiveve this said main browser window has it's windowtype |
|
61 * attribute modified so that it's not considered a browser window any longer. |
|
62 * This is crucial, because otherwise there would be two browser windows around, |
|
63 * said main test window and the one opened by the tests, and hence the new |
|
64 * logic wouldn't be executed at all. |
|
65 * @note Mac only tests the new notifications, as restoring the last window is |
|
66 * not enabled on that platform (platform shim; the application is kept running |
|
67 * although there are no windows left) |
|
68 * @note There is a difference when closing a browser window with |
|
69 * BrowserTryToCloseWindow() as opposed to close(). The former will make |
|
70 * nsSessionStore restore a window next time it gets a chance and will post |
|
71 * notifications. The latter won't. |
|
72 */ |
|
73 |
|
74 function browserWindowsCount(expected, msg) { |
|
75 if (typeof expected == "number") |
|
76 expected = [expected, expected]; |
|
77 let count = 0; |
|
78 let e = Services.wm.getEnumerator("navigator:browser"); |
|
79 while (e.hasMoreElements()) { |
|
80 if (!e.getNext().closed) |
|
81 ++count; |
|
82 } |
|
83 is(count, expected[0], msg + " (nsIWindowMediator)"); |
|
84 let state = ss.getBrowserState(); |
|
85 is(JSON.parse(state).windows.length, expected[1], msg + " (getBrowserState)"); |
|
86 } |
|
87 |
|
88 function test() { |
|
89 browserWindowsCount(1, "Only one browser window should be open initially"); |
|
90 |
|
91 waitForExplicitFinish(); |
|
92 // This test takes some time to run, and it could timeout randomly. |
|
93 // So we require a longer timeout. See bug 528219. |
|
94 requestLongerTimeout(2); |
|
95 |
|
96 // Some urls that might be opened in tabs and/or popups |
|
97 // Do not use about:blank: |
|
98 // That one is reserved for special purposes in the tests |
|
99 const TEST_URLS = ["about:mozilla", "about:buildconfig"]; |
|
100 |
|
101 // Number of -request notifications to except |
|
102 // remember to adjust when adding new tests |
|
103 const NOTIFICATIONS_EXPECTED = 6; |
|
104 |
|
105 // Window features of popup windows |
|
106 const POPUP_FEATURES = "toolbar=no,resizable=no,status=no"; |
|
107 |
|
108 // Window features of browser windows |
|
109 const CHROME_FEATURES = "chrome,all,dialog=no"; |
|
110 |
|
111 // Store the old window type for cleanup |
|
112 let oldWinType = ""; |
|
113 // Store the old tabs.warnOnClose pref so that we may reset it during |
|
114 // cleanup |
|
115 let oldWarnTabsOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose"); |
|
116 |
|
117 // Observe these, and also use to count the number of hits |
|
118 let observing = { |
|
119 "browser-lastwindow-close-requested": 0, |
|
120 "browser-lastwindow-close-granted": 0 |
|
121 }; |
|
122 |
|
123 /** |
|
124 * Helper: Will observe and handle the notifications for us |
|
125 */ |
|
126 let hitCount = 0; |
|
127 function observer(aCancel, aTopic, aData) { |
|
128 // count so that we later may compare |
|
129 observing[aTopic]++; |
|
130 |
|
131 // handle some tests |
|
132 if (++hitCount == 1) { |
|
133 // Test 6 |
|
134 aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true; |
|
135 } |
|
136 } |
|
137 |
|
138 /** |
|
139 * Helper: Sets prefs as the testsuite requires |
|
140 * @note Will be reset in cleanTestSuite just before finishing the tests |
|
141 */ |
|
142 function setPrefs() { |
|
143 gPrefService.setIntPref("browser.startup.page", 3); |
|
144 gPrefService.setBoolPref( |
|
145 "browser.privatebrowsing.keep_current_session", |
|
146 true |
|
147 ); |
|
148 gPrefService.setBoolPref("browser.tabs.warnOnClose", false); |
|
149 } |
|
150 |
|
151 /** |
|
152 * Helper: Sets up this testsuite |
|
153 */ |
|
154 function setupTestsuite(testFn) { |
|
155 // Register our observers |
|
156 for (let o in observing) |
|
157 Services.obs.addObserver(observer, o, false); |
|
158 |
|
159 // Make the main test window not count as a browser window any longer |
|
160 oldWinType = document.documentElement.getAttribute("windowtype"); |
|
161 document.documentElement.setAttribute("windowtype", "navigator:testrunner"); |
|
162 } |
|
163 |
|
164 /** |
|
165 * Helper: Cleans up behind the testsuite |
|
166 */ |
|
167 function cleanupTestsuite(callback) { |
|
168 // Finally remove observers again |
|
169 for (let o in observing) |
|
170 Services.obs.removeObserver(observer, o); |
|
171 |
|
172 // Reset the prefs we touched |
|
173 [ |
|
174 "browser.startup.page", |
|
175 "browser.privatebrowsing.keep_current_session" |
|
176 ].forEach(function (pref) { |
|
177 if (gPrefService.prefHasUserValue(pref)) |
|
178 gPrefService.clearUserPref(pref); |
|
179 }); |
|
180 gPrefService.setBoolPref("browser.tabs.warnOnClose", oldWarnTabsOnClose); |
|
181 |
|
182 // Reset the window type |
|
183 document.documentElement.setAttribute("windowtype", oldWinType); |
|
184 } |
|
185 |
|
186 /** |
|
187 * Helper: sets the prefs and a new window with our test tabs |
|
188 */ |
|
189 function setupTestAndRun(aIsPrivateWindow, testFn) { |
|
190 // Prepare the prefs |
|
191 setPrefs(); |
|
192 |
|
193 // Prepare a window; open it and add more tabs |
|
194 let options = {}; |
|
195 if (aIsPrivateWindow) { |
|
196 options = {private: true}; |
|
197 } |
|
198 |
|
199 whenNewWindowLoaded(options, function (newWin) { |
|
200 TEST_URLS.forEach(function (url) { |
|
201 newWin.gBrowser.addTab(url); |
|
202 }); |
|
203 |
|
204 executeSoon(() => testFn(newWin)); |
|
205 }); |
|
206 } |
|
207 |
|
208 /** |
|
209 * Test 1: Normal in-session restore |
|
210 * @note: Non-Mac only |
|
211 */ |
|
212 function testOpenCloseNormal(nextFn) { |
|
213 setupTestAndRun(false, function(newWin) { |
|
214 // Close the window |
|
215 // window.close doesn't push any close events, |
|
216 // so use BrowserTryToCloseWindow |
|
217 newWin.BrowserTryToCloseWindow(); |
|
218 |
|
219 // The first request to close is denied by our observer (Test 6) |
|
220 ok(!newWin.closed, "First close request was denied"); |
|
221 if (!newWin.closed) { |
|
222 newWin.BrowserTryToCloseWindow(); |
|
223 ok(newWin.closed, "Second close request was granted"); |
|
224 } |
|
225 |
|
226 // Open a new window |
|
227 // The previously closed window should be restored |
|
228 whenNewWindowLoaded({}, function (newWin) { |
|
229 is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1, |
|
230 "Restored window in-session with otherpopup windows around"); |
|
231 |
|
232 // Cleanup |
|
233 newWin.close(); |
|
234 |
|
235 // Next please |
|
236 executeSoon(nextFn); |
|
237 }); |
|
238 }); |
|
239 } |
|
240 |
|
241 /** |
|
242 * Test 2: PrivateBrowsing in-session restore |
|
243 * @note: Non-Mac only |
|
244 */ |
|
245 function testOpenClosePrivateBrowsing(nextFn) { |
|
246 setupTestAndRun(false, function(newWin) { |
|
247 // Close the window |
|
248 newWin.BrowserTryToCloseWindow(); |
|
249 |
|
250 // Enter private browsing mode |
|
251 // Open a new window. |
|
252 // The previously closed window should NOT be restored |
|
253 whenNewWindowLoaded({private: true}, function (newWin) { |
|
254 is(newWin.gBrowser.browsers.length, 1, |
|
255 "Did not restore in private browing mode"); |
|
256 |
|
257 // Cleanup |
|
258 newWin.BrowserTryToCloseWindow(); |
|
259 |
|
260 // Exit private browsing mode again |
|
261 whenNewWindowLoaded({}, function (newWin) { |
|
262 is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1, |
|
263 "Restored after leaving private browsing again"); |
|
264 |
|
265 newWin.close(); |
|
266 |
|
267 // Next please |
|
268 executeSoon(nextFn); |
|
269 }); |
|
270 }); |
|
271 }); |
|
272 } |
|
273 |
|
274 /** |
|
275 * Test 3: Open some popup windows to check those aren't restored, but |
|
276 * the browser window is |
|
277 * @note: Non-Mac only |
|
278 */ |
|
279 function testOpenCloseWindowAndPopup(nextFn) { |
|
280 setupTestAndRun(false, function(newWin) { |
|
281 // open some popups |
|
282 let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]); |
|
283 let popup2 = openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]); |
|
284 popup2.addEventListener("load", function() { |
|
285 popup2.removeEventListener("load", arguments.callee, false); |
|
286 popup2.gBrowser.addEventListener("load", function() { |
|
287 popup2.gBrowser.removeEventListener("load", arguments.callee, true); |
|
288 popup2.gBrowser.addTab(TEST_URLS[0]); |
|
289 // close the window |
|
290 newWin.BrowserTryToCloseWindow(); |
|
291 |
|
292 // Close the popup window |
|
293 // The test is successful when not this popup window is restored |
|
294 // but instead newWin |
|
295 popup2.close(); |
|
296 |
|
297 // open a new window the previously closed window should be restored to |
|
298 whenNewWindowLoaded({}, function (newWin) { |
|
299 is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1, |
|
300 "Restored window and associated tabs in session"); |
|
301 |
|
302 // Cleanup |
|
303 newWin.close(); |
|
304 popup.close(); |
|
305 |
|
306 // Next please |
|
307 executeSoon(nextFn); |
|
308 }); |
|
309 }, true); |
|
310 }, false); |
|
311 }); |
|
312 } |
|
313 |
|
314 /** |
|
315 * Test 4: Open some popup window to check it isn't restored. |
|
316 * Instead nothing at all should be restored |
|
317 * @note: Non-Mac only |
|
318 */ |
|
319 function testOpenCloseOnlyPopup(nextFn) { |
|
320 // prepare the prefs |
|
321 setPrefs(); |
|
322 |
|
323 // This will cause nsSessionStore to restore a window the next time it |
|
324 // gets a chance. |
|
325 let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]); |
|
326 popup.addEventListener("load", function() { |
|
327 this.removeEventListener("load", arguments.callee, true); |
|
328 is(popup.gBrowser.browsers.length, 1, |
|
329 "Did not restore the popup window (1)"); |
|
330 popup.BrowserTryToCloseWindow(); |
|
331 |
|
332 // Real tests |
|
333 popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]); |
|
334 popup.addEventListener("load", function() { |
|
335 popup.removeEventListener("load", arguments.callee, false); |
|
336 popup.gBrowser.addEventListener("load", function() { |
|
337 popup.gBrowser.removeEventListener("load", arguments.callee, true); |
|
338 popup.gBrowser.addTab(TEST_URLS[0]); |
|
339 |
|
340 is(popup.gBrowser.browsers.length, 2, |
|
341 "Did not restore to the popup window (2)"); |
|
342 |
|
343 // Close the popup window |
|
344 // The test is successful when not this popup window is restored |
|
345 // but instead a new window is opened without restoring anything |
|
346 popup.close(); |
|
347 |
|
348 whenNewWindowLoaded({}, function (newWin) { |
|
349 isnot(newWin.gBrowser.browsers.length, 2, |
|
350 "Did not restore the popup window"); |
|
351 is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1, |
|
352 "Did not restore the popup window (2)"); |
|
353 |
|
354 // Cleanup |
|
355 newWin.close(); |
|
356 |
|
357 // Next please |
|
358 executeSoon(nextFn); |
|
359 }); |
|
360 }, true); |
|
361 }, false); |
|
362 }, true); |
|
363 } |
|
364 |
|
365 /** |
|
366 * Test 5: Open some windows and do undoCloseWindow. This should prevent any |
|
367 * restoring later in the test |
|
368 * @note: Non-Mac only |
|
369 */ |
|
370 function testOpenCloseRestoreFromPopup(nextFn) { |
|
371 setupTestAndRun(false, function(newWin) { |
|
372 setupTestAndRun(false, function(newWin2) { |
|
373 newWin.BrowserTryToCloseWindow(); |
|
374 newWin2.BrowserTryToCloseWindow(); |
|
375 |
|
376 browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup"); |
|
377 |
|
378 newWin = undoCloseWindow(0); |
|
379 |
|
380 whenNewWindowLoaded({}, function (newWin2) { |
|
381 is(newWin2.gBrowser.browsers.length, 1, |
|
382 "Did not restore, as undoCloseWindow() was last called"); |
|
383 is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1, |
|
384 "Did not restore, as undoCloseWindow() was last called (2)"); |
|
385 |
|
386 browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup"); |
|
387 |
|
388 // Cleanup |
|
389 newWin.close(); |
|
390 newWin2.close(); |
|
391 |
|
392 browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup"); |
|
393 |
|
394 // Next please |
|
395 executeSoon(nextFn); |
|
396 }); |
|
397 }); |
|
398 }); |
|
399 } |
|
400 |
|
401 /** |
|
402 * Test 7: Check whether the right number of notifications was received during |
|
403 * the tests |
|
404 */ |
|
405 function testNotificationCount(nextFn) { |
|
406 is(observing["browser-lastwindow-close-requested"], NOTIFICATIONS_EXPECTED, |
|
407 "browser-lastwindow-close-requested notifications observed"); |
|
408 |
|
409 // -request must be one more as we cancel the first one we hit, |
|
410 // and hence won't produce a corresponding -grant |
|
411 // @see observer.observe |
|
412 is(observing["browser-lastwindow-close-requested"], |
|
413 observing["browser-lastwindow-close-granted"] + 1, |
|
414 "Notification count for -request and -grant matches"); |
|
415 |
|
416 executeSoon(nextFn); |
|
417 } |
|
418 |
|
419 /** |
|
420 * Test 8: Test if closing can be denied on Mac |
|
421 * Futhermore prepares the testNotificationCount test (Test 7) |
|
422 * @note: Mac only |
|
423 */ |
|
424 function testMacNotifications(nextFn, iteration) { |
|
425 iteration = iteration || 1; |
|
426 setupTestAndRun(false, function(newWin) { |
|
427 // close the window |
|
428 // window.close doesn't push any close events, |
|
429 // so use BrowserTryToCloseWindow |
|
430 newWin.BrowserTryToCloseWindow(); |
|
431 if (iteration == 1) { |
|
432 ok(!newWin.closed, "First close attempt denied"); |
|
433 if (!newWin.closed) { |
|
434 newWin.BrowserTryToCloseWindow(); |
|
435 ok(newWin.closed, "Second close attempt granted"); |
|
436 } |
|
437 } |
|
438 |
|
439 if (iteration < NOTIFICATIONS_EXPECTED - 1) { |
|
440 executeSoon(function() testMacNotifications(nextFn, ++iteration)); |
|
441 } |
|
442 else { |
|
443 executeSoon(nextFn); |
|
444 } |
|
445 }); |
|
446 } |
|
447 |
|
448 // Execution starts here |
|
449 |
|
450 setupTestsuite(); |
|
451 if (navigator.platform.match(/Mac/)) { |
|
452 // Mac tests |
|
453 testMacNotifications(function () { |
|
454 testNotificationCount(function () { |
|
455 cleanupTestsuite(); |
|
456 browserWindowsCount(1, "Only one browser window should be open eventually"); |
|
457 finish(); |
|
458 }); |
|
459 }); |
|
460 } |
|
461 else { |
|
462 // Non-Mac Tests |
|
463 testOpenCloseNormal(function () { |
|
464 browserWindowsCount([0, 1], "browser windows after testOpenCloseNormal"); |
|
465 testOpenClosePrivateBrowsing(function () { |
|
466 browserWindowsCount([0, 1], "browser windows after testOpenClosePrivateBrowsing"); |
|
467 testOpenCloseWindowAndPopup(function () { |
|
468 browserWindowsCount([0, 1], "browser windows after testOpenCloseWindowAndPopup"); |
|
469 testOpenCloseOnlyPopup(function () { |
|
470 browserWindowsCount([0, 1], "browser windows after testOpenCloseOnlyPopup"); |
|
471 testOpenCloseRestoreFromPopup(function () { |
|
472 browserWindowsCount([0, 1], "browser windows after testOpenCloseRestoreFromPopup"); |
|
473 testNotificationCount(function () { |
|
474 cleanupTestsuite(); |
|
475 browserWindowsCount(1, "browser windows after testNotificationCount"); |
|
476 finish(); |
|
477 }); |
|
478 }); |
|
479 }); |
|
480 }); |
|
481 }); |
|
482 }); |
|
483 } |
|
484 } |