Skip to content

Commit 29e732f

Browse files
committed
Bug 1942036 - Don't ignore negative margins for scroll{Width,Height} on overflow:visible frames. r=dshin
Differential Revision: https://phabricator.services.mozilla.com/D234711
1 parent 345ec3c commit 29e732f

File tree

3 files changed

+91
-24
lines changed

3 files changed

+91
-24
lines changed

layout/generic/nsBlockFrame.cpp

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,15 @@ static nscolor GetBackplateColor(nsIFrame* aFrame) {
270270
return NS_ComposeColors(backgroundColor, currentBackgroundColor);
271271
}
272272

273-
static nsRect GetNormalMarginRect(nsIFrame* aFrame) {
274-
auto rect = aFrame->GetMarginRectRelativeToSelf();
275-
return rect + aFrame->GetNormalPosition();
273+
static nsRect GetNormalMarginRect(const nsIFrame& aFrame,
274+
bool aIncludePositiveMargins = true) {
275+
nsMargin m = aFrame.GetUsedMargin().ApplySkipSides(aFrame.GetSkipSides());
276+
if (!aIncludePositiveMargins) {
277+
m.EnsureAtMost(nsMargin());
278+
}
279+
auto rect = aFrame.GetRectRelativeToSelf();
280+
rect.Inflate(m);
281+
return rect + aFrame.GetNormalPosition();
276282
}
277283

278284
#ifdef DEBUG
@@ -2600,7 +2606,7 @@ nsRect nsBlockFrame::ComputePaddingInflatedScrollableOverflow(
26002606

26012607
Maybe<nsRect> nsBlockFrame::GetLineFrameInFlowBounds(
26022608
const nsLineBox& aLine, const nsIFrame& aLineChildFrame,
2603-
bool aConsiderMargins) const {
2609+
bool aConsiderPositiveMargins) const {
26042610
MOZ_ASSERT(aLineChildFrame.GetParent() == this,
26052611
"Line's frame doesn't belong to this block frame?");
26062612
// Line participants are considered in-flow for content within the line
@@ -2612,9 +2618,7 @@ Maybe<nsRect> nsBlockFrame::GetLineFrameInFlowBounds(
26122618
return Nothing{};
26132619
}
26142620
if (aLine.IsInline()) {
2615-
auto rect = aConsiderMargins ? aLineChildFrame.GetMarginRectRelativeToSelf()
2616-
: aLineChildFrame.GetRectRelativeToSelf();
2617-
return Some(rect + aLineChildFrame.GetNormalPosition());
2621+
return Some(GetNormalMarginRect(aLineChildFrame, aConsiderPositiveMargins));
26182622
}
26192623
const auto wm = GetWritingMode();
26202624
auto rect = aLineChildFrame.GetRectRelativeToSelf();
@@ -2626,14 +2630,19 @@ Maybe<nsRect> nsBlockFrame::GetLineFrameInFlowBounds(
26262630
const auto normalPosition = aLineChildFrame.GetLogicalSize(wm).BSize(wm) == 0
26272631
? linePoint
26282632
: aLineChildFrame.GetNormalPosition();
2629-
if (aConsiderMargins) {
2630-
// Ensure we use the margin we actually carried out.
2633+
// Ensure we use the margin we actually carried out.
2634+
nsMargin margin;
2635+
if (aConsiderPositiveMargins) {
26312636
auto logicalMargin = aLineChildFrame.GetLogicalUsedMargin(wm);
26322637
logicalMargin.BEnd(wm) = aLine.GetCarriedOutBEndMargin().Get();
2633-
const auto margin = logicalMargin.GetPhysicalMargin(wm).ApplySkipSides(
2638+
margin = logicalMargin.GetPhysicalMargin(wm).ApplySkipSides(
2639+
aLineChildFrame.GetSkipSides());
2640+
} else {
2641+
margin = aLineChildFrame.GetUsedMargin().ApplySkipSides(
26342642
aLineChildFrame.GetSkipSides());
2635-
rect.Inflate(margin);
2643+
margin.EnsureAtMost(nsMargin());
26362644
}
2645+
rect.Inflate(margin);
26372646
return Some(rect + normalPosition);
26382647
}
26392648

@@ -2653,7 +2662,7 @@ void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
26532662
// happen only for overflow: visible), so that we don't incorrectly account
26542663
// for margins that otherwise collapse through, see bug 1936156. Note that
26552664
// ::-moz-scrolled-content is always a BFC (see `AnonymousBoxIsBFC`).
2656-
const bool considerMarginsForInFlowChildBounds =
2665+
const bool considerPositiveMarginsForInFlowChildBounds =
26572666
isScrolled && HasAnyStateBits(NS_BLOCK_BFC);
26582667

26592668
// Relying on aOverflowAreas having been set to frame border rect (if
@@ -2686,7 +2695,7 @@ void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
26862695
}
26872696

26882697
if (auto lineFrameBounds = GetLineFrameInFlowBounds(
2689-
line, *lineFrame, considerMarginsForInFlowChildBounds)) {
2698+
line, *lineFrame, considerPositiveMarginsForInFlowChildBounds)) {
26902699
inFlowChildBounds = inFlowChildBounds.UnionEdges(*lineFrameBounds);
26912700
}
26922701
}
@@ -2698,9 +2707,8 @@ void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
26982707
if (inkOverflowOnly || !isScrolled) {
26992708
continue;
27002709
}
2701-
auto rect = considerMarginsForInFlowChildBounds
2702-
? GetNormalMarginRect(f)
2703-
: f->GetRectRelativeToSelf() + f->GetNormalPosition();
2710+
auto rect = GetNormalMarginRect(
2711+
*f, considerPositiveMarginsForInFlowChildBounds);
27042712
inFlowChildBounds = inFlowChildBounds.UnionEdges(rect);
27052713
}
27062714
}
@@ -5774,12 +5782,11 @@ bool nsBlockFrame::PlaceLine(BlockReflowState& aState,
57745782
lineOverflowAreas.UnionWith(aLine->GetOverflowAreas());
57755783
aLine->SetOverflowAreas(lineOverflowAreas);
57765784
if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
5777-
auto itr = aLine->Floats().begin();
5785+
Span<const nsIFrame* const> floats(aLine->Floats());
57785786
// Guaranteed to have at least 1 element since `HasFloats()` is true.
5779-
auto floatRect = GetNormalMarginRect(*itr);
5780-
++itr;
5781-
for (; itr != aLine->Floats().end(); ++itr) {
5782-
floatRect = floatRect.UnionEdges(GetNormalMarginRect(*itr));
5787+
auto floatRect = GetNormalMarginRect(*floats[0]);
5788+
for (const nsIFrame* f : floats.From(1)) {
5789+
floatRect = floatRect.UnionEdges(GetNormalMarginRect(*f));
57835790
}
57845791
auto inFlowBounds = aLine->GetInFlowChildBounds();
57855792
aLine->SetInFlowChildBounds(

layout/generic/nsBlockFrame.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,9 @@ class nsBlockFrame : public nsContainerFrame {
265265

266266
nsRect ComputePaddingInflatedScrollableOverflow(
267267
const nsRect& aInFlowChildBounds) const;
268-
Maybe<nsRect> GetLineFrameInFlowBounds(const nsLineBox& aLine,
269-
const nsIFrame& aLineChildFrame,
270-
bool aConsiderMargins = true) const;
268+
Maybe<nsRect> GetLineFrameInFlowBounds(
269+
const nsLineBox& aLine, const nsIFrame& aLineChildFrame,
270+
bool aConsiderPositiveMargins = true) const;
271271

272272
template <typename LineIteratorType>
273273
Maybe<nscoord> GetBaselineBOffset(LineIteratorType aStart,
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!doctype html>
2+
<meta charset="utf-8">
3+
<meta name="viewport" content="width=device-width, initial-scale=1">
4+
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
5+
<link rel="author" title="Mozilla" href="https://mozilla.org">
6+
<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth">
7+
<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollheight">
8+
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1936156">
9+
<title>scroll{Width,Height} shouldn't account for collapsed margins, in order not to report unnecessary overflow</title>
10+
<script src="/resources/testharness.js"></script>
11+
<script src="/resources/testharnessreport.js"></script>
12+
<style>
13+
#target {
14+
width: 20px;
15+
flex-direction: column;
16+
}
17+
#target > div {
18+
background-color: green;
19+
margin: -5px -7px;
20+
}
21+
22+
#target > div > div {
23+
height: 20px;
24+
width: 20px;
25+
}
26+
</style>
27+
<div id="target">
28+
<div><div></div></div>
29+
<div><div></div></div>
30+
<div><div></div></div>
31+
<div><div></div></div>
32+
</div>
33+
<script>
34+
let target = document.getElementById("target");
35+
// "clip" is not really scrollable, but should match as well.
36+
for (let overflow of ["visible", "hidden", "auto", "scroll", "clip"]) {
37+
// First one creates no overflow, last one does create overflow.
38+
// TODO(emilio): Figure out why the overflow padding and some of the grid cases
39+
// are not interoperable across platforms / engines. Maybe scrollbar-width dependent?
40+
for (let padding of ["10px 20px" /* , "2px" */]) {
41+
for (let border of ["0", "3px solid"]) {
42+
for (let display of ["flex", "block", "flow-root", "inline-block", "inline-flex", /* "grid", "inline-grid" */]) {
43+
test(function() {
44+
let [expectedOverflowY, expectedOverflowX] = padding == "10px 20px" ? [0, 0] : [3, 5];
45+
target.style.overflow = overflow;
46+
target.style.display = display;
47+
target.style.border = border;
48+
target.style.padding = padding;
49+
let sh = target.scrollHeight;
50+
let sw = target.scrollWidth;
51+
let ch = target.clientHeight;
52+
let cw = target.clientWidth;
53+
assert_equals(sh, ch + expectedOverflowY, `scrollHeight expecting ${expectedOverflowY}px of overflow`);
54+
assert_equals(sw, cw + expectedOverflowX, `scrollWidth expecting ${expectedOverflowX}px of overflow`);
55+
}, `scroll{Width,Height} with negative margins with overflow: ${overflow}, padding: ${padding}, border: ${border}, display: ${display}`);
56+
}
57+
}
58+
}
59+
}
60+
</script>

0 commit comments

Comments
 (0)