Bug 1476495 - Treat overflow in contain:layout elements as ink overflow. draft
authorMorgan Rae Reschenberg <mreschenberg@mozilla.com>
Mon, 23 Jul 2018 11:04:50 -0700
changeset 828145 befd4d1f375b2764501683e6bbf0e30c787ed0db
parent 827948 f650c0df72f9f1dc616bb1510a36f894400e8b84
push id118645
push userbmo:mreschenberg@berkeley.edu
push dateFri, 10 Aug 2018 05:35:32 +0000
bugs1476495
milestone63.0a1
Bug 1476495 - Treat overflow in contain:layout elements as ink overflow. MozReview-Commit-ID: 2gRm0LOUTI6
layout/generic/nsBlockFrame.cpp
layout/generic/nsFrame.cpp
layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-001-ref.html
layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-001.html
layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-002-ref.html
layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-002.html
layout/reftests/w3c-css/submitted/contain/reftest.list
layout/style/nsStyleStruct.h
testing/web-platform/meta/css/css-contain/contain-layout-015.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-013.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-014.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-015.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-016.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-017.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-018.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-019.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-020.html.ini
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1785,78 +1785,112 @@ nsBlockFrame::ComputeFinalSize(const Ref
     printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
   }
 #endif
 }
 
 static void
 ConsiderBlockEndEdgeOfChildren(const WritingMode aWritingMode,
                                nscoord aBEndEdgeOfChildren,
-                               nsOverflowAreas& aOverflowAreas)
+                               nsOverflowAreas& aOverflowAreas,
+                               const nsStyleDisplay* aDisplay)
 {
   // Factor in the block-end edge of the children.  Child frames will be added
   // to the overflow area as we iterate through the lines, but their margins
   // won't, so we need to account for block-end margins here.
   // REVIEW: For now, we do this for both visual and scrollable area,
   // although when we make scrollable overflow area not be a subset of
   // visual, we can change this.
   // XXX Currently, overflow areas are stored as physical rects, so we have
   // to handle writing modes explicitly here. If we change overflow rects
   // to be stored logically, this can be simplified again.
   if (aWritingMode.IsVertical()) {
     if (aWritingMode.IsVerticalLR()) {
       NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
-        nsRect& o = aOverflowAreas.Overflow(otype);
-        o.width = std::max(o.XMost(), aBEndEdgeOfChildren) - o.x;
+        if (!(aDisplay->IsContainLayout() && otype == eScrollableOverflow)) {
+          // Layout containment should force all overflow to be ink (visual)
+          // overflow, so if we're layout-contained, we only add our children's
+          // block-end edge to the ink (visual) overflow -- not to the
+          // scrollable overflow.
+          nsRect& o = aOverflowAreas.Overflow(otype);
+          o.width = std::max(o.XMost(), aBEndEdgeOfChildren) - o.x;
+        }
       }
     } else {
       NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
-        nsRect& o = aOverflowAreas.Overflow(otype);
-        nscoord xmost = o.XMost();
-        o.x = std::min(o.x, xmost - aBEndEdgeOfChildren);
-        o.width = xmost - o.x;
+        if (!(aDisplay->IsContainLayout() && otype == eScrollableOverflow)) {
+          nsRect& o = aOverflowAreas.Overflow(otype);
+          nscoord xmost = o.XMost();
+          o.x = std::min(o.x, xmost - aBEndEdgeOfChildren);
+          o.width = xmost - o.x;
+        }
       }
     }
   } else {
     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
-      nsRect& o = aOverflowAreas.Overflow(otype);
-      o.height = std::max(o.YMost(), aBEndEdgeOfChildren) - o.y;
+      if (!(aDisplay->IsContainLayout() && otype == eScrollableOverflow)) {
+        nsRect& o = aOverflowAreas.Overflow(otype);
+        o.height = std::max(o.YMost(), aBEndEdgeOfChildren) - o.y;
+      }
     }
   }
 }
 
 void
 nsBlockFrame::ComputeOverflowAreas(const nsRect&         aBounds,
                                    const nsStyleDisplay* aDisplay,
                                    nscoord               aBEndEdgeOfChildren,
                                    nsOverflowAreas&      aOverflowAreas)
 {
   // Compute the overflow areas of our children
   // XXX_perf: This can be done incrementally.  It is currently one of
   // the things that makes incremental reflow O(N^2).
   nsOverflowAreas areas(aBounds, aBounds);
+  if (mComputedStyle->GetPseudo() == nsCSSAnonBoxes::scrolledContent &&
+      mParent->StyleDisplay()->IsContainLayout()) {
+    // If we are a scrollframe's inner anonymous box and our parent
+    // has layout containment, we want to pass our parent's style to
+    // ConsiderBlockEndEdgeOfChildren to make sure all overflow from the
+    // layout contained element is processed as ink (visual) overflow.
+    aDisplay = mParent->StyleDisplay();
+  }
   if (!ShouldApplyOverflowClipping(this, aDisplay)) {
     for (LineIterator line = LinesBegin(), line_end = LinesEnd();
          line != line_end;
          ++line) {
-      areas.UnionWith(line->GetOverflowAreas());
+      if (aDisplay->IsContainLayout()) {
+        // If we have layout containment (or, per above, we are a scrollframe's
+        // inner anonymous box and our parent has layout containment), we should
+        // only consider our child's visual overflow, leaving the scrollable
+        // regions of the parent unaffected.
+        // Note: scrollable overflow is a subset of visual overflow,
+        // so this has the same affect as unioning the child's visual and
+        // scrollable overflow with its parent's visual overflow.
+        nsRect childVisualRect = line->GetVisualOverflowArea();
+        nsOverflowAreas childVisualArea = nsOverflowAreas(
+          childVisualRect,
+          nsRect());
+        areas.UnionWith(childVisualArea);
+      } else {
+        areas.UnionWith(line->GetOverflowAreas());
+      }
     }
 
     // Factor an outside bullet in; normally the bullet will be factored into
     // the line-box's overflow areas. However, if the line is a block
     // line then it won't; if there are no lines, it won't. So just
     // factor it in anyway (it can't hurt if it was already done).
     // XXXldb Can we just fix GetOverflowArea instead?
     nsIFrame* outsideBullet = GetOutsideBullet();
     if (outsideBullet) {
       areas.UnionAllWith(outsideBullet->GetRect());
     }
 
     ConsiderBlockEndEdgeOfChildren(GetWritingMode(),
-                                   aBEndEdgeOfChildren, areas);
+                                   aBEndEdgeOfChildren, areas, aDisplay);
   }
 
 #ifdef NOISY_COMBINED_AREA
   ListTag(stdout);
   const nsRect& vis = areas.VisualOverflow();
   printf(": VisualOverflowArea CA=%d,%d,%d,%d\n", vis.x, vis.y, vis.width, vis.height);
   const nsRect& scr = areas.ScrollableOverflow();
   printf(": ScrollableOverflowArea CA=%d,%d,%d,%d\n", scr.x, scr.y, scr.width, scr.height);
@@ -1904,17 +1938,18 @@ nsBlockFrame::UnionChildOverflow(nsOverf
 bool
 nsBlockFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
 {
   bool found;
   nscoord blockEndEdgeOfChildren =
     GetProperty(BlockEndEdgeOfChildrenProperty(), &found);
   if (found) {
     ConsiderBlockEndEdgeOfChildren(GetWritingMode(),
-                                   blockEndEdgeOfChildren, aOverflowAreas);
+                                   blockEndEdgeOfChildren, aOverflowAreas,
+                                   StyleDisplay());
   }
 
   // Line cursor invariants depend on the overflow areas of the lines, so
   // we must clear the line cursor since those areas may have changed.
   ClearLineCursor();
   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
 }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -9673,18 +9673,42 @@ nsIFrame::GetDepthInFrameTree() const
   }
   return result;
 }
 
 void
 nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
                                nsIFrame* aChildFrame)
 {
-  aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
-                           aChildFrame->GetPosition());
+  const nsStyleDisplay* display = StyleDisplay();
+  if (mComputedStyle->GetPseudo() == nsCSSAnonBoxes::scrolledContent) {
+    // If we are a scrollframe's inner anonymous box, we'll want to check if
+    // our parent has contain:layout below, so we change the nsStyleDisplay we
+    // read from here.
+    display = mParent->StyleDisplay();
+  }
+  if (display->IsContainLayout() && IsFrameOfType(eSupportsContainLayoutAndPaint)) {
+    // If we have layout containment and are not a non-atomic, inline-level
+    // principal box (or, if we are a scrollframe's inner anonymous box and
+    // our parent has layout containment) we should only consider our child's
+    // visual (ink) overflow, leaving the scrollable regions of the parent
+    // unaffected.
+    // Note: scrollable overflow is a subset of visual overflow,
+    // so this has the same affect as unioning the child's visual and
+    // scrollable overflow with the parent's visual overflow.
+    // XXX doesn't work correctly for floats - bug 1481951
+    nsRect childVisual = aChildFrame->GetVisualOverflowRect();
+    nsOverflowAreas combined = nsOverflowAreas(
+      childVisual,
+      nsRect());
+    aOverflowAreas.UnionWith(combined + aChildFrame->GetPosition());
+  } else {
+    aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
+                             aChildFrame->GetPosition());
+  }
 }
 
 bool
 nsFrame::ShouldAvoidBreakInside(const ReflowInput& aReflowInput) const
 {
   const auto* disp = StyleDisplay();
   return !aReflowInput.mFlags.mIsTopOfPage &&
     NS_STYLE_PAGE_BREAK_AVOID == disp->mBreakInside &&
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-001-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg-1 {
+    height: 95px;
+    width: 95px;
+    background: lightblue;
+  }
+  .inner-lg-2 {
+    height: 200px;
+    width: 200px;
+  }
+  .pass {
+    background: green;
+  }
+  .border {
+    border: 5px solid green;
+  }
+
+  </style>
+</head>
+<body>
+  <div class="outer">
+    <div class="inner-sm"></div>
+  </div>
+  <br>
+
+  <div class="outer auto">
+    <div class="inner-lg-2 pass">
+    </div>
+  </div>
+  <br>
+
+  <div class="inner-sm border">
+    <div class="inner-lg-1">
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-001.html
@@ -0,0 +1,71 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' should force all overflow to be ink overflow.</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel="match" href="contain-layout-overflow-001-ref.html">
+  <style>
+  .contain {
+    contain: layout;
+  }
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg {
+    height: 200px;
+    width: 200px;
+    background: lightblue;
+  }
+  .pass {
+    background: green;
+  }
+  .fail {
+    background: red;
+  }
+  .border {
+    border: 5px solid green;
+  }
+  </style>
+</head>
+<body>
+  <!--CSS Test: Elements with contain:layout that do not produce scrollable overflow should paint as if containment were not applied. -->
+  <div class="outer">
+    <div class="inner-sm contain"></div>
+  </div>
+  <br>
+
+  <!--CSS Test: Layout-contained elements that overflow their container and have children who overflow should produce the same amount of scrollable overflow as if there were no children. -->
+  <div class="outer auto">
+    <div class="inner-lg contain">
+      <div class="inner-lg pass"></div>
+      <div class="inner-lg fail"></div>
+    </div>
+  </div>
+  <br>
+
+  <!--CSS Test: Layout-contained elements that do not overflow their container, but have children who overflow, should not allow their children to affect the scrollable overflow regions of their parent. -->
+  <div class="outer auto">
+    <div class="inner-sm contain border">
+      <div class="inner-lg">
+      </div>
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-002-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg-1 {
+    height: 95px;
+    width: 95px;
+    float:left;
+    background: lightblue;
+  }
+  .inner-lg-2 {
+    height: 200px;
+    width: 200px;
+    float:left;
+  }
+  .pass {
+    background: green;
+  }
+  .border {
+    border: 5px solid green;
+  }
+
+  </style>
+</head>
+<body>
+  <div class="outer">
+    <div class="inner-sm" style="float:left;"></div>
+  </div>
+  <br>
+
+  <div class="outer auto">
+    <div class="inner-lg-2 pass">
+    </div>
+  </div>
+  <br>
+
+  <div class="inner-sm border">
+    <div class="inner-lg-1">
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/contain/contain-layout-overflow-002.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' should force all overflow to be ink overflow.</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel="match" href="contain-layout-overflow-002-ref.html">
+  <style>
+  .contain {
+    contain: layout;
+  }
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg {
+    height: 200px;
+    width: 200px;
+    background: lightblue;
+    float: left;
+  }
+  .pass {
+    background: green;
+  }
+  .fail {
+    background: red;
+  }
+  .border {
+    border: 5px solid green;
+  }
+  </style>
+</head>
+<body>
+  <!--CSS Test: Elements with contain:layout that do not produce scrollable overflow should paint as if containment were not applied. -->
+  <div class="outer">
+    <div class="inner-sm contain" style="float:left;"></div>
+  </div>
+  <br>
+
+  <!--CSS Test: Layout-contained elements that overflow their container and have children who overflow should produce the same amount of scrollable overflow as if there were no children. -->
+  <div class="outer auto">
+    <div class="outer contain">
+      <div class="inner-lg pass"></div>
+      <div class="inner-lg fail"></div>
+    </div>
+  </div>
+  <br>
+
+
+  <!--CSS Test: Layout-contained elements that do not overflow their container, but have children who overflow, should not allow their children to affect the scrollable overflow regions of their parent. -->
+  <div class="outer auto">
+    <div class="inner-sm contain border">
+      <div class="inner-lg">
+      </div>
+    </div>
+  </div>
+</body>
+</html>
--- a/layout/reftests/w3c-css/submitted/contain/reftest.list
+++ b/layout/reftests/w3c-css/submitted/contain/reftest.list
@@ -22,9 +22,11 @@ pref(layout.css.overflow-clip-box.enable
 == contain-size-inline-block-001.html contain-size-inline-block-001-ref.html
 == contain-size-flex-001.html contain-size-flex-001-ref.html
 fuzzy-if(webrender&&winWidget,3,2) == contain-size-inline-flex-001.html contain-size-inline-flex-001-ref.html # bug 1474093
 == contain-size-multicol-001.html contain-size-multicol-001-ref.html
 == contain-size-fieldset-001.html contain-size-fieldset-001-ref.html
 == contain-size-fieldset-002.html contain-size-fieldset-002-ref.html
 == contain-size-multicol-002.html contain-size-multicol-002-ref.html
 == contain-size-multicol-003.html contain-size-multicol-003-ref.html
+== contain-layout-overflow-001.html contain-layout-overflow-001-ref.html
+fails == contain-layout-overflow-002.html contain-layout-overflow-002-ref.html # bug 1481951
 == contain-size-table-caption-001.html contain-size-table-caption-001-ref.html
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2342,16 +2342,29 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   }
 
   bool IsContainPaint() const {
     return (NS_STYLE_CONTAIN_PAINT & mContain) &&
            !IsInternalRubyDisplayType() &&
            !IsInternalTableStyleExceptCell();
   }
 
+  bool IsContainLayout() const {
+    // Note: The spec for layout containment says it should
+    // have no effect on non-atomic, inline-level boxes. We
+    // don't check for these here because we don't know
+    // what type of element is involved. Callers are
+    // responsible for checking if the box in question is
+    // non-atomic and inline-level, and creating an
+    // exemption as necessary.
+    return (NS_STYLE_CONTAIN_LAYOUT & mContain) &&
+            !IsInternalRubyDisplayType() &&
+            !IsInternalTableStyleExceptCell();
+  }
+
   bool IsContainSize() const {
     // Note: The spec for size containment says it should
     // have no effect on non-atomic, inline-level boxes. We
     // don't check for these here because we don't know
     // what type of element is involved. Callers are
     // responsible for checking if the box in question is
     // non-atomic and inline-level, and creating an
     // exemption as necessary.
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-015.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-015.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-013.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-013.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-014.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-014.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-015.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-015.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-016.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-016.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-017.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-017.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-018.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-018.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-019.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-019.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-layout-ink-overflow-020.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-layout-ink-overflow-020.html]
-  expected: FAIL