tor-browser

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

browser_restoreLastClosedTabOrWindowOrSession.js (10403B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { _LastSession, _lastClosedActions } = ChromeUtils.importESModule(
      7  "resource:///modules/sessionstore/SessionStore.sys.mjs"
      8 );
      9 
     10 async function testLastClosedActionsEntries() {
     11  SessionStore.resetLastClosedActions();
     12 
     13  let win2 = await BrowserTestUtils.openNewBrowserWindow();
     14  BrowserTestUtils.startLoadingURIString(
     15    win2.gBrowser.selectedBrowser,
     16    "https://www.mozilla.org/"
     17  );
     18 
     19  await BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
     20  await openAndCloseTab(win2, "https://example.org/");
     21 
     22  Assert.equal(
     23    SessionStore.lastClosedActions.length,
     24    1,
     25    `1 closed action has been recorded`
     26  );
     27 
     28  await BrowserTestUtils.closeWindow(win2);
     29 
     30  Assert.equal(
     31    SessionStore.lastClosedActions.length,
     32    2,
     33    `2 closed actions have been recorded`
     34  );
     35 }
     36 
     37 add_setup(() => {
     38  forgetClosedTabs(window);
     39  registerCleanupFunction(() => {
     40    forgetClosedTabs(window);
     41  });
     42 
     43  // needed for verify tests so that forgetting tabs isn't recorded
     44  SessionStore.resetLastClosedActions();
     45 });
     46 
     47 /**
     48 * Tests that if the user invokes restoreLastClosedTabOrWindowOrSession it will
     49 * result in either the last session will be restored, if possible, the last
     50 * tab (or multiple selected tabs) that was closed is reopened, or the last
     51 * window that is closed is reopened.
     52 */
     53 add_task(async function test_undo_last_action() {
     54  const state = {
     55    windows: [
     56      {
     57        tabs: [
     58          {
     59            entries: [
     60              {
     61                title: "example.org",
     62                url: "https://example.org/",
     63                triggeringPrincipal_base64,
     64              },
     65            ],
     66          },
     67          {
     68            entries: [
     69              {
     70                title: "example.com",
     71                url: "https://example.com/",
     72                triggeringPrincipal_base64,
     73              },
     74            ],
     75          },
     76          {
     77            entries: [
     78              {
     79                title: "mozilla",
     80                url: "https://www.mozilla.org/",
     81                triggeringPrincipal_base64,
     82              },
     83            ],
     84          },
     85        ],
     86        selected: 3,
     87      },
     88    ],
     89  };
     90 
     91  _LastSession.setState(state);
     92 
     93  let sessionRestored = promiseSessionStoreLoads(3 /* total restored tabs */);
     94  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
     95  await sessionRestored;
     96  Assert.equal(
     97    window.gBrowser.tabs.length,
     98    3,
     99    "Window has three tabs after session is restored"
    100  );
    101 
    102  // open and close a window, then reopen it
    103  let win2 = await BrowserTestUtils.openNewBrowserWindow();
    104  Assert.equal(win2.gBrowser.tabs.length, 1, "Second window has one open tab");
    105  BrowserTestUtils.startLoadingURIString(
    106    win2.gBrowser.selectedBrowser,
    107    "https://example.com/"
    108  );
    109  await BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
    110  await BrowserTestUtils.closeWindow(win2);
    111  let restoredWinPromise = BrowserTestUtils.waitForNewWindow({
    112    url: "https://example.com/",
    113  });
    114  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    115  let restoredWin = await restoredWinPromise;
    116  Assert.equal(
    117    restoredWin.gBrowser.tabs.length,
    118    1,
    119    "First tab in the second window has been reopened"
    120  );
    121  await BrowserTestUtils.closeWindow(restoredWin);
    122  SessionStore.forgetClosedWindow(restoredWin.index);
    123  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    124 
    125  // close one tab and reopen it
    126  BrowserTestUtils.removeTab(window.gBrowser.tabs[2]);
    127  Assert.equal(window.gBrowser.tabs.length, 2, "Window has two open tabs");
    128  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    129  Assert.equal(
    130    window.gBrowser.tabs.length,
    131    3,
    132    "Window now has three open tabs"
    133  );
    134 
    135  // select 2 tabs and close both via the 'close 2 tabs' context menu option
    136  let tab2 = window.gBrowser.tabs[1];
    137  let tab3 = window.gBrowser.tabs[2];
    138  await triggerClickOn(tab2, { ctrlKey: true });
    139  Assert.equal(tab2.multiselected, true);
    140  Assert.equal(tab3.multiselected, true);
    141 
    142  let menu = await openTabMenuFor(tab3);
    143  let menuItemCloseTab = document.getElementById("context_closeTab");
    144  let tab2Closing = BrowserTestUtils.waitForTabClosing(tab2);
    145  let tab3Closing = BrowserTestUtils.waitForTabClosing(tab3);
    146  menu.activateItem(menuItemCloseTab);
    147  await tab2Closing;
    148  await tab3Closing;
    149  Assert.equal(window.gBrowser.tabs[0].selected, true);
    150  await TestUtils.waitForCondition(() => window.gBrowser.tabs.length == 1);
    151  Assert.equal(
    152    window.gBrowser.tabs.length,
    153    1,
    154    "Window now has one open tab after closing two multi-selected tabs"
    155  );
    156 
    157  // ensure both tabs are reopened with a single click
    158  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    159  Assert.equal(
    160    window.gBrowser.tabs.length,
    161    3,
    162    "Window now has three open tabs after reopening closed tabs"
    163  );
    164 
    165  // close one tab and forget it - it should not be reopened
    166  BrowserTestUtils.removeTab(window.gBrowser.tabs[2]);
    167  Assert.equal(window.gBrowser.tabs.length, 2, "Window has two open tabs");
    168  SessionStore.forgetClosedTab(window, 0);
    169  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    170  Assert.equal(
    171    window.gBrowser.tabs.length,
    172    2,
    173    "Window still has two open tabs"
    174  );
    175 
    176  // create a tab group, delete it, and re-open it
    177  const groupedTab = BrowserTestUtils.addTab(
    178    window.gBrowser,
    179    "https://example.com"
    180  );
    181  await BrowserTestUtils.browserLoaded(groupedTab.linkedBrowser);
    182  const tabGroup = window.gBrowser.addTabGroup([groupedTab]);
    183  Assert.equal(
    184    window.gBrowser.tabs.length,
    185    3,
    186    "Window has three tabs after creating tab group"
    187  );
    188 
    189  await TabStateFlusher.flushWindow(window);
    190  let removePromise = BrowserTestUtils.waitForEvent(
    191    tabGroup,
    192    "TabGroupRemoved"
    193  );
    194  window.gBrowser.removeTabGroup(tabGroup);
    195  await removePromise;
    196 
    197  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    198  Assert.equal(
    199    window.gBrowser.tabs.length,
    200    3,
    201    "Window has three tabs after restoring tab group"
    202  );
    203  Assert.equal(
    204    window.gBrowser.tabGroups.length,
    205    1,
    206    "Tab group exists on the tab strip after restore"
    207  );
    208 
    209  // Save and close the tab group, and then re-open it
    210  const groupedTabToSave = BrowserTestUtils.addTab(
    211    window.gBrowser,
    212    "https://example.com"
    213  );
    214  await BrowserTestUtils.browserLoaded(groupedTabToSave.linkedBrowser);
    215  const tabGroupToSave = window.gBrowser.addTabGroup([groupedTabToSave]);
    216  Assert.equal(
    217    window.gBrowser.tabs.length,
    218    4,
    219    "Window has four tabs after creating tab group"
    220  );
    221 
    222  await TabStateFlusher.flushWindow(window);
    223  tabGroupToSave.save();
    224  removePromise = BrowserTestUtils.waitForEvent(
    225    tabGroupToSave,
    226    "TabGroupRemoved"
    227  );
    228  window.gBrowser.removeTabGroup(tabGroupToSave);
    229  await removePromise;
    230 
    231  await TabStateFlusher.flushWindow(window);
    232  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    233  Assert.equal(
    234    window.gBrowser.tabs.length,
    235    4,
    236    "Window has four tabs after restoring tab group"
    237  );
    238  Assert.equal(
    239    window.gBrowser.tabGroups.length,
    240    2,
    241    "Tab group exists on the tab strip after restore"
    242  );
    243 
    244  gBrowser.removeAllTabsBut(gBrowser.tabs[0]);
    245 });
    246 
    247 add_task(async function test_forget_closed_window() {
    248  await testLastClosedActionsEntries();
    249  SessionStore.forgetClosedWindow();
    250 
    251  // both the forgotten window and its closed tab has been removed from the list
    252  Assert.ok(
    253    !SessionStore.lastClosedActions.length,
    254    `0 closed actions have been recorded`
    255  );
    256 });
    257 
    258 add_task(async function test_user_clears_history() {
    259  await testLastClosedActionsEntries();
    260  Services.obs.notifyObservers(null, "browser:purge-session-history");
    261 
    262  // both the forgotten window and its closed tab has been removed from the list
    263  Assert.ok(
    264    !SessionStore.lastClosedActions.length,
    265    `0 closed actions have been recorded`
    266  );
    267 });
    268 
    269 /**
    270 * It the browser has restarted and the closed actions list is empty, we
    271 * should fallback to re-opening the last closed tab if one exists.
    272 */
    273 add_task(async function test_reopen_last_tab_if_no_closed_actions() {
    274  await BrowserTestUtils.withNewTab(
    275    {
    276      gBrowser,
    277      url: "about:blank",
    278    },
    279    async () => {
    280      const TEST_URL = "https://example.com/";
    281      let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
    282      let update = BrowserTestUtils.waitForSessionStoreUpdate(tab);
    283      BrowserTestUtils.removeTab(tab);
    284      await update;
    285 
    286      SessionStore.resetLastClosedActions();
    287 
    288      let promiseTab = BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL);
    289      SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    290      let newTab = await promiseTab;
    291      Assert.equal(newTab.linkedBrowser.currentURI.spec, TEST_URL);
    292    }
    293  );
    294 });
    295 
    296 /**
    297 * It the browser has restarted and the closed actions list is empty, and
    298 * no closed tabs exist for the window, we should fallback to re-opening
    299 * the last session if one exists.
    300 */
    301 add_task(async function test_reopen_last_session_if_no_closed_actions() {
    302  gBrowser.removeAllTabsBut(gBrowser.tabs[0]);
    303  await TabStateFlusher.flushWindow(window);
    304 
    305  forgetClosedTabs(window);
    306 
    307  const state = {
    308    windows: [
    309      {
    310        tabs: [
    311          {
    312            entries: [
    313              {
    314                title: "example.org",
    315                url: "https://example.org/",
    316                triggeringPrincipal_base64,
    317              },
    318            ],
    319          },
    320          {
    321            entries: [
    322              {
    323                title: "example.com",
    324                url: "https://example.com/",
    325                triggeringPrincipal_base64,
    326              },
    327            ],
    328          },
    329          {
    330            entries: [
    331              {
    332                title: "mozilla",
    333                url: "https://www.mozilla.org/",
    334                triggeringPrincipal_base64,
    335              },
    336            ],
    337          },
    338        ],
    339        selected: 3,
    340      },
    341    ],
    342  };
    343 
    344  _LastSession.setState(state);
    345  SessionStore.resetLastClosedActions();
    346 
    347  let sessionRestored = promiseSessionStoreLoads(3 /* total restored tabs */);
    348  SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
    349  await sessionRestored;
    350  Assert.equal(gBrowser.tabs.length, 4, "Got the expected number of tabs");
    351  gBrowser.removeAllTabsBut(gBrowser.tabs[0]);
    352 });