From ff5683680fa1a46c36c3278a58b727e84f25e457 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 4 Mar 2025 06:58:46 +0100 Subject: [PATCH] layout: Partial support for keyword sizes on preferred cross size (#35682) This changes `FlexItem::content_cross_size` into `Size` to preserve keyword sizes. The calculation of the hypothetical cross size still ignores them though, that will be addressed in a follow-up. Also, browsers don't follow the spec and treat a stretch size different than a stretch alignment: the former stretches to the containing block, while the latter stretches to the line. This aligns Servo with that behavior (following the spec would require bigger refactorings), so `stretches()` is renamed to `stretches_to_line()` for clarity. Signed-off-by: Oriol Brufau --- components/layout_2020/flexbox/layout.rs | 122 ++++++++++-------- ...riting-modes-and-intrinsic-sizing.html.ini | 3 - .../keyword-sizes-on-flex-item-001.html.ini | 36 ++---- .../keyword-sizes-on-flex-item-002.html.ini | 24 ---- .../stretch/stretch-block-size-001.html.ini | 6 + .../stretch/stretch-inline-size-001.html.ini | 6 - 6 files changed, 84 insertions(+), 113 deletions(-) delete mode 100644 tests/wpt/meta/css/css-flexbox/orthogonal-writing-modes-and-intrinsic-sizing.html.ini diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 470f8d0b744..c85318be26d 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -62,7 +62,7 @@ struct FlexContext<'a> { /// A flex item with some intermediate results struct FlexItem<'a> { box_: &'a FlexItemBox, - content_cross_size: AuOrAuto, + content_cross_size: Size, content_min_size: FlexRelativeVec2, content_max_size: FlexRelativeVec2>, padding: FlexRelativeSides, @@ -1222,8 +1222,7 @@ impl<'a> FlexItem<'a> { Self { box_, - // TODO(#32853): handle size keywords. - content_cross_size: flex_relative_content_box_size.cross.to_auto_or(), + content_cross_size: flex_relative_content_box_size.cross, content_min_size: flex_relative_content_min_size, content_max_size: flex_relative_content_max_size, padding, @@ -1239,8 +1238,11 @@ impl<'a> FlexItem<'a> { } } - fn stretches(&self) -> bool { - self.content_cross_size.is_auto() && + fn stretches_to_line(&self) -> bool { + // Note this returns false for a `stretch` size, because that stretches to the + // containing block, not to the flex line. + // To be discussed in https://github.com/w3c/csswg-drafts/issues/11784. + self.content_cross_size.is_initial() && item_with_auto_cross_size_stretches_to_line_size(self.align_self, &self.margin) } } @@ -1693,7 +1695,7 @@ impl InitialFlexLineLayout<'_> { let mut item_used_cross_sizes = Vec::with_capacity(item_count); let mut item_margins = Vec::with_capacity(item_count); for item in self.items.iter_mut() { - let stretches = item.item.stretches(); + let stretches = item.item.stretches_to_line(); let used_cross_size = if stretches { (final_line_cross_size - item.item.pbm_auto_is_zero.cross).clamp_between_extremums( item.item.content_min_size.cross, @@ -1885,15 +1887,6 @@ impl FlexItem<'_> { ) }); - let cross_size = match used_cross_size_override { - Some(s) => SizeConstraint::Definite(s), - None => SizeConstraint::new( - self.content_cross_size.non_auto(), - self.content_min_size.cross, - self.content_max_size.cross, - ), - }; - let independent_formatting_context = &self.box_.independent_formatting_context; let item_writing_mode = independent_formatting_context.style().writing_mode; let item_is_horizontal = item_writing_mode.is_horizontal(); @@ -1905,19 +1898,40 @@ impl FlexItem<'_> { ); let (inline_size, block_size) = if cross_axis_is_item_block_axis { + let cross_size = match used_cross_size_override { + Some(s) => SizeConstraint::Definite(s), + None => { + // This means that an auto size with stretch alignment will behave different than + // a stretch size. That's not what the spec says, but matches other browsers. + // To be discussed in https://github.com/w3c/csswg-drafts/issues/11784. + let stretch_size = containing_block + .size + .block + .to_definite() + .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross)); + SizeConstraint::new( + self.content_cross_size + .maybe_resolve_extrinsic(stretch_size), + self.content_min_size.cross, + self.content_max_size.cross, + ) + }, + }; (used_main_size, cross_size) } else { - ( - cross_size.to_definite().unwrap_or_else(|| { - let style = self.box_.style(); - let stretch_size = - Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross); - if flex_context - .config - .item_with_auto_cross_size_stretches_to_container_size(style, &self.margin) - { - return stretch_size; - } + let cross_size = used_cross_size_override.unwrap_or_else(|| { + let style = self.box_.style(); + let automatic_size = if flex_context + .config + .item_with_auto_cross_size_stretches_to_container_size(style, &self.margin) + { + Size::Stretch + } else { + Size::FitContent + }; + let stretch_size = + Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross); + let content_size = LazyCell::new(|| { let constraint_space = ConstraintSpace::new( SizeConstraint::Definite(used_main_size), item_writing_mode, @@ -1926,26 +1940,28 @@ impl FlexItem<'_> { independent_formatting_context .inline_content_sizes(flex_context.layout_context, &constraint_space) .sizes - .shrink_to_fit(stretch_size) - .clamp_between_extremums( - self.content_min_size.cross, - self.content_max_size.cross, - ) - }), - // The main size of a flex item is considered to be definite if its flex basis is definite - // or the flex container has a definite main size. - // - if self.flex_base_size_is_definite || - flex_context - .container_inner_size_constraint - .main - .is_definite() - { - SizeConstraint::Definite(used_main_size) - } else { - SizeConstraint::default() - }, - ) + }); + self.content_cross_size + .resolve_for_preferred(automatic_size, Some(stretch_size), &content_size) + .clamp_between_extremums( + self.content_min_size.cross, + self.content_max_size.cross, + ) + }); + // The main size of a flex item is considered to be definite if its flex basis is definite + // or the flex container has a definite main size. + // + let main_size = if self.flex_base_size_is_definite || + flex_context + .container_inner_size_constraint + .main + .is_definite() + { + SizeConstraint::Definite(used_main_size) + } else { + SizeConstraint::default() + }; + (cross_size, main_size) }; let container_writing_mode = containing_block.style.writing_mode; @@ -2013,13 +2029,13 @@ impl FlexItem<'_> { }, IndependentFormattingContextContents::NonReplaced(non_replaced) => { let calculate_hypothetical_cross_size = |content_block_size| { + // TODO(#32853): handle size keywords. self.content_cross_size - .auto_is(|| { - if cross_axis_is_item_block_axis { - content_block_size - } else { - inline_size - } + .to_numeric() + .unwrap_or(if cross_axis_is_item_block_axis { + content_block_size + } else { + inline_size }) .clamp_between_extremums( self.content_min_size.cross, @@ -2069,7 +2085,7 @@ impl FlexItem<'_> { .. } = layout; let depends_on_block_constraints = depends_on_block_constraints || - (flex_axis == FlexAxis::Row && self.stretches()); + (flex_axis == FlexAxis::Row && self.stretches_to_line()); let has_child_which_depends_on_block_constraints = fragments.iter().any(|fragment| { fragment.base().is_some_and(|base| diff --git a/tests/wpt/meta/css/css-flexbox/orthogonal-writing-modes-and-intrinsic-sizing.html.ini b/tests/wpt/meta/css/css-flexbox/orthogonal-writing-modes-and-intrinsic-sizing.html.ini deleted file mode 100644 index 61e8a9048fa..00000000000 --- a/tests/wpt/meta/css/css-flexbox/orthogonal-writing-modes-and-intrinsic-sizing.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[orthogonal-writing-modes-and-intrinsic-sizing.html] - [.flexbox 2] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-001.html.ini b/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-001.html.ini index 71d18a9e17e..9cd556f2ece 100644 --- a/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-001.html.ini +++ b/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-001.html.ini @@ -53,15 +53,6 @@ [.test 27] expected: FAIL - [.test 28] - expected: FAIL - - [.test 29] - expected: FAIL - - [.test 30] - expected: FAIL - [.test 31] expected: FAIL @@ -98,6 +89,15 @@ [.test 45] expected: FAIL + [.test 46] + expected: FAIL + + [.test 47] + expected: FAIL + + [.test 48] + expected: FAIL + [.test 49] expected: FAIL @@ -116,24 +116,6 @@ [.test 54] expected: FAIL - [.test 57] - expected: FAIL - - [.test 58] - expected: FAIL - - [.test 59] - expected: FAIL - - [.test 66] - expected: FAIL - - [.test 67] - expected: FAIL - - [.test 68] - expected: FAIL - [.test 69] expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-002.html.ini b/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-002.html.ini index 33c0021a067..25aae054dbb 100644 --- a/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-002.html.ini +++ b/tests/wpt/meta/css/css-sizing/keyword-sizes-on-flex-item-002.html.ini @@ -1,28 +1,4 @@ [keyword-sizes-on-flex-item-002.html] - [.test 1] - expected: FAIL - - [.test 2] - expected: FAIL - - [.test 3] - expected: FAIL - - [.test 4] - expected: FAIL - - [.test 6] - expected: FAIL - - [.test 7] - expected: FAIL - - [.test 8] - expected: FAIL - - [.test 9] - expected: FAIL - [.test 10] expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/stretch/stretch-block-size-001.html.ini b/tests/wpt/meta/css/css-sizing/stretch/stretch-block-size-001.html.ini index db73de5952d..f5d4623da35 100644 --- a/tests/wpt/meta/css/css-sizing/stretch/stretch-block-size-001.html.ini +++ b/tests/wpt/meta/css/css-sizing/stretch/stretch-block-size-001.html.ini @@ -2,6 +2,9 @@ [[data-expected-height\] 8] expected: FAIL + [[data-expected-height\] 19] + expected: FAIL + [[data-expected-height\] 21] expected: FAIL @@ -11,6 +14,9 @@ [[data-expected-height\] 31] expected: FAIL + [[data-expected-height\] 42] + expected: FAIL + [[data-expected-height\] 44] expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/stretch/stretch-inline-size-001.html.ini b/tests/wpt/meta/css/css-sizing/stretch/stretch-inline-size-001.html.ini index c286c29b718..b5214465c31 100644 --- a/tests/wpt/meta/css/css-sizing/stretch/stretch-inline-size-001.html.ini +++ b/tests/wpt/meta/css/css-sizing/stretch/stretch-inline-size-001.html.ini @@ -2,17 +2,11 @@ [[data-expected-width\] 8] expected: FAIL - [[data-expected-width\] 21] - expected: FAIL - [[data-expected-width\] 22] expected: FAIL [[data-expected-width\] 31] expected: FAIL - [[data-expected-width\] 44] - expected: FAIL - [[data-expected-width\] 45] expected: FAIL