servo/components/layout/fragment_tree/positioning_fragment.rs
Martin Robinson 8808f9a468
layout: Add a repaint-only incremental layout mode (#36978)
This change adds the simplest kind of incremental layout. When Servo
detects that all style changes only require a repaint, only run stacking
context tree and WebRender display list generation. This means that
these kind of restyles do not need a re-layout. Instead, the existing
box and fragment trees will be used and the styles of damaged nodes will
be updated in their box and fragment tree nodes.

This requires a new style repair DOM traversal for nodes that have had
their style damaged. In addition, careful accounting of all the places
where we store style must happen in order ot update those styles.

Testing: This is covered by existing WPT tests as it should not change
observable behavior.

We have created a test case which shows a 50% speedup when run
in Servo, even though there still a long way to go to match the speed
of other browsers:
https://gist.github.com/mrobinson/44ec87d028c0198917a7715a06dd98a0

Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-05-12 17:03:50 +00:00

98 lines
3.3 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use app_units::Au;
use base::print_tree::PrintTree;
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc as ServoArc;
use style::properties::ComputedValues;
use super::{BaseFragment, BaseFragmentInfo, Fragment};
use crate::cell::ArcRefCell;
use crate::geom::PhysicalRect;
/// Can contain child fragments with relative coordinates, but does not contribute to painting
/// itself. [`PositioningFragment`]s may be completely anonymous, or just non-painting Fragments
/// generated by boxes.
#[derive(MallocSizeOf)]
pub(crate) struct PositioningFragment {
pub base: BaseFragment,
pub rect: PhysicalRect<Au>,
pub children: Vec<Fragment>,
/// The scrollable overflow of this anonymous fragment's children.
pub scrollable_overflow: PhysicalRect<Au>,
/// The style of the fragment.
pub style: ServoArc<ComputedValues>,
/// This [`PositioningFragment`]'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>,
}
impl PositioningFragment {
pub fn new_anonymous(
style: ServoArc<ComputedValues>,
rect: PhysicalRect<Au>,
children: Vec<Fragment>,
) -> ArcRefCell<Self> {
Self::new_with_base_fragment(BaseFragment::anonymous(), style, rect, children)
}
pub fn new_empty(
base_fragment_info: BaseFragmentInfo,
rect: PhysicalRect<Au>,
style: ServoArc<ComputedValues>,
) -> ArcRefCell<Self> {
Self::new_with_base_fragment(base_fragment_info.into(), style, rect, Vec::new())
}
fn new_with_base_fragment(
base: BaseFragment,
style: ServoArc<ComputedValues>,
rect: PhysicalRect<Au>,
children: Vec<Fragment>,
) -> ArcRefCell<Self> {
let content_origin = rect.origin;
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
acc.union(
&child
.scrollable_overflow_for_parent()
.translate(content_origin.to_vector()),
)
});
ArcRefCell::new(PositioningFragment {
base,
style,
rect,
children,
scrollable_overflow,
cumulative_containing_block_rect: PhysicalRect::zero(),
})
}
pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect<Au>) {
self.cumulative_containing_block_rect = *containing_block;
}
pub fn offset_by_containing_block(&self, rect: &PhysicalRect<Au>) -> PhysicalRect<Au> {
rect.translate(self.cumulative_containing_block_rect.origin.to_vector())
}
pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!(
"PositioningFragment\
\nbase={:?}\
\nrect={:?}\
\nscrollable_overflow={:?}",
self.base, self.rect, self.scrollable_overflow
));
for child in &self.children {
child.print(tree);
}
tree.end_level();
}
}