Bug 1507279 - Add a mechanism for the main thread to set a visual viewport offset. r=kats
authorBotond Ballo <botond@mozilla.com>
Thu, 10 Jan 2019 20:59:13 +0000
changeset 453347 bea2de966d140c1a43baca40ee936421257f0b65
parent 453346 b4bda4351a9cb9af39368b83766a3497298a45ad
child 453348 47c259e23cfe3de3786766c67d41023db6a7ea85
push id35352
push userdvarga@mozilla.com
push dateFri, 11 Jan 2019 04:12:48 +0000
treeherdermozilla-central@65326bd78f83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1507279
milestone66.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 1507279 - Add a mechanism for the main thread to set a visual viewport offset. r=kats Differential Revision: https://phabricator.services.mozilla.com/D16141
gfx/layers/FrameMetrics.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/ipc/LayersMessageUtils.h
layout/base/PresShell.cpp
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -99,16 +99,18 @@ struct FrameMetrics {
         mSmoothScrollOffset(0, 0),
         mRootCompositionSize(0, 0),
         mDisplayPortMargins(0, 0, 0, 0),
         mPresShellId(-1),
         mLayoutViewport(0, 0, 0, 0),
         mExtraResolution(),
         mPaintRequestTime(),
         mScrollUpdateType(eNone),
+        mVisualViewportOffset(0, 0),
+        mVisualScrollUpdateType(eNone),
         mIsRootContent(false),
         mIsRelative(false),
         mDoSmoothScroll(false),
         mIsScrollInfoLayer(false) {}
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const {
@@ -128,16 +130,18 @@ struct FrameMetrics {
            mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
            mRootCompositionSize == aOther.mRootCompositionSize &&
            mDisplayPortMargins == aOther.mDisplayPortMargins &&
            mPresShellId == aOther.mPresShellId &&
            mLayoutViewport.IsEqualEdges(aOther.mLayoutViewport) &&
            mExtraResolution == aOther.mExtraResolution &&
            mPaintRequestTime == aOther.mPaintRequestTime &&
            mScrollUpdateType == aOther.mScrollUpdateType &&
+           mVisualViewportOffset == aOther.mVisualViewportOffset &&
+           mVisualScrollUpdateType == aOther.mVisualScrollUpdateType &&
            mIsRootContent == aOther.mIsRootContent &&
            mIsRelative == aOther.mIsRelative &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
   }
 
   bool operator!=(const FrameMetrics& aOther) const {
     return !operator==(aOther);
@@ -475,16 +479,30 @@ struct FrameMetrics {
   }
   const TimeStamp& GetPaintRequestTime() const { return mPaintRequestTime; }
 
   void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
     mIsScrollInfoLayer = aIsScrollInfoLayer;
   }
   bool IsScrollInfoLayer() const { return mIsScrollInfoLayer; }
 
+  void SetVisualViewportOffset(const CSSPoint& aVisualViewportOffset) {
+    mVisualViewportOffset = aVisualViewportOffset;
+  }
+  const CSSPoint& GetVisualViewportOffset() const {
+    return mVisualViewportOffset;
+  }
+
+  void SetVisualScrollUpdateType(ScrollOffsetUpdateType aUpdateType) {
+    mVisualScrollUpdateType = aUpdateType;
+  }
+  ScrollOffsetUpdateType GetVisualScrollUpdateType() const {
+    return mVisualScrollUpdateType;
+  }
+
   // Determine if the visual viewport is outside of the layout viewport and
   // adjust the x,y-offset in mLayoutViewport accordingly. This is necessary to
   // allow APZ to async-scroll the layout viewport.
   //
   // This is a no-op if mIsRootContent is false.
   void RecalculateLayoutViewportOffset();
 
   // Helper function for RecalculateViewportOffset(). Exposed so that
@@ -635,16 +653,28 @@ struct FrameMetrics {
 
   // The time at which the APZC last requested a repaint for this scroll frame.
   TimeStamp mPaintRequestTime;
 
   // Whether mScrollOffset was updated by something other than the APZ code, and
   // if the APZC receiving this metrics should update its local copy.
   ScrollOffsetUpdateType mScrollUpdateType;
 
+  // These fields are used when the main thread wants to set a visual viewport
+  // offset that's distinct from the layout viewport offset.
+  // In this case, mVisualScrollUpdateType is set to eMainThread, and
+  // mVisualViewportOffset is set to desired visual viewport offset (relative
+  // to the document, like mScrollOffset).
+  // TODO: Get rid of mVisualViewportOffset: between mViewport.TopLeft() and
+  //       mScrollOffset, we have enough storage for the two scroll offsets.
+  //       However, to avoid confusion, that first requires refactoring
+  //       existing to consistently use the two fields for those two purposes.
+  CSSPoint mVisualViewportOffset;
+  ScrollOffsetUpdateType mVisualScrollUpdateType;
+
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRootContent : 1;
 
   // When mIsRelative, the scroll offset was updated using a relative API,
   // such as `ScrollBy`, and can combined with an async scroll.
   bool mIsRelative : 1;
 
   // When mDoSmoothScroll, the scroll offset should be animated to
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -4291,17 +4291,17 @@ void AsyncPanZoomController::NotifyLayer
                           Metrics().GetCompositionBounds().Width()) &&
       FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(),
                           Metrics().GetCompositionBounds().Height());
 #if defined(MOZ_WIDGET_ANDROID)
   entertainViewportUpdates = true;
 #endif
   if (entertainViewportUpdates) {
     if (Metrics().GetLayoutViewport().Size() !=
-            aLayerMetrics.GetLayoutViewport().Size()) {
+        aLayerMetrics.GetLayoutViewport().Size()) {
       needContentRepaint = true;
       viewportUpdated = true;
     }
     if (viewportUpdated || scrollOffsetUpdated) {
       Metrics().SetLayoutViewport(aLayerMetrics.GetLayoutViewport());
     }
   }
 
@@ -4495,16 +4495,43 @@ void AsyncPanZoomController::NotifyLayer
       Metrics().ApplySmoothScrollUpdateFrom(aLayerMetrics);
     }
     needContentRepaint = true;
     mExpectedGeckoMetrics = aLayerMetrics;
 
     SmoothScrollTo(Metrics().GetSmoothScrollOffset());
   }
 
+  // If the main thread asked us to scroll the visual viewport to a particular
+  // location, do so. This is different from a layout viewport offset update
+  // in that the layout viewport offset is limited to the layout scroll range
+  // (this will be enforced by the main thread once bug 1516056 is fixed),
+  // while the visual viewport offset is not. The main thread can also ask
+  // us to scroll both the layout and visual viewports to distinct (but
+  // compatible) locations.
+  FrameMetrics::ScrollOffsetUpdateType visualUpdateType =
+      aLayerMetrics.GetVisualScrollUpdateType();
+  MOZ_ASSERT(visualUpdateType == FrameMetrics::eNone ||
+             visualUpdateType == FrameMetrics::eMainThread);
+  if (visualUpdateType == FrameMetrics::eMainThread) {
+    Metrics().ClampAndSetScrollOffset(aLayerMetrics.GetVisualViewportOffset());
+
+    // The rest of this branch largely follows the code in the
+    // |if (scrollOffsetUpdated)| branch above.
+    Metrics().RecalculateLayoutViewportOffset();
+    mCompositedLayoutViewport = Metrics().GetLayoutViewport();
+    mCompositedScrollOffset = Metrics().GetScrollOffset();
+    mExpectedGeckoMetrics = aLayerMetrics;
+    if (!mAnimation || !mAnimation->HandleScrollOffsetUpdate(Nothing())) {
+      CancelAnimation();
+    }
+    needContentRepaint = true;
+    ScheduleComposite();
+  }
+
   if (viewportUpdated) {
     // While we want to accept the main thread's layout viewport _size_,
     // its position may be out of date in light of async scrolling, to
     // adjust it if necessary to make sure it continues to enclose the
     // visual viewport.
     // Note: it's important to do this _after_ we've accepted any
     // updated composition bounds.
     Metrics().RecalculateLayoutViewportOffset();
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -190,16 +190,18 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mSmoothScrollOffset);
     WriteParam(aMsg, aParam.mRootCompositionSize);
     WriteParam(aMsg, aParam.mDisplayPortMargins);
     WriteParam(aMsg, aParam.mPresShellId);
     WriteParam(aMsg, aParam.mLayoutViewport);
     WriteParam(aMsg, aParam.mExtraResolution);
     WriteParam(aMsg, aParam.mPaintRequestTime);
     WriteParam(aMsg, aParam.mScrollUpdateType);
+    WriteParam(aMsg, aParam.mVisualViewportOffset);
+    WriteParam(aMsg, aParam.mVisualScrollUpdateType);
     WriteParam(aMsg, aParam.mIsRootContent);
     WriteParam(aMsg, aParam.mIsRelative);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mIsScrollInfoLayer);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
@@ -219,16 +221,18 @@ struct ParamTraits<mozilla::layers::Fram
         ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
         ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
         ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
         ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
         ReadParam(aMsg, aIter, &aResult->mLayoutViewport) &&
         ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
         ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) &&
         ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) &&
+        ReadParam(aMsg, aIter, &aResult->mVisualViewportOffset) &&
+        ReadParam(aMsg, aIter, &aResult->mVisualScrollUpdateType) &&
         ReadBoolForBitfield(aMsg, aIter, aResult,
                             &paramType::SetIsRootContent) &&
         ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsRelative) &&
         ReadBoolForBitfield(aMsg, aIter, aResult,
                             &paramType::SetDoSmoothScroll) &&
         ReadBoolForBitfield(aMsg, aIter, aResult,
                             &paramType::SetIsScrollInfoLayer));
   }
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6788,28 +6788,28 @@ nsresult PresShell::HandleEvent(nsIFrame
         mDelayedEvents.AppendElement(std::move(event));
       }
 
       // If there is a suppressed event listener associated with the document,
       // notify it about the suppressed mouse event. This allows devtools
       // features to continue receiving mouse events even when the devtools
       // debugger has paused execution in a page.
       RefPtr<EventListener> suppressedListener =
-        frame->PresContext()->Document()->GetSuppressedEventListener();
+          frame->PresContext()->Document()->GetSuppressedEventListener();
       if (suppressedListener &&
           aEvent->AsMouseEvent()->mReason != WidgetMouseEvent::eSynthesized) {
         nsCOMPtr<nsIContent> targetContent;
         frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
         if (targetContent) {
           aEvent->mTarget = targetContent;
         }
 
         nsCOMPtr<EventTarget> et = aEvent->mTarget;
         RefPtr<Event> event = EventDispatcher::CreateEvent(
-                et, frame->PresContext(), aEvent, EmptyString());
+            et, frame->PresContext(), aEvent, EmptyString());
 
         suppressedListener->HandleEvent(*event);
       }
 
       return NS_OK;
     }
 
     if (!frame) {
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1649,16 +1649,30 @@ class nsIPresShell : public nsStubDocume
 
   void SetVisualViewportOffset(const nsPoint& aScrollOffset,
                                const nsPoint& aPrevLayoutScrollPos);
 
   nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; }
 
   nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const;
 
+  // Ask APZ in the next transaction to scroll to the given visual viewport 
+  // offset (relative to the document).
+  // Use this sparingly, as it will clobber JS-driven scrolling that happens
+  // in the same frame. This is mostly intended to be used in special
+  // situations like "first paint" or session restore.
+  // Please request APZ review if adding a new call site.
+  void SetPendingVisualViewportOffset(
+      const mozilla::Maybe<nsPoint>& aPendingVisualViewportOffset) {
+    mPendingVisualViewportOffset = aPendingVisualViewportOffset;
+  }
+  const mozilla::Maybe<nsPoint>& GetPendingVisualViewportOffset() const {
+    return mPendingVisualViewportOffset;
+  }
+
   nsPoint GetLayoutViewportOffset() const;
 
   virtual void WindowSizeMoveDone() = 0;
   virtual void SysColorChanged() = 0;
   virtual void ThemeChanged() = 0;
   virtual void BackingScaleFactorChanged() = 0;
 
   /**
@@ -1730,16 +1744,21 @@ class nsIPresShell : public nsStubDocume
 
   // Count of the number of times this presshell has been painted to a window.
   uint64_t mPaintCount;
 
   nsSize mVisualViewportSize;
 
   nsPoint mVisualViewportOffset;
 
+  // A pending visual viewport offset that we will ask APZ to scroll to
+  // during the next transaction. Cleared when we send the transaction.
+  // Only applicable to the RCD pres shell.
+  mozilla::Maybe<nsPoint> mPendingVisualViewportOffset;
+
   // A list of stack weak frames. This is a pointer to the last item in the
   // list.
   AutoWeakFrame* mAutoWeakFrames;
 
   // A hash table of heap allocated weak frames.
   nsTHashtable<nsPtrHashKey<WeakFrame>> mWeakFrames;
 
   class DirtyRootsList {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8702,16 +8702,25 @@ static void MaybeReflowForInflationScree
   if (scrollableFrame) {
     CSSPoint scrollPosition =
         CSSPoint::FromAppUnits(scrollableFrame->GetScrollPosition());
     CSSPoint apzScrollPosition =
         CSSPoint::FromAppUnits(scrollableFrame->GetApzScrollPosition());
     metrics.SetScrollOffset(scrollPosition);
     metrics.SetBaseScrollOffset(apzScrollPosition);
 
+    if (aIsRootContent) {
+      if (const Maybe<nsPoint>& visualOffset =
+              presShell->GetPendingVisualViewportOffset()) {
+        metrics.SetVisualViewportOffset(CSSPoint::FromAppUnits(*visualOffset));
+        metrics.SetVisualScrollUpdateType(FrameMetrics::eMainThread);
+        presShell->SetPendingVisualViewportOffset(Nothing());
+      }
+    }
+
     CSSRect viewport = metrics.GetLayoutViewport();
     viewport.MoveTo(scrollPosition);
     metrics.SetLayoutViewport(viewport);
 
     nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
     metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
 
     // If the frame was scrolled since the last layers update, and by something