Bug 1526972 - P8: Reset InInvalidSubtree when processing unrelated frames. r=mattwoodrow
☠☠ backed out by 9f0f38c38ccc ☠ ☠
authorDan Glastonbury <dan.glastonbury@gmail.com>
Mon, 15 Apr 2019 00:23:21 +0000
changeset 469454 815543d81a1d2ab79ed4e27612dba769e4b697a8
parent 469453 a895c9028b319523f537677318a0970baa217d82
child 469455 3619626a7662df350d6136343ad86c3799e3ab0d
push id112792
push userncsoregi@mozilla.com
push dateMon, 15 Apr 2019 09:49:11 +0000
treeherdermozilla-inbound@a57f27d3ccd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1526972
milestone68.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 1526972 - P8: Reset InInvalidSubtree when processing unrelated frames. r=mattwoodrow Differential Revision: https://phabricator.services.mozilla.com/D26141
layout/generic/ViewportFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsPageFrame.cpp
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -101,18 +101,21 @@ static void BuildDisplayListForTopLayerF
     // root scroll frame.
     clipState.SetClipChainForContainingBlockDescendants(
         savedOutOfFlowData->mCombinedClipChain);
     clipState.ClipContainingBlockDescendantsExtra(
         visible + aBuilder->ToReferenceFrame(aFrame), nullptr);
     asrSetter.SetCurrentActiveScrolledRoot(
         savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
   }
+  // This function jumps into random frames that may not be descendants of
+  // aBuilder->mCurrentFrame, so aBuilder->mInInvalidSubtree is unrelated.
+  // Request recalculation of mInInvalidSubtree.
   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
-      aBuilder, aFrame, visible, dirty);
+      aBuilder, aFrame, visible, dirty, nsDisplayListBuilder::RIIS_YES);
 
   nsDisplayList list;
   aFrame->BuildDisplayListForStackingContext(aBuilder, &list);
   aList->AppendToTop(&list);
 }
 
 void ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
                                                 nsDisplayList* aList) {
@@ -148,18 +151,20 @@ void ViewportFrame::BuildDisplayListForT
               frame->GetChildList(kBackdropList).FirstChild()) {
         MOZ_ASSERT(backdropPh->IsPlaceholderFrame());
         MOZ_ASSERT(!backdropPh->GetNextSibling(), "more than one ::backdrop?");
         MOZ_ASSERT(backdropPh->HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
                    "did you intend to reflow ::backdrop placeholders?");
         nsIFrame* backdropFrame =
             static_cast<nsPlaceholderFrame*>(backdropPh)->GetOutOfFlowFrame();
         MOZ_ASSERT(backdropFrame);
+
         BuildDisplayListForTopLayerFrame(aBuilder, backdropFrame, aList);
       }
+
       BuildDisplayListForTopLayerFrame(aBuilder, frame, aList);
     }
   }
 
   if (nsCanvasFrame* canvasFrame = PresShell()->GetCanvasFrame()) {
     if (Element* container = canvasFrame->GetCustomContentContainer()) {
       if (nsIFrame* frame = container->GetPrimaryFrame()) {
         MOZ_ASSERT(frame->StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3802,18 +3802,25 @@ void nsIFrame::BuildDisplayListForChild(
        IsSVGContentWithCSSClip(child))) {
     pseudoStackingContext = true;
     awayFromCommonPath = true;
   }
 
   NS_ASSERTION(!isStackingContext || pseudoStackingContext,
                "Stacking contexts must also be pseudo-stacking-contexts");
 
+  // nsBlockFrame paints pushed floats directly, rather than through their
+  // placeholder, which is why we force a recallculation of InInvalidSubtree
+  // state.
+  auto recalcInInvalidSubtree =
+      (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
+          ? nsDisplayListBuilder::RIIS_YES
+          : nsDisplayListBuilder::RIIS_NO;
   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
-      aBuilder, child, visible, dirty);
+      aBuilder, child, visible, dirty, recalcInInvalidSubtree);
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   CheckForApzAwareEventHandlers(aBuilder, child);
 
   if (savedOutOfFlowData) {
     aBuilder->SetBuildingInvisibleItems(false);
 
     clipState.SetClipChainForContainingBlockDescendants(
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3759,16 +3759,22 @@ void ScrollFrameHelper::BuildDisplayList
 
 void ScrollFrameHelper::MaybeAddTopLayerItems(nsDisplayListBuilder* aBuilder,
                                               const nsDisplayListSet& aLists) {
   if (mIsRoot) {
     if (ViewportFrame* viewportFrame = do_QueryFrame(mOuter->GetParent())) {
       nsDisplayList topLayerList;
       viewportFrame->BuildDisplayListForTopLayer(aBuilder, &topLayerList);
       if (!topLayerList.IsEmpty()) {
+        // This function jumps into random frames that may not be descendants of
+        // aBuilder->mCurrentFrame, so aBuilder->mInInvalidSubtree is unrelated.
+        // Request recalculation of mInInvalidSubtree.
+        nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
+            aBuilder, viewportFrame, nsDisplayListBuilder::RIIS_YES);
+
         // Wrap the whole top layer in a single item with maximum z-index,
         // and append it at the very end, so that it stays at the topmost.
         nsDisplayWrapList* wrapList = MakeDisplayItem<nsDisplayWrapList>(
             aBuilder, viewportFrame, &topLayerList,
             aBuilder->CurrentActiveScrolledRoot(), false, 2);
         if (wrapList) {
           wrapList->SetOverrideZIndex(
               std::numeric_limits<decltype(wrapList->ZIndex())>::max());
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -519,18 +519,22 @@ void nsPageFrame::BuildDisplayList(nsDis
     // we don't have to process earlier pages. The display lists for
     // these extra pages are pruned so that only display items for the
     // page we currently care about (which we would have reached by
     // following placeholders to their out-of-flows) end up on the list.
     nsIFrame* page = child;
     while ((page = GetNextPage(page)) != nullptr) {
       nsRect childVisible = visibleRect + child->GetOffsetTo(page);
 
+      // This function jumps into random frames that may not be descendants of
+      // aBuilder->mCurrentFrame, so aBuilder->mInInvalidSubtree is unrelated.
+      // Request recalculation of mInInvalidSubtree.
       nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
-          aBuilder, page, childVisible, childVisible);
+          aBuilder, page, childVisible, childVisible,
+          nsDisplayListBuilder::RIIS_YES);
       BuildDisplayListForExtraPage(aBuilder, this, page, &content);
     }
 
     // Invoke AutoBuildingDisplayList to ensure that the correct visibleRect
     // is used to compute the visible rect if AddCanvasBackgroundColorItem
     // creates a display item.
     nsDisplayListBuilder::AutoBuildingDisplayList building(
         aBuilder, child, visibleRect, visibleRect);
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -209,25 +209,31 @@ void RetainedDisplayListBuilder::Increme
 
   nsIPresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
   MOZ_ASSERT(presShell);
 
   mBuilder.IncrementPresShellPaintCount(presShell);
 }
 
 bool AnyContentAncestorModified(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
-  for (nsIFrame* f = aFrame; f;
-       f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
+  nsIFrame* f = aFrame;
+  while (f) {
     if (f->IsFrameModified()) {
       return true;
     }
 
     if (aStopAtFrame && f == aStopAtFrame) {
       break;
     }
+
+    if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
+      f = f->GetParent();
+    } else {
+      f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f);
+    }
   }
 
   return false;
 }
 
 static Maybe<const ActiveScrolledRoot*> SelectContainerASR(
     const DisplayItemClipChain* aClipChain, const ActiveScrolledRoot* aItemASR,
     Maybe<const ActiveScrolledRoot*>& aContainerASR) {
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -10280,33 +10280,63 @@ PaintTelemetry::AutoRecord::~AutoRecord(
     return;
   }
 
   sMetrics[mMetric] += (TimeStamp::Now() - mStart).ToMilliseconds();
 }
 
 }  // namespace mozilla
 
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
+  if (aFrame->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
+    return aFrame;
+  }
+
+  if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
+      !aFrame->GetPrevInFlow()) {
+    return aFrame->GetPlaceholderFrame();
+  }
+
+  return aFrame;
+}
+
+static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
+  nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
+  MOZ_ASSERT(f);
+  return nsLayoutUtils::GetCrossDocParentFrame(f);
+}
+#endif
+
 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
     nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
     const nsRect& aVisibleRect, const nsRect& aDirtyRect,
-    const bool aIsTransformed)
+    const bool aIsTransformed, RecalcInInvalidSubtree aRecalcInvalidSubtree)
     : mBuilder(aBuilder),
       mPrevFrame(aBuilder->mCurrentFrame),
       mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
       mPrevHitTestArea(aBuilder->mHitTestArea),
       mPrevHitTestInfo(aBuilder->mHitTestInfo),
       mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
       mPrevVisibleRect(aBuilder->mVisibleRect),
       mPrevDirtyRect(aBuilder->mDirtyRect),
       mPrevAGR(aBuilder->mCurrentAGR),
       mPrevAncestorHasApzAwareEventHandler(
           aBuilder->mAncestorHasApzAwareEventHandler),
       mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
       mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  // Validate that aForChild is being visited from it's parent frame if
+  // recalculation of mInInvalidSubtree isn't requested.
+  const nsIFrame* ancestor = GetAncestorFor(aForChild);
+  MOZ_DIAGNOSTIC_ASSERT(aRecalcInvalidSubtree ==
+                            nsDisplayListBuilder::RIIS_YES ||
+                        aForChild == mPrevFrame || ancestor == mPrevFrame);
+#endif
+
   if (aIsTransformed) {
     aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
     aBuilder->mCurrentReferenceFrame = aForChild;
   } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
     aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
   } else {
     aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
         aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
@@ -10321,15 +10351,19 @@ nsDisplayListBuilder::AutoBuildingDispla
           aBuilder->WrapAGRForFrame(aForChild, isAsync, aBuilder->mCurrentAGR);
     }
   } else if (aBuilder->mCurrentFrame != aForChild) {
     aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
   }
 
   MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
       aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
-  aBuilder->mInInvalidSubtree =
-      aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
+  if (!aRecalcInvalidSubtree) {
+    aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree ||
+      aForChild->IsFrameModified();
+  } else {
+    aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
+  }
   aBuilder->mCurrentFrame = aForChild;
   aBuilder->mVisibleRect = aVisibleRect;
   aBuilder->mDirtyRect =
       aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
 }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -437,16 +437,21 @@ class nsDisplayListBuilder {
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
   typedef mozilla::layers::ScrollableLayerGuid::ViewID ViewID;
   typedef mozilla::gfx::CompositorHitTestInfo CompositorHitTestInfo;
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
   typedef mozilla::Maybe<mozilla::layers::ScrollDirection> MaybeScrollDirection;
 
   /**
+   * Does InInvalidSubtree need to recalculated?
+   */
+  enum RecalcInInvalidSubtree { RIIS_NO, RIIS_YES };
+
+  /**
    * @param aReferenceFrame the frame at the root of the subtree; its origin
    * is the origin of the reference coordinate system for this display list
    * @param aMode encodes what the builder is being used for.
    * @param aBuildCaret whether or not we should include the caret in any
    * display lists that we make.
    */
   nsDisplayListBuilder(nsIFrame* aReferenceFrame,
                        nsDisplayListBuilderMode aMode, bool aBuildCaret,
@@ -1127,25 +1132,35 @@ class nsDisplayListBuilder {
   /**
    * A helper class used to temporarily set nsDisplayListBuilder properties for
    * building display items.
    * aVisibleRect and aDirtyRect are relative to aForChild.
    */
   class AutoBuildingDisplayList {
    public:
     AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
-                            const nsRect& aVisibleRect,
-                            const nsRect& aDirtyRect)
+                            RecalcInInvalidSubtree aRecalcInvalidSubtree)
+        : AutoBuildingDisplayList(
+              aBuilder, aForChild, aBuilder->GetVisibleRect(),
+              aBuilder->GetDirtyRect(), aForChild->IsTransformed(),
+              aRecalcInvalidSubtree) {}
+
+    AutoBuildingDisplayList(
+        nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
+        const nsRect& aVisibleRect, const nsRect& aDirtyRect,
+        RecalcInInvalidSubtree aRecalcInvalidSubtree = RIIS_NO)
         : AutoBuildingDisplayList(aBuilder, aForChild, aVisibleRect, aDirtyRect,
-                                  aForChild->IsTransformed()) {}
-
-    AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
-                            const nsRect& aVisibleRect,
-                            const nsRect& aDirtyRect,
-                            const bool aIsTransformed);
+                                  aForChild->IsTransformed(),
+                                  aRecalcInvalidSubtree) {}
+
+    AutoBuildingDisplayList(
+        nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
+        const nsRect& aVisibleRect, const nsRect& aDirtyRect,
+        const bool aIsTransformed,
+        RecalcInInvalidSubtree aRecalcInvalidSubtree = RIIS_NO);
 
     void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame,
                                            const nsPoint& aOffset) {
       mBuilder->mCurrentReferenceFrame = aFrame;
       mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
     }
 
     bool IsAnimatedGeometryRoot() const { return mCurrentAGRState == AGR_YES; }