tor-browser

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

browser_589246.js (8539B)


      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 SessionStore.sys.mjs
      6 const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
      7 
      8 var stateBackup = ss.getBrowserState();
      9 
     10 var originalWarnOnClose = Services.prefs.getBoolPref(
     11  "browser.tabs.warnOnClose"
     12 );
     13 var originalStartupPage = Services.prefs.getIntPref("browser.startup.page");
     14 var originalWindowType = document.documentElement.getAttribute("windowtype");
     15 
     16 var gotLastWindowClosedTopic = false;
     17 var shouldPinTab = false;
     18 var shouldOpenTabs = false;
     19 var shouldCloseTab = false;
     20 var testNum = 0;
     21 var afterTestCallback;
     22 
     23 // Set state so we know the closed windows content
     24 var testState = {
     25  windows: [{ tabs: [{ entries: [{ url: "http://example.org" }] }] }],
     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 var 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 SessionStore.sys.mjs.
     42    // This isn't the best approach, but neither is comparing JSON strings
     43    WINDOW_ATTRIBUTES.forEach(attr => delete expectedState[0][attr]);
     44 
     45    is(
     46      aCurState,
     47      JSON.stringify(expectedState),
     48      "test #" + num + ": closedWindowState is as expected"
     49    );
     50  };
     51 }
     52 function checkNoWindowsGenerator(num) {
     53  return function (aPreviousState, aCurState) {
     54    is(
     55      aCurState,
     56      "[]",
     57      "test #" + num + ": there should be no closedWindowsLeft"
     58    );
     59  };
     60 }
     61 
     62 // The first test has 0 pinned tabs and 1 unpinned tab
     63 tests.push({
     64  pinned: false,
     65  extra: false,
     66  close: false,
     67  checkWinLin: checkNoWindowsGenerator(1),
     68  checkOSX(aPreviousState, aCurState) {
     69    is(aCurState, aPreviousState, "test #1: closed window state is unchanged");
     70  },
     71 });
     72 
     73 // The second test has 1 pinned tab and 0 unpinned tabs.
     74 tests.push({
     75  pinned: true,
     76  extra: false,
     77  close: false,
     78  checkWinLin: checkNoWindowsGenerator(2),
     79  checkOSX: checkNoWindowsGenerator(2),
     80 });
     81 
     82 // The third test has 1 pinned tab and 2 unpinned tabs.
     83 tests.push({
     84  pinned: true,
     85  extra: true,
     86  close: false,
     87  checkWinLin: checkNoWindowsGenerator(3),
     88  checkOSX: checkOSX34Generator(3),
     89 });
     90 
     91 // The fourth test has 1 pinned tab, 2 unpinned tabs, and closes one unpinned tab.
     92 tests.push({
     93  pinned: true,
     94  extra: true,
     95  close: "one",
     96  checkWinLin: checkNoWindowsGenerator(4),
     97  checkOSX: checkOSX34Generator(4),
     98 });
     99 
    100 // The fifth test has 1 pinned tab, 2 unpinned tabs, and closes both unpinned tabs.
    101 tests.push({
    102  pinned: true,
    103  extra: true,
    104  close: "both",
    105  checkWinLin: checkNoWindowsGenerator(5),
    106  checkOSX: checkNoWindowsGenerator(5),
    107 });
    108 
    109 function test() {
    110  /**
    111   * Test for Bug 589246 - Closed window state getting corrupted when closing
    112   * and reopening last browser window without exiting browser
    113   */
    114  waitForExplicitFinish();
    115  // windows opening & closing, so extending the timeout
    116  requestLongerTimeout(2);
    117 
    118  // We don't want the quit dialog pref
    119  Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
    120  // Ensure that we would restore the session (important for Windows)
    121  Services.prefs.setIntPref("browser.startup.page", 3);
    122 
    123  runNextTestOrFinish();
    124 }
    125 
    126 function runNextTestOrFinish() {
    127  if (tests.length) {
    128    setupForTest(tests.shift());
    129  } else {
    130    // some state is cleaned up at the end of each test, but not all
    131    ["browser.tabs.warnOnClose", "browser.startup.page"].forEach(function (p) {
    132      if (Services.prefs.prefHasUserValue(p)) {
    133        Services.prefs.clearUserPref(p);
    134      }
    135    });
    136 
    137    ss.setBrowserState(stateBackup);
    138    executeSoon(finish);
    139  }
    140 }
    141 
    142 function setupForTest(aConditions) {
    143  // reset some checks
    144  gotLastWindowClosedTopic = false;
    145  shouldPinTab = aConditions.pinned;
    146  shouldOpenTabs = aConditions.extra;
    147  shouldCloseTab = aConditions.close;
    148  testNum++;
    149 
    150  // set our test callback
    151  afterTestCallback = /Mac/.test(navigator.platform)
    152    ? aConditions.checkOSX
    153    : aConditions.checkWinLin;
    154 
    155  // Add observers
    156  Services.obs.addObserver(
    157    onLastWindowClosed,
    158    "browser-lastwindow-close-granted"
    159  );
    160 
    161  // Set the state
    162  Services.obs.addObserver(
    163    onStateRestored,
    164    "sessionstore-browser-state-restored"
    165  );
    166  ss.setBrowserState(JSON.stringify(testState));
    167 }
    168 
    169 function onStateRestored() {
    170  info("test #" + testNum + ": onStateRestored");
    171  Services.obs.removeObserver(
    172    onStateRestored,
    173    "sessionstore-browser-state-restored"
    174  );
    175 
    176  // change this window's windowtype so that closing a new window will trigger
    177  // browser-lastwindow-close-granted.
    178  document.documentElement.setAttribute("windowtype", "navigator:testrunner");
    179 
    180  let newWin = openDialog(
    181    location,
    182    "_blank",
    183    "chrome,all,dialog=no",
    184    "http://example.com"
    185  );
    186  newWin.addEventListener(
    187    "load",
    188    function () {
    189      promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => {
    190        // pin this tab
    191        if (shouldPinTab) {
    192          newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab);
    193        }
    194 
    195        newWin.addEventListener(
    196          "unload",
    197          function () {
    198            onWindowUnloaded();
    199          },
    200          { once: true }
    201        );
    202        // Open a new tab as well. On Windows/Linux this will be restored when the
    203        // new window is opened below (in onWindowUnloaded). On OS X we'll just
    204        // restore the pinned tabs, leaving the unpinned tab in the closedWindowsData.
    205        if (shouldOpenTabs) {
    206          let newTab = BrowserTestUtils.addTab(newWin.gBrowser, "about:config");
    207          let newTab2 = BrowserTestUtils.addTab(
    208            newWin.gBrowser,
    209            "about:buildconfig"
    210          );
    211 
    212          newTab.linkedBrowser.addEventListener(
    213            "load",
    214            function () {
    215              if (shouldCloseTab == "one") {
    216                newWin.gBrowser.removeTab(newTab2);
    217              } else if (shouldCloseTab == "both") {
    218                newWin.gBrowser.removeTab(newTab);
    219                newWin.gBrowser.removeTab(newTab2);
    220              }
    221              newWin.BrowserCommands.tryToCloseWindow();
    222            },
    223            { capture: true, once: true }
    224          );
    225        } else {
    226          newWin.BrowserCommands.tryToCloseWindow();
    227        }
    228      });
    229    },
    230    { once: true }
    231  );
    232 }
    233 
    234 // This will be called before the window is actually closed
    235 function onLastWindowClosed() {
    236  info("test #" + testNum + ": onLastWindowClosed");
    237  Services.obs.removeObserver(
    238    onLastWindowClosed,
    239    "browser-lastwindow-close-granted"
    240  );
    241  gotLastWindowClosedTopic = true;
    242 }
    243 
    244 // This is the unload event listener on the new window (from onStateRestored).
    245 // Unload is fired after the window is closed, so sessionstore has already
    246 // updated _closedWindows (which is important). We'll open a new window here
    247 // which should actually trigger the bug.
    248 function onWindowUnloaded() {
    249  info("test #" + testNum + ": onWindowClosed");
    250  ok(
    251    gotLastWindowClosedTopic,
    252    "test #" + testNum + ": browser-lastwindow-close-granted was notified prior"
    253  );
    254 
    255  let previousClosedWindowData = ss.getClosedWindowData();
    256 
    257  // Now we want to open a new window
    258  let newWin = openDialog(
    259    location,
    260    "_blank",
    261    "chrome,all,dialog=no",
    262    "about:mozilla"
    263  );
    264  newWin.addEventListener(
    265    "load",
    266    function () {
    267      newWin.gBrowser.selectedBrowser.addEventListener(
    268        "load",
    269        function () {
    270          // Good enough for checking the state
    271          afterTestCallback(previousClosedWindowData, ss.getClosedWindowData());
    272          afterTestCleanup(newWin);
    273        },
    274        { capture: true, once: true }
    275      );
    276    },
    277    { once: true }
    278  );
    279 }
    280 
    281 function afterTestCleanup(aNewWin) {
    282  executeSoon(function () {
    283    BrowserTestUtils.closeWindow(aNewWin).then(() => {
    284      document.documentElement.setAttribute("windowtype", originalWindowType);
    285      runNextTestOrFinish();
    286    });
    287  });
    288 }