tor-browser

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

browser_tabstrip_overflow_underflow.js (6258B)


      1 "use strict";
      2 
      3 /**
      4 * WHOA THERE: We should never be adding new things to EXPECTED_*_REFLOWS.
      5 * This is a (now empty) list of known reflows.
      6 * Instead of adding more reflows to the lists, you should be modifying your
      7 * code to avoid the reflow.
      8 *
      9 * See https://firefox-source-docs.mozilla.org/performance/bestpractices.html
     10 * for tips on how to do that.
     11 */
     12 const EXPECTED_OVERFLOW_REFLOWS = [
     13  /**
     14   * Nothing here! Please don't add anything new!
     15   */
     16 ];
     17 
     18 const EXPECTED_UNDERFLOW_REFLOWS = [
     19  /**
     20   * Nothing here! Please don't add anything new!
     21   */
     22 ];
     23 
     24 /**
     25 * This test ensures that there are no unexpected uninterruptible reflows when
     26 * opening a new tab that will cause the existing tabs to overflow and the tab
     27 * strip to become scrollable. It also tests that there are no unexpected
     28 * uninterruptible reflows when closing that tab, which causes the tab strip to
     29 * underflow.
     30 */
     31 add_task(async function () {
     32  // Force-enable tab animations
     33  gReduceMotionOverride = false;
     34 
     35  await ensureNoPreloadedBrowser();
     36 
     37  // The test starts on about:blank and opens an about:blank
     38  // tab which triggers opening the toolbar since
     39  // ensureNoPreloadedBrowser sets AboutNewTab.newTabURL to about:blank.
     40  await SpecialPowers.pushPrefEnv({
     41    set: [["browser.toolbars.bookmarks.visibility", "never"]],
     42  });
     43 
     44  const TAB_COUNT_FOR_OVERFLOW = computeMaxTabCount();
     45 
     46  await createTabs(TAB_COUNT_FOR_OVERFLOW);
     47 
     48  gURLBar.focus();
     49  await disableFxaBadge();
     50 
     51  let tabStripRect =
     52    gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
     53  let textBoxRect = gURLBar
     54    .querySelector("moz-input-box")
     55    .getBoundingClientRect();
     56 
     57  let ignoreTabstripRects = {
     58    filter: rects =>
     59      rects.filter(
     60        r =>
     61          !(
     62            // We expect plenty of changed rects within the tab strip.
     63            (
     64              r.y1 >= tabStripRect.top &&
     65              r.y2 <= tabStripRect.bottom &&
     66              r.x1 >= tabStripRect.left &&
     67              r.x2 <= tabStripRect.right
     68            )
     69          )
     70      ),
     71    exceptions: [
     72      {
     73        name: "the urlbar placeolder moves up and down by a few pixels",
     74        condition: r =>
     75          r.x1 >= textBoxRect.left &&
     76          r.x2 <= textBoxRect.right &&
     77          r.y1 >= textBoxRect.top &&
     78          r.y2 <= textBoxRect.bottom,
     79      },
     80      {
     81        name: "bug 1446449 - spurious tab switch spinner",
     82        condition: r =>
     83          // In the content area
     84          r.y1 >=
     85          document.getElementById("tabbrowser-tabbox").getBoundingClientRect()
     86            .top,
     87      },
     88    ],
     89  };
     90 
     91  await withPerfObserver(
     92    async function () {
     93      let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     94      BrowserCommands.openTab();
     95      await BrowserTestUtils.waitForEvent(
     96        gBrowser.selectedTab,
     97        "TabAnimationEnd"
     98      );
     99      await switchDone;
    100      await TestUtils.waitForCondition(() => {
    101        return gBrowser.tabContainer.arrowScrollbox.hasAttribute(
    102          "scrolledtoend"
    103        );
    104      });
    105    },
    106    { expectedReflows: EXPECTED_OVERFLOW_REFLOWS, frames: ignoreTabstripRects }
    107  );
    108 
    109  Assert.ok(
    110    gBrowser.tabContainer.overflowing,
    111    "Tabs should now be overflowed."
    112  );
    113 
    114  // Now test that opening and closing a tab while overflowed doesn't cause
    115  // us to reflow.
    116  await withPerfObserver(
    117    async function () {
    118      let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
    119      BrowserCommands.openTab();
    120      await switchDone;
    121      await TestUtils.waitForCondition(() => {
    122        return gBrowser.tabContainer.arrowScrollbox.hasAttribute(
    123          "scrolledtoend"
    124        );
    125      });
    126    },
    127    { expectedReflows: [], frames: ignoreTabstripRects }
    128  );
    129 
    130  await withPerfObserver(
    131    async function () {
    132      let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
    133      BrowserTestUtils.removeTab(gBrowser.selectedTab, { animate: true });
    134      await switchDone;
    135    },
    136    { expectedReflows: [], frames: ignoreTabstripRects }
    137  );
    138 
    139  // At this point, we have an overflowed tab strip, and we've got the last tab
    140  // selected. This should mean that the first tab is scrolled out of view.
    141  // Let's test that we don't reflow when switching to that first tab.
    142  let lastTab = gBrowser.selectedTab;
    143  let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
    144 
    145  // First, we'll check that the first tab is actually scrolled
    146  // at least partially out of view.
    147  Assert.greater(
    148    arrowScrollbox.scrollPosition,
    149    0,
    150    "First tab should be partially scrolled out of view."
    151  );
    152 
    153  // Now switch to the first tab. We shouldn't flush layout at all.
    154  await withPerfObserver(
    155    async function () {
    156      let firstTab = gBrowser.tabs[0];
    157      await BrowserTestUtils.switchTab(gBrowser, firstTab);
    158      await TestUtils.waitForCondition(() => {
    159        return gBrowser.tabContainer.arrowScrollbox.hasAttribute(
    160          "scrolledtostart"
    161        );
    162      });
    163    },
    164    { expectedReflows: [], frames: ignoreTabstripRects }
    165  );
    166 
    167  // Okay, now close the last tab. The tabstrip should stay overflowed, but removing
    168  // one more after that should underflow it.
    169  BrowserTestUtils.removeTab(lastTab);
    170 
    171  Assert.ok(
    172    gBrowser.tabContainer.overflowing,
    173    "Tabs should still be overflowed."
    174  );
    175 
    176  // Depending on the size of the window, it might take one or more tab
    177  // removals to put the tab strip out of the overflow state, so we'll just
    178  // keep testing removals until that occurs.
    179  while (gBrowser.tabContainer.overflowing) {
    180    lastTab = gBrowser.tabs.at(-1);
    181    if (gBrowser.selectedTab !== lastTab) {
    182      await BrowserTestUtils.switchTab(gBrowser, lastTab);
    183    }
    184 
    185    // ... and make sure we don't flush layout when closing it, and exiting
    186    // the overflowed state.
    187    await withPerfObserver(
    188      async function () {
    189        let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
    190        BrowserTestUtils.removeTab(lastTab, { animate: true });
    191        await switchDone;
    192        await TestUtils.waitForCondition(() => !lastTab.isConnected);
    193      },
    194      {
    195        expectedReflows: EXPECTED_UNDERFLOW_REFLOWS,
    196        frames: ignoreTabstripRects,
    197      }
    198    );
    199  }
    200 
    201  await removeAllButFirstTab();
    202 });