merge mozilla-central to mozilla-inbound. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Thu, 20 Jul 2017 13:25:57 +0200
changeset 418663 84f072290811909edb1d2f4b31c238b6643f4d0a
parent 418662 b7d81fea0b3324261236e91aafa0c70c8e08395b (current diff)
parent 418530 68046a58f82913eb7804e4796ec981f6f8ea490e (diff)
child 418664 760368896800947676137055fbfc907088e2437b
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-central to mozilla-inbound. r=merge a=merge
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -10,9 +10,8 @@ skip-if = !debug
 [browser_tabclose_grow_reflows.js]
 [browser_tabclose_reflows.js]
 [browser_tabopen_reflows.js]
 [browser_tabopen_squeeze_reflows.js]
 [browser_tabswitch_reflows.js]
 [browser_toolbariconcolor_restyles.js]
 [browser_windowclose_reflows.js]
 [browser_windowopen_reflows.js]
-skip-if = os == 'linux' # Disabled due to frequent failures. Bug 1380465.
--- a/browser/base/content/test/performance/browser_appmenu_reflows.js
+++ b/browser/base/content/test/performance/browser_appmenu_reflows.js
@@ -100,58 +100,30 @@ const EXPECTED_APPMENU_SUBVIEW_REFLOWS =
     "onTransitionEnd@resource:///modules/PanelMultiView.jsm",
   ],
    */
   /**
    * Please don't add anything new!
    */
 ];
 
-const WIN_DEBUG_E10S = Services.appinfo.OS == "WINNT" &&
-                       AppConstants.DEBUG &&
-                       gMultiProcessBrowser;
-
-if (WIN_DEBUG_E10S) {
-  // Bug 1380671 - For some reason, on Windows Debug with e10s disabled in
-  // automation, we fire more popuppositioned events causing reflows.
-  EXPECTED_APPMENU_SUBVIEW_REFLOWS.push(
-    [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
-      "adjustArrowPosition@chrome://global/content/bindings/popup.xml",
-      "onxblpopuppositioned@chrome://global/content/bindings/popup.xml",
-    ],
-
-    [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
-      "adjustArrowPosition@chrome://global/content/bindings/popup.xml",
-      "onxblpopuppositioned@chrome://global/content/bindings/popup.xml",
-    ],
-
-    [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
-      "adjustArrowPosition@chrome://global/content/bindings/popup.xml",
-      "onxblpopuppositioned@chrome://global/content/bindings/popup.xml",
-    ],
-  );
-}
-
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
   await SpecialPowers.pushPrefEnv({
     set: [["browser.photon.structure.enabled", true]],
   });
 
   // First, open the appmenu.
   await withReflowObserver(async function() {
     let popupPositioned =
       BrowserTestUtils.waitForEvent(PanelUI.panel, "popuppositioned");
     await PanelUI.show();
     await popupPositioned;
-  }, EXPECTED_APPMENU_OPEN_REFLOWS);
+  }, EXPECTED_APPMENU_OPEN_REFLOWS, window, PanelUI.panel);
 
   // Now open a series of subviews, and then close the appmenu. We
   // should not reflow during any of this.
   await withReflowObserver(async function() {
     // This recursive function will take the current main or subview,
     // find all of the buttons that navigate to subviews inside it,
     // and click each one individually. Upon entering the new view,
     // we recurse. When the subviews within a view have been
@@ -183,10 +155,10 @@ add_task(async function() {
       }
     }
 
     await openSubViewsRecursively(PanelUI.mainView);
 
     let hidden = BrowserTestUtils.waitForEvent(PanelUI.panel, "popuphidden");
     PanelUI.hide();
     await hidden;
-  }, EXPECTED_APPMENU_SUBVIEW_REFLOWS);
+  }, EXPECTED_APPMENU_SUBVIEW_REFLOWS, window, PanelUI.panel);
 });
--- a/browser/base/content/test/performance/browser_tabclose_grow_reflows.js
+++ b/browser/base/content/test/performance/browser_tabclose_grow_reflows.js
@@ -31,22 +31,28 @@ add_task(async function() {
 
   // Compute the number of tabs we can put into the strip without
   // overflowing. If we remove one of the tabs, we know that the
   // remaining tabs will grow to fill the remaining space in the
   // tabstrip.
   const TAB_COUNT_FOR_GROWTH = computeMaxTabCount();
   await createTabs(TAB_COUNT_FOR_GROWTH);
 
+  // Because the tab strip is a scrollable frame, we can't use the
+  // default dirtying function from withReflowObserver and reliably
+  // get reflows for the strip. Instead, we provide a node that's
+  // already in the scrollable frame to dirty - in this case, the
+  // original tab.
+  let origTab = gBrowser.selectedTab;
   let lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
   await BrowserTestUtils.switchTab(gBrowser, lastTab);
 
   await withReflowObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     let tab = gBrowser.tabs[gBrowser.tabs.length - 1];
     gBrowser.removeTab(tab, { animate: true });
     await BrowserTestUtils.waitForEvent(tab, "transitionend",
       false, e => e.propertyName === "max-width");
     await switchDone;
-  }, EXPECTED_REFLOWS);
+  }, EXPECTED_REFLOWS, window, origTab);
 
   await removeAllButFirstTab();
 });
--- a/browser/base/content/test/performance/browser_tabclose_reflows.js
+++ b/browser/base/content/test/performance/browser_tabclose_reflows.js
@@ -17,21 +17,28 @@ const EXPECTED_REFLOWS = [
 
 /*
  * This test ensures that there are no unexpected
  * uninterruptible reflows when closing new tabs.
  */
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
+  // Because the tab strip is a scrollable frame, we can't use the
+  // default dirtying function from withReflowObserver and reliably
+  // get reflows for the strip. Instead, we provide a node that's
+  // already in the scrollable frame to dirty - in this case, the
+  // original tab.
+  let origTab = gBrowser.selectedTab;
+
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
   await BrowserTestUtils.waitForCondition(() => tab._fullyOpen);
 
   // Add a reflow observer and open a new tab.
   await withReflowObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     gBrowser.removeTab(tab, { animate: true });
     await BrowserTestUtils.waitForEvent(tab, "transitionend",
         false, e => e.propertyName === "max-width");
     await switchDone;
-  }, EXPECTED_REFLOWS);
+  }, EXPECTED_REFLOWS, window, origTab);
   is(EXPECTED_REFLOWS.length, 0, "No reflows are expected when closing a tab");
 });
--- a/browser/base/content/test/performance/browser_tabopen_reflows.js
+++ b/browser/base/content/test/performance/browser_tabopen_reflows.js
@@ -22,21 +22,28 @@ const EXPECTED_REFLOWS = [
 
 /*
  * This test ensures that there are no unexpected
  * uninterruptible reflows when opening new tabs.
  */
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
+  // Because the tab strip is a scrollable frame, we can't use the
+  // default dirtying function from withReflowObserver and reliably
+  // get reflows for the strip. Instead, we provide a node that's
+  // already in the scrollable frame to dirty - in this case, the
+  // original tab.
+  let origTab = gBrowser.selectedTab;
+
   // Add a reflow observer and open a new tab.
   await withReflowObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     BrowserOpenTab();
     await BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "transitionend",
         false, e => e.propertyName === "max-width");
     await switchDone;
-  }, EXPECTED_REFLOWS);
+  }, EXPECTED_REFLOWS, window, origTab);
 
   let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
   await switchDone;
 });
--- a/browser/base/content/test/performance/browser_tabopen_squeeze_reflows.js
+++ b/browser/base/content/test/performance/browser_tabopen_squeeze_reflows.js
@@ -28,18 +28,25 @@ add_task(async function() {
   // Compute the number of tabs we can put into the strip without
   // overflowing, and remove one, so that we can create
   // TAB_COUNT_FOR_SQUEEE tabs, and then one more, which should
   // cause the tab to squeeze to a smaller size rather than overflow.
   const TAB_COUNT_FOR_SQUEEZE = computeMaxTabCount() - 1;
 
   await createTabs(TAB_COUNT_FOR_SQUEEZE);
 
+  // Because the tab strip is a scrollable frame, we can't use the
+  // default dirtying function from withReflowObserver and reliably
+  // get reflows for the strip. Instead, we provide a node that's
+  // already in the scrollable frame to dirty - in this case, the
+  // original tab.
+  let origTab = gBrowser.selectedTab;
+
   await withReflowObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     BrowserOpenTab();
     await BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "transitionend",
       false, e => e.propertyName === "max-width");
     await switchDone;
-  }, EXPECTED_REFLOWS);
+  }, EXPECTED_REFLOWS, window, origTab);
 
   await removeAllButFirstTab();
 });
--- a/browser/base/content/test/performance/browser_tabswitch_reflows.js
+++ b/browser/base/content/test/performance/browser_tabswitch_reflows.js
@@ -27,21 +27,27 @@ add_task(async function() {
   await ensureNoPreloadedBrowser();
 
   // At the time of writing, there are no reflows on simple tab switching.
   // Mochitest will fail if we have no assertions, so we add one here
   // to make sure nobody adds any new ones.
   Assert.equal(EXPECTED_REFLOWS.length, 0,
     "We shouldn't have added any new expected reflows.");
 
+  // Because the tab strip is a scrollable frame, we can't use the
+  // default dirtying function from withReflowObserver and reliably
+  // get reflows for the strip. Instead, we provide a node that's
+  // already in the scrollable frame to dirty - in this case, the
+  // original tab.
   let origTab = gBrowser.selectedTab;
+
   let firstSwitchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
   let otherTab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
   await firstSwitchDone;
 
   await withReflowObserver(async function() {
     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
     gBrowser.selectedTab = origTab;
     await switchDone;
-  }, EXPECTED_REFLOWS);
+  }, EXPECTED_REFLOWS, window, origTab);
 
   await BrowserTestUtils.removeTab(otherTab);
 });
--- a/browser/base/content/test/performance/browser_windowopen_reflows.js
+++ b/browser/base/content/test/performance/browser_windowopen_reflows.js
@@ -15,138 +15,45 @@
 const EXPECTED_REFLOWS = [
   [
     "select@chrome://global/content/bindings/textbox.xml",
     "focusAndSelectUrlBar@chrome://browser/content/browser.js",
     "_delayedStartup@chrome://browser/content/browser.js",
   ],
 ];
 
-if (Services.appinfo.OS == "Linux") {
-  if (gMultiProcessBrowser) {
-    EXPECTED_REFLOWS.push(
-      [
-        "handleEvent@chrome://browser/content/tabbrowser.xml",
-        "EventListener.handleEvent*tabbrowser-tabs_XBL_Constructor@chrome://browser/content/tabbrowser.xml",
-      ],
-    );
-  } else {
-    EXPECTED_REFLOWS.push(
-      [
-        "handleEvent@chrome://browser/content/tabbrowser.xml",
-        "inferFromText@chrome://browser/content/browser.js",
-        "handleEvent@chrome://browser/content/browser.js",
-      ],
-    );
-  }
+if (Services.appinfo.OS == "Darwin") {
+  // TabsInTitlebar._update causes a reflow on OS X trying to do calculations
+  // since layout info is already dirty. This doesn't seem to happen before
+  // MozAfterPaint on Linux.
+  EXPECTED_REFLOWS.push(
+    [
+      "rect@chrome://browser/content/browser-tabsintitlebar.js",
+      "_update@chrome://browser/content/browser-tabsintitlebar.js",
+      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
+      "handleEvent@chrome://browser/content/tabbrowser.xml",
+    ],
+  );
 }
 
-if (Services.appinfo.OS == "Darwin") {
+if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
   EXPECTED_REFLOWS.push(
     [
       "handleEvent@chrome://browser/content/tabbrowser.xml",
       "inferFromText@chrome://browser/content/browser.js",
       "handleEvent@chrome://browser/content/browser.js",
     ],
   );
 }
 
-if (Services.appinfo.OS == "WINNT") {
-  EXPECTED_REFLOWS.push(
-    [
-      "verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-      "inferFromText@chrome://browser/content/browser.js",
-      "handleEvent@chrome://browser/content/browser.js",
-    ],
-
-    [
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-      "EventListener.handleEvent*tabbrowser-tabs_XBL_Constructor@chrome://browser/content/tabbrowser.xml",
-    ],
-  );
-}
-
-if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
-  EXPECTED_REFLOWS.push(
-    [
-      "rect@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "rect@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "rect@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "rect@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-
-    [
-      "verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
-      "_update@chrome://browser/content/browser-tabsintitlebar.js",
-      "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
-      "handleEvent@chrome://browser/content/tabbrowser.xml",
-    ],
-  );
-}
-
 /*
  * This test ensures that there are no unexpected
  * uninterruptible reflows when opening new windows.
  */
 add_task(async function() {
-  const IS_WIN8 = (navigator.userAgent.indexOf("Windows NT 6.2") != -1);
-  if (IS_WIN8) {
-    ok(true, "Skipping this test because of perma-failures on Windows 8 x64 (bug 1381521)");
-    return;
-  }
-
-  // Flushing all caches helps to ensure that we get consistent
-  // behaviour when opening a new window, even if windows have been
-  // opened in previous tests.
-  Services.obs.notifyObservers(null, "startupcache-invalidate");
-  Services.obs.notifyObservers(null, "chrome-flush-skin-caches");
-  Services.obs.notifyObservers(null, "chrome-flush-caches");
-
   let win = OpenBrowserWindow();
 
   await withReflowObserver(async function() {
     let resizeEvent = BrowserTestUtils.waitForEvent(win, "resize");
     let delayedStartup =
       TestUtils.topicObserved("browser-delayed-startup-finished",
                               subject => subject == win);
     await resizeEvent;
--- a/browser/base/content/test/performance/head.js
+++ b/browser/base/content/test/performance/head.js
@@ -60,27 +60,30 @@
  *
  *        Note that line numbers are not included in the stacks.
  *
  *        Order of the reflows doesn't matter. Expected reflows that aren't seen
  *        will cause an assertion failure. When this argument is not passed,
  *        it defaults to the empty Array, meaning no reflows are expected.
  * @param window (browser window, optional)
  *        The browser window to monitor. Defaults to the current window.
+ * @param elemToDirty (DOM node, optional)
+ *        Callers can provide a custom DOM node to change some layout style
+ *        on in the event that the action being tested is occurring within
+ *        a scrollable frame. Otherwise, withReflowObserver defaults to dirtying
+ *        the first element child of the window.
  */
-async function withReflowObserver(testFn, expectedStacks = [], win = window) {
-  let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIDOMWindowUtils);
-  let dirtyFrameFn = () => {
-    try {
-      dwu.ensureDirtyRootFrame();
-    } catch (e) {
-      // If this fails, we should probably make note of it, but it's not fatal.
-      info("Note: ensureDirtyRootFrame threw an exception.");
-    }
+async function withReflowObserver(testFn, expectedStacks = [], win = window, elemToDirty) {
+  if (!elemToDirty) {
+    elemToDirty = win.document.firstElementChild;
+  }
+
+  let i = 0;
+  let dirtyFrameFn = (e) => {
+    elemToDirty.style.margin = (++i % 4) + "px";
   };
 
   let els = Cc["@mozilla.org/eventlistenerservice;1"]
               .getService(Ci.nsIEventListenerService);
 
   // We're going to remove the stacks one by one as we see them so that
   // we can check for expected, unseen reflows, so let's clone the array.
   expectedStacks = expectedStacks.slice(0);
@@ -112,19 +115,17 @@ async function withReflowObserver(testFn
         expectedStacks.splice(index, 1);
       } else {
         Assert.ok(false, "unexpected uninterruptible reflow \n" +
                          JSON.stringify(pathWithLineNumbers, null, "\t") + "\n");
       }
     },
 
     reflowInterruptible(start, end) {
-      // We're not interested in interruptible reflows, but might as well take the
-      // opportuntiy to dirty the root frame.
-      dirtyFrameFn();
+      // We're not interested in interruptible reflows.
     },
 
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
                                            Ci.nsISupportsWeakReference])
   };
 
   let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
@@ -142,16 +143,18 @@ async function withReflowObserver(testFn
                 `Unused expected reflow: ${JSON.stringify(remainder, null, "\t")}.\n` +
                 "This is probably a good thing - just remove it from the " +
                 "expected list.");
     }
 
 
     els.removeListenerForAllEvents(win, dirtyFrameFn, true);
     docShell.removeWeakReflowObserver(observer);
+
+    elemToDirty.style.margin = "";
   }
 }
 
 async function ensureNoPreloadedBrowser() {
   // If we've got a preloaded browser, get rid of it so that it
   // doesn't interfere with the test if it's loading. We have to
   // do this before we disable preloading or changing the new tab
   // URL, otherwise _getPreloadedBrowser will return null, despite
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4437,36 +4437,16 @@ nsDOMWindowUtils::GetDirectionFromText(c
     case eDir_Auto:
       MOZ_ASSERT_UNREACHABLE("GetDirectionFromText should never return this value");
       return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::EnsureDirtyRootFrame()
-{
-  nsIDocument* doc = GetDocument();
-  nsIPresShell* presShell = doc ? doc->GetShell() : nullptr;
-
-  if (!presShell) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsIFrame* frame = presShell->GetRootFrame();
-  if (!frame) {
-    return NS_ERROR_FAILURE;
-  }
-
-  presShell->FrameNeedsReflow(frame, nsIPresShell::eStyleChange,
-                              NS_FRAME_IS_DIRTY);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDOMWindowUtils::GetIsStyledByServo(bool* aStyledByServo)
 {
   nsIDocument* doc = GetDocument();
   *aStyledByServo = doc && doc->IsStyledByServo();
   return NS_OK;
 }
 
 NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -2019,24 +2019,16 @@ interface nsIDOMWindowUtils : nsISupport
    *
    * @param aString the string to retrieve the direction for.
    * @return one of DIRECTION_LTR, DIRECTION_RTL or DIRECTION_NOT_SET depending
    *         on the first-strong character found in the string.
    */
   long getDirectionFromText(in AString aString);
 
   /**
-   * Calls FrameNeedsReflow on that root frame so that a layout flush
-   * will be necessary.
-   *
-   * This should only be used for testing.
-   */
-  void ensureDirtyRootFrame();
-
-  /**
    * Whether the current document is styled by Servo's style engine.
    *
    * This calls nsIDocument::IsStyledByServo().
    */
   readonly attribute boolean isStyledByServo;
 
   // These consts are only for testing purposes.
   const long DEFAULT_MOUSE_POINTER_ID = 0;