Bug 1284586 - Disable paint-skipping for scrollframes that we detect as having a CSS-clipped descendant. r=mstange, a=gchang
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 13 Jul 2016 16:05:53 -0400
changeset 341977 1c72f075dd79e2f4013909681c7541d4e2275345
parent 341976 4f2339c77cfcd694eed4d7261491d5791ac735b6
child 341978 9538d7587ec9ccb4b80ff33bf63b4699eb63298a
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, gchang
bugs1284586
milestone49.0a2
Bug 1284586 - Disable paint-skipping for scrollframes that we detect as having a CSS-clipped descendant. r=mstange, a=gchang MozReview-Commit-ID: AvjokFZMwdd
layout/base/DisplayListClipState.cpp
layout/base/DisplayListClipState.h
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
layout/generic/nsViewportFrame.cpp
--- a/layout/base/DisplayListClipState.cpp
+++ b/layout/base/DisplayListClipState.cpp
@@ -31,29 +31,48 @@ DisplayListClipState::GetCurrentCombined
   } else {
     mCurrentCombinedClip =
       aBuilder->AllocateDisplayItemClip(*mClipContainingBlockDescendants);
   }
   return mCurrentCombinedClip;
 }
 
 void
+DisplayListClipState::SetScrollClipForContainingBlockDescendants(
+    nsDisplayListBuilder* aBuilder,
+    const DisplayItemScrollClip* aScrollClip)
+{
+  if (aBuilder->IsPaintingToWindow() &&
+      mClipContentDescendants &&
+      aScrollClip != mScrollClipContainingBlockDescendants &&
+      !DisplayItemScrollClip::IsAncestor(mClipContentDescendantsScrollClip, aScrollClip)) {
+    if (mClipContentDescendantsScrollClip && mClipContentDescendantsScrollClip->mScrollableFrame) {
+      mClipContentDescendantsScrollClip->mScrollableFrame->SetScrollsClipOnUnscrolledOutOfFlow();
+    }
+    mClipContentDescendantsScrollClip = nullptr;
+  }
+  mScrollClipContainingBlockDescendants = aScrollClip;
+  mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
+}
+
+void
 DisplayListClipState::ClipContainingBlockDescendants(const nsRect& aRect,
                                                      const nscoord* aRadii,
                                                      DisplayItemClip& aClipOnStack)
 {
   if (aRadii) {
     aClipOnStack.SetTo(aRect, aRadii);
   } else {
     aClipOnStack.SetTo(aRect);
   }
   if (mClipContainingBlockDescendants) {
     aClipOnStack.IntersectWith(*mClipContainingBlockDescendants);
   }
   mClipContainingBlockDescendants = &aClipOnStack;
+  mClipContentDescendantsScrollClip = GetCurrentInnermostScrollClip();
   mCurrentCombinedClip = nullptr;
 }
 
 void
 DisplayListClipState::ClipContentDescendants(const nsRect& aRect,
                                              const nscoord* aRadii,
                                              DisplayItemClip& aClipOnStack)
 {
--- a/layout/base/DisplayListClipState.h
+++ b/layout/base/DisplayListClipState.h
@@ -24,16 +24,17 @@ namespace mozilla {
 class DisplayListClipState {
 public:
   DisplayListClipState()
     : mClipContentDescendants(nullptr)
     , mClipContainingBlockDescendants(nullptr)
     , mCurrentCombinedClip(nullptr)
     , mScrollClipContentDescendants(nullptr)
     , mScrollClipContainingBlockDescendants(nullptr)
+    , mClipContentDescendantsScrollClip(nullptr)
     , mStackingContextAncestorSC(nullptr)
   {}
 
   /**
    * Returns intersection of mClipContainingBlockDescendants and
    * mClipContentDescendants, allocated on aBuilder's arena.
    */
   const DisplayItemClip* GetCurrentCombinedClip(nsDisplayListBuilder* aBuilder);
@@ -69,21 +70,18 @@ public:
 
 private:
   void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
   {
     mClipContainingBlockDescendants = aClip;
     mCurrentCombinedClip = nullptr;
   }
 
-  void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
-  {
-    mScrollClipContainingBlockDescendants = aScrollClip;
-    mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
-  }
+  void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+                                                  const DisplayItemScrollClip* aScrollClip);
 
   void Clear()
   {
     mClipContentDescendants = nullptr;
     mClipContainingBlockDescendants = nullptr;
     mCurrentCombinedClip = nullptr;
     // We do not clear scroll clips.
   }
@@ -179,16 +177,21 @@ private:
 
   /**
    * The same for scroll clips.
    */
   const DisplayItemScrollClip* mScrollClipContentDescendants;
   const DisplayItemScrollClip* mScrollClipContainingBlockDescendants;
 
   /**
+   * The scroll clip that was in effect when mClipContentDescendants was set.
+   */
+  const DisplayItemScrollClip* mClipContentDescendantsScrollClip;
+
+  /**
    * A scroll clip that is an ancestor of all the scroll clips that were
    * "current" on this clip state since EnterStackingContextContents was
    * called.
    */
   const DisplayItemScrollClip* mStackingContextAncestorSC;
 };
 
 /**
@@ -408,19 +411,20 @@ public:
   /**
    * *aClip must survive longer than this object. Be careful!!!
    */
   void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
   {
     mState.SetClipForContainingBlockDescendants(aClip);
   }
 
-  void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
+  void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+                                                  const DisplayItemScrollClip* aScrollClip)
   {
-    mState.SetScrollClipForContainingBlockDescendants(aScrollClip);
+    mState.SetScrollClipForContainingBlockDescendants(aBuilder, aScrollClip);
   }
 
   /**
    * Intersects the given clip rect (with optional aRadii) with the current
    * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
    * the result, stored in aClipOnStack.
    */
   void ClipContainingBlockDescendantsExtra(const nsRect& aRect,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2756,32 +2756,32 @@ nsIFrame::BuildDisplayListForChild(nsDis
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   CheckForApzAwareEventHandlers(aBuilder, child);
 
   if (savedOutOfFlowData) {
     aBuilder->SetBuildingInvisibleItems(false);
 
     clipState.SetClipForContainingBlockDescendants(
       &savedOutOfFlowData->mContainingBlockClip);
-    clipState.SetScrollClipForContainingBlockDescendants(
+    clipState.SetScrollClipForContainingBlockDescendants(aBuilder,
       savedOutOfFlowData->mContainingBlockScrollClip);
   } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
              isPlaceholder) {
     NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect");
     // Every item we build from now until we descent into an out of flow that
     // does have saved out of flow data should be invisible. This state gets
     // restored when AutoBuildingDisplayList gets out of scope.
     aBuilder->SetBuildingInvisibleItems(true);
 
     // If we have nested out-of-flow frames and the outer one isn't visible
     // then we won't have stored clip data for it. We can just clear the clip
     // instead since we know we won't render anything, and the inner out-of-flow
     // frame will setup the correct clip for itself.
     clipState.SetClipForContainingBlockDescendants(nullptr);
-    clipState.SetScrollClipForContainingBlockDescendants(nullptr);
+    clipState.SetScrollClipForContainingBlockDescendants(aBuilder, nullptr);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1886,16 +1886,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
   , mWillBuildScrollableLayer(false)
   , mIsScrollParent(false)
   , mIsScrollableLayerInRootContainer(false)
   , mHasBeenScrolled(false)
   , mIgnoreMomentumScroll(false)
   , mTransformingByAPZ(false)
   , mScrollableByAPZ(false)
   , mZoomableByAPZ(false)
+  , mScrollsClipOnUnscrolledOutOfFlow(false)
   , mVelocityQueue(aOuter->PresContext())
   , mAsyncScrollEvent(END_DOM)
 {
   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
     mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
   }
 
   EnsureFrameVisPrefsCached();
@@ -2114,16 +2115,22 @@ ScrollFrameHelper::HasPluginFrames()
 bool
 ScrollFrameHelper::HasPerspective() const
 {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
   return disp->mChildPerspective.GetUnit() != eStyleUnit_None;
 }
 
 void
+ScrollFrameHelper::SetScrollsClipOnUnscrolledOutOfFlow()
+{
+  mScrollsClipOnUnscrolledOutOfFlow = true;
+}
+
+void
 ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
                                      nsIScrollableFrame::ScrollMode aMode)
 {
   nsPoint current = GetScrollPosition();
   CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
   nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
   nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2*halfPixel - 1, 2*halfPixel - 1);
@@ -2750,21 +2757,21 @@ 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\n",
+    PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d clip %d\n",
         Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
         usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
-        mScrollableByAPZ, HasPluginFrames(), HasPerspective());
-    if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) && !HasPerspective()) {
+        mScrollableByAPZ, HasPluginFrames(), HasPerspective(), mScrollsClipOnUnscrolledOutOfFlow);
+    if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) && !HasPerspective() && !mScrollsClipOnUnscrolledOutOfFlow) {
       bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
       bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
       if (!apzDisabled) {
         if (LastScrollOrigin() == nsGkAtoms::apz) {
           schedulePaint = false;
           PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
         } else if (mScrollableByAPZ && !HasPluginFrames()) {
           nsIWidget* widget = presContext->GetNearestWidget();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -381,16 +381,17 @@ public:
     }
     NotifyPluginFrames(aTransforming ? BEGIN_APZ : END_APZ);
   }
   bool IsTransformingByAPZ() const {
     return mTransformingByAPZ;
   }
   void SetScrollableByAPZ(bool aScrollable);
   void SetZoomableByAPZ(bool aZoomable);
+  void SetScrollsClipOnUnscrolledOutOfFlow();
 
   bool UsesContainerScrolling() const;
 
   ScrollSnapInfo GetScrollSnapInfo() const;
 
   bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                              nsRect* aDirtyRect,
                              bool aAllowCreateDisplayPort);
@@ -576,16 +577,18 @@ public:
   bool mScrollableByAPZ:1;
 
   // True if the APZ is allowed to zoom this scrollframe.
   bool mZoomableByAPZ:1;
 
   // True if we don't want the scrollbar to repaint itself right now.
   bool mSuppressScrollbarRepaints:1;
 
+  bool mScrollsClipOnUnscrolledOutOfFlow:1;
+
   mozilla::layout::ScrollVelocityQueue mVelocityQueue;
 
 protected:
   class AutoScrollbarRepaintSuppression;
   friend class AutoScrollbarRepaintSuppression;
   class AutoScrollbarRepaintSuppression {
   public:
     AutoScrollbarRepaintSuppression(ScrollFrameHelper* aHelper, bool aSuppress)
@@ -1000,16 +1003,19 @@ public:
     return mHelper.IsTransformingByAPZ();
   }
   void SetScrollableByAPZ(bool aScrollable) override {
     mHelper.SetScrollableByAPZ(aScrollable);
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  void SetScrollsClipOnUnscrolledOutOfFlow() override {
+    mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
+  }
   
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
@@ -1403,16 +1409,19 @@ public:
     return mHelper.IsTransformingByAPZ();
   }
   void SetScrollableByAPZ(bool aScrollable) override {
     mHelper.SetScrollableByAPZ(aScrollable);
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  void SetScrollsClipOnUnscrolledOutOfFlow() override {
+    mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
+  }
   virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                                      nsRect* aDirtyRect,
                                      bool aAllowCreateDisplayPort) override {
     return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
   }
   virtual void NotifyApproximateFrameVisibilityUpdate() override {
     mHelper.NotifyApproximateFrameVisibilityUpdate();
   }
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -459,11 +459,13 @@ public:
    * own displayport and schedule a timer to do that if it is safe.
    */
   virtual void TriggerDisplayPortExpiration() = 0;
 
   /**
    * Returns information required to determine where to snap to after a scroll.
    */
   virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
+
+  virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
 };
 
 #endif
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -102,17 +102,17 @@ BuildDisplayListForTopLayerFrame(nsDispl
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   nsDisplayListBuilder::OutOfFlowDisplayData*
     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(aFrame);
   if (savedOutOfFlowData) {
     dirty = savedOutOfFlowData->mDirtyRect;
     clipState.SetClipForContainingBlockDescendants(
       &savedOutOfFlowData->mContainingBlockClip);
     clipState.SetScrollClipForContainingBlockDescendants(
-      savedOutOfFlowData->mContainingBlockScrollClip);
+      aBuilder, savedOutOfFlowData->mContainingBlockScrollClip);
   }
   nsDisplayList list;
   aFrame->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
   aList->AppendToTop(&list);
 }
 
 void
 ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,