Bug 1476495 - Treat overflow in contain:layout elements as ink overflow.
MozReview-Commit-ID: 2gRm0LOUTI6
--- 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