Bug 1476495 - Treat overflow in contain:layout elements as ink overflow. r=dholbert
☠☠ backed out by 29b6e333a0ed ☠ ☠
authorMorgan Rae Reschenberg <mreschenberg@mozilla.com>
Mon, 23 Jul 2018 11:04:50 -0700
changeset 485960 b580e5b9f9e43851d654fc93c6777a655c875c62
parent 485959 6e6e3fb61b3e573751227e249b75a138887baabb
child 485961 32b8e73c790291f91d03e344057234d16e0b8d64
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1476495
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1476495 - Treat overflow in contain:layout elements as ink overflow. r=dholbert 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
--- 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,0-3,0-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