Bug 1344971 - Part 1: Create OutOfFlowDisplayData for the parent of the OOF frame so they can be shared. r?mstange draft
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 01 Mar 2017 12:09:43 +1300
changeset 494301 be592d4e6902b9e1c587470b7fa1627a90a3215c
parent 494263 b7e42143bbbc9dc3e5c05bd1e93b6485ce1d0ad4
child 494302 637e3ce6df84ea520f34b8a8d691fce5f792bf8b
push id48011
push usermwoodrow@mozilla.com
push dateTue, 07 Mar 2017 01:18:12 +0000
reviewersmstange
bugs1344971
milestone55.0a1
Bug 1344971 - Part 1: Create OutOfFlowDisplayData for the parent of the OOF frame so they can be shared. r?mstange MozReview-Commit-ID: 8MH4JAyLU8w
layout/generic/ViewportFrame.cpp
layout/generic/nsFrame.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -101,17 +101,17 @@ BuildDisplayListForTopLayerFrame(nsDispl
                                  nsDisplayList* aList)
 {
   nsRect dirty;
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   nsDisplayListBuilder::OutOfFlowDisplayData*
     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(aFrame);
   if (savedOutOfFlowData) {
-    dirty = savedOutOfFlowData->mDirtyRect;
+    dirty = savedOutOfFlowData->GetDirtyRectForFrame(aBuilder, aFrame);
     // This function is called after we've finished building display items for
     // the root scroll frame. That means that the content clip from the root
     // scroll frame is no longer on aBuilder. However, we need to make sure
     // that the display items we build in this function have finite clipped
     // bounds with respect to the root ASR, so we restore the *combined clip*
     // that we saved earlier. The combined clip will include the clip from the
     // root scroll frame.
     clipState.SetClipChainForContainingBlockDescendants(
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2817,17 +2817,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     // could call GetType again but since we don't currently need it, let's
     // avoid the virtual call.
     childType = nullptr;
     // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
     if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
       return;
     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
     if (savedOutOfFlowData) {
-      dirty = savedOutOfFlowData->mDirtyRect;
+      dirty = savedOutOfFlowData->GetDirtyRectForFrame(aBuilder, child);
     } else {
       // The out-of-flow frame did not intersect the dirty area. We may still
       // need to traverse into it, since it may contain placeholders we need
       // to enter to reach other out-of-flow frames that are visible.
       dirty.SetEmpty();
     }
     pseudoStackingContext = true;
   }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1022,82 +1022,58 @@ nsDisplayListBuilder::FindAnimatedGeomet
     if (viewportFrame) {
       return FindAnimatedGeometryRootFor(viewportFrame);
     }
   }
   return FindAnimatedGeometryRootFor(aItem->Frame());
 }
 
 
-void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
+bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
                                                         nsIFrame* aFrame,
                                                         const nsRect& aDirtyRect)
 {
-  nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
-  if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
-      IsPaintingToWindow()) {
-    NS_ASSERTION(aDirtyFrame == aFrame->GetParent(), "Dirty frame should be viewport frame");
-    // position: fixed items are reflowed into and only drawn inside the
-    // viewport, or the scroll position clamping scrollport size, if one is
-    // set.
-    nsIPresShell* ps = aFrame->PresContext()->PresShell();
-    dirtyRectRelativeToDirtyFrame.MoveTo(0, 0);
-    if (ps->IsScrollPositionClampingScrollPortSizeSet()) {
-      dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize());
-    } else {
-      dirtyRectRelativeToDirtyFrame.SizeTo(aDirtyFrame->GetSize());
-    }
-  }
-  nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame);
+  MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
+  nsRect dirty = OutOfFlowDisplayData::ComputeDirtyRectForFrame(this, aFrame, aDirtyRect);
   nsRect overflowRect = aFrame->GetVisualOverflowRect();
 
   if (aFrame->IsTransformed() &&
       EffectCompositor::HasAnimationsForCompositor(aFrame,
                                                    eCSSProperty_transform)) {
    /**
     * Add a fuzz factor to the overflow rectangle so that elements only just
     * out of view are pulled into the display list, so they can be
     * prerendered if necessary.
     */
     overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
   }
 
   if (!dirty.IntersectRect(dirty, overflowRect) &&
       !(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
-    return;
-  }
-
-  // mClipState.GetClipChainForContainingBlockDescendants can return pointers
-  // to objects on the stack, so we need to clone the chain.
-  const DisplayItemClipChain* clipChain =
-    CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
-  const DisplayItemClipChain* combinedClipChain = mClipState.GetCurrentCombinedClipChain(this);
-  const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
-  OutOfFlowDisplayData* data = new OutOfFlowDisplayData(clipChain, combinedClipChain, asr, dirty);
-  aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
+    return false;
+  }
 
   MarkFrameForDisplay(aFrame, aDirtyFrame);
+  return true;
 }
 
 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
-  nsPresContext* presContext = aFrame->PresContext();
-  presContext->PropertyTable()->
-    Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
-
   for (nsIFrame* f = aFrame; f;
        f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
     if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
       return;
     f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
   }
 }
 
 nsDisplayListBuilder::~nsDisplayListBuilder() {
   NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
                "All frames should have been unmarked");
+  NS_ASSERTION(mFramesWithOOFData.Length() == 0,
+               "All OOF data should have been removed");
   NS_ASSERTION(mPresShellStates.Length() == 0,
                "All presshells should have been exited");
   NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
 
   nsCSSRendering::EndFrameTreesLocked();
 
   for (ActiveScrolledRoot* asr : mActiveScrolledRoots) {
     asr->ActiveScrolledRoot::~ActiveScrolledRoot();
@@ -1150,16 +1126,17 @@ nsDisplayListBuilder::GetCaret() {
 void
 nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
                                      bool aPointerEventsNoneDoc)
 {
   PresShellState* state = mPresShellStates.AppendElement();
   state->mPresShell = aReferenceFrame->PresContext()->PresShell();
   state->mCaretFrame = nullptr;
   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
+  state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
 
   state->mPresShell->UpdateCanvasBackground();
 
   if (mIsPaintingToWindow) {
     mReferenceFrame->AddPaintedPresShell(state->mPresShell);
 
     state->mPresShell->IncrementPaintCount();
   }
@@ -1255,36 +1232,62 @@ nsDisplayListBuilder::ResetMarkedFramesF
 {
   // Unmark and pop off the frames marked for display in this pres shell.
   uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay;
   for (uint32_t i = firstFrameForShell;
        i < mFramesMarkedForDisplay.Length(); ++i) {
     UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
   }
   mFramesMarkedForDisplay.SetLength(firstFrameForShell);
+
+  firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
+  for (uint32_t i = firstFrameForShell;
+       i < mFramesWithOOFData.Length(); ++i) {
+    nsPresContext* presContext = mFramesWithOOFData[i]->PresContext();
+    presContext->PropertyTable()->
+      Delete(mFramesWithOOFData[i], nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
+
+  }
+  mFramesWithOOFData.SetLength(firstFrameForShell);
 }
 
 void
 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
                                                const nsFrameList& aFrames,
-                                               const nsRect& aDirtyRect) {
+                                               const nsRect& aDirtyRect)
+{
   mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength());
+  bool markedFrames = false;
   for (nsIFrame* e : aFrames) {
     // Skip the AccessibleCaret frame when building no caret.
     if (!IsBuildingCaret()) {
       nsIContent* content = e->GetContent();
       if (content && content->IsInNativeAnonymousSubtree() && content->IsElement()) {
         auto classList = content->AsElement()->ClassList();
         if (classList->Contains(NS_LITERAL_STRING("moz-accessiblecaret"))) {
           continue;
         }
       }
     }
-    mFramesMarkedForDisplay.AppendElement(e);
-    MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, aDirtyRect);
+    if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, aDirtyRect)) {
+      mFramesMarkedForDisplay.AppendElement(e);
+      markedFrames = true;
+    }
+  }
+
+  if (markedFrames) {
+    // mClipState.GetClipChainForContainingBlockDescendants can return pointers
+    // to objects on the stack, so we need to clone the chain.
+    const DisplayItemClipChain* clipChain =
+      CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
+    const DisplayItemClipChain* combinedClipChain = mClipState.GetCurrentCombinedClipChain(this);
+    const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
+    OutOfFlowDisplayData* data = new OutOfFlowDisplayData(clipChain, combinedClipChain, asr, aDirtyRect);
+    aDirtyFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
+    mFramesWithOOFData.AppendElement(aDirtyFrame);
   }
 
   if (!aDirtyFrame->GetParent()) {
     // This is the viewport frame of aDirtyFrame's presshell.
     // Store the current display data so that it can be used for fixed
     // background images.
     NS_ASSERTION(CurrentPresShellState()->mPresShell ==
         aDirtyFrame->PresContext()->PresShell(),
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1219,24 +1219,53 @@ public:
       , mCombinedClipChain(aCombinedClipChain)
       , mContainingBlockActiveScrolledRoot(aContainingBlockActiveScrolledRoot)
       , mDirtyRect(aDirtyRect)
     {}
     const DisplayItemClipChain* mContainingBlockClipChain;
     const DisplayItemClipChain* mCombinedClipChain; // only necessary for the special case of top layer
     const ActiveScrolledRoot* mContainingBlockActiveScrolledRoot;
     nsRect mDirtyRect;
+
+
+    static nsRect ComputeDirtyRectForFrame(nsDisplayListBuilder* aBuilder,
+                                           nsIFrame* aFrame,
+                                           const nsRect& aDirtyRect) {
+      nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
+      if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
+          aBuilder->IsPaintingToWindow()) {
+        // position: fixed items are reflowed into and only drawn inside the
+        // viewport, or the scroll position clamping scrollport size, if one is
+        // set.
+        nsIPresShell* ps = aFrame->PresContext()->PresShell();
+        dirtyRectRelativeToDirtyFrame.MoveTo(0, 0);
+        if (ps->IsScrollPositionClampingScrollPortSizeSet()) {
+          dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize());
+        } else {
+          dirtyRectRelativeToDirtyFrame.SizeTo(aFrame->GetParent()->GetSize());
+        }
+      }
+      return dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
+    }
+
+    nsRect GetDirtyRectForFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
+      return ComputeDirtyRectForFrame(aBuilder, aFrame, mDirtyRect);
+    }
   };
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
                                       OutOfFlowDisplayData)
 
   static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame)
   {
-    return aFrame->Properties().Get(OutOfFlowDisplayDataProperty());
+    if (!(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
+        !aFrame->GetParent()) {
+      return nullptr;
+    }
+    return aFrame->GetParent()->Properties().Get(OutOfFlowDisplayDataProperty());
   }
 
   nsPresContext* CurrentPresContext() {
     return CurrentPresShellState()->mPresShell->GetPresContext();
   }
 
   OutOfFlowDisplayData* GetCurrentFixedBackgroundDisplayData()
   {
@@ -1382,17 +1411,17 @@ public:
   bool HitTestShouldStopAtFirstOpaque() const {
     return mHitTestShouldStopAtFirstOpaque;
   }
   void SetHitTestShouldStopAtFirstOpaque(bool aHitTestShouldStopAtFirstOpaque) {
     mHitTestShouldStopAtFirstOpaque = aHitTestShouldStopAtFirstOpaque;
   }
 
 private:
-  void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
+  bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
                                     const nsRect& aDirtyRect);
 
   /**
    * Returns whether a frame acts as an animated geometry root, optionally
    * returning the next ancestor to check.
    */
   bool IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent = nullptr);
 
@@ -1423,16 +1452,17 @@ private:
   bool AddToAGRBudget(nsIFrame* aFrame);
 
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
     mozilla::Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
     uint32_t      mFirstFrameMarkedForDisplay;
+    uint32_t      mFirstFrameWithOOFData;
     bool          mIsBackgroundOnly;
     // This is a per-document flag turning off event handling for all content
     // in the document, and is set when we enter a subdocument for a pointer-
     // events:none frame.
     bool          mInsidePointerEventsNoneDoc;
   };
 
   PresShellState* CurrentPresShellState() {
@@ -1451,16 +1481,17 @@ private:
 
   nsIFrame* const                mReferenceFrame;
   nsIFrame*                      mIgnoreScrollFrame;
   nsDisplayLayerEventRegions*    mLayerEventRegions;
   PLArenaPool                    mPool;
   nsCOMPtr<nsISelection>         mBoundingSelection;
   AutoTArray<PresShellState,8> mPresShellStates;
   AutoTArray<nsIFrame*,100>    mFramesMarkedForDisplay;
+  AutoTArray<nsIFrame*,20>     mFramesWithOOFData;
   AutoTArray<ThemeGeometry,2>  mThemeGeometries;
   nsDisplayTableItem*            mCurrentTableItem;
   DisplayListClipState           mClipState;
   const ActiveScrolledRoot*      mCurrentActiveScrolledRoot;
   const ActiveScrolledRoot*      mCurrentContainerASR;
   // mCurrentFrame is the frame that we're currently calling (or about to call)
   // BuildDisplayList on.
   const nsIFrame*                mCurrentFrame;