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