Bug 961871 - Part 2: Refactor will-change scroll, pass through a nsDisplayListBuilder. r=mattwoodrow
authorBenoit Girard <b56girard@gmail.com>
Tue, 21 Oct 2014 21:54:06 -0400
changeset 211629 9f7ffa570839e2e8ca2c5025a0653b9ed4843855
parent 211628 5a21befb60a1026fd310da32cd612452476bfdcd
child 211630 aea7f440fb6131391b088cb96815329b550c1657
push id50753
push userb56girard@gmail.com
push dateWed, 22 Oct 2014 02:06:56 +0000
treeherdermozilla-inbound@c62411c27b55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs961871
milestone36.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 961871 - Part 2: Refactor will-change scroll, pass through a nsDisplayListBuilder. r=mattwoodrow
layout/base/FrameLayerBuilder.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
layout/generic/nsSubDocumentFrame.cpp
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -3487,17 +3487,17 @@ ContainerState::SetupScrollingMetadata(N
   }
   uint32_t baseLength = metricsArray.Length();
 
   nsIntRect tmpClipRect;
   const nsIntRect* layerClip = aEntry->mLayer->GetClipRect();
   nsIFrame* fParent;
   for (const nsIFrame* f = aEntry->mAnimatedGeometryRoot;
        f != mContainerAnimatedGeometryRoot;
-       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(
+       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(this->mBuilder,
            fParent, mContainerAnimatedGeometryRoot)) {
     fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (!fParent) {
       // This means mContainerAnimatedGeometryRoot was not an ancestor
       // of aEntry->mAnimatedGeometryRoot. This is a weird case but it
       // can happen, e.g. when a scrolled frame contains a frame with opacity
       // which contains a frame that is not scrolled by the scrolled frame.
       // For now, we just don't apply any specific async scrolling to this layer.
@@ -3570,17 +3570,17 @@ ContainerState::PostprocessRetainedLayer
     }
 
     SetOuterVisibleRegionForLayer(e->mLayer, e->mVisibleRegion,
       e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr);
 
     if (!e->mOpaqueRegion.IsEmpty()) {
       const nsIFrame* animatedGeometryRootToCover = animatedGeometryRootForOpaqueness;
       if (e->mOpaqueForAnimatedGeometryRootParent &&
-          nsLayoutUtils::GetAnimatedGeometryRootForFrame(e->mAnimatedGeometryRoot->GetParent(),
+          nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, e->mAnimatedGeometryRoot->GetParent(),
                                                          mContainerAnimatedGeometryRoot)
             == mContainerAnimatedGeometryRoot) {
         animatedGeometryRootToCover = mContainerAnimatedGeometryRoot;
         data = FindOpaqueRegionEntry(opaqueRegions,
             animatedGeometryRootToCover, e->mFixedPosFrameForLayerData);
       }
 
       if (!data) {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1711,17 +1711,18 @@ nsLayoutUtils::SetScrollbarThumbLayeriza
 
 static bool
 IsScrollbarThumbLayerized(nsIFrame* aThumbFrame)
 {
   return reinterpret_cast<intptr_t>(aThumbFrame->Properties().Get(ScrollbarThumbLayerized()));
 }
 
 nsIFrame*
-nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsIFrame* aFrame,
+nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder,
+                                               nsIFrame* aFrame,
                                                const nsIFrame* aStopAtAncestor)
 {
   nsIFrame* f = aFrame;
   nsIFrame* stickyFrame = nullptr;
   while (f != aStopAtAncestor) {
     if (nsLayoutUtils::IsPopup(f))
       break;
     if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(f))
@@ -1745,17 +1746,17 @@ nsLayoutUtils::GetAnimatedGeometryRootFo
     // is also active, just keep a record of sticky frames that we
     // encounter for now.
     if (f->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY &&
         !stickyFrame) {
       stickyFrame = f;
     }
     if (parentType == nsGkAtoms::scrollFrame) {
       nsIScrollableFrame* sf = do_QueryFrame(parent);
-      if (sf->IsScrollingActive() && sf->GetScrolledFrame() == f) {
+      if (sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == f) {
         // If we found a sticky frame inside this active scroll frame,
         // then use that. Otherwise use the scroll frame.
         if (stickyFrame) {
           return stickyFrame;
         }
         return f;
       } else {
         stickyFrame = nullptr;
@@ -1775,31 +1776,31 @@ nsLayoutUtils::GetAnimatedGeometryRootFo
                                           nsDisplayListBuilder* aBuilder,
                                           LayerManager* aManager)
 {
   nsIFrame* f = aItem->Frame();
   if (aItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER) {
     nsDisplayScrollLayer* scrollLayerItem =
       static_cast<nsDisplayScrollLayer*>(aItem);
     nsIFrame* scrolledFrame = scrollLayerItem->GetScrolledFrame();
-    return GetAnimatedGeometryRootForFrame(scrolledFrame,
+    return GetAnimatedGeometryRootForFrame(aBuilder, scrolledFrame,
         aBuilder->FindReferenceFrameFor(scrolledFrame));
   }
   if (aItem->ShouldFixToViewport(aManager)) {
     // Make its active scrolled root be the active scrolled root of
     // the enclosing viewport, since it shouldn't be scrolled by scrolled
     // frames in its document. InvalidateFixedBackgroundFramesFromList in
     // nsGfxScrollFrame will not repaint this item when scrolling occurs.
     nsIFrame* viewportFrame =
       nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame);
     NS_ASSERTION(viewportFrame, "no viewport???");
-    return GetAnimatedGeometryRootForFrame(viewportFrame,
+    return GetAnimatedGeometryRootForFrame(aBuilder, viewportFrame,
         aBuilder->FindReferenceFrameFor(viewportFrame));
   }
-  return GetAnimatedGeometryRootForFrame(f, aItem->ReferenceFrame());
+  return GetAnimatedGeometryRootForFrame(aBuilder, f, aItem->ReferenceFrame());
 }
 
 // static
 nsIScrollableFrame*
 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
                                                      Direction aDirection)
 {
   NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -506,17 +506,18 @@ public:
                                               nsDisplayListBuilder* aBuilder,
                                               mozilla::layers::LayerManager* aManager);
 
   /**
    * Finds the nearest ancestor frame to aFrame that is considered to have (or
    * will have) "animated geometry". This could be aFrame. Returns
    * aStopAtAncestor if no closer ancestor is found.
    */
-  static nsIFrame* GetAnimatedGeometryRootForFrame(nsIFrame* aFrame,
+  static nsIFrame* GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder,
+                                                   nsIFrame* aFrame,
                                                    const nsIFrame* aStopAtAncestor);
 
   /**
     * GetScrollableFrameFor returns the scrollable frame for a scrolled frame
     */
   static nsIScrollableFrame* GetScrollableFrameFor(const nsIFrame *aScrolledFrame);
 
   /**
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1845,19 +1845,19 @@ WrapPreserve3DListInternal(nsIFrame* aFr
     if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX)
       return rv;
   }
     
   return NS_OK;
 }
 
 static bool
-IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame)
-{
-  return aScrollableFrame && aScrollableFrame->IsScrollingActive();
+IsScrollFrameActive(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
+{
+  return aScrollableFrame && aScrollableFrame->IsScrollingActive(aBuilder);
 }
 
 static nsresult
 WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
                    nsDisplayList *aList)
 {
   uint32_t index = 0;
   nsDisplayList temp;
@@ -1969,17 +1969,18 @@ nsIFrame::BuildDisplayListForStackingCon
   if (usingSVGEffects) {
     dirtyRect =
       nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
   }
 
   bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
   bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
   bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
-    IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
+    IsScrollFrameActive(aBuilder,
+                        nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
                         nsLayoutUtils::SCROLLABLE_SAME_DOC |
                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingDisplayList(aBuilder, this, dirtyRect, true);
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
   if (isTransformed || useOpacity || useBlendMode || usingSVGEffects || useStickyPosition) {
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2190,21 +2190,16 @@ bool ScrollFrameHelper::ShouldClampScrol
 }
 
 bool ScrollFrameHelper::IsAlwaysActive() const
 {
   if (nsDisplayItem::ForceActiveLayers()) {
     return true;
   }
 
-  const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
-    return true;
-  }
-
   // Unless this is the root scrollframe for a non-chrome document
   // which is the direct child of a chrome document, we default to not
   // being "active".
   if (!(mIsRoot && mOuter->PresContext()->IsRootContentDocument())) {
      return false;
   }
 
   // If we have scrolled before, then we should stay active.
@@ -2252,20 +2247,19 @@ void ScrollFrameHelper::ScrollVisual(nsP
   // have any effect.
   mHasBeenScrolled = true;
 
   AdjustViews(mScrolledFrame);
   // We need to call this after fixing up the view positions
   // to be consistent with the frame hierarchy.
   bool needToInvalidateOnScroll = NeedToInvalidateOnScroll(mOuter);
   mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
-  if (IsScrollingActive() && needToInvalidateOnScroll) {
+  if (needToInvalidateOnScroll) {
     MarkNotRecentlyScrolled();
-  }
-  if (!needToInvalidateOnScroll) {
+  } else {
     MarkRecentlyScrolled();
   }
 
   mOuter->SchedulePaint();
 }
 
 /**
  * Clamp desired scroll position aDesired and range [aDestLower, aDestUpper]
@@ -2547,17 +2541,17 @@ ScrollFrameHelper::AppendScrollPartsTo(n
       continue;
 
     scrollParts.AppendElement(kid);
   }
   if (scrollParts.IsEmpty()) {
     return;
   }
 
-  mozilla::layers::FrameMetrics::ViewID scrollTargetId = IsScrollingActive()
+  mozilla::layers::FrameMetrics::ViewID scrollTargetId = IsScrollingActive(aBuilder)
     ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
     : mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
 
   scrollParts.Sort(HoveredStateComparator());
 
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
   // Don't let scrollparts extent outside our frame's border-box, if these are
   // viewport scrollbars. They would create layerization problems. This wouldn't
@@ -2766,20 +2760,20 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   if (aBuilder->IsForImageVisibility()) {
     mLastUpdateImagesPos = GetScrollPosition();
   }
 
   mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   if (aBuilder->IsPaintingToWindow()) {
     mScrollPosAtLastPaint = GetScrollPosition();
-    if (IsScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
+    if (IsScrollingActive(aBuilder) && NeedToInvalidateOnScroll(mOuter)) {
       MarkNotRecentlyScrolled();
     }
-    if (IsScrollingActive()) {
+    if (IsScrollingActive(aBuilder)) {
       if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
         mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
       }
     } else {
       mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
     }
   }
 
@@ -2897,16 +2891,20 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   } else {
     shouldBuildLayer =
       nsLayoutUtils::WantSubAPZC() &&
       WantAsyncScroll() &&
       // If we are the root scroll frame for the display root then we don't need a scroll
       // info layer to make a ComputeFrameMetrics call for us as
       // nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
       (!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
+
+    const nsStyleDisplay* disp = mOuter->StyleDisplay();
+    bool willScroll = disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL);
+    shouldBuildLayer |= willScroll;
   }
 
   mScrollParentID = aBuilder->GetCurrentScrollParentId();
 
   nsDisplayListCollection scrolledContent;
   {
     // Note that setting the current scroll parent id here means that positioned children
     // of this scroll info layer will pick up the scroll info layer as their scroll handoff
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -279,18 +279,22 @@ public:
     return (mHasVerticalScrollbar ? nsIScrollableFrame::VERTICAL : 0) |
            (mHasHorizontalScrollbar ? nsIScrollableFrame::HORIZONTAL : 0);
   }
   nsMargin GetActualScrollbarSizes() const;
   nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState);
   nscoord GetNondisappearingScrollbarWidth(nsBoxLayoutState* aState);
   bool IsLTR() const;
   bool IsScrollbarOnRight() const;
-  bool IsScrollingActive() const
+  bool IsScrollingActive(nsDisplayListBuilder* aBuilder) const
   {
+    const nsStyleDisplay* disp = mOuter->StyleDisplay();
+    if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
+      return true;
+    }
     return mHasBeenScrolledRecently ||
         IsAlwaysActive() || mShouldBuildScrollableLayer;
   }
   bool IsProcessingAsyncScroll() const {
     return mAsyncScroll != nullptr || mAsyncSmoothMSDScroll != nullptr;
   }
   void ResetScrollPositionForLayerPixelAlignment()
   {
@@ -691,18 +695,18 @@ public:
    */
   virtual void CurPosAttributeChanged(nsIContent* aChild) MOZ_OVERRIDE {
     mHelper.CurPosAttributeChanged(aChild);
   }
   NS_IMETHOD PostScrolledAreaEventForCurrentArea() MOZ_OVERRIDE {
     mHelper.PostScrolledAreaEvent();
     return NS_OK;
   }
-  virtual bool IsScrollingActive() MOZ_OVERRIDE {
-    return mHelper.IsScrollingActive();
+  virtual bool IsScrollingActive(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE {
+    return mHelper.IsScrollingActive(aBuilder);
   }
   virtual bool IsProcessingAsyncScroll() MOZ_OVERRIDE {
     return mHelper.IsProcessingAsyncScroll();
   }
   virtual void ResetScrollPositionForLayerPixelAlignment() MOZ_OVERRIDE {
     mHelper.ResetScrollPositionForLayerPixelAlignment();
   }
   virtual bool IsResolutionSet() const MOZ_OVERRIDE {
@@ -1049,18 +1053,18 @@ public:
    */
   virtual void CurPosAttributeChanged(nsIContent* aChild) MOZ_OVERRIDE {
     mHelper.CurPosAttributeChanged(aChild);
   }
   NS_IMETHOD PostScrolledAreaEventForCurrentArea() MOZ_OVERRIDE {
     mHelper.PostScrolledAreaEvent();
     return NS_OK;
   }
-  virtual bool IsScrollingActive() MOZ_OVERRIDE {
-    return mHelper.IsScrollingActive();
+  virtual bool IsScrollingActive(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE {
+    return mHelper.IsScrollingActive(aBuilder);
   }
   virtual bool IsProcessingAsyncScroll() MOZ_OVERRIDE {
     return mHelper.IsProcessingAsyncScroll();
   }
   virtual void ResetScrollPositionForLayerPixelAlignment() MOZ_OVERRIDE {
     mHelper.ResetScrollPositionForLayerPixelAlignment();
   }
   virtual bool IsResolutionSet() const MOZ_OVERRIDE {
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -22,16 +22,17 @@
 
 class nsBoxLayoutState;
 class nsIScrollPositionListener;
 class nsIFrame;
 class nsPresContext;
 class nsIContent;
 class nsRenderingContext;
 class nsIAtom;
+class nsDisplayListBuilder;
 
 namespace mozilla {
 struct ContainerLayerParameters;
 namespace layers {
 class Layer;
 }
 }
 
@@ -289,17 +290,17 @@ public:
    */
   NS_IMETHOD PostScrolledAreaEventForCurrentArea() = 0;
 
   /**
    * Returns true if this scrollframe is being "actively scrolled".
    * This basically means that we should allocate resources in the
    * expectation that scrolling is going to happen.
    */
-  virtual bool IsScrollingActive() = 0;
+  virtual bool IsScrollingActive(nsDisplayListBuilder* aBuilder) = 0;
   /**
    * Returns true if the scrollframe is currently processing an async
    * or smooth scroll.
    */
   virtual bool IsProcessingAsyncScroll() = 0;
   /**
    * Call this when the layer(s) induced by active scrolling are being
    * completely redrawn.
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -440,17 +440,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   }
 
   nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable();
   bool constructResolutionItem = subdocRootFrame &&
     (presShell->GetXResolution() != 1.0 || presShell->GetYResolution() != 1.0);
   bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
   bool needsOwnLayer = constructResolutionItem || constructZoomItem ||
     haveDisplayPort ||
-    presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive());
+    presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive(aBuilder));
 
   nsDisplayList childItems;
 
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     if (needsOwnLayer) {
       // Clear current clip. There's no point in propagating it down, since
       // the layer we will construct will be clipped by the current clip.