summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js')
-rw-r--r--browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js b/browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js
new file mode 100644
index 0000000000..1fd33ed836
--- /dev/null
+++ b/browser/base/content/test/performance/browser_tabstrip_overflow_underflow.js
@@ -0,0 +1,200 @@
+"use strict";
+
+/**
+ * WHOA THERE: We should never be adding new things to EXPECTED_*_REFLOWS.
+ * This is a (now empty) list of known reflows.
+ * Instead of adding more reflows to the lists, you should be modifying your
+ * code to avoid the reflow.
+ *
+ * See https://firefox-source-docs.mozilla.org/performance/bestpractices.html
+ * for tips on how to do that.
+ */
+const EXPECTED_OVERFLOW_REFLOWS = [
+ /**
+ * Nothing here! Please don't add anything new!
+ */
+];
+
+const EXPECTED_UNDERFLOW_REFLOWS = [
+ /**
+ * Nothing here! Please don't add anything new!
+ */
+];
+
+/**
+ * This test ensures that there are no unexpected uninterruptible reflows when
+ * opening a new tab that will cause the existing tabs to overflow and the tab
+ * strip to become scrollable. It also tests that there are no unexpected
+ * uninterruptible reflows when closing that tab, which causes the tab strip to
+ * underflow.
+ */
+add_task(async function () {
+ // Force-enable tab animations
+ gReduceMotionOverride = false;
+
+ await ensureNoPreloadedBrowser();
+
+ // The test starts on about:blank and opens an about:blank
+ // tab which triggers opening the toolbar since
+ // ensureNoPreloadedBrowser sets AboutNewTab.newTabURL to about:blank.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.toolbars.bookmarks.visibility", "never"]],
+ });
+
+ const TAB_COUNT_FOR_OVERFLOW = computeMaxTabCount();
+
+ await createTabs(TAB_COUNT_FOR_OVERFLOW);
+
+ gURLBar.focus();
+ await disableFxaBadge();
+
+ let tabStripRect =
+ gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
+ let textBoxRect = gURLBar
+ .querySelector("moz-input-box")
+ .getBoundingClientRect();
+
+ let ignoreTabstripRects = {
+ filter: rects =>
+ rects.filter(
+ r =>
+ !(
+ // We expect plenty of changed rects within the tab strip.
+ (
+ r.y1 >= tabStripRect.top &&
+ r.y2 <= tabStripRect.bottom &&
+ r.x1 >= tabStripRect.left &&
+ r.x2 <= tabStripRect.right
+ )
+ )
+ ),
+ exceptions: [
+ {
+ name: "the urlbar placeolder moves up and down by a few pixels",
+ condition: r =>
+ r.x1 >= textBoxRect.left &&
+ r.x2 <= textBoxRect.right &&
+ r.y1 >= textBoxRect.top &&
+ r.y2 <= textBoxRect.bottom,
+ },
+ {
+ name: "bug 1446449 - spurious tab switch spinner",
+ condition: r =>
+ // In the content area
+ r.y1 >=
+ document.getElementById("appcontent").getBoundingClientRect().top,
+ },
+ ],
+ };
+
+ await withPerfObserver(
+ async function () {
+ let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+ BrowserOpenTab();
+ await BrowserTestUtils.waitForEvent(
+ gBrowser.selectedTab,
+ "TabAnimationEnd"
+ );
+ await switchDone;
+ await TestUtils.waitForCondition(() => {
+ return gBrowser.tabContainer.arrowScrollbox.hasAttribute(
+ "scrolledtoend"
+ );
+ });
+ },
+ { expectedReflows: EXPECTED_OVERFLOW_REFLOWS, frames: ignoreTabstripRects }
+ );
+
+ Assert.ok(
+ gBrowser.tabContainer.hasAttribute("overflow"),
+ "Tabs should now be overflowed."
+ );
+
+ // Now test that opening and closing a tab while overflowed doesn't cause
+ // us to reflow.
+ await withPerfObserver(
+ async function () {
+ let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+ BrowserOpenTab();
+ await switchDone;
+ await TestUtils.waitForCondition(() => {
+ return gBrowser.tabContainer.arrowScrollbox.hasAttribute(
+ "scrolledtoend"
+ );
+ });
+ },
+ { expectedReflows: [], frames: ignoreTabstripRects }
+ );
+
+ await withPerfObserver(
+ async function () {
+ let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+ BrowserTestUtils.removeTab(gBrowser.selectedTab, { animate: true });
+ await switchDone;
+ },
+ { expectedReflows: [], frames: ignoreTabstripRects }
+ );
+
+ // At this point, we have an overflowed tab strip, and we've got the last tab
+ // selected. This should mean that the first tab is scrolled out of view.
+ // Let's test that we don't reflow when switching to that first tab.
+ let lastTab = gBrowser.selectedTab;
+ let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
+
+ // First, we'll check that the first tab is actually scrolled
+ // at least partially out of view.
+ Assert.ok(
+ arrowScrollbox.scrollPosition > 0,
+ "First tab should be partially scrolled out of view."
+ );
+
+ // Now switch to the first tab. We shouldn't flush layout at all.
+ await withPerfObserver(
+ async function () {
+ let firstTab = gBrowser.tabs[0];
+ await BrowserTestUtils.switchTab(gBrowser, firstTab);
+ await TestUtils.waitForCondition(() => {
+ return gBrowser.tabContainer.arrowScrollbox.hasAttribute(
+ "scrolledtostart"
+ );
+ });
+ },
+ { expectedReflows: [], frames: ignoreTabstripRects }
+ );
+
+ // Okay, now close the last tab. The tabstrip should stay overflowed, but removing
+ // one more after that should underflow it.
+ BrowserTestUtils.removeTab(lastTab);
+
+ Assert.ok(
+ gBrowser.tabContainer.hasAttribute("overflow"),
+ "Tabs should still be overflowed."
+ );
+
+ // Depending on the size of the window, it might take one or more tab
+ // removals to put the tab strip out of the overflow state, so we'll just
+ // keep testing removals until that occurs.
+ while (gBrowser.tabContainer.hasAttribute("overflow")) {
+ lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
+ if (gBrowser.selectedTab !== lastTab) {
+ await BrowserTestUtils.switchTab(gBrowser, lastTab);
+ }
+
+ // ... and make sure we don't flush layout when closing it, and exiting
+ // the overflowed state.
+ await withPerfObserver(
+ async function () {
+ let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+ BrowserTestUtils.removeTab(lastTab, { animate: true });
+ await switchDone;
+ await TestUtils.waitForCondition(() => !lastTab.isConnected);
+ },
+ {
+ expectedReflows: EXPECTED_UNDERFLOW_REFLOWS,
+ frames: ignoreTabstripRects,
+ }
+ );
+ }
+
+ await removeAllButFirstTab();
+});