diff options
author | Oriol Brufau <obrufau@igalia.com> | 2024-12-18 16:52:18 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-19 00:52:18 +0000 |
commit | e2a0ac07ff2aeece5485f491210e77ebc2af127c (patch) | |
tree | c25df0f0f89a7ef23920017d508cb970e7a2aec7 /components | |
parent | 28e330c9b6bf4edfdb42172968268fba85ceecf5 (diff) | |
download | servo-e2a0ac07ff2aeece5485f491210e77ebc2af127c.tar.gz servo-e2a0ac07ff2aeece5485f491210e77ebc2af127c.zip |
Refactor box size computation (#34671)
in each layout logic, in order to correctly resolve sizing keywords.
This patch adds a new `Sizes` struct which holds the preferred, min and
max sizing values for one axis, and unifies the logic to resolve the
final size into there.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components')
-rw-r--r-- | components/layout_2020/flexbox/layout.rs | 44 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 259 | ||||
-rw-r--r-- | components/layout_2020/geom.rs | 119 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 76 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 70 | ||||
-rw-r--r-- | components/layout_2020/sizing.rs | 33 | ||||
-rw-r--r-- | components/layout_2020/style_ext.rs | 29 | ||||
-rw-r--r-- | components/layout_2020/taffy/layout.rs | 24 |
8 files changed, 311 insertions, 343 deletions
diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 668477cbd32..84cc08232fc 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -34,7 +34,7 @@ use crate::formatting_contexts::{ Baselines, IndependentFormattingContextContents, IndependentLayout, }; use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags}; -use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, Size}; +use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes}; use crate::positioned::{ relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, }; @@ -1957,20 +1957,22 @@ impl FlexItem<'_> { let item_style = independent_formatting_context.style(); match &independent_formatting_context.contents { IndependentFormattingContextContents::Replaced(replaced) => { + let min_size = flex_axis.vec2_to_flow_relative(self.content_min_size); + let max_size = flex_axis.vec2_to_flow_relative(self.content_max_size); let size = replaced.used_size_as_if_inline_element_from_content_box_sizes( containing_block, item_style, self.preferred_aspect_ratio, - LogicalVec2 { - inline: Size::Numeric(inline_size), - block: block_size.non_auto().map_or(Size::Initial, Size::Numeric), - }, - flex_axis - .vec2_to_flow_relative(self.content_min_size) - .map(|size| Size::Numeric(*size)), - flex_axis - .vec2_to_flow_relative(self.content_max_size) - .map(|size| size.map_or(Size::Initial, Size::Numeric)), + &Sizes::new( + block_size.non_auto().map_or(Size::Initial, Size::Numeric), + Size::Numeric(min_size.block), + max_size.block.map_or(Size::Initial, Size::Numeric), + ), + &Sizes::new( + Size::Numeric(inline_size), + Size::Numeric(min_size.inline), + max_size.inline.map_or(Size::Initial, Size::Numeric), + ), flex_axis.vec2_to_flow_relative(self.pbm_auto_is_zero), ); let hypothetical_cross_size = flex_axis.vec2_to_flex_relative(size).cross; @@ -2777,10 +2779,22 @@ impl FlexItemBox { flex_context.containing_block, style, preferred_aspect_ratio, - content_box_size - .map(|size| size.non_auto().map_or(Size::Initial, Size::Numeric)), - min_size.map(|size| Size::Numeric(*size)), - max_size.map(|size| size.map_or(Size::Initial, Size::Numeric)), + &Sizes::new( + content_box_size + .block + .non_auto() + .map_or(Size::Initial, Size::Numeric), + Size::Numeric(min_size.block), + max_size.block.map_or(Size::Initial, Size::Numeric), + ), + &Sizes::new( + content_box_size + .inline + .non_auto() + .map_or(Size::Initial, Size::Numeric), + Size::Numeric(min_size.inline), + max_size.inline.map_or(Size::Initial, Size::Numeric), + ), padding_border_margin.padding_border_sums + padding_border_margin.margin.auto_is(Au::zero).sum(), ) diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index d66ab0332a5..e94301391e2 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -5,8 +5,6 @@ //! Flow layout, also known as block-and-inline layout. -use std::cell::LazyCell; - use app_units::Au; use inline::InlineFormattingContext; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -35,7 +33,7 @@ use crate::fragment_tree::{ }; use crate::geom::{ AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, - Size, ToLogical, ToLogicalWithContainingBlock, + Size, Sizes, ToLogical, ToLogicalWithContainingBlock, }; use crate::layout_box_base::LayoutBoxBase; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; @@ -796,9 +794,7 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context( let ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm, - preferred_block_size, - min_block_size, - max_block_size, + block_sizes, depends_on_block_constraints, available_block_size, } = solve_containing_block_padding_and_border_for_in_flow_box( @@ -939,16 +935,12 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context( content_block_size += collapsible_margins_in_children.end.solve(); } - let available_block_size = available_block_size.unwrap_or(content_block_size); - let block_content_sizes = LazyCell::new(|| content_block_size.into()); - let preferred_block_size = - preferred_block_size.resolve(Size::FitContent, available_block_size, &block_content_sizes); - let min_block_size = min_block_size - .resolve_non_initial(available_block_size, &block_content_sizes) - .unwrap_or_default(); - let max_block_size = - max_block_size.resolve_non_initial(available_block_size, &block_content_sizes); - let block_size = preferred_block_size.clamp_between_extremums(min_block_size, max_block_size); + let block_size = block_sizes.resolve( + Size::FitContent, + Au::zero(), + available_block_size.unwrap_or(content_block_size), + || content_block_size.into(), + ); if let Some(ref mut sequential_layout_state) = sequential_layout_state { // Now that we're done laying out our children, we can restore the @@ -1042,9 +1034,7 @@ impl IndependentNonReplacedContents { let ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm, - preferred_block_size, - min_block_size, - max_block_size, + block_sizes, depends_on_block_constraints, available_block_size, } = solve_containing_block_padding_and_border_for_in_flow_box( @@ -1063,21 +1053,12 @@ impl IndependentNonReplacedContents { let (block_size, inline_size) = match layout.content_inline_size_for_table { Some(inline_size) => (layout.content_block_size, inline_size), None => { - let available_block_size = - available_block_size.unwrap_or(layout.content_block_size); - let block_content_sizes = LazyCell::new(|| layout.content_block_size.into()); - let preferred_block_size = preferred_block_size.resolve( + let block_size = block_sizes.resolve( Size::FitContent, - available_block_size, - &block_content_sizes, + Au::zero(), + available_block_size.unwrap_or(layout.content_block_size), + || layout.content_block_size.into(), ); - let min_block_size = min_block_size - .resolve_non_initial(available_block_size, &block_content_sizes) - .unwrap_or_default(); - let max_block_size = - max_block_size.resolve_non_initial(available_block_size, &block_content_sizes); - let block_size = - preferred_block_size.clamp_between_extremums(min_block_size, max_block_size); (block_size, containing_block_for_children.size.inline) }, }; @@ -1137,9 +1118,7 @@ impl IndependentNonReplacedContents { let style = &base.style; let containing_block_writing_mode = containing_block.style.writing_mode; let ContentBoxSizesAndPBM { - content_box_size, - content_min_box_size, - content_max_box_size, + content_box_sizes, pbm, depends_on_block_constraints, } = style.content_box_sizes_and_padding_border_margin(&containing_block.into()); @@ -1181,21 +1160,14 @@ impl IndependentNonReplacedContents { .block .non_auto() .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum())); - let preferred_block_size = content_box_size - .block - .maybe_resolve_extrinsic(available_block_size); - let min_block_size = content_min_box_size - .block - .maybe_resolve_extrinsic(available_block_size) - .unwrap_or_default(); - let max_block_size = content_max_box_size + let (preferred_block_size, min_block_size, max_block_size) = content_box_sizes .block - .maybe_resolve_extrinsic(available_block_size); + .resolve_each_extrinsic(Size::FitContent, Au::zero(), available_block_size); let tentative_block_size = SizeConstraint::new(preferred_block_size, min_block_size, max_block_size); // With the tentative block size we can compute the inline min/max-content sizes. - let inline_content_sizes = LazyCell::new(|| { + let get_inline_content_sizes = || { let constraint_space = ConstraintSpace::new( tentative_block_size, style.writing_mode, @@ -1203,55 +1175,29 @@ impl IndependentNonReplacedContents { ); base.inline_content_sizes(layout_context, &constraint_space, self) .sizes - }); + }; - // The final inline size can depend on the available space, which depends on where - // we are placing the box, since floats reduce the available space. - // TODO: this logic could be refined further, since even if some of the 3 sizes - // depends on the available space, the resulting size might not. For example, - // `min-width: 200px; width: 100px; max-width: stretch`. - let inline_size_depends_on_available_space = matches!( - content_box_size.inline, - Size::Initial | Size::Stretch | Size::FitContent - ) || matches!( - content_min_box_size.inline, - Size::Stretch | Size::FitContent - ) || matches!( - content_max_box_size.inline, - Size::Stretch | Size::FitContent - ); let compute_inline_size = |stretch_size| { - let preferred_inline_size = - content_box_size - .inline - .resolve(Size::Stretch, stretch_size, &inline_content_sizes); - let min_inline_size = content_min_box_size - .inline - .resolve_non_initial(stretch_size, &inline_content_sizes) - .unwrap_or_default(); - let max_inline_size = content_max_box_size - .inline - .resolve_non_initial(stretch_size, &inline_content_sizes); - preferred_inline_size.clamp_between_extremums(min_inline_size, max_inline_size) + content_box_sizes.inline.resolve( + Size::Stretch, + Au::zero(), + stretch_size, + get_inline_content_sizes, + ) }; let compute_block_size = |layout: &IndependentLayout| { - let stretch_size = available_block_size.unwrap_or(layout.content_block_size); - let block_contents_size = LazyCell::new(|| layout.content_block_size.into()); - let min_block_size = content_min_box_size - .block - .resolve_non_initial(stretch_size, &block_contents_size) - .unwrap_or_default(); - let max_block_size = content_max_box_size - .block - .resolve_non_initial(stretch_size, &block_contents_size); - tentative_block_size - .to_definite() - .unwrap_or(layout.content_block_size) - .clamp_between_extremums(min_block_size, max_block_size) + content_box_sizes.block.resolve( + Size::FitContent, + Au::zero(), + available_block_size.unwrap_or(layout.content_block_size), + || layout.content_block_size.into(), + ) }; - if !inline_size_depends_on_available_space { + // The final inline size can depend on the available space, which depends on where + // we are placing the box, since floats reduce the available space. + if !content_box_sizes.inline.depends_on_available_space() { // If the inline size doesn't depend on the available inline space, we can just // compute it with an available inline space of zero. Then, after layout we can // compute the block size, and finally place among floats. @@ -1591,9 +1537,7 @@ impl ReplacedContents { struct ContainingBlockPaddingAndBorder<'a> { containing_block: ContainingBlock<'a>, pbm: PaddingBorderMargin, - preferred_block_size: Size<Au>, - min_block_size: Size<Au>, - max_block_size: Size<Au>, + block_sizes: Sizes, depends_on_block_constraints: bool, available_block_size: Option<Au>, } @@ -1637,9 +1581,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>( return ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm: PaddingBorderMargin::zero(), - preferred_block_size: Size::default(), - min_block_size: Size::default(), - max_block_size: Size::default(), + block_sizes: Sizes::default(), depends_on_block_constraints: false, // The available block size may actually be definite, but it should be irrelevant // since the sizing properties are set to their initial value. @@ -1648,9 +1590,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>( } let ContentBoxSizesAndPBM { - content_box_size, - content_min_box_size, - content_max_box_size, + content_box_sizes, pbm, depends_on_block_constraints, } = style.content_box_sizes_and_padding_border_margin(&containing_block.into()); @@ -1668,42 +1608,27 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>( // https://drafts.csswg.org/css2/#the-height-property // https://drafts.csswg.org/css2/visudet.html#min-max-heights - let preferred_block_size = content_box_size - .block - .maybe_resolve_extrinsic(available_block_size); - let min_block_size = content_min_box_size - .block - .maybe_resolve_extrinsic(available_block_size) - .unwrap_or_default(); - let max_block_size = content_max_box_size - .block - .maybe_resolve_extrinsic(available_block_size); - let tentative_block_size = - SizeConstraint::new(preferred_block_size, min_block_size, max_block_size); + let tentative_block_size = content_box_sizes.block.resolve_extrinsic( + Size::FitContent, + Au::zero(), + available_block_size, + ); // https://drafts.csswg.org/css2/#the-width-property // https://drafts.csswg.org/css2/visudet.html#min-max-widths - let inline_content_sizes = LazyCell::new(|| { + let get_inline_content_sizes = || { get_inline_content_sizes(&ConstraintSpace::new( tentative_block_size, writing_mode, None, /* TODO: support preferred aspect ratios on non-replaced boxes */ )) - }); - let preferred_inline_size = content_box_size.inline.resolve( + }; + let inline_size = content_box_sizes.inline.resolve( Size::Stretch, + Au::zero(), available_inline_size, - &inline_content_sizes, + get_inline_content_sizes, ); - let min_inline_size = content_min_box_size - .inline - .resolve_non_initial(available_inline_size, &inline_content_sizes) - .unwrap_or_default(); - let max_inline_size = content_max_box_size - .inline - .resolve_non_initial(available_inline_size, &inline_content_sizes); - let inline_size = - preferred_inline_size.clamp_between_extremums(min_inline_size, max_inline_size); let containing_block_for_children = ContainingBlock { size: ContainingBlockSize { @@ -1724,9 +1649,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>( ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm, - preferred_block_size: content_box_size.block, - min_block_size: content_min_box_size.block, - max_block_size: content_max_box_size.block, + block_sizes: content_box_sizes.block, depends_on_block_constraints, available_block_size, } @@ -2182,23 +2105,12 @@ impl IndependentFormattingContext { .block .non_auto() .map(|block_size| (block_size - pbm_sums.block_sum()).max(Au::zero())); - let preferred_block_size = content_box_sizes_and_pbm - .content_box_size - .block - .maybe_resolve_extrinsic(available_block_size); - let min_block_size = content_box_sizes_and_pbm - .content_min_box_size + let tentative_block_size = content_box_sizes_and_pbm + .content_box_sizes .block - .maybe_resolve_extrinsic(available_block_size) - .unwrap_or_default(); - let max_block_size = content_box_sizes_and_pbm - .content_max_box_size - .block - .maybe_resolve_extrinsic(available_block_size); - let tentative_block_size = - SizeConstraint::new(preferred_block_size, min_block_size, max_block_size); + .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size); - let content_size = LazyCell::new(|| { + let get_content_size = || { let constraint_space = ConstraintSpace::new( tentative_block_size, writing_mode, @@ -2206,29 +2118,14 @@ impl IndependentFormattingContext { ); self.inline_content_sizes(layout_context, &constraint_space) .sizes - }); + }; - // https://drafts.csswg.org/css2/visudet.html#float-width - // https://drafts.csswg.org/css2/visudet.html#inlineblock-width - let tentative_inline_size = content_box_sizes_and_pbm - .content_box_size - .inline - .resolve(Size::FitContent, available_inline_size, &content_size); - - // https://drafts.csswg.org/css2/visudet.html#min-max-widths - // In this case “applying the rules above again” with a non-auto inline-size - // always results in that size. - let min_inline_size = content_box_sizes_and_pbm - .content_min_box_size - .inline - .resolve_non_initial(available_inline_size, &content_size) - .unwrap_or_default(); - let max_inline_size = content_box_sizes_and_pbm - .content_max_box_size - .inline - .resolve_non_initial(available_inline_size, &content_size); - let inline_size = - tentative_inline_size.clamp_between_extremums(min_inline_size, max_inline_size); + let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve( + Size::FitContent, + Au::zero(), + available_inline_size, + get_content_size, + ); let containing_block_for_children = ContainingBlock { size: ContainingBlockSize { @@ -2249,31 +2146,21 @@ impl IndependentFormattingContext { &containing_block_for_children, containing_block, ); - let (inline_size, block_size) = - match independent_layout.content_inline_size_for_table { - Some(inline) => (inline, independent_layout.content_block_size), - None => { - // https://drafts.csswg.org/css2/visudet.html#block-root-margin - let stretch_size = available_block_size - .unwrap_or(independent_layout.content_block_size); - let content_size = - LazyCell::new(|| independent_layout.content_block_size.into()); - let min_block_size = content_box_sizes_and_pbm - .content_min_box_size - .block - .resolve_non_initial(stretch_size, &content_size) - .unwrap_or_default(); - let max_block_size = content_box_sizes_and_pbm - .content_max_box_size - .block - .resolve_non_initial(stretch_size, &content_size); - let block_size = tentative_block_size - .to_definite() - .unwrap_or(independent_layout.content_block_size) - .clamp_between_extremums(min_block_size, max_block_size); - (inline_size, block_size) - }, - }; + let (inline_size, block_size) = match independent_layout + .content_inline_size_for_table + { + Some(inline) => (inline, independent_layout.content_block_size), + None => { + // https://drafts.csswg.org/css2/visudet.html#block-root-margin + let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve( + Size::FitContent, + Au::zero(), + available_block_size.unwrap_or(independent_layout.content_block_size), + || independent_layout.content_block_size.into(), + ); + (inline_size, block_size) + }, + }; let content_size = LogicalVec2 { block: block_size, diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index 1684e0304a3..4df12676c50 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -870,3 +870,122 @@ impl SizeConstraint { .map_or(AutoOr::Auto, AutoOr::LengthPercentage) } } + +#[derive(Clone, Default)] +pub(crate) struct Sizes { + /// <https://drafts.csswg.org/css-sizing-3/#preferred-size-properties> + pub preferred: Size<Au>, + /// <https://drafts.csswg.org/css-sizing-3/#min-size-properties> + pub min: Size<Au>, + /// <https://drafts.csswg.org/css-sizing-3/#max-size-properties> + pub max: Size<Au>, +} + +impl Sizes { + #[inline] + pub(crate) fn new(preferred: Size<Au>, min: Size<Au>, max: Size<Au>) -> Self { + Self { + preferred, + min, + max, + } + } + + #[inline] + pub(crate) fn depends_on_available_space(&self) -> bool { + // TODO: this logic could be refined further, since even if some of the 3 sizes + // depends on the available space, the resulting size might not. For example, + // `min-width: 200px; width: 100px; max-width: stretch`. + matches!( + self.preferred, + Size::Initial | Size::Stretch | Size::FitContent + ) || matches!(self.min, Size::Stretch | Size::FitContent) || + matches!(self.max, Size::Stretch | Size::FitContent) + } + + /// Resolves the three sizes into a single numerical value. + #[inline] + pub(crate) fn resolve( + &self, + automatic_size: Size<Au>, + automatic_minimum_size: Au, + stretch_size: Au, + get_content_size: impl FnOnce() -> ContentSizes, + ) -> Au { + let (preferred, min, max) = self.resolve_each( + automatic_size, + automatic_minimum_size, + stretch_size, + get_content_size, + ); + preferred.clamp_between_extremums(min, max) + } + + /// Resolves each of the three sizes into a numerical value, separately. + #[inline] + pub(crate) fn resolve_each( + &self, + automatic_size: Size<Au>, + automatic_minimum_size: Au, + stretch_size: Au, + get_content_size: impl FnOnce() -> ContentSizes, + ) -> (Au, Au, Option<Au>) { + // The provided `get_content_size` is a FnOnce but we may need its result multiple times. + // A LazyCell will only invoke it once if needed, and then reuse the result. + let content_size = LazyCell::new(get_content_size); + ( + self.preferred + .resolve(automatic_size, stretch_size, &content_size), + self.min + .resolve_non_initial(stretch_size, &content_size) + .unwrap_or(automatic_minimum_size), + self.max.resolve_non_initial(stretch_size, &content_size), + ) + } + + /// Tries to extrinsically resolve the three sizes into a single [`SizeConstraint`]. + /// Values that are intrinsic or need `stretch_size` when it's `None` are handled as such: + /// - On the preferred size, they make the returned value be an indefinite [`SizeConstraint::MinMax`]. + /// - On the min size, they are treated as `auto`, enforcing the automatic minimum size. + /// - On the max size, they are treated as `none`, enforcing no maximum. + #[inline] + pub(crate) fn resolve_extrinsic( + &self, + automatic_size: Size<Au>, + automatic_minimum_size: Au, + stretch_size: Option<Au>, + ) -> SizeConstraint { + let (preferred, min, max) = + self.resolve_each_extrinsic(automatic_size, automatic_minimum_size, stretch_size); + SizeConstraint::new(preferred, min, max) + } + + /// Tries to extrinsically resolve each of the three sizes into a numerical value, separately. + /// This can't resolve values that are intrinsic or need `stretch_size` but it's `None`. + /// - The 1st returned value is the resolved preferred size. If it can't be resolved then + /// the returned value is `None`. Note that this is different than treating it as `auto`. + /// TODO: This needs to be discussed in <https://github.com/w3c/csswg-drafts/issues/11387>. + /// - The 2nd returned value is the resolved minimum size. If it can't be resolved then we + /// treat it as the initial `auto`, returning the automatic minimum size. + /// - The 3rd returned value is the resolved maximum size. If it can't be resolved then we + /// treat it as the initial `none`, returning `None`. + #[inline] + pub(crate) fn resolve_each_extrinsic( + &self, + automatic_size: Size<Au>, + automatic_minimum_size: Au, + stretch_size: Option<Au>, + ) -> (Option<Au>, Au, Option<Au>) { + ( + if self.preferred.is_initial() { + automatic_size.maybe_resolve_extrinsic(stretch_size) + } else { + self.preferred.maybe_resolve_extrinsic(stretch_size) + }, + self.min + .maybe_resolve_extrinsic(stretch_size) + .unwrap_or(automatic_minimum_size), + self.max.maybe_resolve_extrinsic(stretch_size), + ) + } +} diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 9841f9b6c86..0b1ba1e02e6 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::cell::LazyCell; use std::mem; use app_units::Au; @@ -28,10 +27,10 @@ use crate::fragment_tree::{ }; use crate::geom::{ AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint, - PhysicalRect, PhysicalVec, Size, ToLogical, ToLogicalWithContainingBlock, + PhysicalRect, PhysicalVec, Size, Sizes, ToLogical, ToLogicalWithContainingBlock, }; use crate::sizing::ContentSizes; -use crate::style_ext::{ComputedValuesExt, DisplayInside}; +use crate::style_ext::{ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside}; use crate::{ ConstraintSpace, ContainingBlock, ContainingBlockSize, DefiniteContainingBlock, SizeConstraint, }; @@ -460,11 +459,12 @@ impl HoistedAbsolutelyPositionedBox { let absolutely_positioned_box = self.absolutely_positioned_box.borrow(); let context = &absolutely_positioned_box.context; let style = context.style().clone(); + let ContentBoxSizesAndPBM { + content_box_sizes, + pbm, + .. + } = style.content_box_sizes_and_padding_border_margin(&containing_block.into()); let containing_block = &containing_block.into(); - let pbm = style.padding_border_margin(containing_block); - let computed_size = style.content_box_size(containing_block, &pbm); - let computed_min_size = style.content_min_box_size(containing_block, &pbm); - let computed_max_size = style.content_max_box_size(containing_block, &pbm); let shared_fragment = self.fragment.borrow(); let static_position_rect = shared_fragment @@ -489,9 +489,7 @@ impl HoistedAbsolutelyPositionedBox { padding_border_sum: pbm.padding_border_sums.inline, computed_margin_start: pbm.margin.inline_start, computed_margin_end: pbm.margin.inline_end, - computed_size: computed_size.inline, - computed_min_size: computed_min_size.inline, - computed_max_size: computed_max_size.inline, + computed_sizes: content_box_sizes.inline, avoid_negative_margin_start: true, box_offsets: inline_box_offsets, static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Inline), @@ -515,9 +513,7 @@ impl HoistedAbsolutelyPositionedBox { padding_border_sum: pbm.padding_border_sums.block, computed_margin_start: pbm.margin.block_start, computed_margin_end: pbm.margin.block_end, - computed_size: computed_size.block, - computed_min_size: computed_min_size.block, - computed_max_size: computed_max_size.block, + computed_sizes: content_box_sizes.block, avoid_negative_margin_start: false, box_offsets: block_box_offsets, static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Block), @@ -536,9 +532,8 @@ impl HoistedAbsolutelyPositionedBox { containing_block, &style, context.preferred_aspect_ratio(&pbm.padding_border_sums), - computed_size, - computed_min_size, - computed_max_size, + &block_axis_solver.computed_sizes, + &inline_axis_solver.computed_sizes, pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums, ); inline_axis_solver.override_size(used_size.inline); @@ -746,9 +741,7 @@ struct AbsoluteAxisSolver<'a> { padding_border_sum: Au, computed_margin_start: AuOrAuto, computed_margin_end: AuOrAuto, - computed_size: Size<Au>, - computed_min_size: Size<Au>, - computed_max_size: Size<Au>, + computed_sizes: Sizes, avoid_negative_margin_start: bool, box_offsets: AbsoluteBoxOffsets<'a>, static_position_rect_axis: RectAxis, @@ -795,40 +788,21 @@ impl<'a> AbsoluteAxisSolver<'a> { /// /// In the replaced case, `size` is never `Auto`. fn solve(&self, get_content_size: Option<impl FnOnce() -> ContentSizes>) -> AxisResult { - // The provided `get_content_size` is a FnOnce but we may need its result multiple times. - // A LazyCell will only invoke it once if needed, and then reuse the result. - let content_size = get_content_size.map(LazyCell::new); let solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint { let stretch_size = stretch_size.max(Au::zero()); - if let Some(ref content_size) = content_size { - let preferred_size = Some(self.computed_size.resolve( + if let Some(get_content_size) = get_content_size { + SizeConstraint::Definite(self.computed_sizes.resolve( initial_behavior, + Au::zero(), stretch_size, - content_size, - )); - let min_size = self - .computed_min_size - .resolve_non_initial(stretch_size, content_size) - .unwrap_or_default(); - let max_size = self - .computed_max_size - .resolve_non_initial(stretch_size, content_size); - SizeConstraint::new(preferred_size, min_size, max_size) + get_content_size, + )) } else { - let preferred_size = if self.computed_size.is_initial() { - initial_behavior - } else { - self.computed_size - } - .maybe_resolve_extrinsic(Some(stretch_size)); - let min_size = self - .computed_min_size - .maybe_resolve_extrinsic(Some(stretch_size)) - .unwrap_or_default(); - let max_size = self - .computed_max_size - .maybe_resolve_extrinsic(Some(stretch_size)); - SizeConstraint::new(preferred_size, min_size, max_size) + self.computed_sizes.resolve_extrinsic( + initial_behavior, + Au::zero(), + Some(stretch_size), + ) } }; if self.box_offsets.either_auto() { @@ -891,9 +865,9 @@ impl<'a> AbsoluteAxisSolver<'a> { } fn override_size(&mut self, size: Au) { - self.computed_size = Size::Numeric(size); - self.computed_min_size = Size::default(); - self.computed_max_size = Size::default(); + self.computed_sizes.preferred = Size::Numeric(size); + self.computed_sizes.min = Size::default(); + self.computed_sizes.max = Size::default(); } fn origin_for_margin_box( diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 86345e0b8f5..df845841438 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -30,7 +30,7 @@ use webrender_api::ImageKey; use crate::context::LayoutContext; use crate::dom::NodeExt; use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment}; -use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size}; +use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size, Sizes}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM}; use crate::{ConstraintSpace, ContainingBlock, SizeConstraint}; @@ -439,9 +439,8 @@ impl ReplacedContents { containing_block, style, self.preferred_aspect_ratio(style, &pbm.padding_border_sums), - content_box_sizes_and_pbm.content_box_size, - content_box_sizes_and_pbm.content_min_box_size, - content_box_sizes_and_pbm.content_max_box_size, + &content_box_sizes_and_pbm.content_box_sizes.block, + &content_box_sizes_and_pbm.content_box_sizes.inline, pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum(), ) } @@ -481,15 +480,13 @@ impl ReplacedContents { /// /// <https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers> /// <https://github.com/w3c/csswg-drafts/issues/6071#issuecomment-2243986313> - #[allow(clippy::too_many_arguments)] pub(crate) fn used_size_as_if_inline_element_from_content_box_sizes( &self, containing_block: &ContainingBlock, style: &ComputedValues, preferred_aspect_ratio: Option<AspectRatio>, - box_size: LogicalVec2<Size<Au>>, - min_box_size: LogicalVec2<Size<Au>>, - max_box_size: LogicalVec2<Size<Au>>, + block_sizes: &Sizes, + inline_sizes: &Sizes, pbm_sums: LogicalVec2<Au>, ) -> LogicalVec2<Au> { // <https://drafts.csswg.org/css-images-3/#natural-dimensions> @@ -520,19 +517,9 @@ impl ReplacedContents { // First, compute the inline size. Intrinsic values depend on the block sizing properties // through the aspect ratio, but these can also be intrinsic and depend on the inline size. // Therefore, we tentatively treat intrinsic block sizing properties as their initial value. - let inline_content_size = LazyCell::new(|| { - let get_block_size = || { - SizeConstraint::new( - box_size.block.maybe_resolve_extrinsic(block_stretch_size), - min_box_size - .block - .maybe_resolve_extrinsic(block_stretch_size) - .unwrap_or_default(), - max_box_size - .block - .maybe_resolve_extrinsic(block_stretch_size), - ) - }; + let get_inline_content_size = || { + let get_block_size = + || block_sizes.resolve_extrinsic(Size::FitContent, Au::zero(), block_stretch_size); self.content_size( Direction::Inline, preferred_aspect_ratio, @@ -540,24 +527,19 @@ impl ReplacedContents { &get_inline_fallback_size, ) .into() - }); - let preferred_inline = - box_size - .inline - .resolve(Size::FitContent, inline_stretch_size, &inline_content_size); - let min_inline = min_box_size - .inline - .resolve_non_initial(inline_stretch_size, &inline_content_size) - .unwrap_or_default(); - let max_inline = max_box_size - .inline - .resolve_non_initial(inline_stretch_size, &inline_content_size); + }; + let (preferred_inline, min_inline, max_inline) = inline_sizes.resolve_each( + Size::FitContent, + Au::zero(), + inline_stretch_size, + get_inline_content_size, + ); let inline_size = preferred_inline.clamp_between_extremums(min_inline, max_inline); // Now we can compute the block size, using the inline size from above. let block_content_size = LazyCell::new(|| -> ContentSizes { let get_inline_size = || { - if box_size.inline.is_initial() { + if inline_sizes.preferred.is_initial() { // TODO: do we really need to special-case `auto`? // https://github.com/w3c/csswg-drafts/issues/11236 SizeConstraint::MinMax(min_inline, max_inline) @@ -573,20 +555,12 @@ impl ReplacedContents { ) .into() }); - let block_stretch_size = - block_stretch_size.unwrap_or_else(|| block_content_size.max_content); - let preferred_block = - box_size - .block - .resolve(Size::FitContent, block_stretch_size, &block_content_size); - let min_block = min_box_size - .block - .resolve_non_initial(block_stretch_size, &block_content_size) - .unwrap_or_default(); - let max_block = max_box_size - .block - .resolve_non_initial(block_stretch_size, &block_content_size); - let block_size = preferred_block.clamp_between_extremums(min_block, max_block); + let block_size = block_sizes.resolve( + Size::FitContent, + Au::zero(), + block_stretch_size.unwrap_or_else(|| block_content_size.max_content), + || *block_content_size, + ); LogicalVec2 { inline: inline_size, diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 7715e5627da..1e8fe74d6e6 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -15,7 +15,7 @@ use style::Zero; use crate::context::LayoutContext; use crate::geom::Size; use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM}; -use crate::{ConstraintSpace, IndefiniteContainingBlock, LogicalVec2, SizeConstraint}; +use crate::{ConstraintSpace, IndefiniteContainingBlock, LogicalVec2}; #[derive(PartialEq)] pub(crate) enum IntrinsicSizingMode { @@ -118,9 +118,7 @@ pub(crate) fn outer_inline( get_content_size: impl FnOnce(&ConstraintSpace) -> InlineContentSizesResult, ) -> InlineContentSizesResult { let ContentBoxSizesAndPBM { - content_box_size, - content_min_box_size, - content_max_box_size, + content_box_sizes, pbm, mut depends_on_block_constraints, } = style.content_box_sizes_and_padding_border_margin(containing_block); @@ -135,25 +133,20 @@ pub(crate) fn outer_inline( .block .non_auto() .map(|v| Au::zero().max(v - pbm_sums.block)); - let preferred_block_size = if content_box_size.block.is_initial() && + let automatic_size = if content_box_sizes.block.preferred.is_initial() && auto_block_size_stretches_to_containing_block { depends_on_block_constraints = true; - available_block_size + Size::Stretch } else { - content_box_size - .block - .maybe_resolve_extrinsic(available_block_size) + Size::FitContent }; - let min_block_size = content_min_box_size - .block - .maybe_resolve_extrinsic(available_block_size) - .unwrap_or(auto_minimum.block); - let max_block_size = content_max_box_size - .block - .maybe_resolve_extrinsic(available_block_size); get_content_size(&ConstraintSpace::new( - SizeConstraint::new(preferred_block_size, min_block_size, max_block_size), + content_box_sizes.block.resolve_extrinsic( + automatic_size, + auto_minimum.block, + available_block_size, + ), style.writing_mode, get_preferred_aspect_ratio(&pbm.padding_border_sums), )) @@ -180,14 +173,14 @@ pub(crate) fn outer_inline( }) }; let (preferred_min_content, preferred_max_content, preferred_depends_on_block_constraints) = - resolve_non_initial(content_box_size.inline) + resolve_non_initial(content_box_sizes.inline.preferred) .unwrap_or_else(|| resolve_non_initial(Size::FitContent).unwrap()); let (min_min_content, min_max_content, min_depends_on_block_constraints) = resolve_non_initial( - content_min_box_size.inline, + content_box_sizes.inline.min, ) .unwrap_or((auto_minimum.inline, auto_minimum.inline, false)); let (max_min_content, max_max_content, max_depends_on_block_constraints) = - resolve_non_initial(content_max_box_size.inline) + resolve_non_initial(content_box_sizes.inline.max) .map(|(min_content, max_content, depends_on_block_constraints)| { ( Some(min_content), diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 34f95bef7cf..e6733ac998e 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -29,7 +29,7 @@ use crate::dom_traversal::Contents; use crate::fragment_tree::FragmentFlags; use crate::geom::{ AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize, - PhysicalVec, Size, + PhysicalVec, Size, Sizes, }; use crate::{ContainingBlock, IndefiniteContainingBlock}; @@ -189,9 +189,7 @@ impl AspectRatio { #[derive(Clone)] pub(crate) struct ContentBoxSizesAndPBM { - pub content_box_size: LogicalVec2<Size<Au>>, - pub content_min_box_size: LogicalVec2<Size<Au>>, - pub content_max_box_size: LogicalVec2<Size<Au>>, + pub content_box_sizes: LogicalVec2<Sizes>, pub pbm: PaddingBorderMargin, pub depends_on_block_constraints: bool, } @@ -199,9 +197,11 @@ pub(crate) struct ContentBoxSizesAndPBM { impl From<ContentBoxSizesAndPBM> for ContentBoxSizesAndPBMDeprecated { fn from(sizes: ContentBoxSizesAndPBM) -> Self { Self { - content_box_size: sizes.content_box_size.map(Size::to_auto_or), - content_min_box_size: sizes.content_min_box_size.map(Size::to_auto_or), - content_max_box_size: sizes.content_max_box_size.map(Size::to_numeric), + content_box_size: sizes + .content_box_sizes + .map(|size| size.preferred.to_auto_or()), + content_min_box_size: sizes.content_box_sizes.map(|size| size.min.to_auto_or()), + content_max_box_size: sizes.content_box_sizes.map(|size| size.max.to_numeric()), pbm: sizes.pbm.clone(), depends_on_block_constraints: sizes.depends_on_block_constraints, } @@ -560,9 +560,18 @@ impl ComputedValuesExt for ComputedValues { .content_max_box_size_for_max_size(max_size, &pbm) .map(|v| v.map(Au::from)); ContentBoxSizesAndPBM { - content_box_size, - content_min_box_size, - content_max_box_size, + content_box_sizes: LogicalVec2 { + block: Sizes::new( + content_box_size.block, + content_min_box_size.block, + content_max_box_size.block, + ), + inline: Sizes::new( + content_box_size.inline, + content_min_box_size.inline, + content_max_box_size.inline, + ), + }, pbm, depends_on_block_constraints, } diff --git a/components/layout_2020/taffy/layout.rs b/components/layout_2020/taffy/layout.rs index 4ed13aa008b..3cdf162899e 100644 --- a/components/layout_2020/taffy/layout.rs +++ b/components/layout_2020/taffy/layout.rs @@ -22,7 +22,7 @@ use crate::formatting_contexts::{ use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::{ LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size, - SizeConstraint, + SizeConstraint, Sizes, }; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; @@ -155,18 +155,16 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> { style, independent_context .preferred_aspect_ratio(&pbm.padding_border_sums), - LogicalVec2 { - inline: option_f32_to_size(content_box_known_dimensions.width), - block: option_f32_to_size(content_box_known_dimensions.height), - }, - LogicalVec2 { - inline: Size::Numeric(Au::zero()), - block: Size::Numeric(Au::zero()), - }, - LogicalVec2 { - inline: Size::Initial, - block: Size::Initial, - }, + &Sizes::new( + option_f32_to_size(content_box_known_dimensions.height), + Size::Initial, + Size::Initial, + ), + &Sizes::new( + option_f32_to_size(content_box_known_dimensions.width), + Size::Initial, + Size::Initial, + ), pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum(), ) .to_physical_size(self.style.writing_mode); |