tor-browser

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

browser_multiselect_tabs_unload_telemetry.js (9226B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /**
      5 * Opens the context menu on a tab and waits for it to be shown.
      6 *
      7 * @param tab The tab to open a context menu on
      8 * @returns {Promise} Promise object with the menu.
      9 */
     10 async function openTabMenuFor(tab) {
     11  let tabMenu = tab.ownerDocument.getElementById("tabContextMenu");
     12 
     13  let tabMenuShown = BrowserTestUtils.waitForPopupEvent(tabMenu, "shown");
     14  EventUtils.synthesizeMouseAtCenter(
     15    tab,
     16    { type: "contextmenu" },
     17    tab.ownerGlobal
     18  );
     19  await tabMenuShown;
     20 
     21  return tabMenu;
     22 }
     23 
     24 /**
     25 * Adds new tabs to `gBrowser`.
     26 *
     27 * @param {number} numberOfTabs The number of new tabs to add.
     28 * @returns {Promise<Array>} Promise object containing the new tabs that were added.
     29 */
     30 async function addBrowserTabs(numberOfTabs) {
     31  let tabs = [];
     32  for (let i = 0; i < numberOfTabs; i++) {
     33    tabs.push(await addTab(`http://mochi.test:8888/#${i}`));
     34  }
     35  return tabs;
     36 }
     37 
     38 add_setup(async function () {
     39  // This is helpful to avoid some weird race conditions in the test, specifically
     40  // the assertion that !this.blankTab in AsyncTabSwitcher when adding a new tab.
     41  await BrowserTestUtils.loadURIString({
     42    browser: gBrowser.selectedTab.linkedBrowser,
     43    uriString: "http://mochi.test:8888/#originalTab",
     44  });
     45  let originalTab = gBrowser.selectedTab;
     46  // switch to Firefox View tab to initialize it
     47  FirefoxViewHandler.openTab();
     48  // switch back to the original tab since tests expect this
     49  await BrowserTestUtils.switchTab(gBrowser, originalTab);
     50 
     51  await SpecialPowers.pushPrefEnv({
     52    set: [
     53      ["test.wait300msAfterTabSwitch", true],
     54      ["browser.tabs.unloadTabInContextMenu", true],
     55    ],
     56  });
     57 });
     58 
     59 /**
     60 * Checks various common properties of a tabExplicitUnload event.
     61 *
     62 * @param e The telemetry event to check
     63 */
     64 function checkEventCommon(e) {
     65  Assert.equal(e.category, "browser.engagement", "correct category");
     66  Assert.equal(e.name, "tab_explicit_unload", "correct name");
     67  // Since we're unloading small pages here, the memory may not change much or might even
     68  // go up a smidge.
     69  let memoryBefore = parseInt(e.extra.memory_before, 10);
     70  let memoryAfter = parseInt(e.extra.memory_after, 10);
     71  Assert.less(
     72    memoryAfter - memoryBefore,
     73    100000,
     74    `Memory should go down after unload (before: ${memoryBefore}, after ${memoryAfter})`
     75  );
     76  Assert.greaterOrEqual(
     77    parseInt(e.extra.time_to_unload_in_ms, 10),
     78    0,
     79    "time_to_unload should be >= 0"
     80  );
     81  Assert.less(
     82    parseInt(e.extra.time_to_unload_in_ms, 10),
     83    10000,
     84    "time_to_unload should be within reason"
     85  );
     86 }
     87 
     88 /**
     89 * Gets a promise that will be fulfilled when the tab is unloaded.
     90 *
     91 * @param tab The tab that will be unloaded
     92 * @returns {Promise} A promise that will be fulfilled when
     93 *                    the tab is unloaded.
     94 */
     95 function getWaitForUnloadedPromise(tab) {
     96  return BrowserTestUtils.waitForEvent(tab, "TabBrowserDiscarded");
     97 }
     98 
     99 /**
    100 * Waits for a tabExplicitUnload telemetry event to occur.
    101 *
    102 * @returns {Promise} A promise that will be fulfilled when the
    103 *                    telemetry event occurs.
    104 */
    105 async function waitForTelemetryEvent() {
    106  await TestUtils.waitForCondition(() => {
    107    let events = Glean.browserEngagement.tabExplicitUnload.testGetValue();
    108    if (!events) {
    109      return false;
    110    }
    111    return !!events.length;
    112  });
    113 }
    114 
    115 /**
    116 * Unload the currently selected tab and one other and ensure
    117 * the telemetry is correct.
    118 */
    119 add_task(async function test_unload_selected_and_one_other_tab() {
    120  let [tab1, tab2, tab3] = await addBrowserTabs(3);
    121 
    122  let menuItemUnload = document.getElementById("context_unloadTab");
    123 
    124  await BrowserTestUtils.switchTab(gBrowser, tab1);
    125  await triggerClickOn(tab2, { ctrlKey: true });
    126 
    127  Services.fog.testResetFOG();
    128  updateTabContextMenu(tab1);
    129  let tab1UnloadedPromise = getWaitForUnloadedPromise(tab1);
    130  let tab2UnloadedPromise = getWaitForUnloadedPromise(tab2);
    131  {
    132    let menu = await openTabMenuFor(tab1);
    133    let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
    134    menu.activateItem(menuItemUnload);
    135    await menuHiddenPromise;
    136  }
    137 
    138  await Promise.all([tab1UnloadedPromise, tab2UnloadedPromise]);
    139 
    140  await waitForTelemetryEvent();
    141  let unloadTelemetry =
    142    Glean.browserEngagement.tabExplicitUnload.testGetValue();
    143  Assert.equal(
    144    unloadTelemetry.length,
    145    1,
    146    "should get exactly one telemetry event"
    147  );
    148  let e = unloadTelemetry[0];
    149  checkEventCommon(e);
    150  Assert.equal(e.extra.unload_selected_tab, "true", "did unload selected tab");
    151  Assert.equal(e.extra.all_tabs_unloaded, "false", "did not unload everything");
    152  Assert.equal(e.extra.tabs_unloaded, "2", "correct number of tabs unloaded");
    153 
    154  Services.fog.testResetFOG();
    155  await BrowserTestUtils.removeTab(tab3);
    156  await BrowserTestUtils.removeTab(tab2);
    157  await BrowserTestUtils.removeTab(tab1);
    158 });
    159 
    160 /**
    161 * Unload one unselected tab and ensure the telemetry is correct.
    162 */
    163 add_task(async function test_unload_one_unselected_tab() {
    164  let [tab1, tab2, tab3] = await addBrowserTabs(3);
    165 
    166  let menuItemUnload = document.getElementById("context_unloadTab");
    167  await BrowserTestUtils.switchTab(gBrowser, tab1);
    168  updateTabContextMenu(tab2);
    169  Services.fog.testResetFOG();
    170  let tab2UnloadedPromise = getWaitForUnloadedPromise(tab2);
    171  {
    172    let menu = await openTabMenuFor(tab2);
    173    let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
    174    menu.activateItem(menuItemUnload);
    175    await menuHiddenPromise;
    176  }
    177  await tab2UnloadedPromise;
    178 
    179  await waitForTelemetryEvent();
    180  let unloadTelemetry =
    181    Glean.browserEngagement.tabExplicitUnload.testGetValue();
    182  Assert.equal(
    183    unloadTelemetry.length,
    184    1,
    185    "should get exactly one telemetry event"
    186  );
    187  let e = unloadTelemetry[0];
    188  checkEventCommon(e);
    189  Assert.equal(
    190    e.extra.unload_selected_tab,
    191    "false",
    192    "did not unload selected tab"
    193  );
    194  Assert.equal(e.extra.all_tabs_unloaded, "false", "did not unload everything");
    195  Assert.equal(e.extra.tabs_unloaded, "1", "correct number of tabs unloaded");
    196 
    197  Services.fog.testResetFOG();
    198  await BrowserTestUtils.removeTab(tab3);
    199  await BrowserTestUtils.removeTab(tab2);
    200  await BrowserTestUtils.removeTab(tab1);
    201 });
    202 
    203 /**
    204 * Unload just the selected tab and ensure the telemetry is correct.
    205 */
    206 add_task(async function test_unload_selected_tab() {
    207  let [tab1, tab2, tab3] = await addBrowserTabs(3);
    208 
    209  let menuItemUnload = document.getElementById("context_unloadTab");
    210  await BrowserTestUtils.switchTab(gBrowser, tab1);
    211  Services.fog.testResetFOG();
    212  let tab1UnloadedPromise = getWaitForUnloadedPromise(tab1);
    213  {
    214    let menu = await openTabMenuFor(tab1);
    215    let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
    216    menu.activateItem(menuItemUnload);
    217    await menuHiddenPromise;
    218  }
    219  await tab1UnloadedPromise;
    220 
    221  await waitForTelemetryEvent();
    222  let unloadTelemetry =
    223    Glean.browserEngagement.tabExplicitUnload.testGetValue();
    224  Assert.equal(
    225    unloadTelemetry.length,
    226    1,
    227    "should get exactly one telemetry event"
    228  );
    229  let e = unloadTelemetry[0];
    230  checkEventCommon(e);
    231  Assert.equal(e.extra.unload_selected_tab, "true", "did unload selected tab");
    232  Assert.equal(e.extra.all_tabs_unloaded, "false", "did not unload everything");
    233  Assert.equal(e.extra.tabs_unloaded, "1", "correct number of tabs unloaded");
    234 
    235  Services.fog.testResetFOG();
    236  await BrowserTestUtils.removeTab(tab3);
    237  await BrowserTestUtils.removeTab(tab2);
    238  await BrowserTestUtils.removeTab(tab1);
    239 });
    240 
    241 /**
    242 * Unload all tabs in the window and ensure
    243 * the telemetry is correct.
    244 */
    245 add_task(async function test_unload_all_tabs() {
    246  let originalTab = gBrowser.selectedTab;
    247  let [tab1, tab2, tab3] = await addBrowserTabs(3);
    248 
    249  let menuItemUnload = document.getElementById("context_unloadTab");
    250  await triggerClickOn(tab1, { ctrlKey: true });
    251  await triggerClickOn(tab2, { ctrlKey: true });
    252  await triggerClickOn(tab3, { ctrlKey: true });
    253  updateTabContextMenu(originalTab);
    254  Services.fog.testResetFOG();
    255  let allTabsUnloadedPromises = [originalTab, tab1, tab2, tab3].map(
    256    getWaitForUnloadedPromise
    257  );
    258  {
    259    let menu = await openTabMenuFor(originalTab);
    260    let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
    261    menu.activateItem(menuItemUnload);
    262    await menuHiddenPromise;
    263  }
    264 
    265  await Promise.all(allTabsUnloadedPromises);
    266 
    267  await waitForTelemetryEvent();
    268  let unloadTelemetry =
    269    Glean.browserEngagement.tabExplicitUnload.testGetValue();
    270  Assert.equal(
    271    unloadTelemetry.length,
    272    1,
    273    "should get exactly one telemetry event"
    274  );
    275  let e = unloadTelemetry[0];
    276  checkEventCommon(e);
    277  Assert.equal(e.extra.unload_selected_tab, "true", "did unload selected tab");
    278  Assert.equal(e.extra.all_tabs_unloaded, "true", "did unload everything");
    279  Assert.equal(e.extra.tabs_unloaded, "4", "correct number of tabs unloaded");
    280 
    281  Services.fog.testResetFOG();
    282  await BrowserTestUtils.removeTab(tab3);
    283  await BrowserTestUtils.removeTab(tab2);
    284  await BrowserTestUtils.removeTab(tab1);
    285 });
    286 
    287 add_task(async function test_cleanup() {
    288  await BrowserTestUtils.removeTab(FirefoxViewHandler.tab);
    289 });