layout: Add a new FragmentTree pass to calculate containing block rectangles (#36629)

When doing any kind of query, up until now, containing block rectangles
were calculated by walking the `FragmentTree` until the node being
queried was found. In order to make possible answering queries without
walking the `FragmentTree`, `Fragment`s need to cache their cumulative
containing block rectangles.

This change adds a new `FragmentTree` pass (during construction) that
takes care of calculating and caching these values. The new cached value
is used during resolved style queries and also scrolling area queries
(with the idea that all queries will eventually use them).

In addition, extra `FragmentTree` walks used for cancelling animations
for elements no longer in the `FragmentTree` are integrated into this
new traversal.

Testing: Covered by existing WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2025-04-23 11:14:15 +02:00 committed by GitHub
parent 554fa26da2
commit e9e103b46c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 206 additions and 207 deletions

View file

@ -65,6 +65,10 @@ pub(crate) struct BoxFragment {
/// does not include padding, border, or margin -- it only includes content.
pub content_rect: PhysicalRect<Au>,
/// This [`BoxFragment`]'s containing block rectangle in coordinates relative to
/// the initial containing block, but not taking into account any transforms.
pub cumulative_containing_block_rect: PhysicalRect<Au>,
pub padding: PhysicalSides<Au>,
pub border: PhysicalSides<Au>,
pub margin: PhysicalSides<Au>,
@ -120,6 +124,7 @@ impl BoxFragment {
style,
children,
content_rect,
cumulative_containing_block_rect: Default::default(),
padding,
border,
margin,
@ -195,6 +200,8 @@ impl BoxFragment {
self
}
/// Get the scrollable overflow for this [`BoxFragment`] relative to its
/// containing block.
pub fn scrollable_overflow(&self) -> PhysicalRect<Au> {
let physical_padding_rect = self.padding_rect();
let content_origin = self.content_rect.origin.to_vector();
@ -205,6 +212,10 @@ impl BoxFragment {
)
}
pub fn offset_by_containing_block(&self, rect: &PhysicalRect<Au>) -> PhysicalRect<Au> {
rect.translate(self.cumulative_containing_block_rect.origin.to_vector())
}
pub(crate) fn padding_rect(&self) -> PhysicalRect<Au> {
self.content_rect.outer_rect(self.padding)
}
@ -278,10 +289,7 @@ impl BoxFragment {
overflow
}
pub(crate) fn calculate_resolved_insets_if_positioned(
&self,
containing_block: &PhysicalRect<Au>,
) -> PhysicalSides<AuOrAuto> {
pub(crate) fn calculate_resolved_insets_if_positioned(&self) -> PhysicalSides<AuOrAuto> {
let position = self.style.get_box().position;
debug_assert_ne!(
position,
@ -309,7 +317,10 @@ impl BoxFragment {
// used value. Otherwise the resolved value is the computed value."
// https://drafts.csswg.org/cssom/#resolved-values
let insets = self.style.physical_box_offsets();
let (cb_width, cb_height) = (containing_block.width(), containing_block.height());
let (cb_width, cb_height) = (
self.cumulative_containing_block_rect.width(),
self.cumulative_containing_block_rect.height(),
);
if position == ComputedPosition::Relative {
let get_resolved_axis = |start: &LengthPercentageOrAuto,
end: &LengthPercentageOrAuto,
@ -394,4 +405,8 @@ impl BoxFragment {
_ => CollapsedBlockMargins::zero(),
}
}
pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect<Au>) {
self.cumulative_containing_block_rect = *containing_block;
}
}