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