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

@ -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