Bug 1465616 - Use layout viewport transformations to async-adjust fixed position elements. r?botond draft
authorKashav Madan <kshvmdn@gmail.com>
Fri, 20 Jul 2018 17:45:48 -0400
changeset 830109 84137acdf43c38ad91ccbac36ae2c034cd6dfb61
parent 830108 96b62222a107300fb80e435e813b8c72f8c0ad07
child 830110 ff297bc935576a9fa7f4ee3a9f23803d96d74046
push id118813
push userbmo:kshvmdn@gmail.com
push dateSun, 19 Aug 2018 00:42:00 +0000
reviewersbotond
bugs1465616
milestone63.0a1
Bug 1465616 - Use layout viewport transformations to async-adjust fixed position elements. r?botond This results in fixed position elements being attached to the layout viewport when being async-scrolled by APZ (when the layout viewport is larger than the visual viewport). MozReview-Commit-ID: 2YYIDnTWgVn
gfx/layers/apz/public/APZSampler.h
gfx/layers/apz/src/APZSampler.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/composite/AsyncCompositionManager.cpp
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -80,16 +80,17 @@ public:
       const LayerMetricsWrapper& aContent,
       const ScrollbarData& aThumbData,
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
   CSSRect GetCurrentAsyncLayoutViewport(const LayerMetricsWrapper& aLayer);
   ParentLayerPoint GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer);
   AsyncTransform GetCurrentAsyncTransform(const LayerMetricsWrapper& aLayer);
+  AsyncTransform GetCurrentAsyncTransformForFixedAdjustment(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetOverscrollTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(const LayerMetricsWrapper& aLayer);
 
   void MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer);
   bool HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer);
 
   /**
    * Return a new AutoApplyAsyncTestAttributes instance. This should be called
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -176,16 +176,26 @@ APZSampler::GetCurrentAsyncTransform(con
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
 
   MOZ_ASSERT(aLayer.GetApzc());
   return aLayer.GetApzc()->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
 }
 
+AsyncTransform
+APZSampler::GetCurrentAsyncTransformForFixedAdjustment(const LayerMetricsWrapper& aLayer)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  AssertOnSamplerThread();
+
+  MOZ_ASSERT(aLayer.GetApzc());
+  return aLayer.GetApzc()->GetCurrentAsyncTransformForFixedAdjustment(AsyncPanZoomController::eForCompositing);
+}
+
 AsyncTransformComponentMatrix
 APZSampler::GetOverscrollTransform(const LayerMetricsWrapper& aLayer)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
 
   MOZ_ASSERT(aLayer.GetApzc());
   return aLayer.GetApzc()->GetOverscrollTransform(AsyncPanZoomController::eForCompositing);
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3809,16 +3809,72 @@ AsyncPanZoomController::GetCurrentAsyncS
   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
     return mLastContentPaintMetrics.GetScrollOffset();
   }
 
   return GetEffectiveScrollOffset(aMode);
 }
 
 AsyncTransform
+AsyncPanZoomController::GetCurrentAsyncViewportTransform(AsyncTransformConsumer aMode) const
+{
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
+
+  if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
+    return AsyncTransform();
+  }
+
+  CSSRect lastPaintViewport;
+  if (mLastContentPaintMetrics.IsScrollable()) {
+    lastPaintViewport = mLastContentPaintMetrics.GetViewport();
+  }
+
+  CSSRect currentViewport = GetEffectiveLayoutViewport(aMode);
+  CSSPoint currentViewportOffset = currentViewport.TopLeft();
+
+  // If checkerboarding has been disallowed, clamp the scroll position to stay
+  // within rendered content.
+  //
+  // TODO: This calculation is slightly inaccurate and this function will
+  // likely report an inconsistent transformation if apz.allow_checkerboarding
+  // is disabled. The correct calculation requires the following:
+  //
+  //   * Calculating a clamped visual scroll offset, like in
+  //     AsyncPanZoomController::GetCurrentAsyncTransform.
+  //   * Calling FrameMetrics::RecalculateViewportOffset to compute the layout
+  //     viewport offset corresponding to that visual offset and using that as
+  //     the clamped layout viewport offset.
+  //
+  if (!gfxPrefs::APZAllowCheckerboarding() &&
+      !mLastContentPaintMetrics.GetDisplayPort().IsEmpty()) {
+    CSSSize viewportSize = currentViewport.Size();
+    CSSPoint maxViewportOffset = lastPaintViewport.TopLeft() +
+      CSSPoint(mLastContentPaintMetrics.GetDisplayPort().XMost() - viewportSize.width,
+               mLastContentPaintMetrics.GetDisplayPort().YMost() - viewportSize.height);
+    CSSPoint minViewportOffset = lastPaintViewport.TopLeft() +
+      mLastContentPaintMetrics.GetDisplayPort().TopLeft();
+
+    if (minViewportOffset.x < maxViewportOffset.x) {
+      currentViewportOffset.x = clamped(currentViewportOffset.x, minViewportOffset.x, maxViewportOffset.x);
+    }
+    if (minViewportOffset.y < maxViewportOffset.y) {
+      currentViewportOffset.y = clamped(currentViewportOffset.y, minViewportOffset.y, maxViewportOffset.y);
+    }
+  }
+
+  CSSToParentLayerScale2D effectiveZoom = GetEffectiveZoom(aMode);
+  ParentLayerPoint translation =
+    (currentViewportOffset - lastPaintViewport.TopLeft()) * effectiveZoom;
+  LayerToParentLayerScale compositedAsyncZoom =
+    (effectiveZoom / Metrics().LayersPixelsPerCSSPixel()).ToScaleFactor();
+
+  return AsyncTransform(compositedAsyncZoom, -translation);
+}
+
+AsyncTransform
 AsyncPanZoomController::GetCurrentAsyncTransform(AsyncTransformConsumer aMode) const
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
     return AsyncTransform();
   }
 
@@ -3851,16 +3907,32 @@ AsyncPanZoomController::GetCurrentAsyncT
   ParentLayerPoint translation =
     (currentScrollOffset - lastPaintScrollOffset) * effectiveZoom;
   LayerToParentLayerScale compositedAsyncZoom =
     (effectiveZoom / Metrics().LayersPixelsPerCSSPixel()).ToScaleFactor();
 
   return AsyncTransform(compositedAsyncZoom, -translation);
 }
 
+AsyncTransform
+AsyncPanZoomController::GetCurrentAsyncTransformForFixedAdjustment(AsyncTransformConsumer aMode) const
+{
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
+
+  // Use the layout viewport to adjust fixed position elements if and only if
+  // it's larger than the visual viewport (assuming we're scrolling the RCD-RSF
+  // with apz.allow_zooming enabled).
+  return (
+      gfxPrefs::APZAllowZooming() &&
+      Metrics().IsRootContent() &&
+      Metrics().GetVisualViewport().Size() <= Metrics().GetViewport().Size()
+  ) ? GetCurrentAsyncViewportTransform(aMode)
+    : GetCurrentAsyncTransform(aMode);
+}
+
 AsyncTransformComponentMatrix
 AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncTransformConsumer aMode) const
 {
   return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode))
        * GetOverscrollTransform(aMode);
 }
 
 CSSRect
@@ -3889,17 +3961,18 @@ AsyncPanZoomController::GetEffectiveZoom
   }
   return Metrics().GetZoom();
 }
 
 bool
 AsyncPanZoomController::SampleCompositedAsyncTransform()
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
-  if (mCompositedScrollOffset != Metrics().GetScrollOffset() ||
+  if (!mCompositedLayoutViewport.IsEqualEdges(Metrics().GetViewport()) ||
+      mCompositedScrollOffset != Metrics().GetScrollOffset() ||
       mCompositedZoom != Metrics().GetZoom()) {
     mCompositedLayoutViewport = Metrics().GetViewport();
     mCompositedScrollOffset = Metrics().GetScrollOffset();
     mCompositedZoom = Metrics().GetZoom();
     return true;
   }
   return false;
 }
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1043,24 +1043,39 @@ public:
 
   /**
    * Return a visual effect that reflects this apzc's
    * overscrolled state, if any.
    */
   AsyncTransformComponentMatrix GetOverscrollTransform(AsyncTransformConsumer aMode) const;
 
   /**
+   * Returns the incremental transformation corresponding to the async
+   * panning/zooming of the layout viewport (unlike GetCurrentAsyncTransform,
+   * which deals with async movement of the visual viewport). That is, when
+   * this transform is multiplied with the layer's existing transform, it will
+   * make the layer appear with the desired pan/zoom amount.
+   */
+  AsyncTransform GetCurrentAsyncViewportTransform(AsyncTransformConsumer aMode) const;
+
+  /**
    * Returns the incremental transformation corresponding to the async pan/zoom
    * in progress. That is, when this transform is multiplied with the layer's
    * existing transform, it will make the layer appear with the desired pan/zoom
    * amount.
    */
   AsyncTransform GetCurrentAsyncTransform(AsyncTransformConsumer aMode) const;
 
   /**
+   * Returns the incremental transformation corresponding to the async
+   * panning/zooming of the larger of the visual or layout viewport.
+   */
+  AsyncTransform GetCurrentAsyncTransformForFixedAdjustment(AsyncTransformConsumer aMode) const;
+
+  /**
    * Returns the same transform as GetCurrentAsyncTransform(), but includes
    * any transform due to axis over-scroll.
    */
   AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncTransformConsumer aMode) const;
 
 private:
   /**
    * Samples the composited async transform, making the result of
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -1063,21 +1063,21 @@ AsyncCompositionManager::ApplyAsyncConte
 
             // For the purpose of aligning fixed and sticky layers, we disregard
             // the overscroll transform as well as any OMTA transform when computing the
             // 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
             // and OMTA transforms are not unapplied, and therefore that the visual
             // effects apply to fixed and sticky layers. We do this by using
             // GetTransform() as the base transform rather than GetLocalTransform(),
             // which would include those factors.
-            LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
-                layer->GetTransformTyped()
-              * CompleteAsyncTransform(
-                  AdjustForClip(asyncTransformWithoutOverscroll, layer));
-
+            AsyncTransform asyncTransformForFixedAdjustment
+              = sampler->GetCurrentAsyncTransformForFixedAdjustment(wrapper);
+            LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta
+              = layer->GetTransformTyped()
+              * CompleteAsyncTransform(AdjustForClip(asyncTransformForFixedAdjustment, layer));
             AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(), oldTransform,
                                       transformWithoutOverscrollOrOmta, fixedLayerMargins,
                                       &clipPartsCache);
 
             // Combine the local clip with the ancestor scrollframe clip. This is not
             // included in the async transform above, since the ancestor clip should not
             // move with this APZC.
             if (scrollMetadata.HasScrollClip()) {