diff options
author | Oriol Brufau <obrufau@igalia.com> | 2024-11-11 12:38:19 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-11 11:38:19 +0000 |
commit | b28260aa130ce36230bb01686495845b5523ebc1 (patch) | |
tree | e6ec8b8efb3f1ecc88791660c5996201ead295b7 /components/layout_2020 | |
parent | 72971bd2716a7e574a4c6c3395c7a710652427b4 (diff) | |
download | servo-b28260aa130ce36230bb01686495845b5523ebc1.tar.gz servo-b28260aa130ce36230bb01686495845b5523ebc1.zip |
Fix inline content sizes of intrinsic element with indefinite block size (#34152)
To compute the min-content and max-content inline sizes of a replaced
element, we were only using the aspect ratio to transfer definite block
sizes resulting from clamping the preferred block size between the min
and max block sizes.
However, if the preferred block size is indefinite, then we weren't
transfering the min and max through the aspect ratio.
This patch adds a `SizeConstraint` enum that can represent these cases,
and a `ConstraintSpace` struct analogous to `IndefiniteContainingBlock`
but with no inline size, and a `SizeConstraint` block size.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components/layout_2020')
-rw-r--r-- | components/layout_2020/flexbox/layout.rs | 67 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/mod.rs | 23 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 59 | ||||
-rw-r--r-- | components/layout_2020/formatting_contexts.rs | 45 | ||||
-rw-r--r-- | components/layout_2020/geom.rs | 43 | ||||
-rw-r--r-- | components/layout_2020/lib.rs | 46 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 94 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 142 | ||||
-rw-r--r-- | components/layout_2020/sizing.rs | 36 | ||||
-rw-r--r-- | components/layout_2020/table/layout.rs | 20 |
10 files changed, 297 insertions, 278 deletions
diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 90b179b7f15..a968926cdca 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -40,7 +40,7 @@ use crate::sizing::{ContentSizes, InlineContentSizesResult, IntrinsicSizingMode} use crate::style_ext::{ Clamp, ComputedValuesExt, ContentBoxSizesAndPBMDeprecated, PaddingBorderMargin, }; -use crate::{ContainingBlock, IndefiniteContainingBlock}; +use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SizeConstraint}; /// Layout parameters and intermediate results about a flex container, /// grouped to avoid passing around many parameters @@ -401,19 +401,17 @@ impl FlexContainer { pub fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, ) -> InlineContentSizesResult { match self.config.flex_axis { FlexAxis::Row => { - self.main_content_sizes(layout_context, containing_block_for_children, || { + self.main_content_sizes(layout_context, &constraint_space.into(), || { unreachable!( "Unexpected FlexContext query during row flex intrinsic size calculation." ) }) }, - FlexAxis::Column => { - self.cross_content_sizes(layout_context, containing_block_for_children) - }, + FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()), } } @@ -1882,17 +1880,16 @@ impl FlexItem<'_> { } else { ( cross_size.auto_is(|| { - let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode_and_block_size( - item_writing_mode, - AuOrAuto::LengthPercentage(used_main_size), - ); + let constraint_space = ConstraintSpace::new( + SizeConstraint::Definite(used_main_size), + item_writing_mode, + ); let content_contributions = self .box_ .independent_formatting_context .inline_content_sizes( flex_context.layout_context, - &containing_block_for_children, + &constraint_space, &containing_block.into(), ) .sizes @@ -2474,7 +2471,7 @@ impl FlexItemBox { Direction::Block }; - let cross_size = + let cross_size = SizeConstraint::new( if content_box_size.cross.is_auto() && auto_cross_size_stretches_to_container_size { if cross_axis_is_item_block_axis { containing_block.size.block @@ -2485,14 +2482,17 @@ impl FlexItemBox { } else { content_box_size.cross } - .map(|v| v.clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross)); + .non_auto(), + min_size.cross.auto_is(Au::zero), + max_size.cross, + ); // > **transferred size suggestion** // > If the item has a preferred aspect ratio and its preferred cross size is definite, then the // > transferred size suggestion is that size (clamped by its minimum and maximum cross sizes if they // > are definite), converted through the aspect ratio. It is otherwise undefined. let transferred_size_suggestion = match (ratio, cross_size) { - (Some(ratio), AuOrAuto::LengthPercentage(cross_size)) => { + (Some(ratio), SizeConstraint::Definite(cross_size)) => { Some(ratio.compute_dependent_size(main_axis, cross_size)) }, _ => None, @@ -2504,17 +2504,9 @@ impl FlexItemBox { // > aspect ratio. let main_content_size = if cross_axis_is_item_block_axis { let writing_mode = self.independent_formatting_context.style().writing_mode; - let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode_and_block_size( - writing_mode, - cross_size, - ); + let constraint_space = ConstraintSpace::new(cross_size, writing_mode); self.independent_formatting_context - .inline_content_sizes( - layout_context, - &containing_block_for_children, - containing_block, - ) + .inline_content_sizes(layout_context, &constraint_space, containing_block) .sizes .min_content } else { @@ -2664,23 +2656,16 @@ impl FlexItemBox { // The main axis is the inline axis, so we can get the content size from the normal // preferred widths calculation. let writing_mode = flex_item.style().writing_mode; - let block_size = content_box_size.cross.map(|v| { - v.clamp_between_extremums( + let constraint_space = ConstraintSpace::new( + SizeConstraint::new( + content_box_size.cross.non_auto(), content_min_box_size.cross, content_max_box_size.cross, - ) - }); - let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode_and_block_size( - writing_mode, - block_size, - ); + ), + writing_mode, + ); let max_content = flex_item - .inline_content_sizes( - layout_context, - &containing_block_for_children, - containing_block, - ) + .inline_content_sizes(layout_context, &constraint_space, containing_block) .sizes .max_content; if let Some(ratio) = ratio { @@ -2764,9 +2749,7 @@ impl FlexItemBox { containing_block_inline_size_minus_pbm } else { let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode( - non_replaced.style.writing_mode, - ); + ConstraintSpace::new_for_style(&non_replaced.style); non_replaced .inline_content_sizes( flex_context.layout_context, diff --git a/components/layout_2020/flow/inline/mod.rs b/components/layout_2020/flow/inline/mod.rs index c4de3a9ca42..13714235b0b 100644 --- a/components/layout_2020/flow/inline/mod.rs +++ b/components/layout_2020/flow/inline/mod.rs @@ -128,7 +128,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::sizing::{ContentSizes, InlineContentSizesResult}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; -use crate::{ContainingBlock, IndefiniteContainingBlock}; +use crate::{ConstraintSpace, ContainingBlock}; // From gfxFontConstants.h in Firefox. static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; @@ -1574,9 +1574,9 @@ impl InlineFormattingContext { pub(super) fn inline_content_sizes( &self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, ) -> InlineContentSizesResult { - ContentSizesComputation::compute(self, layout_context, containing_block_for_children) + ContentSizesComputation::compute(self, layout_context, constraint_space) } pub(super) fn layout( @@ -2188,7 +2188,7 @@ fn inline_container_needs_strut( /// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`]. struct ContentSizesComputation<'layout_data> { layout_context: &'layout_data LayoutContext<'layout_data>, - containing_block: &'layout_data IndefiniteContainingBlock, + constraint_space: &'layout_data ConstraintSpace, paragraph: ContentSizes, current_line: ContentSizes, /// Size for whitespace pending to be added to this line. @@ -2234,16 +2234,15 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { let inline_box = inline_formatting_context.inline_boxes.get(identifier); let inline_box = (*inline_box).borrow(); let zero = Au::zero(); + let writing_mode = self.constraint_space.writing_mode; let padding = inline_box .style - .padding(self.containing_block.writing_mode) + .padding(writing_mode) .percentages_relative_to(zero); - let border = inline_box - .style - .border_width(self.containing_block.writing_mode); + let border = inline_box.style.border_width(writing_mode); let margin = inline_box .style - .margin(self.containing_block.writing_mode) + .margin(writing_mode) .percentages_relative_to(zero) .auto_is(Au::zero); @@ -2329,7 +2328,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { depends_on_block_constraints, } = atomic.outer_inline_content_sizes( self.layout_context, - self.containing_block, + &self.constraint_space.into(), &LogicalVec2::zero(), false, /* auto_block_size_stretches_to_containing_block */ ); @@ -2384,11 +2383,11 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { fn compute( inline_formatting_context: &InlineFormattingContext, layout_context: &'layout_data LayoutContext, - containing_block: &'layout_data IndefiniteContainingBlock, + constraint_space: &'layout_data ConstraintSpace, ) -> InlineContentSizesResult { Self { layout_context, - containing_block, + constraint_space, paragraph: ContentSizes::zero(), current_line: ContentSizes::zero(), pending_whitespace: ContentSizes::zero(), diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index a3b4deddccf..94a8bb55931 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -41,7 +41,7 @@ use crate::sizing::{self, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{ Clamp, ComputedValuesExt, ContentBoxSizesAndPBMDeprecated, PaddingBorderMargin, }; -use crate::{ContainingBlock, IndefiniteContainingBlock}; +use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SizeConstraint}; mod construct; pub mod float; @@ -235,7 +235,7 @@ impl OutsideMarker { ) -> Fragment { let content_sizes = self.block_container.inline_content_sizes( layout_context, - &IndefiniteContainingBlock::new_for_writing_mode(self.marker_style.writing_mode), + &ConstraintSpace::new_for_style(&self.marker_style), ); let containing_block_for_children = ContainingBlock { inline_size: content_sizes.sizes.max_content, @@ -393,8 +393,8 @@ fn calculate_inline_content_size_for_block_level_boxes( containing_block, &LogicalVec2::zero(), false, /* auto_block_size_stretches_to_containing_block */ - |containing_block_for_children| { - contents.inline_content_sizes(layout_context, containing_block_for_children) + |constraint_space| { + contents.inline_content_sizes(layout_context, constraint_space) }, ); // A block in the same BFC can overlap floats, it's not moved next to them, @@ -528,16 +528,16 @@ impl BlockContainer { pub(super) fn inline_content_sizes( &self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, ) -> InlineContentSizesResult { match &self { Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes( boxes, layout_context, - containing_block_for_children, + &constraint_space.into(), ), Self::InlineFormattingContext(context) => { - context.inline_content_sizes(layout_context, containing_block_for_children) + context.inline_content_sizes(layout_context, constraint_space) }, } } @@ -2030,39 +2030,33 @@ impl IndependentFormattingContext { (fragments, content_rect, None) }, IndependentFormattingContext::NonReplaced(non_replaced) => { - let style = non_replaced.style.clone(); + let writing_mode = non_replaced.style.writing_mode; let available_inline_size = (containing_block.inline_size - pbm_sums.inline_sum()).max(Au::zero()); let available_block_size = containing_block .block_size .non_auto() .map(|block_size| (block_size - pbm_sums.block_sum()).max(Au::zero())); - let tentative_block_size = content_box_sizes_and_pbm + 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 + .block .maybe_resolve_extrinsic(available_block_size) - .map(|size| { - let min_block_size = content_box_sizes_and_pbm - .content_min_box_size - .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); - size.clamp_between_extremums(min_block_size, max_block_size) - }) - .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); + .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); let mut get_content_size = || { - let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode_and_block_size( - style.writing_mode, - tentative_block_size, - ); + let constraint_space = ConstraintSpace::new(tentative_block_size, writing_mode); non_replaced - .inline_content_sizes(layout_context, &containing_block_for_children) + .inline_content_sizes(layout_context, &constraint_space) .sizes }; @@ -2092,12 +2086,12 @@ impl IndependentFormattingContext { let containing_block_for_children = ContainingBlock { inline_size, - block_size: tentative_block_size, - style: &style, + block_size: tentative_block_size.to_auto_or(), + style: &non_replaced.style, }; assert_eq!( container_writing_mode.is_horizontal(), - style.writing_mode.is_horizontal(), + writing_mode.is_horizontal(), "Mixed horizontal and vertical writing modes are not supported yet" ); @@ -2126,7 +2120,8 @@ impl IndependentFormattingContext { .block .resolve_non_initial(stretch_size, &mut get_content_size); let block_size = tentative_block_size - .auto_is(|| independent_layout.content_block_size) + .to_definite() + .unwrap_or(independent_layout.content_block_size) .clamp_between_extremums(min_block_size, max_block_size); (inline_size, block_size) }, diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index d843ca4d32c..6dd214b1580 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -21,7 +21,9 @@ use crate::replaced::ReplacedContent; use crate::sizing::{self, InlineContentSizesResult}; use crate::style_ext::{AspectRatio, DisplayInside}; use crate::table::Table; -use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2}; +use crate::{ + ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, LogicalVec2, SizeConstraint, +}; /// <https://drafts.csswg.org/css-display/#independent-formatting-context> #[derive(Debug, Serialize)] @@ -36,7 +38,7 @@ pub(crate) struct NonReplacedFormattingContext { #[serde(skip_serializing)] pub style: Arc<ComputedValues>, /// If it was requested during construction - pub content_sizes_result: Option<(AuOrAuto, InlineContentSizesResult)>, + pub content_sizes_result: Option<(SizeConstraint, InlineContentSizesResult)>, pub contents: NonReplacedFormattingContextContents, } @@ -188,16 +190,16 @@ impl IndependentFormattingContext { pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, containing_block: &IndefiniteContainingBlock, ) -> InlineContentSizesResult { match self { Self::NonReplaced(inner) => { - inner.inline_content_sizes(layout_context, containing_block_for_children) + inner.inline_content_sizes(layout_context, constraint_space) }, Self::Replaced(inner) => inner.contents.inline_content_sizes( layout_context, - containing_block_for_children, + constraint_space, inner.preferred_aspect_ratio(containing_block), ), } @@ -222,10 +224,10 @@ impl IndependentFormattingContext { containing_block, auto_minimum, auto_block_size_stretches_to_containing_block, - |containing_block_for_children| { + |constraint_space| { replaced.contents.inline_content_sizes( layout_context, - containing_block_for_children, + constraint_space, replaced.preferred_aspect_ratio(containing_block), ) }, @@ -276,16 +278,11 @@ impl NonReplacedFormattingContext { pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, ) -> InlineContentSizesResult { - assert_eq!( - containing_block_for_children.size.inline, - AuOrAuto::Auto, - "inline_content_sizes() got non-auto containing block inline-size", - ); if let Some((previous_cb_block_size, result)) = self.content_sizes_result { if !result.depends_on_block_constraints || - previous_cb_block_size == containing_block_for_children.size.block + previous_cb_block_size == constraint_space.block_size { return result; } @@ -294,9 +291,9 @@ impl NonReplacedFormattingContext { self.content_sizes_result .insert(( - containing_block_for_children.size.block, + constraint_space.block_size, self.contents - .inline_content_sizes(layout_context, containing_block_for_children), + .inline_content_sizes(layout_context, constraint_space), )) .1 } @@ -313,9 +310,7 @@ impl NonReplacedFormattingContext { containing_block, auto_minimum, auto_block_size_stretches_to_containing_block, - |containing_block_for_children| { - self.inline_content_sizes(layout_context, containing_block_for_children) - }, + |constraint_space| self.inline_content_sizes(layout_context, constraint_space), ) } } @@ -324,18 +319,14 @@ impl NonReplacedFormattingContextContents { pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, ) -> InlineContentSizesResult { match self { Self::Flow(inner) => inner .contents - .inline_content_sizes(layout_context, containing_block_for_children), - Self::Flex(inner) => { - inner.inline_content_sizes(layout_context, containing_block_for_children) - }, - Self::Table(table) => { - table.inline_content_sizes(layout_context, containing_block_for_children) - }, + .inline_content_sizes(layout_context, constraint_space), + Self::Flex(inner) => inner.inline_content_sizes(layout_context, constraint_space), + Self::Table(table) => table.inline_content_sizes(layout_context, constraint_space), } } } diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index a213fc99d31..ba7349ad81f 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -17,6 +17,7 @@ use style::Zero; use style_traits::CSSPixel; use crate::sizing::ContentSizes; +use crate::style_ext::Clamp; use crate::ContainingBlock; pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>; @@ -848,3 +849,45 @@ impl Size<Au> { } } } + +/// Represents the sizing constraint that the preferred, min and max sizing properties +/// impose on one axis. +#[derive(Clone, Copy, Debug, PartialEq, Serialize)] +pub(crate) enum SizeConstraint { + /// Represents a definite preferred size, clamped by minimum and maximum sizes (if any). + Definite(Au), + /// Represents an indefinite preferred size that allows a range of values between + /// the first argument (minimum size) and the second one (maximum size). + MinMax(Au, Option<Au>), +} + +impl Default for SizeConstraint { + #[inline] + fn default() -> Self { + Self::MinMax(Au::default(), None) + } +} + +impl SizeConstraint { + #[inline] + pub(crate) fn new(preferred_size: Option<Au>, min_size: Au, max_size: Option<Au>) -> Self { + preferred_size.map_or_else( + || Self::MinMax(min_size, max_size), + |size| Self::Definite(size.clamp_between_extremums(min_size, max_size)), + ) + } + + #[inline] + pub(crate) fn to_definite(self) -> Option<Au> { + match self { + Self::Definite(size) => Some(size), + _ => None, + } + } + + #[inline] + pub(crate) fn to_auto_or(self) -> AutoOr<Au> { + self.to_definite() + .map_or(AutoOr::Auto, AutoOr::LengthPercentage) + } +} diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 3d00123aa53..5a4dfc5e72b 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -32,34 +32,44 @@ use geom::AuOrAuto; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; -use crate::geom::LogicalVec2; +use crate::geom::{LogicalVec2, SizeConstraint}; -/// A containing block useful for calculating inline content sizes, which may -/// have inline sizes that depend on block sizes due to aspect ratio. -pub(crate) struct IndefiniteContainingBlock { - pub size: LogicalVec2<AuOrAuto>, +/// Represents the set of constraints that we use when computing the min-content +/// and max-content inline sizes of an element. +pub(crate) struct ConstraintSpace { + pub block_size: SizeConstraint, pub writing_mode: WritingMode, } -impl IndefiniteContainingBlock { - fn new_for_writing_mode(writing_mode: WritingMode) -> Self { - Self::new_for_writing_mode_and_block_size(writing_mode, AuOrAuto::Auto) +impl ConstraintSpace { + fn new(block_size: SizeConstraint, writing_mode: WritingMode) -> Self { + Self { + block_size, + writing_mode, + } + } + + fn new_for_style(style: &ComputedValues) -> Self { + Self::new(SizeConstraint::default(), style.writing_mode) } +} - /// Creates an [`IndefiniteContainingBlock`] with the provided style and block size, - /// and the inline size is set to auto. - /// This is useful when finding the min-content or max-content size of an element, - /// since then we ignore its 'inline-size', 'min-inline-size' and 'max-inline-size'. - fn new_for_writing_mode_and_block_size( - writing_mode: WritingMode, - block_size: AuOrAuto, - ) -> Self { +/// A variant of [`ContainingBlock`] that allows an indefinite inline size. +/// Useful for code that is shared for both layout (where we know the inline size +/// of the containing block) and intrinsic sizing (where we don't know it). +pub(crate) struct IndefiniteContainingBlock { + pub size: LogicalVec2<AuOrAuto>, + pub writing_mode: WritingMode, +} + +impl From<&ConstraintSpace> for IndefiniteContainingBlock { + fn from(constraint_space: &ConstraintSpace) -> Self { Self { size: LogicalVec2 { inline: AuOrAuto::Auto, - block: block_size, + block: constraint_space.block_size.to_auto_or(), }, - writing_mode, + writing_mode: constraint_space.writing_mode, } } } diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 100808d012c..8b59b72216f 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -29,8 +29,8 @@ use crate::geom::{ PhysicalRect, PhysicalVec, Size, ToLogical, ToLogicalWithContainingBlock, }; use crate::sizing::ContentSizes; -use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside}; -use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock}; +use crate::style_ext::{ComputedValuesExt, DisplayInside}; +use crate::{ConstraintSpace, ContainingBlock, DefiniteContainingBlock, SizeConstraint}; #[derive(Debug, Serialize)] pub(crate) struct AbsolutelyPositionedBox { @@ -546,17 +546,9 @@ impl HoistedAbsolutelyPositionedBox { // The inline axis can be fully resolved, computing intrinsic sizes using the // tentative block size. let mut inline_axis = inline_axis_solver.solve(Some(|| { - let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode_and_block_size( - style.writing_mode, - block_axis.size, - ); + let constraint_space = ConstraintSpace::new(block_axis.size, style.writing_mode); context - .inline_content_sizes( - layout_context, - &containing_block_for_children, - &containing_block.into(), - ) + .inline_content_sizes(layout_context, &constraint_space, &containing_block.into()) .sizes })); @@ -578,10 +570,10 @@ impl HoistedAbsolutelyPositionedBox { IndependentFormattingContext::NonReplaced(non_replaced) => { // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height - let inline_size = inline_axis.size.non_auto().unwrap(); + let inline_size = inline_axis.size.to_definite().unwrap(); let containing_block_for_children = ContainingBlock { inline_size, - block_size: block_axis.size, + block_size: block_axis.size.to_auto_or(), style: &style, }; // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows @@ -608,7 +600,7 @@ impl HoistedAbsolutelyPositionedBox { inline_axis = inline_axis_solver.solve_with_size(table_inline_size); } let table_block_size = independent_layout.content_block_size; - if block_axis.size != AuOrAuto::LengthPercentage(table_block_size) { + if block_axis.size != SizeConstraint::Definite(table_block_size) { block_axis = block_axis_solver.solve_with_size(table_block_size); } (table_block_size, table_inline_size) @@ -617,7 +609,7 @@ impl HoistedAbsolutelyPositionedBox { // Now we can properly solve the block size. block_axis = block_axis_solver .solve(Some(|| independent_layout.content_block_size.into())); - (block_axis.size.non_auto().unwrap(), inline_size) + (block_axis.size.to_definite().unwrap(), inline_size) }, }; @@ -747,7 +739,7 @@ impl Anchor { struct AxisResult { anchor: Anchor, - size: AuOrAuto, + size: SizeConstraint, margin_start: Au, margin_end: Au, } @@ -787,38 +779,37 @@ impl<'a> AbsoluteAxisSolver<'a> { let content_size = LazyCell::new(get_content_size); move || *content_size }); - let mut solve_size = |initial_behavior, stretch_size: Au| { + let mut solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint { let initial_is_stretch = initial_behavior == Size::Stretch; let stretch_size = stretch_size.max(Au::zero()); - get_content_size - .as_mut() - .map(|mut get_content_size| { - let min_size = self - .computed_min_size - .resolve_non_initial(stretch_size, &mut get_content_size) - .unwrap_or_default(); - let max_size = self - .computed_max_size - .resolve_non_initial(stretch_size, &mut get_content_size); - self.computed_size - .resolve(initial_behavior, stretch_size, &mut get_content_size) - .clamp_between_extremums(min_size, max_size) - }) - .or_else(|| { - self.computed_size - .maybe_resolve_extrinsic(Some(stretch_size)) - .or(initial_is_stretch.then_some(stretch_size)) - .map(|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)); - size.clamp_between_extremums(min_size, max_size) - }) - }) + if let Some(mut get_content_size) = get_content_size.as_mut() { + let preferred_size = Some(self.computed_size.resolve( + initial_behavior, + stretch_size, + &mut get_content_size, + )); + let min_size = self + .computed_min_size + .resolve_non_initial(stretch_size, &mut get_content_size) + .unwrap_or_default(); + let max_size = self + .computed_max_size + .resolve_non_initial(stretch_size, &mut get_content_size); + SizeConstraint::new(preferred_size, min_size, max_size) + } else { + let preferred_size = self + .computed_size + .maybe_resolve_extrinsic(Some(stretch_size)) + .or(initial_is_stretch.then_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) + } }; let mut solve_for_anchor = |anchor: Anchor| { let margin_start = self.computed_margin_start.auto_is(Au::zero); @@ -828,8 +819,7 @@ impl<'a> AbsoluteAxisSolver<'a> { self.padding_border_sum - margin_start - margin_end; - let size = solve_size(Size::FitContent, stretch_size) - .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); + let size = solve_size(Size::FitContent, stretch_size); AxisResult { anchor, size, @@ -859,7 +849,9 @@ impl<'a> AbsoluteAxisSolver<'a> { let stretch_size = free_space - self.computed_margin_start.auto_is(Au::zero) - self.computed_margin_end.auto_is(Au::zero); - let used_size = solve_size(Size::Stretch, stretch_size).unwrap(); + let used_size = solve_size(Size::Stretch, stretch_size) + .to_definite() + .unwrap(); free_space -= used_size; let (margin_start, margin_end) = match (self.computed_margin_start, self.computed_margin_end) { @@ -883,7 +875,7 @@ impl<'a> AbsoluteAxisSolver<'a> { }; AxisResult { anchor: Anchor::Start(start), - size: AuOrAuto::LengthPercentage(used_size), + size: SizeConstraint::Definite(used_size), margin_start, margin_end, } diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index ddccda13e4f..8aa2b567212 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -31,7 +31,7 @@ use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFrag use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size}; use crate::sizing::InlineContentSizesResult; use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM}; -use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock}; +use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SizeConstraint}; #[derive(Debug, Serialize)] pub(crate) struct ReplacedContent { @@ -259,36 +259,46 @@ impl ReplacedContent { }) } + #[inline] + fn content_size( + &self, + axis: Direction, + preferred_aspect_ratio: Option<AspectRatio>, + get_size_in_opposite_axis: &dyn Fn() -> SizeConstraint, + get_fallback_size: &dyn Fn() -> Au, + ) -> Au { + let Some(ratio) = preferred_aspect_ratio else { + return get_fallback_size(); + }; + let transfer = |size| ratio.compute_dependent_size(axis, size); + match get_size_in_opposite_axis() { + SizeConstraint::Definite(size) => transfer(size), + SizeConstraint::MinMax(min_size, max_size) => get_fallback_size() + .clamp_between_extremums(transfer(min_size), max_size.map(transfer)), + } + } + pub fn inline_content_sizes( &self, _: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, preferred_aspect_ratio: Option<AspectRatio>, ) -> InlineContentSizesResult { - // FIXME: min/max-content of replaced elements is not defined in - // https://dbaron.org/css/intrinsic/ - // This seems sensible? - let block_size = containing_block_for_children.size.block; - match (block_size, preferred_aspect_ratio) { - (AuOrAuto::LengthPercentage(block_size), Some(ratio)) => InlineContentSizesResult { - sizes: ratio - .compute_dependent_size(Direction::Inline, block_size) - .into(), - depends_on_block_constraints: true, - }, - _ => { - let writing_mode = containing_block_for_children.writing_mode; - InlineContentSizesResult { - sizes: self - .flow_relative_natural_size(writing_mode) - .inline - .unwrap_or_else(|| { - Self::flow_relative_default_object_size(writing_mode).inline - }) - .into(), - depends_on_block_constraints: false, - } - }, + let get_inline_fallback_size = || { + let writing_mode = constraint_space.writing_mode; + self.flow_relative_natural_size(writing_mode) + .inline + .unwrap_or_else(|| Self::flow_relative_default_object_size(writing_mode).inline) + }; + let inline_content_size = self.content_size( + Direction::Inline, + preferred_aspect_ratio, + &|| constraint_space.block_size, + &get_inline_fallback_size, + ); + InlineContentSizesResult { + sizes: inline_content_size.into(), + depends_on_block_constraints: preferred_aspect_ratio.is_some(), } } @@ -529,51 +539,51 @@ impl ReplacedContent { .map(|block_size| Au::zero().max(block_size - pbm_sums.block)); // <https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes> - // FIXME: Use ReplacedContent::inline_content_sizes() once it's fixed to correctly handle - // min and max constraints. let inline_content_size = LazyCell::new(|| { - let Some(ratio) = ratio else { - return get_inline_fallback_size(); + let get_block_size = || { + let block_stretch_size = block_stretch_size.unwrap_or_else(get_block_fallback_size); + SizeConstraint::new( + box_size + .block + .maybe_resolve_extrinsic(Some(block_stretch_size)), + min_box_size + .block + .maybe_resolve_extrinsic(Some(block_stretch_size)) + .unwrap_or_default(), + max_box_size + .block + .maybe_resolve_extrinsic(Some(block_stretch_size)), + ) }; - let block_stretch_size = block_stretch_size.unwrap_or_else(get_block_fallback_size); - let transfer = |size| ratio.compute_dependent_size(Direction::Inline, size); - let min = transfer( - min_box_size - .block - .maybe_resolve_extrinsic(Some(block_stretch_size)) - .unwrap_or_default(), - ); - let max = max_box_size - .block - .maybe_resolve_extrinsic(Some(block_stretch_size)) - .map(transfer); - box_size - .block - .maybe_resolve_extrinsic(Some(block_stretch_size)) - .map_or_else(get_inline_fallback_size, transfer) - .clamp_between_extremums(min, max) + self.content_size( + Direction::Inline, + ratio, + &get_block_size, + &get_inline_fallback_size, + ) }); let block_content_size = LazyCell::new(|| { - let Some(ratio) = ratio else { - return get_block_fallback_size(); + let get_inline_size = || { + let mut get_inline_content_size = || (*inline_content_size).into(); + SizeConstraint::new( + box_size + .inline + .maybe_resolve_extrinsic(Some(inline_stretch_size)), + min_box_size + .inline + .resolve_non_initial(inline_stretch_size, &mut get_inline_content_size) + .unwrap_or_default(), + max_box_size + .inline + .resolve_non_initial(inline_stretch_size, &mut get_inline_content_size), + ) }; - let mut get_inline_content_size = || (*inline_content_size).into(); - let transfer = |size| ratio.compute_dependent_size(Direction::Block, size); - let min = transfer( - min_box_size - .inline - .resolve_non_initial(inline_stretch_size, &mut get_inline_content_size) - .unwrap_or_default(), - ); - let max = max_box_size - .inline - .resolve_non_initial(inline_stretch_size, &mut get_inline_content_size) - .map(transfer); - box_size - .inline - .maybe_resolve_extrinsic(Some(inline_stretch_size)) - .map_or_else(get_block_fallback_size, transfer) - .clamp_between_extremums(min, max) + self.content_size( + Direction::Block, + ratio, + &get_inline_size, + &get_block_fallback_size, + ) }); let mut get_inline_content_size = || (*inline_content_size).into(); let mut get_block_content_size = || (*block_content_size).into(); diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 60e9128cb86..f7b691dbcde 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -14,7 +14,7 @@ use style::Zero; use crate::geom::Size; use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM}; -use crate::{AuOrAuto, IndefiniteContainingBlock, LogicalVec2}; +use crate::{ConstraintSpace, IndefiniteContainingBlock, LogicalVec2, SizeConstraint}; #[derive(PartialEq)] pub(crate) enum IntrinsicSizingMode { @@ -120,7 +120,7 @@ pub(crate) fn outer_inline( containing_block: &IndefiniteContainingBlock, auto_minimum: &LogicalVec2<Au>, auto_block_size_stretches_to_containing_block: bool, - get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> InlineContentSizesResult, + get_content_size: impl FnOnce(&ConstraintSpace) -> InlineContentSizesResult, ) -> InlineContentSizesResult { let ContentBoxSizesAndPBM { content_box_size, @@ -140,7 +140,7 @@ pub(crate) fn outer_inline( .block .non_auto() .map(|v| Au::zero().max(v - pbm_sums.block)); - let block_size = if content_box_size.block.is_initial() && + let preferred_block_size = if content_box_size.block.is_initial() && auto_block_size_stretches_to_containing_block { depends_on_block_constraints = true; @@ -149,24 +149,18 @@ pub(crate) fn outer_inline( content_box_size .block .maybe_resolve_extrinsic(available_block_size) - } - .map(|block_size| { - 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); - block_size.clamp_between_extremums(min_block_size, max_block_size) - }) - .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); - let containing_block_for_children = - IndefiniteContainingBlock::new_for_writing_mode_and_block_size( - style.writing_mode, - block_size, - ); - get_content_size(&containing_block_for_children) + }; + 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), + style.writing_mode, + )) }); let resolve_non_initial = |inline_size| { Some(match inline_size { diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 9a521df1927..da49cc00015 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -16,7 +16,6 @@ use style::computed_values::empty_cells::T as EmptyCells; use style::computed_values::position::T as Position; use style::computed_values::table_layout::T as TableLayoutMode; use style::computed_values::visibility::T as Visibility; -use style::logical_geometry::WritingMode; use style::properties::ComputedValues; use style::values::computed::{ BorderStyle, LengthPercentage as ComputedLengthPercentage, Percentage, @@ -40,7 +39,7 @@ use crate::positioned::{relative_adjustement, PositioningContext, PositioningCon use crate::sizing::{ContentSizes, InlineContentSizesResult}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::table::TableSlotCoordinates; -use crate::{ContainingBlock, IndefiniteContainingBlock}; +use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, WritingMode}; /// A result of a final or speculative layout of a single cell in /// the table. Note that this is only done for slots that are not @@ -301,9 +300,7 @@ impl<'a> TableLayout<'a> { .contents .inline_content_sizes( layout_context, - &IndefiniteContainingBlock::new_for_writing_mode( - cell.style.writing_mode, - ), + &ConstraintSpace::new_for_style(&cell.style), ) .sizes }; @@ -776,8 +773,13 @@ impl<'a> TableLayout<'a> { /// Compute CAPMIN: <https://drafts.csswg.org/css-tables/#capmin> fn compute_caption_minimum_inline_size(&mut self, layout_context: &LayoutContext) -> Au { - let containing_block = - IndefiniteContainingBlock::new_for_writing_mode(self.table.style.writing_mode); + let containing_block = IndefiniteContainingBlock { + size: LogicalVec2 { + inline: AuOrAuto::Auto, + block: AuOrAuto::Auto, + }, + writing_mode: self.table.style.writing_mode, + }; self.table .captions .iter() @@ -2630,9 +2632,9 @@ impl Table { pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_for_children: &IndefiniteContainingBlock, + constraint_space: &ConstraintSpace, ) -> InlineContentSizesResult { - let writing_mode = containing_block_for_children.writing_mode; + let writing_mode = constraint_space.writing_mode; let mut layout = TableLayout::new(self); let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode); |