diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs index 85489981c8d..6eff15421c3 100644 --- a/components/layout/flexbox/layout.rs +++ b/components/layout/flexbox/layout.rs @@ -2214,12 +2214,30 @@ impl FlexItemBox { .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)), }; - // - // > If a single-line flex container has a definite cross size, the automatic preferred - // > outer cross size of any stretched flex items is the flex container’s inner cross size - // > (clamped to the flex item’s min and max cross size) and is considered definite. - let (preferred_cross_size, min_cross_size, max_cross_size) = content_cross_sizes - .resolve_each_extrinsic(Size::FitContent, Au::zero(), stretch_size.cross); + let is_table = self.is_table(); + let tentative_cross_content_size = if cross_axis_is_item_block_axis { + self.independent_formatting_context + .tentative_block_content_size(preferred_aspect_ratio) + } else { + None + }; + let (preferred_cross_size, min_cross_size, max_cross_size) = + if let Some(cross_content_size) = tentative_cross_content_size { + let (preferred, min, max) = content_cross_sizes.resolve_each( + Size::FitContent, + Au::zero, + stretch_size.cross, + || cross_content_size, + is_table, + ); + (Some(preferred), min, max) + } else { + content_cross_sizes.resolve_each_extrinsic( + Size::FitContent, + Au::zero(), + stretch_size.cross, + ) + }; let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size); // @@ -2344,7 +2362,7 @@ impl FlexItemBox { get_automatic_minimum_size, stretch_size.main, &main_content_sizes, - self.is_table(), + is_table, ); FlexItem { diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs index 92cabc4b904..a9f57b2151a 100644 --- a/components/layout/flow/mod.rs +++ b/components/layout/flow/mod.rs @@ -534,6 +534,7 @@ fn compute_inline_content_sizes_for_block_level_boxes( |constraint_space| { base.inline_content_sizes(layout_context, constraint_space, contents) }, + |_aspect_ratio| None, ); // A block in the same BFC can overlap floats, it's not moved next to them, // so we shouldn't add its size to the size of the floats. diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs index 5b3a88182e3..8fe8bd7cba5 100644 --- a/components/layout/formatting_contexts.rs +++ b/components/layout/formatting_contexts.rs @@ -7,6 +7,7 @@ use malloc_size_of_derive::MallocSizeOf; use script::layout_dom::{ServoLayoutElement, ServoLayoutNode}; use servo_arc::Arc; use style::context::SharedStyleContext; +use style::logical_geometry::Direction; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; @@ -21,7 +22,7 @@ use crate::layout_box_base::{ }; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContents; -use crate::sizing::{self, ComputeInlineContentSizes, InlineContentSizesResult}; +use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{AspectRatio, DisplayInside, LayoutStyle}; use crate::table::Table; use crate::taffy::TaffyContainer; @@ -175,6 +176,35 @@ impl IndependentFormattingContext { } } + /// Computes the tentative intrinsic block sizes that may be needed while computing + /// the intrinsic inline sizes. Therefore, this ignores the values of the sizing + /// properties in both axes. + /// A return value of `None` indicates that there is no suitable tentative intrinsic + /// block size, so intrinsic keywords in the block sizing properties will be ignored, + /// possibly resulting in an indefinite [`SizeConstraint`] for computing the intrinsic + /// inline sizes and laying out the contents. + /// A return value of `Some` indicates that intrinsic keywords in the block sizing + /// properties will be resolved as the contained value, guaranteeing a definite amount + /// for computing the intrinsic inline sizes and laying out the contents. + pub(crate) fn tentative_block_content_size( + &self, + preferred_aspect_ratio: Option, + ) -> Option { + // See regarding the difference + // in behavior for the replaced and non-replaced cases. + match &self.contents { + IndependentFormattingContextContents::NonReplaced(_) => None, + IndependentFormattingContextContents::Replaced(contents) => { + // For replaced elements with no ratio, the returned value doesn't matter. + let ratio = preferred_aspect_ratio?; + let writing_mode = self.style().writing_mode; + let inline_size = contents.fallback_inline_size(writing_mode); + let block_size = ratio.compute_dependent_size(Direction::Block, inline_size); + Some(block_size.into()) + }, + } + } + pub(crate) fn outer_inline_content_sizes( &self, layout_context: &LayoutContext, @@ -191,6 +221,7 @@ impl IndependentFormattingContext { true, /* establishes_containing_block */ |padding_border_sums| self.preferred_aspect_ratio(padding_border_sums), |constraint_space| self.inline_content_sizes(layout_context, constraint_space), + |preferred_aspect_ratio| self.tentative_block_content_size(preferred_aspect_ratio), ) } diff --git a/components/layout/geom.rs b/components/layout/geom.rs index 4065b785832..b01ea4df847 100644 --- a/components/layout/geom.rs +++ b/components/layout/geom.rs @@ -1031,31 +1031,52 @@ impl Sizes { get_content_size: impl FnOnce() -> ContentSizes, is_table: bool, ) -> 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); - if is_table && axis == Direction::Block { // The intrinsic block size of a table already takes sizing properties into account, // but it can be a smaller amount if there are collapsed rows. // Therefore, disregard sizing properties and just defer to the intrinsic size. // This is being discussed in https://github.com/w3c/csswg-drafts/issues/11408 - return content_size.max_content; + return get_content_size().max_content; } - - let preferred = - self.preferred - .resolve_for_preferred(automatic_size, stretch_size, &content_size); - let min = self.min.resolve_for_min( + let (preferred, min, max) = self.resolve_each( + automatic_size, get_automatic_minimum_size, stretch_size, - &content_size, + get_content_size, is_table, ); - let max = self.max.resolve_for_max(stretch_size, &content_size); preferred.clamp_between_extremums(min, max) } + /// Resolves each of the three sizes into a numerical value, separately. + /// - The 1st returned value is the resolved preferred size. + /// - The 2nd returned value is the resolved minimum size. + /// - The 3rd returned value is the resolved maximum size. `None` means no maximum. + #[inline] + pub(crate) fn resolve_each( + &self, + automatic_size: Size, + get_automatic_minimum_size: impl FnOnce() -> Au, + stretch_size: Option, + get_content_size: impl FnOnce() -> ContentSizes, + is_table: bool, + ) -> (Au, Au, Option) { + // 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_for_preferred(automatic_size, stretch_size, &content_size), + self.min.resolve_for_min( + get_automatic_minimum_size, + stretch_size, + &content_size, + is_table, + ), + self.max.resolve_for_max(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`]. diff --git a/components/layout/sizing.rs b/components/layout/sizing.rs index c6e4b7f9498..a572db1b5ed 100644 --- a/components/layout/sizing.rs +++ b/components/layout/sizing.rs @@ -10,10 +10,11 @@ use std::ops::{Add, AddAssign}; use app_units::Au; use malloc_size_of_derive::MallocSizeOf; use style::Zero; +use style::logical_geometry::Direction; use style::values::computed::LengthPercentage; use crate::context::LayoutContext; -use crate::geom::Size; +use crate::geom::{Size, SizeConstraint}; use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle}; use crate::{ConstraintSpace, IndefiniteContainingBlock, LogicalVec2}; @@ -125,7 +126,8 @@ pub(crate) fn outer_inline( is_replaced: bool, establishes_containing_block: bool, get_preferred_aspect_ratio: impl FnOnce(&LogicalVec2) -> Option, - get_content_size: impl FnOnce(&ConstraintSpace) -> InlineContentSizesResult, + get_inline_content_size: impl FnOnce(&ConstraintSpace) -> InlineContentSizesResult, + get_tentative_block_content_size: impl FnOnce(Option) -> Option, ) -> InlineContentSizesResult { let ContentBoxSizesAndPBM { content_box_sizes, @@ -139,6 +141,7 @@ pub(crate) fn outer_inline( inline: pbm.padding_border_sums.inline + margin.inline_sum(), }; let style = layout_style.style(); + let is_table = layout_style.is_table(); let content_size = LazyCell::new(|| { let constraint_space = if establishes_containing_block { let available_block_size = containing_block @@ -153,15 +156,25 @@ pub(crate) fn outer_inline( } else { Size::FitContent }; - ConstraintSpace::new( - 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), - ) + let aspect_ratio = get_preferred_aspect_ratio(&pbm.padding_border_sums); + let block_size = + if let Some(block_content_size) = get_tentative_block_content_size(aspect_ratio) { + SizeConstraint::Definite(content_box_sizes.block.resolve( + Direction::Block, + automatic_size, + || auto_minimum.block, + available_block_size, + || block_content_size, + is_table, + )) + } else { + content_box_sizes.block.resolve_extrinsic( + automatic_size, + auto_minimum.block, + available_block_size, + ) + }; + ConstraintSpace::new(block_size, style.writing_mode, aspect_ratio) } else { // This assumes that there is no preferred aspect ratio, or that there is no // block size constraint to be transferred so the ratio is irrelevant. @@ -172,7 +185,7 @@ pub(crate) fn outer_inline( None, ) }; - get_content_size(&constraint_space) + get_inline_content_size(&constraint_space) }); let resolve_non_initial = |inline_size, stretch_values| { Some(match inline_size { @@ -246,7 +259,7 @@ pub(crate) fn outer_inline( // Regardless of their sizing properties, tables are always forced to be at least // as big as their min-content size, so floor the minimums. - if layout_style.is_table() { + if is_table { min_min_content.max_assign(content_size.sizes.min_content); min_max_content.max_assign(content_size.sizes.min_content); min_depends_on_block_constraints |= content_size.depends_on_block_constraints; diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 269ce9cac15..9de05cf9d35 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -610358,6 +610358,13 @@ {} ] ], + "keyword-sizes-for-intrinsic-contributions-002.html": [ + "a053d88c1eefd0379ff7e612dfd3d1c39e7c581e", + [ + null, + {} + ] + ], "keyword-sizes-for-intrinsic-contributions.html": [ "b42bd71b8e7ccc802ba11e3bf3384b3854a0c3fa", [ diff --git a/tests/wpt/tests/css/css-sizing/keyword-sizes-for-intrinsic-contributions-002.html b/tests/wpt/tests/css/css-sizing/keyword-sizes-for-intrinsic-contributions-002.html new file mode 100644 index 00000000000..a053d88c1ee --- /dev/null +++ b/tests/wpt/tests/css/css-sizing/keyword-sizes-for-intrinsic-contributions-002.html @@ -0,0 +1,44 @@ + +Keyword sizes for intrinsic contributions + + + + + + + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + + + +