layout/flow/inline/
inline_box.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
5use std::vec::IntoIter;
6
7use app_units::Au;
8use fonts::FontMetrics;
9use malloc_size_of_derive::MallocSizeOf;
10use script::layout_dom::ServoThreadSafeLayoutNode;
11use servo_arc::Arc as ServoArc;
12use style::properties::ComputedValues;
13
14use super::{
15    InlineContainerState, InlineContainerStateFlags, SharedInlineStyles,
16    inline_container_needs_strut,
17};
18use crate::ContainingBlock;
19use crate::cell::ArcRefCell;
20use crate::context::LayoutContext;
21use crate::dom_traversal::NodeAndStyleInfo;
22use crate::fragment_tree::BaseFragmentInfo;
23use crate::layout_box_base::LayoutBoxBase;
24use crate::style_ext::{LayoutStyle, PaddingBorderMargin};
25
26#[derive(Debug, MallocSizeOf)]
27pub(crate) struct InlineBox {
28    pub base: LayoutBoxBase,
29    /// The [`SharedInlineStyles`] for this [`InlineBox`] that are used to share styles
30    /// with all [`super::TextRun`] children.
31    pub(super) shared_inline_styles: SharedInlineStyles,
32    /// The identifier of this inline box in the containing [`super::InlineFormattingContext`].
33    pub(super) identifier: InlineBoxIdentifier,
34    /// Whether or not this is the first instance of an [`InlineBox`] before a possible
35    /// block-in-inline split. When no split occurs, this is always true.
36    pub is_first_split: bool,
37    /// Whether or not this is the last instance of an [`InlineBox`] before a possible
38    /// block-in-inline split. When no split occurs, this is always true.
39    pub is_last_split: bool,
40    /// The index of the default font in the [`super::InlineFormattingContext`]'s font metrics store.
41    /// This is initialized during IFC shaping.
42    pub default_font_index: Option<usize>,
43}
44
45impl InlineBox {
46    pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
47        Self {
48            base: LayoutBoxBase::new(info.into(), info.style.clone()),
49            shared_inline_styles: info.into(),
50            // This will be assigned later, when the box is actually added to the IFC.
51            identifier: InlineBoxIdentifier::default(),
52            is_first_split: true,
53            is_last_split: false,
54            default_font_index: None,
55        }
56    }
57
58    pub(crate) fn split_around_block(&self) -> Self {
59        Self {
60            base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()),
61            shared_inline_styles: self.shared_inline_styles.clone(),
62            is_first_split: false,
63            is_last_split: false,
64            ..*self
65        }
66    }
67
68    #[inline]
69    pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
70        LayoutStyle::Default(&self.base.style)
71    }
72
73    pub(crate) fn repair_style(
74        &mut self,
75        node: &ServoThreadSafeLayoutNode,
76        new_style: &ServoArc<ComputedValues>,
77    ) {
78        self.base.repair_style(new_style);
79        *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
80        *self.shared_inline_styles.selected.borrow_mut() = node.selected_style();
81    }
82}
83
84#[derive(Debug, Default, MallocSizeOf)]
85pub(crate) struct InlineBoxes {
86    /// A collection of all inline boxes in a particular [`super::InlineFormattingContext`].
87    inline_boxes: Vec<ArcRefCell<InlineBox>>,
88
89    /// A list of tokens that represent the actual tree of inline boxes, while allowing
90    /// easy traversal forward and backwards through the tree. This structure is also
91    /// stored in the [`super::InlineFormattingContext::inline_items`], but this version is
92    /// faster to iterate.
93    inline_box_tree: Vec<InlineBoxTreePathToken>,
94}
95
96impl InlineBoxes {
97    pub(super) fn len(&self) -> usize {
98        self.inline_boxes.len()
99    }
100
101    pub(super) fn iter(&self) -> impl Iterator<Item = &ArcRefCell<InlineBox>> {
102        self.inline_boxes.iter()
103    }
104
105    pub(super) fn get(&self, identifier: &InlineBoxIdentifier) -> ArcRefCell<InlineBox> {
106        self.inline_boxes[identifier.index_in_inline_boxes as usize].clone()
107    }
108
109    pub(super) fn end_inline_box(&mut self, identifier: InlineBoxIdentifier) {
110        self.inline_box_tree
111            .push(InlineBoxTreePathToken::End(identifier));
112    }
113
114    pub(super) fn start_inline_box(
115        &mut self,
116        inline_box: ArcRefCell<InlineBox>,
117    ) -> InlineBoxIdentifier {
118        assert!(self.inline_boxes.len() <= u32::MAX as usize);
119        assert!(self.inline_box_tree.len() <= u32::MAX as usize);
120
121        let index_in_inline_boxes = self.inline_boxes.len() as u32;
122        let index_of_start_in_tree = self.inline_box_tree.len() as u32;
123
124        let identifier = InlineBoxIdentifier {
125            index_of_start_in_tree,
126            index_in_inline_boxes,
127        };
128        inline_box.borrow_mut().identifier = identifier;
129
130        self.inline_boxes.push(inline_box);
131        self.inline_box_tree
132            .push(InlineBoxTreePathToken::Start(identifier));
133
134        identifier
135    }
136
137    pub(super) fn get_path(
138        &self,
139        from: Option<InlineBoxIdentifier>,
140        to: InlineBoxIdentifier,
141    ) -> IntoIter<InlineBoxTreePathToken> {
142        if from == Some(to) {
143            return Vec::new().into_iter();
144        }
145
146        let mut from_index = match from {
147            Some(InlineBoxIdentifier {
148                index_of_start_in_tree,
149                ..
150            }) => index_of_start_in_tree as usize,
151            None => 0,
152        };
153        let mut to_index = to.index_of_start_in_tree as usize;
154        let is_reversed = to_index < from_index;
155
156        // Do not include the first or final token, depending on direction. These can be equal
157        // if we are starting or going to the the root of the inline formatting context, in which
158        // case we don't want to adjust.
159        if to_index > from_index && from.is_some() {
160            from_index += 1;
161        } else if to_index < from_index {
162            to_index += 1;
163        }
164
165        let mut path = Vec::with_capacity(from_index.abs_diff(to_index));
166        let min = from_index.min(to_index);
167        let max = from_index.max(to_index);
168
169        for token in &self.inline_box_tree[min..=max] {
170            // Skip useless recursion into inline boxes; we are looking for a direct path.
171            if Some(&token.reverse()) == path.last() {
172                path.pop();
173            } else {
174                path.push(*token);
175            }
176        }
177
178        if is_reversed {
179            path.reverse();
180            for token in path.iter_mut() {
181                *token = token.reverse();
182            }
183        }
184
185        path.into_iter()
186    }
187}
188
189#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
190pub(super) enum InlineBoxTreePathToken {
191    Start(InlineBoxIdentifier),
192    End(InlineBoxIdentifier),
193}
194
195impl InlineBoxTreePathToken {
196    fn reverse(&self) -> Self {
197        match self {
198            Self::Start(index) => Self::End(*index),
199            Self::End(index) => Self::Start(*index),
200        }
201    }
202}
203
204/// An identifier for a particular [`InlineBox`] to be used to fetch it from an [`InlineBoxes`]
205/// store of inline boxes.
206///
207/// [`u32`] is used for the index, in order to save space. The value refers to the token
208/// in the start tree data structure which can be fetched to find the actual index of
209/// of the [`InlineBox`] in [`InlineBoxes::inline_boxes`].
210#[derive(Clone, Copy, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq)]
211pub(crate) struct InlineBoxIdentifier {
212    pub index_of_start_in_tree: u32,
213    pub index_in_inline_boxes: u32,
214}
215
216pub(super) struct InlineBoxContainerState {
217    /// The container state common to both [`InlineBox`] and the root of the
218    /// [`super::InlineFormattingContext`].
219    pub base: InlineContainerState,
220
221    /// The [`InlineBoxIdentifier`] of this inline container state. If this is the root
222    /// the identifier is [`None`].
223    pub identifier: InlineBoxIdentifier,
224
225    /// The [`BaseFragmentInfo`] of the [`InlineBox`] that this state tracks.
226    pub base_fragment_info: BaseFragmentInfo,
227
228    /// The [`PaddingBorderMargin`] of the [`InlineBox`] that this state tracks.
229    pub pbm: PaddingBorderMargin,
230
231    /// Whether this is the last fragment of this InlineBox. This may not be the case if
232    /// the InlineBox is split due to an block-in-inline-split and this is not the last of
233    /// that split.
234    pub is_last_fragment: bool,
235}
236
237impl InlineBoxContainerState {
238    pub(super) fn new(
239        inline_box: &InlineBox,
240        containing_block: &ContainingBlock,
241        layout_context: &LayoutContext,
242        parent_container: &InlineContainerState,
243        is_last_fragment: bool,
244        font_metrics: Option<&FontMetrics>,
245    ) -> Self {
246        let style = inline_box.base.style.clone();
247        let pbm = inline_box
248            .layout_style()
249            .padding_border_margin(containing_block);
250
251        let mut flags = InlineContainerStateFlags::empty();
252        if inline_container_needs_strut(&style, layout_context, Some(&pbm)) {
253            flags.insert(InlineContainerStateFlags::CREATE_STRUT);
254        }
255
256        Self {
257            base: InlineContainerState::new(style, flags, Some(parent_container), font_metrics),
258            identifier: inline_box.identifier,
259            base_fragment_info: inline_box.base.base_fragment_info,
260            pbm,
261            is_last_fragment,
262        }
263    }
264
265    pub(super) fn calculate_space_above_baseline(&self) -> Au {
266        let (ascent, descent, line_gap) = (
267            self.base.font_metrics.ascent,
268            self.base.font_metrics.descent,
269            self.base.font_metrics.line_gap,
270        );
271        let leading = line_gap - (ascent + descent);
272        leading.scale_by(0.5) + ascent
273    }
274}