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