From 6be0a64939c04fdcd9b8a11a863f1a04f07e4c9c Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Mon, 23 Dec 2024 03:00:37 -0800 Subject: [PATCH] layout: Implement keyword sizes for block layout heuristics (#34695) Block layout uses some heuristics to guess whether margins are separated by clearance and then don't collapse. These heuristics now take the min-content, max-content, fit-content and stretch sizing keywords into account. Signed-off-by: Oriol Brufau --- components/layout_2020/flow/mod.rs | 104 ++++++++++++------ components/layout_2020/style_ext.rs | 31 ------ tests/wpt/meta/MANIFEST.json | 52 +++++++++ ...ontent-and-padding-percentage-003.html.ini | 2 + ...ontent-and-padding-percentage-004.html.ini | 2 + ...it-content-and-padding-percentage-001.html | 22 ++++ ...it-content-and-padding-percentage-002.html | 22 ++++ ...it-content-and-padding-percentage-003.html | 20 ++++ ...it-content-and-padding-percentage-004.html | 20 ++++ 9 files changed, 212 insertions(+), 63 deletions(-) create mode 100644 tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html.ini create mode 100644 tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html.ini create mode 100644 tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-001.html create mode 100644 tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-002.html create mode 100644 tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html create mode 100644 tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 06e7b6a4093..62bb828374a 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -39,7 +39,7 @@ use crate::layout_box_base::LayoutBoxBase; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::replaced::ReplacedContents; use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; -use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, PaddingBorderMargin}; +use crate::style_ext::{ComputedValuesExt, ContentBoxSizesAndPBM, PaddingBorderMargin}; use crate::{ ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock, SizeConstraint, @@ -102,6 +102,7 @@ impl BlockLevelBox { fn find_block_margin_collapsing_with_parent( &self, + layout_context: &LayoutContext, collected_margin: &mut CollapsedMargin, containing_block: &ContainingBlock, ) -> bool { @@ -122,9 +123,13 @@ impl BlockLevelBox { return false; } - let pbm = style.padding_border_margin(containing_block); - let start_margin = pbm.margin.block_start.auto_is(Au::zero); - collected_margin.adjoin_assign(&CollapsedMargin::new(start_margin)); + let ContentBoxSizesAndPBM { + content_box_sizes, + pbm, + .. + } = style.content_box_sizes_and_padding_border_margin(&containing_block.into()); + let margin = pbm.margin.auto_is(Au::zero); + collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_start)); let child_boxes = match self { BlockLevelBox::SameFormattingContextBlock { ref contents, .. } => match contents { @@ -138,35 +143,45 @@ impl BlockLevelBox { return false; } - let min_size = style - .content_min_box_size_deprecated(containing_block, &pbm) - .auto_is(Au::zero); - let max_size = style.content_max_box_size_deprecated(containing_block, &pbm); - let prefered_size = style.content_box_size_deprecated(containing_block, &pbm); - let inline_size = prefered_size - .inline - .auto_is(|| { - let margin_inline_start = pbm.margin.inline_start.auto_is(Au::zero); - let margin_inline_end = pbm.margin.inline_end.auto_is(Au::zero); - containing_block.size.inline - - pbm.padding_border_sums.inline - - margin_inline_start - - margin_inline_end - }) - .clamp_between_extremums(min_size.inline, max_size.inline); - let block_size = prefered_size - .block - .map(|size| size.clamp_between_extremums(min_size.block, max_size.block)); + // FIXME: For BlockLevelBox::Independent, this should take floats into account. + let available_inline_size = + containing_block.size.inline - pbm.padding_border_sums.inline - margin.inline_sum(); + let available_block_size = containing_block.size.block.non_auto().map(|block_size| { + Au::zero().max(block_size - pbm.padding_border_sums.block - margin.block_sum()) + }); + + let tentative_block_size = content_box_sizes.block.resolve_extrinsic( + Size::FitContent, + Au::zero(), + available_block_size, + ); + + let get_inline_content_sizes = || { + let constraint_space = ConstraintSpace::new( + tentative_block_size, + style.writing_mode, + None, /* TODO: support preferred aspect ratios on non-replaced boxes */ + ); + self.inline_content_sizes(layout_context, &constraint_space) + .sizes + }; + let inline_size = content_box_sizes.inline.resolve( + Size::Stretch, + Au::zero(), + available_inline_size, + get_inline_content_sizes, + ); let containing_block_for_children = ContainingBlock { size: ContainingBlockSize { inline: inline_size, - block: block_size, + block: tentative_block_size.to_auto_or(), }, style, }; if !Self::find_block_margin_collapsing_with_parent_from_slice( + layout_context, child_boxes, collected_margin, &containing_block_for_children, @@ -181,13 +196,13 @@ impl BlockLevelBox { return false; } - let end_margin = pbm.margin.block_end.auto_is(Au::zero); - collected_margin.adjoin_assign(&CollapsedMargin::new(end_margin)); + collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_end)); true } fn find_block_margin_collapsing_with_parent_from_slice( + layout_context: &LayoutContext, boxes: &[ArcRefCell], margin: &mut CollapsedMargin, containing_block: &ContainingBlock, @@ -195,7 +210,7 @@ impl BlockLevelBox { boxes.iter().all(|block_level_box| { block_level_box .borrow() - .find_block_margin_collapsing_with_parent(margin, containing_block) + .find_block_margin_collapsing_with_parent(layout_context, margin, containing_block) }) } } @@ -230,6 +245,15 @@ impl OutsideMarker { &self.base.style } + fn inline_content_sizes( + &self, + layout_context: &LayoutContext, + constraint_space: &ConstraintSpace, + ) -> InlineContentSizesResult { + self.base + .inline_content_sizes(layout_context, constraint_space, &self.block_container) + } + fn layout( &self, layout_context: &LayoutContext<'_>, @@ -242,11 +266,7 @@ impl OutsideMarker { &self.marker_style, None, /* TODO: support preferred aspect ratios on non-replaced boxes */ ); - let content_sizes = self.base.inline_content_sizes( - layout_context, - &constraint_space, - &self.block_container, - ); + let content_sizes = self.inline_content_sizes(layout_context, &constraint_space); let containing_block_for_children = ContainingBlock { size: ContainingBlockSize { inline: content_sizes.sizes.max_content, @@ -770,6 +790,25 @@ impl BlockLevelBox { ), } } + + fn inline_content_sizes( + &self, + layout_context: &LayoutContext, + constraint_space: &ConstraintSpace, + ) -> InlineContentSizesResult { + let independent_formatting_context = match self { + BlockLevelBox::Independent(independent) => independent, + BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context, + BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents, + BlockLevelBox::OutsideMarker(outside_marker) => { + return outside_marker.inline_content_sizes(layout_context, constraint_space) + }, + BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => { + return base.inline_content_sizes(layout_context, constraint_space, contents) + }, + }; + independent_formatting_context.inline_content_sizes(layout_context, constraint_space) + } } /// Lay out a normal flow non-replaced block that does not establish a new formatting @@ -844,6 +883,7 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context( if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children { if let BlockContainer::BlockLevelBoxes(child_boxes) = contents { BlockLevelBox::find_block_margin_collapsing_with_parent_from_slice( + layout_context, child_boxes, &mut block_start_margin, containing_block, diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index e6733ac998e..04ad55ddaac 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -261,16 +261,6 @@ pub(crate) trait ComputedValuesExt { box_size: LogicalVec2>, pbm: &PaddingBorderMargin, ) -> LogicalVec2>; - fn content_max_box_size( - &self, - containing_block: &ContainingBlock, - pbm: &PaddingBorderMargin, - ) -> LogicalVec2>; - fn content_max_box_size_deprecated( - &self, - containing_block: &ContainingBlock, - pbm: &PaddingBorderMargin, - ) -> LogicalVec2>; fn content_max_box_size_for_max_size( &self, box_size: LogicalVec2>, @@ -470,27 +460,6 @@ impl ComputedValuesExt for ComputedValues { } } - fn content_max_box_size( - &self, - containing_block: &ContainingBlock, - pbm: &PaddingBorderMargin, - ) -> LogicalVec2> { - let max_box_size = self - .max_box_size(containing_block.style.writing_mode) - .percentages_relative_to(containing_block); - - self.content_max_box_size_for_max_size(max_box_size, pbm) - } - - fn content_max_box_size_deprecated( - &self, - containing_block: &ContainingBlock, - pbm: &PaddingBorderMargin, - ) -> LogicalVec2> { - self.content_max_box_size(containing_block, pbm) - .map(Size::to_numeric) - } - fn content_max_box_size_for_max_size( &self, max_box_size: LogicalVec2>, diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index ddd61bb8185..97d0ad8b040 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -243091,6 +243091,58 @@ {} ] ], + "float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-001.html": [ + "d5e293f85e9cde73c1efa60d66ccdea4618512db", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-002.html": [ + "a9e0e95522403b396a86aac3a5400ef8693c56d4", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html": [ + "0e27b9ef7d16c19c76fab7314e089a122b8b9123", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html": [ + "6d08bd81a73703fd068849117c0e12210097adee", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "grid-item-image-percentage-min-height-computes-as-0.html": [ "ef3da03100229a846ca77156bdf34e5e37c92508", [ diff --git a/tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html.ini b/tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html.ini new file mode 100644 index 00000000000..f7395d94874 --- /dev/null +++ b/tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html.ini @@ -0,0 +1,2 @@ +[float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html.ini b/tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html.ini new file mode 100644 index 00000000000..211086a4c08 --- /dev/null +++ b/tests/wpt/meta/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html.ini @@ -0,0 +1,2 @@ +[float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html] + expected: FAIL diff --git a/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-001.html b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-001.html new file mode 100644 index 00000000000..d5e293f85e9 --- /dev/null +++ b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-001.html @@ -0,0 +1,22 @@ + + + + + + + + +

Test passes if there is a filled green square and no red.

+
+
+
+
+
+
+
+
diff --git a/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-002.html b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-002.html new file mode 100644 index 00000000000..a9e0e955224 --- /dev/null +++ b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-002.html @@ -0,0 +1,22 @@ + + + + + + + + +

Test passes if there is a filled green square and no red.

+
+
+
+
+
+
+
+
diff --git a/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html new file mode 100644 index 00000000000..0e27b9ef7d1 --- /dev/null +++ b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-003.html @@ -0,0 +1,20 @@ + + + + + + + + +

Test passes if there is a filled green square and no red.

+
+
+
+
+
+
diff --git a/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html new file mode 100644 index 00000000000..6d08bd81a73 --- /dev/null +++ b/tests/wpt/tests/css/css-sizing/float-clearance-with-margin-collapse-and-fit-content-and-padding-percentage-004.html @@ -0,0 +1,20 @@ + + + + + + + + +

Test passes if there is a filled green square and no red.

+
+
+
+
+
+