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