layout: Share styles to inline box children via SharedInlineStyles (#36896)

`TextRun`s use their parent style to render. Previously, these styles
were cloned and stored directly in the box tree `TextRun` and resulting
`TextFragment`s. This presents a problem for incremental layout.
Wrapping the style in another layer of shared ownership and mutability
will allow updating all `TextFragment`s during repaint-only incremental
layout by simply updating the box tree styles of the original text
parents.

This adds a new set of borrows when accessing text styles, but also
makes it so that during box tree block construction
`InlineFormattingContext`s are created lazily and now
`InlineFormattingContextBuilder::finish` consumes the builder, making
the API make a bit more sense. This should also improve performance of
box tree block construction slightly.

Testing: This should not change observable behavior and thus is 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-05-12 11:38:50 +02:00 committed by GitHub
parent db83601b62
commit a0dd2c1beb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 369 additions and 187 deletions

View file

@ -14,6 +14,7 @@ use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
use fonts::GlyphStore;
use gradient::WebRenderGradient;
use range::Range as ServoRange;
use servo_arc::Arc as ServoArc;
use servo_geometry::MaxRect;
use style::Zero;
use style::color::{AbsoluteColor, ColorSpace};
@ -544,7 +545,13 @@ impl Fragment {
},
Fragment::Text(text) => {
let text = &*text.borrow();
match text.parent_style.get_inherited_box().visibility {
match text
.inline_styles
.style
.borrow()
.get_inherited_box()
.visibility
{
Visibility::Visible => {
self.build_display_list_for_text_fragment(text, builder, containing_block)
},
@ -604,22 +611,23 @@ impl Fragment {
return;
}
let parent_style = fragment.inline_styles.style.borrow();
self.maybe_push_hit_test_for_style_and_tag(
builder,
&fragment.parent_style,
&parent_style,
fragment.base.tag,
rect,
Cursor::Text,
);
let color = fragment.parent_style.clone_color();
let color = parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
let dppx = builder.context.style_context.device_pixel_ratio().get();
let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
let common = builder.common_properties(rect.to_webrender(), &parent_style);
// Shadows. According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front to
// back).
let shadows = &fragment.parent_style.get_inherited_text().text_shadow;
let shadows = &parent_style.get_inherited_text().text_shadow;
for shadow in shadows.0.iter().rev() {
builder.wr().push_shadow(
&wr::SpaceAndClipInfo {
@ -642,7 +650,7 @@ impl Fragment {
let mut rect = rect;
rect.origin.y += font_metrics.ascent - font_metrics.underline_offset;
rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
if fragment
@ -651,7 +659,7 @@ impl Fragment {
{
let mut rect = rect;
rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
// TODO: This caret/text selection implementation currently does not account for vertical text
@ -678,12 +686,13 @@ impl Fragment {
Point2D::new(end.x.to_f32_px(), containing_block.max_y().to_f32_px()),
);
if let Some(selection_color) = fragment
.selected_style
.inline_styles
.selected
.borrow()
.clone_background_color()
.as_absolute()
{
let selection_common =
builder.common_properties(selection_rect, &fragment.parent_style);
let selection_common = builder.common_properties(selection_rect, &parent_style);
builder.wr().push_rect(
&selection_common,
selection_rect,
@ -709,7 +718,7 @@ impl Fragment {
),
);
let insertion_point_common =
builder.common_properties(insertion_point_rect, &fragment.parent_style);
builder.common_properties(insertion_point_rect, &parent_style);
// TODO: The color of the caret is currently hardcoded to the text color.
// We should be retrieving the caret color from the style properly.
builder
@ -734,7 +743,7 @@ impl Fragment {
let mut rect = rect;
rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset;
rect.size.height = Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx));
self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
if !shadows.0.is_empty() {
@ -744,23 +753,22 @@ impl Fragment {
fn build_display_list_for_text_decoration(
&self,
fragment: &TextFragment,
parent_style: &ServoArc<ComputedValues>,
builder: &mut DisplayListBuilder,
rect: &PhysicalRect<Au>,
color: &AbsoluteColor,
) {
let rect = rect.to_webrender();
let wavy_line_thickness = (0.33 * rect.size().height).ceil();
let text_decoration_color = fragment
.parent_style
let text_decoration_color = parent_style
.clone_text_decoration_color()
.resolve_to_absolute(color);
let text_decoration_style = fragment.parent_style.clone_text_decoration_style();
let text_decoration_style = parent_style.clone_text_decoration_style();
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
return;
}
builder.display_list.wr.push_line(
&builder.common_properties(rect, &fragment.parent_style),
&builder.common_properties(rect, parent_style),
&rect,
wavy_line_thickness,
wr::LineOrientation::Horizontal,
@ -1026,7 +1034,7 @@ impl<'a> BuilderForBoxFragment<'a> {
for extra_background in extra_backgrounds {
let positioning_area = extra_background.rect;
let painter = BackgroundPainter {
style: &extra_background.style.borrow_mut().0,
style: &extra_background.style.borrow_mut(),
painting_area_override: None,
positioning_area_override: Some(
positioning_area