Bug 1417601 - Override dirty rect for OOF frames with containing block outside a modified stacking context draft
authorMiko Mynttinen <mikokm@gmail.com>
Thu, 16 Nov 2017 16:09:16 +0100
changeset 699757 030de4a6f6e5be041b29855f0b92aa2bacfb19ea
parent 699096 a3f183201f7f183c263d554bfb15fbf0b0ed2ea4
child 740712 969842f01bbfadd7d144f282a78e4b7a462a17de
push id89662
push userbmo:mikokm@gmail.com
push dateFri, 17 Nov 2017 17:35:25 +0000
bugs1417601
milestone59.0a1
Bug 1417601 - Override dirty rect for OOF frames with containing block outside a modified stacking context MozReview-Commit-ID: DqvdDECU7dM
layout/painting/nsDisplayList.cpp
layout/reftests/display-list/1417601-1-ref.html
layout/reftests/display-list/1417601-1.html
layout/reftests/display-list/reftest.list
--- 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