diff options
Diffstat (limited to 'components/layout_2020')
-rw-r--r-- | components/layout_2020/flexbox/layout.rs | 420 | ||||
-rw-r--r-- | components/layout_2020/flow/float.rs | 1 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/mod.rs | 8 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 5 | ||||
-rw-r--r-- | components/layout_2020/formatting_contexts.rs | 13 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 1 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 52 | ||||
-rw-r--r-- | components/layout_2020/sizing.rs | 18 |
8 files changed, 331 insertions, 187 deletions
diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 5890924db67..5fd8c3dd44d 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -14,6 +14,7 @@ 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; use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap; +use style::properties::ComputedValues; use style::values::computed::length::Size; use style::values::computed::Length; use style::values::generics::flex::GenericFlexBasis as FlexBasis; @@ -21,9 +22,7 @@ use style::values::generics::length::{GenericLengthPercentageOrAuto, LengthPerce use style::values::specified::align::AlignFlags; use style::Zero; -use super::geom::{ - FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2, MainStartCrossStart, -}; +use super::geom::{FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2}; use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; @@ -244,6 +243,32 @@ struct FinalFlexLineLayout { all_baselines: Baselines, } +impl FlexContainerConfig { + fn align_for(&self, align_self: AlignSelf) -> AlignItems { + let value = align_self.0 .0.value(); + let mapped_value = match value { + AlignFlags::AUTO | AlignFlags::NORMAL => self.align_items.0, + _ => value, + }; + AlignItems(mapped_value) + } + + /// Whether an item with an `auto` preferred cross size needs to be stretched + /// to fill the flex container. + /// <https://drafts.csswg.org/css-flexbox/#stretched> + fn item_with_auto_cross_size_stretches_to_container_size( + &self, + item_style: &ComputedValues, + item_margin: &FlexRelativeSides<AuOrAuto>, + ) -> bool { + self.container_is_single_line && + item_with_auto_cross_size_stretches_to_line_size( + self.align_for(item_style.clone_align_self()), + item_margin, + ) + } +} + impl FlexContext<'_> { fn vec2_to_flex_relative<T>(&self, x: LogicalVec2<T>) -> FlexRelativeVec2<T> { self.config.flex_axis.vec2_to_flex_relative(x) @@ -275,12 +300,7 @@ impl FlexContext<'_> { } fn align_for(&self, align_self: AlignSelf) -> AlignItems { - let value = align_self.0 .0.value(); - let mapped_value = match value { - AlignFlags::AUTO | AlignFlags::NORMAL => self.config.align_items.0, - _ => value, - }; - AlignItems(mapped_value) + self.config.align_for(align_self) } } @@ -388,8 +408,7 @@ impl FlexContainer { layout_context, containing_block_for_children, container_is_horizontal, - self.config.flex_axis, - self.config.main_start_cross_start_sides_are, + &self.config, &flex_context_getter, ); @@ -950,6 +969,20 @@ impl<'a> FlexItem<'a> { .content_min_box_size(containing_block, &pbm) .map(|v| v.map(Au::from)); + let margin_auto_is_zero = flex_context.sides_to_flex_relative(pbm.margin.auto_is(Au::zero)); + let padding = flex_context.sides_to_flex_relative(pbm.padding); + let border = flex_context.sides_to_flex_relative(pbm.border); + let margin = flex_context.sides_to_flex_relative(pbm.margin); + let padding_border = padding.sum_by_axis() + border.sum_by_axis(); + let pbm_auto_is_zero = FlexRelativeVec2 { + main: padding_border.main, + cross: padding_border.cross, + } + margin_auto_is_zero.sum_by_axis(); + + let item_with_auto_cross_size_stretches_to_container_size = flex_context + .config + .item_with_auto_cross_size_stretches_to_container_size(&box_.style(), &margin); + let flex_relative_content_box_size = flex_context.vec2_to_flex_relative(content_box_size); let flex_relative_content_max_size = flex_context.vec2_to_flex_relative(max_size); let flex_relative_content_min_size = flex_context.vec2_to_flex_relative(min_size); @@ -962,6 +995,8 @@ impl<'a> FlexItem<'a> { flex_relative_content_box_size, flex_relative_content_min_size, flex_relative_content_max_size, + &pbm_auto_is_zero, + item_with_auto_cross_size_stretches_to_container_size, |item| { let min_size_auto_is_zero = min_size.auto_is(Au::zero); @@ -971,6 +1006,7 @@ impl<'a> FlexItem<'a> { content_box_size, min_size_auto_is_zero, max_size, + item_with_auto_cross_size_stretches_to_container_size, IntrinsicSizingMode::Size, ) }, @@ -979,15 +1015,6 @@ impl<'a> FlexItem<'a> { cross: flex_relative_content_min_size.cross.auto_is(Au::zero), }; - let margin_auto_is_zero = flex_context.sides_to_flex_relative(pbm.margin.auto_is(Au::zero)); - let padding = flex_context.sides_to_flex_relative(pbm.padding); - let border = flex_context.sides_to_flex_relative(pbm.border); - let padding_border = padding.sum_by_axis() + border.sum_by_axis(); - let pbm_auto_is_zero = FlexRelativeVec2 { - main: padding_border.main, - cross: padding_border.cross, - } + margin_auto_is_zero.sum_by_axis(); - let align_self = flex_context.align_for(box_.style().clone_align_self()); let (flex_base_size, flex_base_size_is_definite) = box_.flex_base_size( @@ -999,6 +1026,8 @@ impl<'a> FlexItem<'a> { flex_relative_content_min_size, flex_relative_content_max_size, padding_border, + &pbm_auto_is_zero, + item_with_auto_cross_size_stretches_to_container_size, |item| { let min_size = flex_context .config @@ -1010,6 +1039,7 @@ impl<'a> FlexItem<'a> { content_box_size, min_size, max_size, + item_with_auto_cross_size_stretches_to_container_size, IntrinsicSizingMode::Size, ) }, @@ -1036,6 +1066,11 @@ impl<'a> FlexItem<'a> { align_self, } } + + fn stretches(&self) -> bool { + self.content_box_size.cross.is_auto() && + item_with_auto_cross_size_stretches_to_line_size(self.align_self, &self.margin) + } } fn cross_axis_is_item_block_axis( @@ -1049,6 +1084,17 @@ fn cross_axis_is_item_block_axis( container_is_row ^ item_is_orthogonal } +/// Whether an item with an `auto` preferred cross size will stretched to fill the cross size of its flex line. +/// <https://drafts.csswg.org/css-flexbox/#stretched> +fn item_with_auto_cross_size_stretches_to_line_size( + align_self: AlignItems, + margin: &FlexRelativeSides<AuOrAuto>, +) -> bool { + align_self.0.value() == AlignFlags::STRETCH && + !margin.cross_start.is_auto() && + !margin.cross_end.is_auto() +} + // “Collect flex items into flex lines” // https://drafts.csswg.org/css-flexbox/#algo-line-break fn do_initial_flex_line_layout<'items>( @@ -1411,11 +1457,8 @@ impl InitialFlexLineLayout<'_> { self.item_layout_results.iter_mut(), self.item_used_main_sizes.iter(), ) { - let has_stretch = item.align_self.0.value() == AlignFlags::STRETCH; - let used_cross_size = if has_stretch && - item.content_box_size.cross.is_auto() && - !(item.margin.cross_start.is_auto() || item.margin.cross_end.is_auto()) - { + let stretches = item.stretches(); + let used_cross_size = if stretches { (final_line_cross_size - item.pbm_auto_is_zero.cross).clamp_between_extremums( item.content_min_size.cross, item.content_max_size.cross, @@ -1425,7 +1468,7 @@ impl InitialFlexLineLayout<'_> { }; item_used_cross_sizes.push(used_cross_size); - if has_stretch { + if stretches { // “If the flex item has `align-self: stretch`, redo layout for its contents, // treating this used size as its definite cross size // so that percentage-sized children can be resolved.” @@ -1585,7 +1628,7 @@ impl FlexItem<'_> { flex_context: &FlexContext, used_cross_size_override: Option<Au>, ) -> FlexItemLayoutResult { - let containing_block = &flex_context.containing_block; + let containing_block = flex_context.containing_block; let mut positioning_context = PositioningContext::new_for_style(self.box_.style()) .unwrap_or_else(|| { PositioningContext::new_for_subtree( @@ -1604,24 +1647,83 @@ impl FlexItem<'_> { ); // … and also the item’s inline axis. + let cross_size = match used_cross_size_override { + Some(s) => AuOrAuto::LengthPercentage(s), + None => self.content_box_size.cross.map(|cross_size| { + cross_size.clamp_between_extremums( + self.content_min_size.cross, + self.content_max_size.cross, + ) + }), + }; + + let ifc = &mut self.box_.independent_formatting_context; + let item_writing_mode = ifc.style().effective_writing_mode(); + let item_is_horizontal = item_writing_mode.is_horizontal(); let flex_axis = flex_context.config.flex_axis; - match &mut self.box_.independent_formatting_context { + let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis( + containing_block + .style + .effective_writing_mode() + .is_horizontal(), + item_is_horizontal, + 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 style = ifc.style().clone(); + let containing_block_for_children = + IndefiniteContainingBlock::new_for_style_and_block_size( + &style, + AuOrAuto::LengthPercentage(used_main_size), + ); + let content_contributions = ifc + .inline_content_sizes( + flex_context.layout_context, + &containing_block_for_children, + &containing_block.into(), + ) + .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) + }), + // 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. + // <https://drafts.csswg.org/css-flexbox-1/#definite-sizes> + if self.flex_base_size_is_definite || + flex_context.container_definite_inner_size.main.is_some() + { + AuOrAuto::LengthPercentage(used_main_size) + } else { + AuOrAuto::Auto + }, + ) + }; + + match ifc { IndependentFormattingContext::Replaced(replaced) => { - let pbm = replaced.style.padding_border_margin(containing_block); - let box_size = used_cross_size_override.map(|size| LogicalVec2 { - inline: replaced - .style - .content_box_size(containing_block, &pbm) - .inline - .map(Au::from), - block: AuOrAuto::LengthPercentage(size), - }); - let size = replaced.contents.used_size_as_if_inline_element( - containing_block, - &replaced.style, - box_size, - &pbm, - ); + let size = replaced + .contents + .used_size_as_if_inline_element_from_content_box_sizes( + containing_block, + &replaced.style, + LogicalVec2 { + inline: AuOrAuto::LengthPercentage(inline_size), + block: block_size, + }, + flex_axis.vec2_to_flow_relative(self.content_min_size), + flex_axis.vec2_to_flow_relative(self.content_max_size), + ); let cross_size = flex_axis.vec2_to_flex_relative(size).cross; let fragments = replaced.contents.make_fragments( &replaced.style, @@ -1640,66 +1742,6 @@ impl FlexItem<'_> { } }, IndependentFormattingContext::NonReplaced(non_replaced) => { - let cross_size = match used_cross_size_override { - Some(s) => AuOrAuto::LengthPercentage(s), - None => self.content_box_size.cross.map(|cross_size| { - cross_size.clamp_between_extremums( - self.content_min_size.cross, - self.content_max_size.cross, - ) - }), - }; - - let item_writing_mode = non_replaced.style.effective_writing_mode(); - let item_is_horizontal = item_writing_mode.is_horizontal(); - let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis( - containing_block - .style - .effective_writing_mode() - .is_horizontal(), - item_is_horizontal, - 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 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) - }), - // 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. - // <https://drafts.csswg.org/css-flexbox-1/#definite-sizes> - if self.flex_base_size_is_definite || - flex_context.container_definite_inner_size.main.is_some() - { - AuOrAuto::LengthPercentage(used_main_size) - } else { - AuOrAuto::Auto - }, - ) - }; - let item_as_containing_block = ContainingBlock { inline_size, block_size, @@ -1903,10 +1945,11 @@ impl FlexItemBox { layout_context: &LayoutContext, containing_block: &IndefiniteContainingBlock, container_is_horizontal: bool, - flex_axis: FlexAxis, - main_start_cross_start: MainStartCrossStart, + config: &FlexContainerConfig, flex_context_getter: &impl Fn() -> &'a FlexContext<'a>, ) -> FlexItemBoxInlineContentSizesInfo { + let flex_axis = config.flex_axis; + let main_start_cross_start = config.main_start_cross_start_sides_are; let style = self.style().clone(); let item_writing_mode = style.effective_writing_mode(); let item_is_horizontal = item_writing_mode.is_horizontal(); @@ -1915,6 +1958,19 @@ impl FlexItemBox { let (content_box_size, content_min_size, content_max_size, pbm) = style.content_box_sizes_and_padding_border_margin(containing_block); + let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding); + let border = main_start_cross_start.sides_to_flex_relative(pbm.border); + let margin = main_start_cross_start.sides_to_flex_relative(pbm.margin); + let padding_border = padding.sum_by_axis() + border.sum_by_axis(); + let margin_auto_is_zero = pbm.margin.auto_is(Au::zero); + let margin_auto_is_zero = + main_start_cross_start.sides_to_flex_relative(margin_auto_is_zero); + let pbm_auto_is_zero = FlexRelativeVec2 { + main: padding_border.main, + cross: padding_border.cross, + } + margin_auto_is_zero.sum_by_axis(); + let item_with_auto_cross_size_stretches_to_container_size = + config.item_with_auto_cross_size_stretches_to_container_size(&style, &margin); let automatic_min_size = self.automatic_min_size( layout_context, containing_block, @@ -1922,6 +1978,8 @@ impl FlexItemBox { flex_axis.vec2_to_flex_relative(content_box_size), flex_axis.vec2_to_flex_relative(content_min_size), flex_axis.vec2_to_flex_relative(content_max_size), + &pbm_auto_is_zero, + item_with_auto_cross_size_stretches_to_container_size, |item| { item.layout_for_block_content_size( flex_context_getter(), @@ -1929,6 +1987,7 @@ impl FlexItemBox { content_box_size, content_min_size.map(|v| v.auto_is(Au::zero)), content_max_size, + item_with_auto_cross_size_stretches_to_container_size, IntrinsicSizingMode::Size, ) }, @@ -1951,6 +2010,7 @@ impl FlexItemBox { content_box_size, content_min_size_no_auto, content_max_size, + item_with_auto_cross_size_stretches_to_container_size, IntrinsicSizingMode::Size, ) }; @@ -1964,6 +2024,7 @@ impl FlexItemBox { layout_context, containing_block, &content_min_size_no_auto, + item_with_auto_cross_size_stretches_to_container_size, ), FlexAxis::Column => self .layout_for_block_content_size( @@ -1972,6 +2033,7 @@ impl FlexItemBox { content_box_size, content_min_size_no_auto, content_max_size, + item_with_auto_cross_size_stretches_to_container_size, IntrinsicSizingMode::Contribution, ) .into(), @@ -1981,31 +2043,21 @@ impl FlexItemBox { let content_min_size_no_auto = flex_axis.vec2_to_flex_relative(content_min_size_no_auto); let content_max_size = flex_axis.vec2_to_flex_relative(content_max_size); - let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding); - let border = main_start_cross_start.sides_to_flex_relative(pbm.border); - let padding_border = padding.sum_by_axis() + border.sum_by_axis(); - let margin_auto_is_zero = pbm.margin.auto_is(Au::zero); - let margin_auto_is_zero = - main_start_cross_start.sides_to_flex_relative(margin_auto_is_zero); - let pbm_auto_is_zero = FlexRelativeVec2 { - main: padding_border.main, - cross: padding_border.cross, - } + margin_auto_is_zero.sum_by_axis(); - // TODO: when laying out a column container with an indefinite main size, // we compute the base sizes of the items twice. We should consider caching. let (flex_base_size, _) = self.flex_base_size( layout_context, containing_block, - FlexRelativeVec2 { - main: None, - cross: None, - }, + config + .flex_axis + .vec2_to_flex_relative(containing_block.size.map(|v| v.non_auto())), cross_axis_is_item_block_axis, content_box_size, content_min_size_no_auto, content_max_size, padding_border, + &pbm_auto_is_zero, + item_with_auto_cross_size_stretches_to_container_size, block_content_size_callback, ); @@ -2109,6 +2161,8 @@ impl FlexItemBox { content_box_size: FlexRelativeVec2<AuOrAuto>, min_size: FlexRelativeVec2<GenericLengthPercentageOrAuto<Au>>, max_size: FlexRelativeVec2<Option<Au>>, + pbm_auto_is_zero: &FlexRelativeVec2<Au>, + auto_cross_size_stretches_to_container_size: bool, block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au, ) -> Au { // FIXME(stshine): Consider more situations when auto min size is not needed. @@ -2137,14 +2191,25 @@ impl FlexItemBox { Direction::Block }; + let cross_size = + 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 + } else { + containing_block.size.inline + } + .map(|v| v - pbm_auto_is_zero.cross) + } else { + content_box_size.cross + } + .map(|v| v.clamp_between_extremums(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, content_box_size.cross) { + let transferred_size_suggestion = match (ratio, cross_size) { (Some(ratio), AuOrAuto::LengthPercentage(cross_size)) => { - let cross_size = cross_size - .clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross); Some(ratio.compute_dependent_size(main_axis, cross_size)) }, _ => None, @@ -2155,12 +2220,9 @@ 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); + IndefiniteContainingBlock::new_for_style_and_block_size(&style, cross_size); self.independent_formatting_context .inline_content_sizes( layout_context, @@ -2204,9 +2266,11 @@ impl FlexItemBox { container_definite_inner_size: FlexRelativeVec2<Option<Au>>, cross_axis_is_item_block_axis: bool, content_box_size: FlexRelativeVec2<AuOrAuto>, - min_size: FlexRelativeVec2<Au>, - max_size: FlexRelativeVec2<Option<Au>>, + content_min_box_size: FlexRelativeVec2<Au>, + content_max_box_size: FlexRelativeVec2<Option<Au>>, padding_border_sums: FlexRelativeVec2<Au>, + pbm_auto_is_zero: &FlexRelativeVec2<Au>, + item_with_auto_cross_size_stretches_to_container_size: bool, block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au, ) -> (Au, bool) { let flex_item = &mut self.independent_formatting_context; @@ -2268,7 +2332,38 @@ impl FlexItemBox { (length, true) }, FlexBasis::Content => { - // FIXME: implement cases B, C, D. + // > B: If the flex item has ... + // > - a preferred aspect ratio, + // > - a used flex basis of content, and + // > - a definite cross size, + // > then the flex base size is calculated from its used cross size and the flex item’s aspect ratio. + let ratio = flex_item.preferred_aspect_ratio(containing_block); + let main_axis = if cross_axis_is_item_block_axis { + Direction::Inline + } else { + Direction::Block + }; + // > 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 cross_size = if content_box_size.cross.is_auto() && + item_with_auto_cross_size_stretches_to_container_size + { + container_definite_inner_size + .cross + .map(|v| v - pbm_auto_is_zero.cross) + } else { + content_box_size.cross.non_auto() + }; + if let (Some(ratio), Some(cross_size)) = (ratio, cross_size) { + let cross_size = cross_size.clamp_between_extremums( + content_min_box_size.cross, + content_max_box_size.cross, + ); + return (ratio.compute_dependent_size(main_axis, cross_size), true); + } + + // FIXME: implement cases C, D. // > E. Otherwise, size the item into the available space using its used flex basis in place of // > its main size, treating a value of content as max-content. If a cross size is needed to @@ -2279,19 +2374,32 @@ 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. - let style = style.clone(); - let block_size = content_box_size - .cross - .map(|v| v.clamp_between_extremums(min_size.cross, max_size.cross)); + let style = flex_item.style().clone(); + let block_size = content_box_size.cross.map(|v| { + v.clamp_between_extremums( + content_min_box_size.cross, + content_max_box_size.cross, + ) + }); let containing_block_for_children = IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size); - flex_item + let max_content = flex_item .inline_content_sizes( layout_context, &containing_block_for_children, containing_block, ) - .max_content + .max_content; + if let Some(ratio) = ratio { + max_content.clamp_between_extremums( + ratio.compute_dependent_size(main_axis, content_min_box_size.cross), + content_max_box_size + .cross + .map(|v| ratio.compute_dependent_size(main_axis, v)), + ) + } else { + max_content + } } else { block_content_size_callback(self) }; @@ -2304,9 +2412,10 @@ impl FlexItemBox { &mut self, flex_context: &FlexContext, padding_border_margin: &PaddingBorderMargin, - content_box_size: LogicalVec2<AuOrAuto>, - min_size: LogicalVec2<Au>, - max_size: LogicalVec2<Option<Au>>, + mut content_box_size: LogicalVec2<AuOrAuto>, + mut min_size: LogicalVec2<Au>, + mut max_size: LogicalVec2<Option<Au>>, + item_with_auto_cross_size_stretches_to_container_size: bool, intrinsic_sizing_mode: IntrinsicSizingMode, ) -> Au { let mut positioning_context = PositioningContext::new_for_subtree( @@ -2317,14 +2426,22 @@ impl FlexItemBox { match &mut self.independent_formatting_context { IndependentFormattingContext::Replaced(replaced) => { - // TODO: handle intrinsic_sizing_mode. - let size = replaced.contents.used_size_as_if_inline_element( - flex_context.containing_block, - &replaced.style, - None, - padding_border_margin, - ); - size.block + content_box_size.inline = content_box_size.inline.map(|v| v.max(Au::zero())); + if intrinsic_sizing_mode == IntrinsicSizingMode::Size { + content_box_size.block = AuOrAuto::Auto; + min_size.block = Au::zero(); + max_size.block = None; + } + replaced + .contents + .used_size_as_if_inline_element_from_content_box_sizes( + flex_context.containing_block, + &replaced.style, + content_box_size, + min_size, + max_size, + ) + .block }, IndependentFormattingContext::NonReplaced(non_replaced) => { // TODO: This is wrong if the item writing mode is different from the flex @@ -2332,18 +2449,11 @@ impl FlexItemBox { let inline_size = content_box_size .inline .auto_is(|| { - let will_stretch = flex_context - .align_for(non_replaced.style.clone_align_self()) - .0 == - AlignFlags::STRETCH && - !padding_border_margin.margin.inline_start.is_auto() && - !padding_border_margin.margin.inline_end.is_auto(); - let containing_block_inline_size_minus_pbm = flex_context.containing_block.inline_size - padding_border_margin.padding_border_sums.inline; - if will_stretch { + if item_with_auto_cross_size_stretches_to_container_size { containing_block_inline_size_minus_pbm } else { let style = non_replaced.style.clone(); diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 4b1aa1844ce..687a305820f 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -980,7 +980,6 @@ impl FloatBox { content_size = replaced.contents.used_size_as_if_inline_element( containing_block, &replaced.style, - None, &pbm, ); children = replaced.contents.make_fragments( diff --git a/components/layout_2020/flow/inline/mod.rs b/components/layout_2020/flow/inline/mod.rs index d78359e8da7..edf1ba81e18 100644 --- a/components/layout_2020/flow/inline/mod.rs +++ b/components/layout_2020/flow/inline/mod.rs @@ -1917,12 +1917,7 @@ impl IndependentFormattingContext { IndependentFormattingContext::Replaced(replaced) => { let size = replaced .contents - .used_size_as_if_inline_element( - layout.containing_block, - &replaced.style, - None, - &pbm, - ) + .used_size_as_if_inline_element(layout.containing_block, &replaced.style, &pbm) .to_physical_size(container_writing_mode); let fragments = replaced.contents.make_fragments(&replaced.style, size); let content_rect = PhysicalRect::new(pbm_physical_origin, size); @@ -2383,6 +2378,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { self.layout_context, self.containing_block, &LogicalVec2::zero(), + false, /* auto_block_size_stretches_to_containing_block */ ); if !inline_formatting_context diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 0d5d233eeb0..3f3f7334182 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -372,6 +372,7 @@ fn calculate_inline_content_size_for_block_level_boxes( layout_context, containing_block, &LogicalVec2::zero(), + false, /* auto_block_size_stretches_to_containing_block */ ) .max(ContentSizes::zero()); let style_box = &float_box.contents.style().get_box(); @@ -384,6 +385,7 @@ fn calculate_inline_content_size_for_block_level_boxes( style, &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) }, @@ -400,6 +402,7 @@ fn calculate_inline_content_size_for_block_level_boxes( layout_context, containing_block, &LogicalVec2::zero(), + false, /* auto_block_size_stretches_to_containing_block */ ) .max(ContentSizes::zero()); Some((size, Float::None, independent.style().get_box().clear)) @@ -1296,7 +1299,7 @@ fn layout_in_flow_replaced_block_level( mut sequential_layout_state: Option<&mut SequentialLayoutState>, ) -> BoxFragment { let pbm = style.padding_border_margin(containing_block); - let content_size = replaced.used_size_as_if_inline_element(containing_block, style, None, &pbm); + let content_size = replaced.used_size_as_if_inline_element(containing_block, style, &pbm); let margin_inline_start; let margin_inline_end; diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index aec1b97947a..1941e7c27ff 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -198,12 +198,14 @@ impl IndependentFormattingContext { layout_context: &LayoutContext, containing_block: &IndefiniteContainingBlock, auto_minimum: &LogicalVec2<Au>, + auto_block_size_stretches_to_containing_block: bool, ) -> ContentSizes { match self { Self::NonReplaced(non_replaced) => sizing::outer_inline( &non_replaced.style.clone(), containing_block, auto_minimum, + auto_block_size_stretches_to_containing_block, |containing_block_for_children| { non_replaced.inline_content_sizes(layout_context, containing_block_for_children) }, @@ -212,6 +214,7 @@ impl IndependentFormattingContext { &replaced.style, containing_block, auto_minimum, + auto_block_size_stretches_to_containing_block, |containing_block_for_children| { replaced.contents.inline_content_sizes( layout_context, @@ -222,6 +225,16 @@ impl IndependentFormattingContext { ), } } + + pub(crate) fn preferred_aspect_ratio( + &self, + containing_block: &IndefiniteContainingBlock, + ) -> Option<AspectRatio> { + match self { + Self::NonReplaced(_) => None, + Self::Replaced(replaced) => replaced.preferred_aspect_ratio(containing_block), + } + } } impl NonReplacedFormattingContext { diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 433c9a4827f..10f86fea676 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -499,7 +499,6 @@ impl HoistedAbsolutelyPositionedBox { let used_size = replaced.contents.used_size_as_if_inline_element( &containing_block.into(), &replaced.style, - None, &pbm, ); LogicalVec2 { diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index d8a9f5d07aa..aec71c15acb 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -360,32 +360,50 @@ impl ReplacedContent { /// /// Also used in other cases, for example /// <https://drafts.csswg.org/css2/visudet.html#block-replaced-width> - pub fn used_size_as_if_inline_element( + pub(crate) fn used_size_as_if_inline_element( &self, containing_block: &ContainingBlock, style: &ComputedValues, - box_size: Option<LogicalVec2<AuOrAuto>>, pbm: &PaddingBorderMargin, ) -> LogicalVec2<Au> { - let mode = style.effective_writing_mode(); - let intrinsic_size = self.flow_relative_intrinsic_size(style); - let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style); - - let box_size = box_size.unwrap_or( - style - .content_box_size(containing_block, pbm) - // We need to clamp to zero here to obtain the proper aspect - // ratio when box-sizing is border-box and the inner box size - // would otherwise be negative. - .map(|v| v.map(|v| Au::from(v).max(Au::zero()))), - ); - let max_box_size = style - .content_max_box_size(containing_block, pbm) - .map(|v| v.map(Au::from)); + let box_size = style + .content_box_size(containing_block, pbm) + // We need to clamp to zero here to obtain the proper aspect + // ratio when box-sizing is border-box and the inner box size + // would otherwise be negative. + .map(|v| v.map(|v| Au::from(v).max(Au::zero()))); let min_box_size = style .content_min_box_size(containing_block, pbm) .map(|v| v.map(Au::from)) .auto_is(Au::zero); + let max_box_size = style + .content_max_box_size(containing_block, pbm) + .map(|v| v.map(Au::from)); + self.used_size_as_if_inline_element_from_content_box_sizes( + containing_block, + style, + box_size, + min_box_size, + max_box_size, + ) + } + + /// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-width> + /// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height> + /// + /// Also used in other cases, for example + /// <https://drafts.csswg.org/css2/visudet.html#block-replaced-width> + pub(crate) fn used_size_as_if_inline_element_from_content_box_sizes( + &self, + containing_block: &ContainingBlock, + style: &ComputedValues, + box_size: LogicalVec2<AuOrAuto>, + min_box_size: LogicalVec2<Au>, + max_box_size: LogicalVec2<Option<Au>>, + ) -> LogicalVec2<Au> { + let mode = style.effective_writing_mode(); + let intrinsic_size = self.flow_relative_intrinsic_size(style); + let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style); let default_object_size = || { // FIXME: diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index f83d71e118c..70c726d3c0e 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -111,6 +111,7 @@ pub(crate) fn outer_inline( style: &ComputedValues, containing_block: &IndefiniteContainingBlock, auto_minimum: &LogicalVec2<Au>, + auto_block_size_stretches_to_containing_block: bool, get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes, ) -> ContentSizes { let (content_box_size, content_min_size, content_max_size, pbm) = @@ -119,18 +120,23 @@ pub(crate) fn outer_inline( inline: content_min_size.inline.auto_is(|| auto_minimum.inline), block: content_min_size.block.auto_is(|| auto_minimum.block), }; - 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 margin = pbm.margin.map(|v| v.auto_is(Au::zero)); + let pbm_inline_sum = pbm.padding_border_sums.inline + margin.inline_sum(); 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 block_size = if content_box_size.block.is_auto() && + auto_block_size_stretches_to_containing_block + { + let outer_block_size = containing_block.size.block; + outer_block_size.map(|v| v - pbm.padding_border_sums.block - margin.block_sum()) + } else { + 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) |