diff options
Diffstat (limited to 'components/layout/flow/inline')
-rw-r--r-- | components/layout/flow/inline/construct.rs | 71 | ||||
-rw-r--r-- | components/layout/flow/inline/inline_box.rs | 26 | ||||
-rw-r--r-- | components/layout/flow/inline/line.rs | 21 | ||||
-rw-r--r-- | components/layout/flow/inline/mod.rs | 85 | ||||
-rw-r--r-- | components/layout/flow/inline/text_run.rs | 72 |
5 files changed, 206 insertions, 69 deletions
diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs index 74b0cf4ea7d..a99de1679a4 100644 --- a/components/layout/flow/inline/construct.rs +++ b/components/layout/flow/inline/construct.rs @@ -7,13 +7,15 @@ use std::char::{ToLowercase, ToUppercase}; use icu_segmenter::WordSegmenter; use itertools::izip; -use servo_arc::Arc; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; use style::values::specified::text::TextTransformCase; use unicode_bidi::Level; use super::text_run::TextRun; -use super::{InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem}; +use super::{ + InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem, + SharedInlineStyles, +}; use crate::PropagatedBoxTreeData; use crate::cell::ArcRefCell; use crate::context::LayoutContext; @@ -25,6 +27,12 @@ use crate::style_ext::ComputedValuesExt; #[derive(Default)] pub(crate) struct InlineFormattingContextBuilder { + /// A stack of [`SharedInlineStyles`] including one for the root, one for each inline box on the + /// inline box stack, and importantly, one for every `display: contents` element that we are + /// currently processing. Normally `display: contents` elements don't affect the structure of + /// the [`InlineFormattingContext`], but the styles they provide do style their children. + shared_inline_styles_stack: Vec<SharedInlineStyles>, + /// The collection of text strings that make up this [`InlineFormattingContext`] under /// construction. pub text_segments: Vec<String>, @@ -63,7 +71,7 @@ pub(crate) struct InlineFormattingContextBuilder { /// The traversal is at all times as deep in the tree as this stack is, /// which is why the code doesn't need to keep track of the actual /// container root (see `handle_inline_level_element`). - /// + //_ /// When an inline box ends, it's removed from this stack. inline_box_stack: Vec<InlineBoxIdentifier>, @@ -83,10 +91,17 @@ pub(crate) struct InlineFormattingContextBuilder { } impl InlineFormattingContextBuilder { - pub(crate) fn new() -> Self { - // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary. + pub(crate) fn new(info: &NodeAndStyleInfo) -> Self { + Self::new_for_shared_styles(vec![info.into()]) + } + + pub(crate) fn new_for_shared_styles( + shared_inline_styles_stack: Vec<SharedInlineStyles>, + ) -> Self { Self { + // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary. on_word_boundary: true, + shared_inline_styles_stack, ..Default::default() } } @@ -100,6 +115,13 @@ impl InlineFormattingContextBuilder { self.current_text_offset += string_to_push.len(); } + fn shared_inline_styles(&self) -> SharedInlineStyles { + self.shared_inline_styles_stack + .last() + .expect("Should always have at least one SharedInlineStyles") + .clone() + } + /// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring /// during box tree construction. An IFC is empty if it only contains TextRuns with /// completely collapsible whitespace. When that happens it can be ignored completely. @@ -135,7 +157,7 @@ impl InlineFormattingContextBuilder { independent_formatting_context: IndependentFormattingContext, ) -> ArcRefCell<InlineItem> { let inline_level_box = ArcRefCell::new(InlineItem::Atomic( - Arc::new(independent_formatting_context), + ArcRefCell::new(independent_formatting_context), self.current_text_offset, Level::ltr(), /* This will be assigned later if necessary. */ )); @@ -166,7 +188,8 @@ impl InlineFormattingContextBuilder { } pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineItem> { - let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowFloatBox(Arc::new(float_box))); + let inline_level_box = + ArcRefCell::new(InlineItem::OutOfFlowFloatBox(ArcRefCell::new(float_box))); self.inline_items.push(inline_level_box.clone()); self.contains_floats = true; inline_level_box @@ -179,6 +202,14 @@ impl InlineFormattingContextBuilder { ) { self.push_control_character_string(inline_box.base.style.bidi_control_chars().0); + // Don't push a `SharedInlineStyles` if we are pushing this box when splitting + // an IFC for a block-in-inline split. Shared styles are pushed as part of setting + // up the second split of the IFC. + if inline_box.is_first_split { + self.shared_inline_styles_stack + .push(inline_box.shared_inline_styles.clone()); + } + let (identifier, inline_box) = self.inline_boxes.start_inline_box(inline_box); let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box)); self.inline_items.push(inline_level_box.clone()); @@ -194,6 +225,8 @@ impl InlineFormattingContextBuilder { /// a single box tree items may be produced for a single inline box when that inline /// box is split around a block-level element. pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> { + self.shared_inline_styles_stack.pop(); + let (identifier, block_in_inline_splits) = self.end_inline_box_internal(); let inline_level_box = self.inline_boxes.get(&identifier); { @@ -272,8 +305,6 @@ impl InlineFormattingContextBuilder { } let selection_range = info.get_selection_range(); - let selected_style = info.get_selected_style(); - if let Some(last_character) = new_text.chars().next_back() { self.on_word_boundary = last_character.is_whitespace(); self.last_inline_box_ended_with_collapsible_white_space = @@ -295,14 +326,21 @@ impl InlineFormattingContextBuilder { .push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new( TextRun::new( info.into(), - info.style.clone(), + self.shared_inline_styles(), new_range, selection_range, - selected_style, ), )))); } + pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) { + self.shared_inline_styles_stack.push(shared_inline_styles); + } + + pub(crate) fn leave_display_contents(&mut self) { + self.shared_inline_styles_stack.pop(); + } + pub(crate) fn split_around_block_and_finish( &mut self, layout_context: &LayoutContext, @@ -318,7 +356,8 @@ impl InlineFormattingContextBuilder { // context. It has the same inline box structure as this builder, except the boxes are // marked as not being the first fragment. No inline content is carried over to this new // builder. - let mut new_builder = InlineFormattingContextBuilder::new(); + let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone()); + let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits); for (identifier, historical_inline_boxes) in izip!(self.inline_box_stack.iter(), block_in_inline_splits) @@ -356,7 +395,7 @@ impl InlineFormattingContextBuilder { /// Finish the current inline formatting context, returning [`None`] if the context was empty. pub(crate) fn finish( - &mut self, + self, layout_context: &LayoutContext, propagated_data: PropagatedBoxTreeData, has_first_formatted_line: bool, @@ -367,11 +406,9 @@ impl InlineFormattingContextBuilder { return None; } - let old_builder = std::mem::replace(self, InlineFormattingContextBuilder::new()); - assert!(old_builder.inline_box_stack.is_empty()); - + assert!(self.inline_box_stack.is_empty()); Some(InlineFormattingContext::new_with_builder( - old_builder, + self, layout_context, propagated_data, has_first_formatted_line, diff --git a/components/layout/flow/inline/inline_box.rs b/components/layout/flow/inline/inline_box.rs index 1c953c13074..b547f3b5935 100644 --- a/components/layout/flow/inline/inline_box.rs +++ b/components/layout/flow/inline/inline_box.rs @@ -7,8 +7,15 @@ use std::vec::IntoIter; use app_units::Au; use fonts::FontMetrics; use malloc_size_of_derive::MallocSizeOf; - -use super::{InlineContainerState, InlineContainerStateFlags, inline_container_needs_strut}; +use script::layout_dom::ServoLayoutNode; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; +use servo_arc::Arc as ServoArc; +use style::properties::ComputedValues; + +use super::{ + InlineContainerState, InlineContainerStateFlags, SharedInlineStyles, + inline_container_needs_strut, +}; use crate::ContainingBlock; use crate::cell::ArcRefCell; use crate::context::LayoutContext; @@ -20,6 +27,9 @@ use crate::style_ext::{LayoutStyle, PaddingBorderMargin}; #[derive(Debug, MallocSizeOf)] pub(crate) struct InlineBox { pub base: LayoutBoxBase, + /// The [`SharedInlineStyles`] for this [`InlineBox`] that are used to share styles + /// with all [`super::TextRun`] children. + pub(super) shared_inline_styles: SharedInlineStyles, /// The identifier of this inline box in the containing [`super::InlineFormattingContext`]. pub(super) identifier: InlineBoxIdentifier, /// Whether or not this is the first instance of an [`InlineBox`] before a possible @@ -37,6 +47,7 @@ impl InlineBox { pub(crate) fn new(info: &NodeAndStyleInfo) -> Self { Self { base: LayoutBoxBase::new(info.into(), info.style.clone()), + shared_inline_styles: info.into(), // This will be assigned later, when the box is actually added to the IFC. identifier: InlineBoxIdentifier::default(), is_first_split: true, @@ -48,6 +59,7 @@ impl InlineBox { pub(crate) fn split_around_block(&self) -> Self { Self { base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()), + shared_inline_styles: self.shared_inline_styles.clone(), is_first_split: false, is_last_split: false, ..*self @@ -58,6 +70,16 @@ impl InlineBox { pub(crate) fn layout_style(&self) -> LayoutStyle { LayoutStyle::Default(&self.base.style) } + + pub(crate) fn repair_style( + &mut self, + node: &ServoLayoutNode, + new_style: &ServoArc<ComputedValues>, + ) { + self.base.repair_style(new_style); + *self.shared_inline_styles.style.borrow_mut() = new_style.clone(); + *self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style(); + } } #[derive(Debug, Default, MallocSizeOf)] diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs index 80bab1080ed..3b92078d67d 100644 --- a/components/layout/flow/inline/line.rs +++ b/components/layout/flow/inline/line.rs @@ -7,7 +7,6 @@ use bitflags::bitflags; use fonts::{ByteIndex, FontMetrics, GlyphStore}; use itertools::Either; use range::Range; -use servo_arc::Arc; use style::Zero; use style::computed_values::position::T as Position; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; @@ -21,7 +20,7 @@ use unicode_bidi::{BidiInfo, Level}; use webrender_api::FontInstanceKey; use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken}; -use super::{InlineFormattingContextLayout, LineBlockSizes}; +use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles}; use crate::cell::ArcRefCell; use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, TextFragment}; use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical}; @@ -568,7 +567,7 @@ impl LineItemLayout<'_, '_> { self.current_state.fragments.push(( Fragment::Text(ArcRefCell::new(TextFragment { base: text_item.base_fragment_info.into(), - parent_style: text_item.parent_style, + inline_styles: text_item.inline_styles.clone(), rect: PhysicalRect::zero(), font_metrics: text_item.font_metrics, font_key: text_item.font_key, @@ -576,7 +575,6 @@ impl LineItemLayout<'_, '_> { text_decoration_line: text_item.text_decoration_line, justification_adjustment: self.justification_adjustment, selection_range: text_item.selection_range, - selected_style: text_item.selected_style, })), content_rect, )); @@ -763,7 +761,7 @@ impl LineItem { pub(super) struct TextRunLineItem { pub base_fragment_info: BaseFragmentInfo, - pub parent_style: Arc<ComputedValues>, + pub inline_styles: SharedInlineStyles, pub text: Vec<std::sync::Arc<GlyphStore>>, pub font_metrics: FontMetrics, pub font_key: FontInstanceKey, @@ -771,13 +769,16 @@ pub(super) struct TextRunLineItem { /// The BiDi level of this [`TextRunLineItem`] to enable reordering. pub bidi_level: Level, pub selection_range: Option<Range<ByteIndex>>, - pub selected_style: Arc<ComputedValues>, } impl TextRunLineItem { fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool { if matches!( - self.parent_style.get_inherited_text().white_space_collapse, + self.inline_styles + .style + .borrow() + .get_inherited_text() + .white_space_collapse, WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces ) { return false; @@ -803,7 +804,11 @@ impl TextRunLineItem { fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool { if matches!( - self.parent_style.get_inherited_text().white_space_collapse, + self.inline_styles + .style + .borrow() + .get_inherited_text() + .white_space_collapse, WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces ) { return false; diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index 2023f4e7174..7e69aa1aaae 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -90,12 +90,13 @@ use line::{ use line_breaker::LineBreaker; use malloc_size_of_derive::MallocSizeOf; use range::Range; +use script::layout_dom::ServoLayoutNode; use servo_arc::Arc; use style::Zero; use style::computed_values::text_wrap_mode::T as TextWrapMode; use style::computed_values::vertical_align::T as VerticalAlign; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; -use style::context::QuirksMode; +use style::context::{QuirksMode, SharedStyleContext}; use style::properties::ComputedValues; use style::properties::style_structs::InheritedText; use style::values::generics::box_::VerticalAlignKeyword; @@ -118,6 +119,7 @@ use super::{ }; use crate::cell::ArcRefCell; use crate::context::LayoutContext; +use crate::dom_traversal::NodeAndStyleInfo; use crate::flow::CollapsibleWithParentStartMargin; use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::formatting_contexts::{ @@ -131,7 +133,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; -use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData}; +use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData, SharedStyle}; // From gfxFontConstants.h in Firefox. static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; @@ -173,6 +175,25 @@ pub(crate) struct InlineFormattingContext { pub(super) has_right_to_left_content: bool, } +/// [`TextRun`] and `TextFragment`s need a handle on their parent inline box (or inline +/// formatting context root)'s style. In order to implement incremental layout, these are +/// wrapped in [`SharedStyle`]. This allows updating the parent box tree element without +/// updating every single descendant box tree node and fragment. +#[derive(Clone, Debug, MallocSizeOf)] +pub(crate) struct SharedInlineStyles { + pub style: SharedStyle, + pub selected: SharedStyle, +} + +impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles { + fn from(info: &NodeAndStyleInfo) -> Self { + Self { + style: SharedStyle::new(info.style.clone()), + selected: SharedStyle::new(info.get_selected_style()), + } + } +} + /// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`] #[derive(Debug, MallocSizeOf)] pub(crate) struct FontKeyAndMetrics { @@ -190,15 +211,41 @@ pub(crate) enum InlineItem { ArcRefCell<AbsolutelyPositionedBox>, usize, /* offset_in_text */ ), - OutOfFlowFloatBox(#[conditional_malloc_size_of] Arc<FloatBox>), + OutOfFlowFloatBox(ArcRefCell<FloatBox>), Atomic( - #[conditional_malloc_size_of] Arc<IndependentFormattingContext>, + ArcRefCell<IndependentFormattingContext>, usize, /* offset_in_text */ Level, /* bidi_level */ ), } impl InlineItem { + pub(crate) fn repair_style( + &self, + context: &SharedStyleContext, + node: &ServoLayoutNode, + new_style: &Arc<ComputedValues>, + ) { + match self { + InlineItem::StartInlineBox(inline_box) => { + inline_box.borrow_mut().repair_style(node, new_style); + }, + InlineItem::EndInlineBox => {}, + // TextRun holds a handle the `InlineSharedStyles` which is updated when repairing inline box + // and `display: contents` styles. + InlineItem::TextRun(..) => {}, + InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box + .borrow_mut() + .context + .repair_style(context, new_style), + InlineItem::OutOfFlowFloatBox(float_box) => float_box + .borrow_mut() + .contents + .repair_style(context, new_style), + InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style), + } + } + pub(crate) fn invalidate_cached_fragment(&self) { match self { InlineItem::StartInlineBox(inline_box) => { @@ -212,11 +259,14 @@ impl InlineItem { .base .invalidate_cached_fragment(); }, - InlineItem::OutOfFlowFloatBox(float_box) => { - float_box.contents.base.invalidate_cached_fragment() - }, + InlineItem::OutOfFlowFloatBox(float_box) => float_box + .borrow() + .contents + .base + .invalidate_cached_fragment(), InlineItem::Atomic(independent_formatting_context, ..) => { independent_formatting_context + .borrow() .base .invalidate_cached_fragment(); }, @@ -232,9 +282,11 @@ impl InlineItem { InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => { positioned_box.borrow().context.base.fragments() }, - InlineItem::OutOfFlowFloatBox(float_box) => float_box.contents.base.fragments(), + InlineItem::OutOfFlowFloatBox(float_box) => { + float_box.borrow().contents.base.fragments() + }, InlineItem::Atomic(independent_formatting_context, ..) => { - independent_formatting_context.base.fragments() + independent_formatting_context.borrow().base.fragments() }, } } @@ -958,6 +1010,7 @@ impl InlineFormattingContextLayout<'_> { .as_physical(Some(self.containing_block)); self.fragments .push(Fragment::Positioning(PositioningFragment::new_anonymous( + self.root_nesting_level.style.clone(), physical_line_rect, fragments, ))); @@ -1313,7 +1366,7 @@ impl InlineFormattingContextLayout<'_> { ) { let inline_advance = glyph_store.total_advance(); let flags = if glyph_store.is_whitespace() { - SegmentContentFlags::from(text_run.parent_style.get_inherited_text()) + SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text()) } else { SegmentContentFlags::empty() }; @@ -1398,13 +1451,12 @@ impl InlineFormattingContextLayout<'_> { TextRunLineItem { text: vec![glyph_store], base_fragment_info: text_run.base_fragment_info, - parent_style: text_run.parent_style.clone(), + inline_styles: text_run.inline_styles.clone(), font_metrics, font_key: ifc_font_info.key, text_decoration_line: self.current_inline_container_state().text_decoration_line, bidi_level, selection_range, - selected_style: text_run.selected_style.clone(), }, )); } @@ -1751,7 +1803,7 @@ impl InlineFormattingContext { InlineItem::EndInlineBox => layout.finish_inline_box(), InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout), InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => { - atomic_formatting_context.layout_into_line_items( + atomic_formatting_context.borrow().layout_into_line_items( &mut layout, *offset_in_text, *bidi_level, @@ -1766,7 +1818,7 @@ impl InlineFormattingContext { )); }, InlineItem::OutOfFlowFloatBox(float_box) => { - float_box.layout_into_line_items(&mut layout); + float_box.borrow().layout_into_line_items(&mut layout); }, } } @@ -2363,8 +2415,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { }, InlineItem::TextRun(text_run) => { let text_run = &*text_run.borrow(); + let parent_style = text_run.inline_styles.style.borrow(); for segment in text_run.shaped_text.iter() { - let style_text = text_run.parent_style.get_inherited_text(); + let style_text = parent_style.get_inherited_text(); let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap; // TODO: This should take account whether or not the first and last character prevent @@ -2428,7 +2481,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { let InlineContentSizesResult { sizes: outer, depends_on_block_constraints, - } = atomic.outer_inline_content_sizes( + } = atomic.borrow().outer_inline_content_sizes( self.layout_context, &self.constraint_space.into(), &LogicalVec2::zero(), diff --git a/components/layout/flow/inline/text_run.rs b/components/layout/flow/inline/text_run.rs index 0d0c6398017..591c7b9b5e2 100644 --- a/components/layout/flow/inline/text_run.rs +++ b/components/layout/flow/inline/text_run.rs @@ -26,7 +26,7 @@ use unicode_script::Script; use xi_unicode::linebreak_property; use super::line_breaker::LineBreaker; -use super::{FontKeyAndMetrics, InlineFormattingContextLayout}; +use super::{FontKeyAndMetrics, InlineFormattingContextLayout, SharedInlineStyles}; use crate::fragment_tree::BaseFragmentInfo; // These constants are the xi-unicode line breaking classes that are defined in @@ -37,22 +37,6 @@ pub(crate) const XI_LINE_BREAKING_CLASS_ZW: u8 = 28; pub(crate) const XI_LINE_BREAKING_CLASS_WJ: u8 = 30; pub(crate) const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 42; -/// <https://www.w3.org/TR/css-display-3/#css-text-run> -#[derive(Debug, MallocSizeOf)] -pub(crate) struct TextRun { - pub base_fragment_info: BaseFragmentInfo, - #[conditional_malloc_size_of] - pub parent_style: Arc<ComputedValues>, - pub text_range: Range<usize>, - - /// The text of this [`TextRun`] with a font selected, broken into unbreakable - /// segments, and shaped. - pub shaped_text: Vec<TextRunSegment>, - pub selection_range: Option<ServoRange<ByteIndex>>, - #[conditional_malloc_size_of] - pub selected_style: Arc<ComputedValues>, -} - // There are two reasons why we might want to break at the start: // // 1. The line breaker told us that a break was necessary between two separate @@ -334,21 +318,49 @@ impl TextRunSegment { } } +/// A single [`TextRun`] for the box tree. These are all descendants of +/// [`super::InlineBox`] or the root of the [`super::InlineFormattingContext`]. During +/// box tree construction, text is split into [`TextRun`]s based on their font, script, +/// etc. When these are created text is already shaped. +/// +/// <https://www.w3.org/TR/css-display-3/#css-text-run> +#[derive(Debug, MallocSizeOf)] +pub(crate) struct TextRun { + /// The [`BaseFragmentInfo`] for this [`TextRun`]. Usually this comes from the + /// original text node in the DOM for the text. + pub base_fragment_info: BaseFragmentInfo, + + /// The [`crate::SharedStyle`] from this [`TextRun`]s parent element. This is + /// shared so that incremental layout can simply update the parent element and + /// this [`TextRun`] will be updated automatically. + pub inline_styles: SharedInlineStyles, + + /// The range of text in [`super::InlineFormattingContext::text_content`] of the + /// [`super::InlineFormattingContext`] that owns this [`TextRun`]. These are UTF-8 offsets. + pub text_range: Range<usize>, + + /// The text of this [`TextRun`] with a font selected, broken into unbreakable + /// segments, and shaped. + pub shaped_text: Vec<TextRunSegment>, + + /// The selection range for the DOM text node that originated this [`TextRun`]. This + /// comes directly from the DOM. + pub selection_range: Option<ServoRange<ByteIndex>>, +} + impl TextRun { pub(crate) fn new( base_fragment_info: BaseFragmentInfo, - parent_style: Arc<ComputedValues>, + inline_styles: SharedInlineStyles, text_range: Range<usize>, selection_range: Option<ServoRange<ByteIndex>>, - selected_style: Arc<ComputedValues>, ) -> Self { Self { base_fragment_info, - parent_style, + inline_styles, text_range, shaped_text: Vec::new(), selection_range, - selected_style, } } @@ -360,11 +372,12 @@ impl TextRun { font_cache: &mut Vec<FontKeyAndMetrics>, bidi_info: &BidiInfo, ) { - let inherited_text_style = self.parent_style.get_inherited_text().clone(); + let parent_style = self.inline_styles.style.borrow().clone(); + let inherited_text_style = parent_style.get_inherited_text().clone(); let letter_spacing = inherited_text_style .letter_spacing .0 - .resolve(self.parent_style.clone_font().font_size.computed_size()); + .resolve(parent_style.clone_font().font_size.computed_size()); let letter_spacing = if letter_spacing.px() != 0. { Some(app_units::Au::from(letter_spacing)) } else { @@ -384,7 +397,13 @@ impl TextRun { let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into()); let segments = self - .segment_text_by_font(formatting_context_text, font_context, font_cache, bidi_info) + .segment_text_by_font( + formatting_context_text, + font_context, + font_cache, + bidi_info, + &parent_style, + ) .into_iter() .map(|(mut segment, font)| { let word_spacing = style_word_spacing.unwrap_or_else(|| { @@ -407,7 +426,7 @@ impl TextRun { }; segment.shape_text( - &self.parent_style, + &parent_style, formatting_context_text, linebreaker, &shaping_options, @@ -430,8 +449,9 @@ impl TextRun { font_context: &FontContext, font_cache: &mut Vec<FontKeyAndMetrics>, bidi_info: &BidiInfo, + parent_style: &Arc<ComputedValues>, ) -> Vec<(TextRunSegment, FontRef)> { - let font_group = font_context.font_group(self.parent_style.clone_font()); + let font_group = font_context.font_group(parent_style.clone_font()); let mut current: Option<(TextRunSegment, FontRef)> = None; let mut results = Vec::new(); |