Bug 1404181 - Part 24: Rebuild all display items when we encounter a blend mode, since we can't easily track changes to whether we need the blend container or not. r?mstange
MozReview-Commit-ID: 9vw1vgDUp60
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2625,16 +2625,24 @@ nsIFrame::BuildDisplayListForStackingCon
if (HasOverrideDirtyRegion() && !aBuilder->InInvalidSubtree()) {
nsDisplayListBuilder::DisplayListBuildingData* data =
GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
if (data) {
dirtyRect = data->mDirtyRect.Intersect(visibleRect);
hasOverrideDirtyRect = true;
}
}
+ // Always build the entire display list if we previously had a blend
+ // container since a partial build might make us think we no longer
+ // need the container even though the merged result will.
+ if (aBuilder->IsRetainingDisplayList() && BuiltBlendContainer()) {
+ dirtyRect = visibleRect;
+ aBuilder->MarkFrameModifiedDuringBuilding(this);
+ }
+
bool usingFilter = StyleEffects()->HasFilters();
bool usingMask = nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
bool usingSVGEffects = usingFilter || usingMask;
nsRect visibleRectOutsideSVGEffects = visibleRect;
nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
if (usingSVGEffects) {
dirtyRect =
@@ -2744,26 +2752,75 @@ nsIFrame::BuildDisplayListForStackingCon
// extend3DContext also guarantees that applyAbsPosClipping and usingSVGEffects are false
// We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
if (extend3DContext) {
// Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
// going to be forced to descend into frames.
aBuilder->MarkPreserve3DFramesForDisplayList(this);
}
- MarkAbsoluteFramesForDisplayList(aBuilder);
+ aBuilder->AdjustWindowDraggingRegion(this);
nsDisplayLayerEventRegions* eventRegions = nullptr;
if (aBuilder->IsBuildingLayerEventRegions()) {
eventRegions = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
eventRegions->AddFrame(aBuilder, this);
aBuilder->SetLayerEventRegions(eventRegions);
}
- aBuilder->AdjustWindowDraggingRegion(this);
+
+ MarkAbsoluteFramesForDisplayList(aBuilder);
BuildDisplayList(aBuilder, set);
+
+ // Blend modes are a real pain for retained display lists. We build a blend
+ // container item if the built list contains any blend mode items within
+ // the current stacking context. This can change without an invalidation
+ // to the stacking context frame, or the blend mode frame (e.g. by moving
+ // an intermediate frame).
+ // When we gain/remove a blend container item, we need to mark this frame
+ // as invalid and have the full display list for merging to track
+ // the change correctly.
+ // It seems really hard to track this in advance, as the bookkeeping
+ // required to note which stacking contexts have blend descendants
+ // is complex and likely to be buggy.
+ // Instead we're doing the sad thing, detecting it afterwards, and just
+ // repeating display list building if it changed.
+
+ // If we changed whether we're going to build a blend mode item,
+ // then we need to make sure we're marked as invalid and we've built
+ // the full display list.
+ if (aBuilder->ContainsBlendMode() != BuiltBlendContainer() &&
+ aBuilder->IsRetainingDisplayList()) {
+ SetBuiltBlendContainer(aBuilder->ContainsBlendMode());
+ aBuilder->MarkFrameModifiedDuringBuilding(this);
+
+ // If we did a partial build then delete all the items we just built
+ // and repeat building with the full area.
+ if (!aBuilder->GetDirtyRect().Contains(aBuilder->GetVisibleRect())) {
+ aBuilder->SetDirtyRect(aBuilder->GetVisibleRect());
+ set.DeleteAll(aBuilder);
+
+ if (eventRegions) {
+ eventRegions->Destroy(aBuilder);
+ eventRegions = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
+ eventRegions->AddFrame(aBuilder, this);
+ aBuilder->SetLayerEventRegions(eventRegions);
+ }
+
+ // If this is the root frame, then the previous call to
+ // MarkAbsoluteFramesForDisplayList might have stored some fixed
+ // background data. Clear that now.
+ if (!GetParent()) {
+ aBuilder->ClearFixedBackgroundDisplayData();
+ }
+
+ MarkAbsoluteFramesForDisplayList(aBuilder);
+ BuildDisplayList(aBuilder, set);
+ }
+ }
+
if (eventRegions) {
// If the event regions item ended up empty, throw it away rather than
// adding it to the display list.
if (!eventRegions->IsEmpty()) {
set.BorderBackground()->AppendToBottom(eventRegions);
} else {
aBuilder->SetLayerEventRegions(nullptr);
eventRegions->Destroy(aBuilder);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -624,16 +624,17 @@ public:
, mParentIsWrapperAnonBox(false)
, mIsWrapperBoxNeedingRestyle(false)
, mReflowRequestedForCharDataChange(false)
, mForceDescendIntoIfVisible(false)
, mBuiltDisplayList(false)
, mFrameIsModified(false)
, mHasOverrideDirtyRegion(false)
, mMayHaveWillChangeBudget(false)
+ , mBuiltBlendContainer(false)
, mIsPrimaryFrame(false)
{
mozilla::PodZero(&mOverflow);
}
nsPresContext* PresContext() const {
return StyleContext()->PresContext();
}
@@ -4119,16 +4120,19 @@ public:
void SetFrameIsModified(bool aFrameIsModified) { mFrameIsModified = aFrameIsModified; }
bool HasOverrideDirtyRegion() { return mHasOverrideDirtyRegion; }
void SetHasOverrideDirtyRegion(bool aHasDirtyRegion) { mHasOverrideDirtyRegion = aHasDirtyRegion; }
bool MayHaveWillChangeBudget() { return mMayHaveWillChangeBudget; }
void SetMayHaveWillChangeBudget(bool aHasBudget) { mMayHaveWillChangeBudget = aHasBudget; }
+ bool BuiltBlendContainer() { return mBuiltBlendContainer; }
+ void SetBuiltBlendContainer(bool aBuilt) { mBuiltBlendContainer = aBuilt; }
+
protected:
/**
* Reparent this frame's view if it has one.
*/
void ReparentFrameViewTo(nsViewManager* aViewManager,
nsView* aNewParentView,
nsView* aOldParentView);
@@ -4305,25 +4309,31 @@ protected:
bool mHasOverrideDirtyRegion : 1;
/**
* True if frame has will-change, and currently has display
* items consuming some of the will-change budget.
*/
bool mMayHaveWillChangeBudget : 1;
+ /**
+ * True if we built an nsDisplayBlendContainer last time
+ * we did retained display list building for this frame.
+ */
+ bool mBuiltBlendContainer : 1;
+
private:
/**
* True if this is the primary frame for mContent.
*/
bool mIsPrimaryFrame : 1;
protected:
- // There is a 4-bit gap left here.
+ // There is a 3-bit gap left here.
// Helpers
/**
* Can we stop inside this frame when we're skipping non-rendered whitespace?
* @param aForward [in] Are we moving forward (or backward) in content order.
* @param aOffset [in/out] At what offset into the frame to start looking.
* on output - what offset was reached (whether or not we found a place to stop).
* @return STOP: An appropriate offset was found within this frame,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1385,16 +1385,22 @@ nsDisplayListBuilder::ResetMarkedFramesF
for (uint32_t i = firstFrameForShell;
i < mFramesMarkedForDisplay.Length(); ++i) {
UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
}
mFramesMarkedForDisplay.SetLength(firstFrameForShell);
}
void
+nsDisplayListBuilder::ClearFixedBackgroundDisplayData()
+{
+ CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
+}
+
+void
nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
const nsFrameList& aFrames) {
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();
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -825,16 +825,17 @@ public:
* 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 = nullptr);
void MarkFrameForDisplayIfVisible(nsIFrame* aFrame, nsIFrame* aStopAtFrame = nullptr);
+ 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);
/**