Backed out 4 changesets (bug 1488953) for causing Bug 1559690. a=backout
authorCsoregi Natalia <ncsoregi@mozilla.com>
Thu, 27 Jun 2019 01:03:50 +0300
changeset 480327 91c42888cf7fa67f2b9d08d0e62cb203ad1e6619
parent 480260 3752d5cac66b9f72bfe7252e399349cb0eb3f59f
child 480343 70e7c3ef6cae2266147c38ad250692ffe84aec26
push id88628
push userccoroiu@mozilla.com
push dateThu, 27 Jun 2019 09:43:10 +0000
treeherderautoland@bba53c82b50b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1488953, 1559690
milestone69.0a1
backs out6b9cfebe807648f7c68f5a618f54cb0633298045
6759badeec0e4accbd7f98d39d821c276212faef
5555e12078d379f7f54ad89ffa7b9dbc99d3cf14
2a638724408b5b272c052cf06e663ea1f4177181
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
Backed out 4 changesets (bug 1488953) for causing Bug 1559690. a=backout Backed out changeset 6b9cfebe8076 (bug 1488953) Backed out changeset 6759badeec0e (bug 1488953) Backed out changeset 5555e12078d3 (bug 1488953) Backed out changeset 2a638724408b (bug 1488953)
browser/base/content/test/performance/browser_windowopen.js
browser/base/content/test/tabs/browser_tabCloseSpacer.js
layout/base/PresShell.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/generic/nsGfxScrollFrame.cpp
--- a/browser/base/content/test/performance/browser_windowopen.js
+++ b/browser/base/content/test/performance/browser_windowopen.js
@@ -16,24 +16,22 @@ const EXPECTED_REFLOWS = [
   /**
    * Nothing here! Please don't add anything new!
    */
 ];
 
 // We'll assume the changes we are seeing are due to this focus change if
 // there are at least 5 areas that changed near the top of the screen, or if
 // the toolbar background is involved on OSX, but will only ignore this once.
-function filterLikelyFocusChange(rects) {
-  if (rects.length > 5 && rects.every(r => r.y2 < 100)) {
-    return [];
-  }
-  if (Services.appinfo.OS == "Darwin" && rects.length >= 2) {
-    return rects.filter(r => r.y1 != 0 || r.h != 33);
-  }
-  return rects;
+function isLikelyFocusChange(rects) {
+  if (rects.length > 5 && rects.every(r => r.y2 < 100))
+    return true;
+  if (Services.appinfo.OS == "Darwin" && rects.length == 2 && rects.every(r => r.y1 == 0 && r.h == 33))
+    return true;
+  return false;
 }
 
 /*
  * This test ensures that there are no unexpected
  * uninterruptible reflows or flickering areas when opening new windows.
  */
 add_task(async function() {
   // Flushing all caches helps to ensure that we get consistent
@@ -49,25 +47,22 @@ add_task(async function() {
   let alreadyFocused = false;
   let inRange = (val, min, max) => min <= val && val <= max;
   let expectations = {
     expectedReflows: EXPECTED_REFLOWS,
     frames: {
       filter(rects, frame, previousFrame) {
         // The first screenshot we get in OSX / Windows shows an unfocused browser
         // window for some reason. See bug 1445161.
-        if (!alreadyFocused) {
+        if (!alreadyFocused && isLikelyFocusChange(rects)) {
           alreadyFocused = true;
-          let filteredRects = filterLikelyFocusChange(rects);
-          if (rects !== filteredRects) {
-            todo(false,
-                 "bug 1445161 - the window should be focused at first paint, " +
-                 rects.filter(r => !filteredRects.includes(r)).toSource());
-          }
-          return filteredRects;
+          todo(false,
+               "bug 1445161 - the window should be focused at first paint, " +
+               rects.toSource());
+          return [];
         }
 
         return rects;
       },
       exceptions: [
         {name: "bug 1421463 - reload toolbar icon shouldn't flicker",
          condition: r => inRange(r.h, 13, 14) && inRange(r.w, 14, 16) && // icon size
                          inRange(r.y1, 40, 80) && // in the toolbar
--- a/browser/base/content/test/tabs/browser_tabCloseSpacer.js
+++ b/browser/base/content/test/tabs/browser_tabCloseSpacer.js
@@ -37,33 +37,23 @@ add_task(async function() {
   ok(!gBrowser.tabContainer.hasAttribute("overflow"), "not overflowing");
   ok(gBrowser.tabContainer.hasAttribute("using-closing-tabs-spacer"), "using spacer");
   is(downButton.clientWidth, 0, "down button has no width");
   isnot(closingTabsSpacer.clientWidth, 0, "spacer has some width");
 });
 
 async function overflowTabs() {
   let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
-  const originalSmoothScroll = arrowScrollbox.smoothScroll;
-  arrowScrollbox.smoothScroll = false;
-  registerCleanupFunction(() => {
-    arrowScrollbox.smoothScroll = originalSmoothScroll;
-  });
-
   let width = ele => ele.getBoundingClientRect().width;
   let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
   let tabCountForOverflow = Math.ceil(width(arrowScrollbox) / tabMinWidth * 1.1);
   while (gBrowser.tabs.length < tabCountForOverflow) {
     BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true, index: 0 });
   }
-
-  // Make sure scrolling finished.
-  await new Promise(resolve => {
-    arrowScrollbox.addEventListener("scrollend", resolve, { once: true });
-  });
+  await window.promiseDocumentFlushed(() => {});
 }
 
 function getLastCloseButton() {
   let lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
   return lastTab.closeButton;
 }
 
 function getLastCloseButtonLocation() {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4460,16 +4460,22 @@ void PresShell::ReconstructFrames() {
 
 nsresult PresShell::RenderDocument(const nsRect& aRect,
                                    RenderDocumentFlags aFlags,
                                    nscolor aBackgroundColor,
                                    gfxContext* aThebesContext) {
   NS_ENSURE_TRUE(!(aFlags & RenderDocumentFlags::IsUntrusted),
                  NS_ERROR_NOT_IMPLEMENTED);
 
+  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
+  if (rootPresContext) {
+    rootPresContext->FlushWillPaintObservers();
+    if (mIsDestroying) return NS_OK;
+  }
+
   nsAutoScriptBlocker blockScripts;
 
   // Set up the rectangle as the path in aThebesContext
   gfxRect r(0, 0, nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
             nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
   aThebesContext->NewPath();
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   aThebesContext->SnappedRectangle(r);
@@ -8824,16 +8830,27 @@ void PresShell::WillPaint() {
   // as GetRootPresContext, for the case of a browser with a large
   // number of tabs.
   // Don't bother doing anything if some viewmanager in our tree is painting
   // while we still have painting suppressed or we are not active.
   if (!mIsActive || mPaintingSuppressed || !IsVisible()) {
     return;
   }
 
+  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
+  if (!rootPresContext) {
+    // In some edge cases, such as when we don't have a root frame yet,
+    // we can't find the root prescontext. There's nothing to do in that
+    // case.
+    return;
+  }
+
+  rootPresContext->FlushWillPaintObservers();
+  if (mIsDestroying) return;
+
   // Process reflows, if we have them, to reduce flicker due to invalidates and
   // reflow being interspersed.  Note that we _do_ allow this to be
   // interruptible; if we can't do all the reflows it's better to flicker a bit
   // than to freeze up.
   FlushPendingNotifications(
       ChangesToFlush(FlushType::InterruptibleLayout, false));
 }
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2704,16 +2704,39 @@ void nsRootPresContext::CollectPluginGeo
   SortConfigurations(&configurations);
   if (clm) {
     clm->StorePluginWidgetConfigurations(configurations);
   }
   PluginDidSetGeometry(mRegisteredPlugins);
 #endif  // #ifndef XP_MACOSX
 }
 
+void nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable) {
+  if (!mWillPaintFallbackEvent.IsPending()) {
+    mWillPaintFallbackEvent = new RunWillPaintObservers(this);
+    Document()->Dispatch(TaskCategory::Other,
+                         do_AddRef(mWillPaintFallbackEvent));
+  }
+  mWillPaintObservers.AppendElement(aRunnable);
+}
+
+/**
+ * Run all runnables that need to get called before the next paint.
+ */
+void nsRootPresContext::FlushWillPaintObservers() {
+  mWillPaintFallbackEvent = nullptr;
+  nsTArray<nsCOMPtr<nsIRunnable>> observers;
+  observers.SwapElements(mWillPaintObservers);
+  for (uint32_t i = 0; i < observers.Length(); ++i) {
+    observers[i]->Run();
+  }
+}
+
 size_t nsRootPresContext::SizeOfExcludingThis(
     MallocSizeOf aMallocSizeOf) const {
   return nsPresContext::SizeOfExcludingThis(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mRegisteredPlugins
+  // - mWillPaintObservers
+  // - mWillPaintFallbackEvent
 }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1349,33 +1349,63 @@ class nsRootPresContext final : public n
    * Transfer stored plugin geometry updates to the compositor. Called during
    * reflow, data is shipped over with layer updates. e10s specific.
    */
   void CollectPluginGeometryUpdates(
       mozilla::layers::LayerManager* aLayerManager);
 
   virtual bool IsRoot() override { return true; }
 
+  /**
+   * Add a runnable that will get called before the next paint. They will get
+   * run eventually even if painting doesn't happen. They might run well before
+   * painting happens.
+   */
+  void AddWillPaintObserver(nsIRunnable* aRunnable);
+
+  /**
+   * Run all runnables that need to get called before the next paint.
+   */
+  void FlushWillPaintObservers();
+
   virtual size_t SizeOfExcludingThis(
       mozilla::MallocSizeOf aMallocSizeOf) const override;
 
  protected:
   /**
    * Start a timer to ensure we eventually run ApplyPluginGeometryUpdates.
    */
   void InitApplyPluginGeometryTimer();
   /**
    * Cancel the timer that ensures we eventually run ApplyPluginGeometryUpdates.
    */
   void CancelApplyPluginGeometryTimer();
 
+  class RunWillPaintObservers : public mozilla::Runnable {
+   public:
+    explicit RunWillPaintObservers(nsRootPresContext* aPresContext)
+        : Runnable("nsPresContextType::RunWillPaintObservers"),
+          mPresContext(aPresContext) {}
+    void Revoke() { mPresContext = nullptr; }
+    NS_IMETHOD Run() override {
+      if (mPresContext) {
+        mPresContext->FlushWillPaintObservers();
+      }
+      return NS_OK;
+    }
+    // The lifetime of this reference is handled by an nsRevocableEventPtr
+    nsRootPresContext* MOZ_NON_OWNING_REF mPresContext;
+  };
+
   friend class nsPresContext;
 
   nsCOMPtr<nsITimer> mApplyPluginGeometryTimer;
   nsTHashtable<nsRefPtrHashKey<nsIContent>> mRegisteredPlugins;
+  nsTArray<nsCOMPtr<nsIRunnable>> mWillPaintObservers;
+  nsRevocableEventPtr<RunWillPaintObservers> mWillPaintFallbackEvent;
 };
 
 #ifdef MOZ_REFLOW_PERF
 
 #  define DO_GLOBAL_REFLOW_COUNT(_name) \
     aPresContext->CountReflows((_name), (nsIFrame*)this);
 #else
 #  define DO_GLOBAL_REFLOW_COUNT(_name)
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -5371,18 +5371,23 @@ void ScrollFrameHelper::PostOverflowEven
 
   bool newHorizontalOverflow = childSize.width > scrollportSize.width;
   bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
 
   if (!vertChanged && !horizChanged) {
     return;
   }
 
+  nsRootPresContext* rpc = mOuter->PresContext()->GetRootPresContext();
+  if (!rpc) {
+    return;
+  }
+
   mAsyncScrollPortEvent = new AsyncScrollPortEvent(this);
-  nsContentUtils::AddScriptRunner(mAsyncScrollPortEvent.get());
+  rpc->AddWillPaintObserver(mAsyncScrollPortEvent.get());
 }
 
 nsIFrame* ScrollFrameHelper::GetFrameForDir() const {
   nsIFrame* frame = mOuter;
   // XXX This is a bit on the slow side.
   if (mIsRoot) {
     // https://drafts.csswg.org/css-writing-modes-4/#principal-flow
     // If we're the root scrollframe, we need the root element's style data.