diff options
-rw-r--r-- | components/layout_2020/dom.rs | 3 | ||||
-rw-r--r-- | components/layout_2020/flow/construct.rs | 3 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/construct.rs | 11 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/inline_box.rs | 245 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/line.rs | 933 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/mod.rs | 412 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 2 |
7 files changed, 931 insertions, 678 deletions
diff --git a/components/layout_2020/dom.rs b/components/layout_2020/dom.rs index e30b22cb376..24ad7f16ed7 100644 --- a/components/layout_2020/dom.rs +++ b/components/layout_2020/dom.rs @@ -22,7 +22,8 @@ use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::dom_traversal::WhichPseudoElement; use crate::flexbox::FlexLevelBox; -use crate::flow::inline::{InlineBox, InlineItem}; +use crate::flow::inline::inline_box::InlineBox; +use crate::flow::inline::InlineItem; use crate::flow::BlockLevelBox; use crate::geom::PhysicalSize; use crate::replaced::{CanvasInfo, CanvasSource}; diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 986ae3e46d7..c52790d6348 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -14,7 +14,8 @@ use style::str::char_is_whitespace; use style::values::specified::text::TextDecorationLine; use super::inline::construct::InlineFormattingContextBuilder; -use super::inline::{InlineBox, InlineFormattingContext}; +use super::inline::inline_box::InlineBox; +use super::inline::InlineFormattingContext; use super::OutsideMarker; use crate::cell::ArcRefCell; use crate::context::LayoutContext; diff --git a/components/layout_2020/flow/inline/construct.rs b/components/layout_2020/flow/inline/construct.rs index 88b86961564..02751e356de 100644 --- a/components/layout_2020/flow/inline/construct.rs +++ b/components/layout_2020/flow/inline/construct.rs @@ -165,12 +165,15 @@ impl InlineFormattingContextBuilder { } fn end_inline_box_internal(&mut self) -> InlineBoxIdentifier { - self.inline_boxes.end_inline_box(); + let identifier = self + .inline_box_stack + .pop() + .expect("Ended non-existent inline box"); self.inline_items .push(ArcRefCell::new(InlineItem::EndInlineBox)); - self.inline_box_stack - .pop() - .expect("Ended non-existent inline box") + + self.inline_boxes.end_inline_box(identifier); + identifier } pub(crate) fn push_text<'dom, Node: NodeExt<'dom>>( diff --git a/components/layout_2020/flow/inline/inline_box.rs b/components/layout_2020/flow/inline/inline_box.rs new file mode 100644 index 00000000000..a8406611098 --- /dev/null +++ b/components/layout_2020/flow/inline/inline_box.rs @@ -0,0 +1,245 @@ +/* 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 std::vec::IntoIter; + +use app_units::Au; +use fonts::FontMetrics; +use serde::Serialize; +use servo_arc::Arc; +use style::properties::ComputedValues; + +use super::{inline_container_needs_strut, InlineContainerState, InlineContainerStateFlags}; +use crate::cell::ArcRefCell; +use crate::context::LayoutContext; +use crate::dom::NodeExt; +use crate::dom_traversal::NodeAndStyleInfo; +use crate::fragment_tree::BaseFragmentInfo; +use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; +use crate::ContainingBlock; + +#[derive(Debug, Serialize)] +pub(crate) struct InlineBox { + pub base_fragment_info: BaseFragmentInfo, + #[serde(skip_serializing)] + pub style: Arc<ComputedValues>, + /// The identifier of this inline box in the containing [`InlineFormattingContext`]. + pub(super) identifier: InlineBoxIdentifier, + pub is_first_fragment: bool, + pub is_last_fragment: bool, + /// The index of the default font in the [`InlineFormattingContext`]'s font metrics store. + /// This is initialized during IFC shaping. + pub default_font_index: Option<usize>, +} + +impl InlineBox { + pub(crate) fn new<'dom, Node: NodeExt<'dom>>(info: &NodeAndStyleInfo<Node>) -> Self { + Self { + base_fragment_info: info.into(), + style: info.style.clone(), + // This will be assigned later, when the box is actually added to the IFC. + identifier: InlineBoxIdentifier::default(), + is_first_fragment: true, + is_last_fragment: false, + default_font_index: None, + } + } + + pub(crate) fn split_around_block(&self) -> Self { + Self { + style: self.style.clone(), + is_first_fragment: false, + is_last_fragment: false, + ..*self + } + } +} + +#[derive(Debug, Default, Serialize)] +pub(crate) struct InlineBoxes { + /// A collection of all inline boxes in a particular [`InlineFormattingContext`]. + inline_boxes: Vec<ArcRefCell<InlineBox>>, + + /// A list of tokens that represent the actual tree of inline boxes, while allowing + /// easy traversal forward and backwards through the tree. This structure is also + /// stored in the [`InlineFormattingContext::inline_items`], but this version is + /// faster to iterate. + inline_box_tree: Vec<InlineBoxTreePathToken>, +} + +impl InlineBoxes { + pub(super) fn len(&self) -> usize { + self.inline_boxes.len() + } + + pub(super) fn get(&self, identifier: &InlineBoxIdentifier) -> ArcRefCell<InlineBox> { + self.inline_boxes[identifier.index_in_inline_boxes as usize].clone() + } + + pub(super) fn end_inline_box(&mut self, identifier: InlineBoxIdentifier) { + self.inline_box_tree + .push(InlineBoxTreePathToken::End(identifier)); + } + + pub(super) fn start_inline_box(&mut self, mut inline_box: InlineBox) -> InlineBoxIdentifier { + assert!(self.inline_boxes.len() <= u32::MAX as usize); + assert!(self.inline_box_tree.len() <= u32::MAX as usize); + + let index_in_inline_boxes = self.inline_boxes.len() as u32; + let index_of_start_in_tree = self.inline_box_tree.len() as u32; + + let identifier = InlineBoxIdentifier { + index_of_start_in_tree, + index_in_inline_boxes, + }; + inline_box.identifier = identifier; + + self.inline_boxes.push(ArcRefCell::new(inline_box)); + self.inline_box_tree + .push(InlineBoxTreePathToken::Start(identifier)); + identifier + } + + pub(super) fn get_path( + &self, + from: Option<InlineBoxIdentifier>, + to: InlineBoxIdentifier, + ) -> IntoIter<InlineBoxTreePathToken> { + if from == Some(to) { + return Vec::new().into_iter(); + } + + let mut from_index = match from { + Some(InlineBoxIdentifier { + index_of_start_in_tree, + .. + }) => index_of_start_in_tree as usize, + None => 0, + }; + let mut to_index = to.index_of_start_in_tree as usize; + let is_reversed = to_index < from_index; + + // Do not include the first or final token, depending on direction. These can be equal + // if we are starting or going to the the root of the inline formatting context, in which + // case we don't want to adjust. + if to_index > from_index && from.is_some() { + from_index += 1; + } else if to_index < from_index { + to_index += 1; + } + + let mut path = Vec::with_capacity(from_index.abs_diff(to_index)); + let min = from_index.min(to_index); + let max = from_index.max(to_index); + + for token in &self.inline_box_tree[min..=max] { + // Skip useless recursion into inline boxes; we are looking for a direct path. + if Some(&token.reverse()) == path.last() { + path.pop(); + } else { + path.push(*token); + } + } + + if is_reversed { + path.reverse(); + for token in path.iter_mut() { + *token = token.reverse(); + } + } + + path.into_iter() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize)] +pub(super) enum InlineBoxTreePathToken { + Start(InlineBoxIdentifier), + End(InlineBoxIdentifier), +} + +impl InlineBoxTreePathToken { + fn reverse(&self) -> Self { + match self { + Self::Start(index) => Self::End(*index), + Self::End(index) => Self::Start(*index), + } + } +} + +/// An identifier for a particular [`InlineBox`] to be used to fetch it from an [`InlineBoxes`] +/// store of inline boxes. +/// +/// [`u32`] is used for the index, in order to save space. The value refers to the token +/// in the start tree data structure which can be fetched to find the actual index of +/// of the [`InlineBox`] in [`InlineBoxes::inline_boxes`]. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Serialize)] +pub(crate) struct InlineBoxIdentifier { + pub index_of_start_in_tree: u32, + pub index_in_inline_boxes: u32, +} + +pub(super) struct InlineBoxContainerState { + /// The container state common to both [`InlineBox`] and the root of the + /// [`InlineFormattingContext`]. + pub base: InlineContainerState, + + /// The [`InlineBoxIdentifier`] of this inline container state. If this is the root + /// the identifier is [`None`]. + pub identifier: InlineBoxIdentifier, + + /// The [`BaseFragmentInfo`] of the [`InlineBox`] that this state tracks. + pub base_fragment_info: BaseFragmentInfo, + + /// The [`PaddingBorderMargin`] of the [`InlineBox`] that this state tracks. + pub pbm: PaddingBorderMargin, + + /// Whether this is the last fragment of this InlineBox. This may not be the case if + /// the InlineBox is split due to an block-in-inline-split and this is not the last of + /// that split. + pub is_last_fragment: bool, +} + +impl InlineBoxContainerState { + pub(super) fn new( + inline_box: &InlineBox, + containing_block: &ContainingBlock, + layout_context: &LayoutContext, + parent_container: &InlineContainerState, + is_last_fragment: bool, + font_metrics: Option<&FontMetrics>, + ) -> Self { + let style = inline_box.style.clone(); + let pbm = style.padding_border_margin(containing_block); + + let mut flags = InlineContainerStateFlags::empty(); + if inline_container_needs_strut(&style, layout_context, Some(&pbm)) { + flags.insert(InlineContainerStateFlags::CREATE_STRUT); + } + + Self { + base: InlineContainerState::new( + style, + flags, + Some(parent_container), + parent_container.text_decoration_line, + font_metrics, + ), + identifier: inline_box.identifier, + base_fragment_info: inline_box.base_fragment_info, + pbm, + is_last_fragment, + } + } + + pub(super) fn calculate_space_above_baseline(&self) -> Au { + let (ascent, descent, line_gap) = ( + self.base.font_metrics.ascent, + self.base.font_metrics.descent, + self.base.font_metrics.line_gap, + ); + let leading = line_gap - (ascent + descent); + leading.scale_by(0.5) + ascent + } +} diff --git a/components/layout_2020/flow/inline/line.rs b/components/layout_2020/flow/inline/line.rs index 08d645fcb23..57d6c183082 100644 --- a/components/layout_2020/flow/inline/line.rs +++ b/components/layout_2020/flow/inline/line.rs @@ -2,10 +2,11 @@ * 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 std::rc::Rc; use std::vec::IntoIter; use app_units::Au; -use atomic_refcell::AtomicRef; +use bitflags::bitflags; use fonts::{FontMetrics, GlyphStore}; use servo_arc::Arc; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; @@ -15,366 +16,373 @@ use style::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword}; use style::values::generics::font::LineHeight; use style::values::specified::box_::DisplayOutside; use style::values::specified::text::TextDecorationLine; +use style::values::Either; use style::Zero; use webrender_api::FontInstanceKey; +use super::inline_box::{ + InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken, InlineBoxes, +}; +use super::{InlineFormattingContextState, LineBlockSizes}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::fragment_tree::{ - BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, HoistedSharedFragment, - TextFragment, + BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment, }; use crate::geom::{LogicalRect, LogicalVec2}; use crate::positioned::{ relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, }; -use crate::style_ext::PaddingBorderMargin; use crate::ContainingBlock; pub(super) struct LineMetrics { /// The block offset of the line start in the containing /// [`crate::flow::InlineFormattingContext`]. - pub block_offset: Length, + pub block_offset: Au, /// The block size of this line. - pub block_size: Length, + pub block_size: Au, /// The block offset of this line's baseline from [`Self::block_offset`]. pub baseline_block_offset: Au, } -/// State used when laying out the [`LineItem`]s collected for the line currently being -/// laid out. -pub(super) struct LineItemLayoutState<'a> { - pub inline_position: Length, +bitflags! { + struct LineLayoutInlineContainerFlags: u8 { + /// Whether or not any line items were processed for this inline box, this includes + /// any child inline boxes. + const HAD_ANY_LINE_ITEMS = 1 << 0; + /// Whether or not the starting inline border, padding, or margin of the inline box + /// was encountered. + const HAD_START_PBM = 1 << 2; + /// Whether or not the ending inline border, padding, or margin of the inline box + /// was encountered. + const HAD_END_PBM = 1 << 3; + /// Whether or not any floats were encountered while laying out this inline box. + const HAD_ANY_FLOATS = 1 << 4; + } +} + +/// The state used when laying out a collection of [`LineItem`]s into a line. This state is stored +/// per-inline container. For instance, when laying out the conents of a `<span>` a fresh +/// [`LineItemLayoutInlineContainerState`] is pushed onto [`LineItemLayout`]'s stack of states. +pub(super) struct LineItemLayoutInlineContainerState { + /// If this inline container is not the root inline container, the identifier of the [`super::InlineBox`] + /// that is currently being laid out. + pub identifier: Option<InlineBoxIdentifier>, + + /// The fragments that are laid out into this inline container on a line. + pub fragments: Vec<Fragment>, + + /// The current inline adavnce of the layout in the coordinates of this inline box. + pub inline_advance: Au, + + /// Flags which track various features during layout. + flags: LineLayoutInlineContainerFlags, /// The offset of the parent, relative to the start position of the line. - pub parent_offset: LogicalVec2<Length>, + pub parent_offset: LogicalVec2<Au>, /// The block offset of the parent's baseline relative to the block start of the line. This /// is often the same as [`Self::parent_offset`], but can be different for the root /// element. pub baseline_offset: Au, - pub ifc_containing_block: &'a ContainingBlock<'a>, - pub positioning_context: &'a mut PositioningContext, - - /// The amount of space to add to each justification opportunity in order to implement - /// `text-align: justify`. - pub justification_adjustment: Length, - - /// The metrics of this line, which should remain constant throughout the - /// layout process. - pub line_metrics: &'a LineMetrics, -} - -pub(super) fn layout_line_items( - iterator: &mut IntoIter<LineItem>, - layout_context: &LayoutContext, - state: &mut LineItemLayoutState, - saw_end: &mut bool, -) -> Vec<Fragment> { - let mut fragments = vec![]; - while let Some(item) = iterator.next() { - match item { - LineItem::TextRun(text_line_item) => { - if let Some(fragment) = text_line_item.layout(state) { - fragments.push(Fragment::Text(fragment)); - } - }, - LineItem::StartInlineBox(box_line_item) => { - if let Some(fragment) = box_line_item.layout(iterator, layout_context, state) { - fragments.push(Fragment::Box(fragment)) - } - }, - LineItem::EndInlineBox => { - *saw_end = true; - break; - }, - LineItem::Atomic(atomic_line_item) => { - fragments.push(Fragment::Box(atomic_line_item.layout(state))); - }, - LineItem::AbsolutelyPositioned(absolute_line_item) => { - fragments.push(Fragment::AbsoluteOrFixedPositioned( - absolute_line_item.layout(state), - )); - }, - LineItem::Float(float_line_item) => { - fragments.push(Fragment::Float(float_line_item.layout(state))); - }, - } - } - fragments -} - -pub(super) enum LineItem { - TextRun(TextRunLineItem), - StartInlineBox(InlineBoxLineItem), - EndInlineBox, - Atomic(AtomicLineItem), - AbsolutelyPositioned(AbsolutelyPositionedLineItem), - Float(FloatLineItem), + /// If this inline box establishes a containing block for positioned elements, this + /// is a fresh positioning context to contain them. Otherwise, this holds the starting + /// offset in the *parent* positioning context so that static positions can be updated + /// at the end of layout. + pub positioning_context_or_start_offset_in_parent: + Either<PositioningContext, PositioningContextLength>, } -impl LineItem { - pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool { - match self { - LineItem::TextRun(ref mut item) => item.trim_whitespace_at_end(whitespace_trimmed), - LineItem::StartInlineBox(_) => true, - LineItem::EndInlineBox => true, - LineItem::Atomic(_) => false, - LineItem::AbsolutelyPositioned(_) => true, - LineItem::Float(_) => true, +impl LineItemLayoutInlineContainerState { + fn new( + identifier: Option<InlineBoxIdentifier>, + parent_offset: LogicalVec2<Au>, + baseline_offset: Au, + positioning_context_or_start_offset_in_parent: Either< + PositioningContext, + PositioningContextLength, + >, + ) -> Self { + Self { + identifier, + fragments: Vec::new(), + inline_advance: Au::zero(), + flags: LineLayoutInlineContainerFlags::empty(), + parent_offset, + baseline_offset, + positioning_context_or_start_offset_in_parent, } } - pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool { - match self { - LineItem::TextRun(ref mut item) => item.trim_whitespace_at_start(whitespace_trimmed), - LineItem::StartInlineBox(_) => true, - LineItem::EndInlineBox => true, - LineItem::Atomic(_) => false, - LineItem::AbsolutelyPositioned(_) => true, - LineItem::Float(_) => true, - } + fn root(starting_inline_advance: Au, baseline_offset: Au) -> Self { + let mut state = Self::new( + None, + LogicalVec2::zero(), + baseline_offset, + Either::Second(PositioningContextLength::zero()), + ); + state.inline_advance = starting_inline_advance; + state } } -pub(super) struct TextRunLineItem { - pub base_fragment_info: BaseFragmentInfo, - pub parent_style: Arc<ComputedValues>, - pub text: Vec<std::sync::Arc<GlyphStore>>, - pub font_metrics: FontMetrics, - pub font_key: FontInstanceKey, - pub text_decoration_line: TextDecorationLine, -} +/// The second phase of [`super::InlineFormattingContext`] layout: once items are gathered +/// for a line, we must lay them out and create fragments for them, properly positioning them +/// according to their baselines and also handling absolutely positioned children. +pub(super) struct LineItemLayout<'a> { + /// The set of [`super::InlineBox`]es for the [`super::InlineFormattingContext`]. This + /// does *not* include any state from during phase one of layout. + pub inline_boxes: &'a InlineBoxes, -impl TextRunLineItem { - fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool { - if matches!( - self.parent_style.get_inherited_text().white_space_collapse, - WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces - ) { - return false; - } + /// The set of [`super::InlineBoxContainerState`] from phase one of IFC layout. There is + /// one of these for every inline box, *not* for the root inline container. + pub inline_box_states: &'a [Rc<InlineBoxContainerState>], - let index_of_last_non_whitespace = self - .text - .iter() - .rev() - .position(|glyph| !glyph.is_whitespace()) - .map(|offset_from_end| self.text.len() - offset_from_end); + /// The set of [`super::LineItemLayoutInlineContainerState`] created while laying out items + /// on this line. This does not include the current level of recursion. + pub state_stack: Vec<LineItemLayoutInlineContainerState>, - let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0); - *whitespace_trimmed += self - .text - .drain(first_whitespace_index..) - .map(|glyph| Length::from(glyph.total_advance())) - .sum(); + /// The current [`super::LineItemLayoutInlineContainerState`]. + pub state: LineItemLayoutInlineContainerState, - // Only keep going if we only encountered whitespace. - index_of_last_non_whitespace.is_none() - } + /// The [`LayoutContext`] to use for laying out absolutely positioned line items. + pub layout_context: &'a LayoutContext<'a>, - fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool { - if matches!( - self.parent_style.get_inherited_text().white_space_collapse, - WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces - ) { - return false; - } + /// The root positioning context for this layout. + pub root_positioning_context: &'a mut PositioningContext, - let index_of_first_non_whitespace = self - .text - .iter() - .position(|glyph| !glyph.is_whitespace()) - .unwrap_or(self.text.len()); + /// The [`ContainingBlock`] of the parent [`super::InlineFormattingContext`] of the line being + /// laid out. + pub ifc_containing_block: &'a ContainingBlock<'a>, - *whitespace_trimmed += self - .text - .drain(0..index_of_first_non_whitespace) - .map(|glyph| Length::from(glyph.total_advance())) - .sum(); + /// The metrics of this line, which should remain constant throughout the + /// layout process. + pub line_metrics: LineMetrics, - // Only keep going if we only encountered whitespace. - self.text.is_empty() - } + /// The amount of space to add to each justification opportunity in order to implement + /// `text-align: justify`. + pub justification_adjustment: Au, +} - fn layout(self, state: &mut LineItemLayoutState) -> Option<TextFragment> { - if self.text.is_empty() { - return None; +impl<'a> LineItemLayout<'a> { + pub(super) fn layout_line_items( + state: &mut InlineFormattingContextState, + iterator: &mut IntoIter<LineItem>, + start_position: LogicalVec2<Au>, + effective_block_advance: &LineBlockSizes, + justification_adjustment: Au, + ) -> Vec<Fragment> { + let baseline_offset = effective_block_advance.find_baseline_offset(); + LineItemLayout { + inline_boxes: state.inline_boxes, + inline_box_states: &state.inline_box_states, + state_stack: Vec::new(), + root_positioning_context: state.positioning_context, + layout_context: state.layout_context, + state: LineItemLayoutInlineContainerState::root(start_position.inline, baseline_offset), + ifc_containing_block: state.containing_block, + line_metrics: LineMetrics { + block_offset: start_position.block, + block_size: effective_block_advance.resolve(), + baseline_block_offset: baseline_offset, + }, + justification_adjustment, } + .layout(iterator) + } - let mut number_of_justification_opportunities = 0; - let mut inline_advance: Length = self - .text - .iter() - .map(|glyph_store| { - number_of_justification_opportunities += glyph_store.total_word_separators(); - Length::from(glyph_store.total_advance()) - }) - .sum(); + /// Start and end inline boxes in tree order, so that it reflects the given inline box. + fn prepare_layout_for_inline_box(&mut self, new_inline_box: Option<InlineBoxIdentifier>) { + // Optimize the case where we are moving to the root of the inline box stack. + let Some(new_inline_box) = new_inline_box else { + while !self.state_stack.is_empty() { + self.end_inline_box(); + } + return; + }; - if !state.justification_adjustment.is_zero() { - inline_advance += - state.justification_adjustment * number_of_justification_opportunities as f32; + // Otherwise, follow the path given to us by our collection of inline boxes, so we know which + // inline boxes to start and end. + let path = self + .inline_boxes + .get_path(self.state.identifier, new_inline_box); + for token in path { + match token { + InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier), + InlineBoxTreePathToken::End(_) => self.end_inline_box(), + } } + } - // The block start of the TextRun is often zero (meaning it has the same font metrics as the - // inline box's strut), but for children of the inline formatting context root or for - // fallback fonts that use baseline relatve alignment, it might be different. - let start_corner = LogicalVec2 { - inline: state.inline_position, - block: (state.baseline_offset - self.font_metrics.ascent).into(), - } - state.parent_offset; + pub(super) fn layout(&mut self, iterator: &mut IntoIter<LineItem>) -> Vec<Fragment> { + for item in iterator.by_ref() { + // When preparing to lay out a new line item, start and end inline boxes, so that the current + // inline box state reflects the item's parent. Items in the line are not necessarily in tree + // order due to BiDi and other reordering so the inline box of the item could potentially be + // any in the inline formatting context. + self.prepare_layout_for_inline_box(item.inline_box_identifier()); + + self.state + .flags + .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS); + match item { + LineItem::StartInlineBoxPaddingBorderMargin(_) => { + self.state + .flags + .insert(LineLayoutInlineContainerFlags::HAD_START_PBM); + }, + LineItem::EndInlineBoxPaddingBorderMargin(_) => { + self.state + .flags + .insert(LineLayoutInlineContainerFlags::HAD_END_PBM); + }, + LineItem::TextRun(_, text_run) => self.layout_text_run(text_run), + LineItem::Atomic(_, atomic) => self.layout_atomic(atomic), + LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute), + LineItem::Float(_, float) => self.layout_float(float), + } + } - let rect = LogicalRect { - start_corner, - size: LogicalVec2 { - block: self.font_metrics.line_gap.into(), - inline: inline_advance, - }, - }; + // Move back to the root of the inline box tree, so that all boxes are ended. + self.prepare_layout_for_inline_box(None); + std::mem::take(&mut self.state.fragments) + } - state.inline_position += inline_advance; - Some(TextFragment { - base: self.base_fragment_info.into(), - parent_style: self.parent_style, - rect: rect.into(), - font_metrics: self.font_metrics, - font_key: self.font_key, - glyphs: self.text, - text_decoration_line: self.text_decoration_line, - justification_adjustment: state.justification_adjustment.into(), - }) + fn current_positioning_context_mut(&mut self) -> &mut PositioningContext { + if let Either::First(ref mut positioning_context) = + self.state.positioning_context_or_start_offset_in_parent + { + return positioning_context; + } + self.state_stack + .iter_mut() + .rev() + .find_map( + |state| match state.positioning_context_or_start_offset_in_parent { + Either::First(ref mut positioning_context) => Some(positioning_context), + Either::Second(_) => None, + }, + ) + .unwrap_or(self.root_positioning_context) } -} -#[derive(Clone)] -pub(super) struct InlineBoxLineItem { - pub base_fragment_info: BaseFragmentInfo, - pub style: Arc<ComputedValues>, - pub pbm: PaddingBorderMargin, + fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) { + let inline_box_state = &*self.inline_box_states[identifier.index_in_inline_boxes as usize]; + let inline_box = self.inline_boxes.get(identifier); + let inline_box = &*(inline_box.borrow()); - /// Whether this is the first fragment for this inline box. This means that it's the - /// first potentially split box of a block-in-inline-split (or only if there's no - /// split) and also the first appearance of this fragment on any line. - pub is_first_fragment: bool, + let style = &inline_box.style; + let space_above_baseline = inline_box_state.calculate_space_above_baseline(); + let block_start_offset = + self.calculate_inline_box_block_start(inline_box_state, space_above_baseline); - /// Whether this is the last fragment for this inline box. This means that it's the - /// last potentially split box of a block-in-inline-split (or the only fragment if - /// there's no split). - pub is_last_fragment_of_ib_split: bool, + let positioning_context_or_start_offset_in_parent = + match PositioningContext::new_for_style(style) { + Some(positioning_context) => Either::First(positioning_context), + None => Either::Second(self.current_positioning_context_mut().len()), + }; - /// The FontMetrics for the default font used in this inline box. - pub font_metrics: FontMetrics, + let parent_offset = LogicalVec2 { + inline: self.state.inline_advance + self.state.parent_offset.inline, + block: block_start_offset, + }; - /// The block offset of this baseline relative to the baseline of the line. This will be - /// zero for boxes with `vertical-align: top` and `vertical-align: bottom` since their - /// baselines are calculated late in layout. - pub baseline_offset: Au, -} + let outer_state = std::mem::replace( + &mut self.state, + LineItemLayoutInlineContainerState::new( + Some(*identifier), + parent_offset, + block_start_offset + space_above_baseline, + positioning_context_or_start_offset_in_parent, + ), + ); -impl InlineBoxLineItem { - fn layout( - self, - iterator: &mut IntoIter<LineItem>, - layout_context: &LayoutContext, - state: &mut LineItemLayoutState, - ) -> Option<BoxFragment> { - let style = self.style.clone(); - let mut padding = self.pbm.padding; - let mut border = self.pbm.border; - let mut margin = self.pbm.margin.auto_is(Au::zero); - - if !self.is_first_fragment { + self.state_stack.push(outer_state); + } + + fn end_inline_box(&mut self) { + let outer_state = self.state_stack.pop().expect("Ended unknown inline box 11"); + let mut inner_state = std::mem::replace(&mut self.state, outer_state); + + let identifier = inner_state.identifier.expect("Ended unknown inline box 22"); + let inline_box_state = &*self.inline_box_states[identifier.index_in_inline_boxes as usize]; + let inline_box = self.inline_boxes.get(&identifier); + let inline_box = &*(inline_box.borrow()); + + let mut padding = inline_box_state.pbm.padding; + let mut border = inline_box_state.pbm.border; + let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero); + if !inner_state + .flags + .contains(LineLayoutInlineContainerFlags::HAD_START_PBM) + { padding.inline_start = Au::zero(); border.inline_start = Au::zero(); margin.inline_start = Au::zero(); } - if !self.is_last_fragment_of_ib_split { + if !inner_state + .flags + .contains(LineLayoutInlineContainerFlags::HAD_END_PBM) + { padding.inline_end = Au::zero(); border.inline_end = Au::zero(); margin.inline_end = Au::zero(); } - let pbm_sums = padding + border + margin; - state.inline_position += pbm_sums.inline_start.into(); - - let space_above_baseline = self.calculate_space_above_baseline(); - let block_start_offset = self.calculate_block_start(state, space_above_baseline); - - let mut positioning_context = PositioningContext::new_for_style(&style); - let nested_positioning_context = match positioning_context.as_mut() { - Some(positioning_context) => positioning_context, - None => &mut state.positioning_context, - }; - let original_nested_positioning_context_length = nested_positioning_context.len(); - let mut nested_state = LineItemLayoutState { - inline_position: state.inline_position, - parent_offset: LogicalVec2 { - inline: state.inline_position, - block: block_start_offset.into(), - }, - ifc_containing_block: state.ifc_containing_block, - positioning_context: nested_positioning_context, - justification_adjustment: state.justification_adjustment, - line_metrics: state.line_metrics, - baseline_offset: block_start_offset + space_above_baseline, - }; - - let mut saw_end = false; - let fragments = - layout_line_items(iterator, layout_context, &mut nested_state, &mut saw_end); - - // Only add ending padding, border, margin if this is the last fragment of a - // potential block-in-inline split and this line included the actual end of this - // fragment (it doesn't continue on the next line). - if !self.is_last_fragment_of_ib_split || !saw_end { - padding.inline_end = Au::zero(); - border.inline_end = Au::zero(); - margin.inline_end = Au::zero(); - } + // If the inline box didn't have any content at all and it isn't the first fragment for + // an element (needed for layout queries currently) and it didn't have any padding, border, + // or margin do not make a fragment for it. + // + // Note: This is an optimization, but also has side effects. Any fragments on a line will + // force the baseline to advance in the parent IFC. let pbm_sums = padding + border + margin; - - // If the inline box didn't have any content at all, don't add a Fragment for it. - let box_has_padding_border_or_margin = pbm_sums.inline_sum() > Au::zero(); - let box_had_absolutes = - original_nested_positioning_context_length != nested_state.positioning_context.len(); - if !self.is_first_fragment && - fragments.is_empty() && - !box_has_padding_border_or_margin && - !box_had_absolutes + if inner_state.fragments.is_empty() && + !inner_state + .flags + .contains(LineLayoutInlineContainerFlags::HAD_START_PBM) && + pbm_sums.inline_sum().is_zero() { - return None; + return; } + // Make `content_rect` relative to the parent Fragment. let mut content_rect = LogicalRect { start_corner: LogicalVec2 { - inline: state.inline_position, - block: block_start_offset.into(), + inline: self.state.inline_advance + pbm_sums.inline_start, + block: inner_state.parent_offset.block - self.state.parent_offset.block, }, size: LogicalVec2 { - inline: nested_state.inline_position - state.inline_position, - block: self.font_metrics.line_gap.into(), + inline: inner_state.inline_advance, + block: inline_box_state.base.font_metrics.line_gap, }, }; - // Make `content_rect` relative to the parent Fragment. - content_rect.start_corner -= state.parent_offset; + if inner_state + .flags + .contains(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS) + { + for fragment in inner_state.fragments.iter_mut() { + if let Fragment::Float(box_fragment) = fragment { + box_fragment.content_rect.start_corner -= pbm_sums.start_offset(); + } + } + } // Relative adjustment should not affect the rest of line layout, so we can // do it right before creating the Fragment. + let style = &inline_box.style; if style.clone_position().is_relative() { - content_rect.start_corner += - relative_adjustement(&style, state.ifc_containing_block).into(); + content_rect.start_corner += relative_adjustement(style, self.ifc_containing_block); } let mut fragment = BoxFragment::new( - self.base_fragment_info, - self.style.clone(), - fragments, - content_rect.into(), + inline_box.base_fragment_info, + style.clone(), + inner_state.fragments, + content_rect, padding, border, margin, @@ -382,136 +390,139 @@ impl InlineBoxLineItem { CollapsedBlockMargins::zero(), ); - state.inline_position = nested_state.inline_position + pbm_sums.inline_end.into(); - - if let Some(mut positioning_context) = positioning_context.take() { - assert!(original_nested_positioning_context_length == PositioningContextLength::zero()); - positioning_context.layout_collected_children(layout_context, &mut fragment); - positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( - &fragment.content_rect.start_corner, - PositioningContextLength::zero(), - ); - state.positioning_context.append(positioning_context); - } else { - state - .positioning_context - .adjust_static_position_of_hoisted_fragments_with_offset( + match inner_state.positioning_context_or_start_offset_in_parent { + Either::First(mut positioning_context) => { + positioning_context.layout_collected_children(self.layout_context, &mut fragment); + positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( &fragment.content_rect.start_corner, - original_nested_positioning_context_length, + PositioningContextLength::zero(), ); + self.current_positioning_context_mut() + .append(positioning_context); + }, + Either::Second(start_offset) => { + self.current_positioning_context_mut() + .adjust_static_position_of_hoisted_fragments_with_offset( + &fragment.content_rect.start_corner, + start_offset, + ); + }, } - Some(fragment) + self.state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum(); + self.state.fragments.push(Fragment::Box(fragment)); } - /// Given our font metrics, calculate the space above the baseline we need for our content. - /// Note that this space does not include space for any content in child inline boxes, as - /// they are not included in our content rect. - fn calculate_space_above_baseline(&self) -> Au { - let (ascent, descent, line_gap) = ( - self.font_metrics.ascent, - self.font_metrics.descent, - self.font_metrics.line_gap, - ); - let leading = line_gap - (ascent + descent); - leading.scale_by(0.5) + ascent - } - - /// Given the state for a line item layout and the space above the baseline for this inline - /// box, find the block start position relative to the line block start position. - fn calculate_block_start(&self, state: &LineItemLayoutState, space_above_baseline: Au) -> Au { - let line_gap = self.font_metrics.line_gap; + fn calculate_inline_box_block_start( + &self, + inline_box_state: &InlineBoxContainerState, + space_above_baseline: Au, + ) -> Au { + let font_metrics = &inline_box_state.base.font_metrics; + let style = &inline_box_state.base.style; + let line_gap = font_metrics.line_gap; // The baseline offset that we have in `Self::baseline_offset` is relative to the line // baseline, so we need to make it relative to the line block start. - match self.style.clone_vertical_align() { + match inline_box_state.base.style.clone_vertical_align() { GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => { - let line_height: Au = line_height(&self.style, &self.font_metrics).into(); + let line_height: Au = line_height(style, font_metrics).into(); (line_height - line_gap).scale_by(0.5) }, GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => { - let line_height: Au = line_height(&self.style, &self.font_metrics).into(); + let line_height: Au = line_height(style, font_metrics).into(); let half_leading = (line_height - line_gap).scale_by(0.5); - Au::from(state.line_metrics.block_size) - line_height + half_leading + self.line_metrics.block_size - line_height + half_leading }, _ => { - state.line_metrics.baseline_block_offset + self.baseline_offset - + self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset - space_above_baseline }, } } -} -pub(super) struct AtomicLineItem { - pub fragment: BoxFragment, - pub size: LogicalVec2<Au>, - pub positioning_context: Option<PositioningContext>, + fn layout_text_run(&mut self, text_item: TextRunLineItem) { + if text_item.text.is_empty() { + return; + } - /// The block offset of this items' baseline relative to the baseline of the line. - /// This will be zero for boxes with `vertical-align: top` and `vertical-align: - /// bottom` since their baselines are calculated late in layout. - pub baseline_offset_in_parent: Au, + let mut number_of_justification_opportunities = 0; + let mut inline_advance = text_item + .text + .iter() + .map(|glyph_store| { + number_of_justification_opportunities += glyph_store.total_word_separators(); + glyph_store.total_advance() + }) + .sum(); - /// The offset of the baseline inside this item. - pub baseline_offset_in_item: Au, -} + if !self.justification_adjustment.is_zero() { + inline_advance += self + .justification_adjustment + .scale_by(number_of_justification_opportunities as f32); + } -impl AtomicLineItem { - fn layout(mut self, state: &mut LineItemLayoutState) -> BoxFragment { + // The block start of the TextRun is often zero (meaning it has the same font metrics as the + // inline box's strut), but for children of the inline formatting context root or for + // fallback fonts that use baseline relative alignment, it might be different. + let start_corner = LogicalVec2 { + inline: self.state.inline_advance, + block: self.state.baseline_offset - + text_item.font_metrics.ascent - + self.state.parent_offset.block, + }; + + let rect = LogicalRect { + start_corner, + size: LogicalVec2 { + block: text_item.font_metrics.line_gap, + inline: inline_advance, + }, + }; + + self.state.inline_advance += inline_advance; + self.state.fragments.push(Fragment::Text(TextFragment { + base: text_item.base_fragment_info.into(), + parent_style: text_item.parent_style, + rect, + font_metrics: text_item.font_metrics, + font_key: text_item.font_key, + glyphs: text_item.text, + text_decoration_line: text_item.text_decoration_line, + justification_adjustment: self.justification_adjustment, + })); + } + + fn layout_atomic(&mut self, mut atomic: AtomicLineItem) { // The initial `start_corner` of the Fragment is only the PaddingBorderMargin sum start // offset, which is the sum of the start component of the padding, border, and margin. // This needs to be added to the calculated block and inline positions. - self.fragment.content_rect.start_corner.inline += state.inline_position.into(); - self.fragment.content_rect.start_corner.block += - self.calculate_block_start(state.line_metrics).into(); - // Make the final result relative to the parent box. - self.fragment.content_rect.start_corner -= state.parent_offset.into(); + atomic.fragment.content_rect.start_corner.inline += self.state.inline_advance; + atomic.fragment.content_rect.start_corner.block += + atomic.calculate_block_start(&self.line_metrics) - self.state.parent_offset.block; - if self.fragment.style.clone_position().is_relative() { - self.fragment.content_rect.start_corner += - relative_adjustement(&self.fragment.style, state.ifc_containing_block); + if atomic.fragment.style.clone_position().is_relative() { + atomic.fragment.content_rect.start_corner += + relative_adjustement(&atomic.fragment.style, self.ifc_containing_block); } - state.inline_position += self.size.inline.into(); - - if let Some(mut positioning_context) = self.positioning_context { + if let Some(mut positioning_context) = atomic.positioning_context { positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( - &self.fragment.content_rect.start_corner, + &atomic.fragment.content_rect.start_corner, PositioningContextLength::zero(), ); - state.positioning_context.append(positioning_context); + self.current_positioning_context_mut() + .append(positioning_context); } - self.fragment + self.state.inline_advance += atomic.size.inline; + self.state.fragments.push(Fragment::Box(atomic.fragment)); } - /// Given the metrics for a line, our vertical alignment, and our block size, find a block start - /// position relative to the top of the line. - fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Length { - match self.fragment.style.clone_vertical_align() { - GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => Length::zero(), - GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => { - line_metrics.block_size - self.size.block.into() - }, - - // This covers all baseline-relative vertical alignment. - _ => { - let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent; - Length::from(baseline - self.baseline_offset_in_item) - }, - } - } -} - -pub(super) struct AbsolutelyPositionedLineItem { - pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>, -} - -impl AbsolutelyPositionedLineItem { - fn layout(self, state: &mut LineItemLayoutState) -> ArcRefCell<HoistedSharedFragment> { - let box_ = self.absolutely_positioned_box; - let style = AtomicRef::map(box_.borrow(), |box_| box_.context.style()); + fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) { + let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow(); + let style = absolutely_positioned_box.context.style(); // From https://drafts.csswg.org/css2/#abs-non-replaced-width // > The static-position containing block is the containing block of a @@ -529,52 +540,198 @@ impl AbsolutelyPositionedLineItem { if style.get_box().original_display.outside() == DisplayOutside::Inline { // Top of the line at the current inline position. LogicalVec2 { - inline: state.inline_position - state.parent_offset.inline, - block: -state.parent_offset.block, + inline: self.state.inline_advance, + block: -self.state.parent_offset.block, } } else { // After the bottom of the line at the start of the inline formatting context. LogicalVec2 { - inline: Length::zero(), - block: state.line_metrics.block_size - state.parent_offset.block, + inline: -self.state.parent_offset.inline, + block: self.line_metrics.block_size - self.state.parent_offset.block, } }; let hoisted_box = AbsolutelyPositionedBox::to_hoisted( - box_.clone(), - initial_start_corner, - state.ifc_containing_block, + absolute.absolutely_positioned_box.clone(), + initial_start_corner.into(), + self.ifc_containing_block, ); let hoisted_fragment = hoisted_box.fragment.clone(); - state.positioning_context.push(hoisted_box); - hoisted_fragment + self.current_positioning_context_mut().push(hoisted_box); + self.state + .fragments + .push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)); } -} -pub(super) struct FloatLineItem { - pub fragment: BoxFragment, - /// Whether or not this float Fragment has been placed yet. Fragments that - /// do not fit on a line need to be placed after the hypothetical block start - /// of the next line. - pub needs_placement: bool, -} + fn layout_float(&mut self, mut float: FloatLineItem) { + self.state + .flags + .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS); -impl FloatLineItem { - fn layout(mut self, state: &mut LineItemLayoutState<'_>) -> BoxFragment { // The `BoxFragment` for this float is positioned relative to the IFC, so we need - // to move it to be positioned relative to our parent InlineBox line item. Floats + // to move it to be positioned relative to our parent InlineBox line item. Float // fragments are children of these InlineBoxes and not children of the inline // formatting context, so that they are parented properly for StackingContext // properties such as opacity & filters. let distance_from_parent_to_ifc = LogicalVec2 { - inline: state.parent_offset.inline, - block: state.line_metrics.block_offset + state.parent_offset.block, + inline: self.state.parent_offset.inline, + block: self.line_metrics.block_offset + self.state.parent_offset.block, }; - self.fragment.content_rect.start_corner -= distance_from_parent_to_ifc.into(); - self.fragment + float.fragment.content_rect.start_corner -= distance_from_parent_to_ifc; + self.state.fragments.push(Fragment::Float(float.fragment)); + } +} + +pub(super) enum LineItem { + StartInlineBoxPaddingBorderMargin(InlineBoxIdentifier), + EndInlineBoxPaddingBorderMargin(InlineBoxIdentifier), + TextRun(Option<InlineBoxIdentifier>, TextRunLineItem), + Atomic(Option<InlineBoxIdentifier>, AtomicLineItem), + AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem), + Float(Option<InlineBoxIdentifier>, FloatLineItem), +} + +impl LineItem { + fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> { + match self { + LineItem::StartInlineBoxPaddingBorderMargin(identifier) => Some(*identifier), + LineItem::EndInlineBoxPaddingBorderMargin(identifier) => Some(*identifier), + LineItem::TextRun(identifier, _) => *identifier, + LineItem::Atomic(identifier, _) => *identifier, + LineItem::AbsolutelyPositioned(identifier, _) => *identifier, + LineItem::Float(identifier, _) => *identifier, + } + } + + pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool { + match self { + LineItem::StartInlineBoxPaddingBorderMargin(_) => true, + LineItem::EndInlineBoxPaddingBorderMargin(_) => true, + LineItem::TextRun(_, ref mut item) => item.trim_whitespace_at_end(whitespace_trimmed), + LineItem::Atomic(..) => false, + LineItem::AbsolutelyPositioned(..) => true, + LineItem::Float(..) => true, + } + } + + pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool { + match self { + LineItem::StartInlineBoxPaddingBorderMargin(_) => true, + LineItem::EndInlineBoxPaddingBorderMargin(_) => true, + LineItem::TextRun(_, ref mut item) => item.trim_whitespace_at_start(whitespace_trimmed), + LineItem::Atomic(..) => false, + LineItem::AbsolutelyPositioned(..) => true, + LineItem::Float(..) => true, + } + } +} + +pub(super) struct TextRunLineItem { + pub base_fragment_info: BaseFragmentInfo, + pub parent_style: Arc<ComputedValues>, + pub text: Vec<std::sync::Arc<GlyphStore>>, + pub font_metrics: FontMetrics, + pub font_key: FontInstanceKey, + pub text_decoration_line: TextDecorationLine, +} + +impl TextRunLineItem { + fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool { + if matches!( + self.parent_style.get_inherited_text().white_space_collapse, + WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces + ) { + return false; + } + + let index_of_last_non_whitespace = self + .text + .iter() + .rev() + .position(|glyph| !glyph.is_whitespace()) + .map(|offset_from_end| self.text.len() - offset_from_end); + + let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0); + *whitespace_trimmed += self + .text + .drain(first_whitespace_index..) + .map(|glyph| Length::from(glyph.total_advance())) + .sum(); + + // Only keep going if we only encountered whitespace. + index_of_last_non_whitespace.is_none() + } + + fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool { + if matches!( + self.parent_style.get_inherited_text().white_space_collapse, + WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces + ) { + return false; + } + + let index_of_first_non_whitespace = self + .text + .iter() + .position(|glyph| !glyph.is_whitespace()) + .unwrap_or(self.text.len()); + + *whitespace_trimmed += self + .text + .drain(0..index_of_first_non_whitespace) + .map(|glyph| Length::from(glyph.total_advance())) + .sum(); + + // Only keep going if we only encountered whitespace. + self.text.is_empty() } } +pub(super) struct AtomicLineItem { + pub fragment: BoxFragment, + pub size: LogicalVec2<Au>, + pub positioning_context: Option<PositioningContext>, + + /// The block offset of this items' baseline relative to the baseline of the line. + /// This will be zero for boxes with `vertical-align: top` and `vertical-align: + /// bottom` since their baselines are calculated late in layout. + pub baseline_offset_in_parent: Au, + + /// The offset of the baseline inside this item. + pub baseline_offset_in_item: Au, +} + +impl AtomicLineItem { + /// Given the metrics for a line, our vertical alignment, and our block size, find a block start + /// position relative to the top of the line. + fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au { + match self.fragment.style.clone_vertical_align() { + GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => Au::zero(), + GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => { + line_metrics.block_size - self.size.block + }, + + // This covers all baseline-relative vertical alignment. + _ => { + let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent; + baseline - self.baseline_offset_in_item + }, + } + } +} + +pub(super) struct AbsolutelyPositionedLineItem { + pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>, +} + +pub(super) struct FloatLineItem { + pub fragment: BoxFragment, + /// Whether or not this float Fragment has been placed yet. Fragments that + /// do not fit on a line need to be placed after the hypothetical block start + /// of the next line. + pub needs_placement: bool, +} + fn line_height(parent_style: &ComputedValues, font_metrics: &FontMetrics) -> Length { let font = parent_style.get_font(); let font_size = font.font_size.computed_size(); diff --git a/components/layout_2020/flow/inline/mod.rs b/components/layout_2020/flow/inline/mod.rs index 9a890865b0f..ebd04694999 100644 --- a/components/layout_2020/flow/inline/mod.rs +++ b/components/layout_2020/flow/inline/mod.rs @@ -48,9 +48,9 @@ //! a linear series of items that describe the line's hierarchy of inline boxes and content. The //! item types are: //! +//! - [`LineItem::StartInlineBoxPaddingBorderMargin`] +//! - [`LineItem::EndInlineBoxPaddingBorderMargin`] //! - [`LineItem::TextRun`] -//! - [`LineItem::StartInlineBox`] -//! - [`LineItem::EndInlineBox`] //! - [`LineItem::Atomic`] //! - [`LineItem::AbsolutelyPositioned`] //! - [`LineItem::Float`] @@ -69,20 +69,23 @@ //! pub mod construct; +pub mod inline_box; pub mod line; mod line_breaker; pub mod text_run; -use std::cell::OnceCell; +use std::cell::{OnceCell, RefCell}; use std::mem; +use std::rc::Rc; use app_units::Au; use bitflags::bitflags; use construct::InlineFormattingContextBuilder; use fonts::{FontMetrics, GlyphStore}; +use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes}; use line::{ - layout_line_items, AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, - InlineBoxLineItem, LineItem, LineItemLayoutState, LineMetrics, TextRunLineItem, + AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout, + TextRunLineItem, }; use line_breaker::LineBreaker; use serde::Serialize; @@ -107,15 +110,13 @@ use webrender_api::FontInstanceKey; use super::float::PlacementAmongFloats; use crate::cell::ArcRefCell; use crate::context::LayoutContext; -use crate::dom::NodeExt; -use crate::dom_traversal::NodeAndStyleInfo; use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::flow::{CollapsibleWithParentStartMargin, FlowLayout}; use crate::formatting_contexts::{ Baselines, IndependentFormattingContext, NonReplacedFormattingContextContents, }; use crate::fragment_tree::{ - BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, + BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment, }; use crate::geom::{LogicalRect, LogicalVec2}; @@ -178,42 +179,6 @@ pub(crate) enum InlineItem { Atomic(IndependentFormattingContext), } -#[derive(Debug, Serialize)] -pub(crate) struct InlineBox { - pub base_fragment_info: BaseFragmentInfo, - #[serde(skip_serializing)] - pub style: Arc<ComputedValues>, - /// The identifier of this inline box in the containing [`InlineFormattingContext`]. - identifier: InlineBoxIdentifier, - pub is_first_fragment: bool, - pub is_last_fragment: bool, - /// The index of the default font in the [`InlineFormattingContext`]'s font metrics store. - /// This is initialized during IFC shaping. - pub default_font_index: Option<usize>, -} - -impl InlineBox { - pub(crate) fn new<'dom, Node: NodeExt<'dom>>(info: &NodeAndStyleInfo<Node>) -> Self { - Self { - base_fragment_info: info.into(), - style: info.style.clone(), - identifier: InlineBoxIdentifier::root(), - is_first_fragment: true, - is_last_fragment: false, - default_font_index: None, - } - } - - pub(crate) fn split_around_block(&self) -> Self { - Self { - style: self.style.clone(), - is_first_fragment: false, - is_last_fragment: false, - ..*self - } - } -} - /// Information about the current line under construction for a particular /// [`InlineFormattingContextState`]. This tracks position and size information while /// [`LineItem`]s are collected and is used as input when those [`LineItem`]s are @@ -300,7 +265,7 @@ impl LineUnderConstruction { self.line_items .iter() .filter_map(|item| match item { - LineItem::TextRun(text_run) => Some( + LineItem::TextRun(_, text_run) => Some( text_run .text .iter() @@ -523,58 +488,6 @@ impl UnbreakableSegmentUnderConstruction { } self.inline_size -= whitespace_trimmed; } - - /// Prepare this segment for placement on a new and empty line. This happens when the - /// segment is too large to fit on the current line and needs to be placed on a new - /// one. - fn prepare_for_placement_on_empty_line( - &mut self, - line: &LineUnderConstruction, - current_hierarchy_depth: usize, - ) { - self.trim_leading_whitespace(); - - // The segment may start in the middle of an already processed inline box. In that - // case we need to duplicate the `StartInlineBox` tokens as a prefix of the new - // lines. For instance if the following segment is going to be placed on a new line: - // - // line = [StartInlineBox "every"] - // segment = ["good" EndInlineBox "boy"] - // - // Then the segment must be prefixed with `StartInlineBox` before it is committed - // to the empty line. - let mut hierarchy_depth = self - .inline_box_hierarchy_depth - .unwrap_or(current_hierarchy_depth); - if hierarchy_depth == 0 { - return; - } - let mut hierarchy = Vec::new(); - let mut skip_depth = 0; - for item in line.line_items.iter().rev() { - match item { - // We need to skip over any inline boxes that are not in our hierarchy. If - // any inline box ends, we skip until it starts. - LineItem::StartInlineBox(_) if skip_depth > 0 => skip_depth -= 1, - LineItem::EndInlineBox => skip_depth += 1, - - // Otherwise copy the inline box to the hierarchy we are collecting. - LineItem::StartInlineBox(inline_box) => { - let mut cloned_inline_box = inline_box.clone(); - cloned_inline_box.is_first_fragment = false; - hierarchy.push(LineItem::StartInlineBox(cloned_inline_box)); - hierarchy_depth -= 1; - if hierarchy_depth == 0 { - break; - } - }, - _ => {}, - } - } - - let segment_items = mem::take(&mut self.line_items); - self.line_items = hierarchy.into_iter().rev().chain(segment_items).collect(); - } } bitflags! { @@ -584,7 +497,7 @@ bitflags! { } } -struct InlineContainerState { +pub(super) struct InlineContainerState { /// The style of this inline container. style: Arc<ComputedValues>, @@ -593,7 +506,7 @@ struct InlineContainerState { /// Whether or not we have processed any content (an atomic element or text) for /// this inline box on the current line OR any previous line. - has_content: bool, + has_content: RefCell<bool>, /// Indicates whether this nesting level have text decorations in effect. /// From <https://drafts.csswg.org/css-text-decor/#line-decoration> @@ -617,35 +530,22 @@ struct InlineContainerState { /// `vertical-align` property a positive value indicates an offset "below" the /// baseline while a negative value indicates one "above" it (when the block direction /// is vertical). - baseline_offset: Au, + pub baseline_offset: Au, /// The font metrics of the non-fallback font for this container. font_metrics: FontMetrics, } -struct InlineBoxContainerState { - /// The container state common to both [`InlineBox`] and the root of the - /// [`InlineFormattingContext`]. - base: InlineContainerState, - - /// The [`BaseFragmentInfo`] of the [`InlineBox`] that this state tracks. - base_fragment_info: BaseFragmentInfo, - - /// The [`PaddingBorderMargin`] of the [`InlineBox`] that this state tracks. - pbm: PaddingBorderMargin, - - /// Whether this is the last fragment of this InlineBox. This may not be the case if - /// the InlineBox is split due to an block-in-inline-split and this is not the last of - /// that split. - is_last_fragment: bool, -} - pub(super) struct InlineFormattingContextState<'a, 'b> { positioning_context: &'a mut PositioningContext, containing_block: &'b ContainingBlock<'b>, sequential_layout_state: Option<&'a mut SequentialLayoutState>, layout_context: &'b LayoutContext<'b>, + /// The inline boxes collection of the [`InlineFormattingContext`] that this + /// state is laying out. + inline_boxes: &'a InlineBoxes, + /// The list of [`FontMetrics`] used by the [`InlineFormattingContext`] that /// we are laying out. fonts: &'a Vec<FontKeyAndMetrics>, @@ -664,7 +564,13 @@ pub(super) struct InlineFormattingContextState<'a, 'b> { /// A stack of [`InlineBoxContainerState`] that is used to produce [`LineItem`]s either when we /// reach the end of an inline box or when we reach the end of a line. Only at the end /// of the inline box is the state popped from the stack. - inline_box_state_stack: Vec<InlineBoxContainerState>, + inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>, + + /// A collection of [`InlineBoxContainerState`] of all the inlines that are present + /// in this inline formatting context. We keep this as well as the stack, so that we + /// can access them during line layout, which may happen after relevant [`InlineBoxContainerState`]s + /// have been popped of the the stack. + inline_box_states: Vec<Rc<InlineBoxContainerState>>, /// A vector of fragment that are laid out. This includes one [`Fragment::Positioning`] /// per line that is currently laid out plus fragments for all floats, which @@ -742,11 +648,10 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { } } - fn current_inline_container_state_mut(&mut self) -> &mut InlineContainerState { - match self.inline_box_state_stack.last_mut() { - Some(inline_box_state) => &mut inline_box_state.base, - None => &mut self.root_nesting_level, - } + fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> { + self.inline_box_state_stack + .last() + .map(|state| state.identifier) } fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes { @@ -780,7 +685,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { /// Start laying out a particular [`InlineBox`] into line items. This will push /// a new [`InlineBoxContainerState`] onto [`Self::inline_box_state_stack`]. fn start_inline_box(&mut self, inline_box: &InlineBox) { - let mut inline_box_state = InlineBoxContainerState::new( + let inline_box_state = InlineBoxContainerState::new( inline_box, self.containing_block, self.layout_context, @@ -813,12 +718,24 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { .margin .inline_start .auto_is(Au::zero) - .into() + .into(); + self.current_line_segment + .line_items + .push(LineItem::StartInlineBoxPaddingBorderMargin( + inline_box.identifier, + )); } - let line_item = inline_box_state - .layout_into_line_item(inline_box.is_first_fragment, inline_box.is_last_fragment); - self.push_line_item_to_unbreakable_segment(LineItem::StartInlineBox(line_item)); + let inline_box_state = Rc::new(inline_box_state); + + // Push the state onto the IFC-wide collection of states. Inline boxes are numbered in + // the order that they are encountered, so this should correspond to the order they + // are pushed onto `self.inline_box_states`. + assert_eq!( + self.inline_box_states.len(), + inline_box.identifier.index_in_inline_boxes as usize + ); + self.inline_box_states.push(inline_box_state.clone()); self.inline_box_state_stack.push(inline_box_state); } @@ -831,7 +748,6 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { None => return, // We are at the root. }; - self.push_line_item_to_unbreakable_segment(LineItem::EndInlineBox); self.current_line_segment .max_block_size .max_assign(&inline_box_state.base.nested_strut_block_sizes); @@ -840,7 +756,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { // the `white-space` property of its parent to future inline children. This is because // when a soft wrap opportunity is defined by the boundary between two elements, the // `white-space` used is that of their nearest common ancestor. - if inline_box_state.base.has_content { + if *inline_box_state.base.has_content.borrow() { self.propagate_current_nesting_level_white_space_style(); } @@ -854,6 +770,11 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { .auto_is(Au::zero) .into(); self.current_line_segment.inline_size += pbm_end; + self.current_line_segment + .line_items + .push(LineItem::EndInlineBoxPaddingBorderMargin( + inline_box_state.identifier, + )) } } @@ -920,47 +841,38 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { self.deferred_br_clear = Clear::None; } - let mut line_items = std::mem::take(&mut self.current_line.line_items); - if self.current_line.has_floats_waiting_to_be_placed { - place_pending_floats(self, &mut line_items); - } - // Set up the new line now that we no longer need the old one. - self.current_line = LineUnderConstruction::new(LogicalVec2 { - inline: Length::zero(), - block: block_end_position.into(), - }); + let mut line_to_layout = std::mem::replace( + &mut self.current_line, + LineUnderConstruction::new(LogicalVec2 { + inline: Length::zero(), + block: block_end_position.into(), + }), + ); - let baseline_offset = effective_block_advance.find_baseline_offset(); + if line_to_layout.has_floats_waiting_to_be_placed { + place_pending_floats(self, &mut line_to_layout.line_items); + } - let mut state = LineItemLayoutState { - inline_position: inline_start_position, - parent_offset: LogicalVec2::zero(), - baseline_offset, - ifc_containing_block: self.containing_block, - positioning_context: self.positioning_context, - justification_adjustment, - line_metrics: &LineMetrics { - block_offset: block_start_position.into(), - block_size: effective_block_advance.resolve().into(), - baseline_block_offset: baseline_offset, - }, + let start_position = LogicalVec2 { + block: block_start_position, + inline: inline_start_position, }; - let positioning_context_length = state.positioning_context.len(); - let mut saw_end = false; - let fragments = layout_line_items( - &mut line_items.into_iter(), - self.layout_context, - &mut state, - &mut saw_end, + let baseline_offset = effective_block_advance.find_baseline_offset(); + let start_positioning_context_length = self.positioning_context.len(); + let fragments = LineItemLayout::layout_line_items( + self, + &mut line_to_layout.line_items.into_iter(), + start_position, + &effective_block_advance, + justification_adjustment, ); - let line_had_content = - !fragments.is_empty() || state.positioning_context.len() != positioning_context_length; - // If the line doesn't have any fragments, we don't need to add a containing fragment for it. - if !line_had_content { + if fragments.is_empty() && + self.positioning_context.len() == start_positioning_context_length + { return; } @@ -982,11 +894,10 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { }, }; - state - .positioning_context + self.positioning_context .adjust_static_position_of_hoisted_fragments_with_offset( &line_rect.start_corner, - positioning_context_length, + start_positioning_context_length, ); self.fragments @@ -1005,7 +916,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { &self, whitespace_trimmed: Length, last_line_or_forced_line_break: bool, - ) -> (Length, Length) { + ) -> (Au, Au) { enum TextAlign { Start, Center, @@ -1099,7 +1010,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { // that case, do not make any adjustment for justification. let justification_adjustment = justification_adjustment.max(Length::zero()); - (adjusted_line_start, justification_adjustment) + (adjusted_line_start.into(), justification_adjustment.into()) } fn place_float_fragment(&mut self, fragment: &mut BoxFragment) { @@ -1392,22 +1303,29 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { }; self.update_unbreakable_segment_for_new_content(&strut_size, inline_advance, flags); + let current_inline_box_identifier = self.current_inline_box_identifier(); match self.current_line_segment.line_items.last_mut() { - Some(LineItem::TextRun(line_item)) if ifc_font_info.key == line_item.font_key => { + Some(LineItem::TextRun(inline_box_identifier, line_item)) + if ifc_font_info.key == line_item.font_key && + *inline_box_identifier == current_inline_box_identifier => + { line_item.text.push(glyph_store); return; }, _ => {}, } - self.push_line_item_to_unbreakable_segment(LineItem::TextRun(TextRunLineItem { - text: vec![glyph_store], - base_fragment_info: text_run.base_fragment_info, - parent_style: text_run.parent_style.clone(), - font_metrics, - font_key: ifc_font_info.key, - text_decoration_line: self.current_inline_container_state().text_decoration_line, - })); + self.push_line_item_to_unbreakable_segment(LineItem::TextRun( + current_inline_box_identifier, + TextRunLineItem { + text: vec![glyph_store], + base_fragment_info: text_run.base_fragment_info, + parent_style: text_run.parent_style.clone(), + font_metrics, + font_key: ifc_font_info.key, + text_decoration_line: self.current_inline_container_state().text_decoration_line, + }, + )); } fn update_unbreakable_segment_for_new_content( @@ -1441,17 +1359,15 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { self.current_line_segment.inline_size += inline_size; // Propagate the whitespace setting to the current nesting level. - let current_nesting_level = self.current_inline_container_state_mut(); - current_nesting_level.has_content = true; + *self + .current_inline_container_state() + .has_content + .borrow_mut() = true; self.propagate_current_nesting_level_white_space_style(); } fn process_line_break(&mut self, forced_line_break: bool) { - self.current_line_segment - .prepare_for_placement_on_empty_line( - &self.current_line, - self.inline_box_state_stack.len(), - ); + self.current_line_segment.trim_leading_whitespace(); self.finish_current_line_and_reset(forced_line_break); } @@ -1506,7 +1422,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { // Place all floats in this unbreakable segment. let mut segment_items = mem::take(&mut self.current_line_segment.line_items); for item in segment_items.iter_mut() { - if let LineItem::Float(float_item) = item { + if let LineItem::Float(_, float_item) = item { self.place_float_line_item_for_commit_to_line( float_item, line_inline_size_without_trailing_whitespace, @@ -1532,9 +1448,11 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { segment_items.first_mut(), ) { ( - Some(LineItem::TextRun(last_line_item)), - Some(LineItem::TextRun(first_segment_item)), - ) if last_line_item.font_key == first_segment_item.font_key => { + Some(LineItem::TextRun(last_inline_box_identifier, last_line_item)), + Some(LineItem::TextRun(first_inline_box_identifier, first_segment_item)), + ) if last_line_item.font_key == first_segment_item.font_key && + last_inline_box_identifier == first_inline_box_identifier => + { last_line_item.text.append(&mut first_segment_item.text); 1 }, @@ -1695,6 +1613,7 @@ impl InlineFormattingContext { containing_block, sequential_layout_state, layout_context, + inline_boxes: &self.inline_boxes, fonts: &self.font_metrics, fragments: Vec::new(), current_line: LineUnderConstruction::new(LogicalVec2 { @@ -1709,6 +1628,7 @@ impl InlineFormattingContext { default_font_metrics.as_ref(), ), inline_box_state_stack: Vec::new(), + inline_box_states: Vec::with_capacity(self.inline_boxes.len()), current_line_segment: UnbreakableSegmentUnderConstruction::new(), linebreak_before_new_content: false, deferred_br_clear: Clear::None, @@ -1747,6 +1667,7 @@ impl InlineFormattingContext { }, InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { ifc.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned( + ifc.current_inline_box_identifier(), AbsolutelyPositionedLineItem { absolutely_positioned_box: positioned_box.clone(), }, @@ -1819,7 +1740,7 @@ impl InlineContainerState { Self { style, flags, - has_content: false, + has_content: RefCell::new(false), text_decoration_line, nested_strut_block_sizes: nested_block_sizes, strut_block_sizes, @@ -1969,54 +1890,6 @@ impl InlineContainerState { } } -impl InlineBoxContainerState { - fn new( - inline_box: &InlineBox, - containing_block: &ContainingBlock, - layout_context: &LayoutContext, - parent_container: &InlineContainerState, - is_last_fragment: bool, - font_metrics: Option<&FontMetrics>, - ) -> Self { - let style = inline_box.style.clone(); - let pbm = style.padding_border_margin(containing_block); - - let mut flags = InlineContainerStateFlags::empty(); - if inline_container_needs_strut(&style, layout_context, Some(&pbm)) { - flags.insert(InlineContainerStateFlags::CREATE_STRUT); - } - - Self { - base: InlineContainerState::new( - style, - flags, - Some(parent_container), - parent_container.text_decoration_line, - font_metrics, - ), - base_fragment_info: inline_box.base_fragment_info, - pbm, - is_last_fragment, - } - } - - fn layout_into_line_item( - &mut self, - is_first_fragment: bool, - is_last_fragment_of_ib_split: bool, - ) -> InlineBoxLineItem { - InlineBoxLineItem { - base_fragment_info: self.base_fragment_info, - style: self.base.style.clone(), - pbm: self.pbm.clone(), - is_first_fragment, - is_last_fragment_of_ib_split, - font_metrics: self.base.font_metrics.clone(), - baseline_offset: self.base.baseline_offset, - } - } -} - impl IndependentFormattingContext { fn layout_into_line_items( &mut self, @@ -2169,13 +2042,16 @@ impl IndependentFormattingContext { size.inline.into(), SegmentContentFlags::empty(), ); - ifc.push_line_item_to_unbreakable_segment(LineItem::Atomic(AtomicLineItem { - fragment, - size, - positioning_context: child_positioning_context, - baseline_offset_in_parent, - baseline_offset_in_item: baseline_offset, - })); + ifc.push_line_item_to_unbreakable_segment(LineItem::Atomic( + ifc.current_inline_box_identifier(), + AtomicLineItem { + fragment, + size, + positioning_context: child_positioning_context, + baseline_offset_in_parent, + baseline_offset_in_item: baseline_offset, + }, + )); // Defer a soft wrap opportunity for when we next process text content. ifc.have_deferred_soft_wrap_opportunity = true; @@ -2245,16 +2121,19 @@ impl FloatBox { ifc.positioning_context, ifc.containing_block, ); - ifc.push_line_item_to_unbreakable_segment(LineItem::Float(FloatLineItem { - fragment, - needs_placement: true, - })); + ifc.push_line_item_to_unbreakable_segment(LineItem::Float( + ifc.current_inline_box_identifier(), + FloatLineItem { + fragment, + needs_placement: true, + }, + )); } } fn place_pending_floats(ifc: &mut InlineFormattingContextState, line_items: &mut [LineItem]) { for item in line_items.iter_mut() { - if let LineItem::Float(float_line_item) = item { + if let LineItem::Float(_, float_line_item) = item { if float_line_item.needs_placement { ifc.place_float_fragment(&mut float_line_item.fragment); } @@ -2387,7 +2266,7 @@ impl<'a> ContentSizesComputation<'a> { // for determining intrinsic size contributions. // https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution let inline_box = inline_formatting_context.inline_boxes.get(identifier); - let inline_box = inline_box.borrow(); + let inline_box = (*inline_box).borrow(); let zero = Length::zero(); let padding = inline_box .style @@ -2525,36 +2404,3 @@ impl<'a> ContentSizesComputation<'a> { .traverse(inline_formatting_context) } } - -#[derive(Debug, Default, Serialize)] -pub(crate) struct InlineBoxes { - inline_boxes: Vec<ArcRefCell<InlineBox>>, -} - -impl InlineBoxes { - pub(super) fn get(&self, identifier: &InlineBoxIdentifier) -> ArcRefCell<InlineBox> { - self.inline_boxes[identifier.index].clone() - } - - pub(super) fn end_inline_box(&mut self) {} - - pub(super) fn start_inline_box(&mut self, inline_box: InlineBox) -> InlineBoxIdentifier { - let identifier = InlineBoxIdentifier { - index: self.inline_boxes.len(), - }; - - self.inline_boxes.push(ArcRefCell::new(inline_box)); - identifier - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Serialize)] -pub(crate) struct InlineBoxIdentifier { - pub index: usize, -} - -impl InlineBoxIdentifier { - fn root() -> Self { - InlineBoxIdentifier { index: 0 } - } -} diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 184b73a233b..b23f76db69b 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -403,7 +403,7 @@ impl PositioningContext { } /// A data structure which stores the size of a positioning context. -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub(crate) struct PositioningContextLength { /// The number of boxes that will be hoisted the the nearest positioned ancestor for /// layout. |