Bug 1459441 - Make sure we build the full display list when we have blend containers in order to get the correct sorting for them. r?mstange
MozReview-Commit-ID: ECTU7enMb1r
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2934,23 +2934,16 @@ nsIFrame::BuildDisplayListForStackingCon
if (HasOverrideDirtyRegion() && !aBuilder->InInvalidSubtree() && !IsFrameModified()) {
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;
if (usingSVGEffects) {
@@ -3102,52 +3095,27 @@ nsIFrame::BuildDisplayListForStackingCon
// 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() &&
+ // We have to repeat building for the entire display list (or at least
+ // the outer stacking context), since we need to mark this frame as invalid
+ // to remove any existing content that isn't wrapped in the blend container,
+ // and then we need to build content infront/behind the blend container
+ // to get correct positioning during merging.
+ if (aBuilder->ContainsBlendMode() &&
aBuilder->IsRetainingDisplayList()) {
- SetBuiltBlendContainer(aBuilder->ContainsBlendMode());
-
- // 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->MarkCurrentFrameModifiedDuringBuilding();
- set.DeleteAll(aBuilder);
-
- if (eventRegions) {
- eventRegions->Destroy(aBuilder);
- eventRegions = MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, this);
- eventRegions->AddFrame(aBuilder, this);
- aBuilder->SetLayerEventRegions(eventRegions);
- }
-
- aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
- set.BorderBackground(),
- true);
-
- // 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);
- aBuilder->Check();
- BuildDisplayList(aBuilder, set);
- aBuilder->Check();
+ aBuilder->SetPartialBuildFailed(true);
+ } else {
+ aBuilder->SetDisablePartialUpdates(true);
}
}
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);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -607,17 +607,16 @@ public:
, mParentIsWrapperAnonBox(false)
, mIsWrapperBoxNeedingRestyle(false)
, mReflowRequestedForCharDataChange(false)
, mForceDescendIntoIfVisible(false)
, mBuiltDisplayList(false)
, mFrameIsModified(false)
, mHasOverrideDirtyRegion(false)
, mMayHaveWillChangeBudget(false)
- , mBuiltBlendContainer(false)
, mIsPrimaryFrame(false)
, mMayHaveTransformAnimation(false)
, mMayHaveOpacityAnimation(false)
, mAllDescendantsAreInvisible(false)
{
mozilla::PodZero(&mOverflow);
}
@@ -4140,19 +4139,16 @@ 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; }
-
/**
* Returns the set of flags indicating the properties of the frame that the
* compositor might care about for hit-testing purposes. Note that this function
* must be called during Gecko display list construction time (i.e while the
* frame tree is being traversed) because that is when the display list builder
* has the necessary state set up correctly.
*/
mozilla::gfx::CompositorHitTestInfo GetCompositorHitTestInfo(nsDisplayListBuilder* aBuilder);
@@ -4340,22 +4336,16 @@ 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;
bool mMayHaveTransformAnimation : 1;
bool mMayHaveOpacityAnimation : 1;
@@ -4367,17 +4357,17 @@ private:
* fact, all descendants are invisible.
* For example; an element is visibility:visible and has a visibility:hidden
* child. This flag is stil false in such case.
*/
bool mAllDescendantsAreInvisible : 1;
protected:
- // There is no gap left here.
+ // There is a 1-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/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -1071,16 +1071,23 @@ RetainedDisplayListBuilder::AttemptParti
if (!modifiedDL.IsEmpty()) {
nsLayoutUtils::AddExtraBackgroundItems(mBuilder, modifiedDL, mBuilder.RootReferenceFrame(),
nsRect(nsPoint(0, 0), mBuilder.RootReferenceFrame()->GetSize()),
mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf(),
aBackstop);
}
mBuilder.SetPartialUpdate(false);
+ if (mBuilder.PartialBuildFailed()) {
+ mBuilder.SetPartialBuildFailed(false);
+ mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
+ mList.ClearDAG();
+ return PartialUpdateResult::Failed;
+ }
+
if (aChecker) {
aChecker->Set(&modifiedDL, "TM");
}
//printf_stderr("Painting --- Modified list (dirty %d,%d,%d,%d):\n",
// modifiedDirty.x, modifiedDirty.y, modifiedDirty.width, modifiedDirty.height);
//nsFrame::PrintDisplayList(&mBuilder, modifiedDL);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -999,17 +999,18 @@ nsDisplayListBuilder::nsDisplayListBuild
mWindowDraggingAllowed(false),
mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
mForceLayerForScrollParent(false),
mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
mBuildingInvisibleItems(false),
mHitTestIsForVisibility(false),
mIsBuilding(false),
mInInvalidSubtree(false),
- mDisablePartialUpdates(false)
+ mDisablePartialUpdates(false),
+ mPartialBuildFailed(false)
{
MOZ_COUNT_CTOR(nsDisplayListBuilder);
const bool useWRHitTest =
gfxPrefs::WebRenderHitTest() && gfxVars::UseWebRender();
mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting() &&
(useWRHitTest || gfxPrefs::SimpleEventRegionItems());
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -839,16 +839,19 @@ public:
* Return true if we're currently building a display list for a
* nested presshell.
*/
bool IsInSubdocument() { return mPresShellStates.Length() > 1; }
void SetDisablePartialUpdates(bool aDisable) { mDisablePartialUpdates = aDisable; }
bool DisablePartialUpdates() { return mDisablePartialUpdates; }
+ void SetPartialBuildFailed(bool aFailed) { mPartialBuildFailed = aFailed; }
+ bool PartialBuildFailed() { return mPartialBuildFailed; }
+
/**
* Return true if we're currently building a display list for the presshell
* of a chrome document, or if we're building the display list for a popup.
*/
bool IsInChromeDocumentOrPopup() {
return mIsInChromePresContext || mIsBuildingForPopup;
}
@@ -2016,16 +2019,17 @@ private:
bool mAsyncPanZoomEnabled;
bool mBuildingInvisibleItems;
bool mHitTestIsForVisibility;
bool mIsBuilding;
bool mInInvalidSubtree;
bool mBuildCompositorHitTestInfo;
bool mLessEventRegionItems;
bool mDisablePartialUpdates;
+ bool mPartialBuildFailed;
};
class nsDisplayItem;
class nsDisplayList;
class RetainedDisplayList;
/**
* nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
* nsDisplayItemLink holds the link. The lists are linked from lowest to
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/invalidated-blendmode-sorting-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ div {
+ width: 200px;
+ height: 200px;
+ }
+ .sort-marker {
+ position: fixed;
+ background-color: green;
+ }
+ .wrapper {
+ position: absolute;
+ isolation: isolate;
+ }
+ .inner {
+ position: absolute;
+ background-color: blue;
+ }
+</style>
+</head>
+<body>
+ <div class="sort-marker"></div>
+ <div class="wrapper">
+ <div class="inner" style="left: 5px; top: 5px; mix-blend-mode:screen"></div>
+ <div class="inner" id="move" style="left: 221px;"></div>
+ </div>
+ <div class="sort-marker" style="left: 20px; top: 20px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/invalidated-blendmode-sorting.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<style>
+ div {
+ width: 200px;
+ height: 200px;
+ }
+ .sort-marker {
+ position: fixed;
+ background-color: green;
+ }
+ .wrapper {
+ position: absolute;
+ isolation: isolate;
+ }
+ .inner {
+ position: absolute;
+ background-color: blue;
+ }
+</style>
+</head>
+<body>
+ <div class="sort-marker"></div>
+ <div class="wrapper">
+ <div class="inner" style="left: 5px; top: 5px; mix-blend-mode:screen"></div>
+ <div class="inner" id="move" style="left: 220px;"></div>
+ </div>
+ <div class="sort-marker" style="left: 20px; top: 20px;"></div>
+</body>
+<script>
+ function doTest() {
+ document.getElementById("move").style.left = "221px";
+ document.documentElement.removeAttribute("class");
+ }
+ document.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -5,16 +5,17 @@ skip-if(!retainedDisplayList) == retaine
skip-if(!retainedDisplayList||!asyncPan) == retained-dl-async-scrolled-1.html retained-dl-async-scrolled-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-remove-for-ancestor-change-1.html retained-dl-remove-for-ancestor-change-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-scroll-out-of-view-1.html retained-dl-scroll-out-of-view-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-displayport-1.html retained-dl-displayport-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-prerender-transform-1.html retained-dl-prerender-transform-1-ref.html
== retained-dl-wrap-list.html retained-dl-wrap-list-ref.html
== retained-dl-zindex-1.html retained-dl-zindex-1-ref.html
== retained-dl-zindex-2.html retained-dl-zindex-2-ref.html
+== invalidated-blendmode-sorting.html invalidated-blendmode-sorting-ref.html
fuzzy(1,235200) == 1413073.html 1413073-ref.html
== 1416291.html 1416291-ref.html
== 1417601-1.html 1417601-1-ref.html
== 1418945-1.html 1418945-1-ref.html
skip-if(Android) == 1428993-1.html 1428993-1-ref.html
== 1420480-1.html 1420480-1-ref.html
== 1428993-2.html 1428993-2-ref.html
needs-focus == 1429027-1.html 1429027-1-ref.html