mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
layout: Disallow margins from collapsing through blocks with clearance
per CSS 2.1 § 8.3.1. Fixes the test failure in #10458.
This commit is contained in:
parent
e32455f7b8
commit
a3fd226341
37 changed files with 223 additions and 94 deletions
|
@ -809,7 +809,7 @@ impl BlockFlow {
|
|||
// At this point, `cur_b` is at the content edge of our box. Now iterate over children.
|
||||
let mut floats = self.base.floats.clone();
|
||||
let thread_id = self.base.thread_id;
|
||||
let mut had_float_children = false;
|
||||
let (mut had_floated_children, mut had_children_with_clearance) = (false, false);
|
||||
for (child_index, kid) in self.base.child_iter_mut().enumerate() {
|
||||
if flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||
// Assume that the *hypothetical box* for an absolute flow starts immediately
|
||||
|
@ -845,12 +845,12 @@ impl BlockFlow {
|
|||
// before.
|
||||
flow::mut_base(kid).floats = floats.clone();
|
||||
if flow::base(kid).flags.is_float() {
|
||||
had_float_children = true;
|
||||
had_floated_children = true;
|
||||
flow::mut_base(kid).position.start.b = cur_b;
|
||||
{
|
||||
let kid_block = kid.as_mut_block();
|
||||
kid_block.float.as_mut().unwrap().float_ceiling =
|
||||
margin_collapse_info.current_float_ceiling();
|
||||
let float_ceiling = margin_collapse_info.current_float_ceiling();
|
||||
kid_block.float.as_mut().unwrap().float_ceiling = float_ceiling
|
||||
}
|
||||
kid.place_float_if_applicable(layout_context);
|
||||
|
||||
|
@ -875,9 +875,17 @@ impl BlockFlow {
|
|||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
thread_id);
|
||||
|
||||
if !had_children_with_clearance &&
|
||||
floats.is_present() &&
|
||||
(flow::base(kid).flags.contains(CLEARS_LEFT) ||
|
||||
flow::base(kid).flags.contains(CLEARS_RIGHT)) {
|
||||
had_children_with_clearance = true
|
||||
}
|
||||
|
||||
// Handle any (possibly collapsed) top margin.
|
||||
let delta = margin_collapse_info.advance_block_start_margin(
|
||||
&flow::base(kid).collapsible_margins);
|
||||
&flow::base(kid).collapsible_margins,
|
||||
!had_children_with_clearance);
|
||||
translate_including_floats(&mut cur_b, delta, &mut floats);
|
||||
|
||||
// Clear past the floats that came in, if necessary.
|
||||
|
@ -935,7 +943,7 @@ impl BlockFlow {
|
|||
&self.fragment,
|
||||
self.base.block_container_explicit_block_size,
|
||||
can_collapse_block_end_margin_with_kids,
|
||||
!had_float_children);
|
||||
!had_floated_children);
|
||||
self.base.collapsible_margins = collapsible_margins;
|
||||
translate_including_floats(&mut cur_b, delta, &mut floats);
|
||||
|
||||
|
|
|
@ -413,6 +413,10 @@ impl Floats {
|
|||
}
|
||||
clearance
|
||||
}
|
||||
|
||||
pub fn is_present(&self) -> bool {
|
||||
self.list.is_present()
|
||||
}
|
||||
}
|
||||
|
||||
/// The speculated inline sizes of floats flowing through or around a flow (depending on whether
|
||||
|
|
|
@ -18,7 +18,7 @@ use style::values::computed::{BorderRadiusSize, LengthOrPercentageOrAuto};
|
|||
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone};
|
||||
|
||||
/// A collapsible margin. See CSS 2.1 § 8.3.1.
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AdjoiningMargins {
|
||||
/// The value of the greatest positive margin.
|
||||
pub most_positive: Au,
|
||||
|
@ -61,7 +61,7 @@ impl AdjoiningMargins {
|
|||
}
|
||||
|
||||
/// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CollapsibleMargins {
|
||||
/// Margins may not collapse with this flow.
|
||||
None(Au, Au),
|
||||
|
@ -169,12 +169,14 @@ impl MarginCollapseInfo {
|
|||
FinalMarginState::MarginsCollapseThrough => {
|
||||
let advance = self.block_start_margin.collapse();
|
||||
self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
|
||||
(CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in), advance)
|
||||
(CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
|
||||
advance)
|
||||
}
|
||||
FinalMarginState::BottomMarginCollapses => {
|
||||
let advance = self.margin_in.collapse();
|
||||
self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
|
||||
(CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in), advance)
|
||||
(CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
|
||||
advance)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -206,8 +208,14 @@ impl MarginCollapseInfo {
|
|||
/// Adds the child's potentially collapsible block-start margin to the current margin state and
|
||||
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
|
||||
/// that should be added to the Y offset during block layout.
|
||||
pub fn advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins)
|
||||
pub fn advance_block_start_margin(&mut self,
|
||||
child_collapsible_margins: &CollapsibleMargins,
|
||||
can_collapse_block_start_margin: bool)
|
||||
-> Au {
|
||||
if !can_collapse_block_start_margin {
|
||||
self.state = MarginCollapseState::AccumulatingMarginIn
|
||||
}
|
||||
|
||||
match (self.state, *child_collapsible_margins) {
|
||||
(MarginCollapseState::AccumulatingCollapsibleTopMargin,
|
||||
CollapsibleMargins::None(block_start, _)) => {
|
||||
|
@ -226,14 +234,16 @@ impl MarginCollapseInfo {
|
|||
self.margin_in = AdjoiningMargins::new();
|
||||
previous_margin_value + block_start
|
||||
}
|
||||
(MarginCollapseState::AccumulatingMarginIn, CollapsibleMargins::Collapse(block_start, _)) => {
|
||||
(MarginCollapseState::AccumulatingMarginIn,
|
||||
CollapsibleMargins::Collapse(block_start, _)) => {
|
||||
self.margin_in.union(block_start);
|
||||
let margin_value = self.margin_in.collapse();
|
||||
self.margin_in = AdjoiningMargins::new();
|
||||
margin_value
|
||||
}
|
||||
(_, CollapsibleMargins::CollapseThrough(_)) => {
|
||||
// For now, we ignore this; this will be handled by `advance_block-end_margin` below.
|
||||
// For now, we ignore this; this will be handled by `advance_block_end_margin`
|
||||
// below.
|
||||
Au(0)
|
||||
}
|
||||
}
|
||||
|
@ -274,9 +284,11 @@ impl MarginCollapseInfo {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum MarginCollapseState {
|
||||
/// We are accumulating margin on the logical top of this flow.
|
||||
AccumulatingCollapsibleTopMargin,
|
||||
/// We are accumulating margin between two blocks.
|
||||
AccumulatingMarginIn,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[Flexible-order.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[adjacent-floats-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
4
tests/wpt/metadata-css/css21_dev/html4/bidi-010b.htm.ini
Normal file
4
tests/wpt/metadata-css/css21_dev/html4/bidi-010b.htm.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[bidi-010b.htm]
|
||||
type: reftest
|
||||
disabled: intermittent failure on reference, starting from PR #10458
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
[border-right-color-175.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c414-flt-fit-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c414-flt-fit-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c414-flt-fit-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c414-flt-fit-005.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c414-flt-fit-006.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-float-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-float-005.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-float-006.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-float-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-float-008.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[clear-float-009.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[float-006.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[float-applies-to-009.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[float-applies-to-012.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[float-non-replaced-height-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-123.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-141.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-144.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +1,3 @@
|
|||
[floats-145.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
||||
disabled: depends on unspecified line-height behavior, see PR #10458
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[floats-146.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[position-absolute-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1571,6 +1571,30 @@
|
|||
"url": "/_mozilla/css/floated_table_with_margin_a.html"
|
||||
}
|
||||
],
|
||||
"css/floats_margin_collapse_a.html": [
|
||||
{
|
||||
"path": "css/floats_margin_collapse_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/floats_margin_collapse_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/floats_margin_collapse_a.html"
|
||||
}
|
||||
],
|
||||
"css/floats_margin_collapse_with_clearance_a.html": [
|
||||
{
|
||||
"path": "css/floats_margin_collapse_with_clearance_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/floats_margin_collapse_with_clearance_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/floats_margin_collapse_with_clearance_a.html"
|
||||
}
|
||||
],
|
||||
"css/focus_selector.html": [
|
||||
{
|
||||
"path": "css/focus_selector.html",
|
||||
|
@ -8161,6 +8185,30 @@
|
|||
"url": "/_mozilla/css/floated_table_with_margin_a.html"
|
||||
}
|
||||
],
|
||||
"css/floats_margin_collapse_a.html": [
|
||||
{
|
||||
"path": "css/floats_margin_collapse_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/floats_margin_collapse_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/floats_margin_collapse_a.html"
|
||||
}
|
||||
],
|
||||
"css/floats_margin_collapse_with_clearance_a.html": [
|
||||
{
|
||||
"path": "css/floats_margin_collapse_with_clearance_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/floats_margin_collapse_with_clearance_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/floats_margin_collapse_with_clearance_a.html"
|
||||
}
|
||||
],
|
||||
"css/focus_selector.html": [
|
||||
{
|
||||
"path": "css/focus_selector.html",
|
||||
|
|
42
tests/wpt/mozilla/tests/css/floats_margin_collapse_a.html
Normal file
42
tests/wpt/mozilla/tests/css/floats_margin_collapse_a.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link rel="match" href="floats_margin_collapse_ref.html">
|
||||
<!--
|
||||
From https://github.com/servo/servo/pull/10458:
|
||||
|
||||
https://drafts.csswg.org/css2/visuren.html#float-rules
|
||||
|
||||
A floating box's outer top may not be higher than the top of its containing block. When the float occurs between two collapsing margins, the float is positioned as if it had an otherwise empty anonymous block parent taking part in the flow. The position of such a parent is defined by the rules in the section on margin collapsing.
|
||||
|
||||
#a is not "in the flow" since it’s a float, which makes #b the first in-flow child of <body>, which makes the top margins of <body> and #b "adjoining" (even if the former is zero) which makes them collapse.
|
||||
|
||||
So the quoted rule applies and there’s an hypothetical block around #a. Since that blocks has no in-flow content, margins collapse through it.
|
||||
|
||||
Then I believe this rule applies:
|
||||
|
||||
https://drafts.csswg.org/css2/box.html#collapsed-through
|
||||
|
||||
If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
|
||||
|
||||
The top border edge of <body> is below its top margin, which is collapsed with that of #b.
|
||||
-->
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
}
|
||||
div {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
#a {
|
||||
float: left;
|
||||
background: green;
|
||||
}
|
||||
#b {
|
||||
background: blue;
|
||||
margin-top: 32px;
|
||||
}
|
||||
</style>
|
||||
<div id=a></div>
|
||||
<div id=b></div>
|
||||
|
19
tests/wpt/mozilla/tests/css/floats_margin_collapse_ref.html
Normal file
19
tests/wpt/mozilla/tests/css/floats_margin_collapse_ref.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
}
|
||||
div {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
#a {
|
||||
top: 32px;
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
<div id=a></div>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<link rel="match" href="floats_margin_collapse_with_clearance_ref.html">
|
||||
<!--
|
||||
From https://github.com/servo/servo/pull/10458:
|
||||
|
||||
https://drafts.csswg.org/css2/visuren.html#float-rules
|
||||
|
||||
A floating box's outer top may not be higher than the top of its containing block. When the float occurs between two collapsing margins, the float is positioned as if it had an otherwise empty anonymous block parent taking part in the flow. The position of such a parent is defined by the rules in the section on margin collapsing.
|
||||
|
||||
#a is not "in the flow" since it’s a float, which makes #b the first in-flow child of <body>, which makes the top margins of <body> and #b "adjoining" (even if the former is zero) which makes them collapse.
|
||||
|
||||
So the quoted rule applies and there’s an hypothetical block around #a. Since that blocks has no in-flow content, margins collapse through it.
|
||||
|
||||
Then I believe this rule applies:
|
||||
|
||||
https://drafts.csswg.org/css2/box.html#collapsed-through
|
||||
|
||||
If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
|
||||
|
||||
The top border edge of <body> is below its top margin, which is collapsed with that of #b.
|
||||
-->
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
}
|
||||
div {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
#a {
|
||||
float: left;
|
||||
background: green;
|
||||
}
|
||||
#b {
|
||||
background: blue;
|
||||
margin-top: 32px;
|
||||
clear: left;
|
||||
}
|
||||
</style>
|
||||
<div id=a></div>
|
||||
<div id=b></div>
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
}
|
||||
div {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
#a {
|
||||
background: green;
|
||||
top: 0;
|
||||
}
|
||||
#b {
|
||||
background: blue;
|
||||
top: 32px;
|
||||
}
|
||||
</style>
|
||||
<div id=a></div>
|
||||
<div id=b></div>
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue