tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_354894_perwindowpb.js (15159B)


      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 "use strict";
      6 
      7 /**
      8 * Checks that restoring the last browser window in session is actually
      9 * working.
     10 *
     11 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894
     12 * Note: It is implicitly tested that restoring the last window works when
     13 * non-browser windows are around. The "Run Tests" window as well as the main
     14 * browser window (wherein the test code gets executed) won't be considered
     15 * browser windows. To achiveve this said main browser window has its windowtype
     16 * attribute modified so that it's not considered a browser window any longer.
     17 * This is crucial, because otherwise there would be two browser windows around,
     18 * said main test window and the one opened by the tests, and hence the new
     19 * logic wouldn't be executed at all.
     20 * Note: Mac only tests the new notifications, as restoring the last window is
     21 * not enabled on that platform (platform shim; the application is kept running
     22 * although there are no windows left)
     23 * Note: There is a difference when closing a browser window with
     24 * BrowserCommands.tryToCloseWindow() as opposed to close(). The former will make
     25 * nsSessionStore restore a window next time it gets a chance and will post
     26 * notifications. The latter won't.
     27 */
     28 
     29 // The rejection "BrowserWindowTracker.getTopWindow(...) is null" is left
     30 // unhandled in some cases. This bug should be fixed, but for the moment this
     31 // file allows a class of rejections.
     32 //
     33 // NOTE: Allowing a whole class of rejections should be avoided. Normally you
     34 //       should use "expectUncaughtRejection" to flag individual failures.
     35 const { PromiseTestUtils } = ChromeUtils.importESModule(
     36  "resource://testing-common/PromiseTestUtils.sys.mjs"
     37 );
     38 PromiseTestUtils.allowMatchingRejectionsGlobally(/getTopWindow/);
     39 
     40 // Some urls that might be opened in tabs and/or popups
     41 // Do not use about:blank:
     42 // That one is reserved for special purposes in the tests
     43 const TEST_URLS = ["about:mozilla", "about:buildconfig"];
     44 
     45 // Number of -request notifications to except
     46 // remember to adjust when adding new tests
     47 const NOTIFICATIONS_EXPECTED = 6;
     48 
     49 // Window features of popup windows
     50 const POPUP_FEATURES = "toolbar=no,resizable=no,status=no";
     51 
     52 // Window features of browser windows
     53 const CHROME_FEATURES = "chrome,all,dialog=no";
     54 
     55 const IS_MAC = navigator.platform.match(/Mac/);
     56 
     57 /**
     58 * Returns an Object with two properties:
     59 *   open (int):
     60 *     A count of how many non-closed navigator:browser windows there are.
     61 *   winstates (int):
     62 *     A count of how many windows there are in the SessionStore state.
     63 */
     64 function getBrowserWindowsCount() {
     65  let open = 0;
     66  for (let win of Services.wm.getEnumerator("navigator:browser")) {
     67    if (!win.closed) {
     68      ++open;
     69    }
     70  }
     71 
     72  let winstates = JSON.parse(ss.getBrowserState()).windows.length;
     73 
     74  return { open, winstates };
     75 }
     76 
     77 add_setup(async function () {
     78  // Make sure we've only got one browser window to start with
     79  let { open, winstates } = getBrowserWindowsCount();
     80  is(open, 1, "Should only be one open window");
     81  is(winstates, 1, "Should only be one window state in SessionStore");
     82 
     83  // This test takes some time to run, and it could timeout randomly.
     84  // So we require a longer timeout. See bug 528219.
     85  requestLongerTimeout(3);
     86 
     87  // Make the main test window not count as a browser window any longer
     88  let oldWinType = document.documentElement.getAttribute("windowtype");
     89  document.documentElement.setAttribute("windowtype", "navigator:testrunner");
     90 
     91  registerCleanupFunction(() => {
     92    document.documentElement.setAttribute("windowtype", oldWinType);
     93  });
     94 });
     95 
     96 /**
     97 * Sets up one of our tests by setting the right preferences, and
     98 * then opening up a browser window preloaded with some tabs.
     99 *
    100 * @param options (Object)
    101 *        An object that can contain the following properties:
    102 *
    103 *        private:
    104 *          Whether or not the opened window should be private.
    105 *
    106 *        denyFirst:
    107 *          Whether or not the first window that attempts to close
    108 *          via closeWindowForRestoration should be denied.
    109 *
    110 * @param testFunction (Function*)
    111 *        A generator function that yields Promises to be run
    112 *        once the test has been set up.
    113 *
    114 * @returns Promise
    115 *        Resolves once the test has been cleaned up.
    116 */
    117 let setupTest = async function (options, testFunction) {
    118  await pushPrefs(
    119    ["browser.startup.page", 3],
    120    ["browser.tabs.warnOnClose", false]
    121  );
    122  // SessionStartup caches pref values, but as this test tries to simulate a
    123  // startup scenario, we'll reset them here.
    124  SessionStartup.resetForTest();
    125 
    126  // Observe these, and also use to count the number of hits
    127  let observing = {
    128    "browser-lastwindow-close-requested": 0,
    129    "browser-lastwindow-close-granted": 0,
    130  };
    131 
    132  /**
    133   * Helper: Will observe and handle the notifications for us
    134   */
    135  let hitCount = 0;
    136  function observer(aCancel, aTopic) {
    137    // count so that we later may compare
    138    observing[aTopic]++;
    139 
    140    // handle some tests
    141    if (options.denyFirst && ++hitCount == 1) {
    142      aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true;
    143    }
    144  }
    145 
    146  for (let o in observing) {
    147    Services.obs.addObserver(observer, o);
    148  }
    149 
    150  let newWin = await promiseNewWindowLoaded({
    151    private: options.private || false,
    152  });
    153 
    154  await injectTestTabs(newWin);
    155 
    156  await testFunction(newWin, observing);
    157 
    158  let count = getBrowserWindowsCount();
    159  is(count.open, 0, "Got right number of open windows");
    160  is(count.winstates, 1, "Got right number of stored window states");
    161 
    162  for (let o in observing) {
    163    Services.obs.removeObserver(observer, o);
    164  }
    165 
    166  await popPrefs();
    167  // Act like nothing ever happened.
    168  SessionStartup.resetForTest();
    169 };
    170 
    171 /**
    172 * Loads a TEST_URLS into a browser window.
    173 *
    174 * @param win (Window)
    175 *        The browser window to load the tabs in
    176 */
    177 function injectTestTabs(win) {
    178  let promises = TEST_URLS.map(url =>
    179    BrowserTestUtils.addTab(win.gBrowser, url)
    180  ).map(tab => BrowserTestUtils.browserLoaded(tab.linkedBrowser));
    181  return Promise.all(promises);
    182 }
    183 
    184 /**
    185 * Attempts to close a window via BrowserCommands.tryToCloseWindow so that
    186 * we get the browser-lastwindow-close-requested and
    187 * browser-lastwindow-close-granted observer notifications.
    188 *
    189 * @param win (Window)
    190 *        The window to try to close
    191 * @returns Promise
    192 *        Resolves to true if the window closed, or false if the window
    193 *        was denied the ability to close.
    194 */
    195 function closeWindowForRestoration(win) {
    196  return new Promise(resolve => {
    197    let closePromise = BrowserTestUtils.windowClosed(win);
    198    win.BrowserCommands.tryToCloseWindow();
    199    if (!win.closed) {
    200      resolve(false);
    201      return;
    202    }
    203 
    204    closePromise.then(() => {
    205      resolve(true);
    206    });
    207  });
    208 }
    209 
    210 /**
    211 * Normal in-session restore
    212 *
    213 * Note: Non-Mac only
    214 *
    215 * Should do the following:
    216 *  1. Open a new browser window
    217 *  2. Add some tabs
    218 *  3. Close that window
    219 *  4. Opening another window
    220 *  5. Checks that state is restored
    221 */
    222 add_task(async function test_open_close_normal() {
    223  if (IS_MAC) {
    224    return;
    225  }
    226 
    227  await setupTest({ denyFirst: true }, async function (newWin, obs) {
    228    let closed = await closeWindowForRestoration(newWin);
    229    ok(!closed, "First close request should have been denied");
    230 
    231    closed = await closeWindowForRestoration(newWin);
    232    ok(closed, "Second close request should be accepted");
    233 
    234    newWin = await promiseNewWindowLoaded();
    235    is(
    236      newWin.gBrowser.browsers.length,
    237      TEST_URLS.length + 2,
    238      "Restored window in-session with otherpopup windows around"
    239    );
    240 
    241    // Note that this will not result in the the browser-lastwindow-close
    242    // notifications firing for this other newWin.
    243    await BrowserTestUtils.closeWindow(newWin);
    244 
    245    // setupTest gave us a window which was denied for closing once, and then
    246    // closed.
    247    is(
    248      obs["browser-lastwindow-close-requested"],
    249      2,
    250      "Got expected browser-lastwindow-close-requested notifications"
    251    );
    252    is(
    253      obs["browser-lastwindow-close-granted"],
    254      1,
    255      "Got expected browser-lastwindow-close-granted notifications"
    256    );
    257  });
    258 });
    259 
    260 /**
    261 * PrivateBrowsing in-session restore
    262 *
    263 * Note: Non-Mac only
    264 *
    265 * Should do the following:
    266 *  1. Open a new browser window A
    267 *  2. Add some tabs
    268 *  3. Close the window A as the last window
    269 *  4. Open a private browsing window B
    270 *  5. Make sure that B didn't restore the tabs from A
    271 *  6. Close private browsing window B
    272 *  7. Open a new window C
    273 *  8. Make sure that new window C has restored tabs from A
    274 */
    275 add_task(async function test_open_close_private_browsing() {
    276  if (IS_MAC) {
    277    return;
    278  }
    279 
    280  await setupTest({}, async function (newWin, obs) {
    281    let closed = await closeWindowForRestoration(newWin);
    282    ok(closed, "Should be able to close the window");
    283 
    284    newWin = await promiseNewWindowLoaded({ private: true });
    285    is(
    286      newWin.gBrowser.browsers.length,
    287      1,
    288      "Did not restore in private browsing mode"
    289    );
    290 
    291    closed = await closeWindowForRestoration(newWin);
    292    ok(closed, "Should be able to close the window");
    293 
    294    newWin = await promiseNewWindowLoaded();
    295    is(
    296      newWin.gBrowser.browsers.length,
    297      TEST_URLS.length + 2,
    298      "Restored tabs in a new non-private window"
    299    );
    300 
    301    // Note that this will not result in the the browser-lastwindow-close
    302    // notifications firing for this other newWin.
    303    await BrowserTestUtils.closeWindow(newWin);
    304 
    305    // We closed two windows with closeWindowForRestoration, and both
    306    // should have been successful.
    307    is(
    308      obs["browser-lastwindow-close-requested"],
    309      2,
    310      "Got expected browser-lastwindow-close-requested notifications"
    311    );
    312    is(
    313      obs["browser-lastwindow-close-granted"],
    314      2,
    315      "Got expected browser-lastwindow-close-granted notifications"
    316    );
    317  });
    318 });
    319 
    320 /**
    321 * Open some popup window to check it isn't restored. Instead nothing at all
    322 * should be restored
    323 *
    324 * Note: Non-Mac only
    325 *
    326 * Should do the following:
    327 *  1. Open a popup
    328 *  2. Add another tab to the popup (so that it gets stored) and close it again
    329 *  3. Open a window
    330 *  4. Check that nothing at all is restored
    331 *  5. Open two browser windows and close them again
    332 *  6. undoCloseWindow() one
    333 *  7. Open another browser window
    334 *  8. Check that nothing at all is restored
    335 */
    336 add_task(async function test_open_close_only_popup() {
    337  if (IS_MAC) {
    338    return;
    339  }
    340 
    341  await setupTest({}, async function (newWin, obs) {
    342    // We actually don't care about the initial window in this test.
    343    await BrowserTestUtils.closeWindow(newWin);
    344 
    345    // This will cause nsSessionStore to restore a window the next time it
    346    // gets a chance.
    347    let popupPromise = BrowserTestUtils.waitForNewWindow();
    348    openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
    349    let popup = await popupPromise;
    350 
    351    is(
    352      popup.gBrowser.browsers.length,
    353      1,
    354      "Did not restore the popup window (1)"
    355    );
    356 
    357    let closed = await closeWindowForRestoration(popup);
    358    ok(closed, "Should be able to close the window");
    359 
    360    popupPromise = BrowserTestUtils.waitForNewWindow();
    361    openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
    362    popup = await popupPromise;
    363 
    364    BrowserTestUtils.addTab(popup.gBrowser, TEST_URLS[0]);
    365    is(
    366      popup.gBrowser.browsers.length,
    367      2,
    368      "Did not restore to the popup window (2)"
    369    );
    370 
    371    await BrowserTestUtils.closeWindow(popup);
    372 
    373    newWin = await promiseNewWindowLoaded();
    374    isnot(
    375      newWin.gBrowser.browsers.length,
    376      2,
    377      "Did not restore the popup window"
    378    );
    379    is(
    380      TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec),
    381      -1,
    382      "Did not restore the popup window (2)"
    383    );
    384    await BrowserTestUtils.closeWindow(newWin);
    385 
    386    // We closed one popup window with closeWindowForRestoration, and popup
    387    // windows should never fire the browser-lastwindow notifications.
    388    is(
    389      obs["browser-lastwindow-close-requested"],
    390      0,
    391      "Got expected browser-lastwindow-close-requested notifications"
    392    );
    393    is(
    394      obs["browser-lastwindow-close-granted"],
    395      0,
    396      "Got expected browser-lastwindow-close-granted notifications"
    397    );
    398  });
    399 });
    400 
    401 /**
    402 * Open some windows and do undoCloseWindow. This should prevent any
    403 * restoring later in the test
    404 *
    405 * Note: Non-Mac only
    406 *
    407 * Should do the following:
    408 *  1. Open two browser windows and close them again
    409 *  2. undoCloseWindow() one
    410 *  3. Open another browser window
    411 *  4. Make sure nothing at all is restored
    412 */
    413 add_task(async function test_open_close_restore_from_popup() {
    414  if (IS_MAC) {
    415    return;
    416  }
    417 
    418  await setupTest({}, async function (newWin) {
    419    let newWin2 = await promiseNewWindowLoaded();
    420    await injectTestTabs(newWin2);
    421 
    422    let closed = await closeWindowForRestoration(newWin);
    423    ok(closed, "Should be able to close the window");
    424    closed = await closeWindowForRestoration(newWin2);
    425    ok(closed, "Should be able to close the window");
    426 
    427    let counts = getBrowserWindowsCount();
    428    is(counts.open, 0, "Got right number of open windows");
    429    is(counts.winstates, 1, "Got right number of window states");
    430 
    431    newWin = SessionWindowUI.undoCloseWindow(0);
    432    await BrowserTestUtils.waitForEvent(newWin, "load");
    433 
    434    // Make sure we wait until this window is restored.
    435    await BrowserTestUtils.waitForEvent(
    436      newWin.gBrowser.tabContainer,
    437      "SSTabRestored"
    438    );
    439 
    440    newWin2 = await promiseNewWindowLoaded();
    441 
    442    is(
    443      TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec),
    444      -1,
    445      "Did not restore, as undoCloseWindow() was last called (2)"
    446    );
    447 
    448    counts = getBrowserWindowsCount();
    449    is(counts.open, 2, "Got right number of open windows");
    450    is(counts.winstates, 3, "Got right number of window states");
    451 
    452    await BrowserTestUtils.closeWindow(newWin);
    453    await BrowserTestUtils.closeWindow(newWin2);
    454 
    455    counts = getBrowserWindowsCount();
    456    is(counts.open, 0, "Got right number of open windows");
    457    is(counts.winstates, 1, "Got right number of window states");
    458  });
    459 });
    460 
    461 /**
    462 * Test if closing can be denied on Mac.
    463 *
    464 * Note: Mac only
    465 */
    466 add_task(async function test_mac_notifications() {
    467  if (!IS_MAC) {
    468    return;
    469  }
    470 
    471  await setupTest({ denyFirst: true }, async function (newWin, obs) {
    472    let closed = await closeWindowForRestoration(newWin);
    473    ok(!closed, "First close attempt should be denied");
    474    closed = await closeWindowForRestoration(newWin);
    475    ok(closed, "Second close attempt should be granted");
    476 
    477    // We tried closing once, and got denied. Then we tried again and
    478    // succeeded. That means 2 close requests, and 1 close granted.
    479    is(
    480      obs["browser-lastwindow-close-requested"],
    481      2,
    482      "Got expected browser-lastwindow-close-requested notifications"
    483    );
    484    is(
    485      obs["browser-lastwindow-close-granted"],
    486      1,
    487      "Got expected browser-lastwindow-close-granted notifications"
    488    );
    489  });
    490 });