From 93abdf7cb56fa9db6aa160d63e8773292c5e7520 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Thu, 29 Aug 2024 16:10:46 +0200 Subject: [PATCH] layout: Add an indefinite containing block for intrinsic sizing (#33204) When computing the min-content or max-content size of an element we need to ignore `inline-size`, `min-inline-size` and `max-inline-size`. However, we should take the block-axis sizing properties into account. That's because the contents could have percentages depending on them, which can then affect their inline size via an aspect ratio. Therefore, this patch adds `IndefiniteContainingBlock`, which is similar to `ContainingBlock`, but it allows an indefinite inline-size. This struct is then passed arround during intrinsic sizing. More refinement will be needed in follow-up patches in order to fully address the problem. Signed-off-by: Oriol Brufau Co-authored-by: Martin Robinson --- components/layout_2020/flexbox/layout.rs | 163 +++++++++++------- components/layout_2020/flow/float.rs | 67 +++---- components/layout_2020/flow/inline/mod.rs | 34 ++-- components/layout_2020/flow/mod.rs | 41 +++-- components/layout_2020/formatting_contexts.rs | 127 +++++++++----- components/layout_2020/geom.rs | 46 ++++- components/layout_2020/lib.rs | 85 ++++++++- components/layout_2020/positioned.rs | 12 +- components/layout_2020/replaced.rs | 32 +++- components/layout_2020/sizing.rs | 93 +++------- components/layout_2020/style_ext.rs | 98 ++++++++++- components/layout_2020/table/layout.rs | 79 ++++----- tests/wpt/meta/MANIFEST.json | 13 ++ .../css/css-flexbox/flex-basis-011.html.ini | 2 - ...lex-minimum-height-flex-items-015.html.ini | 2 - ...rection-column-percentage-ignored.html.ini | 2 - ...tem-with-max-height-and-scrollbar.html.ini | 2 - ...ading-invalidates-intrinsic-sizes.html.ini | 2 - .../percentage-heights-014.html.ini | 2 - .../css-flexbox/position-fixed-001.html.ini | 2 - ...ntainer-width-when-height-changes.html.ini | 3 - .../intrinsic-percent-replaced-001.html.ini | 2 - ...nsic-percent-replaced-dynamic-002.html.ini | 2 - ...nsic-percent-replaced-dynamic-003.html.ini | 2 - ...nsic-percent-replaced-dynamic-004.html.ini | 2 - ...nsic-percent-replaced-dynamic-006.html.ini | 2 - ...nsic-percent-replaced-dynamic-009.html.ini | 2 - .../css/css-values/calc-rounding-002.html.ini | 2 - .../css-flexbox/percentage-padding-005.html | 11 ++ 29 files changed, 600 insertions(+), 332 deletions(-) delete mode 100644 tests/wpt/meta/css/css-flexbox/flex-basis-011.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/flex-minimum-height-flex-items-015.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/flexbox-flex-direction-column-percentage-ignored.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/item-with-max-height-and-scrollbar.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/nested-flex-image-loading-invalidates-intrinsic-sizes.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/percentage-heights-014.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/position-fixed-001.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/aspect-ratio-affects-container-width-when-height-changes.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-001.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-002.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-003.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-004.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-006.html.ini delete mode 100644 tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-009.html.ini delete mode 100644 tests/wpt/meta/css/css-values/calc-rounding-002.html.ini create mode 100644 tests/wpt/tests/css/css-flexbox/percentage-padding-005.html diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 3247a753c3a..71697240cb3 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -9,7 +9,6 @@ use app_units::Au; use atomic_refcell::AtomicRefMut; use itertools::izip; use style::computed_values::position::T as Position; -use style::logical_geometry::WritingMode; use style::properties::longhands::align_items::computed_value::T as AlignItems; use style::properties::longhands::align_self::computed_value::T as AlignSelf; use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; @@ -35,7 +34,7 @@ use crate::positioned::{ }; use crate::sizing::{ContentSizes, IntrinsicSizingMode}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; -use crate::ContainingBlock; +use crate::{ContainingBlock, IndefiniteContainingBlock}; // FIMXE: “Flex items […] `z-index` values other than `auto` create a stacking context // even if `position` is `static` (behaving exactly as if `position` were `relative`).” @@ -305,19 +304,27 @@ impl FlexContainer { pub fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - writing_mode: WritingMode, + containing_block_for_children: &IndefiniteContainingBlock, ) -> ContentSizes { match self.config.flex_axis { - FlexAxis::Row => self.main_content_sizes(layout_context, writing_mode, || { - unreachable!( - "Unexpected FlexContext query during row flex intrinsic size calculation." - ) - }), - FlexAxis::Column => self.cross_content_sizes(layout_context), + FlexAxis::Row => { + self.main_content_sizes(layout_context, containing_block_for_children, || { + unreachable!( + "Unexpected FlexContext query during row flex intrinsic size calculation." + ) + }) + }, + FlexAxis::Column => { + self.cross_content_sizes(layout_context, containing_block_for_children) + }, } } - fn cross_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes { + fn cross_content_sizes( + &mut self, + layout_context: &LayoutContext, + containing_block_for_children: &IndefiniteContainingBlock, + ) -> ContentSizes { // assert_eq!( self.config.flex_axis, @@ -331,10 +338,15 @@ impl FlexContainer { FlexLevelBox::FlexItem(item) => { // TODO: For the max-content size we should distribute items into // columns, and sum the column sizes and gaps. - content_sizes.max_assign( - item.independent_formatting_context - .inline_content_sizes(layout_context), - ); + // TODO: Use the proper automatic minimum size. + let ifc = &mut item.independent_formatting_context; + content_sizes.max_assign(ifc.inline_content_sizes( + layout_context, + &containing_block_for_children.new_for_intrinsic_inline_size_of_child( + &ifc.style().clone(), + &LogicalVec2::zero(), + ), + )); }, FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {}, } @@ -345,7 +357,7 @@ impl FlexContainer { fn main_content_sizes<'a>( &self, layout_context: &LayoutContext, - writing_mode: WritingMode, + containing_block_for_children: &IndefiniteContainingBlock, flex_context_getter: impl Fn() -> &'a FlexContext<'a>, ) -> ContentSizes { // - TODO: calculate intrinsic cross sizes when container is a column @@ -372,7 +384,7 @@ impl FlexContainer { let info = item.main_content_size_info( layout_context, - writing_mode, + containing_block_for_children, container_is_horizontal, self.config.flex_axis, self.config.main_start_cross_start_sides_are, @@ -514,7 +526,7 @@ impl FlexContainer { let container_main_size = match self.config.flex_axis { FlexAxis::Row => containing_block.inline_size, FlexAxis::Column => containing_block.block_size.auto_is(|| { - self.main_content_sizes(layout_context, self.style.writing_mode, || &flex_context) + self.main_content_sizes(layout_context, &containing_block.into(), || &flex_context) .max_content }), }; @@ -905,16 +917,17 @@ fn allocate_free_cross_space_for_flex_line( impl<'a> FlexItem<'a> { fn new(flex_context: &FlexContext, box_: &'a mut FlexItemBox) -> Self { let containing_block = flex_context.containing_block; + let parent_writing_mode = containing_block.effective_writing_mode(); + let item_writing_mode = box_.style().effective_writing_mode(); // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows assert_eq!( - containing_block.effective_writing_mode(), - box_.style().effective_writing_mode(), + parent_writing_mode, item_writing_mode, "Mixed writing modes are not supported yet" ); - let container_is_horizontal = containing_block.effective_writing_mode().is_horizontal(); - let item_is_horizontal = box_.style().effective_writing_mode().is_horizontal(); + let container_is_horizontal = parent_writing_mode.is_horizontal(); + let item_is_horizontal = item_writing_mode.is_horizontal(); let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis( container_is_horizontal, item_is_horizontal, @@ -979,6 +992,8 @@ impl<'a> FlexItem<'a> { flex_context.container_definite_inner_size, cross_axis_is_item_block_axis, flex_relative_content_box_size, + flex_relative_content_min_size, + flex_relative_content_max_size, padding_border, |item| { let min_size = flex_context @@ -1585,6 +1600,7 @@ impl FlexItem<'_> { ); // … and also the item’s inline axis. + let flex_axis = flex_context.config.flex_axis; match &mut self.box_.independent_formatting_context { IndependentFormattingContext::Replaced(replaced) => { let pbm = replaced.style.padding_border_margin(containing_block); @@ -1602,11 +1618,7 @@ impl FlexItem<'_> { box_size, &pbm, ); - let cross_size = flex_context - .config - .flex_axis - .vec2_to_flex_relative(size) - .cross; + let cross_size = flex_axis.vec2_to_flex_relative(size).cross; let fragments = replaced.contents.make_fragments( &replaced.style, size.to_physical_size(container_writing_mode), @@ -1642,23 +1654,34 @@ impl FlexItem<'_> { .effective_writing_mode() .is_horizontal(), item_is_horizontal, - flex_context.config.flex_axis, + flex_axis, ); + let (inline_size, block_size) = if cross_axis_is_item_block_axis { (used_main_size, cross_size) } else { ( cross_size.auto_is(|| { - let content_contributions = non_replaced.outer_inline_content_sizes( - flex_context.layout_context, - container_writing_mode, - Au::zero, - ); - containing_block - .inline_size + let style = non_replaced.style.clone(); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size( + &style, + AuOrAuto::LengthPercentage(used_main_size), + ); + let content_contributions = non_replaced + .inline_content_sizes( + flex_context.layout_context, + &containing_block_for_children, + ) + .map(|size| { + size.clamp_between_extremums( + self.content_min_size.cross, + self.content_max_size.cross, + ) + }); + (containing_block.inline_size - self.pbm_auto_is_zero.cross) .min(content_contributions.max_content) - .max(content_contributions.min_content) - - self.pbm_auto_is_zero.cross + .max(content_contributions.min_content) }), // The main size of a flex item is considered to be definite if its flex basis is definite // or the flex container has a definite main size. @@ -1874,7 +1897,7 @@ impl FlexItemBox { fn main_content_size_info<'a>( &mut self, layout_context: &LayoutContext, - container_writing_mode: WritingMode, + containing_block: &IndefiniteContainingBlock, container_is_horizontal: bool, flex_axis: FlexAxis, main_start_cross_start: MainStartCrossStart, @@ -1886,25 +1909,8 @@ impl FlexItemBox { let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis); - let pbm = style.padding_border_margin_for_intrinsic_size(item_writing_mode); - let box_size = style - .box_size(item_writing_mode) - .map(|v| v.percentage_relative_to(Length::zero())); - let content_box_size = style - .content_box_size_for_box_size(box_size, &pbm) - .map(|v| v.map(Au::from)); - let min_size = style - .min_box_size(item_writing_mode) - .map(|v| v.percentage_relative_to(Length::zero())); - let content_min_size = style - .content_min_box_size_for_min_size(min_size, &pbm) - .map(|v| v.map(Au::from)); - let max_size = style - .max_box_size(item_writing_mode) - .map(|v| v.map(|v| v.percentage_relative_to(Length::zero()))); - let content_max_size = style - .content_max_box_size_for_max_size(max_size, &pbm) - .map(|v| v.map(Au::from)); + let (content_box_size, content_min_size, content_max_size, pbm) = + style.content_box_sizes_and_padding_border_margin(containing_block); let automatic_min_size = self.automatic_min_size( layout_context, cross_axis_is_item_block_axis, @@ -1942,9 +1948,11 @@ impl FlexItemBox { let content_contribution_sizes = match flex_axis { FlexAxis::Row => self .independent_formatting_context - .outer_inline_content_sizes(layout_context, container_writing_mode, || { - automatic_min_size - }), + .outer_inline_content_sizes( + layout_context, + containing_block, + &content_min_size_no_auto, + ), FlexAxis::Column => self .layout_for_block_content_size( flex_context_getter(), @@ -1982,6 +1990,8 @@ impl FlexItemBox { }, cross_axis_is_item_block_axis, content_box_size, + content_min_size_no_auto, + content_max_size, padding_border, block_content_size_callback, ); @@ -2142,8 +2152,14 @@ impl FlexItemBox { // > preferred aspect ratio, by any definite minimum and maximum cross sizes converted through the // > aspect ratio. let main_content_size = if cross_axis_is_item_block_axis { + let block_size = content_box_size.cross.map(|v| { + v.clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross) + }); + let style = self.independent_formatting_context.style().clone(); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size); self.independent_formatting_context - .inline_content_sizes(layout_context) + .inline_content_sizes(layout_context, &containing_block_for_children) .min_content } else { block_content_size_callback(self) @@ -2178,16 +2194,19 @@ impl FlexItemBox { container_definite_inner_size: FlexRelativeVec2>, cross_axis_is_item_block_axis: bool, content_box_size: FlexRelativeVec2, + min_size: FlexRelativeVec2, + max_size: FlexRelativeVec2>, padding_border_sums: FlexRelativeVec2, block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au, ) -> (Au, bool) { let flex_item = &mut self.independent_formatting_context; + let style = flex_item.style(); - let used_flex_basis = match &flex_item.style().get_position().flex_basis { + let used_flex_basis = match &style.get_position().flex_basis { FlexBasis::Content => FlexBasis::Content, FlexBasis::Size(Size::LengthPercentage(length_percentage)) => { let apply_box_sizing = |length: Au| { - match flex_item.style().get_position().box_sizing { + match style.get_position().box_sizing { BoxSizing::ContentBox => length, BoxSizing::BorderBox => { // This may make `length` negative, @@ -2250,7 +2269,15 @@ impl FlexItemBox { let flex_basis = if cross_axis_is_item_block_axis { // The main axis is the inline axis, so we can get the content size from the normal // preferred widths calculation. - flex_item.inline_content_sizes(layout_context).max_content + let style = style.clone(); + let block_size = content_box_size + .cross + .map(|v| v.clamp_between_extremums(min_size.cross, max_size.cross)); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size); + flex_item + .inline_content_sizes(layout_context, &containing_block_for_children) + .max_content } else { block_content_size_callback(self) }; @@ -2305,8 +2332,14 @@ impl FlexItemBox { if will_stretch { containing_block_inline_size_minus_pbm } else { - let inline_content_sizes = - non_replaced.inline_content_sizes(flex_context.layout_context); + let style = non_replaced.style.clone(); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style(&style); + let inline_content_sizes = non_replaced.inline_content_sizes( + flex_context.layout_context, + &containing_block_for_children, + ); + containing_block_inline_size_minus_pbm .min(inline_content_sizes.max_content) .max(inline_content_sizes.min_content) diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index b211f0bb80d..4b1aa1844ce 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -18,7 +18,7 @@ use servo_arc::Arc; use style::computed_values::float::T as FloatProperty; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; -use style::values::computed::{Clear, Length}; +use style::values::computed::Clear; use style::values::specified::text::TextDecorationLine; use crate::context::LayoutContext; @@ -28,8 +28,8 @@ use crate::formatting_contexts::IndependentFormattingContext; use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin}; use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::positioned::PositioningContext; -use crate::style_ext::{ComputedValuesExt, DisplayInside, PaddingBorderMargin}; -use crate::ContainingBlock; +use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin}; +use crate::{ContainingBlock, IndefiniteContainingBlock}; /// A floating box. #[derive(Debug, Serialize)] @@ -905,35 +905,44 @@ impl FloatBox { IndependentFormattingContext::NonReplaced(ref mut non_replaced) => { // Calculate inline size. // https://drafts.csswg.org/css2/#float-width - let box_size = non_replaced.style.content_box_size(containing_block, &pbm); - let max_box_size = non_replaced - .style - .content_max_box_size(containing_block, &pbm); - let min_box_size = non_replaced - .style + let style = non_replaced.style.clone(); + let box_size = style + .content_box_size(containing_block, &pbm) + .map(|v| v.map(Au::from)); + let max_box_size = style + .content_max_box_size(containing_block, &pbm) + .map(|v| v.map(Au::from)); + let min_box_size = style .content_min_box_size(containing_block, &pbm) - .auto_is(Length::zero); + .map(|v| v.map(Au::from)) + .auto_is(Au::zero); - let tentative_inline_size = box_size.inline.auto_is(|| { - let available_size = - containing_block.inline_size - pbm_sums.inline_sum(); - non_replaced - .inline_content_sizes(layout_context) - .shrink_to_fit(available_size) - .into() - }); - let inline_size = tentative_inline_size - .clamp_between_extremums(min_box_size.inline, max_box_size.inline); let block_size = box_size.block.map(|size| { size.clamp_between_extremums(min_box_size.block, max_box_size.block) }); + let tentative_inline_size = box_size.inline.auto_is(|| { + let available_size = + containing_block.inline_size - pbm_sums.inline_sum(); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size( + &style, block_size, + ); + non_replaced + .inline_content_sizes( + layout_context, + &containing_block_for_children, + ) + .shrink_to_fit(available_size) + }); + let inline_size = tentative_inline_size + .clamp_between_extremums(min_box_size.inline, max_box_size.inline); // Calculate block size. // https://drafts.csswg.org/css2/#block-root-margin // FIXME(pcwalton): Is a tree rank of zero correct here? let containing_block_for_children = ContainingBlock { - inline_size: inline_size.into(), - block_size: block_size.map(|t| t.into()), + inline_size, + block_size, style: &non_replaced.style, }; let independent_layout = non_replaced.layout( @@ -944,13 +953,13 @@ impl FloatBox { ); let (block_size, inline_size) = match independent_layout.content_inline_size_for_table { - Some(inline_size) => ( - independent_layout.content_block_size.into(), - inline_size.into(), - ), + Some(inline_size) => { + (independent_layout.content_block_size, inline_size) + }, None => ( block_size.auto_is(|| { - Length::from(independent_layout.content_block_size) + independent_layout + .content_block_size .clamp_between_extremums( min_box_size.block, max_box_size.block, @@ -960,8 +969,8 @@ impl FloatBox { ), }; content_size = LogicalVec2 { - inline: inline_size.into(), - block: block_size.into(), + inline: inline_size, + block: block_size, }; children = independent_layout.fragments; }, diff --git a/components/layout_2020/flow/inline/mod.rs b/components/layout_2020/flow/inline/mod.rs index bd383ccc14c..d78359e8da7 100644 --- a/components/layout_2020/flow/inline/mod.rs +++ b/components/layout_2020/flow/inline/mod.rs @@ -94,7 +94,6 @@ use style::computed_values::text_wrap_mode::T as TextWrapMode; use style::computed_values::vertical_align::T as VerticalAlign; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; use style::context::QuirksMode; -use style::logical_geometry::WritingMode; use style::properties::style_structs::InheritedText; use style::properties::ComputedValues; use style::values::computed::{Clear, Length}; @@ -128,7 +127,7 @@ use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::sizing::ContentSizes; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; -use crate::ContainingBlock; +use crate::{ContainingBlock, IndefiniteContainingBlock}; // From gfxFontConstants.h in Firefox. static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; @@ -1557,9 +1556,9 @@ impl InlineFormattingContext { pub(super) fn inline_content_sizes( &self, layout_context: &LayoutContext, - containing_block_writing_mode: WritingMode, + containing_block_for_children: &IndefiniteContainingBlock, ) -> ContentSizes { - ContentSizesComputation::compute(self, layout_context, containing_block_writing_mode) + ContentSizesComputation::compute(self, layout_context, containing_block_for_children) } pub(super) fn layout( @@ -1954,13 +1953,19 @@ impl IndependentFormattingContext { .content_min_box_size(layout.containing_block, &pbm) .map(|v| v.map(Au::from)) .auto_is(Au::zero); + let block_size = box_size + .block + .map(|v| v.clamp_between_extremums(min_box_size.block, max_box_size.block)); // https://drafts.csswg.org/css2/visudet.html#inlineblock-width let tentative_inline_size = box_size.inline.auto_is(|| { + let style = non_replaced.style.clone(); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size); let available_size = layout.containing_block.inline_size - pbm_sums.inline_sum(); non_replaced - .inline_content_sizes(layout.layout_context) + .inline_content_sizes(layout.layout_context, &containing_block_for_children) .shrink_to_fit(available_size) }); @@ -1969,9 +1974,6 @@ impl IndependentFormattingContext { // always results in that size. let inline_size = tentative_inline_size .clamp_between_extremums(min_box_size.inline, max_box_size.inline); - let block_size = box_size - .block - .map(|v| v.clamp_between_extremums(min_box_size.block, max_box_size.block)); let containing_block_for_children = ContainingBlock { inline_size, @@ -2259,7 +2261,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_writing_mode: WritingMode, + containing_block: &'layout_data IndefiniteContainingBlock<'layout_data>, paragraph: ContentSizes, current_line: ContentSizes, /// Size for whitepsace pending to be added to this line. @@ -2297,14 +2299,14 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { let zero = Length::zero(); let padding = inline_box .style - .padding(self.containing_block_writing_mode) + .padding(self.containing_block.style.writing_mode) .percentages_relative_to(zero); let border = inline_box .style - .border_width(self.containing_block_writing_mode); + .border_width(self.containing_block.style.writing_mode); let margin = inline_box .style - .margin(self.containing_block_writing_mode) + .margin(self.containing_block.style.writing_mode) .percentages_relative_to(zero) .auto_is(Length::zero); @@ -2379,8 +2381,8 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { let outer = atomic.outer_inline_content_sizes( self.layout_context, - self.containing_block_writing_mode, - Au::zero, + self.containing_block, + &LogicalVec2::zero(), ); if !inline_formatting_context @@ -2428,11 +2430,11 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { fn compute( inline_formatting_context: &InlineFormattingContext, layout_context: &'layout_data LayoutContext, - containing_block_writing_mode: WritingMode, + containing_block: &'layout_data IndefiniteContainingBlock, ) -> ContentSizes { Self { layout_context, - containing_block_writing_mode, + containing_block, paragraph: ContentSizes::zero(), current_line: ContentSizes::zero(), pending_whitespace: Au::zero(), diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index b4dbe04e916..0d5d233eeb0 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -36,7 +36,7 @@ use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, Positioning use crate::replaced::ReplacedContent; use crate::sizing::{self, ContentSizes}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; -use crate::ContainingBlock; +use crate::{ContainingBlock, IndefiniteContainingBlock}; mod construct; pub mod float; @@ -227,10 +227,10 @@ impl OutsideMarker { sequential_layout_state: Option<&mut SequentialLayoutState>, collapsible_with_parent_start_margin: Option, ) -> Fragment { - let containing_block_writing_mode = containing_block.effective_writing_mode(); - let content_sizes = self - .block_container - .inline_content_sizes(layout_context, containing_block_writing_mode); + let content_sizes = self.block_container.inline_content_sizes( + layout_context, + &IndefiniteContainingBlock::new_for_style(&self.marker_style), + ); let containing_block_for_children = ContainingBlock { inline_size: content_sizes.max_content, block_size: AuOrAuto::auto(), @@ -244,6 +244,8 @@ impl OutsideMarker { sequential_layout_state, collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)), ); + + let containing_block_writing_mode = containing_block.effective_writing_mode(); let max_inline_size = flow_layout .fragments @@ -357,7 +359,7 @@ impl BlockFormattingContext { fn calculate_inline_content_size_for_block_level_boxes( boxes: &[ArcRefCell], layout_context: &LayoutContext, - writing_mode: WritingMode, + containing_block: &IndefiniteContainingBlock, ) -> ContentSizes { let get_box_info = |box_: &ArcRefCell| { match &mut *box_.borrow_mut() { @@ -366,7 +368,11 @@ fn calculate_inline_content_size_for_block_level_boxes( BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => { let size = float_box .contents - .outer_inline_content_sizes(layout_context, writing_mode, Au::zero) + .outer_inline_content_sizes( + layout_context, + containing_block, + &LogicalVec2::zero(), + ) .max(ContentSizes::zero()); let style_box = &float_box.contents.style().get_box(); Some((size, style_box.float, style_box.clear)) @@ -376,12 +382,11 @@ fn calculate_inline_content_size_for_block_level_boxes( } => { let size = sizing::outer_inline( style, - writing_mode, - || { - contents - .inline_content_sizes(layout_context, style.effective_writing_mode()) + &containing_block, + &LogicalVec2::zero(), + |containing_block_for_children| { + contents.inline_content_sizes(layout_context, containing_block_for_children) }, - Au::zero, ) .max(ContentSizes::zero()); // A block in the same BFC can overlap floats, it's not moved next to them, @@ -391,7 +396,11 @@ fn calculate_inline_content_size_for_block_level_boxes( }, BlockLevelBox::Independent(ref mut independent) => { let size = independent - .outer_inline_content_sizes(layout_context, writing_mode, Au::zero) + .outer_inline_content_sizes( + layout_context, + containing_block, + &LogicalVec2::zero(), + ) .max(ContentSizes::zero()); Some((size, Float::None, independent.style().get_box().clear)) }, @@ -498,16 +507,16 @@ impl BlockContainer { pub(super) fn inline_content_sizes( &self, layout_context: &LayoutContext, - writing_mode: WritingMode, + containing_block_for_children: &IndefiniteContainingBlock, ) -> ContentSizes { match &self { Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes( boxes, layout_context, - writing_mode, + containing_block_for_children, ), Self::InlineFormattingContext(context) => { - context.inline_content_sizes(layout_context, writing_mode) + context.inline_content_sizes(layout_context, containing_block_for_children) }, } } diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 9381f2b50ef..3588d8c30d1 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -5,7 +5,7 @@ use app_units::Au; use serde::Serialize; use servo_arc::Arc; -use style::logical_geometry::WritingMode; +use style::logical_geometry::Direction; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; use style::values::specified::text::TextDecorationLine; @@ -19,9 +19,9 @@ use crate::fragment_tree::{BaseFragmentInfo, Fragment, FragmentFlags}; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContent; use crate::sizing::{self, ContentSizes}; -use crate::style_ext::{ComputedValuesExt, DisplayInside}; +use crate::style_ext::{AspectRatio, DisplayInside}; use crate::table::Table; -use crate::ContainingBlock; +use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2}; /// #[derive(Debug, Serialize)] @@ -36,7 +36,7 @@ pub(crate) struct NonReplacedFormattingContext { #[serde(skip_serializing)] pub style: Arc, /// If it was requested during construction - pub content_sizes: Option, + pub content_sizes: Option<(AuOrAuto, ContentSizes)>, pub contents: NonReplacedFormattingContextContents, } @@ -176,30 +176,56 @@ impl IndependentFormattingContext { } } - pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes { + pub(crate) fn inline_content_sizes( + &mut self, + layout_context: &LayoutContext, + containing_block_for_children: &IndefiniteContainingBlock, + ) -> ContentSizes { match self { - Self::NonReplaced(inner) => inner.inline_content_sizes(layout_context), - Self::Replaced(inner) => inner.contents.inline_content_sizes(&inner.style), + Self::NonReplaced(inner) => { + inner.inline_content_sizes(layout_context, containing_block_for_children) + }, + Self::Replaced(inner) => inner + .contents + .inline_content_sizes(layout_context, containing_block_for_children), } } - pub fn outer_inline_content_sizes( + pub(crate) fn outer_inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_writing_mode: WritingMode, - get_auto_minimum: impl FnOnce() -> Au, + containing_block: &IndefiniteContainingBlock, + auto_minimum: &LogicalVec2, ) -> ContentSizes { match self { - Self::NonReplaced(non_replaced) => non_replaced.outer_inline_content_sizes( - layout_context, - containing_block_writing_mode, - get_auto_minimum, + Self::NonReplaced(non_replaced) => sizing::outer_inline( + &non_replaced.style.clone(), + containing_block, + auto_minimum, + |containing_block_for_children| { + non_replaced.inline_content_sizes(layout_context, containing_block_for_children) + }, ), Self::Replaced(replaced) => sizing::outer_inline( &replaced.style, - containing_block_writing_mode, - || replaced.contents.inline_content_sizes(&replaced.style), - get_auto_minimum, + containing_block, + auto_minimum, + |containing_block_for_children| { + match ( + containing_block_for_children.size.block, + replaced.preferred_aspect_ratio(containing_block), + ) { + (AuOrAuto::LengthPercentage(block_size), Some(ratio)) => { + return ratio + .compute_dependent_size(Direction::Inline, block_size) + .into(); + }, + _ => {}, + } + replaced + .contents + .inline_content_sizes(layout_context, containing_block_for_children) + }, ), } } @@ -234,46 +260,59 @@ impl NonReplacedFormattingContext { } } - pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes { - let writing_mode = self.style.effective_writing_mode(); - let contents = &mut self.contents; - *self - .content_sizes - .get_or_insert_with(|| contents.inline_content_sizes(layout_context, writing_mode)) - } - - pub fn outer_inline_content_sizes( + pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - containing_block_writing_mode: WritingMode, - get_auto_minimum: impl FnOnce() -> Au, + containing_block_for_children: &IndefiniteContainingBlock, ) -> ContentSizes { - sizing::outer_inline( - &self.style, - containing_block_writing_mode, - || { - *self.content_sizes.get_or_insert_with(|| { - self.contents - .inline_content_sizes(layout_context, self.style.effective_writing_mode()) - }) - }, - get_auto_minimum, - ) + 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 { + if previous_cb_block_size == containing_block_for_children.size.block { + return result; + } + // TODO: Should we keep multiple caches for various block sizes? + } + + self.content_sizes + .insert(( + containing_block_for_children.size.block, + self.contents + .inline_content_sizes(layout_context, &containing_block_for_children), + )) + .1 } } impl NonReplacedFormattingContextContents { - pub fn inline_content_sizes( + pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - writing_mode: WritingMode, + containing_block_for_children: &IndefiniteContainingBlock, ) -> ContentSizes { match self { Self::Flow(inner) => inner .contents - .inline_content_sizes(layout_context, writing_mode), - Self::Flex(inner) => inner.inline_content_sizes(layout_context, writing_mode), - Self::Table(table) => table.inline_content_sizes(layout_context, writing_mode), + .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) + }, } } } + +impl ReplacedFormattingContext { + pub(crate) fn preferred_aspect_ratio( + &self, + containing_block: &IndefiniteContainingBlock, + ) -> Option { + self.contents + .preferred_aspect_ratio(containing_block, &self.style) + } +} diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index 5f3f83835c4..adb60830019 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -24,7 +24,7 @@ pub type AuOrAuto = AutoOr; pub type LengthOrAuto = AutoOr; pub type LengthPercentageOrAuto<'a> = AutoOr<&'a LengthPercentage>; -#[derive(Clone, Copy, Serialize)] +#[derive(Clone, Copy, PartialEq, Serialize)] pub struct LogicalVec2 { pub inline: T, pub block: T, @@ -150,7 +150,7 @@ impl LogicalVec2> { } impl LogicalVec2> { - pub fn percentages_relative_to( + pub(crate) fn percentages_relative_to( &self, containing_block: &ContainingBlock, ) -> LogicalVec2 { @@ -165,8 +165,32 @@ impl LogicalVec2> { } } +impl LogicalVec2> { + pub(crate) fn percentages_relative_to_basis( + &self, + basis: &LogicalVec2, + ) -> LogicalVec2 { + LogicalVec2 { + inline: self.inline.percentage_relative_to(basis.inline), + block: self.block.percentage_relative_to(basis.block), + } + } +} + +impl LogicalVec2> { + pub(crate) fn maybe_percentages_relative_to_basis( + &self, + basis: &LogicalVec2>, + ) -> LogicalVec2 { + LogicalVec2 { + inline: self.inline.maybe_percentage_relative_to(basis.inline), + block: self.block.maybe_percentage_relative_to(basis.block), + } + } +} + impl LogicalVec2> { - pub fn percentages_relative_to( + pub(crate) fn percentages_relative_to( &self, containing_block: &ContainingBlock, ) -> LogicalVec2> { @@ -183,6 +207,22 @@ impl LogicalVec2> { } } +impl LogicalVec2> { + pub(crate) fn maybe_percentages_relative_to_basis( + &self, + basis: &LogicalVec2>, + ) -> LogicalVec2> { + LogicalVec2 { + inline: self + .inline + .and_then(|v| v.maybe_percentage_relative_to(basis.inline)), + block: self + .block + .and_then(|v| v.maybe_percentage_relative_to(basis.block)), + } + } +} + impl LogicalRect { pub fn zero() -> Self { Self { diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 7da7ee1bf01..456ffb7042b 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -31,10 +31,76 @@ pub use fragment_tree::FragmentTree; use geom::AuOrAuto; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; -use style_ext::ComputedValuesExt; +use style_ext::{Clamp, ComputedValuesExt}; use crate::geom::LogicalVec2; +/// 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<'a> { + pub size: LogicalVec2, + pub style: &'a ComputedValues, +} + +impl<'a> IndefiniteContainingBlock<'a> { + fn new_for_style(style: &'a ComputedValues) -> Self { + Self::new_for_style_and_block_size(style, AuOrAuto::Auto) + } + + /// 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_style_and_block_size(style: &'a ComputedValues, block_size: AuOrAuto) -> Self { + Self { + size: LogicalVec2 { + inline: AuOrAuto::Auto, + block: block_size, + }, + style, + } + } + + fn new_for_intrinsic_inline_size_of_child( + &self, + style: &'a ComputedValues, + auto_minimum: &LogicalVec2, + ) -> Self { + let (content_box_size, content_min_size, content_max_size, _) = + style.content_box_sizes_and_padding_border_margin(&self); + let block_size = content_box_size.block.map(|v| { + v.clamp_between_extremums( + content_min_size.block.auto_is(|| auto_minimum.block), + content_max_size.block, + ) + }); + IndefiniteContainingBlock::new_for_style_and_block_size(style, block_size) + } +} + +impl<'a> From<&'_ ContainingBlock<'a>> for IndefiniteContainingBlock<'a> { + fn from(containing_block: &ContainingBlock<'a>) -> Self { + Self { + size: LogicalVec2 { + inline: AuOrAuto::LengthPercentage(containing_block.inline_size), + block: containing_block.block_size, + }, + style: containing_block.style, + } + } +} + +impl<'a> From<&'_ DefiniteContainingBlock<'a>> for IndefiniteContainingBlock<'a> { + fn from(containing_block: &DefiniteContainingBlock<'a>) -> Self { + Self { + size: containing_block + .size + .map(|v| AuOrAuto::LengthPercentage(*v)), + style: containing_block.style, + } + } +} + pub struct ContainingBlock<'a> { inline_size: Au, block_size: AuOrAuto, @@ -47,6 +113,23 @@ impl<'a> ContainingBlock<'a> { } } +impl<'a> TryFrom<&'_ IndefiniteContainingBlock<'a>> for ContainingBlock<'a> { + type Error = &'static str; + + fn try_from( + indefinite_containing_block: &IndefiniteContainingBlock<'a>, + ) -> Result { + match indefinite_containing_block.size.inline { + AuOrAuto::Auto => Err("ContainingBlock doesn't accept auto inline sizes"), + AuOrAuto::LengthPercentage(inline_size) => Ok(ContainingBlock { + inline_size, + block_size: indefinite_containing_block.size.block, + style: indefinite_containing_block.style, + }), + } + } +} + struct DefiniteContainingBlock<'a> { size: LogicalVec2, style: &'a ComputedValues, diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 3b16d197bcd..433c9a4827f 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -27,7 +27,7 @@ use crate::geom::{ PhysicalPoint, PhysicalRect, ToLogical, }; use crate::style_ext::{ComputedValuesExt, DisplayInside}; -use crate::{ContainingBlock, DefiniteContainingBlock}; +use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock}; #[derive(Debug, Serialize)] pub(crate) struct AbsolutelyPositionedBox { @@ -578,8 +578,16 @@ impl HoistedAbsolutelyPositionedBox { let margin_sum = inline_axis.margin_start + inline_axis.margin_end; let available_size = cbis - anchor - pbm.padding_border_sums.inline - margin_sum; + + let style = non_replaced.style.clone(); + let containing_block_for_children = + IndefiniteContainingBlock::from(containing_block) + .new_for_intrinsic_inline_size_of_child( + &style, + &LogicalVec2::zero(), + ); non_replaced - .inline_content_sizes(layout_context) + .inline_content_sizes(layout_context, &containing_block_for_children) .shrink_to_fit(available_size) }); diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 055548e4739..2124783f035 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -29,8 +29,8 @@ use crate::dom::NodeExt; use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment}; use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::sizing::ContentSizes; -use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; -use crate::{AuOrAuto, ContainingBlock}; +use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, PaddingBorderMargin}; +use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock}; #[derive(Debug, Serialize)] pub(crate) struct ReplacedContent { @@ -238,7 +238,7 @@ impl ReplacedContent { LogicalVec2::from_physical_size(&intrinsic_size, style.effective_writing_mode()) } - pub fn inline_size_over_block_size_intrinsic_ratio( + pub(crate) fn inline_size_over_block_size_intrinsic_ratio( &self, style: &ComputedValues, ) -> Option { @@ -251,11 +251,16 @@ impl ReplacedContent { }) } - pub fn inline_content_sizes(&self, style: &ComputedValues) -> ContentSizes { + pub fn inline_content_sizes( + &self, + _: &LayoutContext, + containing_block_for_children: &IndefiniteContainingBlock, + ) -> ContentSizes { // FIXME: min/max-content of replaced elements is not defined in // https://dbaron.org/css/intrinsic/ // This seems sensible? - self.flow_relative_intrinsic_size(style) + + self.flow_relative_intrinsic_size(containing_block_for_children.style) .inline .unwrap_or(Au::zero()) .into() @@ -331,6 +336,18 @@ impl ReplacedContent { } } + pub(crate) fn preferred_aspect_ratio( + &self, + containing_block: &IndefiniteContainingBlock, + style: &ComputedValues, + ) -> Option { + style.preferred_aspect_ratio( + self.inline_size_over_block_size_intrinsic_ratio(style), + containing_block.try_into().ok().as_ref(), + containing_block.style.effective_writing_mode(), + ) + } + /// /// /// @@ -345,10 +362,7 @@ impl ReplacedContent { ) -> LogicalVec2 { let mode = style.effective_writing_mode(); let intrinsic_size = self.flow_relative_intrinsic_size(style); - let intrinsic_ratio = style.preferred_aspect_ratio( - self.inline_size_over_block_size_intrinsic_ratio(style), - containing_block, - ); + let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style); let box_size = box_size.unwrap_or( style diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index c0585a06056..f83d71e118c 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -8,13 +8,11 @@ use std::ops::{Add, AddAssign}; use app_units::Au; use serde::Serialize; -use style::logical_geometry::WritingMode; -use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; use style::properties::ComputedValues; -use style::values::computed::Length; use style::Zero; use crate::style_ext::{Clamp, ComputedValuesExt}; +use crate::{AuOrAuto, IndefiniteContainingBlock, LogicalVec2}; #[derive(PartialEq)] pub(crate) enum IntrinsicSizingMode { @@ -111,70 +109,31 @@ impl From for ContentSizes { pub(crate) fn outer_inline( style: &ComputedValues, - containing_block_writing_mode: WritingMode, - get_content_size: impl FnOnce() -> ContentSizes, - get_auto_minimum: impl FnOnce() -> Au, + containing_block: &IndefiniteContainingBlock, + auto_minimum: &LogicalVec2, + get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes, ) -> ContentSizes { - let padding = style.padding(containing_block_writing_mode); - let border = style.border_width(containing_block_writing_mode); - let margin = style.margin(containing_block_writing_mode); - - // For margins and paddings, a cyclic percentage is resolved against zero - // for determining intrinsic size contributions. - // https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution - let zero = Length::zero(); - let pb_lengths = Au::from( - border.inline_sum() + - padding.inline_start.percentage_relative_to(zero) + - padding.inline_end.percentage_relative_to(zero), - ); - let mut m_lengths = zero; - if let Some(m) = margin.inline_start.non_auto() { - m_lengths += m.percentage_relative_to(zero) - } - if let Some(m) = margin.inline_end.non_auto() { - m_lengths += m.percentage_relative_to(zero) - } - - let box_sizing = style.get_position().box_sizing; - let inline_size = style - .box_size(containing_block_writing_mode) - .inline - .non_auto() - // Percentages for 'width' are treated as 'auto' - .and_then(|lp| lp.to_length()); - let min_inline_size = style - .min_box_size(containing_block_writing_mode) - .inline - // Percentages for 'min-width' are treated as zero - .percentage_relative_to(zero) - .map(Au::from) - .auto_is(get_auto_minimum); - let max_inline_size = style - .max_box_size(containing_block_writing_mode) - .inline - // Percentages for 'max-width' are treated as 'none' - .and_then(|lp| lp.to_length()) - .map(Au::from); - let clamp = |l: Au| l.clamp_between_extremums(min_inline_size, max_inline_size); - - let border_box_sizes = match inline_size { - Some(non_auto) => { - let clamped = clamp(non_auto.into()); - let border_box_size = match box_sizing { - BoxSizing::ContentBox => clamped + pb_lengths, - BoxSizing::BorderBox => clamped, - }; - border_box_size.into() - }, - None => get_content_size().map(|content_box_size| { - match box_sizing { - // Clamp to 'min-width' and 'max-width', which are sizing the… - BoxSizing::ContentBox => clamp(content_box_size) + pb_lengths, - BoxSizing::BorderBox => clamp(content_box_size + pb_lengths), - } - }), + let (content_box_size, content_min_size, content_max_size, pbm) = + style.content_box_sizes_and_padding_border_margin(containing_block); + let content_min_size = LogicalVec2 { + inline: content_min_size.inline.auto_is(|| auto_minimum.inline), + block: content_min_size.block.auto_is(|| auto_minimum.block), }; - - border_box_sizes.map(|s| s + m_lengths.into()) + let pbm_inline_sum = pbm.padding_border_sums.inline + + pbm.margin.inline_start.auto_is(Au::zero) + + pbm.margin.inline_end.auto_is(Au::zero); + let adjust = |v: Au| { + v.clamp_between_extremums(content_min_size.inline, content_max_size.inline) + pbm_inline_sum + }; + match content_box_size.inline { + AuOrAuto::LengthPercentage(inline_size) => adjust(inline_size).into(), + AuOrAuto::Auto => { + let block_size = content_box_size + .block + .map(|v| v.clamp_between_extremums(content_min_size.block, content_max_size.block)); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size(style, block_size); + get_content_size(&containing_block_for_children).map(adjust) + }, + } } diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index cd7d4912ced..2e44f440e70 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -30,7 +30,7 @@ use crate::geom::{ AuOrAuto, LengthOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize, }; -use crate::ContainingBlock; +use crate::{ContainingBlock, IndefiniteContainingBlock}; #[derive(Clone, Copy, Eq, PartialEq)] pub(crate) enum Display { @@ -233,11 +233,25 @@ pub(crate) trait ComputedValuesExt { box_size: LogicalVec2>, pbm: &PaddingBorderMargin, ) -> LogicalVec2>; + fn content_box_sizes_and_padding_border_margin( + &self, + containing_block: &IndefiniteContainingBlock, + ) -> ( + LogicalVec2, + LogicalVec2, + LogicalVec2>, + PaddingBorderMargin, + ); fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin; fn padding_border_margin_for_intrinsic_size( &self, writing_mode: WritingMode, ) -> PaddingBorderMargin; + fn padding_border_margin_with_writing_mode_and_containing_block_inline_size( + &self, + writing_mode: WritingMode, + containing_block_inline_size: Au, + ) -> PaddingBorderMargin; fn padding( &self, containing_block_writing_mode: WritingMode, @@ -263,7 +277,8 @@ pub(crate) trait ComputedValuesExt { fn preferred_aspect_ratio( &self, natural_aspect_ratio: Option, - containing_block: &ContainingBlock, + containing_block: Option<&ContainingBlock>, + containing_block_writing_mode: WritingMode, ) -> Option; fn background_is_transparent(&self) -> bool; fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags; @@ -431,6 +446,49 @@ impl ComputedValuesExt for ComputedValues { } } + fn content_box_sizes_and_padding_border_margin( + &self, + containing_block: &IndefiniteContainingBlock, + ) -> ( + LogicalVec2, + LogicalVec2, + LogicalVec2>, + PaddingBorderMargin, + ) { + // + // If max size properties or preferred size properties are set to a value containing + // indefinite percentages, we treat the entire value as the initial value of the property. + // However, for min size properties, as well as for margins and paddings, + // we instead resolve indefinite percentages against zero. + let containing_block_size = containing_block.size.map(|v| v.non_auto().map(Into::into)); + let containing_block_size_auto_is_zero = + containing_block_size.map(|v| v.unwrap_or_else(Length::zero)); + let writing_mode = self.writing_mode; + let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size( + writing_mode, + containing_block.size.inline.auto_is(Au::zero), + ); + let box_size = self + .box_size(writing_mode) + .maybe_percentages_relative_to_basis(&containing_block_size); + let content_box_size = self + .content_box_size_for_box_size(box_size, &pbm) + .map(|v| v.map(Au::from)); + let min_size = self + .min_box_size(writing_mode) + .percentages_relative_to_basis(&containing_block_size_auto_is_zero); + let content_min_size = self + .content_min_box_size_for_min_size(min_size, &pbm) + .map(|v| v.map(Au::from)); + let max_size = self + .max_box_size(writing_mode) + .maybe_percentages_relative_to_basis(&containing_block_size); + let content_max_size = self + .content_max_box_size_for_max_size(max_size, &pbm) + .map(|v| v.map(Au::from)); + (content_box_size, content_min_size, content_max_size, pbm) + } + fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin { let cbis = containing_block.inline_size; let padding = self @@ -473,6 +531,30 @@ impl ComputedValuesExt for ComputedValues { } } + fn padding_border_margin_with_writing_mode_and_containing_block_inline_size( + &self, + writing_mode: WritingMode, + containing_block_inline_size: Au, + ) -> PaddingBorderMargin { + let containing_block_inline_size = containing_block_inline_size.into(); + let padding = self + .padding(writing_mode) + .percentages_relative_to(containing_block_inline_size); + let border = self.border_width(writing_mode); + let margin = self + .margin(writing_mode) + .percentages_relative_to(containing_block_inline_size); + PaddingBorderMargin { + padding_border_sums: LogicalVec2 { + inline: (padding.inline_sum() + border.inline_sum()).into(), + block: (padding.block_sum() + border.block_sum()).into(), + }, + padding: padding.into(), + border: border.into(), + margin: margin.map(|margin_side| margin_side.map(Into::into)), + } + } + fn padding( &self, containing_block_writing_mode: WritingMode, @@ -677,7 +759,8 @@ impl ComputedValuesExt for ComputedValues { fn preferred_aspect_ratio( &self, natural_aspect_ratio: Option, - containing_block: &ContainingBlock, + containing_block: Option<&ContainingBlock>, + containing_block_writing_mode: WritingMode, ) -> Option { let GenericAspectRatio { auto, @@ -728,8 +811,13 @@ impl ComputedValuesExt for ComputedValues { let box_sizing_adjustment = match self.clone_box_sizing() { BoxSizing::ContentBox => LogicalVec2::zero(), BoxSizing::BorderBox => { - self.padding_border_margin(containing_block) - .padding_border_sums + match containing_block { + Some(containing_block) => self.padding_border_margin(containing_block), + None => self.padding_border_margin_for_intrinsic_size( + containing_block_writing_mode, + ), + } + .padding_border_sums }, }; Some(AspectRatio { diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 2ed78ef37fc..8dbadb3503c 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -35,7 +35,7 @@ use crate::positioned::{relative_adjustement, PositioningContext, PositioningCon use crate::sizing::ContentSizes; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::table::TableSlotCoordinates; -use crate::ContainingBlock; +use crate::{ContainingBlock, IndefiniteContainingBlock}; /// 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 @@ -201,10 +201,10 @@ impl<'a> TableLayout<'a> { let (size, min_size, max_size) = get_outer_sizes_from_style(&cell.style, writing_mode, &padding_border_sums); - let mut inline_content_sizes = cell - .contents - .contents - .inline_content_sizes(layout_context, writing_mode); + let mut inline_content_sizes = cell.contents.contents.inline_content_sizes( + layout_context, + &IndefiniteContainingBlock::new_for_style(&cell.style), + ); inline_content_sizes.min_content += padding_border_sums.inline; inline_content_sizes.max_content += padding_border_sums.inline; @@ -677,52 +677,42 @@ impl<'a> TableLayout<'a> { .captions .iter() .map(|caption| { - let size; - let min_size; - let max_size; - let padding_border_sums; - let size_is_auto; - { - let context = caption.context.borrow(); - let padding = context - .style - .padding(writing_mode) - .percentages_relative_to(Length::zero()); - let border = context.style.border_width(writing_mode); - let margin = context - .style - .margin(writing_mode) - .percentages_relative_to(Length::zero()) - .auto_is(Length::zero); + let mut context = caption.context.borrow_mut(); + let padding = context + .style + .padding(writing_mode) + .percentages_relative_to(Length::zero()); + let border = context.style.border_width(writing_mode); + let margin = context + .style + .margin(writing_mode) + .percentages_relative_to(Length::zero()) + .auto_is(Length::zero); - padding_border_sums = LogicalVec2 { - inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum()) - .into(), - block: (padding.block_sum() + border.block_sum() + margin.block_sum()) - .into(), - }; + let padding_border_sums = LogicalVec2 { + inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum()) + .into(), + block: (padding.block_sum() + border.block_sum() + margin.block_sum()).into(), + }; - (size, min_size, max_size) = get_outer_sizes_from_style( - &context.style, - writing_mode, - &padding_border_sums, - ); - size_is_auto = context.style.box_size(writing_mode).inline.is_auto(); - } + let (size, min_size, max_size) = + get_outer_sizes_from_style(&context.style, writing_mode, &padding_border_sums); + let size_is_auto = context.style.box_size(writing_mode).inline.is_auto(); // If an inline size is defined it should serve as the upper limit and lower limit // of the caption inline size. - let inline_size = if !size_is_auto { + if !size_is_auto { size.inline } else { - let inline_content_sizes = caption - .context - .borrow_mut() - .inline_content_sizes(layout_context); + let style = context.style.clone(); + let inline_content_sizes = context.inline_content_sizes( + layout_context, + &IndefiniteContainingBlock::new_for_style(&style), + ); inline_content_sizes.min_content + padding_border_sums.inline - }; - - inline_size.min(max_size.inline).max(min_size.inline) + } + .min(max_size.inline) + .max(min_size.inline) }) .max() .unwrap_or_default() @@ -2432,8 +2422,9 @@ impl Table { pub(crate) fn inline_content_sizes( &mut self, layout_context: &LayoutContext, - writing_mode: WritingMode, + containing_block_for_children: &IndefiniteContainingBlock, ) -> ContentSizes { + let writing_mode = containing_block_for_children.style.effective_writing_mode(); let mut layout = TableLayout::new(self); let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode); diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 71c9ed2611e..7891fe10149 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -176439,6 +176439,19 @@ {} ] ], + "percentage-padding-005.html": [ + "928286a590407bd5607fe4797b19be3b4ecb14d8", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square-only.html", + "==" + ] + ], + {} + ] + ], "percentage-size-subitems-001.html": [ "70f3953052a3a770c6cd15ee169607a00fc452b0", [ diff --git a/tests/wpt/meta/css/css-flexbox/flex-basis-011.html.ini b/tests/wpt/meta/css/css-flexbox/flex-basis-011.html.ini deleted file mode 100644 index 03b30d6b482..00000000000 --- a/tests/wpt/meta/css/css-flexbox/flex-basis-011.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[flex-basis-011.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/flex-minimum-height-flex-items-015.html.ini b/tests/wpt/meta/css/css-flexbox/flex-minimum-height-flex-items-015.html.ini deleted file mode 100644 index 40d03e94e60..00000000000 --- a/tests/wpt/meta/css/css-flexbox/flex-minimum-height-flex-items-015.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[flex-minimum-height-flex-items-015.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/flexbox-flex-direction-column-percentage-ignored.html.ini b/tests/wpt/meta/css/css-flexbox/flexbox-flex-direction-column-percentage-ignored.html.ini deleted file mode 100644 index 9ef8a2d553b..00000000000 --- a/tests/wpt/meta/css/css-flexbox/flexbox-flex-direction-column-percentage-ignored.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[flexbox-flex-direction-column-percentage-ignored.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/item-with-max-height-and-scrollbar.html.ini b/tests/wpt/meta/css/css-flexbox/item-with-max-height-and-scrollbar.html.ini deleted file mode 100644 index 58c2f7fbcf5..00000000000 --- a/tests/wpt/meta/css/css-flexbox/item-with-max-height-and-scrollbar.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[item-with-max-height-and-scrollbar.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/nested-flex-image-loading-invalidates-intrinsic-sizes.html.ini b/tests/wpt/meta/css/css-flexbox/nested-flex-image-loading-invalidates-intrinsic-sizes.html.ini deleted file mode 100644 index c6b12cfcf5d..00000000000 --- a/tests/wpt/meta/css/css-flexbox/nested-flex-image-loading-invalidates-intrinsic-sizes.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[nested-flex-image-loading-invalidates-intrinsic-sizes.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/percentage-heights-014.html.ini b/tests/wpt/meta/css/css-flexbox/percentage-heights-014.html.ini deleted file mode 100644 index d0931b7035b..00000000000 --- a/tests/wpt/meta/css/css-flexbox/percentage-heights-014.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[percentage-heights-014.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/position-fixed-001.html.ini b/tests/wpt/meta/css/css-flexbox/position-fixed-001.html.ini deleted file mode 100644 index bfb8983f15e..00000000000 --- a/tests/wpt/meta/css/css-flexbox/position-fixed-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[position-fixed-001.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/aspect-ratio-affects-container-width-when-height-changes.html.ini b/tests/wpt/meta/css/css-sizing/aspect-ratio-affects-container-width-when-height-changes.html.ini deleted file mode 100644 index 2e9c03f50ba..00000000000 --- a/tests/wpt/meta/css/css-sizing/aspect-ratio-affects-container-width-when-height-changes.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[aspect-ratio-affects-container-width-when-height-changes.html] - [#container 1] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-001.html.ini b/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-001.html.ini deleted file mode 100644 index 9304efb5093..00000000000 --- a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[intrinsic-percent-replaced-001.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-002.html.ini b/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-002.html.ini deleted file mode 100644 index 12af7bb7fb7..00000000000 --- a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[intrinsic-percent-replaced-dynamic-002.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-003.html.ini b/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-003.html.ini deleted file mode 100644 index 879b6c8143b..00000000000 --- a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-003.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[intrinsic-percent-replaced-dynamic-003.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-004.html.ini b/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-004.html.ini deleted file mode 100644 index 166bcba9a24..00000000000 --- a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-004.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[intrinsic-percent-replaced-dynamic-004.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-006.html.ini b/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-006.html.ini deleted file mode 100644 index b26a3bc82dd..00000000000 --- a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-006.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[intrinsic-percent-replaced-dynamic-006.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-009.html.ini b/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-009.html.ini deleted file mode 100644 index 58e6b1a546e..00000000000 --- a/tests/wpt/meta/css/css-sizing/intrinsic-percent-replaced-dynamic-009.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[intrinsic-percent-replaced-dynamic-009.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-values/calc-rounding-002.html.ini b/tests/wpt/meta/css/css-values/calc-rounding-002.html.ini deleted file mode 100644 index 4c6fc94a159..00000000000 --- a/tests/wpt/meta/css/css-values/calc-rounding-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[calc-rounding-002.html] - expected: FAIL diff --git a/tests/wpt/tests/css/css-flexbox/percentage-padding-005.html b/tests/wpt/tests/css/css-flexbox/percentage-padding-005.html new file mode 100644 index 00000000000..928286a5904 --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/percentage-padding-005.html @@ -0,0 +1,11 @@ + + + + + + + +

Test passes if there is a filled green square.

+
+
+