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