diff options
Diffstat (limited to 'components/layout_2020/flow/inline/construct.rs')
-rw-r--r-- | components/layout_2020/flow/inline/construct.rs | 169 |
1 files changed, 74 insertions, 95 deletions
diff --git a/components/layout_2020/flow/inline/construct.rs b/components/layout_2020/flow/inline/construct.rs index e3f4d8055a0..603a807a62e 100644 --- a/components/layout_2020/flow/inline/construct.rs +++ b/components/layout_2020/flow/inline/construct.rs @@ -11,7 +11,7 @@ use style::values::specified::text::TextTransformCase; use unicode_segmentation::UnicodeSegmentation; use super::text_run::TextRun; -use super::{InlineBox, InlineFormattingContext, InlineLevelBox}; +use super::{InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::dom::NodeExt; @@ -22,7 +22,12 @@ use crate::positioned::AbsolutelyPositionedBox; #[derive(Default)] pub(crate) struct InlineFormattingContextBuilder { + /// The collection of text strings that make up this [`InlineFormattingContext`] under + /// construction. pub text_segments: Vec<String>, + + /// The current offset in the final text string of this [`InlineFormattingContext`], + /// used to properly set the text range of new [`InlineItem::TextRun`]s. current_text_offset: usize, /// Whether the last processed node ended with whitespace. This is used to @@ -41,13 +46,13 @@ pub(crate) struct InlineFormattingContextBuilder { /// Whether or not this inline formatting context will contain floats. pub contains_floats: bool, - /// Inline elements are direct descendants of the element that establishes - /// the inline formatting context that this builder builds. - pub root_inline_boxes: Vec<ArcRefCell<InlineLevelBox>>, + /// The current list of [`InlineItem`]s in this [`InlineFormattingContext`] under + /// construction. This is stored in a flat list to make it easy to access the last + /// item. + pub inline_items: Vec<ArcRefCell<InlineItem>>, - /// Whether or not the inline formatting context under construction has any - /// uncollapsible text content. - pub has_uncollapsible_text_content: bool, + /// The current [`InlineBox`] tree of this [`InlineFormattingContext`] under construction. + pub inline_boxes: InlineBoxes, /// The ongoing stack of inline boxes stack of the builder. /// @@ -56,16 +61,12 @@ pub(crate) struct InlineFormattingContextBuilder { /// which is why the code doesn't need to keep track of the actual /// container root (see `handle_inline_level_element`). /// - /// Whenever the end of a DOM element that represents an inline box is - /// reached, the inline box at the top of this stack is complete and ready - /// to be pushed to the children of the next last ongoing inline box - /// the ongoing inline formatting context if the stack is now empty, - /// which means we reached the end of a child of the actual - /// container root (see `move_to_next_sibling`). - /// - /// When an inline box ends, it's removed from this stack and added to - /// [`Self::root_inline_boxes`]. - inline_box_stack: Vec<InlineBox>, + /// When an inline box ends, it's removed from this stack. + inline_box_stack: Vec<InlineBoxIdentifier>, + + /// Whether or not the inline formatting context under construction has any + /// uncollapsible text content. + pub has_uncollapsible_text_content: bool, } impl InlineFormattingContextBuilder { @@ -93,44 +94,30 @@ impl InlineFormattingContextBuilder { return false; } - fn inline_level_boxes_are_empty(boxes: &[ArcRefCell<InlineLevelBox>]) -> bool { - boxes - .iter() - .all(|inline_level_box| inline_level_box_is_empty(&inline_level_box.borrow())) - } - - fn inline_level_box_is_empty(inline_level_box: &InlineLevelBox) -> bool { + fn inline_level_box_is_empty(inline_level_box: &InlineItem) -> bool { match inline_level_box { - InlineLevelBox::InlineBox(_) => false, + InlineItem::StartInlineBox(_) => false, + InlineItem::EndInlineBox => false, // Text content is handled by `self.has_uncollapsible_text` content above in order // to avoid having to iterate through the character once again. - InlineLevelBox::TextRun(_) => true, - InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => false, - InlineLevelBox::OutOfFlowFloatBox(_) => false, - InlineLevelBox::Atomic(_) => false, + InlineItem::TextRun(_) => true, + InlineItem::OutOfFlowAbsolutelyPositionedBox(_) => false, + InlineItem::OutOfFlowFloatBox(_) => false, + InlineItem::Atomic(_) => false, } } - inline_level_boxes_are_empty(&self.root_inline_boxes) - } - - // Retrieves the mutable reference of inline boxes either from the last - // element of a stack or directly from the formatting context, depending on the situation. - fn current_inline_level_boxes(&mut self) -> &mut Vec<ArcRefCell<InlineLevelBox>> { - match self.inline_box_stack.last_mut() { - Some(last) => &mut last.children, - None => &mut self.root_inline_boxes, - } + self.inline_items + .iter() + .all(|inline_level_box| inline_level_box_is_empty(&inline_level_box.borrow())) } pub(crate) fn push_atomic( &mut self, independent_formatting_context: IndependentFormattingContext, - ) -> ArcRefCell<InlineLevelBox> { - let inline_level_box = - ArcRefCell::new(InlineLevelBox::Atomic(independent_formatting_context)); - self.current_inline_level_boxes() - .push(inline_level_box.clone()); + ) -> ArcRefCell<InlineItem> { + let inline_level_box = ArcRefCell::new(InlineItem::Atomic(independent_formatting_context)); + self.inline_items.push(inline_level_box.clone()); // Push an object replacement character for this atomic, which will ensure that the line breaker // inserts a line breaking opportunity here. @@ -147,49 +134,43 @@ impl InlineFormattingContextBuilder { pub(crate) fn push_absolutely_positioned_box( &mut self, absolutely_positioned_box: AbsolutelyPositionedBox, - ) -> ArcRefCell<InlineLevelBox> { + ) -> ArcRefCell<InlineItem> { let absolutely_positioned_box = ArcRefCell::new(absolutely_positioned_box); - let inline_level_box = ArcRefCell::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox( + let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowAbsolutelyPositionedBox( absolutely_positioned_box, )); - self.current_inline_level_boxes() - .push(inline_level_box.clone()); + self.inline_items.push(inline_level_box.clone()); inline_level_box } - pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineLevelBox> { - let inline_level_box = ArcRefCell::new(InlineLevelBox::OutOfFlowFloatBox(float_box)); - self.current_inline_level_boxes() - .push(inline_level_box.clone()); + pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineItem> { + let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowFloatBox(float_box)); + self.inline_items.push(inline_level_box.clone()); self.contains_floats = true; inline_level_box } - pub(crate) fn start_inline_box<'dom, Node: NodeExt<'dom>>( - &mut self, - info: &NodeAndStyleInfo<Node>, - ) { - self.inline_box_stack.push(InlineBox::new(info)) + pub(crate) fn start_inline_box(&mut self, inline_box: InlineBox) { + let identifier = self.inline_boxes.start_inline_box(inline_box); + self.inline_items + .push(ArcRefCell::new(InlineItem::StartInlineBox(identifier))); + self.inline_box_stack.push(identifier); } - pub(crate) fn end_inline_box(&mut self) -> ArcRefCell<InlineLevelBox> { - self.end_inline_box_internal(true) + pub(crate) fn end_inline_box(&mut self) -> ArcRefCell<InlineBox> { + let identifier = self.end_inline_box_internal(); + let inline_level_box = self.inline_boxes.get(&identifier); + inline_level_box.borrow_mut().is_last_fragment = true; + inline_level_box } - fn end_inline_box_internal(&mut self, is_last_fragment: bool) -> ArcRefCell<InlineLevelBox> { - let mut inline_box = self - .inline_box_stack + fn end_inline_box_internal(&mut self) -> InlineBoxIdentifier { + self.inline_boxes.end_inline_box(); + self.inline_items + .push(ArcRefCell::new(InlineItem::EndInlineBox)); + self.inline_box_stack .pop() - .expect("no ongoing inline level box found"); - - if is_last_fragment { - inline_box.is_last_fragment = true; - } - - let inline_box = ArcRefCell::new(InlineLevelBox::InlineBox(inline_box)); - self.current_inline_level_boxes().push(inline_box.clone()); - - inline_box + .expect("Ended non-existent inline box") } pub(crate) fn push_text<'dom, Node: NodeExt<'dom>>( @@ -251,19 +232,19 @@ impl InlineFormattingContextBuilder { self.current_text_offset = new_range.end; self.text_segments.push(new_text); - let inlines = self.current_inline_level_boxes(); - if let Some(mut last_box) = inlines.last_mut().map(|last| last.borrow_mut()) { - if let InlineLevelBox::TextRun(ref mut text_run) = *last_box { + if let Some(inline_item) = self.inline_items.last() { + if let InlineItem::TextRun(text_run) = &mut *inline_item.borrow_mut() { text_run.text_range.end = new_range.end; return; } } - inlines.push(ArcRefCell::new(InlineLevelBox::TextRun(TextRun::new( - info.into(), - info.style.clone(), - new_range, - )))); + self.inline_items + .push(ArcRefCell::new(InlineItem::TextRun(TextRun::new( + info.into(), + info.style.clone(), + new_range, + )))); } pub(crate) fn split_around_block_and_finish( @@ -280,27 +261,25 @@ 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 inline_buidler_from_before_split = std::mem::replace( - self, - InlineFormattingContextBuilder { - on_word_boundary: true, - inline_box_stack: self - .inline_box_stack - .iter() - .map(|inline_box| inline_box.split_around_block()) - .collect(), - ..Default::default() - }, - ); + let mut new_builder = InlineFormattingContextBuilder::new(); + for identifier in self.inline_box_stack.iter() { + new_builder.start_inline_box( + self.inline_boxes + .get(&identifier) + .borrow() + .split_around_block(), + ); + } + let mut inline_builder_from_before_split = std::mem::replace(self, new_builder); // End all ongoing inline boxes in the first builder, but ensure that they are not // marked as the final fragments, so that they do not get inline end margin, borders, // and padding. - while !inline_buidler_from_before_split.inline_box_stack.is_empty() { - inline_buidler_from_before_split.end_inline_box_internal(false); + while !inline_builder_from_before_split.inline_box_stack.is_empty() { + inline_builder_from_before_split.end_inline_box_internal(); } - inline_buidler_from_before_split.finish( + inline_builder_from_before_split.finish( layout_context, text_decoration_line, has_first_formatted_line, |