Skip to main content

layout/flow/inline/
mod.rs

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