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 {
base_fragment_info: info.into(),
style: info.style.clone(),
content_sizes: None,
content_sizes_result: None,
contents: NonReplacedFormattingContextContents::Flow(
block_formatting_context,
),

View file

@ -31,7 +31,7 @@ use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
use crate::positioned::{
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::{ContainingBlock, IndefiniteContainingBlock};
@ -336,6 +336,7 @@ struct FlexItemBoxInlineContentSizesInfo {
min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
min_content_main_size_for_multiline_container: Au,
depends_on_block_constraints: bool,
}
impl FlexContainer {
@ -348,7 +349,7 @@ impl FlexContainer {
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
match self.config.flex_axis {
FlexAxis::Row => {
self.main_content_sizes(layout_context, containing_block_for_children, || {
@ -367,14 +368,15 @@ impl FlexContainer {
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
// <https://drafts.csswg.org/css-flexbox/#intrinsic-cross-sizes>
assert_eq!(
self.config.flex_axis,
FlexAxis::Column,
"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() {
let kid = &mut *kid.borrow_mut();
match kid {
@ -383,17 +385,22 @@ impl FlexContainer {
// columns, and sum the column sizes and gaps.
// TODO: Use the proper automatic minimum size.
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,
containing_block_for_children,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
));
);
sizes.max_assign(result.sizes);
depends_on_block_constraints |= result.depends_on_block_constraints;
},
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
}
}
content_sizes
InlineContentSizesResult {
sizes,
depends_on_block_constraints,
}
}
fn main_content_sizes<'a>(
@ -401,7 +408,7 @@ impl FlexContainer {
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
) -> ContentSizes {
) -> InlineContentSizesResult {
// - TODO: calculate intrinsic cross sizes when container is a column
// (and check for writing-mode?)
// - TODO: Collapsed flex items need to be skipped for intrinsic size calculation.
@ -483,6 +490,7 @@ impl FlexContainer {
} else {
Au::zero()
};
let mut container_depends_on_block_constraints = false;
for FlexItemBoxInlineContentSizesInfo {
outer_flex_base_size,
@ -492,6 +500,7 @@ impl FlexContainer {
min_flex_factors,
max_flex_factors,
min_content_main_size_for_multiline_container,
depends_on_block_constraints,
} in item_infos.iter()
{
// > 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
.max_assign(*min_content_main_size_for_multiline_container);
}
container_depends_on_block_constraints |= depends_on_block_constraints;
}
ContentSizes {
min_content: container_min_content_size,
max_content: container_max_content_size,
InlineContentSizesResult {
sizes: ContentSizes {
min_content: container_min_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::Column => containing_block.block_size.auto_is(|| {
self.main_content_sizes(layout_context, &containing_block.into(), || &flex_context)
.sizes
.max_content
}),
};
@ -1023,7 +1038,7 @@ impl<'a> FlexItem<'a> {
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()
.content_box_sizes_and_padding_border_margin_deprecated(&containing_block.into());
@ -1735,6 +1750,7 @@ impl FlexItem<'_> {
&containing_block_for_children,
&containing_block.into(),
)
.sizes
.map(|size| {
size.clamp_between_extremums(
self.content_min_size.cross,
@ -2008,7 +2024,7 @@ impl FlexItemBox {
let cross_axis_is_item_block_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);
let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding);
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.
// <https://drafts.csswg.org/css-flexbox/#intrinsic-item-contributions>
let content_contribution_sizes = match flex_axis {
FlexAxis::Row => self
.independent_formatting_context
.outer_inline_content_sizes(
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(
let (content_contribution_sizes, depends_on_block_constraints) = match flex_axis {
FlexAxis::Row => {
let InlineContentSizesResult {
sizes,
depends_on_block_constraints,
} = self
.independent_formatting_context
.outer_inline_content_sizes(
layout_context,
containing_block,
&content_min_size_no_auto,
item_with_auto_cross_size_stretches_to_container_size,
);
(sizes, depends_on_block_constraints)
},
FlexAxis::Column => {
let size = self.layout_for_block_content_size(
flex_context_getter(),
&pbm,
content_box_size,
@ -2087,8 +2109,9 @@ impl FlexItemBox {
content_max_size,
item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Contribution,
)
.into(),
);
(size.into(), true)
},
};
let content_box_size = flex_axis.vec2_to_flex_relative(content_box_size);
@ -2155,6 +2178,7 @@ impl FlexItemBox {
min_flex_factors,
max_flex_factors,
min_content_main_size_for_multiline_container,
depends_on_block_constraints,
}
}
@ -2282,6 +2306,7 @@ impl FlexItemBox {
&containing_block_for_children,
containing_block,
)
.sizes
.min_content
} else {
block_content_size_callback(self)
@ -2444,6 +2469,7 @@ impl FlexItemBox {
&containing_block_for_children,
containing_block,
)
.sizes
.max_content;
if let Some(ratio) = ratio {
max_content.clamp_between_extremums(
@ -2522,11 +2548,12 @@ impl FlexItemBox {
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,
);
inline_content_sizes
non_replaced
.inline_content_sizes(
flex_context.layout_context,
&containing_block_for_children,
)
.sizes
.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::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::ContentSizes;
use crate::sizing::{ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -1565,7 +1565,7 @@ impl InlineFormattingContext {
&self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
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
/// when an inline box finishes.
ending_inline_pbm_stack: Vec<Au>,
depends_on_block_constraints: bool,
}
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() {
self.process_item(&mut inline_item.borrow_mut(), inline_formatting_context);
}
self.forced_line_break();
self.paragraph
InlineContentSizesResult {
sizes: self.paragraph,
depends_on_block_constraints: self.depends_on_block_constraints,
}
}
fn process_item(
@ -2299,12 +2306,16 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
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.containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
);
self.depends_on_block_constraints |= depends_on_block_constraints;
if !inline_formatting_context
.next_character_prevents_soft_wrap_opportunity(*offset_in_text)
@ -2356,7 +2367,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
inline_formatting_context: &InlineFormattingContext,
layout_context: &'layout_data LayoutContext,
containing_block: &'layout_data IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
Self {
layout_context,
containing_block,
@ -2366,6 +2377,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
had_content_yet_for_min_content: false,
had_content_yet_for_max_content: false,
ending_inline_pbm_stack: Vec::new(),
depends_on_block_constraints: false,
}
.traverse(inline_formatting_context)
}

View file

@ -37,7 +37,7 @@ use crate::geom::{
};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes};
use crate::sizing::{self, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -234,7 +234,7 @@ impl OutsideMarker {
&IndefiniteContainingBlock::new_for_style(&self.marker_style),
);
let containing_block_for_children = ContainingBlock {
inline_size: content_sizes.max_content,
inline_size: content_sizes.sizes.max_content,
block_size: AuOrAuto::auto(),
style: &self.marker_style,
};
@ -361,28 +361,29 @@ fn calculate_inline_content_size_for_block_level_boxes(
boxes: &[ArcRefCell<BlockLevelBox>],
layout_context: &LayoutContext,
containing_block: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
match &mut *box_.borrow_mut() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
BlockLevelBox::OutsideMarker { .. } => None,
BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => {
let size = float_box
.contents
.outer_inline_content_sizes(
layout_context,
containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
)
.max(ContentSizes::zero());
let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
layout_context,
containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
);
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 {
style, contents, ..
} => {
let size = sizing::outer_inline(
let inline_content_sizes_result = sizing::outer_inline(
style,
containing_block,
&LogicalVec2::zero(),
@ -390,30 +391,34 @@ fn calculate_inline_content_size_for_block_level_boxes(
|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,
// so we shouldn't add its size to the size of the floats.
// 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) => {
let size = independent
.outer_inline_content_sizes(
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))
let inline_content_sizes_result = independent.outer_inline_content_sizes(
layout_context,
containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
);
Some((
inline_content_sizes_result,
Float::None,
independent.style().get_box().clear,
))
},
}
};
/// When iterating the block-level boxes to compute the inline content sizes,
/// this struct contains the data accumulated up to the current box.
#[derive(Default)]
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.
max_size: ContentSizes,
/// The size of the trailing uncleared floats with 'float: left'.
@ -447,37 +452,44 @@ fn calculate_inline_content_size_for_block_level_boxes(
}
}
let accumulate = |mut data: AccumulatedData, (size, float, clear)| {
data.clear_floats(clear);
match float {
Float::Left => data.left_floats = data.left_floats.union(&size),
Float::Right => data.right_floats = data.right_floats.union(&size),
Float::None => {
data.max_size = data
.max_size
.max(data.left_floats.union(&data.right_floats).union(&size));
data.left_floats = ContentSizes::zero();
data.right_floats = ContentSizes::zero();
},
}
data
};
let zero = AccumulatedData {
max_size: ContentSizes::zero(),
left_floats: ContentSizes::zero(),
right_floats: ContentSizes::zero(),
};
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);
match float {
Float::Left => data.left_floats = data.left_floats.union(&size),
Float::Right => data.right_floats = data.right_floats.union(&size),
Float::None => {
data.max_size = data
.max_size
.max(data.left_floats.union(&data.right_floats).union(&size));
data.left_floats = ContentSizes::zero();
data.right_floats = ContentSizes::zero();
},
}
data
};
let data = if layout_context.use_rayon {
boxes
.par_iter()
.filter_map(get_box_info)
.collect::<Vec<_>>()
.into_iter()
.fold(zero, accumulate)
.fold(AccumulatedData::default(), accumulate)
} 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 {
@ -512,7 +524,7 @@ impl BlockContainer {
&self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
match &self {
Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes(
boxes,
@ -1984,6 +1996,7 @@ impl IndependentFormattingContext {
);
non_replaced
.inline_content_sizes(layout_context, &containing_block_for_children)
.sizes
};
// 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::positioned::PositioningContext;
use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes};
use crate::sizing::{self, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, DisplayInside};
use crate::table::Table;
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2};
@ -36,7 +36,7 @@ pub(crate) struct NonReplacedFormattingContext {
#[serde(skip_serializing)]
pub style: Arc<ComputedValues>,
/// If it was requested during construction
pub content_sizes: Option<(AuOrAuto, ContentSizes)>,
pub content_sizes_result: Option<(AuOrAuto, InlineContentSizesResult)>,
pub contents: NonReplacedFormattingContextContents,
}
@ -152,7 +152,7 @@ impl IndependentFormattingContext {
Self::NonReplaced(NonReplacedFormattingContext {
style: Arc::clone(&node_and_style_info.style),
base_fragment_info,
content_sizes: None,
content_sizes_result: None,
contents,
})
},
@ -187,7 +187,7 @@ impl IndependentFormattingContext {
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
containing_block: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
match self {
Self::NonReplaced(inner) => {
inner.inline_content_sizes(layout_context, containing_block_for_children)
@ -206,7 +206,7 @@ impl IndependentFormattingContext {
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
) -> ContentSizes {
) -> InlineContentSizesResult {
match self {
Self::NonReplaced(non_replaced) => non_replaced.outer_inline_content_sizes(
layout_context,
@ -274,20 +274,22 @@ impl NonReplacedFormattingContext {
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
assert_eq!(
containing_block_for_children.size.inline,
AuOrAuto::Auto,
"inline_content_sizes() got non-auto containing block inline-size",
);
if let Some((previous_cb_block_size, result)) = self.content_sizes {
if previous_cb_block_size == containing_block_for_children.size.block {
if let Some((previous_cb_block_size, result)) = self.content_sizes_result {
if !result.depends_on_block_constraints ||
previous_cb_block_size == containing_block_for_children.size.block
{
return result;
}
// TODO: Should we keep multiple caches for various block sizes?
}
self.content_sizes
self.content_sizes_result
.insert((
containing_block_for_children.size.block,
self.contents
@ -302,7 +304,7 @@ impl NonReplacedFormattingContext {
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
) -> ContentSizes {
) -> InlineContentSizesResult {
sizing::outer_inline(
&self.style.clone(),
containing_block,
@ -320,7 +322,7 @@ impl NonReplacedFormattingContextContents {
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
) -> InlineContentSizesResult {
match self {
Self::Flow(inner) => inner
.contents

View file

@ -65,7 +65,7 @@ impl<'a> IndefiniteContainingBlock<'a> {
style: &'a ComputedValues,
auto_minimum: &LogicalVec2<Au>,
) -> 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);
let block_size = content_box_size.block.map(|v| {
v.clamp_between_extremums(

View file

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

View file

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

View file

@ -118,10 +118,15 @@ pub(crate) fn outer_inline(
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes,
) -> ContentSizes {
let (content_box_size, content_min_size, content_max_size, pbm) =
style.content_box_sizes_and_padding_border_margin_deprecated(containing_block);
get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> InlineContentSizesResult,
) -> InlineContentSizesResult {
let (
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 {
inline: content_min_size.inline.auto_is(|| auto_minimum.inline),
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
};
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 => {
let block_size = if content_box_size.block.is_auto() &&
auto_block_size_stretches_to_containing_block
{
depends_on_block_constraints = true;
let outer_block_size = containing_block.size.block;
outer_block_size.map(|v| v - pbm.padding_border_sums.block - margin.block_sum())
} else {
@ -145,7 +154,18 @@ pub(crate) fn outer_inline(
.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)
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::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize,
PhysicalVec, Size,
PhysicalVec, Size, SizeKeyword,
};
use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -249,6 +249,7 @@ pub(crate) trait ComputedValuesExt {
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
);
fn content_box_sizes_and_padding_border_margin_deprecated(
&self,
@ -258,6 +259,7 @@ pub(crate) trait ComputedValuesExt {
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
);
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
fn padding_border_margin_for_intrinsic_size(
@ -499,6 +501,7 @@ impl ComputedValuesExt for ComputedValues {
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
) {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
// If max size properties or preferred size properties are set to a value containing
@ -513,25 +516,45 @@ impl ComputedValuesExt for ComputedValues {
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 box_size = self.box_size(writing_mode);
let min_size = self.min_box_size(writing_mode);
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
.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 min_size = min_size.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 max_size = max_size.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)
(
content_box_size,
content_min_size,
content_max_size,
pbm,
depends_on_block_constraints,
)
}
fn content_box_sizes_and_padding_border_margin_deprecated(
@ -542,14 +565,21 @@ impl ComputedValuesExt for ComputedValues {
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
) {
let (content_box_size, content_min_size, content_max_size, pbm) =
self.content_box_sizes_and_padding_border_margin(containing_block);
let (
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_min_size.map(Size::to_auto_or),
content_max_size.map(Size::to_numeric),
pbm,
depends_on_block_constraints,
)
}

View file

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

View file

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