From c1b744b2b2c37cdb119a3d3f761822d22f938339 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 8 Oct 2024 15:07:59 +0200 Subject: [PATCH] Implement keyword sizes on floats (#33666) Adds support for min-content, max-content, fit-content and stretch, for floated elements. Signed-off-by: Oriol Brufau --- components/layout_2020/flow/float.rs | 104 ++++++++----- components/layout_2020/geom.rs | 78 +++++++++- tests/wpt/meta/MANIFEST.json | 7 + .../keyword-sizes-on-floated-element.html | 142 ++++++++++++++++++ 4 files changed, 290 insertions(+), 41 deletions(-) create mode 100644 tests/wpt/tests/css/css-sizing/keyword-sizes-on-floated-element.html diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index b4d77b35e80..367c73c59d4 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -27,10 +27,10 @@ use crate::dom::NodeExt; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin}; -use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, ToLogical}; +use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, Size, ToLogical}; use crate::positioned::{relative_adjustement, PositioningContext}; use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin}; -use crate::{ContainingBlock, IndefiniteContainingBlock}; +use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock}; /// A floating box. #[derive(Debug, Serialize)] @@ -914,43 +914,61 @@ impl FloatBox { // Calculate inline size. // https://drafts.csswg.org/css2/#float-width let style = non_replaced.style.clone(); - let box_size = style - .content_box_size_deprecated(containing_block, &pbm) - .map(|v| v.map(Au::from)); - let max_box_size = style - .content_max_box_size_deprecated(containing_block, &pbm) - .map(|v| v.map(Au::from)); - let min_box_size = style - .content_min_box_size_deprecated(containing_block, &pbm) - .map(|v| v.map(Au::from)) - .auto_is(Au::zero); - - let block_size = box_size.block.map(|size| { - size.clamp_between_extremums(min_box_size.block, max_box_size.block) - }); - let tentative_inline_size = box_size.inline.auto_is(|| { - let available_size = - containing_block.inline_size - pbm_sums.inline_sum(); + let box_size = style.content_box_size(containing_block, &pbm); + let max_box_size = style.content_max_box_size(containing_block, &pbm); + let min_box_size = style.content_min_box_size(containing_block, &pbm); + let available_inline_size = + containing_block.inline_size - pbm_sums.inline_sum(); + let available_block_size = containing_block + .block_size + .non_auto() + .map(|block_size| block_size - pbm_sums.block_sum()); + let tentative_block_size = box_size + .block + .maybe_resolve_extrinsic(available_block_size) + .map(|size| { + let min_block_size = min_box_size + .block + .maybe_resolve_extrinsic(available_block_size) + .unwrap_or_default(); + let max_block_size = max_box_size + .block + .maybe_resolve_extrinsic(available_block_size); + size.clamp_between_extremums(min_block_size, max_block_size) + }) + .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); + let mut get_content_size = || { let containing_block_for_children = IndefiniteContainingBlock::new_for_style_and_block_size( - &style, block_size, + &style, + tentative_block_size, ); - non_replaced - .inline_content_sizes( - layout_context, - &containing_block_for_children, - ) - .shrink_to_fit(available_size) - }); + non_replaced.inline_content_sizes( + layout_context, + &containing_block_for_children, + ) + }; + let tentative_inline_size = box_size.inline.resolve( + Size::fit_content, + available_inline_size, + &mut get_content_size, + ); + let min_inline_size = min_box_size + .inline + .resolve_non_initial(available_inline_size, &mut get_content_size) + .unwrap_or_default(); + let max_inline_size = max_box_size + .inline + .resolve_non_initial(available_inline_size, &mut get_content_size); let inline_size = tentative_inline_size - .clamp_between_extremums(min_box_size.inline, max_box_size.inline); + .clamp_between_extremums(min_inline_size, max_inline_size); // Calculate block size. // https://drafts.csswg.org/css2/#block-root-margin // FIXME(pcwalton): Is a tree rank of zero correct here? let containing_block_for_children = ContainingBlock { inline_size, - block_size, + block_size: tentative_block_size, style: &non_replaced.style, }; let independent_layout = non_replaced.layout( @@ -964,17 +982,23 @@ impl FloatBox { Some(inline_size) => { (independent_layout.content_block_size, inline_size) }, - None => ( - block_size.auto_is(|| { - independent_layout - .content_block_size - .clamp_between_extremums( - min_box_size.block, - max_box_size.block, - ) - }), - inline_size, - ), + None => { + let stretch_size = available_block_size + .unwrap_or(independent_layout.content_block_size); + let mut get_content_size = + || independent_layout.content_block_size.into(); + let min_block_size = min_box_size + .block + .resolve_non_initial(stretch_size, &mut get_content_size) + .unwrap_or_default(); + let max_block_size = max_box_size + .block + .resolve_non_initial(stretch_size, &mut get_content_size); + let block_size = tentative_block_size + .auto_is(|| independent_layout.content_block_size) + .clamp_between_extremums(min_block_size, max_block_size); + (block_size, inline_size) + }, }; content_size = LogicalVec2 { inline: inline_size, diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index 0d97e45b2d2..d58fec63fbb 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -16,6 +16,7 @@ use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr; use style::Zero; use style_traits::CSSPixel; +use crate::sizing::ContentSizes; use crate::ContainingBlock; pub type PhysicalPoint = euclid::Point2D; @@ -656,12 +657,24 @@ impl Default for Size { } } -impl Size { +impl Size { + #[inline] + pub(crate) fn fit_content() -> Self { + Self::Keyword(SizeKeyword::FitContent) + } + #[inline] pub(crate) fn is_keyword(&self) -> bool { matches!(self, Self::Keyword(_)) } + #[inline] + pub(crate) fn is_initial(&self) -> bool { + matches!(self, Self::Keyword(SizeKeyword::Initial)) + } +} + +impl Size { #[inline] pub(crate) fn to_numeric(&self) -> Option { match self { @@ -761,3 +774,66 @@ impl LogicalVec2> { } } } + +impl Size { + /// Resolves any size into a numerical value. + #[inline] + pub(crate) fn resolve( + &self, + get_initial_behavior: impl Fn() -> Self, + stretch_size: Au, + get_content_size: &mut impl FnMut() -> ContentSizes, + ) -> Au { + if self.is_initial() { + let initial_behavior = get_initial_behavior(); + assert!(!initial_behavior.is_initial()); + initial_behavior.resolve_non_initial(stretch_size, get_content_size) + } else { + self.resolve_non_initial(stretch_size, get_content_size) + } + .unwrap() + } + + /// Resolves a non-initial size into a numerical value. + /// Returns `None` if the size is the initial one. + #[inline] + pub(crate) fn resolve_non_initial( + &self, + stretch_size: Au, + get_content_size: &mut impl FnMut() -> ContentSizes, + ) -> Option { + match self { + Self::Keyword(SizeKeyword::Initial) => None, + Self::Keyword(SizeKeyword::MinContent) => Some(get_content_size().min_content), + Self::Keyword(SizeKeyword::MaxContent) => Some(get_content_size().max_content), + Self::Keyword(SizeKeyword::FitContent) => { + Some(get_content_size().shrink_to_fit(stretch_size)) + }, + Self::Keyword(SizeKeyword::Stretch) => Some(stretch_size), + Self::Numeric(numeric) => Some(*numeric), + } + } + + /// Tries to resolve an extrinsic size into a numerical value. + /// Extrinsic sizes are those based on the context of an element, without regard for its contents. + /// + /// + /// Returns `None` if either: + /// - The size is intrinsic. + /// - The size is the initial one. + /// TODO: should we allow it to behave as `stretch` instead of assuming it's intrinsic? + /// - The provided `stretch_size` is `None` but we need its value. + #[inline] + pub(crate) fn maybe_resolve_extrinsic(&self, stretch_size: Option) -> Option { + match self { + Self::Keyword(keyword) => match keyword { + SizeKeyword::Initial | + SizeKeyword::MinContent | + SizeKeyword::MaxContent | + SizeKeyword::FitContent => None, + SizeKeyword::Stretch => stretch_size, + }, + Self::Numeric(numeric) => Some(*numeric), + } + } +} diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 77470263559..0139c8a4ab6 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -565459,6 +565459,13 @@ {} ] ], + "keyword-sizes-on-floated-element.html": [ + "adfffba8061aa983800822746a0cd5fc8f6f0fa8", + [ + null, + {} + ] + ], "min-max-content-orthogonal-flow-crash-001.html": [ "d2617f8aa2d1c966e394abb1d1617c012ea4648e", [ diff --git a/tests/wpt/tests/css/css-sizing/keyword-sizes-on-floated-element.html b/tests/wpt/tests/css/css-sizing/keyword-sizes-on-floated-element.html new file mode 100644 index 00000000000..adfffba8061 --- /dev/null +++ b/tests/wpt/tests/css/css-sizing/keyword-sizes-on-floated-element.html @@ -0,0 +1,142 @@ + +Keyword sizes on floated element + + + + + + + + + +
+ + +
+
X X
+
X X
+
X X
+ +
XXX XXX
+
XXX XXX
+
XXX XXX
+ +
XXXXX XXXXX
+
XXXXX XXXXX
+
XXXXX XXXXX
+ +
+ +
X X
+
X X
+
X X
+ +
XXX XXX
+
XXX XXX
+
XXX XXX
+ +
XXXXX XXXXX
+
XXXXX XXXXX
+
XXXXX XXXXX
+ +
+ +
X X
+
X X
+
X X
+ +
XXX XXX
+
XXX XXX
+
XXX XXX
+ +
XXXXX XXXXX
+
XXXXX XXXXX
+
XXXXX XXXXX
+ +
+ +
X X
+
X X
+
X X
+ +
X X
+
X X
+
X X
+ +
X X
+
X X
+
X X
+
+ + +
+
X X
+
XXX XXX
+
XXXXX XXXXX
+ +
X X
+
XXX XXX
+
XXXXX XXXXX
+ +
X X
+
XXX XXX
+
XXXXX XXXXX
+ +
X X
+
XXX XXX<
+
XXXXX XXXXX
+ +
X X
+
XXX XXX
+
XXXXX XXXXX
+ +
X X
+
XXX XXX
+
XXXXX XXXXX
+
+ + +
+
X X
+
XXX XXX
+
XXXXX XXXXX
+ +
X X
+
XXX XXX
+
XXXXX XXXXX
+ +
X X
+
XXX XXX
+
XXXXX XXXXX
+
+ + + + +