Bug 1453541 - Part 2: Look for Out Of Flow frames with modified ancestors during ProcessFrame instead of during display list building. r?mstange
MozReview-Commit-ID: KsgrFqr2gVN
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -7,16 +7,17 @@
#include "RetainedDisplayListBuilder.h"
#include "DisplayListChecker.h"
#include "gfxPrefs.h"
#include "nsPlaceholderFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsViewManager.h"
+#include "nsCanvasFrame.h"
/**
* Code for doing display list building for a modified subset of the window,
* and then merging it into the existing display list (for the full window).
*
* The approach primarily hinges on the observation that the ‘true’ ordering of
* display items is represented by a DAG (only items that intersect in 2d space
* have a defined ordering). Our display list is just one of a many possible linear
@@ -885,16 +886,53 @@ RetainedDisplayListBuilder::ProcessFrame
} else if (agr && *aOutModifiedAGR != agr) {
CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
return false;
}
}
return true;
}
+static void
+AddFramesForContainingBlock(nsIFrame* aBlock,
+ const nsFrameList& aFrames,
+ nsTArray<nsIFrame*>& aExtraFrames)
+{
+ for (nsIFrame* f : aFrames) {
+ if (!f->IsFrameModified() &&
+ AnyContentAncestorModified(f, aBlock)) {
+ CRR_LOG("Adding invalid OOF %p\n", f);
+ aExtraFrames.AppendElement(f);
+ }
+ }
+}
+
+// Placeholder descendants of aFrame don't contribute to aFrame's overflow area.
+// Find all the containing blocks that might own placeholders under us, walk
+// their OOF frames list, and manually invalidate any frames that are descendants
+// of a modified frame (us, or another frame we'll get to soon).
+// This is combined with the work required for MarkFrameForDisplayIfVisible,
+// so that we can avoid an extra ancestor walk, and we can reuse the flag
+// to detect when we've already visited an ancestor (and thus all further ancestors
+// must also be visited).
+void FindContainingBlocks(nsIFrame* aFrame,
+ nsTArray<nsIFrame*>& aExtraFrames)
+{
+ for (nsIFrame* f = aFrame; f;
+ f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
+ if (f->ForceDescendIntoIfVisible())
+ return;
+ f->SetForceDescendIntoIfVisible(true);
+ CRR_LOG("Considering OOFs for %p\n", f);
+
+ AddFramesForContainingBlock(f, f->GetChildList(nsIFrame::kFloatList), aExtraFrames);
+ AddFramesForContainingBlock(f, f->GetChildList(f->GetAbsoluteListID()), aExtraFrames);
+ }
+}
+
/**
* Given a list of frames that has been modified, computes the region that we need to
* do display list building for in order to build all modified display items.
*
* When a modified frame is within a stacking context (with an existing display item),
* then we only contribute to the build area within the stacking context, as well as forcing
* display list building to descend to the stacking context. We don't need to add build
* area outside of the stacking context (and force items above/below the stacking context
@@ -922,17 +960,28 @@ RetainedDisplayListBuilder::ComputeRebui
AnimatedGeometryRoot** aOutModifiedAGR,
nsTArray<nsIFrame*>& aOutFramesWithProps)
{
CRR_LOG("Computing rebuild regions for %zu frames:\n", aModifiedFrames.Length());
nsTArray<nsIFrame*> extraFrames;
for (nsIFrame* f : aModifiedFrames) {
MOZ_ASSERT(f);
- aBuilder.MarkFrameForDisplayIfVisible(aFrame, aBuilder.RootReferenceFrame());
+ mBuilder.AddFrameMarkedForDisplayIfVisible(f);
+ FindContainingBlocks(f, extraFrames);
+
+ if (!ProcessFrame(f, mBuilder, mBuilder.RootReferenceFrame(),
+ aOutFramesWithProps, true,
+ aOutDirty, aOutModifiedAGR)) {
+ return false;
+ }
+ }
+
+ for (nsIFrame* f : extraFrames) {
+ mBuilder.MarkFrameModifiedDuringBuilding(f);
if (!ProcessFrame(f, mBuilder, mBuilder.RootReferenceFrame(),
aOutFramesWithProps, true,
aOutDirty, aOutModifiedAGR)) {
return false;
}
}
@@ -1066,16 +1115,26 @@ RetainedDisplayListBuilder::AttemptParti
!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
&modifiedAGR, framesWithProps.Frames()) ||
!PreProcessDisplayList(&mList, modifiedAGR)) {
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
mList.ClearDAG();
return PartialUpdateResult::Failed;
}
+ // This is normally handled by EnterPresShell, but we skipped it so that we
+ // didn't call MarkFrameForDisplayIfVisible before ComputeRebuildRegion.
+ nsIScrollableFrame* sf = mBuilder.RootReferenceFrame()->PresShell()->GetRootScrollFrameAsScrollable();
+ if (sf) {
+ nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
+ if (canvasFrame) {
+ mBuilder.MarkFrameForDisplayIfVisible(canvasFrame, mBuilder.RootReferenceFrame());
+ }
+ }
+
modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
PartialUpdateResult result = PartialUpdateResult::NoChange;
if (!modifiedDirty.IsEmpty() ||
!framesWithProps.IsEmpty()) {
result = PartialUpdateResult::Updated;
}
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1069,19 +1069,25 @@ nsDisplayListBuilder::MarkFrameForDispla
if (f == aStopAtFrame) {
// we've reached a frame that we know will be painted, so we can stop.
break;
}
}
}
void
-nsDisplayListBuilder::MarkFrameForDisplayIfVisible(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
+nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame)
{
mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
+}
+
+void
+nsDisplayListBuilder::MarkFrameForDisplayIfVisible(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
+{
+ AddFrameMarkedForDisplayIfVisible(aFrame);
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
if (f->ForceDescendIntoIfVisible())
return;
f->SetForceDescendIntoIfVisible(true);
if (f == aStopAtFrame) {
// we've reached a frame that we know will be painted, so we can stop.
break;
@@ -1282,23 +1288,26 @@ nsDisplayListBuilder::EnterPresShell(nsI
{
PresShellState* state = mPresShellStates.AppendElement();
state->mPresShell = aReferenceFrame->PresShell();
state->mCaretFrame = nullptr;
state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
- if (sf) {
+ if (sf && IsInSubdocument()) {
// We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
// that the canvas background color will be set correctly, and that only one
// unscrollable item will be created.
// This is done to avoid, for example, a case where only scrollbar frames
// are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
// and possibly end up with an extra nsDisplaySolidColor item.
+ // We skip this for the root document, since we don't want to use
+ // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
+ // do it manually there.
nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
if (canvasFrame) {
MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
}
}
#ifdef DEBUG
state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(), eLayoutPhase_DisplayListBuilding);
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -894,16 +894,17 @@ public:
* to ensure that display list construction descends into them
* anyway. nsDisplayListBuilder will take care of unmarking them when it is
* destroyed.
*/
void MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
const nsFrameList& aFrames);
void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame);
void MarkFrameForDisplayIfVisible(nsIFrame* aFrame, nsIFrame* aStopAtFrame);
+ void AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame);
void ClearFixedBackgroundDisplayData();
/**
* Mark all child frames that Preserve3D() as needing display.
* Because these frames include transforms set on their parent, dirty rects
* for intermediate frames may be empty, yet child frames could still be visible.
*/
void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame);
@@ -1470,34 +1471,16 @@ public:
, mDirtyRect(aDirtyRect)
{}
const DisplayItemClipChain* mContainingBlockClipChain;
const DisplayItemClipChain* mCombinedClipChain; // only necessary for the special case of top layer
const ActiveScrolledRoot* mContainingBlockActiveScrolledRoot;
nsRect mVisibleRect;
nsRect mDirtyRect;
- static bool
- AnyContentAncestorModified(nsIFrame* aFrame,
- nsIFrame* aStopAtFrame = nullptr)
- {
- for (nsIFrame* f = aFrame; f;
- f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
- if (f->IsFrameModified()) {
- return true;
- }
-
- if (aStopAtFrame && f == aStopAtFrame) {
- break;
- }
- }
-
- return false;
- }
-
static nsRect ComputeVisibleRectForFrame(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
const nsRect& aVisibleRect,
const nsRect& aDirtyRect,
nsRect* aOutDirtyRect) {
nsRect visible = aVisibleRect;
nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
@@ -1533,23 +1516,16 @@ public:
* prerendered if necessary.
*/
overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
}
visible.IntersectRect(visible, overflowRect);
aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
- // If the nearest stacking context for the modified frame is an ancestor of
- // of it, and if the stacking context is a descendant of the containing block
- // of this OOF frame, we override the dirty rect to ensure that the frame will
- // get marked.
- if (AnyContentAncestorModified(aFrame, aFrame->GetParent())) {
- *aOutDirtyRect = visible;
- }
return visible;
}
nsRect GetVisibleRectForFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsRect* aDirtyRect) {
return ComputeVisibleRectForFrame(aBuilder, aFrame, mVisibleRect, mDirtyRect, aDirtyRect);
}
};