Bug 1528052 - Suppress resize events until the initial paint has finished on mobile. r=botond
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 23 Apr 2019 04:14:55 +0000
changeset 470440 0b0a2b12bd0502a0743917abdd29109c2b2cdbfd
parent 470439 0f774c6d8ac396b6241fb27cfd6e7cad88c4b3ba
child 470441 74175527c5eab80b079b60a97b227e5ec5721c66
push id35906
push useraciure@mozilla.com
push dateTue, 23 Apr 2019 22:14:56 +0000
treeherdermozilla-central@0ce3633f8b80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1528052
milestone68.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
Bug 1528052 - Suppress resize events until the initial paint has finished on mobile. r=botond Differential Revision: https://phabricator.services.mozilla.com/D28172
gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
layout/base/GeckoMVMContext.cpp
layout/base/GeckoMVMContext.h
layout/base/MVMContext.h
layout/base/MobileViewportManager.cpp
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/nsDocumentViewer.cpp
layout/base/nsIPresShell.h
testing/web-platform/tests/css/cssom-view/resize-event-on-initial-layout.html
--- a/gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
+++ b/gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
@@ -57,17 +57,18 @@ class MockMVMContext : public MVMContext
     return LayoutDeviceMargin();
   }
   Maybe<LayoutDeviceIntSize> GetContentViewerSize() const {
     return Some(mDisplaySize);
   }
   bool AllowZoomingForDocument() const { return true; }
 
   void SetResolutionAndScaleTo(float aResolution) { mResolution = aResolution; }
-  void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize) {
+  void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
+              ResizeEventFlag aResizeEventFlag) {
     mICBSize = aNewSize;
     mContentSize = mLayoutFunction(mICBSize);
   }
 
   // Allow test code to modify the input metrics.
   void SetMinScale(CSSToScreenScale aMinScale) { mMinScale = aMinScale; }
   void SetMaxScale(CSSToScreenScale aMaxScale) { mMaxScale = aMaxScale; }
   void SetInitialScale(CSSToScreenScale aInitialScale) {
--- a/layout/base/GeckoMVMContext.cpp
+++ b/layout/base/GeckoMVMContext.cpp
@@ -155,19 +155,26 @@ void GeckoMVMContext::UpdateDisplayPortM
     nsLayoutUtils::SetDisplayPortBaseIfNotSet(root->GetContent(),
                                               displayportBase);
     nsIScrollableFrame* scrollable = do_QueryFrame(root);
     nsLayoutUtils::CalculateAndSetDisplayPortMargins(
         scrollable, nsLayoutUtils::RepaintMode::DoNotRepaint);
   }
 }
 
-void GeckoMVMContext::Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize) {
+void GeckoMVMContext::Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
+                             ResizeEventFlag aResizeEventFlag) {
   MOZ_ASSERT(mPresShell);
+
+  ResizeReflowOptions reflowOptions = ResizeReflowOptions::eNoOption;
+  if (aResizeEventFlag == ResizeEventFlag::Suppress) {
+    reflowOptions |= ResizeReflowOptions::eSuppressResizeEvent;
+  }
+
   RefPtr<PresShell> presShell = mPresShell;
   presShell->ResizeReflowIgnoreOverride(
       nsPresContext::CSSPixelsToAppUnits(aNewSize.width),
       nsPresContext::CSSPixelsToAppUnits(aNewSize.height),
       nsPresContext::CSSPixelsToAppUnits(aOldSize.width),
-      nsPresContext::CSSPixelsToAppUnits(aOldSize.height));
+      nsPresContext::CSSPixelsToAppUnits(aOldSize.height), reflowOptions);
 }
 
 }  // namespace mozilla
--- a/layout/base/GeckoMVMContext.h
+++ b/layout/base/GeckoMVMContext.h
@@ -47,17 +47,18 @@ class GeckoMVMContext : public MVMContex
       const override;
   Maybe<LayoutDeviceIntSize> GetContentViewerSize() const override;
   bool AllowZoomingForDocument() const override;
 
   void SetResolutionAndScaleTo(float aResolution) override;
   void SetVisualViewportSize(const CSSSize& aSize) override;
   void UpdateDisplayPortMargins() override;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
-  void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize) override;
+  void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
+              ResizeEventFlag aResizeEventFlag) override;
 
  private:
   RefPtr<dom::Document> mDocument;
   // raw ref since the presShell owns this
   PresShell* MOZ_NON_OWNING_REF mPresShell;
   nsCOMPtr<dom::EventTarget> mEventTarget;
 };
 
--- a/layout/base/MVMContext.h
+++ b/layout/base/MVMContext.h
@@ -49,14 +49,20 @@ class MVMContext {
   virtual LayoutDeviceMargin ScrollbarAreaToExcludeFromCompositionBounds()
       const = 0;
   virtual Maybe<LayoutDeviceIntSize> GetContentViewerSize() const = 0;
   virtual bool AllowZoomingForDocument() const = 0;
 
   virtual void SetResolutionAndScaleTo(float aResolution) = 0;
   virtual void SetVisualViewportSize(const CSSSize& aSize) = 0;
   virtual void UpdateDisplayPortMargins() = 0;
-  virtual void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize) = 0;
+
+  enum class ResizeEventFlag {
+    IfNecessary,  // resize events will be fired if necessary.
+    Suppress,     // resize events will never be fired.
+  };
+  virtual void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
+                      ResizeEventFlag aResizeEventFlag) = 0;
 };
 
 }  // namespace mozilla
 
 #endif  // MVMContext_h_
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -543,17 +543,19 @@ void MobileViewportManager::RefreshViewp
   CSSSize oldSize = mMobileViewportSize;
 
   // Update internal state.
   mMobileViewportSize = viewport;
 
   RefPtr<MobileViewportManager> strongThis(this);
 
   // Kick off a reflow.
-  mContext->Reflow(viewport, oldSize);
+  mContext->Reflow(viewport, oldSize,
+                   mIsFirstPaint ? MVMContext::ResizeEventFlag::Suppress
+                                 : MVMContext::ResizeEventFlag::IfNecessary);
 
   // We are going to fit the content to the display width if the initial-scale
   // is not specied and if the content is still wider than the display width.
   ShrinkToDisplaySizeIfNeeded(viewportInfo, displaySize);
 
   mIsFirstPaint = false;
 }
 
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1869,17 +1869,17 @@ nsresult PresShell::ResizeReflowIgnoreOv
 
     mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
     // There isn't anything useful we can do if the initial reflow hasn't
     // happened.
     return NS_OK;
   }
 
   WritingMode wm = rootFrame->GetWritingMode();
-  const bool shrinkToFit = aOptions == ResizeReflowOptions::eBSizeLimit;
+  const bool shrinkToFit = !!(aOptions & ResizeReflowOptions::eBSizeLimit);
   MOZ_ASSERT(shrinkToFit ||
                  (wm.IsVertical() ? aWidth : aHeight) != NS_UNCONSTRAINEDSIZE,
              "unconstrained bsize only usable with eBSizeLimit");
   MOZ_ASSERT((wm.IsVertical() ? aHeight : aWidth) != NS_UNCONSTRAINEDSIZE,
              "unconstrained isize not allowed");
   bool isBSizeChanging =
       wm.IsVertical() ? aOldWidth != aWidth : aOldHeight != aHeight;
   nscoord targetWidth = aWidth;
@@ -1989,17 +1989,18 @@ nsresult PresShell::ResizeReflowIgnoreOv
     } else {
       if (mPresContext->GetVisibleArea().height == NS_UNCONSTRAINEDSIZE) {
         mPresContext->SetVisibleArea(
             nsRect(0, 0, aWidth, rootFrame->GetRect().height));
       }
     }
   }
 
-  if (!mIsDestroying && !mResizeEventPending) {
+  if (!mIsDestroying && !mResizeEventPending &&
+      !(aOptions & ResizeReflowOptions::eSuppressResizeEvent)) {
     mResizeEventPending = true;
     if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
       mPresContext->RefreshDriver()->AddResizeEventFlushObserver(this);
     }
   }
 
   return NS_OK;  // XXX this needs to be real. MMP
 }
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -91,23 +91,25 @@ class PresShell final : public nsIPresSh
   NS_IMETHOD SetDisplaySelection(int16_t aToggle) override;
   NS_IMETHOD GetDisplaySelection(int16_t* aToggle) override;
   NS_IMETHOD ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
                                      SelectionRegion aRegion,
                                      int16_t aFlags) override;
   NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
 
   nsresult Initialize() override;
-  MOZ_CAN_RUN_SCRIPT nsresult ResizeReflow(
-      nscoord aWidth, nscoord aHeight, nscoord aOldWidth = 0,
-      nscoord aOldHeight = 0,
-      ResizeReflowOptions aOptions = ResizeReflowOptions::eBSizeExact) override;
+  MOZ_CAN_RUN_SCRIPT nsresult
+  ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth = 0,
+               nscoord aOldHeight = 0,
+               mozilla::ResizeReflowOptions aOptions =
+                   mozilla::ResizeReflowOptions::eNoOption) override;
   MOZ_CAN_RUN_SCRIPT nsresult ResizeReflowIgnoreOverride(
       nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight,
-      ResizeReflowOptions aOptions = ResizeReflowOptions::eBSizeExact) override;
+      mozilla::ResizeReflowOptions aOptions =
+          mozilla::ResizeReflowOptions::eNoOption) override;
 
   MOZ_CAN_RUN_SCRIPT
   void DoFlushPendingNotifications(FlushType aType) override;
   MOZ_CAN_RUN_SCRIPT
   void DoFlushPendingNotifications(ChangesToFlush aType) override;
 
   nsRectVisibility GetRectVisibility(nsIFrame* aFrame, const nsRect& aRect,
                                      nscoord aMinTwips) const override;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3126,19 +3126,18 @@ nsresult nsDocumentViewer::GetContentSiz
   {
     RefPtr<gfxContext> rcx(presShell->CreateReferenceRenderingContext());
     prefWidth = root->GetPrefISize(rcx);
   }
   if (prefWidth > aMaxWidth) {
     prefWidth = aMaxWidth;
   }
 
-  nsresult rv =
-      presShell->ResizeReflow(prefWidth, aMaxHeight, 0, 0,
-                              nsIPresShell::ResizeReflowOptions::eBSizeLimit);
+  nsresult rv = presShell->ResizeReflow(
+      prefWidth, aMaxHeight, 0, 0, mozilla::ResizeReflowOptions::eBSizeLimit);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<nsPresContext> presContext = GetPresContext();
   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
 
   // Protect against bogus returns here
   nsRect shellArea = presContext->GetVisibleArea();
   NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -167,16 +167,31 @@ typedef struct CapturingContentInfo {
 enum nsRectVisibility {
   nsRectVisibility_kVisible,
   nsRectVisibility_kAboveViewport,
   nsRectVisibility_kBelowViewport,
   nsRectVisibility_kLeftOfViewport,
   nsRectVisibility_kRightOfViewport
 };
 
+namespace mozilla {
+enum class ResizeReflowOptions : uint32_t {
+  eNoOption = 0,
+  // the resulting BSize can be less than the given one, producing
+  // shrink-to-fit sizing in the block dimension
+  eBSizeLimit = 1 << 0,
+  // suppress resize events even if the content size is changed due to the
+  // reflow.  This flag is used for mobile since on mobile we need to do an
+  // additional reflow to zoom the content by the initial-scale or auto scaling
+  // and we don't want any resize events during the initial paint.
+  eSuppressResizeEvent = 1 << 1,
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ResizeReflowOptions)
+}  // namespace mozilla
+
 /**
  * Presentation shell interface. Presentation shells are the
  * controlling point for managing the presentation of a document. The
  * presentation shell holds a live reference to the document, the
  * presentation context, the style manager, the style set and the root
  * frame. <p>
  *
  * When this object is Release'd, it will release the document, the
@@ -326,38 +341,33 @@ class nsIPresShell : public nsStubDocume
    * object and then enqueues a reflow of the frame model.
    *
    * Callers of this method must hold a reference to this shell that
    * is guaranteed to survive through arbitrary script execution.
    * Calling Initialize can execute arbitrary script.
    */
   virtual nsresult Initialize() = 0;
 
-  enum class ResizeReflowOptions : uint32_t {
-    // the resulting BSize should be exactly as given
-    eBSizeExact,
-    // the resulting BSize can be less than the given one, producing
-    // shrink-to-fit sizing in the block dimension
-    eBSizeLimit
-  };
   /**
    * Reflow the frame model into a new width and height.  The
    * coordinates for aWidth and aHeight must be in standard nscoord's.
    */
   MOZ_CAN_RUN_SCRIPT virtual nsresult ResizeReflow(
       nscoord aWidth, nscoord aHeight, nscoord aOldWidth = 0,
       nscoord aOldHeight = 0,
-      ResizeReflowOptions aOptions = ResizeReflowOptions::eBSizeExact) = 0;
+      mozilla::ResizeReflowOptions aOptions =
+          mozilla::ResizeReflowOptions::eNoOption) = 0;
   /**
    * Do the same thing as ResizeReflow but even if ResizeReflowOverride was
    * called previously.
    */
   MOZ_CAN_RUN_SCRIPT virtual nsresult ResizeReflowIgnoreOverride(
       nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight,
-      ResizeReflowOptions aOptions = ResizeReflowOptions::eBSizeExact) = 0;
+      mozilla::ResizeReflowOptions aOptions =
+          mozilla::ResizeReflowOptions::eNoOption) = 0;
 
   /**
    * Returns true if the platform/pref or docshell require a meta viewport.
    */
   virtual bool GetIsViewportOverridden() = 0;
 
   /**
    * Note that the assumptions that determine the need for a meta viewport
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/resize-event-on-initial-layout.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#run-the-resize-steps"/>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+promise_test(async t => {
+  let gotResizeEvent = false;
+
+  on_event(window, 'resize', () => gotResizeEvent = true);
+
+  await new Promise(resolve  => requestAnimationFrame(resolve));
+  await new Promise(resolve  => requestAnimationFrame(resolve));
+
+  assert_false(gotResizeEvent, 'resize event should not be fired');
+}, 'resize events are not fired on the initial layout');
+</script>