tor-browser

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

browser_pinned_tabs.js (10064B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const REMOTE_URL = "https://www.example.com/";
      7 const ABOUT_ROBOTS_URL = "about:robots";
      8 const NO_TITLE_URL = "data:text/plain,foo";
      9 
     10 const BACKUP_STATE = SessionStore.getBrowserState();
     11 registerCleanupFunction(() => promiseBrowserState(BACKUP_STATE));
     12 
     13 add_setup(async function () {
     14  await SpecialPowers.pushPrefEnv({
     15    set: [
     16      ["browser.sessionstore.restore_on_demand", true],
     17      ["browser.sessionstore.restore_tabs_lazily", true],
     18    ],
     19  });
     20 });
     21 
     22 /**
     23 * When implementing batch insertion of tabs as part of session restore,
     24 * we started reversing the insertion order of pinned tabs (bug 1607441).
     25 * This test checks we don't regress that again.
     26 */
     27 add_task(async function test_pinned_tabs_order() {
     28  // we expect 3 pinned tabs plus the selected tab get content restored.
     29  let allTabsRestored = promiseSessionStoreLoads(4);
     30  await promiseBrowserState({
     31    windows: [
     32      {
     33        selected: 4, // SessionStore uses 1-based indexing.
     34        tabs: [
     35          {
     36            pinned: true,
     37            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
     38          },
     39          {
     40            pinned: true,
     41            entries: [{ url: ABOUT_ROBOTS_URL, triggeringPrincipal_base64 }],
     42          },
     43          {
     44            pinned: true,
     45            entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }],
     46          },
     47          { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
     48          { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
     49        ],
     50      },
     51    ],
     52  });
     53  await allTabsRestored;
     54  let [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
     55  ok(tab1.pinned, "First tab is pinned");
     56  ok(tab2.pinned, "Second tab is pinned");
     57  ok(tab3.pinned, "Third tab is pinned");
     58  ok(!tab4.pinned, "Fourth tab is not pinned");
     59  ok(!tab5.pinned, "Fifth tab is not pinned");
     60 
     61  ok(tab4.selected, "Fourth tab is selected");
     62  is(
     63    tab1.linkedBrowser.currentURI.spec,
     64    REMOTE_URL,
     65    "First tab has matching URL"
     66  );
     67  is(
     68    tab2.linkedBrowser.currentURI.spec,
     69    ABOUT_ROBOTS_URL,
     70    "Second tab has matching URL"
     71  );
     72  is(
     73    tab3.linkedBrowser.currentURI.spec,
     74    NO_TITLE_URL,
     75    "Third tab has matching URL"
     76  );
     77  // Clean up for the next task.
     78  await promiseBrowserState(BACKUP_STATE);
     79 });
     80 
     81 /**
     82 * When fixing the previous regression, pinned tabs started disappearing out
     83 * of sessions with selected pinned tabs. This test checks that case.
     84 */
     85 add_task(async function test_selected_pinned_tab_dataloss() {
     86  // we expect 3 pinned tabs (one of which is selected) get content restored.
     87  let allTabsRestored = promiseSessionStoreLoads(3);
     88  await promiseBrowserState({
     89    windows: [
     90      {
     91        selected: 1, // SessionStore uses 1-based indexing.
     92        tabs: [
     93          {
     94            pinned: true,
     95            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
     96          },
     97          {
     98            pinned: true,
     99            entries: [{ url: ABOUT_ROBOTS_URL, triggeringPrincipal_base64 }],
    100          },
    101          {
    102            pinned: true,
    103            entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }],
    104          },
    105          { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
    106          { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
    107        ],
    108      },
    109    ],
    110  });
    111  await allTabsRestored;
    112  let [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
    113  ok(tab5, "Should have 5 tabs");
    114  ok(tab1.pinned, "First tab is pinned");
    115  ok(tab2.pinned, "Second tab is pinned");
    116  ok(tab3.pinned, "Third tab is pinned");
    117  ok(tab4 && !tab4.pinned, "Fourth tab is not pinned");
    118  ok(tab5 && !tab5.pinned, "Fifth tab is not pinned");
    119 
    120  ok(tab1 && tab1.selected, "First (pinned) tab is selected");
    121  is(
    122    tab1.linkedBrowser.currentURI.spec,
    123    REMOTE_URL,
    124    "First tab has matching URL"
    125  );
    126  is(
    127    tab2.linkedBrowser.currentURI.spec,
    128    ABOUT_ROBOTS_URL,
    129    "Second tab has matching URL"
    130  );
    131  is(
    132    tab3.linkedBrowser.currentURI.spec,
    133    NO_TITLE_URL,
    134    "Third tab has matching URL"
    135  );
    136  // Clean up for the next task.
    137  await promiseBrowserState(BACKUP_STATE);
    138 });
    139 
    140 /**
    141 * While we're here, it seems useful to have a test for mixed pinned and
    142 * unpinned tabs in session store state, as well as userContextId.
    143 */
    144 add_task(async function test_mixed_pinned_unpinned() {
    145  // we expect 3 pinned tabs plus the selected tab get content restored.
    146  let allTabsRestored = promiseSessionStoreLoads(4);
    147  await promiseBrowserState({
    148    windows: [
    149      {
    150        selected: 4, // SessionStore uses 1-based indexing.
    151        tabs: [
    152          {
    153            pinned: true,
    154            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    155          },
    156          { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
    157          {
    158            pinned: true,
    159            entries: [{ url: ABOUT_ROBOTS_URL, triggeringPrincipal_base64 }],
    160          },
    161          { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
    162          {
    163            pinned: true,
    164            entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }],
    165          },
    166        ],
    167      },
    168    ],
    169  });
    170  await allTabsRestored;
    171  let [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
    172  ok(tab1.pinned, "First tab is pinned");
    173  ok(tab2.pinned, "Second tab is pinned");
    174  ok(tab3.pinned, "Third tab is pinned");
    175  ok(!tab4.pinned, "Fourth tab is not pinned");
    176  ok(!tab5.pinned, "Fifth tab is not pinned");
    177 
    178  // This is confusing to read - the 4th entry in the session data is
    179  // selected. But the 5th entry is pinned, so it moves to the start of the
    180  // tabstrip, so when we fetch `gBrowser.tabs`, the 4th entry in the list
    181  // is actually the 5th tab.
    182  ok(tab5.selected, "Fifth tab is selected");
    183  is(
    184    tab1.linkedBrowser.currentURI.spec,
    185    REMOTE_URL,
    186    "First tab has matching URL"
    187  );
    188  is(
    189    tab2.linkedBrowser.currentURI.spec,
    190    ABOUT_ROBOTS_URL,
    191    "Second tab has matching URL"
    192  );
    193  is(
    194    tab3.linkedBrowser.currentURI.spec,
    195    NO_TITLE_URL,
    196    "Third tab has matching URL"
    197  );
    198  // Clean up for the next task.
    199  await promiseBrowserState(BACKUP_STATE);
    200 });
    201 
    202 /**
    203 * After session restore, if we crash an unpinned tab, we noticed pinned tabs
    204 * created in the same process would lose all data (Bug 1624511). This test
    205 * checks that case.
    206 */
    207 add_task(async function test_pinned_tab_dataloss() {
    208  // We do not run if there are no crash reporters to avoid
    209  // problems with the intentional crash.
    210  if (!AppConstants.MOZ_CRASHREPORTER) {
    211    return;
    212  }
    213  // If we end up increasing the process count limit in future,
    214  // we want to ensure that we don't stop testing this case
    215  // of pinned tab data loss.
    216  if (SpecialPowers.getIntPref("dom.ipc.processCount") > 8) {
    217    ok(
    218      false,
    219      "Process count is greater than 8, update the number of pinned tabs in test."
    220    );
    221  }
    222 
    223  // We expect 17 pinned tabs plus the selected tab get content restored.
    224  // Given that the default process count is currently 8, we need this
    225  // number of pinned tabs to reproduce the data loss. If this changes,
    226  // please add more pinned tabs.
    227  let allTabsRestored = promiseSessionStoreLoads(18);
    228  await promiseBrowserState({
    229    windows: [
    230      {
    231        selected: 18, // SessionStore uses 1-based indexing.
    232        tabs: [
    233          {
    234            pinned: true,
    235            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    236          },
    237          {
    238            pinned: true,
    239            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    240          },
    241          {
    242            pinned: true,
    243            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    244          },
    245          {
    246            pinned: true,
    247            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    248          },
    249          {
    250            pinned: true,
    251            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    252          },
    253          {
    254            pinned: true,
    255            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    256          },
    257          {
    258            pinned: true,
    259            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    260          },
    261          {
    262            pinned: true,
    263            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    264          },
    265          {
    266            pinned: true,
    267            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    268          },
    269          {
    270            pinned: true,
    271            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    272          },
    273          {
    274            pinned: true,
    275            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    276          },
    277          {
    278            pinned: true,
    279            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    280          },
    281          {
    282            pinned: true,
    283            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    284          },
    285          {
    286            pinned: true,
    287            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    288          },
    289          {
    290            pinned: true,
    291            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    292          },
    293          {
    294            pinned: true,
    295            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    296          },
    297          {
    298            pinned: true,
    299            entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }],
    300          },
    301          { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
    302        ],
    303      },
    304    ],
    305  });
    306  await allTabsRestored;
    307 
    308  let tabs = gBrowser.tabs;
    309  await BrowserTestUtils.crashFrame(tabs[17].linkedBrowser);
    310 
    311  await TestUtils.topicObserved("sessionstore-state-write-complete");
    312 
    313  for (let i = 0; i < tabs.length; i++) {
    314    let tab = tabs[i];
    315    is(
    316      tab.linkedBrowser.currentURI.spec,
    317      REMOTE_URL,
    318      `Tab ${i + 1} should have matching URL`
    319    );
    320  }
    321 
    322  // Clean up for the next task.
    323  await promiseBrowserState(BACKUP_STATE);
    324 });