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