Sketching in a test to ensure no reflows draft
authorSam Foster <sfoster@mozilla.com>
Tue, 14 Mar 2017 13:59:52 -0700
changeset 500753 3718931b7702114e096df97b167c3704853f785a
parent 500752 485c19ab1f389b40823adddfda68ad0e430202da
child 500754 8a736633fdc1dcde8b88f5ff380a19f1ab191a5a
push id49786
push userbmo:sfoster@mozilla.com
push dateFri, 17 Mar 2017 16:12:29 +0000
milestone55.0a1
Sketching in a test to ensure no reflows MozReview-Commit-ID: ArBUk2N41JQ
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_window_activestate_reflows.js
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -430,8 +430,9 @@ tags = fullscreen
 [browser_menuButtonBadgeManager.js]
 [browser_newTabDrop.js]
 [browser_newWindowDrop.js]
 [browser_newwindow_focus.js]
 skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason.
 [browser_bug1299667.js]
 [browser_close_dependent_tabs.js]
 skip-if = !e10s # GroupedSHistory is e10s-only
+[browser_window_activestate_reflows.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_window_activestate_reflows.js
@@ -0,0 +1,137 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+XPCOMUtils.defineLazyGetter(this, "docShell", () => {
+  return window.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIWebNavigation)
+               .QueryInterface(Ci.nsIDocShell);
+});
+
+const EXPECTED_REFLOWS = [
+  // tabbrowser.adjustTabstrip() call after tabopen animation has finished
+  "adjustTabstrip@chrome://browser/content/tabbrowser.xml|" +
+    "_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
+    "onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
+
+  // switching focus in updateCurrentBrowser() causes reflows
+  "_adjustFocusAfterTabSwitch@chrome://browser/content/tabbrowser.xml|" +
+    "updateCurrentBrowser@chrome://browser/content/tabbrowser.xml|" +
+    "onselect@chrome://browser/content/browser.xul|",
+
+  // switching focus in openLinkIn() causes reflows
+  "openLinkIn@chrome://browser/content/utilityOverlay.js|" +
+    "openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
+    "BrowserOpenTab@chrome://browser/content/browser.js|",
+
+  // accessing element.scrollPosition in _fillTrailingGap() flushes layout
+  "get_scrollPosition@chrome://global/content/bindings/scrollbox.xml|" +
+    "_fillTrailingGap@chrome://browser/content/tabbrowser.xml|" +
+    "_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
+    "onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
+
+  // SessionStore.getWindowDimensions()
+  "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm|" +
+    "ssi_updateWindowFeatures/<@resource:///modules/sessionstore/SessionStore.jsm|" +
+    "ssi_updateWindowFeatures@resource:///modules/sessionstore/SessionStore.jsm|" +
+    "ssi_collectWindowData@resource:///modules/sessionstore/SessionStore.jsm|",
+
+  // selection change notification may cause querying the focused editor content
+  // by IME and that will cause reflow.
+  "select@chrome://global/content/bindings/textbox.xml|" +
+    "focusAndSelectUrlBar@chrome://browser/content/browser.js|" +
+    "openLinkIn@chrome://browser/content/utilityOverlay.js|" +
+    "openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
+    "BrowserOpenTab@chrome://browser/content/browser.js|",
+
+];
+
+/*
+ * This test ensures that there are no unexpected
+ * uninterruptible reflows when toggling between 2 windows
+ */
+add_task(function*() {
+
+  var win1 = OpenBrowserWindow();
+  var win1Promise = new win1.Promise(resolve => {
+    win1.addEventListener("load", function() {
+      resolve();
+    }, {once: true});
+  });
+  yield win1Promise;
+
+  var win2 = OpenBrowserWindow();
+  var win2Promise = new win2.Promise(resolve => {
+    win2.addEventListener("load", function() {
+      resolve();
+    }, {once: true});
+  });
+  yield win2Promise;
+
+
+  // place focus/make the first window active
+  yield SimpleTest.promiseFocus(win1);
+  // add reflow observer to win1
+  // add reflow observer to win2
+
+  // place focus/make the 2nd window active
+  yield SimpleTest.promiseFocus(win2);
+
+  // fail test if we got an unexpected reflow
+  ok(true);
+  // let Promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
+
+  // place focus/make the first window active
+  yield SimpleTest.promiseFocus(win1);
+
+  // fail test if we got an unexpected reflow
+  ok(true);
+
+  // unhook the observers and clean up the windows
+  yield BrowserTestUtils.closeWindow(win1);
+  yield BrowserTestUtils.closeWindow(win2);
+
+});
+
+var observer = {
+  reflow(start, end) {
+    // Gather information about the current code path.
+    let path = (new Error().stack).split("\n").slice(1).map(line => {
+      return line.replace(/:\d+:\d+$/, "");
+    }).join("|");
+    let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|");
+
+    // Stack trace is empty. Reflow was triggered by native code.
+    if (path === "") {
+      return;
+    }
+
+    // Check if this is an expected reflow.
+    for (let stack of EXPECTED_REFLOWS) {
+      if (path.startsWith(stack)) {
+        ok(true, "expected uninterruptible reflow '" + stack + "'");
+        return;
+      }
+    }
+
+    ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'");
+  },
+
+  reflowInterruptible(start, end) {
+    // We're not interested in interruptible reflows.
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
+                                         Ci.nsISupportsWeakReference])
+};
+
+function waitForTransitionEnd() {
+  return new Promise(resolve => {
+    let tab = gBrowser.selectedTab;
+    tab.addEventListener("transitionend", function onEnd(event) {
+      if (event.propertyName === "max-width") {
+        tab.removeEventListener("transitionend", onEnd);
+        resolve();
+      }
+    });
+  });
+}