Skip to main content

layout/flow/inline/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! # Inline Formatting Context Layout
6//!
7//! Inline layout is divided into three phases:
8//!
9//! 1. Box Tree Construction
10//! 2. Box to Line Layout
11//! 3. Line to Fragment Layout
12//!
13//! The first phase happens during normal box tree constrution, while the second two phases happen
14//! during fragment tree construction (sometimes called just "layout").
15//!
16//! ## Box Tree Construction
17//!
18//! During box tree construction, DOM elements are transformed into a box tree. This phase collects
19//! all of the inline boxes, text, atomic inline elements (boxes with `display: inline-block` or
20//! `display: inline-table` as well as things like images and canvas), absolutely positioned blocks,
21//! and floated blocks.
22//!
23//! During the last part of this phase, whitespace is collapsed and text is segmented into
24//! [`TextRun`]s based on script, chosen font, and line breaking opportunities. In addition, default
25//! fonts are selected for every inline box. Each segment of text is shaped using HarfBuzz and
26//! turned into a series of glyphs, which all have a size and a position relative to the origin of
27//! the [`TextRun`] (calculated in later phases).
28//!
29//! The code for this phase is mainly in `construct.rs`, but text handling can also be found in
30//! `text_runs.rs.`
31//!
32//! ## Box to Line Layout
33//!
34//! During the first phase of fragment tree construction, box tree items are laid out into
35//! [`LineItem`]s and fragmented based on line boundaries. This is where line breaking happens. This
36//! part of layout fragments boxes and their contents across multiple lines while positioning floats
37//! and making sure non-floated contents flow around them. In addition, all atomic elements are laid
38//! out, which may descend into their respective trees and create fragments. Finally, absolutely
39//! positioned content is collected in order to later hoist it to the containing block for
40//! absolutes.
41//!
42//! Note that during this phase, layout does not know the final block position of content. Only
43//! during line to fragment layout, are the final block positions calculated based on the line's
44//! final content and its vertical alignment. Instead, positions and line heights are calculated
45//! relative to the line's final baseline which will be determined in the final phase.
46//!
47//! [`LineItem`]s represent a particular set of content on a line. Currently this is represented by
48//! a linear series of items that describe the line's hierarchy of inline boxes and content. The
49//! item types are:
50//!
51//!  - [`LineItem::InlineStartBoxPaddingBorderMargin`]
52//!  - [`LineItem::InlineEndBoxPaddingBorderMargin`]
53//!  - [`LineItem::TextRun`]
54//!  - [`LineItem::Atomic`]
55//!  - [`LineItem::AbsolutelyPositioned`]
56//!  - [`LineItem::Float`]
57//!
58//! The code for this can be found by looking for methods of the form `layout_into_line_item()`.
59//!
60//! ## Line to Fragment Layout
61//!
62//! During the second phase of fragment tree construction, the final block position of [`LineItem`]s
63//! is calculated and they are converted into [`Fragment`]s. After layout, the [`LineItem`]s are
64//! discarded and the new fragments are incorporated into the fragment tree. The final static
65//! position of absolutely positioned content is calculated and it is hoisted to its containing
66//! block via [`PositioningContext`].
67//!
68//! The code for this phase, can mainly be found in `line.rs`.
69//!
70
71pub mod construct;
72mod full_width;
73pub mod inline_box;
74pub mod line;
75mod line_breaker;
76mod mathml_italics;
77mod shaping_queue;
78mod small_kana;
79pub mod text_run;
80pub mod text_transform;
81
82use std::cell::{Cell, OnceCell};
83use std::mem;
84use std::ops::Range;
85use std::rc::Rc;
86use std::sync::{Arc, OnceLock};
87
88use app_units::{Au, MAX_AU};
89use atomic_refcell::AtomicRef;
90use bitflags::bitflags;
91use construct::InlineFormattingContextBuilder;
92use fonts::{FontMetrics, FontRef, ShapedTextSlice};
93use icu_locale_core::LanguageIdentifier;
94use icu_properties::props::{EnumeratedProperty, LineBreak as ICULineBreak};
95use icu_segmenter::options::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
96use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
97use layout_api::LayoutNode;
98use line::{
99    AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
100    TextRunLineItem,
101};
102use malloc_size_of_derive::MallocSizeOf;
103use script::layout_dom::ServoLayoutNode;
104use servo_arc::Arc as ServoArc;
105use servo_base::text::{Utf8CodeUnits, Utf32CodeUnits};
106use style::Zero;
107use style::computed_values::line_break::T as LineBreak;
108use style::computed_values::text_wrap_mode::T as TextWrapMode;
109use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
110use style::computed_values::word_break::T as WordBreak;
111use style::context::{QuirksMode, SharedStyleContext};
112use style::properties::ComputedValues;
113use style::values::computed::BaselineShift;
114use style::values::generics::box_::BaselineShiftKeyword;
115use style::values::generics::font::LineHeight;
116use style::values::specified::box_::BaselineSource;
117use style::values::specified::text::TextAlignKeyword;
118use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
119use text_run::{TextRun, get_font_for_first_font_for_style};
120use unicode_bidi::{BidiInfo, Level};
121
122use super::float::{Clear, PlacementAmongFloats};
123use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
124use crate::cell::{ArcRefCell, WeakRefCell};
125use crate::context::LayoutContext;
126use crate::dom::WeakLayoutBox;
127use crate::dom_traversal::NodeAndStyleInfo;
128use crate::flow::float::{FloatBox, SequentialLayoutState};
129use crate::flow::inline::shaping_queue::ShapingQueue;
130use crate::flow::inline::text_run::{
131    CaretPlaceholder, FontAndScriptInfo, TextRunItem, TextRunSegment,
132};
133use crate::flow::{
134    BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
135    compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
136    skippable_for_block_percentage_quirk,
137};
138use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
139use crate::fragment_tree::{CollapsedMargin, Fragment, FragmentFlags, PositioningFragment};
140use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
141use crate::layout_box_base::LayoutBoxBase;
142use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
143use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
144use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
145use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
146
147// From gfxFontConstants.h in Firefox.
148static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
149static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
150
151#[derive(Debug, MallocSizeOf)]
152pub(crate) struct InlineFormattingContext {
153    /// All [`InlineItem`]s in this [`InlineFormattingContext`] stored in a flat array.
154    /// [`InlineItem::StartInlineBox`] and [`InlineItem::EndInlineBox`] allow representing
155    /// the tree of inline boxes within the formatting context, but a flat array allows
156    /// easy iteration through all inline items.
157    inline_items: Vec<InlineItem>,
158
159    /// The tree of inline boxes in this [`InlineFormattingContext`]. These are stored in
160    /// a flat array with each being given a [`InlineBoxIdentifier`].
161    inline_boxes: InlineBoxes,
162
163    /// The text content of this inline formatting context.
164    #[conditional_malloc_size_of]
165    text_content: Arc<OnceLock<String>>,
166
167    /// The [`SharedInlineStyles`] for the root of this [`InlineFormattingContext`] that are used to
168    /// share styles with all [`TextRun`] children.
169    shared_inline_styles: SharedInlineStyles,
170
171    /// The default font that is used for the root of this [`InlineFormattingContext`]. This is the
172    /// font used when the font fallback code path is not taken. It may be `None` if no default
173    /// font was found (this typically means that no characters can be rendered).
174    default_font: Option<FontRef>,
175
176    /// Whether this IFC contains the 1st formatted line of an element:
177    /// <https://www.w3.org/TR/css-pseudo-4/#first-formatted-line>.
178    has_first_formatted_line: bool,
179
180    /// Whether or not this [`InlineFormattingContext`] contains floats.
181    pub(super) contains_floats: bool,
182
183    /// Whether or not this is an [`InlineFormattingContext`] for a single line text input's inner
184    /// text container.
185    is_single_line_text_input: bool,
186
187    /// Whether or not this is an [`InlineFormattingContext`] has right-to-left content, which
188    /// will require reordering during layout.
189    has_right_to_left_content: bool,
190
191    /// The cached multiplier for `tab-size: <number>`:
192    /// <https://drafts.csswg.org/css-text/#tab-size-property>
193    /// > the advance width of the space character (U+0020) of the nearest block container ancestor
194    /// > of the preserved tab, including its associated `letter-spacing` and `word-spacing`.
195    tab_size_multiplier: OnceLock<Au>,
196}
197
198/// [`TextRun`] and `TextFragment`s need a handle on their parent inline box (or inline
199/// formatting context root)'s style. In order to implement incremental layout, these are
200/// wrapped in [`SharedStyle`]. This allows updating the parent box tree element without
201/// updating every single descendant box tree node and fragment.
202#[derive(Clone, Debug, MallocSizeOf)]
203pub(crate) struct SharedInlineStyles {
204    pub style: SharedStyle,
205    pub selected: SharedStyle,
206}
207
208impl SharedInlineStyles {
209    pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
210        self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
211    }
212
213    pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
214        Self {
215            style: SharedStyle::new(info.style.clone()),
216            selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
217        }
218    }
219}
220
221impl BlockLevelBox {
222    fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
223        layout.process_soft_wrap_opportunity();
224        layout.commit_current_segment_to_line();
225        layout.process_line_break(
226            true, /* forced_line_break */
227            true, /* for_block_level */
228        );
229
230        let fragment = layout_block_level_child(
231            layout.layout_context,
232            layout.positioning_context,
233            self,
234            layout.sequential_layout_state.as_deref_mut(),
235            &mut layout.placement_state,
236            layout.ignore_block_margins_for_stretch,
237            true, /* has_inline_parent */
238        );
239
240        let Some(fragment) = fragment.retrieve_box_fragment() else {
241            unreachable!("The fragment should be a Fragment::Box()");
242        };
243
244        // If this Fragment's layout depends on the block size of the containing block,
245        // then the entire layout of the inline formatting context does as well.
246        layout.depends_on_block_constraints |= fragment.base.flags.contains(
247            FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
248        );
249
250        layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
251            layout.current_inline_box_identifier(),
252            fragment.clone(),
253        ));
254
255        layout.commit_current_segment_to_line();
256        layout.process_line_break(
257            true,  /* forced_line_break */
258            false, /* for_block_level */
259        );
260    }
261}
262
263#[derive(Clone, Debug, MallocSizeOf)]
264pub(crate) enum InlineItem {
265    StartInlineBox(ArcRefCell<InlineBox>),
266    EndInlineBox(ArcRefCell<InlineBox>),
267    TextRun(ArcRefCell<TextRun>),
268    OutOfFlowAbsolutelyPositionedBox(
269        ArcRefCell<AbsolutelyPositionedBox>,
270        Utf8CodeUnits, /* offset_in_text */
271    ),
272    OutOfFlowFloatBox(ArcRefCell<FloatBox>),
273    Atomic(
274        ArcRefCell<IndependentFormattingContext>,
275        Utf8CodeUnits, /* offset_in_text */
276        Level,         /* bidi_level */
277    ),
278    BlockLevel(ArcRefCell<BlockLevelBox>),
279}
280
281impl InlineItem {
282    pub(crate) fn repair_style(
283        &self,
284        context: &SharedStyleContext,
285        node: &ServoLayoutNode,
286        new_style: &ServoArc<ComputedValues>,
287    ) {
288        match self {
289            InlineItem::StartInlineBox(inline_box) => {
290                inline_box
291                    .borrow_mut()
292                    .repair_style(context, node, new_style);
293            },
294            InlineItem::EndInlineBox(..) => {},
295            // TextRun holds a handle the `InlineSharedStyles` which is updated when repairing inline box
296            // and `display: contents` styles.
297            InlineItem::TextRun(..) => {},
298            InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
299                .borrow_mut()
300                .context
301                .repair_style(context, node, new_style),
302            InlineItem::OutOfFlowFloatBox(float_box) => float_box
303                .borrow_mut()
304                .contents
305                .repair_style(context, node, new_style),
306            InlineItem::Atomic(atomic, ..) => {
307                atomic.borrow_mut().repair_style(context, node, new_style)
308            },
309            InlineItem::BlockLevel(block_level) => block_level
310                .borrow_mut()
311                .repair_style(context, node, new_style),
312        }
313    }
314
315    pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
316        match self {
317            InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
318            InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
319                unreachable!("Should never have these kind of fragments attached to a DOM node")
320            },
321            InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
322                callback(&positioned_box.borrow().context.base)
323            },
324            InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
325            InlineItem::Atomic(independent_formatting_context, ..) => {
326                callback(&independent_formatting_context.borrow().base)
327            },
328            InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
329        }
330    }
331
332    pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
333        match self {
334            InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
335            InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
336                unreachable!("Should never have these kind of fragments attached to a DOM node")
337            },
338            InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
339                callback(&mut positioned_box.borrow_mut().context.base)
340            },
341            InlineItem::OutOfFlowFloatBox(float_box) => {
342                callback(&mut float_box.borrow_mut().contents.base)
343            },
344            InlineItem::Atomic(independent_formatting_context, ..) => {
345                callback(&mut independent_formatting_context.borrow_mut().base)
346            },
347            InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
348        }
349    }
350
351    pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
352        match self {
353            Self::StartInlineBox(_) | InlineItem::EndInlineBox(..) => {
354                // The parentage of inline items within an inline box is handled when the entire
355                // inline formatting context is attached to the tree.
356            },
357            Self::TextRun(_) => {
358                // Text runs can't have children, so no need to do anything.
359            },
360            Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
361                positioned_box.borrow().context.attached_to_tree(layout_box)
362            },
363            Self::OutOfFlowFloatBox(float_box) => {
364                float_box.borrow().contents.attached_to_tree(layout_box)
365            },
366            Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
367            Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
368        }
369    }
370
371    pub(crate) fn downgrade(&self) -> WeakInlineItem {
372        match self {
373            Self::StartInlineBox(inline_box) => {
374                WeakInlineItem::StartInlineBox(inline_box.downgrade())
375            },
376            Self::EndInlineBox(inline_box) => WeakInlineItem::EndInlineBox(inline_box.downgrade()),
377            Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
378            Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
379                WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
380                    positioned_box.downgrade(),
381                    *offset_in_text,
382                )
383            },
384            Self::OutOfFlowFloatBox(float_box) => {
385                WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
386            },
387            Self::Atomic(atomic, offset_in_text, bidi_level) => {
388                WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
389            },
390            Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
391        }
392    }
393}
394
395#[derive(Clone, Debug, MallocSizeOf)]
396pub(crate) enum WeakInlineItem {
397    StartInlineBox(WeakRefCell<InlineBox>),
398    EndInlineBox(WeakRefCell<InlineBox>),
399    TextRun(WeakRefCell<TextRun>),
400    OutOfFlowAbsolutelyPositionedBox(
401        WeakRefCell<AbsolutelyPositionedBox>,
402        Utf8CodeUnits, /* offset_in_text */
403    ),
404    OutOfFlowFloatBox(WeakRefCell<FloatBox>),
405    Atomic(
406        WeakRefCell<IndependentFormattingContext>,
407        Utf8CodeUnits, /* offset_in_text */
408        Level,         /* bidi_level */
409    ),
410    BlockLevel(WeakRefCell<BlockLevelBox>),
411}
412
413impl WeakInlineItem {
414    pub(crate) fn upgrade(&self) -> Option<InlineItem> {
415        Some(match self {
416            Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
417            Self::EndInlineBox(inline_box) => InlineItem::EndInlineBox(inline_box.upgrade()?),
418            Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
419            Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
420                InlineItem::OutOfFlowAbsolutelyPositionedBox(
421                    positioned_box.upgrade()?,
422                    *offset_in_text,
423                )
424            },
425            Self::OutOfFlowFloatBox(float_box) => {
426                InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
427            },
428            Self::Atomic(atomic, offset_in_text, bidi_level) => {
429                InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
430            },
431            Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
432        })
433    }
434}
435
436/// Information about the current line under construction for a particular
437/// [`InlineFormattingContextLayout`]. This tracks position and size information while
438/// [`LineItem`]s are collected and is used as input when those [`LineItem`]s are
439/// converted into [`Fragment`]s during the final phase of line layout. Note that this
440/// does not store the [`LineItem`]s themselves, as they are stored as part of the
441/// nesting state in the [`InlineFormattingContextLayout`].
442struct LineUnderConstruction {
443    /// The position where this line will start once it is laid out. This includes any
444    /// offset from `text-indent`.
445    start_position: LogicalVec2<Au>,
446
447    /// The current inline position in the line being laid out into [`LineItem`]s in this
448    /// [`InlineFormattingContext`] independent of the depth in the nesting level.
449    inline_position: Au,
450
451    /// The maximum block size of all boxes that ended and are in progress in this line.
452    /// This uses [`LineBlockSizes`] instead of a simple value, because the final block size
453    /// depends on vertical alignment.
454    max_block_size: LineBlockSizes,
455
456    /// Whether any active linebox has added a glyph or atomic element to this line, which
457    /// indicates that the next run that exceeds the line length can cause a line break.
458    has_content: bool,
459
460    /// Whether or not there are floats that did not fit on the current line. Before
461    /// the [`LineItem`]s of this line are laid out, these floats will need to be
462    /// placed directly below this line, but still as children of this line's Fragments.
463    has_floats_waiting_to_be_placed: bool,
464
465    /// A rectangular area (relative to the containing block / inline formatting
466    /// context boundaries) where we can fit the line box without overlapping floats.
467    /// Note that when this is not empty, its start corner takes precedence over
468    /// [`LineUnderConstruction::start_position`].
469    placement_among_floats: OnceCell<LogicalRect<Au>>,
470
471    /// The LineItems for the current line under construction that have already
472    /// been committed to this line.
473    line_items: Vec<LineItem>,
474
475    /// Whether the current line is for a block-level box.
476    for_block_level: bool,
477
478    /// If this line is empty and contains a selection, this field will be used to create
479    /// an empty [`TextFragment`] for holding a text caret.
480    caret_placeholder: Option<CaretPlaceholder>,
481
482    /// The amount of space that will be taken up by all inline padding, borders and
483    /// margins of all inline boxes with `box-decoration-break: clone` that we are
484    /// currently inside of.
485    cloneable_inline_box_pbm_size: LogicalSides1D<Au>,
486
487    /// The inline size of any trailing hangable white space in this line.
488    trailing_hangable_white_space: Au,
489
490    /// The inline size of any trailing removable white space in this line.
491    trailing_removable_white_space: Au,
492}
493
494impl LineUnderConstruction {
495    fn new(start_position: LogicalVec2<Au>) -> Self {
496        Self {
497            inline_position: start_position.inline,
498            start_position,
499            max_block_size: LineBlockSizes::zero(),
500            has_content: false,
501            has_floats_waiting_to_be_placed: false,
502            placement_among_floats: OnceCell::new(),
503            line_items: Vec::new(),
504            for_block_level: false,
505            caret_placeholder: None,
506            cloneable_inline_box_pbm_size: Default::default(),
507            trailing_hangable_white_space: Au::zero(),
508            trailing_removable_white_space: Au::zero(),
509        }
510    }
511
512    fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
513        self.placement_among_floats.take();
514        let _ = self.placement_among_floats.set(new_placement);
515    }
516
517    /// Trim the trailing removable white space in this line, returning the amount of
518    /// white space that hangs.
519    ///
520    /// This is <https://www.w3.org/TR/css-text-3/#white-space-phase-2> steps 3 and 4.
521    ///
522    /// > - Step 3. A sequence of collapsible spaces at the end of a line is removed, as well
523    /// >   as any trailing U+1680 OGHAM SPACE MARK whose white-space property is normal,
524    /// >   nowrap, or pre-line.
525    /// > - Step 4. If there remains any sequence of white space, other space separators,
526    /// >   and/or preserved tabs at the end of a line (after bidi reordering
527    /// >   [CSS-WRITING-MODES-4]):
528    /// >     * If white-space is set to normal, nowrap, or pre-line, the UA must hang this
529    /// >       sequence (unconditionally).
530    /// >     * If white-space is set to pre-wrap, the UA must (unconditionally) hang this
531    /// >       sequence, unless the sequence is followed by a forced line break, in which case it
532    /// >       must conditionally hang the sequence instead. It may also visually collapse the
533    /// >       character advance widths of any that would otherwise overflow.
534    /// >    * If white-space is set to break-spaces, spaces, tabs, and other space separators
535    /// >      are treated the same as other visible characters: they cannot hang nor have their
536    /// >      advance width collapsed.
537    fn trim_trailing_white_space_and_calculate_hanging(
538        &mut self,
539        available_inline_size: Au,
540        last_line_or_forced_line_break: bool,
541    ) -> Au {
542        let mut conditionally_hanging = Au::zero();
543        let mut unconditionally_hanging = Au::zero();
544        let mut removed = Au::zero();
545        let mut white_space_still_hanging_conditionally = last_line_or_forced_line_break;
546
547        for item in self.line_items.iter_mut().rev() {
548            let text_run = match item {
549                LineItem::InlineStartBoxPaddingBorderMargin(_) |
550                LineItem::InlineEndBoxPaddingBorderMargin(_) |
551                LineItem::Float(..) |
552                LineItem::AbsolutelyPositioned(..) => continue,
553                LineItem::Atomic(..) | LineItem::BlockLevel(..) => break,
554                LineItem::Tab {
555                    conditionally_hangs,
556                    advance,
557                    ..
558                } => {
559                    if !*conditionally_hangs {
560                        break;
561                    }
562
563                    if white_space_still_hanging_conditionally {
564                        conditionally_hanging += *advance;
565                    } else {
566                        unconditionally_hanging += *advance;
567                    }
568
569                    continue;
570                },
571                LineItem::TextRun(_, text_run) => text_run,
572            };
573
574            let (mut count, preceded_by_content) = text_run.trailing_white_space();
575            if conditionally_hanging.is_zero() && unconditionally_hanging.is_zero() {
576                text_run.trim_removable_white_space_at_end();
577                removed += count.removable;
578            } else {
579                count.hangable += count.removable;
580            }
581
582            let style = text_run.text_fragment_run_data.inline_styles.style.borrow();
583            if white_space_still_hanging_conditionally && style.white_space_may_hang_conditionally()
584            {
585                conditionally_hanging += count.hangable;
586            } else {
587                white_space_still_hanging_conditionally = false;
588                unconditionally_hanging += count.hangable;
589            }
590
591            if preceded_by_content {
592                break;
593            }
594        }
595
596        self.trailing_removable_white_space = Au::zero();
597        self.inline_position -= removed;
598
599        if !last_line_or_forced_line_break {
600            return unconditionally_hanging + conditionally_hanging;
601        }
602
603        let overflow = (self.inline_position - available_inline_size).max(Au::zero());
604        if overflow < conditionally_hanging {
605            overflow
606        } else {
607            unconditionally_hanging + conditionally_hanging
608        }
609    }
610
611    /// Count the number of justification opportunities in this line.
612    fn count_justification_opportunities(&self) -> usize {
613        let mut saw_content = false;
614        self.line_items
615            .iter()
616            .rev()
617            .filter_map(|item| match item {
618                LineItem::TextRun(_, text_run) => Some(
619                    text_run
620                        .text
621                        .iter()
622                        .rev()
623                        .map(|shaped_text_slice| {
624                            let mut word_separators = shaped_text_slice.total_word_separators();
625                            if !saw_content {
626                                word_separators -=
627                                    shaped_text_slice.hanging_and_removable_word_separators();
628                            }
629                            saw_content |=
630                                shaped_text_slice.has_non_hangable_non_removable_content();
631                            word_separators
632                        })
633                        .sum::<usize>(),
634                ),
635                LineItem::Atomic(..) | LineItem::Tab { .. } => {
636                    saw_content = true;
637                    None
638                },
639                _ => None,
640            })
641            .sum()
642    }
643
644    /// Whether this is a phantom line box.
645    /// <https://drafts.csswg.org/css-inline-3/#invisible-line-boxes>
646    fn is_phantom(&self) -> bool {
647        Self::is_phantom_inner(self.has_content, &self.line_items)
648    }
649
650    fn is_phantom_inner(has_content: bool, line_items: &[LineItem]) -> bool {
651        if has_content {
652            return false;
653        }
654
655        // If the line has no content it might have padding, border, or margin
656        // added by inline boxes.
657        !line_items.iter().any(|item| match item {
658            LineItem::InlineStartBoxPaddingBorderMargin(state) => state.has_pbm_inline_start(),
659            LineItem::InlineEndBoxPaddingBorderMargin(state) => state.has_pbm_inline_end(),
660            _ => false,
661        })
662    }
663
664    /// The advance of the whitespace at the end of this line that would either hang
665    /// or be trimmed if it was at the end of a line.
666    ///
667    /// Note: This is only valid during line construction, because it doesn't take
668    /// into account conditional hanging.
669    fn tentative_hanging_and_removable_white_space_advance(&self) -> Au {
670        self.trailing_hangable_white_space + self.trailing_removable_white_space
671    }
672}
673
674/// A block size relative to a line's final baseline. This is to track the size
675/// contribution of a particular element of a line above and below the baseline.
676/// These sizes can be combined with other baseline relative sizes before the
677/// final baseline position is known. The values here are relative to the
678/// overall line's baseline and *not* the nested baseline of an inline box.
679#[derive(Clone, Debug)]
680struct BaselineRelativeSize {
681    /// The ascent above the baseline, where a positive value means a larger
682    /// ascent. Thus, the top of this size contribution is `baseline_offset -
683    /// ascent`.
684    ascent: Au,
685
686    /// The descent below the baseline, where a positive value means a larger
687    /// descent. Thus, the bottom of this size contribution is `baseline_offset +
688    /// descent`.
689    descent: Au,
690}
691
692impl BaselineRelativeSize {
693    fn zero() -> Self {
694        Self {
695            ascent: Au::zero(),
696            descent: Au::zero(),
697        }
698    }
699
700    fn max(&self, other: &Self) -> Self {
701        BaselineRelativeSize {
702            ascent: self.ascent.max(other.ascent),
703            descent: self.descent.max(other.descent),
704        }
705    }
706
707    /// Given an offset from the line's root baseline, adjust this [`BaselineRelativeSize`]
708    /// by that offset. This is used to adjust a [`BaselineRelativeSize`] for different kinds
709    /// of baseline-relative `vertical-align`. This will "move" measured size of a particular
710    /// inline box's block size. For example, in the following HTML:
711    ///
712    /// ```html
713    ///     <div>
714    ///         <span style="vertical-align: 5px">child content</span>
715    ///     </div>
716    /// ````
717    ///
718    /// If this [`BaselineRelativeSize`] is for the `<span>` then the adjustment
719    /// passed here would be equivalent to -5px.
720    fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
721        self.ascent -= baseline_offset;
722        self.descent += baseline_offset;
723    }
724}
725
726#[derive(Clone, Debug)]
727struct LineBlockSizes {
728    line_height: Au,
729    baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
730    size_for_baseline_positioning: BaselineRelativeSize,
731}
732
733impl LineBlockSizes {
734    fn zero() -> Self {
735        LineBlockSizes {
736            line_height: Au::zero(),
737            baseline_relative_size_for_line_height: None,
738            size_for_baseline_positioning: BaselineRelativeSize::zero(),
739        }
740    }
741
742    fn resolve(&self) -> Au {
743        let height_from_ascent_and_descent = self
744            .baseline_relative_size_for_line_height
745            .as_ref()
746            .map(|size| (size.ascent + size.descent).abs())
747            .unwrap_or_else(Au::zero);
748        self.line_height.max(height_from_ascent_and_descent)
749    }
750
751    fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
752        let baseline_relative_size = match (
753            self.baseline_relative_size_for_line_height.as_ref(),
754            other.baseline_relative_size_for_line_height.as_ref(),
755        ) {
756            (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
757            (our_size, other_size) => our_size.or(other_size).cloned(),
758        };
759        Self {
760            line_height: self.line_height.max(other.line_height),
761            baseline_relative_size_for_line_height: baseline_relative_size,
762            size_for_baseline_positioning: self
763                .size_for_baseline_positioning
764                .max(&other.size_for_baseline_positioning),
765        }
766    }
767
768    fn max_assign(&mut self, other: &LineBlockSizes) {
769        *self = self.max(other);
770    }
771
772    fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
773        if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
774            size.adjust_for_nested_baseline_offset(baseline_offset)
775        }
776        self.size_for_baseline_positioning
777            .adjust_for_nested_baseline_offset(baseline_offset);
778    }
779
780    /// From <https://drafts.csswg.org/css2/visudet.html#line-height>:
781    ///  > The inline-level boxes are aligned vertically according to their 'vertical-align'
782    ///  > property. In case they are aligned 'top' or 'bottom', they must be aligned so as
783    ///  > to minimize the line box height. If such boxes are tall enough, there are multiple
784    ///  > solutions and CSS 2 does not define the position of the line box's baseline (i.e.,
785    ///  > the position of the strut, see below).
786    fn find_baseline_offset(&self) -> Au {
787        match self.baseline_relative_size_for_line_height.as_ref() {
788            Some(size) => size.ascent,
789            None => {
790                // This is the case mentinoned above where there are multiple solutions.
791                // This code is putting the baseline roughly in the middle of the line.
792                let leading = self.resolve() -
793                    (self.size_for_baseline_positioning.ascent +
794                        self.size_for_baseline_positioning.descent);
795                leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
796            },
797        }
798    }
799}
800
801/// The current unbreakable segment under construction for an inline formatting context.
802/// Items accumulate here until we reach a soft line break opportunity during processing
803/// of inline content or we reach the end of the formatting context.
804struct UnbreakableSegmentUnderConstruction {
805    /// The size of this unbreakable segment in both dimension.
806    inline_size: Au,
807
808    /// The maximum block size that this segment has. This uses [`LineBlockSizes`] instead of a
809    /// simple value, because the final block size depends on vertical alignment.
810    max_block_size: LineBlockSizes,
811
812    /// The LineItems for the segment under construction
813    line_items: Vec<LineItem>,
814
815    /// Whether any active linebox has added a glyph or atomic element to this line
816    /// segment, which indicates that the next run that exceeds the line length can cause
817    /// a line break.
818    has_content: bool,
819
820    /// Whether or not this segment incorporates the trailing white space that came before
821    /// it on the line into the line's content.
822    incorporates_trailing_white_space: bool,
823
824    /// The inline size of any trailing hangable white space in this segment.
825    trailing_hangable_white_space: Au,
826
827    /// The inline size of any trailing removable white space in this segment.
828    trailing_removable_white_space: Au,
829}
830
831impl UnbreakableSegmentUnderConstruction {
832    fn new() -> Self {
833        Self {
834            inline_size: Au::zero(),
835            max_block_size: LineBlockSizes {
836                line_height: Au::zero(),
837                baseline_relative_size_for_line_height: None,
838                size_for_baseline_positioning: BaselineRelativeSize::zero(),
839            },
840            line_items: Vec::new(),
841            has_content: false,
842            trailing_hangable_white_space: Au::zero(),
843            trailing_removable_white_space: Au::zero(),
844            incorporates_trailing_white_space: false,
845        }
846    }
847
848    /// Reset this segment after its contents have been committed to a line.
849    fn reset(&mut self) {
850        assert!(self.line_items.is_empty()); // Preserve allocated memory.
851        self.inline_size = Au::zero();
852        self.max_block_size = LineBlockSizes::zero();
853        self.has_content = false;
854        self.trailing_hangable_white_space = Au::zero();
855        self.trailing_removable_white_space = Au::zero();
856        self.incorporates_trailing_white_space = false;
857    }
858
859    /// Push a single line item to this segment.
860    fn push_line_item(&mut self, line_item: LineItem) {
861        self.line_items.push(line_item);
862    }
863
864    /// Trim whitespace from the beginning of this UnbreakbleSegmentUnderConstruction.
865    ///
866    /// From <https://www.w3.org/TR/css-text-3/#white-space-phase-2>:
867    ///
868    /// > Then, the entire block is rendered. Inlines are laid out, taking bidi
869    /// > reordering into account, and wrapping as specified by the text-wrap
870    /// > property. As each line is laid out,
871    /// >  1. A sequence of collapsible spaces at the beginning of a line is removed.
872    ///
873    /// This prevents whitespace from being added to the beginning of a line.
874    fn trim_leading_whitespace(&mut self) {
875        let mut whitespace_trimmed = Au::zero();
876        for item in self.line_items.iter_mut() {
877            if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
878                break;
879            }
880        }
881        self.inline_size -= whitespace_trimmed;
882
883        // If there is no content in this segment, then trimming the leading whitespace may have
884        // also trimmed some of the trailing white space, so we need to adjust those lengths as
885        // well.
886        if !self.incorporates_trailing_white_space {
887            let trimmed_from_hanging = whitespace_trimmed.min(self.trailing_hangable_white_space);
888            self.trailing_hangable_white_space -= trimmed_from_hanging;
889            self.trailing_removable_white_space -= whitespace_trimmed - trimmed_from_hanging;
890        }
891    }
892
893    /// Whether this is segment is phantom. If false, its line box won't be phantom.
894    /// <https://drafts.csswg.org/css-inline-3/#invisible-line-boxes>
895    fn is_phantom(&self) -> bool {
896        LineUnderConstruction::is_phantom_inner(self.has_content, &self.line_items)
897    }
898
899    /// The size of the inline padding, border, and margin start edge of all trailing
900    /// opening inline boxes on this segment. In addition, if those inline boxes will
901    /// clone their box decorations, include the size of the cloned end padding, border,
902    /// and margin as well.
903    fn trailing_inline_box_start_size(&self) -> Au {
904        self.line_items
905            .iter()
906            .rev()
907            .map_while(|item| match item {
908                LineItem::InlineStartBoxPaddingBorderMargin(state) => Some(state),
909                _ => None,
910            })
911            .map(|state| {
912                if state.should_clone_pbm() {
913                    state.pbm_inline_start() + state.pbm_inline_end()
914                } else {
915                    state.pbm_inline_start()
916                }
917            })
918            .sum()
919    }
920
921    /// The advance of the whitespace at the end of this segment that would either hang
922    /// or be trimmed if it was at the end of a line.
923    fn hanging_and_removable_white_space_advance(&self) -> Au {
924        self.trailing_hangable_white_space + self.trailing_removable_white_space
925    }
926}
927
928bitflags! {
929    struct InlineContainerStateFlags: u8 {
930        const CREATE_STRUT = 0b0001;
931        const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
932    }
933}
934
935struct InlineContainerState {
936    /// The style of this inline container.
937    style: ServoArc<ComputedValues>,
938
939    /// Flags which describe details of this [`InlineContainerState`].
940    flags: InlineContainerStateFlags,
941
942    /// Whether or not we have processed any content (an atomic element or text) for
943    /// this inline box on the current line OR any previous line.
944    has_content: Cell<bool>,
945
946    /// The block size contribution of this container's default font ie the size of the
947    /// "strut." Whether this is integrated into the [`Self::nested_strut_block_sizes`]
948    /// depends on the line-height quirk described in
949    /// <https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk>.
950    strut_block_sizes: LineBlockSizes,
951
952    /// The strut block size of this inline container maxed with the strut block
953    /// sizes of all inline container ancestors. In quirks mode, this will be
954    /// zero, until we know that an element has inline content.
955    nested_strut_block_sizes: LineBlockSizes,
956
957    /// The baseline offset of this container from the baseline of the line. The is the
958    /// cumulative offset of this container and all of its parents. In contrast to the
959    /// `vertical-align` property a positive value indicates an offset "below" the
960    /// baseline while a negative value indicates one "above" it (when the block direction
961    /// is vertical).
962    pub baseline_offset: Au,
963
964    /// The primary font used for this container, if one exists. This is the font that is
965    /// used when not falling back.
966    default_font: Option<FontRef>,
967
968    /// The font metrics of the non-fallback font for this container.
969    font_metrics: Arc<FontMetrics>,
970}
971
972struct InlineFormattingContextLayout<'layout_data> {
973    positioning_context: &'layout_data mut PositioningContext,
974    placement_state: PlacementState<'layout_data>,
975    sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
976    layout_context: &'layout_data LayoutContext<'layout_data>,
977
978    /// The [`InlineFormattingContext`] that we are laying out.
979    ifc: &'layout_data InlineFormattingContext,
980
981    /// The [`InlineContainerState`] for the container formed by the root of the
982    /// [`InlineFormattingContext`]. This is effectively the "root inline box" described
983    /// by <https://drafts.csswg.org/css-inline/#model>:
984    ///
985    /// > The block container also generates a root inline box, which is an anonymous
986    /// > inline box that holds all of its inline-level contents. (Thus, all text in an
987    /// > inline formatting context is directly contained by an inline box, whether the root
988    /// > inline box or one of its descendants.) The root inline box inherits from its
989    /// > parent block container, but is otherwise unstyleable.
990    root_nesting_level: InlineContainerState,
991
992    /// A stack of [`InlineBoxContainerState`] that is used to produce [`LineItem`]s either when we
993    /// reach the end of an inline box or when we reach the end of a line. Only at the end
994    /// of the inline box is the state popped from the stack.
995    inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
996
997    /// The amount of space that will be taken up by all inline padding, borders and
998    /// margins of all inline boxes with `box-decoration-break: clone` that we are
999    /// currently inside of.
1000    cloneable_inline_box_pbm_size: LogicalSides1D<Au>,
1001
1002    /// A collection of [`InlineBoxContainerState`] of all the inlines that are present
1003    /// in this inline formatting context. We keep this as well as the stack, so that we
1004    /// can access them during line layout, which may happen after relevant [`InlineBoxContainerState`]s
1005    /// have been popped of the stack.
1006    inline_box_states: Vec<Rc<InlineBoxContainerState>>,
1007
1008    /// A vector of fragment that are laid out. This includes one [`Fragment::Positioning`]
1009    /// per line that is currently laid out plus fragments for all floats, which
1010    /// are currently laid out at the top-level of each [`InlineFormattingContext`].
1011    fragments: Vec<Fragment>,
1012
1013    /// Information about the line currently being laid out into [`LineItem`]s.
1014    current_line: LineUnderConstruction,
1015
1016    /// Information about the unbreakable line segment currently being laid out into [`LineItem`]s.
1017    current_line_segment: UnbreakableSegmentUnderConstruction,
1018
1019    /// After a forced line break (for instance from a `<br>` element) we wait to actually
1020    /// break the line until seeing more content. This allows ongoing inline boxes to finish,
1021    /// since in the case where they have no more content they should not be on the next
1022    /// line.
1023    ///
1024    /// For instance:
1025    ///
1026    /// ``` html
1027    ///    <span style="border-right: 30px solid blue;">
1028    ///         first line<br>
1029    ///    </span>
1030    ///    second line
1031    /// ```
1032    ///
1033    /// In this case, the `<span>` should not extend to the second line. If we linebreak
1034    /// as soon as we encounter the `<br>` the `<span>`'s ending inline borders would be
1035    /// placed on the second line, because we add those borders in
1036    /// [`InlineFormattingContextLayout::finish_inline_box()`].
1037    ///
1038    /// If this field is `true`, a hard line break should be processed before any new content.
1039    force_line_break_before_new_content: bool,
1040
1041    /// When deferring a forced line break, this field stores a potential caret placeholder
1042    /// used to create a [`TextFragment`] to hold a caret on an otherwise empty line.
1043    caret_placeholder: Option<CaretPlaceholder>,
1044
1045    /// When a `<br>` element has `clear`, this needs to be applied after the linebreak,
1046    /// which will be processed *after* the `<br>` element is processed. This member
1047    /// stores any deferred `clear` to apply after a linebreak.
1048    deferred_br_clear: Clear,
1049
1050    /// Whether or not a soft wrap opportunity is queued. Soft wrap opportunities are
1051    /// queued after replaced content and they are processed when the next text content
1052    /// is encountered.
1053    pub have_deferred_soft_wrap_opportunity: bool,
1054
1055    /// Whether or not the layout of this InlineFormattingContext depends on the block size
1056    /// of its container for the purposes of flexbox layout.
1057    depends_on_block_constraints: bool,
1058
1059    /// The currently white-space-collapse setting of this line. This is stored on the
1060    /// [`InlineFormattingContextLayout`] because when a soft wrap opportunity is defined
1061    /// by the boundary between two characters, the white-space-collapse property of their
1062    /// nearest common ancestor is used.
1063    white_space_collapse: WhiteSpaceCollapse,
1064
1065    /// The currently text-wrap-mode setting of this line. This is stored on the
1066    /// [`InlineFormattingContextLayout`] because when a soft wrap opportunity is defined
1067    /// by the boundary between two characters, the text-wrap-mode property of their nearest
1068    /// common ancestor is used.
1069    text_wrap_mode: TextWrapMode,
1070
1071    /// Whether block-level boxes inside this inline formatting context should ignore their
1072    /// margins for the purpose of stretching in the block axis.
1073    ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1074}
1075
1076impl InlineFormattingContextLayout<'_> {
1077    fn current_inline_container_state(&self) -> &InlineContainerState {
1078        match self.inline_box_state_stack.last() {
1079            Some(inline_box_state) => &inline_box_state.base,
1080            None => &self.root_nesting_level,
1081        }
1082    }
1083
1084    fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
1085        self.inline_box_state_stack
1086            .last()
1087            .map(|state| state.identifier)
1088    }
1089
1090    fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
1091        self.current_inline_container_state()
1092            .nested_strut_block_sizes
1093            .max(&self.current_line.max_block_size)
1094    }
1095
1096    fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
1097        self.current_line.placement_among_floats.get().map_or(
1098            self.current_line.start_position.block,
1099            |placement_among_floats| placement_among_floats.start_corner.block,
1100        )
1101    }
1102
1103    fn propagate_current_nesting_level_white_space_style(&mut self) {
1104        let style = match self.inline_box_state_stack.last() {
1105            Some(inline_box_state) => &inline_box_state.base.style,
1106            None => self.placement_state.containing_block.style,
1107        };
1108        let style_text = style.get_inherited_text();
1109        self.white_space_collapse = style_text.white_space_collapse;
1110        self.text_wrap_mode = style_text.text_wrap_mode;
1111    }
1112
1113    fn processing_br_element(&self) -> bool {
1114        self.inline_box_state_stack.last().is_some_and(|state| {
1115            state
1116                .base_fragment_info
1117                .flags
1118                .contains(FragmentFlags::IS_BR_ELEMENT)
1119        })
1120    }
1121
1122    /// Start laying out a particular [`InlineBox`] into line items. This will push
1123    /// a new [`InlineBoxContainerState`] onto [`Self::inline_box_state_stack`].
1124    fn start_inline_box(&mut self, inline_box: &InlineBox) {
1125        let containing_block = self.containing_block();
1126        let inline_box_state = InlineBoxContainerState::new(
1127            inline_box,
1128            containing_block,
1129            self.layout_context,
1130            self.current_inline_container_state(),
1131            inline_box.default_font.clone(),
1132        );
1133
1134        self.depends_on_block_constraints |= inline_box
1135            .base
1136            .style
1137            .depends_on_block_constraints_due_to_relative_positioning(
1138                containing_block.style.writing_mode,
1139            );
1140
1141        // If we are starting a `<br>` element prepare to clear after its deferred linebreak has been
1142        // processed. Note that a `<br>` is composed of the element itself and the inner pseudo-element
1143        // with the actual linebreak. Both will have this `FragmentFlag`; that's why this code only
1144        // sets `deferred_br_clear` if it isn't set yet.
1145        if inline_box_state
1146            .base_fragment_info
1147            .flags
1148            .contains(FragmentFlags::IS_BR_ELEMENT) &&
1149            self.deferred_br_clear == Clear::None
1150        {
1151            self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
1152                &inline_box_state.base.style,
1153                self.containing_block().style.writing_mode,
1154            );
1155        }
1156
1157        let inline_box_state = Rc::new(inline_box_state);
1158        let pbm_inline_start = inline_box_state.pbm_inline_start();
1159        self.current_line_segment.inline_size += pbm_inline_start;
1160
1161        if inline_box_state.should_clone_pbm() {
1162            self.cloneable_inline_box_pbm_size.start += pbm_inline_start;
1163            self.cloneable_inline_box_pbm_size.end += inline_box_state.pbm_inline_end();
1164        }
1165
1166        self.current_line_segment
1167            .line_items
1168            .push(LineItem::InlineStartBoxPaddingBorderMargin(
1169                inline_box_state.clone(),
1170            ));
1171
1172        // Push the state onto the IFC-wide collection of states. Inline boxes are numbered in
1173        // the order that they are encountered, so this should correspond to the order they
1174        // are pushed onto `self.inline_box_states`.
1175        assert_eq!(
1176            self.inline_box_states.len(),
1177            inline_box.identifier.index_in_inline_boxes as usize
1178        );
1179        self.inline_box_states.push(inline_box_state.clone());
1180        self.inline_box_state_stack.push(inline_box_state);
1181    }
1182
1183    /// Finish laying out a particular [`InlineBox`] into line items. This will
1184    /// pop its state off of [`Self::inline_box_state_stack`].
1185    fn finish_inline_box(&mut self) {
1186        let inline_box_state = match self.inline_box_state_stack.pop() {
1187            Some(inline_box_state) => inline_box_state,
1188            None => return, // We are at the root.
1189        };
1190
1191        self.current_line_segment
1192            .max_block_size
1193            .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1194
1195        // If the inline box that we just finished had any content at all, we want to propagate
1196        // the `white-space` property of its parent to future inline children. This is because
1197        // when a soft wrap opportunity is defined by the boundary between two elements, the
1198        // `white-space` used is that of their nearest common ancestor.
1199        if inline_box_state.base.has_content.get() {
1200            self.propagate_current_nesting_level_white_space_style();
1201        }
1202
1203        let pbm_inline_end = inline_box_state.pbm_inline_end();
1204        self.current_line_segment.inline_size += pbm_inline_end;
1205
1206        if inline_box_state.should_clone_pbm() {
1207            self.cloneable_inline_box_pbm_size.start -= inline_box_state.pbm_inline_start();
1208            self.cloneable_inline_box_pbm_size.end -= pbm_inline_end;
1209        }
1210
1211        self.current_line_segment
1212            .line_items
1213            .push(LineItem::InlineEndBoxPaddingBorderMargin(inline_box_state));
1214    }
1215
1216    fn finish_last_line(&mut self) {
1217        // First, process any deferred forced line breaks.
1218        self.possibly_flush_deferred_forced_line_break();
1219
1220        // We are at the end of the IFC, and we need to do a few things to make sure that
1221        // the current segment is committed and that the final line is finished.
1222        //
1223        // A soft wrap opportunity makes it so the current segment is placed on a new line
1224        // if it doesn't fit on the current line under construction.
1225        self.process_soft_wrap_opportunity();
1226
1227        // `process_soft_line_wrap_opportunity` does not commit the segment to a line if
1228        // there is no line wrapping, so this forces the segment into the current line.
1229        self.commit_current_segment_to_line();
1230
1231        // Finally we finish the line itself and convert all of the LineItems into
1232        // fragments.
1233        self.finish_current_line_and_reset(
1234            true,  /* last_line_or_forced_line_break */
1235            false, /* for_block_level */
1236        );
1237    }
1238
1239    /// Finish layout of all inline boxes for the current line. This will gather all
1240    /// [`LineItem`]s and turn them into [`Fragment`]s, then reset the
1241    /// [`InlineFormattingContextLayout`] preparing it for laying out a new line.
1242    fn finish_current_line_and_reset(
1243        &mut self,
1244        last_line_or_forced_line_break: bool,
1245        for_block_level: bool,
1246    ) {
1247        let hanging_white_space = self
1248            .current_line
1249            .trim_trailing_white_space_and_calculate_hanging(
1250                self.available_line_space(),
1251                last_line_or_forced_line_break,
1252            );
1253
1254        self.possibly_push_empty_text_run_to_line_for_text_caret();
1255
1256        // Add space for any cloneable padding, border, and margin ends that will
1257        // be drawn for this line.
1258        if !self.current_line.for_block_level {
1259            self.current_line.inline_position +=
1260                self.current_line.cloneable_inline_box_pbm_size.end;
1261        }
1262
1263        let (inline_start_position, justification_adjustment) = self
1264            .calculate_current_line_inline_start_and_justification_adjustment(
1265                hanging_white_space,
1266                last_line_or_forced_line_break,
1267            );
1268
1269        // https://drafts.csswg.org/css-inline-3/#invisible-line-boxes
1270        // > Line boxes that contain no text, no preserved white space, no inline boxes with non-zero
1271        // > inline-axis margins, padding, or borders, and no other in-flow content (such as atomic
1272        // > inlines or ruby annotations), and do not end with a forced line break are phantom line boxes.
1273        // > Such boxes must be treated as zero-height line boxes for the purposes of determining the
1274        // > positions of any descendant content (such as absolutely positioned boxes), and both the
1275        // > line box and its in-flow content must be treated as not existing for any other layout or
1276        // > rendering purpose.
1277        let is_phantom_line = self.current_line.is_phantom();
1278        if !is_phantom_line {
1279            self.current_line.start_position.block += self.placement_state.current_margin.solve();
1280            self.placement_state.current_margin = CollapsedMargin::zero();
1281        }
1282        let block_start_position =
1283            self.current_line_block_start_considering_placement_among_floats();
1284
1285        let effective_block_advance = if is_phantom_line {
1286            LineBlockSizes::zero()
1287        } else {
1288            self.current_line_max_block_size_including_nested_containers()
1289        };
1290
1291        let resolved_block_advance = effective_block_advance.resolve();
1292        let block_end_position = if self.current_line.for_block_level {
1293            self.placement_state.current_block_direction_position
1294        } else {
1295            let mut block_end_position = block_start_position + resolved_block_advance;
1296            if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1297                if !is_phantom_line {
1298                    sequential_layout_state.commit_margin();
1299                }
1300
1301                // This amount includes both the block size of the line and any extra space
1302                // added to move the line down in order to avoid overlapping floats.
1303                let increment = block_end_position - self.current_line.start_position.block;
1304                sequential_layout_state.advance_block_position(increment);
1305
1306                // This newline may have been triggered by a `<br>` with clearance, in which case we
1307                // want to make sure that we make space not only for the current line, but any clearance
1308                // from floats.
1309                if let Some(clearance) = sequential_layout_state
1310                    .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1311                {
1312                    sequential_layout_state.advance_block_position(clearance);
1313                    block_end_position += clearance;
1314                };
1315                self.deferred_br_clear = Clear::None;
1316            }
1317            block_end_position
1318        };
1319
1320        // Set up the new line now that we no longer need the old one.
1321        let line_to_layout = std::mem::replace(
1322            &mut self.current_line,
1323            LineUnderConstruction::new(LogicalVec2 {
1324                inline: Au::zero(),
1325                block: block_end_position,
1326            }),
1327        );
1328        self.current_line.for_block_level = for_block_level;
1329
1330        // Preserve the current state of the cloneable inline box PBM size.
1331        self.current_line.cloneable_inline_box_pbm_size =
1332            line_to_layout.cloneable_inline_box_pbm_size;
1333        // Reserve space for any cloned inline box starts that will be added to the new line, but
1334        // only if this is not for a block level.
1335        if !self.current_line.for_block_level {
1336            self.current_line.inline_position +=
1337                self.current_line.cloneable_inline_box_pbm_size.start;
1338        }
1339
1340        if !line_to_layout.for_block_level {
1341            self.placement_state.current_block_direction_position = block_end_position;
1342        }
1343
1344        if line_to_layout.has_floats_waiting_to_be_placed {
1345            place_pending_floats(self, &line_to_layout.line_items);
1346        }
1347
1348        let start_position = LogicalVec2 {
1349            block: block_start_position,
1350            inline: inline_start_position,
1351        };
1352
1353        let baseline_offset = effective_block_advance.find_baseline_offset();
1354        let start_positioning_context_length = self.positioning_context.len();
1355        let fragments = LineItemLayout::layout_line_items(
1356            self,
1357            line_to_layout.line_items,
1358            start_position,
1359            &effective_block_advance,
1360            justification_adjustment,
1361            is_phantom_line,
1362            line_to_layout.for_block_level,
1363        );
1364
1365        if !is_phantom_line {
1366            let baseline = baseline_offset + block_start_position;
1367            self.placement_state
1368                .inflow_baselines
1369                .first
1370                .get_or_insert(baseline);
1371            self.placement_state.inflow_baselines.last = Some(baseline);
1372            self.placement_state
1373                .next_in_flow_margin_collapses_with_parent_start_margin = false;
1374        }
1375
1376        // If the line doesn't have any fragments, we don't need to add a containing fragment for it.
1377        if fragments.is_empty() &&
1378            self.positioning_context.len() == start_positioning_context_length
1379        {
1380            return;
1381        }
1382
1383        // The inline part of this start offset was taken into account when determining
1384        // the inline start of the line in `calculate_inline_start_for_current_line` so
1385        // we do not need to include it in the `start_corner` of the line's main Fragment.
1386        let start_corner = LogicalVec2 {
1387            inline: Au::zero(),
1388            block: block_start_position,
1389        };
1390
1391        let logical_origin_in_physical_coordinates =
1392            start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1393        self.positioning_context
1394            .adjust_static_position_of_hoisted_fragments_with_offset(
1395                &logical_origin_in_physical_coordinates,
1396                start_positioning_context_length,
1397            );
1398
1399        let containing_block = self.containing_block();
1400        let physical_line_rect = LogicalRect {
1401            start_corner,
1402            size: LogicalVec2 {
1403                inline: containing_block.size.inline,
1404                block: effective_block_advance.resolve(),
1405            },
1406        }
1407        .as_physical(Some(containing_block));
1408        self.fragments
1409            .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1410                self.root_nesting_level.style.clone(),
1411                physical_line_rect,
1412                fragments,
1413                true, /* is_line_box */
1414            )));
1415    }
1416
1417    fn available_line_space(&self) -> Au {
1418        match self.current_line.placement_among_floats.get() {
1419            Some(placement_among_floats) => placement_among_floats.size.inline,
1420            None => self.containing_block().size.inline,
1421        }
1422    }
1423
1424    /// Given the amount of whitespace trimmed from the line and taking into consideration
1425    /// the `text-align` property, calculate where the line under construction starts in
1426    /// the inline axis as well as the adjustment needed for every justification opportunity
1427    /// to account for `text-align: justify`.
1428    fn calculate_current_line_inline_start_and_justification_adjustment(
1429        &self,
1430        hanging_white_space: Au,
1431        last_line_or_forced_line_break: bool,
1432    ) -> (Au, Au) {
1433        enum TextAlign {
1434            Start,
1435            Center,
1436            End,
1437        }
1438        let containing_block = self.containing_block();
1439        let style = containing_block.style;
1440        let mut text_align_keyword = style.get_text_align();
1441
1442        if last_line_or_forced_line_break {
1443            text_align_keyword = match style.get_text_align_last() {
1444                TextAlignLast::Auto if text_align_keyword == &TextAlignKeyword::Justify => {
1445                    &TextAlignKeyword::Start
1446                },
1447                TextAlignLast::Auto => text_align_keyword,
1448                TextAlignLast::Start => &TextAlignKeyword::Start,
1449                TextAlignLast::End => &TextAlignKeyword::End,
1450                TextAlignLast::Left => &TextAlignKeyword::Left,
1451                TextAlignLast::Right => &TextAlignKeyword::Right,
1452                TextAlignLast::Center => &TextAlignKeyword::Center,
1453                TextAlignLast::Justify => &TextAlignKeyword::Justify,
1454            };
1455        }
1456
1457        let text_align = match text_align_keyword {
1458            TextAlignKeyword::Start => TextAlign::Start,
1459            TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1460            TextAlignKeyword::End => TextAlign::End,
1461            TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1462                if style.writing_mode.line_left_is_inline_start() {
1463                    TextAlign::Start
1464                } else {
1465                    TextAlign::End
1466                }
1467            },
1468            TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1469                if style.writing_mode.line_left_is_inline_start() {
1470                    TextAlign::End
1471                } else {
1472                    TextAlign::Start
1473                }
1474            },
1475            TextAlignKeyword::Justify => TextAlign::Start,
1476        };
1477
1478        let available_space = self.available_line_space();
1479        let line_start = match self.current_line.placement_among_floats.get() {
1480            Some(placement_among_floats) => placement_among_floats.start_corner.inline,
1481            None => Au::zero(),
1482        };
1483
1484        // Properly handling text-indent requires that we do not align the text
1485        // into the text-indent.
1486        // See <https://drafts.csswg.org/css-text/#text-indent-property>
1487        // "This property specifies the indentation applied to lines of inline content in
1488        // a block. The indent is treated as a margin applied to the start edge of the
1489        // line box."
1490        let text_indent = self.current_line.start_position.inline;
1491        let line_length = self.current_line.inline_position - hanging_white_space - text_indent;
1492        let adjusted_line_start = line_start +
1493            match text_align {
1494                TextAlign::Start => text_indent,
1495                TextAlign::End => (available_space - line_length).max(text_indent),
1496                TextAlign::Center => (available_space - line_length + text_indent)
1497                    .scale_by(0.5)
1498                    .max(text_indent),
1499            };
1500
1501        // Calculate the justification adjustment. This is simply the remaining space on the line,
1502        // divided by the number of justification opportunities that we recorded when building
1503        // the line.
1504        let text_justify = containing_block.style.get_text_justify();
1505        let justification_adjustment = match (text_align_keyword, text_justify) {
1506            // `text-justify: none` should disable text justification.
1507            // TODO: Handle more `text-justify` values.
1508            (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1509            (TextAlignKeyword::Justify, _) => {
1510                match self.current_line.count_justification_opportunities() {
1511                    0 => Au::zero(),
1512                    num_justification_opportunities => {
1513                        (available_space - text_indent - line_length)
1514                            .scale_by(1. / num_justification_opportunities as f32)
1515                    },
1516                }
1517            },
1518            _ => Au::zero(),
1519        };
1520
1521        // If the content overflows the line, then justification adjustment will become negative. In
1522        // that case, do not make any adjustment for justification.
1523        let justification_adjustment = justification_adjustment.max(Au::zero());
1524
1525        (adjusted_line_start, justification_adjustment)
1526    }
1527
1528    fn place_float_fragment(&mut self, float: &FloatLineItem) {
1529        let state = self
1530            .sequential_layout_state
1531            .as_mut()
1532            .expect("Tried to lay out a float with no sequential placement state!");
1533
1534        let block_offset_from_containining_block_top = state
1535            .current_block_position_including_margins() -
1536            state.current_containing_block_offset();
1537        state.place_float_fragment(
1538            &float.fragment,
1539            self.placement_state.containing_block,
1540            CollapsedMargin::zero(),
1541            block_offset_from_containining_block_top,
1542        );
1543        self.positioning_context
1544            .adjust_static_position_of_hoisted_fragments_in_range(
1545                &float.fragment.base.rect().origin.to_vector(),
1546                &float.range,
1547            )
1548    }
1549
1550    /// Place a FloatLineItem. This is done when an unbreakable segment is committed to
1551    /// the current line. Placement of FloatLineItems might need to be deferred until the
1552    /// line is complete in the case that floats stop fitting on the current line.
1553    ///
1554    /// When placing floats we do not want to take into account any trailing whitespace on
1555    /// the line, because that whitespace will be trimmed in the case that the line is
1556    /// broken. Thus this function takes as an argument the new size (without whitespace) of
1557    /// the line that these floats are joining.
1558    fn place_float_line_item_for_commit_to_line(
1559        &mut self,
1560        float_item: &mut FloatLineItem,
1561        potential_line_length: Au,
1562    ) {
1563        let containing_block = self.containing_block();
1564        let float_fragment = &float_item.fragment;
1565        let logical_margin_rect_size = float_fragment
1566            .margin_rect()
1567            .size
1568            .to_logical(containing_block.style.writing_mode);
1569        let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1570
1571        let available_inline_size = match self.current_line.placement_among_floats.get() {
1572            Some(placement_among_floats) => placement_among_floats.size.inline,
1573            None => containing_block.size.inline,
1574        } - potential_line_length;
1575
1576        // If this float doesn't fit on the current line or a previous float didn't fit on
1577        // the current line, we need to place it starting at the next line BUT still as
1578        // children of this line's hierarchy of inline boxes (for the purposes of properly
1579        // parenting in their stacking contexts). Once all the line content is gathered we
1580        // will place them later.
1581        let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1582        let fits_on_line = !has_content || inline_size <= available_inline_size;
1583        let needs_placement_later =
1584            self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1585
1586        if needs_placement_later {
1587            self.current_line.has_floats_waiting_to_be_placed = true;
1588        } else {
1589            self.place_float_fragment(float_item);
1590            float_item.needs_placement = false;
1591        }
1592
1593        // We've added a new float to the IFC, but this may have actually changed the
1594        // position of the current line. In order to determine that we regenerate the
1595        // placement among floats for the current line, which may adjust its inline
1596        // start position.
1597        let new_placement = self.place_line_among_floats(&LogicalVec2 {
1598            inline: potential_line_length,
1599            block: self.current_line.max_block_size.resolve(),
1600        });
1601        self.current_line
1602            .replace_placement_among_floats(new_placement);
1603    }
1604
1605    /// Given a new potential line size for the current line, create a "placement" for that line.
1606    /// This tells us whether or not the new potential line will fit in the current block position
1607    /// or need to be moved. In addition, the placement rect determines the inline start and end
1608    /// of the line if it's used as the final placement among floats.
1609    fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1610        let sequential_layout_state = self
1611            .sequential_layout_state
1612            .as_ref()
1613            .expect("Should not have called this function without having floats.");
1614
1615        let ifc_offset_in_float_container = LogicalVec2 {
1616            inline: sequential_layout_state
1617                .floats
1618                .containing_block_info
1619                .inline_start,
1620            block: sequential_layout_state.current_containing_block_offset(),
1621        };
1622
1623        let ceiling = self.current_line_block_start_considering_placement_among_floats();
1624        let mut placement = PlacementAmongFloats::new(
1625            &sequential_layout_state.floats,
1626            ceiling + ifc_offset_in_float_container.block,
1627            LogicalVec2 {
1628                inline: potential_line_size.inline,
1629                block: potential_line_size.block,
1630            },
1631            &PaddingBorderMargin::zero(),
1632        );
1633
1634        let mut placement_rect = placement.place();
1635        placement_rect.start_corner -= ifc_offset_in_float_container;
1636        placement_rect
1637    }
1638
1639    /// Returns true if a new potential line size for the current line would require a line
1640    /// break. This takes into account floats and will also update the "placement among
1641    /// floats" for this line if the potential line size would not cause a line break.
1642    /// Thus, calling this method has side effects and should only be done while in the
1643    /// process of laying out line content that is always going to be committed to this
1644    /// line or the next.
1645    fn new_potential_line_size_causes_line_break(
1646        &mut self,
1647        potential_line_size: &LogicalVec2<Au>,
1648    ) -> bool {
1649        let containing_block = self.containing_block();
1650        let available_line_space = if self.sequential_layout_state.is_some() {
1651            self.current_line
1652                .placement_among_floats
1653                .get_or_init(|| self.place_line_among_floats(potential_line_size))
1654                .size
1655        } else {
1656            LogicalVec2 {
1657                inline: containing_block.size.inline,
1658                block: MAX_AU,
1659            }
1660        };
1661
1662        let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1663        let block_would_overflow = potential_line_size.block > available_line_space.block;
1664
1665        // The first content that is added to a line cannot trigger a line break and
1666        // the `white-space` propertly can also prevent all line breaking.
1667        let can_break = self.current_line.has_content;
1668
1669        // If this is the first content on the line and we already have a float placement,
1670        // that means that the placement was initialized by a leading float in the IFC.
1671        // This placement needs to be updated, because the first line content might push
1672        // the block start of the line downward. If there is no float placement, we want
1673        // to make one to properly set the block position of the line.
1674        if !can_break {
1675            // Even if we cannot break, adding content to this line might change its position.
1676            // In that case we need to redo our placement among floats.
1677            if self.sequential_layout_state.is_some() &&
1678                (inline_would_overflow || block_would_overflow)
1679            {
1680                let new_placement = self.place_line_among_floats(potential_line_size);
1681                self.current_line
1682                    .replace_placement_among_floats(new_placement);
1683            }
1684
1685            return false;
1686        }
1687
1688        // If the potential line is larger than the containing block we do not even need to consider
1689        // floats. We definitely have to do a linebreak.
1690        if potential_line_size.inline > containing_block.size.inline {
1691            return true;
1692        }
1693
1694        // Not fitting in the block space means that our block size has changed and we had a
1695        // placement among floats that is no longer valid. This same placement might just
1696        // need to be expanded or perhaps we need to line break.
1697        if block_would_overflow {
1698            // If we have a limited block size then we are wedging this line between floats.
1699            assert!(self.sequential_layout_state.is_some());
1700            let new_placement = self.place_line_among_floats(potential_line_size);
1701            if new_placement.start_corner.block !=
1702                self.current_line_block_start_considering_placement_among_floats()
1703            {
1704                return true;
1705            } else {
1706                self.current_line
1707                    .replace_placement_among_floats(new_placement);
1708                return false;
1709            }
1710        }
1711
1712        // Otherwise the new potential line size will require a newline if it fits in the
1713        // inline space available for this line. This space may be smaller than the
1714        // containing block if floats shrink the available inline space.
1715        potential_line_size.inline > available_line_space.inline
1716    }
1717
1718    fn defer_forced_line_break_at_character_offset(
1719        &mut self,
1720        caret_placeholder: &Option<CaretPlaceholder>,
1721    ) {
1722        // If the current portion of the unbreakable segment does not fit on the current line
1723        // we need to put it on a new line *before* actually triggering the hard line break.
1724        if !self.unbreakable_segment_fits_on_line() {
1725            self.rewind_trailing_inline_box_starts();
1726            self.process_line_break(
1727                false, /* forced_line_break */
1728                false, /* for_block_level */
1729            );
1730        }
1731
1732        // Defer the actual line break until we've cleared all ending inline boxes.
1733        self.force_line_break_before_new_content = true;
1734        self.caret_placeholder = caret_placeholder.clone();
1735
1736        // In quirks mode, the line-height isn't automatically added to the line. If we consider a
1737        // forced line break a kind of preserved white space, quirks mode requires that we add the
1738        // line-height of the current element to the line box height.
1739        //
1740        // The exception here is `<br>` elements. They are implemented with `pre-line` in Servo, but
1741        // this is an implementation detail. The "magic" behavior of `<br>` elements is that they
1742        // add line-height to the line conditionally: only when they are on an otherwise empty line.
1743        let line_is_empty =
1744            !self.current_line_segment.has_content && !self.current_line.has_content;
1745        if !self.processing_br_element() || line_is_empty {
1746            let strut_size = self
1747                .current_inline_container_state()
1748                .strut_block_sizes
1749                .clone();
1750            self.update_unbreakable_segment_for_new_content(
1751                &strut_size,
1752                Au::zero(),
1753                Au::zero(),
1754                Au::zero(),
1755                SegmentContentFlags::Contentful,
1756            );
1757        }
1758    }
1759
1760    fn possibly_flush_deferred_forced_line_break(&mut self) {
1761        if !self.force_line_break_before_new_content {
1762            return;
1763        }
1764        self.force_line_break_before_new_content = false;
1765
1766        self.commit_current_segment_to_line();
1767        self.process_line_break(
1768            true,  /* forced_line_break */
1769            false, /* for_block_level */
1770        );
1771
1772        self.current_line.caret_placeholder = self.caret_placeholder.take();
1773    }
1774
1775    fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1776        self.current_line_segment.push_line_item(line_item);
1777    }
1778
1779    fn push_shaped_text_slice_to_unbreakable_segment(
1780        &mut self,
1781        slice: Arc<ShapedTextSlice>,
1782        text_run: &TextRun,
1783        info: &FontAndScriptInfo,
1784        character_range: Range<Utf32CodeUnits>,
1785    ) {
1786        let mut block_contribution = LineBlockSizes::zero();
1787        let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1788        let current_inline_container_state = self.current_inline_container_state();
1789        if quirks_mode && !slice.all_removable() {
1790            // Normally, the strut is incorporated into the nested block size. In quirks mode though
1791            // if we find any text that isn't collapsed whitespace, we need to incorporate the strut.
1792            // TODO(mrobinson): This isn't quite right for situations where collapsible white space
1793            // ultimately does not collapse because it is between two other pieces of content.
1794            block_contribution.max_assign(&current_inline_container_state.strut_block_sizes);
1795        }
1796
1797        // If the metrics of this font don't match the default font, we are likely using another
1798        // font from the font list or a fallback and should incorporate its block size into the block
1799        // size of the container.
1800        let font_metrics = info.font_info.font.metrics();
1801        if current_inline_container_state
1802            .font_metrics
1803            .block_metrics_meaningfully_differ(font_metrics)
1804        {
1805            // TODO(mrobinson): This value should probably be cached somewhere.
1806            let baseline_shift = effective_baseline_shift(
1807                &current_inline_container_state.style,
1808                self.inline_box_state_stack.last().map(|c| &c.base),
1809            );
1810            let mut font_block_conribution = current_inline_container_state
1811                .get_block_size_contribution(
1812                    &baseline_shift,
1813                    font_metrics,
1814                    &current_inline_container_state.font_metrics,
1815                );
1816            font_block_conribution
1817                .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1818            block_contribution.max_assign(&font_block_conribution);
1819        }
1820
1821        let mut segment_flags = SegmentContentFlags::empty();
1822        if slice.has_non_hangable_non_removable_content() {
1823            segment_flags.insert(SegmentContentFlags::IncorporateTrailingWhiteSpace);
1824        }
1825        if !slice.all_removable() {
1826            segment_flags.insert(SegmentContentFlags::Contentful);
1827        }
1828
1829        self.update_unbreakable_segment_for_new_content(
1830            &block_contribution,
1831            slice.total_advance(),
1832            slice.hangable_advance(),
1833            slice.removable_advance(),
1834            segment_flags,
1835        );
1836
1837        let current_inline_box_identifier = self.current_inline_box_identifier();
1838        self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1839            current_inline_box_identifier,
1840            TextRunLineItem {
1841                text: vec![slice],
1842                text_fragment_run_data: text_run.run_data.clone(),
1843                base_fragment_info: text_run.base_fragment_info,
1844                info: info.clone(),
1845                character_range_in_dom_node: character_range,
1846                is_empty_for_text_cursor: false,
1847            },
1848        ));
1849    }
1850
1851    /// If the current line is empty and this [`InlineFormattingContext`] has a selection, push an
1852    /// empty [`LineItem::TextRun`] so that text carets can be placed on otherwise empty lines.
1853    fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1854        let Some(caret_placeholder) = self.current_line.caret_placeholder.take() else {
1855            return;
1856        };
1857
1858        // If the last content line item is a text item, then the placeholder for the text caret is not necessary.
1859        if self
1860            .current_line
1861            .line_items
1862            .iter()
1863            .rev()
1864            .find(|line_item| line_item.is_in_flow_content())
1865            .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1866        {
1867            return;
1868        }
1869
1870        let inline_container_state = self.current_inline_container_state();
1871        let Some(font) = inline_container_state.default_font.clone() else {
1872            return;
1873        };
1874
1875        self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1876            self.current_inline_box_identifier(),
1877            TextRunLineItem {
1878                text: Default::default(),
1879                text_fragment_run_data: caret_placeholder.run_data,
1880                base_fragment_info: caret_placeholder.base_fragment_info,
1881                info: FontAndScriptInfo::simple_for_font(font),
1882                character_range_in_dom_node: caret_placeholder.character_index..
1883                    caret_placeholder.character_index + Utf32CodeUnits(1),
1884                is_empty_for_text_cursor: true,
1885            },
1886        ));
1887
1888        let strut_size = self
1889            .current_inline_container_state()
1890            .strut_block_sizes
1891            .clone();
1892        self.update_unbreakable_segment_for_new_content(
1893            &strut_size,
1894            Au::zero(),
1895            Au::zero(),
1896            Au::zero(),
1897            SegmentContentFlags::empty(),
1898        );
1899
1900        self.current_line_segment.has_content = true;
1901        self.commit_current_segment_to_line();
1902    }
1903
1904    fn update_unbreakable_segment_for_new_content(
1905        &mut self,
1906        block_sizes_of_content: &LineBlockSizes,
1907        inline_size: Au,
1908        hangable_size: Au,
1909        removable_size: Au,
1910        segment_flags: SegmentContentFlags,
1911    ) {
1912        if segment_flags.contains(SegmentContentFlags::IncorporateTrailingWhiteSpace) {
1913            self.current_line_segment.trailing_hangable_white_space = Au::zero();
1914            self.current_line_segment.trailing_removable_white_space = Au::zero();
1915            self.current_line_segment.incorporates_trailing_white_space = true;
1916        }
1917
1918        if segment_flags.contains(SegmentContentFlags::Contentful) {
1919            self.current_line_segment.has_content = true;
1920        }
1921
1922        if hangable_size > Au::zero() {
1923            // When hangable space follows space previously considered removable, it can
1924            // no longer be trimmed from the end of the line, but it can hang.
1925            let old_removable_space =
1926                std::mem::take(&mut self.current_line_segment.trailing_removable_white_space);
1927            self.current_line_segment.trailing_hangable_white_space +=
1928                old_removable_space + hangable_size;
1929        }
1930        self.current_line_segment.trailing_removable_white_space += removable_size;
1931
1932        // This may or may not include the size of the strut depending on the quirks mode setting.
1933        let container_max_block_size = &self
1934            .current_inline_container_state()
1935            .nested_strut_block_sizes
1936            .clone();
1937        self.current_line_segment
1938            .max_block_size
1939            .max_assign(container_max_block_size);
1940        self.current_line_segment
1941            .max_block_size
1942            .max_assign(block_sizes_of_content);
1943
1944        self.current_line_segment.inline_size += inline_size;
1945
1946        // Propagate the whitespace setting to the current nesting level.
1947        self.current_inline_container_state().has_content.set(true);
1948        self.propagate_current_nesting_level_white_space_style();
1949    }
1950
1951    fn process_line_break(&mut self, forced_line_break: bool, for_block_level: bool) {
1952        self.current_line_segment.trim_leading_whitespace();
1953        self.finish_current_line_and_reset(forced_line_break, for_block_level);
1954    }
1955
1956    fn hypothetical_line_size(&self) -> LogicalVec2<Au> {
1957        let mut hanging_and_removable_whitespace = self
1958            .current_line_segment
1959            .hanging_and_removable_white_space_advance();
1960        if !self.current_line_segment.incorporates_trailing_white_space {
1961            hanging_and_removable_whitespace += self
1962                .current_line
1963                .tentative_hanging_and_removable_white_space_advance();
1964        }
1965        let inline = self.current_line.inline_position +
1966            self.current_line_segment.inline_size +
1967            self.cloneable_inline_box_pbm_size.end -
1968            hanging_and_removable_whitespace -
1969            self.current_line_segment.trailing_inline_box_start_size();
1970
1971        LogicalVec2 {
1972            inline,
1973            block: self
1974                .current_line_max_block_size_including_nested_containers()
1975                .max(&self.current_line_segment.max_block_size)
1976                .resolve(),
1977        }
1978    }
1979
1980    fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1981        !self.new_potential_line_size_causes_line_break(&self.hypothetical_line_size())
1982    }
1983
1984    /// After a line break triggered by a soft wrap opportunity, any trailing opening
1985    /// inline box items should belong to the next line so that they "stick" to their
1986    /// content. This method runs before the line break and moves those trailing opening
1987    /// inline box items from the line to the segment (which will be part of the next
1988    /// line).
1989    fn rewind_trailing_inline_box_starts(&mut self) {
1990        let mut removal_start = self.current_line.line_items.len();
1991        for item in self.current_line.line_items.iter().rev() {
1992            let LineItem::InlineStartBoxPaddingBorderMargin(state) = item else {
1993                break;
1994            };
1995
1996            removal_start -= 1;
1997
1998            let inline_start = state.pbm_inline_start();
1999            self.current_line.inline_position -= inline_start;
2000            self.current_line_segment.inline_size += inline_start;
2001
2002            if state.should_clone_pbm() {
2003                self.current_line.cloneable_inline_box_pbm_size.start -= inline_start;
2004                self.current_line.cloneable_inline_box_pbm_size.end -= state.pbm_inline_end();
2005            }
2006        }
2007
2008        self.current_line_segment
2009            .line_items
2010            .splice(0..0, self.current_line.line_items.drain(removal_start..));
2011    }
2012
2013    /// Process a soft wrap opportunity. This will either commit the current unbreakable
2014    /// segment to the current line, if it fits within the containing block and float
2015    /// placement boundaries, or do a line break and then commit the segment.
2016    fn process_soft_wrap_opportunity(&mut self) {
2017        if self.current_line_segment.line_items.is_empty() {
2018            return;
2019        }
2020        if self.text_wrap_mode == TextWrapMode::Nowrap {
2021            return;
2022        }
2023        if !self.unbreakable_segment_fits_on_line() {
2024            self.rewind_trailing_inline_box_starts();
2025            self.process_line_break(
2026                false, /* forced_line_break */
2027                false, /* for_block_level */
2028            );
2029        }
2030        self.commit_current_segment_to_line();
2031    }
2032
2033    /// Commit the current unbreakable segment to the current line. In addition, this will
2034    /// place all floats in the unbreakable segment and expand the line dimensions.
2035    fn commit_current_segment_to_line(&mut self) {
2036        // Take a snapshot of the IFC layout's current cloneable PBM. This ensures that the
2037        // line reflects the reality of *only* the committed segments.
2038        self.current_line.cloneable_inline_box_pbm_size = self.cloneable_inline_box_pbm_size;
2039
2040        // The line segments might have no items and have content after processing a forced
2041        // linebreak on an empty line.
2042        if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
2043        {
2044            return;
2045        }
2046
2047        if !self.current_line.has_content {
2048            self.current_line_segment.trim_leading_whitespace();
2049        }
2050
2051        let potential_line_length = self.hypothetical_line_size().inline;
2052
2053        let old_removable_space =
2054            std::mem::take(&mut self.current_line.trailing_removable_white_space);
2055        let old_hangable_space =
2056            std::mem::take(&mut self.current_line.trailing_hangable_white_space);
2057        let mut new_removable_space = self.current_line_segment.trailing_removable_white_space;
2058        let mut new_hangable_space = self.current_line_segment.trailing_hangable_white_space;
2059
2060        // If the white space of the current line is not incorporated into the content by the
2061        // new segment, add it to the segment's white space values.
2062        if !self.current_line_segment.incorporates_trailing_white_space {
2063            // When hangable space follows space previously considered removable, it can
2064            // no longer be trimmed from the end of the line, but it can hang.
2065            if new_hangable_space > Au::zero() {
2066                new_hangable_space += old_removable_space;
2067            } else {
2068                new_removable_space += old_removable_space;
2069            }
2070            new_hangable_space += old_hangable_space;
2071        }
2072
2073        self.current_line.trailing_hangable_white_space = new_hangable_space;
2074        self.current_line.trailing_removable_white_space = new_removable_space;
2075
2076        self.current_line.inline_position += self.current_line_segment.inline_size;
2077        self.current_line.max_block_size = self
2078            .current_line_max_block_size_including_nested_containers()
2079            .max(&self.current_line_segment.max_block_size);
2080
2081        // Place all floats in this unbreakable segment.
2082        let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
2083
2084        for item in segment_items.iter_mut() {
2085            if let LineItem::Float(_, float_item) = item {
2086                self.place_float_line_item_for_commit_to_line(float_item, potential_line_length);
2087            }
2088        }
2089
2090        // If the current line was never placed among floats, we need to do that now based on the
2091        // new size. Calling `new_potential_line_size_causes_line_break()` here triggers the
2092        // new line to be positioned among floats. This should never ask for a line
2093        // break because it is the first content on the line.
2094        if self.current_line.line_items.is_empty() {
2095            let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
2096                inline: potential_line_length,
2097                block: self.current_line_segment.max_block_size.resolve(),
2098            });
2099            assert!(!will_break);
2100        }
2101
2102        debug_assert!(
2103            self.current_line_segment.line_items.is_empty(),
2104            "Committing a segment should not add items to it"
2105        );
2106        // Return the emptied buffer to the segment so that its allocation is reused.
2107        self.current_line.line_items.append(&mut segment_items);
2108        self.current_line_segment.line_items = segment_items;
2109        self.current_line.has_content |= self.current_line_segment.has_content;
2110
2111        self.current_line_segment.reset();
2112    }
2113
2114    #[inline]
2115    fn containing_block(&self) -> &ContainingBlock<'_> {
2116        self.placement_state.containing_block
2117    }
2118}
2119
2120bitflags! {
2121    struct SegmentContentFlags: u8 {
2122        const IncorporateTrailingWhiteSpace = 1 << 0;
2123        const Contentful = 1 << 1;
2124    }
2125}
2126
2127impl InlineFormattingContext {
2128    #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
2129    fn new_with_builder(
2130        mut builder: InlineFormattingContextBuilder,
2131        layout_context: &LayoutContext,
2132        has_first_formatted_line: bool,
2133        is_single_line_text_input: bool,
2134        starting_bidi_level: Level,
2135    ) -> Self {
2136        // This is to prevent a double borrow.
2137        let text_content: String = builder.text_segments.into_iter().collect();
2138
2139        let bidi_levels = BidiLevels {
2140            info: builder
2141                .has_right_to_left_content
2142                .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
2143        };
2144
2145        let shared_inline_styles = builder
2146            .shared_inline_styles_stack
2147            .last()
2148            .expect("Should have at least one SharedInlineStyle for the root of an IFC")
2149            .clone();
2150        let (word_break, line_break, lang) = {
2151            let styles = shared_inline_styles.style.borrow();
2152            let text_style = styles.get_inherited_text();
2153            (
2154                text_style.word_break,
2155                text_style.line_break,
2156                styles.get_font()._x_lang.clone(),
2157            )
2158        };
2159
2160        let mut options = LineBreakOptions::default();
2161
2162        // TODO: These settings need to be handled on an inline box basis and not based
2163        // on the paragraph's style.
2164        options.strictness = Some(match line_break {
2165            LineBreak::Loose => LineBreakStrictness::Loose,
2166            LineBreak::Normal => LineBreakStrictness::Normal,
2167            LineBreak::Strict => LineBreakStrictness::Strict,
2168            LineBreak::Anywhere => LineBreakStrictness::Anywhere,
2169            // For `auto`, the UA determines the set of line-breaking restrictions to use.
2170            // So it's fine if we always treat it as `normal`.
2171            LineBreak::Auto => LineBreakStrictness::Normal,
2172        });
2173        options.word_option = Some(match word_break {
2174            WordBreak::Normal | WordBreak::KeepAll => LineBreakWordOption::Normal,
2175            // TODO: also apply `overflow-wrap: anywhere` for `break-word` once overflow-wrap is supported
2176            WordBreak::BreakWord => LineBreakWordOption::Normal,
2177            WordBreak::BreakAll => LineBreakWordOption::BreakAll,
2178        });
2179
2180        // Enable Chinese/Japanese line breaking behavior when this inline formatting context
2181        // has a Japanese or Chinese language set.
2182        let content_locale = lang.0.parse::<LanguageIdentifier>().ok();
2183        options.content_locale = content_locale.as_ref();
2184
2185        let mut shaping_queue = ShapingQueue::new(&text_content, options);
2186        for item in &mut builder.inline_items {
2187            match item {
2188                InlineItem::TextRun(text_run) => {
2189                    let shaping_queue_entries = text_run.borrow_mut().segment(
2190                        text_run.clone(),
2191                        &text_content,
2192                        layout_context,
2193                        &bidi_levels,
2194                    );
2195                    for entry in shaping_queue_entries.into_iter() {
2196                        shaping_queue.push(entry);
2197                    }
2198                },
2199                InlineItem::StartInlineBox(inline_box) => {
2200                    let inline_box = &mut *inline_box.borrow_mut();
2201                    if let Some(font) = get_font_for_first_font_for_style(
2202                        &inline_box.base.style,
2203                        &layout_context.font_context,
2204                    ) {
2205                        inline_box.default_font = Some(font);
2206                    }
2207
2208                    if inline_box.breaks_shaping_at_start {
2209                        shaping_queue.flush();
2210                    }
2211                },
2212                InlineItem::Atomic(_, index_in_text, bidi_level) => {
2213                    shaping_queue.flush();
2214                    *bidi_level = bidi_levels.level(usize::from(*index_in_text));
2215                },
2216                InlineItem::EndInlineBox(inline_box) => {
2217                    if inline_box.borrow().breaks_shaping_at_end {
2218                        shaping_queue.flush();
2219                    }
2220                },
2221                InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
2222                InlineItem::OutOfFlowFloatBox(_) |
2223                InlineItem::BlockLevel { .. } => {},
2224            }
2225        }
2226
2227        shaping_queue.flush();
2228
2229        let default_font = get_font_for_first_font_for_style(
2230            &shared_inline_styles.style.borrow(),
2231            &layout_context.font_context,
2232        );
2233
2234        let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
2235        builder
2236            .text_content_slot
2237            .set(text_content)
2238            .expect("Text content should not yet be set.");
2239
2240        InlineFormattingContext {
2241            text_content: builder.text_content_slot,
2242            inline_items: builder.inline_items,
2243            inline_boxes: builder.inline_boxes,
2244            shared_inline_styles,
2245            default_font,
2246            has_first_formatted_line,
2247            contains_floats: builder.contains_floats,
2248            is_single_line_text_input,
2249            has_right_to_left_content,
2250            tab_size_multiplier: Default::default(),
2251        }
2252    }
2253
2254    pub(crate) fn text_content(&self) -> &str {
2255        self.text_content.get().map_or("", String::as_str)
2256    }
2257
2258    pub(crate) fn repair_style(
2259        &self,
2260        context: &SharedStyleContext,
2261        node: &ServoLayoutNode,
2262        new_style: &ServoArc<ComputedValues>,
2263    ) {
2264        *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
2265        *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
2266    }
2267
2268    fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
2269        if !self.has_first_formatted_line {
2270            return Au::zero();
2271        }
2272        containing_block
2273            .style
2274            .get_inherited_text()
2275            .text_indent
2276            .length
2277            .to_used_value(containing_block.size.inline.unwrap_or_default())
2278    }
2279
2280    pub(super) fn layout(
2281        &self,
2282        layout_context: &LayoutContext,
2283        positioning_context: &mut PositioningContext,
2284        containing_block: &ContainingBlock,
2285        sequential_layout_state: Option<&mut SequentialLayoutState>,
2286        collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2287        ignore_block_margins_for_stretch: LogicalSides1D<bool>,
2288    ) -> IndependentFormattingContextLayoutResult {
2289        // Clear any cached inline fragments from previous layouts.
2290        for inline_box in self.inline_boxes.iter() {
2291            inline_box.borrow().base.clear_fragments();
2292        }
2293
2294        let style = containing_block.style;
2295
2296        let style_text = containing_block.style.get_inherited_text();
2297        let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2298        if inline_container_needs_strut(style, layout_context, None) {
2299            inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2300        }
2301        if self.is_single_line_text_input {
2302            inline_container_state_flags
2303                .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2304        }
2305        let placement_state =
2306            PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2307
2308        let mut layout = InlineFormattingContextLayout {
2309            positioning_context,
2310            placement_state,
2311            sequential_layout_state,
2312            layout_context,
2313            ifc: self,
2314            fragments: Vec::new(),
2315            current_line: LineUnderConstruction::new(LogicalVec2 {
2316                inline: self.inline_start_for_first_line(containing_block.into()),
2317                block: Au::zero(),
2318            }),
2319            root_nesting_level: InlineContainerState::new(
2320                style.to_arc(),
2321                inline_container_state_flags,
2322                None, /* parent_container */
2323                self.default_font.clone(),
2324            ),
2325            inline_box_state_stack: Vec::new(),
2326            cloneable_inline_box_pbm_size: Default::default(),
2327            inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2328            current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2329            force_line_break_before_new_content: false,
2330            caret_placeholder: None,
2331            deferred_br_clear: Clear::None,
2332            have_deferred_soft_wrap_opportunity: false,
2333            depends_on_block_constraints: false,
2334            white_space_collapse: style_text.white_space_collapse,
2335            text_wrap_mode: style_text.text_wrap_mode,
2336            ignore_block_margins_for_stretch,
2337        };
2338
2339        for item in self.inline_items.iter() {
2340            // Any new box should flush a pending hard line break.
2341            if !matches!(item, InlineItem::EndInlineBox(..)) {
2342                layout.possibly_flush_deferred_forced_line_break();
2343            }
2344
2345            match item {
2346                InlineItem::StartInlineBox(inline_box) => {
2347                    layout.start_inline_box(&inline_box.borrow());
2348                },
2349                InlineItem::EndInlineBox(..) => layout.finish_inline_box(),
2350                InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2351                InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2352                    atomic_formatting_context.borrow().layout_into_line_items(
2353                        &mut layout,
2354                        *offset_in_text,
2355                        *bidi_level,
2356                    );
2357                },
2358                InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2359                    layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2360                        layout.current_inline_box_identifier(),
2361                        AbsolutelyPositionedLineItem {
2362                            absolutely_positioned_box: positioned_box.clone(),
2363                            preceding_line_content_would_produce_phantom_line: layout
2364                                .current_line
2365                                .is_phantom() &&
2366                                layout.current_line_segment.is_phantom(),
2367                        },
2368                    ));
2369                },
2370                InlineItem::OutOfFlowFloatBox(float_box) => {
2371                    float_box.borrow().layout_into_line_items(&mut layout);
2372                },
2373                InlineItem::BlockLevel(block_level) => {
2374                    block_level.borrow().layout_into_line_items(&mut layout);
2375                },
2376            }
2377        }
2378
2379        layout.finish_last_line();
2380        let (content_block_size, collapsible_margins_in_children, baselines) =
2381            layout.placement_state.finish();
2382
2383        IndependentFormattingContextLayoutResult {
2384            fragments: layout.fragments,
2385            content_block_size,
2386            collapsible_margins_in_children,
2387            baselines,
2388            depends_on_block_constraints: layout.depends_on_block_constraints,
2389            content_inline_size_for_table: None,
2390            specific_layout_info: None,
2391        }
2392    }
2393
2394    pub(crate) fn subtree_size(&self) -> usize {
2395        self.inline_items
2396            .iter()
2397            .map(|item| match item {
2398                InlineItem::StartInlineBox(..) => 1,
2399                InlineItem::EndInlineBox(..) => 0,
2400                InlineItem::TextRun(..) => 1,
2401                InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2402                    absolutely_positioned_box
2403                        .borrow()
2404                        .context
2405                        .base
2406                        .subtree_size()
2407                },
2408                InlineItem::OutOfFlowFloatBox(..) => 1,
2409                InlineItem::Atomic(..) => 1,
2410                InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2411            })
2412            .sum()
2413    }
2414
2415    fn next_character_prevents_soft_wrap_opportunity(&self, index: Utf8CodeUnits) -> bool {
2416        let Some(second_character) = self.text_content()[usize::from(index)..].chars().nth(1)
2417        else {
2418            return false;
2419        };
2420        char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(second_character)
2421    }
2422
2423    fn previous_character_prevents_soft_wrap_opportunity(&self, index: Utf8CodeUnits) -> bool {
2424        let Some(character) = self.text_content()[..usize::from(index)]
2425            .chars()
2426            .next_back()
2427        else {
2428            return false;
2429        };
2430        char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2431    }
2432
2433    pub(crate) fn find_block_margin_collapsing_with_parent(
2434        &self,
2435        layout_context: &LayoutContext,
2436        collected_margin: &mut CollapsedMargin,
2437        containing_block_for_children: &ContainingBlock,
2438    ) -> bool {
2439        // Margins can't collapse through line boxes, unless they are phantom line boxes.
2440        // <https://drafts.csswg.org/css-inline-3/#invisible-line-boxes>
2441        // > Line boxes that contain no text, no preserved white space, no inline boxes with non-zero
2442        // > inline-axis margins, padding, or borders, and no other in-flow content (such as atomic
2443        // > inlines or ruby annotations), and do not end with a forced line break are phantom line boxes.
2444        let mut items_iter = self.inline_items.iter();
2445        items_iter.all(|inline_item| match inline_item {
2446            InlineItem::StartInlineBox(inline_box) => {
2447                let pbm = inline_box
2448                    .borrow()
2449                    .layout_style()
2450                    .padding_border_margin(containing_block_for_children);
2451                pbm.padding.inline_start.is_zero() &&
2452                    pbm.border.inline_start.is_zero() &&
2453                    pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2454            },
2455            InlineItem::EndInlineBox(inline_box) => {
2456                let pbm = inline_box
2457                    .borrow()
2458                    .layout_style()
2459                    .padding_border_margin(containing_block_for_children);
2460                pbm.padding.inline_end.is_zero() &&
2461                    pbm.border.inline_end.is_zero() &&
2462                    pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2463            },
2464            InlineItem::TextRun(text_run) => {
2465                let text_run = &*text_run.borrow();
2466                text_run.items.iter().all(|item| match item {
2467                    TextRunItem::LineBreak { .. } => false,
2468                    TextRunItem::Tab { .. } => false,
2469                    TextRunItem::TextSegment(segment) => {
2470                        segment.runs.iter().all(|run| run.all_removable())
2471                    },
2472                })
2473            },
2474            InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2475            InlineItem::OutOfFlowFloatBox(..) => true,
2476            InlineItem::Atomic(..) => false,
2477            InlineItem::BlockLevel(block_level) => block_level
2478                .borrow()
2479                .find_block_margin_collapsing_with_parent(
2480                    layout_context,
2481                    collected_margin,
2482                    containing_block_for_children,
2483                ),
2484        })
2485    }
2486
2487    pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2488        let mut parent_box_stack = Vec::new();
2489        let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2490            parent_box_stack.last().unwrap_or(&layout_box).clone()
2491        };
2492        for inline_item in &self.inline_items {
2493            match inline_item {
2494                InlineItem::StartInlineBox(inline_box) => {
2495                    inline_box
2496                        .borrow_mut()
2497                        .base
2498                        .parent_box
2499                        .replace(current_parent_box(&parent_box_stack));
2500                    parent_box_stack.push(WeakLayoutBox::InlineLevel(
2501                        WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2502                    ));
2503                },
2504                InlineItem::EndInlineBox(..) => {
2505                    parent_box_stack.pop();
2506                },
2507                InlineItem::TextRun(text_run) => {
2508                    text_run
2509                        .borrow_mut()
2510                        .parent_box
2511                        .replace(current_parent_box(&parent_box_stack));
2512                },
2513                _ => inline_item.with_base_mut(|base| {
2514                    base.parent_box
2515                        .replace(current_parent_box(&parent_box_stack));
2516                }),
2517            }
2518        }
2519    }
2520
2521    pub(crate) fn next_tab_stop_after_inline_advance(
2522        &self,
2523        style: &ServoArc<ComputedValues>,
2524        current_inline_advance: Au,
2525    ) -> Au {
2526        let Some(font) = self.default_font.as_ref() else {
2527            return Au::zero();
2528        };
2529
2530        let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2531            let root_style = self.shared_inline_styles.style.borrow();
2532            let inherited_text_style = root_style.get_inherited_text();
2533            let font_size = root_style.get_font().font_size.computed_size().into();
2534            let letter_spacing = inherited_text_style
2535                .letter_spacing
2536                .0
2537                .to_used_value(font_size);
2538            let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2539
2540            // Each "space" character in the tab is considered both a letter and a word separator for
2541            // the purposes of applying word spacing and letter spacing.
2542            font.metrics().space_advance + word_spacing + letter_spacing
2543        });
2544
2545        let tab_stop_advance = match style.get_inherited_text().tab_size {
2546            style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2547                tab_size_multiplier.scale_by(number_of_spaces.0)
2548            },
2549            // When a length is provided we do not apply word spacing or letter spacing.
2550            style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2551        };
2552
2553        if tab_stop_advance.is_zero() {
2554            return Au::zero();
2555        }
2556
2557        // From <https://drafts.csswg.org/css-text-4/#ref-for-tab-size-dfn>
2558        // > If this distance is less than 0.5ch, then the subsequent tab stop is used instead.
2559        // From <https://drafts.csswg.org/css-values/#ch>
2560        // > In the cases where it is impossible or impractical to determine the measure of the “0”
2561        // > glyph, it must be assumed to be 0.5em wide by 1em tall.
2562        let half_ch_advance = font
2563            .metrics()
2564            .zero_horizontal_advance
2565            .unwrap_or(font.metrics().em_size.scale_by(0.5))
2566            .scale_by(0.5);
2567        let number_of_tab_stops =
2568            (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2569        let number_of_tab_stops = number_of_tab_stops.ceil();
2570        tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2571    }
2572
2573    pub(crate) fn non_pseudo_text_runs<'a>(
2574        &'a self,
2575    ) -> impl DoubleEndedIterator<Item = AtomicRef<'a, TextRun>> {
2576        self.inline_items
2577            .iter()
2578            .filter_map(|item| match item {
2579                InlineItem::TextRun(text_run) => Some(text_run.borrow()),
2580                _ => None,
2581            })
2582            .filter(|text_run| {
2583                text_run
2584                    .base_fragment_info
2585                    .tag
2586                    .is_some_and(|tag| tag.pseudo_element_chain.is_empty())
2587            })
2588    }
2589}
2590
2591impl InlineContainerState {
2592    fn new(
2593        style: ServoArc<ComputedValues>,
2594        flags: InlineContainerStateFlags,
2595        parent_container: Option<&InlineContainerState>,
2596        default_font: Option<FontRef>,
2597    ) -> Self {
2598        let font_metrics = default_font
2599            .as_ref()
2600            .map(|font| font.metrics().clone())
2601            .unwrap_or_else(FontMetrics::empty);
2602        let mut baseline_offset = Au::zero();
2603        let mut strut_block_sizes = {
2604            Self::get_block_sizes_with_style(
2605                &effective_baseline_shift(&style, parent_container),
2606                &style,
2607                &font_metrics,
2608                &font_metrics,
2609                &flags,
2610            )
2611        };
2612
2613        if let Some(parent_container) = parent_container {
2614            // The baseline offset from `vertical-align` might adjust where our block size contribution is
2615            // within the line.
2616            baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2617                style.get_alignment_baseline(),
2618                style.get_baseline_shift(),
2619                &strut_block_sizes,
2620            );
2621            strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2622        }
2623
2624        let mut nested_block_sizes = parent_container
2625            .map(|container| container.nested_strut_block_sizes.clone())
2626            .unwrap_or_else(LineBlockSizes::zero);
2627        if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2628            nested_block_sizes.max_assign(&strut_block_sizes);
2629        }
2630
2631        Self {
2632            style,
2633            flags,
2634            has_content: Cell::new(false),
2635            nested_strut_block_sizes: nested_block_sizes,
2636            strut_block_sizes,
2637            baseline_offset,
2638            default_font,
2639            font_metrics,
2640        }
2641    }
2642
2643    fn get_block_sizes_with_style(
2644        baseline_shift: &BaselineShift,
2645        style: &ComputedValues,
2646        font_metrics: &FontMetrics,
2647        font_metrics_of_first_font: &FontMetrics,
2648        flags: &InlineContainerStateFlags,
2649    ) -> LineBlockSizes {
2650        let line_height = line_height(style, font_metrics, flags);
2651
2652        if !is_baseline_relative(baseline_shift) {
2653            return LineBlockSizes {
2654                line_height,
2655                baseline_relative_size_for_line_height: None,
2656                size_for_baseline_positioning: BaselineRelativeSize::zero(),
2657            };
2658        }
2659
2660        // From https://drafts.csswg.org/css-inline/#inline-height
2661        // > If line-height computes to `normal` and either `text-box-edge` is `leading` or this
2662        // > is the root inline box, the font’s line gap metric may also be incorporated
2663        // > into A and D by adding half to each side as half-leading.
2664        //
2665        // `text-box-edge` isn't implemented (and this is a draft specification), so it's
2666        // always effectively `leading`, which means we always take into account the line gap
2667        // when `line-height` is normal.
2668        let mut ascent = font_metrics.ascent;
2669        let mut descent = font_metrics.descent;
2670        if style.get_font().line_height == LineHeight::Normal {
2671            let half_leading_from_line_gap =
2672                (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2673            ascent += half_leading_from_line_gap;
2674            descent += half_leading_from_line_gap;
2675        }
2676
2677        // The ascent and descent we use for computing the line's final line height isn't
2678        // the same the ascent and descent we use for finding the baseline. For finding
2679        // the baseline we want the content rect.
2680        let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2681
2682        // From https://drafts.csswg.org/css-inline/#inline-height
2683        // > When its computed line-height is not normal, its layout bounds are derived solely
2684        // > from metrics of its first available font (ignoring glyphs from other fonts), and
2685        // > leading is used to adjust the effective A and D to add up to the used line-height.
2686        // > Calculate the leading L as L = line-height - (A + D). Half the leading (its
2687        // > half-leading) is added above A of the first available font, and the other half
2688        // > below D of the first available font, giving an effective ascent above the baseline
2689        // > of A′ = A + L/2, and an effective descent of D′ = D + L/2.
2690        //
2691        // Note that leading might be negative here and the line-height might be zero. In
2692        // the case where the height is zero, ascent and descent will move to the same
2693        // point in the block axis.  Even though the contribution to the line height is
2694        // zero in this case, the line may get some height when taking them into
2695        // considering with other zero line height boxes that converge on other block axis
2696        // locations when using the above formula.
2697        if style.get_font().line_height != LineHeight::Normal {
2698            ascent = font_metrics_of_first_font.ascent;
2699            descent = font_metrics_of_first_font.descent;
2700            let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2701            // We want the sum of `ascent` and `descent` to equal `line_height`.
2702            // If we just add `half_leading` to both, then we may not get `line_height`
2703            // due to precision limitations of `Au`. Instead, we set `descent` to
2704            // the value that will guarantee the correct sum.
2705            ascent += half_leading;
2706            descent = line_height - ascent;
2707        }
2708
2709        LineBlockSizes {
2710            line_height,
2711            baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2712            size_for_baseline_positioning,
2713        }
2714    }
2715
2716    fn get_block_size_contribution(
2717        &self,
2718        baseline_shift: &BaselineShift,
2719        font_metrics: &FontMetrics,
2720        font_metrics_of_first_font: &FontMetrics,
2721    ) -> LineBlockSizes {
2722        Self::get_block_sizes_with_style(
2723            baseline_shift,
2724            &self.style,
2725            font_metrics,
2726            font_metrics_of_first_font,
2727            &self.flags,
2728        )
2729    }
2730
2731    fn get_cumulative_baseline_offset_for_child(
2732        &self,
2733        child_alignment_baseline: &AlignmentBaseline,
2734        child_baseline_shift: &BaselineShift,
2735        child_block_size: &LineBlockSizes,
2736    ) -> Au {
2737        let block_size = self.get_block_size_contribution(
2738            child_baseline_shift,
2739            &self.font_metrics,
2740            &self.font_metrics,
2741        );
2742        self.baseline_offset +
2743            match child_alignment_baseline {
2744                AlignmentBaseline::Baseline => Au::zero(),
2745                AlignmentBaseline::TextTop => {
2746                    child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2747                },
2748                AlignmentBaseline::Middle => {
2749                    // "Align the vertical midpoint of the box with the baseline of the parent
2750                    // box plus half the x-height of the parent."
2751                    (child_block_size.size_for_baseline_positioning.ascent -
2752                        child_block_size.size_for_baseline_positioning.descent -
2753                        self.font_metrics.x_height)
2754                        .scale_by(0.5)
2755                },
2756                AlignmentBaseline::TextBottom => {
2757                    self.font_metrics.descent -
2758                        child_block_size.size_for_baseline_positioning.descent
2759                },
2760            } +
2761            match child_baseline_shift {
2762                // `top` and `bottom are not actually relative to the baseline, but this value is unused
2763                // in those cases.
2764                // TODO: We should distinguish these from `baseline` in order to implement "aligned subtrees" properly.
2765                // See https://drafts.csswg.org/css2/#aligned-subtree.
2766                BaselineShift::Keyword(
2767                    BaselineShiftKeyword::Top |
2768                    BaselineShiftKeyword::Bottom |
2769                    BaselineShiftKeyword::Center,
2770                ) => Au::zero(),
2771                BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2772                    block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2773                },
2774                BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2775                    -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2776                },
2777                BaselineShift::Length(length_percentage) => {
2778                    -length_percentage.to_used_value(child_block_size.line_height)
2779                },
2780            }
2781    }
2782}
2783
2784impl IndependentFormattingContext {
2785    fn layout_into_line_items(
2786        &self,
2787        layout: &mut InlineFormattingContextLayout,
2788        offset_in_text: Utf8CodeUnits,
2789        bidi_level: Level,
2790    ) {
2791        // We need to know the inline size of the atomic before deciding whether to do the line break.
2792        let mut child_positioning_context = PositioningContext::default();
2793        let IndependentFloatOrAtomicLayoutResult {
2794            mut fragment,
2795            baselines,
2796            pbm_sums,
2797        } = self.layout_float_or_atomic_inline(
2798            layout.layout_context,
2799            &mut child_positioning_context,
2800            layout.containing_block(),
2801        );
2802
2803        // If this Fragment's layout depends on the block size of the containing block,
2804        // then the entire layout of the inline formatting context does as well.
2805        layout.depends_on_block_constraints |= fragment.base.flags.contains(
2806            FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2807        );
2808
2809        // Offset the content rectangle by the physical offset of the padding, border, and margin.
2810        let container_writing_mode = layout.containing_block().style.writing_mode;
2811        let pbm_physical_offset = pbm_sums
2812            .start_offset()
2813            .to_physical_size(container_writing_mode);
2814        fragment.base.translate_rect(pbm_physical_offset);
2815
2816        // Apply baselines.
2817        fragment = fragment.with_baselines(baselines);
2818
2819        // Lay out absolutely positioned children if this new atomic establishes a containing block
2820        // for absolutes.
2821        let positioning_context = if self.is_replaced() {
2822            None
2823        } else {
2824            if fragment
2825                .style()
2826                .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2827            {
2828                child_positioning_context
2829                    .layout_collected_children(layout.layout_context, &mut fragment);
2830            }
2831            Some(child_positioning_context)
2832        };
2833
2834        if layout.text_wrap_mode == TextWrapMode::Wrap &&
2835            !layout
2836                .ifc
2837                .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2838        {
2839            layout.process_soft_wrap_opportunity();
2840        }
2841
2842        let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2843        let baseline_offset = self
2844            .pick_baseline(&fragment.baselines(container_writing_mode))
2845            .map(|baseline| pbm_sums.block_start + baseline)
2846            .unwrap_or(size.block);
2847
2848        let (block_sizes, baseline_offset_in_parent) =
2849            self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2850
2851        layout.update_unbreakable_segment_for_new_content(
2852            &block_sizes,
2853            size.inline,
2854            Au::zero(),
2855            Au::zero(),
2856            SegmentContentFlags::Contentful | SegmentContentFlags::IncorporateTrailingWhiteSpace,
2857        );
2858
2859        let fragment = Arc::new(fragment);
2860        self.base.set_fragment(Fragment::Box(fragment.clone()));
2861
2862        layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2863            layout.current_inline_box_identifier(),
2864            AtomicLineItem {
2865                fragment,
2866                size,
2867                positioning_context,
2868                baseline_offset_in_parent,
2869                baseline_offset_in_item: baseline_offset,
2870                bidi_level,
2871            },
2872        ));
2873
2874        // If there's a soft wrap opportunity following this atomic, defer a soft wrap opportunity
2875        // for when we next process text content.
2876        if !layout
2877            .ifc
2878            .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2879        {
2880            layout.have_deferred_soft_wrap_opportunity = true;
2881        }
2882    }
2883
2884    /// Picks either the first or the last baseline, depending on `baseline-source`.
2885    /// TODO: clarify that this is not to be used for box alignment in flex/grid
2886    /// <https://drafts.csswg.org/css-inline/#baseline-source>
2887    fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2888        match self.style().get_baseline_source() {
2889            BaselineSource::First => baselines.first,
2890            BaselineSource::Last => baselines.last,
2891            BaselineSource::Auto if self.is_block_container() => baselines.last,
2892            BaselineSource::Auto => baselines.first,
2893        }
2894    }
2895
2896    fn get_block_sizes_and_baseline_offset(
2897        &self,
2898        ifc: &InlineFormattingContextLayout,
2899        block_size: Au,
2900        baseline_offset_in_content_area: Au,
2901    ) -> (LineBlockSizes, Au) {
2902        let mut contribution = if !is_baseline_relative(self.style().get_baseline_shift()) {
2903            LineBlockSizes {
2904                line_height: block_size,
2905                baseline_relative_size_for_line_height: None,
2906                size_for_baseline_positioning: BaselineRelativeSize::zero(),
2907            }
2908        } else {
2909            let baseline_relative_size = BaselineRelativeSize {
2910                ascent: baseline_offset_in_content_area,
2911                descent: block_size - baseline_offset_in_content_area,
2912            };
2913            LineBlockSizes {
2914                line_height: block_size,
2915                baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2916                size_for_baseline_positioning: baseline_relative_size,
2917            }
2918        };
2919
2920        let style = self.style();
2921        let baseline_offset = ifc
2922            .current_inline_container_state()
2923            .get_cumulative_baseline_offset_for_child(
2924                style.get_alignment_baseline(),
2925                style.get_baseline_shift(),
2926                &contribution,
2927            );
2928        contribution.adjust_for_baseline_offset(baseline_offset);
2929
2930        (contribution, baseline_offset)
2931    }
2932}
2933
2934impl FloatBox {
2935    fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2936        let old_len = layout.positioning_context.len();
2937        let fragment = Arc::new(self.layout(
2938            layout.layout_context,
2939            layout.positioning_context,
2940            layout.placement_state.containing_block,
2941        ));
2942        let new_len = layout.positioning_context.len();
2943
2944        self.contents
2945            .base
2946            .set_fragment(Fragment::Box(fragment.clone()));
2947        layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2948            layout.current_inline_box_identifier(),
2949            FloatLineItem {
2950                fragment,
2951                needs_placement: true,
2952                range: old_len..new_len,
2953            },
2954        ));
2955    }
2956}
2957
2958fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2959    for item in line_items.iter() {
2960        if let LineItem::Float(_, float_line_item) = item &&
2961            float_line_item.needs_placement
2962        {
2963            ifc.place_float_fragment(float_line_item);
2964        }
2965    }
2966}
2967
2968fn line_height(
2969    parent_style: &ComputedValues,
2970    font_metrics: &FontMetrics,
2971    flags: &InlineContainerStateFlags,
2972) -> Au {
2973    let font = parent_style.get_font();
2974    let font_size = font.font_size.computed_size();
2975    let mut line_height = match font.line_height {
2976        LineHeight::Normal => font_metrics.line_gap,
2977        LineHeight::Number(number) => (font_size * number.0).into(),
2978        LineHeight::Length(length) => length.0.into(),
2979    };
2980
2981    // The line height of a single-line text input's inner text container is clamped to
2982    // the size of `normal`.
2983    // <https://html.spec.whatwg.org/multipage/#the-input-element-as-a-text-entry-widget>
2984    if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2985        line_height.max_assign(font_metrics.line_gap);
2986    }
2987
2988    line_height
2989}
2990
2991fn effective_baseline_shift(
2992    style: &ComputedValues,
2993    container: Option<&InlineContainerState>,
2994) -> BaselineShift {
2995    if container.is_none() {
2996        // If we are at the root of the inline formatting context, we shouldn't use the
2997        // computed `baseline-shift`, since it has no effect on the contents of this IFC
2998        // (it can just affect how the block container is aligned within the parent IFC).
2999        BaselineShift::zero()
3000    } else {
3001        style.slow_clone_baseline_shift()
3002    }
3003}
3004
3005fn is_baseline_relative(baseline_shift: &BaselineShift) -> bool {
3006    !matches!(
3007        baseline_shift,
3008        BaselineShift::Keyword(
3009            BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
3010        )
3011    )
3012}
3013
3014/// Whether or not a strut should be created for an inline container. Normally
3015/// all inline containers get struts. In quirks mode this isn't always the case
3016/// though.
3017///
3018/// From <https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk>
3019///
3020/// > ### § 3.3. The line height calculation quirk
3021/// > In quirks mode and limited-quirks mode, an inline box that matches the following
3022/// > conditions, must, for the purpose of line height calculation, act as if the box had a
3023/// > line-height of zero.
3024/// >
3025/// >  - The border-top-width, border-bottom-width, padding-top and padding-bottom
3026/// >    properties have a used value of zero and the box has a vertical writing mode, or the
3027/// >    border-right-width, border-left-width, padding-right and padding-left properties have
3028/// >    a used value of zero and the box has a horizontal writing mode.
3029/// >  - It either contains no text or it contains only collapsed whitespace.
3030/// >
3031/// > ### § 3.4. The blocks ignore line-height quirk
3032/// > In quirks mode and limited-quirks mode, for a block container element whose content is
3033/// > composed of inline-level elements, the element’s line-height must be ignored for the
3034/// > purpose of calculating the minimal height of line boxes within the element.
3035///
3036/// Since we incorporate the size of the strut into the line-height calculation when
3037/// adding text, we can simply not incorporate the strut at the start of inline box
3038/// processing. This also works the same for the root of the IFC.
3039fn inline_container_needs_strut(
3040    style: &ComputedValues,
3041    layout_context: &LayoutContext,
3042    pbm: Option<&PaddingBorderMargin>,
3043) -> bool {
3044    if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
3045        return true;
3046    }
3047
3048    // This is not in a standard yet, but all browsers disable this quirk for list items.
3049    // See https://github.com/whatwg/quirks/issues/38.
3050    if style.get_box().display.is_list_item() {
3051        return true;
3052    }
3053
3054    pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
3055}
3056
3057impl ComputeInlineContentSizes for InlineFormattingContext {
3058    // This works on an already-constructed `InlineFormattingContext`,
3059    // Which would have to change if/when
3060    // `BlockContainer::construct` parallelize their construction.
3061    fn compute_inline_content_sizes(
3062        &self,
3063        layout_context: &LayoutContext,
3064        constraint_space: &ConstraintSpace,
3065    ) -> InlineContentSizesResult {
3066        ContentSizesComputation::compute(self, layout_context, constraint_space)
3067    }
3068}
3069
3070/// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`].
3071struct ContentSizesComputation<'layout_data> {
3072    layout_context: &'layout_data LayoutContext<'layout_data>,
3073    constraint_space: &'layout_data ConstraintSpace<'layout_data>,
3074    paragraph: ContentSizes,
3075    current_line: ContentSizes,
3076    /// Size for whitespace pending to be added to this line.
3077    pending_whitespace: ContentSizes,
3078    /// Padding, border, and margins from the opening of inline boxes that
3079    /// has not yet been committed to the line.
3080    pending_opening_pbm_for_inline_boxes: Au,
3081    /// The size of the not yet cleared floats in the inline axis of the containing block.
3082    uncleared_floats: LogicalSides1D<ContentSizes>,
3083    /// The size of the already cleared floats in the inline axis of the containing block.
3084    cleared_floats: LogicalSides1D<ContentSizes>,
3085    /// Whether or not the current line has seen any content (excluding collapsed whitespace),
3086    /// when sizing under a min-content constraint.
3087    had_content_yet_for_min_content: bool,
3088    /// Whether or not the current line has seen any content (excluding collapsed whitespace),
3089    /// when sizing under a max-content constraint.
3090    had_content_yet_for_max_content: bool,
3091    /// Stack of ending padding, margin, and border to add to the length
3092    /// when an inline box finishes.
3093    ending_inline_pbm_stack: Vec<Au>,
3094    /// Stack of [`TextWrapMode`] of ongoing inline boxes.
3095    text_wrap_mode_stack: Vec<TextWrapMode>,
3096    /// Whether the inline content size depends on block constraints.
3097    depends_on_block_constraints: bool,
3098}
3099
3100impl<'layout_data> ContentSizesComputation<'layout_data> {
3101    fn traverse(
3102        mut self,
3103        inline_formatting_context: &InlineFormattingContext,
3104    ) -> InlineContentSizesResult {
3105        self.add_inline_size(
3106            inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
3107        );
3108        for inline_item in &inline_formatting_context.inline_items {
3109            self.process_item(inline_item, inline_formatting_context);
3110        }
3111        self.forced_line_break();
3112        self.flush_floats();
3113
3114        InlineContentSizesResult {
3115            sizes: self.paragraph,
3116            depends_on_block_constraints: self.depends_on_block_constraints,
3117        }
3118    }
3119
3120    fn process_item(
3121        &mut self,
3122        inline_item: &InlineItem,
3123        inline_formatting_context: &InlineFormattingContext,
3124    ) {
3125        match inline_item {
3126            InlineItem::StartInlineBox(inline_box) => {
3127                // For margins and paddings, a cyclic percentage is resolved against zero
3128                // for determining intrinsic size contributions.
3129                // https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
3130                let inline_box = inline_box.borrow();
3131                let zero = Au::zero();
3132                let writing_mode = self.constraint_space.style.writing_mode;
3133                let layout_style = inline_box.layout_style();
3134                let padding = layout_style
3135                    .padding(writing_mode)
3136                    .percentages_relative_to(zero);
3137                let border = layout_style.border_width(writing_mode);
3138                let margin = inline_box
3139                    .base
3140                    .style
3141                    .margin(writing_mode)
3142                    .percentages_relative_to(zero)
3143                    .auto_is(Au::zero);
3144
3145                let pbm = margin + padding + border;
3146                self.pending_opening_pbm_for_inline_boxes += pbm.inline_start;
3147                self.current_line.max_content += pbm.inline_start;
3148                self.ending_inline_pbm_stack.push(pbm.inline_end);
3149
3150                self.text_wrap_mode_stack
3151                    .push(layout_style.style().slow_clone_text_wrap_mode());
3152            },
3153            InlineItem::EndInlineBox(..) => {
3154                let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
3155                self.commit_pending_opening_pbm_for_inline_boxes();
3156                self.add_inline_size(length);
3157                self.text_wrap_mode_stack.pop();
3158            },
3159            InlineItem::TextRun(text_run) => {
3160                let text_run = &*text_run.borrow();
3161                let parent_style = text_run.inline_styles().style.borrow();
3162                for item in text_run.items.iter() {
3163                    match item {
3164                        TextRunItem::LineBreak { .. } => {
3165                            // If this run is a forced line break, we *must* break the line
3166                            // and start measuring from the inline origin once more.
3167                            self.forced_line_break();
3168                        },
3169                        TextRunItem::Tab { break_at_start, .. } => self.process_preserved_tab(
3170                            &parent_style,
3171                            inline_formatting_context,
3172                            *break_at_start,
3173                        ),
3174                        TextRunItem::TextSegment(segment) => {
3175                            self.process_text_segment(&parent_style, segment)
3176                        },
3177                    }
3178                }
3179            },
3180            InlineItem::Atomic(atomic, offset_in_text, _level) => {
3181                let can_wrap = self.text_wrap_mode() == &TextWrapMode::Wrap;
3182                if can_wrap &&
3183                    self.had_content_yet_for_min_content &&
3184                    !inline_formatting_context
3185                        .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
3186                {
3187                    self.line_break_opportunity();
3188                }
3189
3190                self.commit_pending_opening_pbm_for_inline_boxes();
3191                self.commit_pending_whitespace();
3192                let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
3193                self.current_line += outer;
3194
3195                if can_wrap &&
3196                    !inline_formatting_context
3197                        .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
3198                {
3199                    self.line_break_opportunity();
3200                }
3201            },
3202            InlineItem::OutOfFlowFloatBox(float_box) => {
3203                let float_box = float_box.borrow();
3204                let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
3205                let style = &float_box.contents.style();
3206                let container_writing_mode = self.constraint_space.style.writing_mode;
3207                let clear =
3208                    Clear::from_style_and_container_writing_mode(style, container_writing_mode);
3209                self.clear_floats(clear);
3210                let float_side =
3211                    FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
3212                match float_side.expect("A float box needs to float to some side") {
3213                    FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
3214                    FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
3215                }
3216            },
3217            InlineItem::BlockLevel(block_level) => {
3218                self.forced_line_break();
3219                self.flush_floats();
3220                let inline_content_sizes_result =
3221                    compute_inline_content_sizes_for_block_level_boxes(
3222                        std::slice::from_ref(block_level),
3223                        self.layout_context,
3224                        &self.constraint_space.into(),
3225                    );
3226                self.depends_on_block_constraints |=
3227                    inline_content_sizes_result.depends_on_block_constraints;
3228                self.current_line = inline_content_sizes_result.sizes;
3229                self.forced_line_break();
3230            },
3231            InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
3232        }
3233    }
3234
3235    fn process_text_segment(
3236        &mut self,
3237        parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3238        segment: &TextRunSegment,
3239    ) {
3240        let style_text = parent_style.get_inherited_text();
3241        let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
3242
3243        // TODO: This should take account whether or not the first and last character prevent
3244        // linebreaks after atomics as in layout.
3245        let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
3246
3247        // From <https://drafts.csswg.org/css-text-3/#white-space-phase-2>:
3248        // > If white-space is set to pre-wrap, the UA must (unconditionally) hang
3249        // > this sequence, unless the sequence is followed by a forced line break, in
3250        // > which case it must conditionally hang the sequence instead. It may also
3251        // > visually collapse the character advance widths of any that would otherwise
3252        // > overflow.
3253        // From <https://drafts.csswg.org/css-text-3/#conditionally-hang>:
3254        // > In some cases, a glyph at the end of a line can conditionally hang: it hangs
3255        // > only if it does not otherwise fit in the line prior to justification. It is
3256        // > not considered when measuring the line’s contents for fit; however, any part
3257        // > of it that does not fit is considered to hang. Glyphs that conditionally hang
3258        // > are not taken into account when computing min-content sizes and any sizes
3259        // > derived thereof, but they are taken into account for max-content sizes and any
3260        // > sizes derived thereof.
3261        let hangable_characters_count_for_max_content =
3262            parent_style.white_space_may_hang_conditionally();
3263
3264        for (run_index, run) in segment.runs.iter().enumerate() {
3265            // Break before each unbreakable run in this TextRun, except the first unless the
3266            // linebreaker was set to break before the first run.
3267            if can_wrap && (run_index != 0 || break_at_start) {
3268                self.line_break_opportunity();
3269            }
3270
3271            let advance = run.total_advance();
3272            let removable_advance = run.removable_advance();
3273            let hangable_advance = run.hangable_advance();
3274            if run.has_non_hangable_non_removable_content() {
3275                self.commit_pending_opening_pbm_for_inline_boxes();
3276                self.commit_pending_whitespace();
3277                self.add_inline_size(advance - removable_advance - hangable_advance);
3278            }
3279
3280            self.pending_whitespace.min_content += hangable_advance;
3281            if hangable_characters_count_for_max_content {
3282                self.current_line.max_content += hangable_advance;
3283            } else {
3284                self.pending_whitespace.max_content += hangable_advance;
3285            }
3286
3287            if self.had_content_yet_for_min_content {
3288                self.pending_whitespace.min_content += removable_advance;
3289            }
3290            if self.had_content_yet_for_max_content {
3291                self.pending_whitespace.max_content += removable_advance;
3292            }
3293        }
3294    }
3295
3296    fn process_preserved_tab(
3297        &mut self,
3298        parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3299        inline_formatting_context: &InlineFormattingContext,
3300        break_at_start: bool,
3301    ) {
3302        let can_wrap = parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap;
3303        if break_at_start && can_wrap {
3304            self.line_break_opportunity();
3305        }
3306
3307        self.commit_pending_opening_pbm_for_inline_boxes();
3308
3309        let conditionally_hangs = parent_style.white_space_may_hang_conditionally();
3310        if !conditionally_hangs {
3311            self.commit_pending_whitespace();
3312        }
3313
3314        let next_tab_stop_for_min_content = inline_formatting_context
3315            .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
3316        let next_tab_stop_for_max_content = inline_formatting_context
3317            .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
3318
3319        if conditionally_hangs {
3320            self.pending_whitespace.min_content += next_tab_stop_for_min_content;
3321        } else {
3322            self.current_line.min_content += next_tab_stop_for_min_content;
3323        }
3324        self.current_line.max_content += next_tab_stop_for_max_content;
3325
3326        if can_wrap {
3327            self.line_break_opportunity();
3328        }
3329    }
3330
3331    fn add_inline_size(&mut self, size: Au) {
3332        self.current_line.min_content += size;
3333        self.current_line.max_content += size;
3334    }
3335
3336    fn line_break_opportunity(&mut self) {
3337        // Clear the pending whitespace, assuming that at the end of the line
3338        // it needs to either hang or be removed. If that isn't the case,
3339        // `commit_pending_whitespace()` should be called first.
3340        self.pending_whitespace.min_content = Au::zero();
3341        let current_min_content = mem::take(&mut self.current_line.min_content);
3342        self.paragraph.min_content.max_assign(current_min_content);
3343        self.had_content_yet_for_min_content = false;
3344    }
3345
3346    fn forced_line_break(&mut self) {
3347        // A forced line break causes any pending open inline box padding, border,
3348        // and margin to immediately be considered part of the current line.
3349        self.commit_pending_opening_pbm_for_inline_boxes();
3350
3351        // Handle the line break for min-content sizes.
3352        self.line_break_opportunity();
3353
3354        // Repeat the same logic as `line_break_opportunity()`, but now for max-content sizes.
3355        self.pending_whitespace.max_content = Au::zero();
3356        let current_max_content = mem::take(&mut self.current_line.max_content);
3357        self.paragraph.max_content.max_assign(current_max_content);
3358        self.had_content_yet_for_max_content = false;
3359    }
3360
3361    fn commit_pending_whitespace(&mut self) {
3362        self.current_line += mem::take(&mut self.pending_whitespace);
3363        self.had_content_yet_for_min_content = true;
3364        self.had_content_yet_for_max_content = true;
3365    }
3366
3367    fn commit_pending_opening_pbm_for_inline_boxes(&mut self) {
3368        self.current_line.min_content +=
3369            std::mem::take(&mut self.pending_opening_pbm_for_inline_boxes);
3370    }
3371
3372    fn outer_inline_content_sizes_of_float_or_atomic(
3373        &mut self,
3374        context: &IndependentFormattingContext,
3375    ) -> ContentSizes {
3376        let result = context.outer_inline_content_sizes(
3377            self.layout_context,
3378            &self.constraint_space.into(),
3379            &LogicalVec2::zero(),
3380            false, /* auto_block_size_stretches_to_containing_block */
3381            skippable_for_block_percentage_quirk(self.layout_context, &context.base, Some(context)),
3382        );
3383        self.depends_on_block_constraints |= result.depends_on_block_constraints;
3384        result.sizes
3385    }
3386
3387    fn clear_floats(&mut self, clear: Clear) {
3388        match clear {
3389            Clear::InlineStart => {
3390                let start_floats = mem::take(&mut self.uncleared_floats.start);
3391                self.cleared_floats.start.max_assign(start_floats);
3392            },
3393            Clear::InlineEnd => {
3394                let end_floats = mem::take(&mut self.uncleared_floats.end);
3395                self.cleared_floats.end.max_assign(end_floats);
3396            },
3397            Clear::Both => {
3398                let start_floats = mem::take(&mut self.uncleared_floats.start);
3399                let end_floats = mem::take(&mut self.uncleared_floats.end);
3400                self.cleared_floats.start.max_assign(start_floats);
3401                self.cleared_floats.end.max_assign(end_floats);
3402            },
3403            Clear::None => {},
3404        }
3405    }
3406
3407    fn flush_floats(&mut self) {
3408        self.clear_floats(Clear::Both);
3409        let start_floats = mem::take(&mut self.cleared_floats.start);
3410        let end_floats = mem::take(&mut self.cleared_floats.end);
3411        self.paragraph.union_assign(&start_floats);
3412        self.paragraph.union_assign(&end_floats);
3413    }
3414
3415    fn text_wrap_mode(&self) -> &TextWrapMode {
3416        self.text_wrap_mode_stack
3417            .last()
3418            .unwrap_or_else(|| self.constraint_space.style.get_text_wrap_mode())
3419    }
3420
3421    /// Compute the [`ContentSizes`] of the given [`InlineFormattingContext`].
3422    fn compute(
3423        inline_formatting_context: &InlineFormattingContext,
3424        layout_context: &'layout_data LayoutContext,
3425        constraint_space: &'layout_data ConstraintSpace,
3426    ) -> InlineContentSizesResult {
3427        Self {
3428            layout_context,
3429            constraint_space,
3430            paragraph: ContentSizes::zero(),
3431            current_line: ContentSizes::zero(),
3432            pending_whitespace: ContentSizes::zero(),
3433            pending_opening_pbm_for_inline_boxes: Au::zero(),
3434            uncleared_floats: LogicalSides1D::default(),
3435            cleared_floats: LogicalSides1D::default(),
3436            had_content_yet_for_min_content: false,
3437            had_content_yet_for_max_content: false,
3438            ending_inline_pbm_stack: Vec::new(),
3439            text_wrap_mode_stack: Vec::new(),
3440            depends_on_block_constraints: false,
3441        }
3442        .traverse(inline_formatting_context)
3443    }
3444}
3445
3446pub(crate) struct BidiLevels<'a> {
3447    info: Option<BidiInfo<'a>>,
3448}
3449
3450impl BidiLevels<'_> {
3451    fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3452        self.info
3453            .as_ref()
3454            .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3455    }
3456}
3457
3458/// Whether or not this character will prevent a soft wrap opportunity when it
3459/// comes before or after an atomic inline element.
3460///
3461/// From <https://www.w3.org/TR/css-text-3/#line-break-details>:
3462///
3463/// > For Web-compatibility there is a soft wrap opportunity before and after each
3464/// > replaced element or other atomic inline, even when adjacent to a character that
3465/// > would normally suppress them, including U+00A0 NO-BREAK SPACE. However, with
3466/// > the exception of U+00A0 NO-BREAK SPACE, there must be no soft wrap opportunity
3467/// > between atomic inlines and adjacent characters belonging to the Unicode GL, WJ,
3468/// > or ZWJ line breaking classes.
3469fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3470    if character == '\u{00A0}' {
3471        return false;
3472    }
3473    matches!(
3474        ICULineBreak::for_char(character),
3475        ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3476    )
3477}