Bug 1300864 - Disable paint skipping for scroll frames that contain out-of-flow content inside a CSS filter. r=mstange draft
authorBotond Ballo <botond@mozilla.com>
Mon, 27 Nov 2017 16:45:29 -0500
changeset 705278 2c38372e452cf6b78b4906b42e4b9e93a1762d26
parent 705277 b7731b809c5c52501959355539ed34c0057f8c87
child 742325 4a89ba5aac949679c8e5e7836193a0328dbcefcc
push id91431
push userbballo@mozilla.com
push dateWed, 29 Nov 2017 20:26:41 +0000
reviewersmstange
bugs1300864
milestone59.0a1
Bug 1300864 - Disable paint skipping for scroll frames that contain out-of-flow content inside a CSS filter. r=mstange MozReview-Commit-ID: 5TwC75wzOsm
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2931,16 +2931,18 @@ nsIFrame::BuildDisplayListForStackingCon
 
   nsDisplayListCollection set(aBuilder);
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
     nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex
       perspectiveIndex(aBuilder, this);
+    nsDisplayListBuilder::AutoFilterASRSetter
+      filterASRSetter(aBuilder, usingFilter);
 
     CheckForApzAwareEventHandlers(aBuilder, this);
 
     Maybe<nsRect> contentClip =
       GetClipPropClipRect(disp, effects, GetSize());
 
     if (usingMask) {
       contentClip = IntersectMaybeRects(contentClip, clipForMask);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1296,16 +1296,22 @@ ScrollFrameHelper::SetZoomableByAPZ(bool
   if (mZoomableByAPZ != aZoomable) {
     // We might be changing the result of WantAsyncScroll() so schedule a
     // paint to make sure we pick up the result of that change.
     mZoomableByAPZ = aZoomable;
     mOuter->SchedulePaint();
   }
 }
 
+void
+ScrollFrameHelper::SetHasOutOfFlowContentInsideFilter()
+{
+  mHasOutOfFlowContentInsideFilter = true;
+}
+
 bool
 ScrollFrameHelper::WantAsyncScroll() const
 {
   // If zooming is allowed, and this is a frame that's allowed to zoom, then
   // we want it to be async-scrollable or zooming will not be permitted.
   if (mZoomableByAPZ) {
     return true;
   }
@@ -2070,16 +2076,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
   , mWillBuildScrollableLayer(false)
   , mIsScrollParent(false)
   , mIsScrollableLayerInRootContainer(false)
   , mHasBeenScrolled(false)
   , mIgnoreMomentumScroll(false)
   , mTransformingByAPZ(false)
   , mScrollableByAPZ(false)
   , mZoomableByAPZ(false)
+  , mHasOutOfFlowContentInsideFilter(false)
   , mSuppressScrollbarRepaints(false)
   , mVelocityQueue(aOuter->PresContext())
 {
   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
     mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
   }
 
   EnsureFrameVisPrefsCached();
@@ -2874,23 +2881,25 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     // that needs to be painted. So even if the final tile-aligned displayport
     // is the same, we force a repaint for these elements. Bug 1254260 tracks
     // fixing this properly.
     nsRect displayPort;
     bool usingDisplayPort =
       nsLayoutUtils::GetHighResolutionDisplayPort(content, &displayPort);
     displayPort.MoveBy(-mScrolledFrame->GetPosition());
 
-    PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d bglocal %d\n",
+    PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins"
+        "%d perspective %d bglocal %d filter %d\n",
         Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
         usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
         mScrollableByAPZ, HasPluginFrames(), HasPerspective(),
-        HasBgAttachmentLocal());
+        HasBgAttachmentLocal(), mHasOutOfFlowContentInsideFilter);
     if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) &&
-        !HasPerspective() && !HasBgAttachmentLocal()) {
+        !HasPerspective() && !HasBgAttachmentLocal() &&
+        !mHasOutOfFlowContentInsideFilter) {
       bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
       bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
       if (!apzDisabled && !HasPluginFrames()) {
         if (LastScrollOrigin() == nsGkAtoms::apz) {
           schedulePaint = false;
           PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
         } else if (mScrollableByAPZ) {
           nsIWidget* widget = presContext->GetNearestWidget();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -421,16 +421,17 @@ public:
       mOuter->SchedulePaint();
     }
   }
   bool IsTransformingByAPZ() const {
     return mTransformingByAPZ;
   }
   void SetScrollableByAPZ(bool aScrollable);
   void SetZoomableByAPZ(bool aZoomable);
+  void SetHasOutOfFlowContentInsideFilter();
 
   bool UsesContainerScrolling() const;
 
   ScrollSnapInfo GetScrollSnapInfo() const;
 
   bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                              nsRect* aVisibleRect,
                              nsRect* aDirtyRect,
@@ -627,16 +628,20 @@ public:
 
   // True if APZ can scroll this frame asynchronously (i.e. it has an APZC
   // set up for this frame and it's not a scrollinfo layer).
   bool mScrollableByAPZ:1;
 
   // True if the APZ is allowed to zoom this scrollframe.
   bool mZoomableByAPZ:1;
 
+  // True if the scroll frame contains out-of-flow content and is inside
+  // a CSS filter.
+  bool mHasOutOfFlowContentInsideFilter:1;
+
   // True if we don't want the scrollbar to repaint itself right now.
   bool mSuppressScrollbarRepaints:1;
 
   mozilla::layout::ScrollVelocityQueue mVelocityQueue;
 
 protected:
   class AutoScrollbarRepaintSuppression;
   friend class AutoScrollbarRepaintSuppression;
@@ -1057,16 +1062,19 @@ public:
     return mHelper.IsTransformingByAPZ();
   }
   void SetScrollableByAPZ(bool aScrollable) override {
     mHelper.SetScrollableByAPZ(aScrollable);
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  void SetHasOutOfFlowContentInsideFilter() override {
+    mHelper.SetHasOutOfFlowContentInsideFilter();
+  }
 
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
   virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
     return mHelper.DragScroll(aEvent);
   }
@@ -1492,16 +1500,19 @@ public:
     return mHelper.IsTransformingByAPZ();
   }
   void SetScrollableByAPZ(bool aScrollable) override {
     mHelper.SetScrollableByAPZ(aScrollable);
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  void SetHasOutOfFlowContentInsideFilter() override {
+    mHelper.SetHasOutOfFlowContentInsideFilter();
+  }
   virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                                      nsRect* aVisibleRect,
                                      nsRect* aDirtyRect,
                                      bool aSetBase) override {
     return mHelper.DecideScrollableLayer(aBuilder, aVisibleRect, aDirtyRect, aSetBase);
   }
   virtual void NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort) override {
     mHelper.NotifyApproximateFrameVisibilityUpdate(aIgnoreDisplayPort);
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -444,16 +444,23 @@ public:
   virtual void SetScrollableByAPZ(bool aScrollable) = 0;
 
   /**
    * Notify this scroll frame that it can be zoomed by APZ.
    */
   virtual void SetZoomableByAPZ(bool aZoomable) = 0;
 
   /**
+   * Mark this scroll frame as having out-of-flow content inside a CSS filter.
+   * Such content will move incorrectly during async-scrolling; to mitigate
+   * this, paint skipping is disabled for such scroll frames.
+   */
+  virtual void SetHasOutOfFlowContentInsideFilter() = 0;
+
+  /**
    * Whether or not this frame uses containerful scrolling.
    */
   virtual bool UsesContainerScrolling() const = 0;
 
   /**
    * Determine if we should build a scrollable layer for this scroll frame and
    * return the result. It will also record this result on the scroll frame.
    * Pass the visible rect in aVisibleRect. On return it will be set to the
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -914,16 +914,27 @@ nsDisplayListBuilder::AutoCurrentActiveS
   const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
     mContentClipASR, aActiveScrolledRoot);
 
   // mCurrentContainerASR is adjusted so that it's still an ancestor of
   // finiteBoundsASR.
   mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
     mBuilder->mCurrentContainerASR, finiteBoundsASR);
 
+  // If we are entering out-of-flow content inside a CSS filter, mark
+  // scroll frames wrt. which the content is fixed as containing such content.
+  if (mBuilder->mFilterASR &&
+      ActiveScrolledRoot::IsAncestor(aActiveScrolledRoot, mBuilder->mFilterASR)) {
+    for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
+         asr && asr != aActiveScrolledRoot;
+         asr = asr->mParent) {
+      asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
+    }
+  }
+
   mUsed = true;
 }
 
 void
 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::InsertScrollFrame(nsIScrollableFrame* aScrollableFrame)
 {
   MOZ_ASSERT(!mUsed);
   size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
@@ -965,16 +976,17 @@ nsDisplayListBuilder::nsDisplayListBuild
       mScrollInfoItemsForHoisting(nullptr),
       mActiveScrolledRootForRootScrollframe(nullptr),
       mMode(aMode),
       mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarFlags(nsDisplayOwnLayerFlags::eNone),
       mPerspectiveItemIndex(0),
       mSVGEffectsBuildingDepth(0),
+      mFilterASR(nullptr),
       mContainsBlendMode(false),
       mIsBuildingScrollbar(false),
       mCurrentScrollbarWillHaveLayer(false),
       mBuildCaret(aBuildCaret),
       mRetainingDisplayList(aRetainingDisplayList),
       mPartialUpdate(false),
       mIgnoreSuppression(false),
       mIsAtRootOfPseudoStackingContext(false),
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1120,16 +1120,38 @@ public:
     ~AutoInTransformSetter() {
       mBuilder->mInTransform = mOldValue;
     }
   private:
     nsDisplayListBuilder* mBuilder;
     bool                  mOldValue;
   };
 
+  /**
+   * A helper class to temporarily set the value of mFilterASR.
+   */
+  class AutoFilterASRSetter;
+  friend class AutoFilterASRSetter;
+  class AutoFilterASRSetter {
+  public:
+    AutoFilterASRSetter(nsDisplayListBuilder* aBuilder, bool aUsingFilter)
+      : mBuilder(aBuilder), mOldValue(aBuilder->mFilterASR)
+    {
+      if (!aBuilder->mFilterASR && aUsingFilter) {
+        aBuilder->mFilterASR = aBuilder->CurrentActiveScrolledRoot();
+      }
+    }
+    ~AutoFilterASRSetter() {
+      mBuilder->mFilterASR = mOldValue;
+    }
+  private:
+    nsDisplayListBuilder* mBuilder;
+    const ActiveScrolledRoot* mOldValue;
+  };
+
   class AutoSaveRestorePerspectiveIndex;
   friend class AutoSaveRestorePerspectiveIndex;
   class AutoSaveRestorePerspectiveIndex {
   public:
     AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
       : mBuilder(nullptr)
     {
       if (aFrame->ChildrenHavePerspective()) {
@@ -1766,16 +1788,19 @@ private:
   const ActiveScrolledRoot*      mActiveScrolledRootForRootScrollframe;
   nsDisplayListBuilderMode       mMode;
   ViewID                         mCurrentScrollParentId;
   ViewID                         mCurrentScrollbarTarget;
   nsDisplayOwnLayerFlags         mCurrentScrollbarFlags;
   Preserves3DContext             mPreserves3DCtx;
   uint32_t                       mPerspectiveItemIndex;
   int32_t                        mSVGEffectsBuildingDepth;
+  // When we are inside a filter, the current ASR at the time we entered the
+  // filter. Otherwise nullptr.
+  const ActiveScrolledRoot*      mFilterASR;
   bool                           mContainsBlendMode;
   bool                           mIsBuildingScrollbar;
   bool                           mCurrentScrollbarWillHaveLayer;
   bool                           mBuildCaret;
   bool                           mRetainingDisplayList;
   bool                           mPartialUpdate;
   bool                           mIgnoreSuppression;
   bool                           mIsAtRootOfPseudoStackingContext;