Auto merge of #29902 - Loirooriol:clearance-prevents-margin-collapse2, r=mrobinson

Fix interaction of clearance with margin collapse

In #29897 I did the simple naive thing, but it wasn't entirely correct. This patch tries to address the problems. In particular:

 - Clearance should prevent margins from collapsing through if it happens between them, as opposed to on the element that owns them.
 - The margins of an element with clearance can still collapse through, and collapse with other siblings as normal, but the resulting margin can't collapse with the bottom margin of the parent.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #29896
- [X] There are tests for these changes

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2023-06-21 19:05:32 +02:00 committed by GitHub
commit 9edc2c664f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 41 additions and 24 deletions

View file

@ -306,10 +306,11 @@ fn layout_block_level_children_in_parallel(
})
.collect();
let (content_block_size, collapsible_margins_in_children) = placement_state.finish();
FlowLayout {
fragments,
content_block_size: placement_state.current_block_direction_position,
collapsible_margins_in_children: placement_state.collapsible_margins_in_children(),
content_block_size,
collapsible_margins_in_children,
}
}
@ -349,10 +350,11 @@ fn layout_block_level_children_sequentially(
})
.collect();
let (content_block_size, collapsible_margins_in_children) = placement_state.finish();
FlowLayout {
fragments,
content_block_size: placement_state.current_block_direction_position,
collapsible_margins_in_children: placement_state.collapsible_margins_in_children(),
content_block_size,
collapsible_margins_in_children,
}
}
@ -641,7 +643,6 @@ fn layout_in_flow_non_replaced_block_level(
collapsible_margins_in_children.collapsed_through &&
block_is_same_formatting_context &&
pbm.padding_border_sums.block == Length::zero() &&
clearance == Length::zero() &&
block_size.auto_is(|| Length::zero()) == Length::zero() &&
min_box_size.block == Length::zero();
},
@ -775,6 +776,7 @@ fn solve_inline_margins_for_in_flow_block_level(
// mode, this is done right after each block is laid out.
pub(crate) struct PlacementState {
next_in_flow_margin_collapses_with_parent_start_margin: bool,
last_in_flow_margin_collapses_with_parent_end_margin: bool,
start_margin: CollapsedMargin,
current_margin: CollapsedMargin,
current_block_direction_position: Length,
@ -787,6 +789,7 @@ impl PlacementState {
PlacementState {
next_in_flow_margin_collapses_with_parent_start_margin:
collapsible_with_parent_start_margin.0,
last_in_flow_margin_collapses_with_parent_end_margin: true,
start_margin: CollapsedMargin::zero(),
current_margin: CollapsedMargin::zero(),
current_block_direction_position: Length::zero(),
@ -801,7 +804,23 @@ impl PlacementState {
fragment.padding.block_sum() +
fragment.border.block_sum() +
fragment.content_rect.size.block;
// We use `last_in_flow_margin_collapses_with_parent_end_margin` to implement
// this quote from https://drafts.csswg.org/css2/#collapsing-margins
// > If the top and bottom margins of an element with clearance are adjoining,
// > its margins collapse with the adjoining margins of following siblings but that
// > resulting margin does not collapse with the bottom margin of the parent block.
if fragment.clearance != Length::zero() {
// Margins can't be adjoining if they are separated by clearance.
// Setting `next_in_flow_margin_collapses_with_parent_start_margin` to false
// prevents collapsing with the start margin of the parent, and will set
// `collapsed_through` to false, preventing the parent from collapsing through.
self.next_in_flow_margin_collapses_with_parent_start_margin = false;
if fragment_block_margins.collapsed_through {
self.last_in_flow_margin_collapses_with_parent_end_margin = false;
}
} else if !fragment_block_margins.collapsed_through {
self.last_in_flow_margin_collapses_with_parent_end_margin = true;
}
if self.next_in_flow_margin_collapses_with_parent_start_margin {
debug_assert_eq!(self.current_margin.solve(), Length::zero());
self.start_margin
@ -818,6 +837,9 @@ impl PlacementState {
fragment.content_rect.start_corner.block +=
self.current_margin.solve() + self.current_block_direction_position;
if fragment_block_margins.collapsed_through {
// `fragment_block_size` is typically zero when collapsing through,
// but we still need to consider it in case there is clearance.
self.current_block_direction_position += fragment_block_size;
self.current_margin
.adjoin_assign(&fragment_block_margins.end);
return;
@ -838,12 +860,19 @@ impl PlacementState {
}
}
fn collapsible_margins_in_children(&self) -> CollapsedBlockMargins {
CollapsedBlockMargins {
collapsed_through: self.next_in_flow_margin_collapses_with_parent_start_margin,
start: self.start_margin,
end: self.current_margin,
fn finish(mut self) -> (Length, CollapsedBlockMargins) {
if !self.last_in_flow_margin_collapses_with_parent_end_margin {
self.current_block_direction_position += self.current_margin.solve();
self.current_margin = CollapsedMargin::zero();
}
(
self.current_block_direction_position,
CollapsedBlockMargins {
collapsed_through: self.next_in_flow_margin_collapses_with_parent_start_margin,
start: self.start_margin,
end: self.current_margin,
},
)
}
/// When Float fragments are created in block flows, they are positioned
@ -865,7 +894,7 @@ impl PlacementState {
// non-zero sized float inside? The float won't be positioned correctly
// anyway (see the comment in `floats.rs` about margin collapse), but
// this might make the result even worse.
let collapsed_margins = self.collapsible_margins_in_children().start.adjoin(
let collapsed_margins = self.start_margin.adjoin(
&sequential_layout_state
.floats
.containing_block_info

View file

@ -1,2 +0,0 @@
[adjoining-float-nested-forced-clearance-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-033.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-034.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-035.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-clear-012.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-clear-013.xht]
expected: FAIL