layout: Clean up inline layout data structures (#33149)

- Rename `InlineFormattingContextState` to
  `InlineFormattingContextLayout`.
- Have `InlineFormattingContextLayout` hold a reference to the
 `InlineFormattingContext`, so that it does not need to be passed
 around as an argument
- Have `LineItemLayout` hold a reference to
  `InlineFormattingContextLayout` to avoid duplicating so much data.
- Rename some members of `LineItemLayout` to make it clearer what
  they do.
- Give beter names to many lifetimes and combine some that are
  effectively the same.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
This commit is contained in:
Martin Robinson 2024-08-22 05:05:15 -07:00 committed by GitHub
parent 60ef6bc461
commit e956b53827
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 154 additions and 194 deletions

View file

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::rc::Rc;
use app_units::Au; use app_units::Au;
use bitflags::bitflags; use bitflags::bitflags;
use fonts::{FontMetrics, GlyphStore}; use fonts::{FontMetrics, GlyphStore};
@ -20,12 +18,9 @@ use style::Zero;
use unicode_bidi::{BidiInfo, Level}; use unicode_bidi::{BidiInfo, Level};
use webrender_api::FontInstanceKey; use webrender_api::FontInstanceKey;
use super::inline_box::{ use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken, InlineBoxes, use super::{InlineFormattingContextLayout, LineBlockSizes};
};
use super::{InlineFormattingContextState, LineBlockSizes};
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::fragment_tree::{ use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment,
}; };
@ -33,7 +28,6 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{ use crate::positioned::{
relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength,
}; };
use crate::ContainingBlock;
pub(super) struct LineMetrics { pub(super) struct LineMetrics {
/// The block offset of the line start in the containing /// The block offset of the line start in the containing
@ -132,31 +126,16 @@ impl LineItemLayoutInlineContainerState {
/// The second phase of [`super::InlineFormattingContext`] layout: once items are gathered /// 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 /// 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. /// according to their baselines and also handling absolutely positioned children.
pub(super) struct LineItemLayout<'a> { pub(super) struct LineItemLayout<'layout_data, 'layout> {
/// The set of [`super::InlineBox`]es for the [`super::InlineFormattingContext`]. This /// The state of the overall [`super::InlineFormattingContext`] layout.
/// does *not* include any state from during phase one of layout. layout: &'layout mut InlineFormattingContextLayout<'layout_data>,
pub inline_boxes: &'a InlineBoxes,
/// 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>],
/// The set of [`super::LineItemLayoutInlineContainerState`] created while laying out items /// The set of [`super::LineItemLayoutInlineContainerState`] created while laying out items
/// on this line. This does not include the current level of recursion. /// on this line. This does not include the current level of recursion.
pub state_stack: Vec<LineItemLayoutInlineContainerState>, pub state_stack: Vec<LineItemLayoutInlineContainerState>,
/// The current [`super::LineItemLayoutInlineContainerState`]. /// The current [`super::LineItemLayoutInlineContainerState`].
pub state: LineItemLayoutInlineContainerState, pub current_state: LineItemLayoutInlineContainerState,
/// The [`LayoutContext`] to use for laying out absolutely positioned line items.
pub layout_context: &'a LayoutContext<'a>,
/// The root positioning context for this layout.
pub root_positioning_context: &'a mut PositioningContext,
/// The [`ContainingBlock`] of the parent [`super::InlineFormattingContext`] of the line being
/// laid out.
pub ifc_containing_block: &'a ContainingBlock<'a>,
/// The metrics of this line, which should remain constant throughout the /// The metrics of this line, which should remain constant throughout the
/// layout process. /// layout process.
@ -167,9 +146,9 @@ pub(super) struct LineItemLayout<'a> {
pub justification_adjustment: Au, pub justification_adjustment: Au,
} }
impl<'a> LineItemLayout<'a> { impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
pub(super) fn layout_line_items( pub(super) fn layout_line_items(
state: &mut InlineFormattingContextState, layout: &mut InlineFormattingContextLayout,
line_items: Vec<LineItem>, line_items: Vec<LineItem>,
start_position: LogicalVec2<Au>, start_position: LogicalVec2<Au>,
effective_block_advance: &LineBlockSizes, effective_block_advance: &LineBlockSizes,
@ -177,13 +156,12 @@ impl<'a> LineItemLayout<'a> {
) -> Vec<Fragment> { ) -> Vec<Fragment> {
let baseline_offset = effective_block_advance.find_baseline_offset(); let baseline_offset = effective_block_advance.find_baseline_offset();
LineItemLayout { LineItemLayout {
inline_boxes: state.inline_boxes, layout,
inline_box_states: &state.inline_box_states,
state_stack: Vec::new(), state_stack: Vec::new(),
root_positioning_context: state.positioning_context, current_state: LineItemLayoutInlineContainerState::root(
layout_context: state.layout_context, start_position.inline,
state: LineItemLayoutInlineContainerState::root(start_position.inline, baseline_offset), baseline_offset,
ifc_containing_block: state.containing_block, ),
line_metrics: LineMetrics { line_metrics: LineMetrics {
block_offset: start_position.block, block_offset: start_position.block,
block_size: effective_block_advance.resolve(), block_size: effective_block_advance.resolve(),
@ -191,7 +169,7 @@ impl<'a> LineItemLayout<'a> {
}, },
justification_adjustment, justification_adjustment,
} }
.layout(line_items, state.has_right_to_left_content) .layout(line_items)
} }
/// Start and end inline boxes in tree order, so that it reflects the given inline box. /// Start and end inline boxes in tree order, so that it reflects the given inline box.
@ -207,8 +185,10 @@ impl<'a> LineItemLayout<'a> {
// Otherwise, follow the path given to us by our collection of inline boxes, so we know which // Otherwise, follow the path given to us by our collection of inline boxes, so we know which
// inline boxes to start and end. // inline boxes to start and end.
let path = self let path = self
.layout
.ifc
.inline_boxes .inline_boxes
.get_path(self.state.identifier, new_inline_box); .get_path(self.current_state.identifier, new_inline_box);
for token in path { for token in path {
match token { match token {
InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier), InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier),
@ -217,12 +197,8 @@ impl<'a> LineItemLayout<'a> {
} }
} }
pub(super) fn layout( pub(super) fn layout(&mut self, mut line_items: Vec<LineItem>) -> Vec<Fragment> {
&mut self, let mut last_level = Level::ltr();
mut line_items: Vec<LineItem>,
has_right_to_left_content: bool,
) -> Vec<Fragment> {
let mut last_level: Level = Level::ltr();
let levels: Vec<_> = line_items let levels: Vec<_> = line_items
.iter() .iter()
.map(|item| { .map(|item| {
@ -246,7 +222,7 @@ impl<'a> LineItemLayout<'a> {
}) })
.collect(); .collect();
if has_right_to_left_content { if self.layout.ifc.has_right_to_left_content {
sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels)); sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
} }
@ -257,17 +233,17 @@ impl<'a> LineItemLayout<'a> {
// any in the inline formatting context. // any in the inline formatting context.
self.prepare_layout_for_inline_box(item.inline_box_identifier()); self.prepare_layout_for_inline_box(item.inline_box_identifier());
self.state self.current_state
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS); .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
match item { match item {
LineItem::StartInlineBoxPaddingBorderMargin(_) => { LineItem::StartInlineBoxPaddingBorderMargin(_) => {
self.state self.current_state
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_START_PBM); .insert(LineLayoutInlineContainerFlags::HAD_START_PBM);
}, },
LineItem::EndInlineBoxPaddingBorderMargin(_) => { LineItem::EndInlineBoxPaddingBorderMargin(_) => {
self.state self.current_state
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_END_PBM); .insert(LineLayoutInlineContainerFlags::HAD_END_PBM);
}, },
@ -280,12 +256,13 @@ impl<'a> LineItemLayout<'a> {
// Move back to the root of the inline box tree, so that all boxes are ended. // Move back to the root of the inline box tree, so that all boxes are ended.
self.prepare_layout_for_inline_box(None); self.prepare_layout_for_inline_box(None);
std::mem::take(&mut self.state.fragments) std::mem::take(&mut self.current_state.fragments)
} }
fn current_positioning_context_mut(&mut self) -> &mut PositioningContext { fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
if let Either::First(ref mut positioning_context) = if let Either::First(ref mut positioning_context) = self
self.state.positioning_context_or_start_offset_in_parent .current_state
.positioning_context_or_start_offset_in_parent
{ {
return positioning_context; return positioning_context;
} }
@ -298,12 +275,13 @@ impl<'a> LineItemLayout<'a> {
Either::Second(_) => None, Either::Second(_) => None,
}, },
) )
.unwrap_or(self.root_positioning_context) .unwrap_or(self.layout.positioning_context)
} }
fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) { 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_state =
let inline_box = self.inline_boxes.get(identifier); &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
let inline_box = self.layout.ifc.inline_boxes.get(identifier);
let inline_box = &*(inline_box.borrow()); let inline_box = &*(inline_box.borrow());
let style = &inline_box.style; let style = &inline_box.style;
@ -318,12 +296,12 @@ impl<'a> LineItemLayout<'a> {
}; };
let parent_offset = LogicalVec2 { let parent_offset = LogicalVec2 {
inline: self.state.inline_advance + self.state.parent_offset.inline, inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
block: block_start_offset, block: block_start_offset,
}; };
let outer_state = std::mem::replace( let outer_state = std::mem::replace(
&mut self.state, &mut self.current_state,
LineItemLayoutInlineContainerState::new( LineItemLayoutInlineContainerState::new(
Some(*identifier), Some(*identifier),
parent_offset, parent_offset,
@ -337,11 +315,12 @@ impl<'a> LineItemLayout<'a> {
fn end_inline_box(&mut self) { fn end_inline_box(&mut self) {
let outer_state = self.state_stack.pop().expect("Ended unknown inline box"); let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
let mut inner_state = std::mem::replace(&mut self.state, outer_state); let mut inner_state = std::mem::replace(&mut self.current_state, outer_state);
let identifier = inner_state.identifier.expect("Ended unknown inline box"); let identifier = inner_state.identifier.expect("Ended unknown inline box");
let inline_box_state = &*self.inline_box_states[identifier.index_in_inline_boxes as usize]; let inline_box_state =
let inline_box = self.inline_boxes.get(&identifier); &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
let inline_box = &*(inline_box.borrow()); let inline_box = &*(inline_box.borrow());
let mut padding = inline_box_state.pbm.padding; let mut padding = inline_box_state.pbm.padding;
@ -379,8 +358,8 @@ impl<'a> LineItemLayout<'a> {
// Make `content_rect` relative to the parent Fragment. // Make `content_rect` relative to the parent Fragment.
let mut content_rect = LogicalRect { let mut content_rect = LogicalRect {
start_corner: LogicalVec2 { start_corner: LogicalVec2 {
inline: self.state.inline_advance + pbm_sums.inline_start, inline: self.current_state.inline_advance + pbm_sums.inline_start,
block: inner_state.parent_offset.block - self.state.parent_offset.block, block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
}, },
size: LogicalVec2 { size: LogicalVec2 {
inline: inner_state.inline_advance, inline: inner_state.inline_advance,
@ -388,7 +367,7 @@ impl<'a> LineItemLayout<'a> {
}, },
}; };
let ifc_writing_mode = self.ifc_containing_block.effective_writing_mode(); let ifc_writing_mode = self.layout.containing_block.effective_writing_mode();
if inner_state if inner_state
.flags .flags
.contains(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS) .contains(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS)
@ -405,7 +384,7 @@ impl<'a> LineItemLayout<'a> {
// do it right before creating the Fragment. // do it right before creating the Fragment.
let style = &inline_box.style; let style = &inline_box.style;
if style.clone_position().is_relative() { if style.clone_position().is_relative() {
content_rect.start_corner += relative_adjustement(style, self.ifc_containing_block); content_rect.start_corner += relative_adjustement(style, self.layout.containing_block);
} }
let mut fragment = BoxFragment::new( let mut fragment = BoxFragment::new(
@ -422,7 +401,8 @@ impl<'a> LineItemLayout<'a> {
match inner_state.positioning_context_or_start_offset_in_parent { match inner_state.positioning_context_or_start_offset_in_parent {
Either::First(mut positioning_context) => { Either::First(mut positioning_context) => {
positioning_context.layout_collected_children(self.layout_context, &mut fragment); positioning_context
.layout_collected_children(self.layout.layout_context, &mut fragment);
positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
&fragment.content_rect.origin.to_logical(ifc_writing_mode), &fragment.content_rect.origin.to_logical(ifc_writing_mode),
PositioningContextLength::zero(), PositioningContextLength::zero(),
@ -439,8 +419,8 @@ impl<'a> LineItemLayout<'a> {
}, },
} }
self.state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum(); self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
self.state.fragments.push(Fragment::Box(fragment)); self.current_state.fragments.push(Fragment::Box(fragment));
} }
fn calculate_inline_box_block_start( fn calculate_inline_box_block_start(
@ -496,10 +476,10 @@ impl<'a> LineItemLayout<'a> {
// inline box's strut), but for children of the inline formatting context root or for // 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. // fallback fonts that use baseline relative alignment, it might be different.
let start_corner = LogicalVec2 { let start_corner = LogicalVec2 {
inline: self.state.inline_advance, inline: self.current_state.inline_advance,
block: self.state.baseline_offset - block: self.current_state.baseline_offset -
text_item.font_metrics.ascent - text_item.font_metrics.ascent -
self.state.parent_offset.block, self.current_state.parent_offset.block,
}; };
let rect = LogicalRect { let rect = LogicalRect {
@ -510,11 +490,13 @@ impl<'a> LineItemLayout<'a> {
}, },
}; };
self.state.inline_advance += inline_advance; self.current_state.inline_advance += inline_advance;
self.state.fragments.push(Fragment::Text(TextFragment { self.current_state
.fragments
.push(Fragment::Text(TextFragment {
base: text_item.base_fragment_info.into(), base: text_item.base_fragment_info.into(),
parent_style: text_item.parent_style, parent_style: text_item.parent_style,
rect: rect.to_physical(self.ifc_containing_block.effective_writing_mode()), rect: rect.to_physical(self.layout.containing_block.effective_writing_mode()),
font_metrics: text_item.font_metrics, font_metrics: text_item.font_metrics,
font_key: text_item.font_key, font_key: text_item.font_key,
glyphs: text_item.text, glyphs: text_item.text,
@ -529,17 +511,17 @@ impl<'a> LineItemLayout<'a> {
// This needs to be added to the calculated block and inline positions. // This needs to be added to the calculated block and inline positions.
// Make the final result relative to the parent box. // Make the final result relative to the parent box.
let mut atomic_offset = LogicalVec2 { let mut atomic_offset = LogicalVec2 {
inline: self.state.inline_advance, inline: self.current_state.inline_advance,
block: atomic.calculate_block_start(&self.line_metrics) - block: atomic.calculate_block_start(&self.line_metrics) -
self.state.parent_offset.block, self.current_state.parent_offset.block,
}; };
if atomic.fragment.style.clone_position().is_relative() { if atomic.fragment.style.clone_position().is_relative() {
atomic_offset += atomic_offset +=
relative_adjustement(&atomic.fragment.style, self.ifc_containing_block); relative_adjustement(&atomic.fragment.style, self.layout.containing_block);
} }
let ifc_writing_mode = self.ifc_containing_block.effective_writing_mode(); let ifc_writing_mode = self.layout.containing_block.effective_writing_mode();
atomic.fragment.content_rect.origin += atomic_offset.to_physical_size(ifc_writing_mode); atomic.fragment.content_rect.origin += atomic_offset.to_physical_size(ifc_writing_mode);
if let Some(mut positioning_context) = atomic.positioning_context { if let Some(mut positioning_context) = atomic.positioning_context {
@ -555,8 +537,10 @@ impl<'a> LineItemLayout<'a> {
.append(positioning_context); .append(positioning_context);
} }
self.state.inline_advance += atomic.size.inline; self.current_state.inline_advance += atomic.size.inline;
self.state.fragments.push(Fragment::Box(atomic.fragment)); self.current_state
.fragments
.push(Fragment::Box(atomic.fragment));
} }
fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) { fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
@ -579,31 +563,31 @@ impl<'a> LineItemLayout<'a> {
if style.get_box().original_display.outside() == DisplayOutside::Inline { if style.get_box().original_display.outside() == DisplayOutside::Inline {
// Top of the line at the current inline position. // Top of the line at the current inline position.
LogicalVec2 { LogicalVec2 {
inline: self.state.inline_advance, inline: self.current_state.inline_advance,
block: -self.state.parent_offset.block, block: -self.current_state.parent_offset.block,
} }
} else { } else {
// After the bottom of the line at the start of the inline formatting context. // After the bottom of the line at the start of the inline formatting context.
LogicalVec2 { LogicalVec2 {
inline: -self.state.parent_offset.inline, inline: -self.current_state.parent_offset.inline,
block: self.line_metrics.block_size - self.state.parent_offset.block, block: self.line_metrics.block_size - self.current_state.parent_offset.block,
} }
}; };
let hoisted_box = AbsolutelyPositionedBox::to_hoisted( let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
absolute.absolutely_positioned_box.clone(), absolute.absolutely_positioned_box.clone(),
initial_start_corner.into(), initial_start_corner.into(),
self.ifc_containing_block, self.layout.containing_block,
); );
let hoisted_fragment = hoisted_box.fragment.clone(); let hoisted_fragment = hoisted_box.fragment.clone();
self.current_positioning_context_mut().push(hoisted_box); self.current_positioning_context_mut().push(hoisted_box);
self.state self.current_state
.fragments .fragments
.push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)); .push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment));
} }
fn layout_float(&mut self, mut float: FloatLineItem) { fn layout_float(&mut self, mut float: FloatLineItem) {
self.state self.current_state
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS); .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
@ -613,12 +597,14 @@ impl<'a> LineItemLayout<'a> {
// formatting context, so that they are parented properly for StackingContext // formatting context, so that they are parented properly for StackingContext
// properties such as opacity & filters. // properties such as opacity & filters.
let distance_from_parent_to_ifc = LogicalVec2 { let distance_from_parent_to_ifc = LogicalVec2 {
inline: self.state.parent_offset.inline, inline: self.current_state.parent_offset.inline,
block: self.line_metrics.block_offset + self.state.parent_offset.block, block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
}; };
float.fragment.content_rect.origin -= distance_from_parent_to_ifc float.fragment.content_rect.origin -= distance_from_parent_to_ifc
.to_physical_size(self.ifc_containing_block.effective_writing_mode()); .to_physical_size(self.layout.containing_block.effective_writing_mode());
self.state.fragments.push(Fragment::Float(float.fragment)); self.current_state
.fragments
.push(Fragment::Float(float.fragment));
} }
} }

View file

@ -196,11 +196,11 @@ pub(crate) enum InlineItem {
} }
/// Information about the current line under construction for a particular /// Information about the current line under construction for a particular
/// [`InlineFormattingContextState`]. This tracks position and size information while /// [`InlineFormattingContextLayout`]. This tracks position and size information while
/// [`LineItem`]s are collected and is used as input when those [`LineItem`]s are /// [`LineItem`]s are collected and is used as input when those [`LineItem`]s are
/// converted into [`Fragment`]s during the final phase of line layout. Note that this /// converted into [`Fragment`]s during the final phase of line layout. Note that this
/// does not store the [`LineItem`]s themselves, as they are stored as part of the /// does not store the [`LineItem`]s themselves, as they are stored as part of the
/// nesting state in the [`InlineFormattingContextState`]. /// nesting state in the [`InlineFormattingContextLayout`].
struct LineUnderConstruction { struct LineUnderConstruction {
/// The position where this line will start once it is laid out. This includes any /// The position where this line will start once it is laid out. This includes any
/// offset from `text-indent`. /// offset from `text-indent`.
@ -552,19 +552,14 @@ pub(super) struct InlineContainerState {
font_metrics: FontMetrics, font_metrics: FontMetrics,
} }
pub(super) struct InlineFormattingContextState<'a, 'b> { pub(super) struct InlineFormattingContextLayout<'layout_data> {
positioning_context: &'a mut PositioningContext, positioning_context: &'layout_data mut PositioningContext,
containing_block: &'b ContainingBlock<'b>, containing_block: &'layout_data ContainingBlock<'layout_data>,
sequential_layout_state: Option<&'a mut SequentialLayoutState>, sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
layout_context: &'b LayoutContext<'b>, layout_context: &'layout_data LayoutContext<'layout_data>,
/// The inline boxes collection of the [`InlineFormattingContext`] that this /// The [`InlineFormattingContext`] that we are laying out.
/// state is laying out. ifc: &'layout_data InlineFormattingContext,
inline_boxes: &'a InlineBoxes,
/// The list of [`FontMetrics`] used by the [`InlineFormattingContext`] that
/// we are laying out.
fonts: &'a Vec<FontKeyAndMetrics>,
/// The [`InlineContainerState`] for the container formed by the root of the /// The [`InlineContainerState`] for the container formed by the root of the
/// [`InlineFormattingContext`]. This is effectively the "root inline box" described /// [`InlineFormattingContext`]. This is effectively the "root inline box" described
@ -616,7 +611,7 @@ pub(super) struct InlineFormattingContextState<'a, 'b> {
/// In this case, the `<span>` should not extend to the second line. If we linebreak /// In this case, the `<span>` should not extend to the second line. If we linebreak
/// as soon as we encounter the `<br>` the `<span>`'s ending inline borders would be /// as soon as we encounter the `<br>` the `<span>`'s ending inline borders would be
/// placed on the second line, because we add those borders in /// placed on the second line, because we add those borders in
/// [`InlineFormattingContextState::finish_inline_box()`]. /// [`InlineFormattingContextLayout::finish_inline_box()`].
linebreak_before_new_content: bool, linebreak_before_new_content: bool,
/// When a `<br>` element has `clear`, this needs to be applied after the linebreak, /// When a `<br>` element has `clear`, this needs to be applied after the linebreak,
@ -633,13 +628,13 @@ pub(super) struct InlineFormattingContextState<'a, 'b> {
had_inflow_content: bool, had_inflow_content: bool,
/// The currently white-space-collapse setting of this line. This is stored on the /// The currently white-space-collapse setting of this line. This is stored on the
/// [`InlineFormattingContextState`] because when a soft wrap opportunity is defined /// [`InlineFormattingContextLayout`] because when a soft wrap opportunity is defined
/// by the boundary between two characters, the white-space-collapse property of their /// by the boundary between two characters, the white-space-collapse property of their
/// nearest common ancestor is used. /// nearest common ancestor is used.
white_space_collapse: WhiteSpaceCollapse, white_space_collapse: WhiteSpaceCollapse,
/// The currently text-wrap-mode setting of this line. This is stored on the /// The currently text-wrap-mode setting of this line. This is stored on the
/// [`InlineFormattingContextState`] because when a soft wrap opportunity is defined /// [`InlineFormattingContextLayout`] because when a soft wrap opportunity is defined
/// by the boundary between two characters, the text-wrap-mode property of their nearest /// by the boundary between two characters, the text-wrap-mode property of their nearest
/// common ancestor is used. /// common ancestor is used.
text_wrap_mode: TextWrapMode, text_wrap_mode: TextWrapMode,
@ -648,12 +643,9 @@ pub(super) struct InlineFormattingContextState<'a, 'b> {
/// are laying out. This is used to propagate baselines to the ancestors of /// are laying out. This is used to propagate baselines to the ancestors of
/// `display: inline-block` elements and table content. /// `display: inline-block` elements and table content.
baselines: Baselines, baselines: Baselines,
/// Whether or not the [`InlineFormattingContext`] being laid out has right-to-left content.
has_right_to_left_content: bool,
} }
impl<'a, 'b> InlineFormattingContextState<'a, 'b> { impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
fn current_inline_container_state(&self) -> &InlineContainerState { fn current_inline_container_state(&self) -> &InlineContainerState {
match self.inline_box_state_stack.last() { match self.inline_box_state_stack.last() {
Some(inline_box_state) => &inline_box_state.base, Some(inline_box_state) => &inline_box_state.base,
@ -706,7 +698,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
inline_box.is_last_fragment, inline_box.is_last_fragment,
inline_box inline_box
.default_font_index .default_font_index
.map(|index| &self.fonts[index].metrics), .map(|index| &self.ifc.font_metrics[index].metrics),
); );
// If we are starting a `<br>` element prepare to clear after its deferred linebreak has been // If we are starting a `<br>` element prepare to clear after its deferred linebreak has been
@ -799,7 +791,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
/// Finish layout of all inline boxes for the current line. This will gather all /// Finish layout of all inline boxes for the current line. This will gather all
/// [`LineItem`]s and turn them into [`Fragment`]s, then reset the /// [`LineItem`]s and turn them into [`Fragment`]s, then reset the
/// [`InlineFormattingContextState`] preparing it for laying out a new line. /// [`InlineFormattingContextLayout`] preparing it for laying out a new line.
fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) { fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
let whitespace_trimmed = self.current_line.trim_trailing_whitespace(); let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
let (inline_start_position, justification_adjustment) = self let (inline_start_position, justification_adjustment) = self
@ -1265,7 +1257,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
// If the metrics of this font don't match the default font, we are likely using a fallback // If the metrics of this font don't match the default font, we are likely using a fallback
// font and need to adjust the line size to account for a potentially different font. // font and need to adjust the line size to account for a potentially different font.
// If somehow the metrics match, the line size won't change. // If somehow the metrics match, the line size won't change.
let ifc_font_info = &self.fonts[font_index]; let ifc_font_info = &self.ifc.font_metrics[font_index];
let font_metrics = ifc_font_info.metrics.clone(); let font_metrics = ifc_font_info.metrics.clone();
let using_fallback_font = let using_fallback_font =
self.current_inline_container_state().font_metrics != font_metrics; self.current_inline_container_state().font_metrics != font_metrics;
@ -1609,13 +1601,12 @@ impl InlineFormattingContext {
.insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT); .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
} }
let mut ifc = InlineFormattingContextState { let mut layout = InlineFormattingContextLayout {
positioning_context, positioning_context,
containing_block, containing_block,
sequential_layout_state, sequential_layout_state,
layout_context, layout_context,
inline_boxes: &self.inline_boxes, ifc: self,
fonts: &self.font_metrics,
fragments: Vec::new(), fragments: Vec::new(),
current_line: LineUnderConstruction::new(LogicalVec2 { current_line: LineUnderConstruction::new(LogicalVec2 {
inline: first_line_inline_start, inline: first_line_inline_start,
@ -1638,13 +1629,12 @@ impl InlineFormattingContext {
white_space_collapse: style_text.white_space_collapse, white_space_collapse: style_text.white_space_collapse,
text_wrap_mode: style_text.text_wrap_mode, text_wrap_mode: style_text.text_wrap_mode,
baselines: Baselines::default(), baselines: Baselines::default(),
has_right_to_left_content: self.has_right_to_left_content,
}; };
// FIXME(pcwalton): This assumes that margins never collapse through inline formatting // FIXME(pcwalton): This assumes that margins never collapse through inline formatting
// contexts (i.e. that inline formatting contexts are never empty). Is that right? // contexts (i.e. that inline formatting contexts are never empty). Is that right?
// FIXME(mrobinson): This should not happen if the IFC collapses through. // FIXME(mrobinson): This should not happen if the IFC collapses through.
if let Some(ref mut sequential_layout_state) = ifc.sequential_layout_state { if let Some(ref mut sequential_layout_state) = layout.sequential_layout_state {
sequential_layout_state.collapse_margins(); sequential_layout_state.collapse_margins();
// FIXME(mrobinson): Collapse margins in the containing block offsets as well?? // FIXME(mrobinson): Collapse margins in the containing block offsets as well??
} }
@ -1654,51 +1644,49 @@ impl InlineFormattingContext {
// Any new box should flush a pending hard line break. // Any new box should flush a pending hard line break.
if !matches!(item, InlineItem::EndInlineBox) { if !matches!(item, InlineItem::EndInlineBox) {
ifc.possibly_flush_deferred_forced_line_break(); layout.possibly_flush_deferred_forced_line_break();
} }
match item { match item {
InlineItem::StartInlineBox(identifier) => { InlineItem::StartInlineBox(identifier) => {
ifc.start_inline_box(&self.inline_boxes.get(identifier).borrow()); layout.start_inline_box(&self.inline_boxes.get(identifier).borrow());
}, },
InlineItem::EndInlineBox => ifc.finish_inline_box(), InlineItem::EndInlineBox => layout.finish_inline_box(),
InlineItem::TextRun(run) => run.layout_into_line_items(&mut ifc), InlineItem::TextRun(run) => run.layout_into_line_items(&mut layout),
InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => { InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
atomic_formatting_context.layout_into_line_items( atomic_formatting_context.layout_into_line_items(
layout_context, &mut layout,
self,
&mut ifc,
*offset_in_text, *offset_in_text,
*bidi_level, *bidi_level,
); );
}, },
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => { InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
ifc.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned( layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
ifc.current_inline_box_identifier(), layout.current_inline_box_identifier(),
AbsolutelyPositionedLineItem { AbsolutelyPositionedLineItem {
absolutely_positioned_box: positioned_box.clone(), absolutely_positioned_box: positioned_box.clone(),
}, },
)); ));
}, },
InlineItem::OutOfFlowFloatBox(ref mut float_box) => { InlineItem::OutOfFlowFloatBox(ref mut float_box) => {
float_box.layout_into_line_items(layout_context, &mut ifc); float_box.layout_into_line_items(&mut layout);
}, },
} }
} }
ifc.finish_last_line(); layout.finish_last_line();
let mut collapsible_margins_in_children = CollapsedBlockMargins::zero(); let mut collapsible_margins_in_children = CollapsedBlockMargins::zero();
let content_block_size = ifc.current_line.start_position.block; let content_block_size = layout.current_line.start_position.block;
collapsible_margins_in_children.collapsed_through = !ifc.had_inflow_content && collapsible_margins_in_children.collapsed_through = !layout.had_inflow_content &&
content_block_size == Au::zero() && content_block_size == Au::zero() &&
collapsible_with_parent_start_margin.0; collapsible_with_parent_start_margin.0;
FlowLayout { FlowLayout {
fragments: ifc.fragments, fragments: layout.fragments,
content_block_size: content_block_size.into(), content_block_size: content_block_size.into(),
collapsible_margins_in_children, collapsible_margins_in_children,
baselines: ifc.baselines, baselines: layout.baselines,
} }
} }
@ -1913,18 +1901,13 @@ impl InlineContainerState {
impl IndependentFormattingContext { impl IndependentFormattingContext {
fn layout_into_line_items( fn layout_into_line_items(
&mut self, &mut self,
layout_context: &LayoutContext, layout: &mut InlineFormattingContextLayout,
inline_formatting_context: &InlineFormattingContext,
inline_formatting_context_state: &mut InlineFormattingContextState,
offset_in_text: usize, offset_in_text: usize,
bidi_level: Level, bidi_level: Level,
) { ) {
let style = self.style(); let style = self.style();
let container_writing_mode = inline_formatting_context_state let container_writing_mode = layout.containing_block.style.effective_writing_mode();
.containing_block let pbm = style.padding_border_margin(layout.containing_block);
.style
.effective_writing_mode();
let pbm = style.padding_border_margin(inline_formatting_context_state.containing_block);
let margin = pbm.margin.auto_is(Au::zero); let margin = pbm.margin.auto_is(Au::zero);
let pbm_sums = pbm.padding + pbm.border + margin; let pbm_sums = pbm.padding + pbm.border + margin;
let pbm_physical_origin = pbm_sums let pbm_physical_origin = pbm_sums
@ -1938,7 +1921,7 @@ impl IndependentFormattingContext {
let size = replaced let size = replaced
.contents .contents
.used_size_as_if_inline_element( .used_size_as_if_inline_element(
inline_formatting_context_state.containing_block, layout.containing_block,
&replaced.style, &replaced.style,
None, None,
&pbm, &pbm,
@ -1962,22 +1945,21 @@ impl IndependentFormattingContext {
IndependentFormattingContext::NonReplaced(non_replaced) => { IndependentFormattingContext::NonReplaced(non_replaced) => {
let box_size = non_replaced let box_size = non_replaced
.style .style
.content_box_size(inline_formatting_context_state.containing_block, &pbm); .content_box_size(layout.containing_block, &pbm);
let max_box_size = non_replaced let max_box_size = non_replaced
.style .style
.content_max_box_size(inline_formatting_context_state.containing_block, &pbm); .content_max_box_size(layout.containing_block, &pbm);
let min_box_size = non_replaced let min_box_size = non_replaced
.style .style
.content_min_box_size(inline_formatting_context_state.containing_block, &pbm) .content_min_box_size(layout.containing_block, &pbm)
.auto_is(Length::zero); .auto_is(Length::zero);
// https://drafts.csswg.org/css2/visudet.html#inlineblock-width // https://drafts.csswg.org/css2/visudet.html#inlineblock-width
let tentative_inline_size = box_size.inline.auto_is(|| { let tentative_inline_size = box_size.inline.auto_is(|| {
let available_size = let available_size =
inline_formatting_context_state.containing_block.inline_size - layout.containing_block.inline_size - pbm_sums.inline_sum();
pbm_sums.inline_sum();
non_replaced non_replaced
.inline_content_sizes(layout_context) .inline_content_sizes(layout.layout_context)
.shrink_to_fit(available_size) .shrink_to_fit(available_size)
.into() .into()
}); });
@ -1994,11 +1976,7 @@ impl IndependentFormattingContext {
style: &non_replaced.style, style: &non_replaced.style,
}; };
assert_eq!( assert_eq!(
inline_formatting_context_state layout.containing_block.style.writing_mode.is_horizontal(),
.containing_block
.style
.writing_mode
.is_horizontal(),
containing_block_for_children containing_block_for_children
.style .style
.writing_mode .writing_mode
@ -2013,10 +1991,10 @@ impl IndependentFormattingContext {
true, /* collects_for_nearest_positioned_ancestor */ true, /* collects_for_nearest_positioned_ancestor */
)); ));
let independent_layout = non_replaced.layout( let independent_layout = non_replaced.layout(
layout_context, layout.layout_context,
child_positioning_context.as_mut().unwrap(), child_positioning_context.as_mut().unwrap(),
&containing_block_for_children, &containing_block_for_children,
inline_formatting_context_state.containing_block, layout.containing_block,
); );
let (inline_size, block_size) = let (inline_size, block_size) =
match independent_layout.content_inline_size_for_table { match independent_layout.content_inline_size_for_table {
@ -2061,11 +2039,12 @@ impl IndependentFormattingContext {
}, },
}; };
if inline_formatting_context_state.text_wrap_mode == TextWrapMode::Wrap && if layout.text_wrap_mode == TextWrapMode::Wrap &&
!inline_formatting_context !layout
.ifc
.previous_character_prevents_soft_wrap_opportunity(offset_in_text) .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
{ {
inline_formatting_context_state.process_soft_wrap_opportunity(); layout.process_soft_wrap_opportunity();
} }
let size = pbm_sums.sum() + let size = pbm_sums.sum() +
@ -2078,18 +2057,15 @@ impl IndependentFormattingContext {
.map(|baseline| pbm_sums.block_start + baseline) .map(|baseline| pbm_sums.block_start + baseline)
.unwrap_or(size.block); .unwrap_or(size.block);
let (block_sizes, baseline_offset_in_parent) = self.get_block_sizes_and_baseline_offset( let (block_sizes, baseline_offset_in_parent) =
inline_formatting_context_state, self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
size.block, layout.update_unbreakable_segment_for_new_content(
baseline_offset,
);
inline_formatting_context_state.update_unbreakable_segment_for_new_content(
&block_sizes, &block_sizes,
size.inline, size.inline,
SegmentContentFlags::empty(), SegmentContentFlags::empty(),
); );
inline_formatting_context_state.push_line_item_to_unbreakable_segment(LineItem::Atomic( layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
inline_formatting_context_state.current_inline_box_identifier(), layout.current_inline_box_identifier(),
AtomicLineItem { AtomicLineItem {
fragment, fragment,
size, size,
@ -2102,9 +2078,11 @@ impl IndependentFormattingContext {
// If there's a soft wrap opportunity following this atomic, defer a soft wrap opportunity // If there's a soft wrap opportunity following this atomic, defer a soft wrap opportunity
// for when we next process text content. // for when we next process text content.
if !inline_formatting_context.next_character_prevents_soft_wrap_opportunity(offset_in_text) if !layout
.ifc
.next_character_prevents_soft_wrap_opportunity(offset_in_text)
{ {
inline_formatting_context_state.have_deferred_soft_wrap_opportunity = true; layout.have_deferred_soft_wrap_opportunity = true;
} }
} }
@ -2128,7 +2106,7 @@ impl IndependentFormattingContext {
fn get_block_sizes_and_baseline_offset( fn get_block_sizes_and_baseline_offset(
&self, &self,
ifc: &InlineFormattingContextState, ifc: &InlineFormattingContextLayout,
block_size: Au, block_size: Au,
baseline_offset_in_content_area: Au, baseline_offset_in_content_area: Au,
) -> (LineBlockSizes, Au) { ) -> (LineBlockSizes, Au) {
@ -2163,18 +2141,14 @@ impl IndependentFormattingContext {
} }
impl FloatBox { impl FloatBox {
fn layout_into_line_items( fn layout_into_line_items(&mut self, layout: &mut InlineFormattingContextLayout) {
&mut self,
layout_context: &LayoutContext,
ifc: &mut InlineFormattingContextState,
) {
let fragment = self.layout( let fragment = self.layout(
layout_context, layout.layout_context,
ifc.positioning_context, layout.positioning_context,
ifc.containing_block, layout.containing_block,
); );
ifc.push_line_item_to_unbreakable_segment(LineItem::Float( layout.push_line_item_to_unbreakable_segment(LineItem::Float(
ifc.current_inline_box_identifier(), layout.current_inline_box_identifier(),
FloatLineItem { FloatLineItem {
fragment, fragment,
needs_placement: true, needs_placement: true,
@ -2183,7 +2157,7 @@ impl FloatBox {
} }
} }
fn place_pending_floats(ifc: &mut InlineFormattingContextState, line_items: &mut [LineItem]) { fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
for item in line_items.iter_mut() { 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 { if float_line_item.needs_placement {
@ -2282,8 +2256,8 @@ fn inline_container_needs_strut(
} }
/// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`]. /// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`].
struct ContentSizesComputation<'a> { struct ContentSizesComputation<'layout_data> {
layout_context: &'a LayoutContext<'a>, layout_context: &'layout_data LayoutContext<'layout_data>,
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
paragraph: ContentSizes, paragraph: ContentSizes,
current_line: ContentSizes, current_line: ContentSizes,
@ -2297,7 +2271,7 @@ struct ContentSizesComputation<'a> {
ending_inline_pbm_stack: Vec<Au>, ending_inline_pbm_stack: Vec<Au>,
} }
impl<'a> ContentSizesComputation<'a> { impl<'layout_data> ContentSizesComputation<'layout_data> {
fn traverse(mut self, inline_formatting_context: &InlineFormattingContext) -> ContentSizes { fn traverse(mut self, inline_formatting_context: &InlineFormattingContext) -> ContentSizes {
for inline_item in inline_formatting_context.inline_items.iter() { for inline_item in inline_formatting_context.inline_items.iter() {
self.process_item(&mut inline_item.borrow_mut(), inline_formatting_context); self.process_item(&mut inline_item.borrow_mut(), inline_formatting_context);
@ -2452,7 +2426,7 @@ impl<'a> ContentSizesComputation<'a> {
/// Compute the [`ContentSizes`] of the given [`InlineFormattingContext`]. /// Compute the [`ContentSizes`] of the given [`InlineFormattingContext`].
fn compute( fn compute(
inline_formatting_context: &InlineFormattingContext, inline_formatting_context: &InlineFormattingContext,
layout_context: &'a LayoutContext, layout_context: &'layout_data LayoutContext,
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
) -> ContentSizes { ) -> ContentSizes {
Self { Self {

View file

@ -27,7 +27,7 @@ use unicode_script::Script;
use xi_unicode::linebreak_property; use xi_unicode::linebreak_property;
use super::line_breaker::LineBreaker; use super::line_breaker::LineBreaker;
use super::{FontKeyAndMetrics, InlineFormattingContextState}; use super::{FontKeyAndMetrics, InlineFormattingContextLayout};
use crate::fragment_tree::BaseFragmentInfo; use crate::fragment_tree::BaseFragmentInfo;
// These constants are the xi-unicode line breaking classes that are defined in // These constants are the xi-unicode line breaking classes that are defined in
@ -136,7 +136,7 @@ impl TextRunSegment {
&self, &self,
text_run: &TextRun, text_run: &TextRun,
mut soft_wrap_policy: SegmentStartSoftWrapPolicy, mut soft_wrap_policy: SegmentStartSoftWrapPolicy,
ifc: &mut InlineFormattingContextState, ifc: &mut InlineFormattingContextLayout,
) { ) {
if self.break_at_start && soft_wrap_policy == SegmentStartSoftWrapPolicy::FollowLinebreaker if self.break_at_start && soft_wrap_policy == SegmentStartSoftWrapPolicy::FollowLinebreaker
{ {
@ -492,7 +492,7 @@ impl TextRun {
results results
} }
pub(super) fn layout_into_line_items(&self, ifc: &mut InlineFormattingContextState) { pub(super) fn layout_into_line_items(&self, ifc: &mut InlineFormattingContextLayout) {
if self.text_range.is_empty() { if self.text_range.is_empty() {
return; return;
} }