Avoid recomputing inline_content_sizes() when not needed (#33806)

The result of `inline_content_sizes()` may depend on the block size of
the containing block, so we were always recomputing in case we got
a different block size.

However, if no content has a vertical percentage or stretches vertically,
then we don't need to recompute: the result will be the same anyways.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Oriol Brufau 2024-10-14 18:06:27 +02:00 committed by GitHub
parent 821bed1d11
commit b9ed45942d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 257 additions and 139 deletions

View file

@ -171,7 +171,7 @@ where
let non_replaced = NonReplacedFormattingContext { let non_replaced = NonReplacedFormattingContext {
base_fragment_info: info.into(), base_fragment_info: info.into(),
style: info.style.clone(), style: info.style.clone(),
content_sizes: None, content_sizes_result: None,
contents: NonReplacedFormattingContextContents::Flow( contents: NonReplacedFormattingContextContents::Flow(
block_formatting_context, block_formatting_context,
), ),

View file

@ -31,7 +31,7 @@ use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
use crate::positioned::{ use crate::positioned::{
relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength,
}; };
use crate::sizing::{ContentSizes, IntrinsicSizingMode}; use crate::sizing::{ContentSizes, InlineContentSizesResult, IntrinsicSizingMode};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -336,6 +336,7 @@ struct FlexItemBoxInlineContentSizesInfo {
min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor, min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor, max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
min_content_main_size_for_multiline_container: Au, min_content_main_size_for_multiline_container: Au,
depends_on_block_constraints: bool,
} }
impl FlexContainer { impl FlexContainer {
@ -348,7 +349,7 @@ impl FlexContainer {
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
match self.config.flex_axis { match self.config.flex_axis {
FlexAxis::Row => { FlexAxis::Row => {
self.main_content_sizes(layout_context, containing_block_for_children, || { self.main_content_sizes(layout_context, containing_block_for_children, || {
@ -367,14 +368,15 @@ impl FlexContainer {
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
// <https://drafts.csswg.org/css-flexbox/#intrinsic-cross-sizes> // <https://drafts.csswg.org/css-flexbox/#intrinsic-cross-sizes>
assert_eq!( assert_eq!(
self.config.flex_axis, self.config.flex_axis,
FlexAxis::Column, FlexAxis::Column,
"The cross axis should be the inline one" "The cross axis should be the inline one"
); );
let mut content_sizes = ContentSizes::zero(); let mut sizes = ContentSizes::zero();
let mut depends_on_block_constraints = false;
for kid in self.children.iter() { for kid in self.children.iter() {
let kid = &mut *kid.borrow_mut(); let kid = &mut *kid.borrow_mut();
match kid { match kid {
@ -383,17 +385,22 @@ impl FlexContainer {
// columns, and sum the column sizes and gaps. // columns, and sum the column sizes and gaps.
// TODO: Use the proper automatic minimum size. // TODO: Use the proper automatic minimum size.
let ifc = &mut item.independent_formatting_context; let ifc = &mut item.independent_formatting_context;
content_sizes.max_assign(ifc.outer_inline_content_sizes( let result = ifc.outer_inline_content_sizes(
layout_context, layout_context,
containing_block_for_children, containing_block_for_children,
&LogicalVec2::zero(), &LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */ false, /* auto_block_size_stretches_to_containing_block */
)); );
sizes.max_assign(result.sizes);
depends_on_block_constraints |= result.depends_on_block_constraints;
}, },
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {}, FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
} }
} }
content_sizes InlineContentSizesResult {
sizes,
depends_on_block_constraints,
}
} }
fn main_content_sizes<'a>( fn main_content_sizes<'a>(
@ -401,7 +408,7 @@ impl FlexContainer {
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
flex_context_getter: impl Fn() -> &'a FlexContext<'a>, flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
) -> ContentSizes { ) -> InlineContentSizesResult {
// - TODO: calculate intrinsic cross sizes when container is a column // - TODO: calculate intrinsic cross sizes when container is a column
// (and check for writing-mode?) // (and check for writing-mode?)
// - TODO: Collapsed flex items need to be skipped for intrinsic size calculation. // - TODO: Collapsed flex items need to be skipped for intrinsic size calculation.
@ -483,6 +490,7 @@ impl FlexContainer {
} else { } else {
Au::zero() Au::zero()
}; };
let mut container_depends_on_block_constraints = false;
for FlexItemBoxInlineContentSizesInfo { for FlexItemBoxInlineContentSizesInfo {
outer_flex_base_size, outer_flex_base_size,
@ -492,6 +500,7 @@ impl FlexContainer {
min_flex_factors, min_flex_factors,
max_flex_factors, max_flex_factors,
min_content_main_size_for_multiline_container, min_content_main_size_for_multiline_container,
depends_on_block_constraints,
} in item_infos.iter() } in item_infos.iter()
{ {
// > 4. Add each items flex base size to the product of its flex grow factor (scaled flex shrink // > 4. Add each items flex base size to the product of its flex grow factor (scaled flex shrink
@ -528,11 +537,16 @@ impl FlexContainer {
container_min_content_size container_min_content_size
.max_assign(*min_content_main_size_for_multiline_container); .max_assign(*min_content_main_size_for_multiline_container);
} }
container_depends_on_block_constraints |= depends_on_block_constraints;
} }
ContentSizes { InlineContentSizesResult {
sizes: ContentSizes {
min_content: container_min_content_size, min_content: container_min_content_size,
max_content: container_max_content_size, max_content: container_max_content_size,
},
depends_on_block_constraints: container_depends_on_block_constraints,
} }
} }
@ -573,6 +587,7 @@ impl FlexContainer {
FlexAxis::Row => containing_block.inline_size, FlexAxis::Row => containing_block.inline_size,
FlexAxis::Column => containing_block.block_size.auto_is(|| { FlexAxis::Column => containing_block.block_size.auto_is(|| {
self.main_content_sizes(layout_context, &containing_block.into(), || &flex_context) self.main_content_sizes(layout_context, &containing_block.into(), || &flex_context)
.sizes
.max_content .max_content
}), }),
}; };
@ -1023,7 +1038,7 @@ impl<'a> FlexItem<'a> {
flex_context.config.flex_axis, flex_context.config.flex_axis,
); );
let (content_box_size, min_size, max_size, pbm) = box_ let (content_box_size, min_size, max_size, pbm, _) = box_
.style() .style()
.content_box_sizes_and_padding_border_margin_deprecated(&containing_block.into()); .content_box_sizes_and_padding_border_margin_deprecated(&containing_block.into());
@ -1735,6 +1750,7 @@ impl FlexItem<'_> {
&containing_block_for_children, &containing_block_for_children,
&containing_block.into(), &containing_block.into(),
) )
.sizes
.map(|size| { .map(|size| {
size.clamp_between_extremums( size.clamp_between_extremums(
self.content_min_size.cross, self.content_min_size.cross,
@ -2008,7 +2024,7 @@ impl FlexItemBox {
let cross_axis_is_item_block_axis = let cross_axis_is_item_block_axis =
cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis); cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis);
let (content_box_size, content_min_size, content_max_size, pbm) = let (content_box_size, content_min_size, content_max_size, pbm, _) =
style.content_box_sizes_and_padding_border_margin_deprecated(containing_block); style.content_box_sizes_and_padding_border_margin_deprecated(containing_block);
let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding); 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 border = main_start_cross_start.sides_to_flex_relative(pbm.border);
@ -2069,17 +2085,23 @@ impl FlexItemBox {
// Compute the min-content and max-content contributions of the item. // Compute the min-content and max-content contributions of the item.
// <https://drafts.csswg.org/css-flexbox/#intrinsic-item-contributions> // <https://drafts.csswg.org/css-flexbox/#intrinsic-item-contributions>
let content_contribution_sizes = match flex_axis { let (content_contribution_sizes, depends_on_block_constraints) = match flex_axis {
FlexAxis::Row => self FlexAxis::Row => {
let InlineContentSizesResult {
sizes,
depends_on_block_constraints,
} = self
.independent_formatting_context .independent_formatting_context
.outer_inline_content_sizes( .outer_inline_content_sizes(
layout_context, layout_context,
containing_block, containing_block,
&content_min_size_no_auto, &content_min_size_no_auto,
item_with_auto_cross_size_stretches_to_container_size, item_with_auto_cross_size_stretches_to_container_size,
), );
FlexAxis::Column => self (sizes, depends_on_block_constraints)
.layout_for_block_content_size( },
FlexAxis::Column => {
let size = self.layout_for_block_content_size(
flex_context_getter(), flex_context_getter(),
&pbm, &pbm,
content_box_size, content_box_size,
@ -2087,8 +2109,9 @@ impl FlexItemBox {
content_max_size, content_max_size,
item_with_auto_cross_size_stretches_to_container_size, item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Contribution, IntrinsicSizingMode::Contribution,
) );
.into(), (size.into(), true)
},
}; };
let content_box_size = flex_axis.vec2_to_flex_relative(content_box_size); let content_box_size = flex_axis.vec2_to_flex_relative(content_box_size);
@ -2155,6 +2178,7 @@ impl FlexItemBox {
min_flex_factors, min_flex_factors,
max_flex_factors, max_flex_factors,
min_content_main_size_for_multiline_container, min_content_main_size_for_multiline_container,
depends_on_block_constraints,
} }
} }
@ -2282,6 +2306,7 @@ impl FlexItemBox {
&containing_block_for_children, &containing_block_for_children,
containing_block, containing_block,
) )
.sizes
.min_content .min_content
} else { } else {
block_content_size_callback(self) block_content_size_callback(self)
@ -2444,6 +2469,7 @@ impl FlexItemBox {
&containing_block_for_children, &containing_block_for_children,
containing_block, containing_block,
) )
.sizes
.max_content; .max_content;
if let Some(ratio) = ratio { if let Some(ratio) = ratio {
max_content.clamp_between_extremums( max_content.clamp_between_extremums(
@ -2522,11 +2548,12 @@ impl FlexItemBox {
let style = non_replaced.style.clone(); let style = non_replaced.style.clone();
let containing_block_for_children = let containing_block_for_children =
IndefiniteContainingBlock::new_for_style(&style); IndefiniteContainingBlock::new_for_style(&style);
let inline_content_sizes = non_replaced.inline_content_sizes( non_replaced
.inline_content_sizes(
flex_context.layout_context, flex_context.layout_context,
&containing_block_for_children, &containing_block_for_children,
); )
inline_content_sizes .sizes
.shrink_to_fit(containing_block_inline_size_minus_pbm) .shrink_to_fit(containing_block_inline_size_minus_pbm)
} }
}) })

View file

@ -126,7 +126,7 @@ use crate::fragment_tree::{
}; };
use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::ContentSizes; use crate::sizing::{ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -1565,7 +1565,7 @@ impl InlineFormattingContext {
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
ContentSizesComputation::compute(self, layout_context, containing_block_for_children) ContentSizesComputation::compute(self, layout_context, containing_block_for_children)
} }
@ -2184,16 +2184,23 @@ struct ContentSizesComputation<'layout_data> {
/// Stack of ending padding, margin, and border to add to the length /// Stack of ending padding, margin, and border to add to the length
/// when an inline box finishes. /// when an inline box finishes.
ending_inline_pbm_stack: Vec<Au>, ending_inline_pbm_stack: Vec<Au>,
depends_on_block_constraints: bool,
} }
impl<'layout_data> ContentSizesComputation<'layout_data> { impl<'layout_data> ContentSizesComputation<'layout_data> {
fn traverse(mut self, inline_formatting_context: &InlineFormattingContext) -> ContentSizes { fn traverse(
mut self,
inline_formatting_context: &InlineFormattingContext,
) -> InlineContentSizesResult {
for inline_item in inline_formatting_context.inline_items.iter() { for inline_item in inline_formatting_context.inline_items.iter() {
self.process_item(&mut inline_item.borrow_mut(), inline_formatting_context); self.process_item(&mut inline_item.borrow_mut(), inline_formatting_context);
} }
self.forced_line_break(); self.forced_line_break();
self.paragraph InlineContentSizesResult {
sizes: self.paragraph,
depends_on_block_constraints: self.depends_on_block_constraints,
}
} }
fn process_item( fn process_item(
@ -2299,12 +2306,16 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
self.line_break_opportunity(); self.line_break_opportunity();
} }
let outer = atomic.outer_inline_content_sizes( let InlineContentSizesResult {
sizes: outer,
depends_on_block_constraints,
} = atomic.outer_inline_content_sizes(
self.layout_context, self.layout_context,
self.containing_block, self.containing_block,
&LogicalVec2::zero(), &LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */ false, /* auto_block_size_stretches_to_containing_block */
); );
self.depends_on_block_constraints |= depends_on_block_constraints;
if !inline_formatting_context if !inline_formatting_context
.next_character_prevents_soft_wrap_opportunity(*offset_in_text) .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
@ -2356,7 +2367,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
inline_formatting_context: &InlineFormattingContext, inline_formatting_context: &InlineFormattingContext,
layout_context: &'layout_data LayoutContext, layout_context: &'layout_data LayoutContext,
containing_block: &'layout_data IndefiniteContainingBlock, containing_block: &'layout_data IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
Self { Self {
layout_context, layout_context,
containing_block, containing_block,
@ -2366,6 +2377,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
had_content_yet_for_min_content: false, had_content_yet_for_min_content: false,
had_content_yet_for_max_content: false, had_content_yet_for_max_content: false,
ending_inline_pbm_stack: Vec::new(), ending_inline_pbm_stack: Vec::new(),
depends_on_block_constraints: false,
} }
.traverse(inline_formatting_context) .traverse(inline_formatting_context)
} }

View file

@ -37,7 +37,7 @@ use crate::geom::{
}; };
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes}; use crate::sizing::{self, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -234,7 +234,7 @@ impl OutsideMarker {
&IndefiniteContainingBlock::new_for_style(&self.marker_style), &IndefiniteContainingBlock::new_for_style(&self.marker_style),
); );
let containing_block_for_children = ContainingBlock { let containing_block_for_children = ContainingBlock {
inline_size: content_sizes.max_content, inline_size: content_sizes.sizes.max_content,
block_size: AuOrAuto::auto(), block_size: AuOrAuto::auto(),
style: &self.marker_style, style: &self.marker_style,
}; };
@ -361,28 +361,29 @@ fn calculate_inline_content_size_for_block_level_boxes(
boxes: &[ArcRefCell<BlockLevelBox>], boxes: &[ArcRefCell<BlockLevelBox>],
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block: &IndefiniteContainingBlock, containing_block: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| { let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
match &mut *box_.borrow_mut() { match &mut *box_.borrow_mut() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) | BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
BlockLevelBox::OutsideMarker { .. } => None, BlockLevelBox::OutsideMarker { .. } => None,
BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => { BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => {
let size = float_box let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
.contents
.outer_inline_content_sizes(
layout_context, layout_context,
containing_block, containing_block,
&LogicalVec2::zero(), &LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */ false, /* auto_block_size_stretches_to_containing_block */
) );
.max(ContentSizes::zero());
let style_box = &float_box.contents.style().get_box(); let style_box = &float_box.contents.style().get_box();
Some((size, style_box.float, style_box.clear)) Some((
inline_content_sizes_result,
style_box.float,
style_box.clear,
))
}, },
BlockLevelBox::SameFormattingContextBlock { BlockLevelBox::SameFormattingContextBlock {
style, contents, .. style, contents, ..
} => { } => {
let size = sizing::outer_inline( let inline_content_sizes_result = sizing::outer_inline(
style, style,
containing_block, containing_block,
&LogicalVec2::zero(), &LogicalVec2::zero(),
@ -390,30 +391,34 @@ fn calculate_inline_content_size_for_block_level_boxes(
|containing_block_for_children| { |containing_block_for_children| {
contents.inline_content_sizes(layout_context, containing_block_for_children) contents.inline_content_sizes(layout_context, containing_block_for_children)
}, },
) );
.max(ContentSizes::zero());
// A block in the same BFC can overlap floats, it's not moved next to them, // A block in the same BFC can overlap floats, it's not moved next to them,
// so we shouldn't add its size to the size of the floats. // so we shouldn't add its size to the size of the floats.
// Instead, we treat it like an independent block with 'clear: both'. // Instead, we treat it like an independent block with 'clear: both'.
Some((size, Float::None, Clear::Both)) Some((inline_content_sizes_result, Float::None, Clear::Both))
}, },
BlockLevelBox::Independent(ref mut independent) => { BlockLevelBox::Independent(ref mut independent) => {
let size = independent let inline_content_sizes_result = independent.outer_inline_content_sizes(
.outer_inline_content_sizes(
layout_context, layout_context,
containing_block, containing_block,
&LogicalVec2::zero(), &LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */ false, /* auto_block_size_stretches_to_containing_block */
) );
.max(ContentSizes::zero()); Some((
Some((size, Float::None, independent.style().get_box().clear)) inline_content_sizes_result,
Float::None,
independent.style().get_box().clear,
))
}, },
} }
}; };
/// When iterating the block-level boxes to compute the inline content sizes, /// When iterating the block-level boxes to compute the inline content sizes,
/// this struct contains the data accumulated up to the current box. /// this struct contains the data accumulated up to the current box.
#[derive(Default)]
struct AccumulatedData { struct AccumulatedData {
/// Whether the inline size depends on the block one.
depends_on_block_constraints: bool,
/// The maximum size seen so far, not including trailing uncleared floats. /// The maximum size seen so far, not including trailing uncleared floats.
max_size: ContentSizes, max_size: ContentSizes,
/// The size of the trailing uncleared floats with 'float: left'. /// The size of the trailing uncleared floats with 'float: left'.
@ -447,7 +452,13 @@ fn calculate_inline_content_size_for_block_level_boxes(
} }
} }
let accumulate = |mut data: AccumulatedData, (size, float, clear)| { let accumulate =
|mut data: AccumulatedData,
(inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
let depends_on_block_constraints =
inline_content_sizes_result.depends_on_block_constraints;
data.depends_on_block_constraints |= depends_on_block_constraints;
data.clear_floats(clear); data.clear_floats(clear);
match float { match float {
Float::Left => data.left_floats = data.left_floats.union(&size), Float::Left => data.left_floats = data.left_floats.union(&size),
@ -462,22 +473,23 @@ fn calculate_inline_content_size_for_block_level_boxes(
} }
data data
}; };
let zero = AccumulatedData {
max_size: ContentSizes::zero(),
left_floats: ContentSizes::zero(),
right_floats: ContentSizes::zero(),
};
let data = if layout_context.use_rayon { let data = if layout_context.use_rayon {
boxes boxes
.par_iter() .par_iter()
.filter_map(get_box_info) .filter_map(get_box_info)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
.fold(zero, accumulate) .fold(AccumulatedData::default(), accumulate)
} else { } else {
boxes.iter().filter_map(get_box_info).fold(zero, accumulate) boxes
.iter()
.filter_map(get_box_info)
.fold(AccumulatedData::default(), accumulate)
}; };
data.max_size_including_uncleared_floats() InlineContentSizesResult {
depends_on_block_constraints: data.depends_on_block_constraints,
sizes: data.max_size_including_uncleared_floats(),
}
} }
impl BlockContainer { impl BlockContainer {
@ -512,7 +524,7 @@ impl BlockContainer {
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
match &self { match &self {
Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes( Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes(
boxes, boxes,
@ -1984,6 +1996,7 @@ impl IndependentFormattingContext {
); );
non_replaced non_replaced
.inline_content_sizes(layout_context, &containing_block_for_children) .inline_content_sizes(layout_context, &containing_block_for_children)
.sizes
}; };
// https://drafts.csswg.org/css2/visudet.html#float-width // https://drafts.csswg.org/css2/visudet.html#float-width

View file

@ -18,7 +18,7 @@ use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, FragmentFlag
use crate::geom::LogicalSides; use crate::geom::LogicalSides;
use crate::positioned::PositioningContext; use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes}; use crate::sizing::{self, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, DisplayInside}; use crate::style_ext::{AspectRatio, DisplayInside};
use crate::table::Table; use crate::table::Table;
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2}; use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2};
@ -36,7 +36,7 @@ pub(crate) struct NonReplacedFormattingContext {
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
/// If it was requested during construction /// If it was requested during construction
pub content_sizes: Option<(AuOrAuto, ContentSizes)>, pub content_sizes_result: Option<(AuOrAuto, InlineContentSizesResult)>,
pub contents: NonReplacedFormattingContextContents, pub contents: NonReplacedFormattingContextContents,
} }
@ -152,7 +152,7 @@ impl IndependentFormattingContext {
Self::NonReplaced(NonReplacedFormattingContext { Self::NonReplaced(NonReplacedFormattingContext {
style: Arc::clone(&node_and_style_info.style), style: Arc::clone(&node_and_style_info.style),
base_fragment_info, base_fragment_info,
content_sizes: None, content_sizes_result: None,
contents, contents,
}) })
}, },
@ -187,7 +187,7 @@ impl IndependentFormattingContext {
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
containing_block: &IndefiniteContainingBlock, containing_block: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
match self { match self {
Self::NonReplaced(inner) => { Self::NonReplaced(inner) => {
inner.inline_content_sizes(layout_context, containing_block_for_children) inner.inline_content_sizes(layout_context, containing_block_for_children)
@ -206,7 +206,7 @@ impl IndependentFormattingContext {
containing_block: &IndefiniteContainingBlock, containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>, auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool, auto_block_size_stretches_to_containing_block: bool,
) -> ContentSizes { ) -> InlineContentSizesResult {
match self { match self {
Self::NonReplaced(non_replaced) => non_replaced.outer_inline_content_sizes( Self::NonReplaced(non_replaced) => non_replaced.outer_inline_content_sizes(
layout_context, layout_context,
@ -274,20 +274,22 @@ impl NonReplacedFormattingContext {
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
assert_eq!( assert_eq!(
containing_block_for_children.size.inline, containing_block_for_children.size.inline,
AuOrAuto::Auto, AuOrAuto::Auto,
"inline_content_sizes() got non-auto containing block inline-size", "inline_content_sizes() got non-auto containing block inline-size",
); );
if let Some((previous_cb_block_size, result)) = self.content_sizes { if let Some((previous_cb_block_size, result)) = self.content_sizes_result {
if previous_cb_block_size == containing_block_for_children.size.block { if !result.depends_on_block_constraints ||
previous_cb_block_size == containing_block_for_children.size.block
{
return result; return result;
} }
// TODO: Should we keep multiple caches for various block sizes? // TODO: Should we keep multiple caches for various block sizes?
} }
self.content_sizes self.content_sizes_result
.insert(( .insert((
containing_block_for_children.size.block, containing_block_for_children.size.block,
self.contents self.contents
@ -302,7 +304,7 @@ impl NonReplacedFormattingContext {
containing_block: &IndefiniteContainingBlock, containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>, auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool, auto_block_size_stretches_to_containing_block: bool,
) -> ContentSizes { ) -> InlineContentSizesResult {
sizing::outer_inline( sizing::outer_inline(
&self.style.clone(), &self.style.clone(),
containing_block, containing_block,
@ -320,7 +322,7 @@ impl NonReplacedFormattingContextContents {
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
match self { match self {
Self::Flow(inner) => inner Self::Flow(inner) => inner
.contents .contents

View file

@ -65,7 +65,7 @@ impl<'a> IndefiniteContainingBlock<'a> {
style: &'a ComputedValues, style: &'a ComputedValues,
auto_minimum: &LogicalVec2<Au>, auto_minimum: &LogicalVec2<Au>,
) -> Self { ) -> Self {
let (content_box_size, content_min_size, content_max_size, _) = let (content_box_size, content_min_size, content_max_size, _, _) =
style.content_box_sizes_and_padding_border_margin_deprecated(self); style.content_box_sizes_and_padding_border_margin_deprecated(self);
let block_size = content_box_size.block.map(|v| { let block_size = content_box_size.block.map(|v| {
v.clamp_between_extremums( v.clamp_between_extremums(

View file

@ -587,6 +587,7 @@ impl HoistedAbsolutelyPositionedBox {
); );
non_replaced non_replaced
.inline_content_sizes(layout_context, &containing_block_for_children) .inline_content_sizes(layout_context, &containing_block_for_children)
.sizes
.shrink_to_fit(available_size) .shrink_to_fit(available_size)
}); });

View file

@ -29,7 +29,7 @@ use crate::context::LayoutContext;
use crate::dom::NodeExt; use crate::dom::NodeExt;
use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment}; use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment};
use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::sizing::ContentSizes; use crate::sizing::InlineContentSizesResult;
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock}; use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock};
@ -263,21 +263,27 @@ impl ReplacedContent {
_: &LayoutContext, _: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
preferred_aspect_ratio: Option<AspectRatio>, preferred_aspect_ratio: Option<AspectRatio>,
) -> ContentSizes { ) -> InlineContentSizesResult {
// FIXME: min/max-content of replaced elements is not defined in // FIXME: min/max-content of replaced elements is not defined in
// https://dbaron.org/css/intrinsic/ // https://dbaron.org/css/intrinsic/
// This seems sensible? // This seems sensible?
let block_size = containing_block_for_children.size.block; let block_size = containing_block_for_children.size.block;
let inline_size = match (block_size, preferred_aspect_ratio) { match (block_size, preferred_aspect_ratio) {
(AuOrAuto::LengthPercentage(block_size), Some(ratio)) => { (AuOrAuto::LengthPercentage(block_size), Some(ratio)) => InlineContentSizesResult {
ratio.compute_dependent_size(Direction::Inline, block_size) sizes: ratio
.compute_dependent_size(Direction::Inline, block_size)
.into(),
depends_on_block_constraints: true,
}, },
_ => self _ => InlineContentSizesResult {
sizes: self
.flow_relative_intrinsic_size(containing_block_for_children.style) .flow_relative_intrinsic_size(containing_block_for_children.style)
.inline .inline
.unwrap_or_else(Au::zero), .unwrap_or_else(Au::zero)
}; .into(),
inline_size.into() depends_on_block_constraints: false,
},
}
} }
pub fn make_fragments( pub fn make_fragments(

View file

@ -118,10 +118,15 @@ pub(crate) fn outer_inline(
containing_block: &IndefiniteContainingBlock, containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>, auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool, auto_block_size_stretches_to_containing_block: bool,
get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes, get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> InlineContentSizesResult,
) -> ContentSizes { ) -> InlineContentSizesResult {
let (content_box_size, content_min_size, content_max_size, pbm) = let (
style.content_box_sizes_and_padding_border_margin_deprecated(containing_block); content_box_size,
content_min_size,
content_max_size,
pbm,
mut depends_on_block_constraints,
) = style.content_box_sizes_and_padding_border_margin_deprecated(containing_block);
let content_min_size = LogicalVec2 { let content_min_size = LogicalVec2 {
inline: content_min_size.inline.auto_is(|| auto_minimum.inline), inline: content_min_size.inline.auto_is(|| auto_minimum.inline),
block: content_min_size.block.auto_is(|| auto_minimum.block), block: content_min_size.block.auto_is(|| auto_minimum.block),
@ -132,11 +137,15 @@ pub(crate) fn outer_inline(
v.clamp_between_extremums(content_min_size.inline, content_max_size.inline) + pbm_inline_sum v.clamp_between_extremums(content_min_size.inline, content_max_size.inline) + pbm_inline_sum
}; };
match content_box_size.inline { match content_box_size.inline {
AuOrAuto::LengthPercentage(inline_size) => adjust(inline_size).into(), AuOrAuto::LengthPercentage(inline_size) => InlineContentSizesResult {
sizes: adjust(inline_size).into(),
depends_on_block_constraints: false,
},
AuOrAuto::Auto => { AuOrAuto::Auto => {
let block_size = if content_box_size.block.is_auto() && let block_size = if content_box_size.block.is_auto() &&
auto_block_size_stretches_to_containing_block auto_block_size_stretches_to_containing_block
{ {
depends_on_block_constraints = true;
let outer_block_size = containing_block.size.block; let outer_block_size = containing_block.size.block;
outer_block_size.map(|v| v - pbm.padding_border_sums.block - margin.block_sum()) outer_block_size.map(|v| v - pbm.padding_border_sums.block - margin.block_sum())
} else { } else {
@ -145,7 +154,18 @@ pub(crate) fn outer_inline(
.map(|v| v.clamp_between_extremums(content_min_size.block, content_max_size.block)); .map(|v| v.clamp_between_extremums(content_min_size.block, content_max_size.block));
let containing_block_for_children = let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(style, block_size); IndefiniteContainingBlock::new_for_style_and_block_size(style, block_size);
get_content_size(&containing_block_for_children).map(adjust) let content_result = get_content_size(&containing_block_for_children);
InlineContentSizesResult {
sizes: content_result.sizes.map(adjust),
depends_on_block_constraints: content_result.depends_on_block_constraints &&
depends_on_block_constraints,
}
}, },
} }
} }
#[derive(Clone, Copy, Debug, Serialize)]
pub(crate) struct InlineContentSizesResult {
pub sizes: ContentSizes,
pub depends_on_block_constraints: bool,
}

View file

@ -29,7 +29,7 @@ use crate::dom_traversal::Contents;
use crate::fragment_tree::FragmentFlags; use crate::fragment_tree::FragmentFlags;
use crate::geom::{ use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize, AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize,
PhysicalVec, Size, PhysicalVec, Size, SizeKeyword,
}; };
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -249,6 +249,7 @@ pub(crate) trait ComputedValuesExt {
LogicalVec2<Size<Au>>, LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>, LogicalVec2<Size<Au>>,
PaddingBorderMargin, PaddingBorderMargin,
bool, /* depends_on_block_constraints */
); );
fn content_box_sizes_and_padding_border_margin_deprecated( fn content_box_sizes_and_padding_border_margin_deprecated(
&self, &self,
@ -258,6 +259,7 @@ pub(crate) trait ComputedValuesExt {
LogicalVec2<AuOrAuto>, LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>, LogicalVec2<Option<Au>>,
PaddingBorderMargin, PaddingBorderMargin,
bool, /* depends_on_block_constraints */
); );
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin; fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
fn padding_border_margin_for_intrinsic_size( fn padding_border_margin_for_intrinsic_size(
@ -499,6 +501,7 @@ impl ComputedValuesExt for ComputedValues {
LogicalVec2<Size<Au>>, LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>, LogicalVec2<Size<Au>>,
PaddingBorderMargin, PaddingBorderMargin,
bool, /* depends_on_block_constraints */
) { ) {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution> // <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
// If max size properties or preferred size properties are set to a value containing // If max size properties or preferred size properties are set to a value containing
@ -513,25 +516,45 @@ impl ComputedValuesExt for ComputedValues {
writing_mode, writing_mode,
containing_block.size.inline.auto_is(Au::zero), containing_block.size.inline.auto_is(Au::zero),
); );
let box_size = self let box_size = self.box_size(writing_mode);
.box_size(writing_mode) let min_size = self.min_box_size(writing_mode);
.maybe_percentages_relative_to_basis(&containing_block_size); let max_size = self.max_box_size(writing_mode);
let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
match size {
// fit-content is like clamp(min-content, stretch, max-content), but currently
// min-content and max-content have the same behavior in the block axis,
// so there is no dependency on block constraints.
// TODO: for flex and grid layout, min-content and max-content should be different.
// TODO: We are assuming that Size::Initial doesn't stretch. However, it may actually
// stretch flex and grid items depending on the CSS Align properties, in that case
// the caller needs to take care of it.
Size::Keyword(SizeKeyword::Stretch) => true,
Size::Numeric(length_percentage) => length_percentage.has_percentage(),
_ => false,
}
};
let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
depends_on_block_constraints(&min_size.block) ||
depends_on_block_constraints(&max_size.block);
let box_size = box_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_box_size = self let content_box_size = self
.content_box_size_for_box_size(box_size, &pbm) .content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from)); .map(|v| v.map(Au::from));
let min_size = self let min_size = min_size.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
.min_box_size(writing_mode)
.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
let content_min_size = self let content_min_size = self
.content_min_box_size_for_min_size(min_size, &pbm) .content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from)); .map(|v| v.map(Au::from));
let max_size = self let max_size = max_size.maybe_percentages_relative_to_basis(&containing_block_size);
.max_box_size(writing_mode)
.maybe_percentages_relative_to_basis(&containing_block_size);
let content_max_size = self let content_max_size = self
.content_max_box_size_for_max_size(max_size, &pbm) .content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from)); .map(|v| v.map(Au::from));
(content_box_size, content_min_size, content_max_size, pbm) (
content_box_size,
content_min_size,
content_max_size,
pbm,
depends_on_block_constraints,
)
} }
fn content_box_sizes_and_padding_border_margin_deprecated( fn content_box_sizes_and_padding_border_margin_deprecated(
@ -542,14 +565,21 @@ impl ComputedValuesExt for ComputedValues {
LogicalVec2<AuOrAuto>, LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>, LogicalVec2<Option<Au>>,
PaddingBorderMargin, PaddingBorderMargin,
bool, /* depends_on_block_constraints */
) { ) {
let (content_box_size, content_min_size, content_max_size, pbm) = let (
self.content_box_sizes_and_padding_border_margin(containing_block); content_box_size,
content_min_size,
content_max_size,
pbm,
depends_on_block_constraints,
) = self.content_box_sizes_and_padding_border_margin(containing_block);
( (
content_box_size.map(Size::to_auto_or), content_box_size.map(Size::to_auto_or),
content_min_size.map(Size::to_auto_or), content_min_size.map(Size::to_auto_or),
content_max_size.map(Size::to_numeric), content_max_size.map(Size::to_numeric),
pbm, pbm,
depends_on_block_constraints,
) )
} }

View file

@ -137,7 +137,7 @@ impl Table {
IndependentFormattingContext::NonReplaced(NonReplacedFormattingContext { IndependentFormattingContext::NonReplaced(NonReplacedFormattingContext {
base_fragment_info: (&anonymous_info).into(), base_fragment_info: (&anonymous_info).into(),
style: grid_and_wrapper_style, style: grid_and_wrapper_style,
content_sizes: None, content_sizes_result: None,
contents: NonReplacedFormattingContextContents::Table(table), contents: NonReplacedFormattingContextContents::Table(table),
}) })
} }
@ -858,7 +858,7 @@ where
context: ArcRefCell::new(NonReplacedFormattingContext { context: ArcRefCell::new(NonReplacedFormattingContext {
style: info.style.clone(), style: info.style.clone(),
base_fragment_info: info.into(), base_fragment_info: info.into(),
content_sizes: None, content_sizes_result: None,
contents, contents,
}), }),
}; };

View file

@ -38,7 +38,7 @@ use crate::geom::{
Size, SizeKeyword, ToLogical, ToLogicalWithContainingBlock, Size, SizeKeyword, ToLogical, ToLogicalWithContainingBlock,
}; };
use crate::positioned::{relative_adjustement, PositioningContext, PositioningContextLength}; use crate::positioned::{relative_adjustement, PositioningContext, PositioningContextLength};
use crate::sizing::ContentSizes; use crate::sizing::{ContentSizes, InlineContentSizesResult};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::table::TableSlotCoordinates; use crate::table::TableSlotCoordinates;
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -298,10 +298,13 @@ impl<'a> TableLayout<'a> {
let mut inline_content_sizes = if is_in_fixed_mode { let mut inline_content_sizes = if is_in_fixed_mode {
ContentSizes::zero() ContentSizes::zero()
} else { } else {
cell.contents.contents.inline_content_sizes( cell.contents
.contents
.inline_content_sizes(
layout_context, layout_context,
&IndefiniteContainingBlock::new_for_style(&cell.style), &IndefiniteContainingBlock::new_for_style(&cell.style),
) )
.sizes
}; };
inline_content_sizes.min_content += padding_border_sums.inline; inline_content_sizes.min_content += padding_border_sums.inline;
inline_content_sizes.max_content += padding_border_sums.inline; inline_content_sizes.max_content += padding_border_sums.inline;
@ -784,6 +787,7 @@ impl<'a> TableLayout<'a> {
&LogicalVec2::zero(), &LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */ false, /* auto_block_size_stretches_to_containing_block */
) )
.sizes
.min_content .min_content
}) })
.max() .max()
@ -2611,7 +2615,7 @@ impl Table {
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> InlineContentSizesResult {
let writing_mode = containing_block_for_children.style.writing_mode; let writing_mode = containing_block_for_children.style.writing_mode;
let mut layout = TableLayout::new(self); let mut layout = TableLayout::new(self);
let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode); let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode);
@ -2638,7 +2642,10 @@ impl Table {
.max_assign(caption_minimum_inline_size); .max_assign(caption_minimum_inline_size);
} }
table_content_sizes InlineContentSizesResult {
sizes: table_content_sizes,
depends_on_block_constraints: false,
}
} }
fn get_column_measure_for_column_at_index( fn get_column_measure_for_column_at_index(