Bug 1417601 - Override dirty rect for OOF frames with containing block outside a modified stacking context
MozReview-Commit-ID: DqvdDECU7dM
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1110,22 +1110,40 @@ nsDisplayListBuilder::FindAnimatedGeomet
aItem->Frame(), LayoutFrameType::Viewport, RootReferenceFrame());
if (viewportFrame) {
return FindAnimatedGeometryRootFor(viewportFrame);
}
}
return FindAnimatedGeometryRootFor(aItem->Frame());
}
+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;
+}
void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
nsIFrame* aFrame)
{
nsRect visible = GetVisibleRect();
nsRect dirtyRectRelativeToDirtyFrame = GetDirtyRect();
+
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->PresShell();
if (ps->IsScrollPositionClampingScrollPortSizeSet()) {
@@ -1159,16 +1177,24 @@ void nsDisplayListBuilder::MarkOutOfFlow
visible.IntersectRect(visible, overflowRect);
dirty.IntersectRect(dirty, overflowRect);
if (!(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
visible.IsEmpty()) {
return;
}
+ // 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, aDirtyFrame)) {
+ dirty = visible;
+ }
+
// Always store OutOfFlowDisplayData for visible frames (even when they aren't dirty)
// since a nested out-of-flow frame might force building and we'll need to have this
// data available.
// mClipState.GetClipChainForContainingBlockDescendants can return pointers
// to objects on the stack, so we need to clone the chain.
const DisplayItemClipChain* clipChain =
CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1417601-1-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+
+<style>
+#modified {
+ left: 10px;
+}
+</style>
+
+</head>
+<body>
+<div style="opacity:0.9">
+ <div style="position:fixed; left:300px; top:300px; width:200px; height:200px; background-color:red; z-index:-10"></div>
+ <div style="width:200px; height:200px; position:relative; background-color:green" id="modified">
+ <div style="opacity:0.9">
+ <div style="position:fixed; left:300px; top:300px; width:200px; height:200px; background-color:green; z-index:-10"></div>
+ </div>
+ </div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1417601-1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+
+<style>
+#modified {
+ left: 0px;
+}
+</style>
+
+<script type="text/javascript">
+function doTest() {
+ document.getElementById("modified").style.left = "10px";
+ document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+
+</head>
+<body>
+<div style="opacity:0.9">
+ <div style="position:fixed; left:300px; top:300px; width:200px; height:200px; background-color:red; z-index:-10"></div>
+ <div style="width:200px; height:200px; position:relative; background-color:green" id="modified">
+ <div style="opacity:0.9">
+ <div style="position:fixed; left:300px; top:300px; width:200px; height:200px; background-color:green; z-index:-10"></div>
+ </div>
+ </div>
+</div>
+</body>
+</html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -5,8 +5,9 @@ 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
fuzzy(1,235200) == 1413073.html 1413073-ref.html
== 1416291.html 1416291-ref.html
+== 1417601-1.html 1417601-1-ref.html