Bug 1511137 - Track more accurately when the main thread originates a resolution change. r=kats
authorBotond Ballo <botond@mozilla.com>
Wed, 05 Dec 2018 16:29:18 +0000
changeset 449445 79e8494d5df5c0e59a373e054ba25f1f25bb40bf
parent 449444 4ad8241eb92a80771580ac497e4e4c2468f861da
child 449446 8049d1a1188073f9cb5bfb489b690ef57ee3e5d9
push id35164
push usershindli@mozilla.com
push dateThu, 06 Dec 2018 04:02:49 +0000
treeherdermozilla-central@d932537fec3b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1511137
milestone65.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 1511137 - Track more accurately when the main thread originates a resolution change. r=kats The tracking is done using nsAtom origins, similarly to how updates to the scroll offset are tracked. Currently, APZ still uses some heuristics to deduce that the main thread originated a resolution change in some cases, but the intention is to try to remove those and rely only on this mechanism in the future. Differential Revision: https://phabricator.services.mozilla.com/D13741
dom/base/nsDOMWindowUtils.cpp
dom/base/nsDocument.cpp
gfx/layers/FrameMetrics.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/util/APZCCallbackHelper.cpp
gfx/layers/ipc/LayersMessageUtils.h
layout/base/MobileViewportManager.cpp
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/generic/nsGfxScrollFrame.cpp
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -555,17 +555,17 @@ nsDOMWindowUtils::SetResolution(float aR
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SetResolutionAndScaleTo(float aResolution) {
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     return NS_ERROR_FAILURE;
   }
 
-  presShell->SetResolutionAndScaleTo(aResolution);
+  presShell->SetResolutionAndScaleTo(aResolution, nsGkAtoms::other);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SetRestoreResolution(float aResolution,
                                        uint32_t aDisplayWidth,
                                        uint32_t aDisplayHeight) {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -10174,17 +10174,17 @@ void nsIDocument::CleanupFullscreenState
     }
   }
   mFullscreenStack.Clear();
   mFullscreenRoot = nullptr;
 
   // Restore the zoom level that was in place prior to entering fullscreen.
   if (nsIPresShell* shell = GetShell()) {
     if (shell->GetMobileViewportManager()) {
-      shell->SetResolutionAndScaleTo(mSavedResolution);
+      shell->SetResolutionAndScaleTo(mSavedResolution, nsGkAtoms::restore);
     }
   }
 
   UpdateViewportScrollbarOverrideForFullscreen(this);
 }
 
 bool nsIDocument::FullscreenStackPush(Element* aElement) {
   NS_ASSERTION(aElement, "Must pass non-null to FullscreenStackPush()");
@@ -10571,17 +10571,18 @@ bool nsIDocument::ApplyFullscreen(Unique
     // fixed elements are sized to the layout viewport).
     // This also ensures that things like video controls aren't zoomed in
     // when in fullscreen mode.
     if (nsIPresShell* shell = child->GetShell()) {
       if (RefPtr<MobileViewportManager> manager =
               shell->GetMobileViewportManager()) {
         // Save the previous resolution so it can be restored.
         child->mSavedResolution = shell->GetResolution();
-        shell->SetResolutionAndScaleTo(manager->ComputeIntrinsicResolution());
+        shell->SetResolutionAndScaleTo(manager->ComputeIntrinsicResolution(),
+                                       nsGkAtoms::other);
       }
     }
 
     NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
                  "Fullscreen root should be set!");
     if (child == fullScreenRootDoc) {
       break;
     }
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -807,31 +807,33 @@ struct ScrollMetadata {
         mLineScrollAmount(0, 0),
         mPageScrollAmount(0, 0),
         mScrollClip(),
         mHasScrollgrab(false),
         mIsLayersIdRoot(false),
         mIsAutoDirRootContentRTL(false),
         mUsesContainerScrolling(false),
         mForceDisableApz(false),
+        mResolutionUpdated(false),
         mOverscrollBehavior() {}
 
   bool operator==(const ScrollMetadata& aOther) const {
     return mMetrics == aOther.mMetrics && mSnapInfo == aOther.mSnapInfo &&
            mScrollParentId == aOther.mScrollParentId &&
            mBackgroundColor == aOther.mBackgroundColor &&
            // don't compare mContentDescription
            mLineScrollAmount == aOther.mLineScrollAmount &&
            mPageScrollAmount == aOther.mPageScrollAmount &&
            mScrollClip == aOther.mScrollClip &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
            mIsAutoDirRootContentRTL == aOther.mIsAutoDirRootContentRTL &&
            mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
            mForceDisableApz == aOther.mForceDisableApz &&
+           mResolutionUpdated == aOther.mResolutionUpdated &&
            mDisregardedDirection == aOther.mDisregardedDirection &&
            mOverscrollBehavior == aOther.mOverscrollBehavior;
   }
 
   bool operator!=(const ScrollMetadata& aOther) const {
     return !operator==(aOther);
   }
 
@@ -902,16 +904,18 @@ struct ScrollMetadata {
   // Implemented out of line because the implementation needs gfxPrefs.h
   // and we don't want to include that from FrameMetrics.h.
   void SetUsesContainerScrolling(bool aValue);
   bool UsesContainerScrolling() const { return mUsesContainerScrolling; }
   void SetForceDisableApz(bool aForceDisable) {
     mForceDisableApz = aForceDisable;
   }
   bool IsApzForceDisabled() const { return mForceDisableApz; }
+  void SetResolutionUpdated(bool aUpdated) { mResolutionUpdated = aUpdated; }
+  bool IsResolutionUpdated() const { return mResolutionUpdated; }
 
   // For more details about the concept of a disregarded direction, refer to the
   // code which defines mDisregardedDirection.
   Maybe<ScrollDirection> GetDisregardedDirection() const {
     return mDisregardedDirection;
   }
   void SetDisregardedDirection(const Maybe<ScrollDirection>& aValue) {
     mDisregardedDirection = aValue;
@@ -978,16 +982,21 @@ struct ScrollMetadata {
   // True if scrolling using containers, false otherwise. This can be removed
   // when containerful scrolling is eliminated.
   bool mUsesContainerScrolling : 1;
 
   // Whether or not the compositor should actually do APZ-scrolling on this
   // scrollframe.
   bool mForceDisableApz : 1;
 
+  // Whether the pres shell resolution stored in mMetrics reflects a change
+  // originated by the main thread. Plays a similar role for the resolution as
+  // FrameMetrics::mScrollUpdateType) does for the scroll offset.
+  bool mResolutionUpdated : 1;
+
   // The disregarded direction means the direction which is disregarded anyway,
   // even if the scroll frame overflows in that direction and the direction is
   // specified as scrollable. This could happen in some scenarios, for instance,
   // a single-line text control frame should disregard wheel scroll in
   // its block-flow direction even if it overflows in that direction.
   Maybe<ScrollDirection> mDisregardedDirection;
 
   // The overscroll behavior for this scroll frame.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -4413,21 +4413,23 @@ void AsyncPanZoomController::NotifyLayer
                this);
       needContentRepaint = true;
     }
   } else {
     // If we're not taking the aLayerMetrics wholesale we still need to pull
     // in some things into our local Metrics() because these things are
     // determined by Gecko and our copy in Metrics() may be stale.
 
+    // TODO: Rely entirely on |aScrollMetadata.IsResolutionUpdated()| to
+    //       determine which branch to take, and drop the other conditions.
     if (FuzzyEqualsAdditive(Metrics().GetCompositionBounds().Width(),
                             aLayerMetrics.GetCompositionBounds().Width()) &&
         Metrics().GetDevPixelsPerCSSPixel() ==
             aLayerMetrics.GetDevPixelsPerCSSPixel() &&
-        !viewportUpdated) {
+        !viewportUpdated && !aScrollMetadata.IsResolutionUpdated()) {
       // Any change to the pres shell resolution was requested by APZ and is
       // already included in our zoom; however, other components of the
       // cumulative resolution (a parent document's pres-shell resolution, or
       // the css-driven resolution) may have changed, and we need to update
       // our zoom to reflect that. Note that we can't just take
       // aLayerMetrics.mZoom because the APZ may have additional async zoom
       // since the repaint request.
       gfxSize totalResolutionChange = aLayerMetrics.GetCumulativeResolution() /
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -312,17 +312,17 @@ void APZCCallbackHelper::UpdateRootFrame
                                    aRequest.GetPresShellResolution())) {
       return;
     }
 
     // The pres shell resolution is updated by the the async zoom since the
     // last paint.
     presShellResolution =
         aRequest.GetPresShellResolution() * aRequest.GetAsyncZoom().scale;
-    shell->SetResolutionAndScaleTo(presShellResolution);
+    shell->SetResolutionAndScaleTo(presShellResolution, nsGkAtoms::apz);
   }
 
   // Do this as late as possible since scrolling can flush layout. It also
   // adjusts the display port margins, so do it before we set those.
   ScreenMargin displayPortMargins = ScrollFrame(content, aRequest);
 
   SetDisplayPortMargins(shell, content, displayPortMargins,
                         aRequest.CalculateCompositedSizeInCssPixels());
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -334,16 +334,17 @@ struct ParamTraits<mozilla::layers::Scro
     WriteParam(aMsg, aParam.mLineScrollAmount);
     WriteParam(aMsg, aParam.mPageScrollAmount);
     WriteParam(aMsg, aParam.mScrollClip);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mIsLayersIdRoot);
     WriteParam(aMsg, aParam.mIsAutoDirRootContentRTL);
     WriteParam(aMsg, aParam.mUsesContainerScrolling);
     WriteParam(aMsg, aParam.mForceDisableApz);
+    WriteParam(aMsg, aParam.mResolutionUpdated);
     WriteParam(aMsg, aParam.mDisregardedDirection);
     WriteParam(aMsg, aParam.mOverscrollBehavior);
   }
 
   static bool ReadContentDescription(const Message* aMsg, PickleIterator* aIter,
                                      paramType* aResult) {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
@@ -368,16 +369,18 @@ struct ParamTraits<mozilla::layers::Scro
             ReadBoolForBitfield(aMsg, aIter, aResult,
                                 &paramType::SetIsLayersIdRoot) &&
             ReadBoolForBitfield(aMsg, aIter, aResult,
                                 &paramType::SetIsAutoDirRootContentRTL) &&
             ReadBoolForBitfield(aMsg, aIter, aResult,
                                 &paramType::SetUsesContainerScrolling) &&
             ReadBoolForBitfield(aMsg, aIter, aResult,
                                 &paramType::SetForceDisableApz) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult,
+                                &paramType::SetResolutionUpdated) &&
             ReadParam(aMsg, aIter, &aResult->mDisregardedDirection) &&
             ReadParam(aMsg, aIter, &aResult->mOverscrollBehavior));
   }
 };
 
 template <>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier> {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -324,17 +324,17 @@ void MobileViewportManager::UpdateResolu
       }
     }
   }
 
   // If the zoom has changed, update the pres shell resolution accordingly.
   if (newZoom) {
     LayoutDeviceToLayerScale resolution = ZoomToResolution(*newZoom, cssToDev);
     MVM_LOG("%p: setting resolution %f\n", this, resolution.scale);
-    mPresShell->SetResolutionAndScaleTo(resolution.scale);
+    mPresShell->SetResolutionAndScaleTo(resolution.scale, nsGkAtoms::other);
 
     MVM_LOG("%p: New zoom is %f\n", this, newZoom->scale);
   }
 
   // The visual viewport size depends on both the zoom and the display size,
   // and needs to be updated if either might have changed.
   if (newZoom || aType == UpdateType::ViewportSize) {
     UpdateVisualViewportSize(aDisplaySize, newZoom ? *newZoom : zoom);
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -776,16 +776,17 @@ PresShell::PresShell()
       mApproximateFrameVisibilityVisited(false),
       mNextPaintCompressed(false),
       mHasCSSBackgroundColor(false),
       mScaleToResolution(false),
       mIsLastChromeOnlyEscapeKeyConsumed(false),
       mHasReceivedPaintMessage(false),
       mIsLastKeyDownCanceled(false),
       mHasHandledUserInput(false),
+      mResolutionUpdated(false),
       mForceDispatchKeyPressEventsForNonPrintableKeys(false),
       mForceUseLegacyKeyCodeAndCharCodeValues(false),
       mInitializedWithKeyPressEventDispatchingBlacklist(false) {
   MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
 
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = MakeUnique<ReflowCountMgr>();
   mReflowCountMgr->SetPresContext(mPresContext);
@@ -5101,31 +5102,35 @@ void PresShell::SetIgnoreViewportScrolli
   }
   RenderingState state(this);
   state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore,
                                   STATE_IGNORING_VIEWPORT_SCROLLING);
   SetRenderingState(state);
 }
 
 nsresult PresShell::SetResolutionImpl(float aResolution,
-                                      bool aScaleToResolution) {
+                                      bool aScaleToResolution,
+                                      nsAtom* aOrigin) {
   if (!(aResolution > 0.0)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   if (aResolution == mResolution.valueOr(0.0)) {
     MOZ_ASSERT(mResolution.isSome());
     return NS_OK;
   }
   RenderingState state(this);
   state.mResolution = Some(aResolution);
   SetRenderingState(state);
   mScaleToResolution = aScaleToResolution;
   if (mMobileViewportManager) {
     mMobileViewportManager->ResolutionUpdated();
   }
+  if (aOrigin != nsGkAtoms::apz) {
+    mResolutionUpdated = true;
+  }
 
   return NS_OK;
 }
 
 bool PresShell::ScaleToResolution() const { return mScaleToResolution; }
 
 float PresShell::GetCumulativeResolution() {
   float resolution = GetResolution();
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -190,22 +190,29 @@ class PresShell final : public nsIPresSh
 
   LayerManager* GetLayerManager() override;
 
   bool AsyncPanZoomEnabled() override;
 
   void SetIgnoreViewportScrolling(bool aIgnore) override;
 
   nsresult SetResolution(float aResolution) override {
-    return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
+    return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false,
+                             nsGkAtoms::other);
   }
-  nsresult SetResolutionAndScaleTo(float aResolution) override {
-    return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true);
+  nsresult SetResolutionAndScaleTo(float aResolution,
+                                   nsAtom* aOrigin) override {
+    return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true,
+                             aOrigin);
   }
   bool ScaleToResolution() const override;
+  bool IsResolutionUpdated() const override { return mResolutionUpdated; }
+  void SetResolutionUpdated(bool aUpdated) override {
+    mResolutionUpdated = aUpdated;
+  }
   float GetCumulativeResolution() override;
   float GetCumulativeNonRootScaleResolution() override;
   void SetRestoreResolution(float aResolution,
                             LayoutDeviceIntSize aDisplaySize) override;
 
   // nsIViewObserver interface
 
   void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
@@ -706,17 +713,18 @@ class PresShell final : public nsIPresSh
 
   nsRevocableEventPtr<nsRunnableMethod<PresShell>>
       mUpdateApproximateFrameVisibilityEvent;
 
   // A set of frames that were visible or could be visible soon at the time
   // that we last did an approximate frame visibility update.
   VisibleFrames mApproximatelyVisibleFrames;
 
-  nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
+  nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution,
+                             nsAtom* aOrigin);
 
   nsIContent* GetOverrideClickTarget(WidgetGUIEvent* aEvent, nsIFrame* aFrame);
 #ifdef DEBUG
   // The reflow root under which we're currently reflowing.  Null when
   // not in reflow.
   nsIFrame* mCurrentReflowRoot;
 #endif
 
@@ -823,16 +831,20 @@ class PresShell final : public nsIPresSh
   // Whether the widget has received a paint message yet.
   bool mHasReceivedPaintMessage : 1;
 
   bool mIsLastKeyDownCanceled : 1;
 
   // Whether we have ever handled a user input event
   bool mHasHandledUserInput : 1;
 
+  // Whether the most recent change to the pres shell resolution was
+  // originated by the main thread.
+  bool mResolutionUpdated : 1;
+
   // Whether we should dispatch keypress events even for non-printable keys
   // for keeping backward compatibility.
   bool mForceDispatchKeyPressEventsForNonPrintableKeys : 1;
   // Whether we should set keyCode or charCode value of keypress events whose
   // value is zero to the other value or not.  When this is set to true, we
   // should keep using legacy keyCode and charCode values (i.e., one of them
   // is always 0).
   bool mForceUseLegacyKeyCodeAndCharCodeValues : 1;
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1367,31 +1367,43 @@ class nsIPresShell : public nsStubDocume
    *
    * The resolution defaults to 1.0.
    */
   virtual nsresult SetResolution(float aResolution) = 0;
   float GetResolution() const { return mResolution.valueOr(1.0); }
   virtual float GetCumulativeResolution() = 0;
 
   /**
+   * Accessors for a flag that tracks whether the most recent change to
+   * the pres shell's resolution was originated by the main thread.
+   */
+  virtual bool IsResolutionUpdated() const = 0;
+  virtual void SetResolutionUpdated(bool aUpdated) = 0;
+
+  /**
    * Calculate the cumulative scale resolution from this document up to
    * but not including the root document.
    */
   virtual float GetCumulativeNonRootScaleResolution() = 0;
 
   /**
    * Was the current resolution set by the user or just default initialized?
    */
   bool IsResolutionSet() { return mResolution.isSome(); }
 
   /**
    * Similar to SetResolution() but also increases the scale of the content
    * by the same amount.
+   * |aOrigin| specifies who originated the resolution change. For changes
+   * sent by APZ, pass nsGkAtoms::apz. For changes sent by the main thread,
+   * use pass nsGkAtoms::other or nsGkAtoms::restore (similar to the |aOrigin|
+   * parameter of nsIScrollableFrame::ScrollToCSSPixels()).
    */
-  virtual nsresult SetResolutionAndScaleTo(float aResolution) = 0;
+  virtual nsresult SetResolutionAndScaleTo(float aResolution,
+                                           nsAtom* aOrigin) = 0;
 
   /**
    * Return whether we are scaling to the set resolution.
    * This is initially false; it's set to true by a call to
    * SetResolutionAndScaleTo(), and set to false by a call to SetResolution().
    */
   virtual bool ScaleToResolution() const = 0;
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8815,16 +8815,23 @@ static void MaybeReflowForInflationScree
 
   // Only the root scrollable frame for a given presShell should pick up
   // the presShell's resolution. All the other frames are 1.0.
   if (isRootScrollFrame) {
     metrics.SetPresShellResolution(presShell->GetResolution());
   } else {
     metrics.SetPresShellResolution(1.0f);
   }
+
+  if (presShell->IsResolutionUpdated()) {
+    metadata.SetResolutionUpdated(true);
+    // We only need to tell APZ about the resolution update once.
+    presShell->SetResolutionUpdated(false);
+  }
+
   // The cumulative resolution is the resolution at which the scroll frame's
   // content is actually rendered. It includes the pres shell resolutions of
   // all the pres shells from here up to the root, as well as any css-driven
   // resolution. We don't need to compute it as it's already stored in the
   // container parameters... except if we're in WebRender in which case we
   // don't have a aContainerParameters. In that case we're also not rasterizing
   // in Gecko anyway, so the only resolution we care about here is the presShell
   // resolution which we need to propagate to WebRender.
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -6140,17 +6140,18 @@ void ScrollFrameHelper::RestoreState(Pre
 
   // Resolution properties should only exist on root scroll frames.
   MOZ_ASSERT(mIsRoot ||
              (!aState->scaleToResolution() && aState->resolution() == 1.0));
 
   if (mIsRoot) {
     nsIPresShell* presShell = mOuter->PresShell();
     if (aState->scaleToResolution()) {
-      presShell->SetResolutionAndScaleTo(aState->resolution());
+      presShell->SetResolutionAndScaleTo(aState->resolution(),
+                                         nsGkAtoms::restore);
     } else {
       presShell->SetResolution(aState->resolution());
     }
   }
 }
 
 void ScrollFrameHelper::PostScrolledAreaEvent() {
   if (mScrolledAreaEvent.IsPending()) {