aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout_2020/dom.rs3
-rw-r--r--components/layout_2020/flow/construct.rs3
-rw-r--r--components/layout_2020/flow/inline/construct.rs11
-rw-r--r--components/layout_2020/flow/inline/inline_box.rs245
-rw-r--r--components/layout_2020/flow/inline/line.rs933
-rw-r--r--components/layout_2020/flow/inline/mod.rs412
-rw-r--r--components/layout_2020/positioned.rs2
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.