layout/fragment_tree/
base_fragment.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::ops::Deref;
6
7use app_units::Au;
8use atomic_refcell::AtomicRef;
9use bitflags::bitflags;
10use html5ever::local_name;
11use layout_api::combine_id_with_fragment_type;
12use layout_api::wrapper_traits::{
13    PseudoElementChain, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
14};
15use malloc_size_of::malloc_size_of_is_0;
16use malloc_size_of_derive::MallocSizeOf;
17use script::layout_dom::ServoThreadSafeLayoutNode;
18use servo_arc::Arc as ServoArc;
19use style::dom::OpaqueNode;
20use style::properties::ComputedValues;
21use style::selector_parser::PseudoElement;
22
23use crate::SharedStyle;
24use crate::dom_traversal::NodeAndStyleInfo;
25use crate::geom::PhysicalRect;
26
27pub(crate) enum BaseFragmentStyleRef<'a> {
28    Owned(&'a ServoArc<ComputedValues>),
29    Shared(AtomicRef<'a, ServoArc<ComputedValues>>),
30}
31
32impl<'a> Deref for BaseFragmentStyleRef<'a> {
33    type Target = ServoArc<ComputedValues>;
34
35    fn deref(&self) -> &Self::Target {
36        match self {
37            BaseFragmentStyleRef::Owned(style) => style,
38            BaseFragmentStyleRef::Shared(style_ref) => style_ref.deref(),
39        }
40    }
41}
42
43#[derive(Clone, MallocSizeOf)]
44pub(crate) enum BaseFragmentStyle {
45    Owned(ServoArc<ComputedValues>),
46    Shared(SharedStyle),
47}
48
49impl From<ServoArc<ComputedValues>> for BaseFragmentStyle {
50    fn from(style: ServoArc<ComputedValues>) -> Self {
51        BaseFragmentStyle::Owned(style)
52    }
53}
54
55impl From<SharedStyle> for BaseFragmentStyle {
56    fn from(style: SharedStyle) -> Self {
57        BaseFragmentStyle::Shared(style)
58    }
59}
60
61impl std::fmt::Debug for BaseFragmentStyle {
62    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            BaseFragmentStyle::Owned(..) => write!(formatter, "BaseFragmentStyle::Owned"),
65            BaseFragmentStyle::Shared(..) => write!(formatter, "BaseFragmentStyle::Shared"),
66        }
67    }
68}
69
70#[derive(Clone, Debug, Default, MallocSizeOf)]
71pub(crate) enum FragmentStatus {
72    /// This is a brand new fragment.
73    #[default]
74    New,
75    /// The style of the fragment has changed.
76    StyleChanged,
77    /// The fragment hasn't changed.
78    Clean,
79}
80
81/// This data structure stores fields that are common to all non-base
82/// Fragment types and should generally be the first member of all
83/// concrete fragments.
84#[derive(Clone, Debug, MallocSizeOf)]
85pub(crate) struct BaseFragment {
86    /// A tag which identifies the DOM node and pseudo element of this
87    /// Fragment's content. If this fragment is for an anonymous box,
88    /// the tag will be None.
89    pub tag: Option<Tag>,
90
91    /// Flags which various information about this fragment used during
92    /// layout.
93    pub flags: FragmentFlags,
94
95    /// The style for this [`BaseFragment`]. Depending on the fragment type this is either
96    /// a shared or non-shared style.
97    pub style: BaseFragmentStyle,
98
99    /// The content rect of this fragment in the parent fragment's content rectangle. This
100    /// does not include padding, border, or margin -- it only includes content. This is
101    /// relative to the parent containing block.
102    pub rect: PhysicalRect<Au>,
103
104    /// A [`FragmentStatus`] used to track fragment reuse when collecting reflow statistics.
105    pub status: FragmentStatus,
106}
107
108impl BaseFragment {
109    pub(crate) fn new(
110        base_fragment_info: BaseFragmentInfo,
111        style: BaseFragmentStyle,
112        rect: PhysicalRect<Au>,
113    ) -> Self {
114        Self {
115            tag: base_fragment_info.tag,
116            flags: base_fragment_info.flags,
117            style,
118            rect,
119            status: Default::default(),
120        }
121    }
122
123    pub(crate) fn is_anonymous(&self) -> bool {
124        self.tag.is_none()
125    }
126
127    pub(crate) fn repair_style(&mut self, style: &ServoArc<ComputedValues>) {
128        self.style = style.clone().into();
129        self.status = FragmentStatus::StyleChanged;
130    }
131
132    pub(crate) fn style<'a>(&'a self) -> BaseFragmentStyleRef<'a> {
133        match &self.style {
134            BaseFragmentStyle::Owned(computed_values) => {
135                BaseFragmentStyleRef::Owned(computed_values)
136            },
137            BaseFragmentStyle::Shared(shared_style) => {
138                BaseFragmentStyleRef::Shared(shared_style.borrow())
139            },
140        }
141    }
142}
143
144/// Information necessary to construct a new BaseFragment.
145#[derive(Clone, Copy, Debug, MallocSizeOf)]
146pub(crate) struct BaseFragmentInfo {
147    /// The tag to use for the new BaseFragment, if it is not an anonymous Fragment.
148    pub tag: Option<Tag>,
149
150    /// The flags to use for the new BaseFragment.
151    pub flags: FragmentFlags,
152}
153
154impl BaseFragmentInfo {
155    pub(crate) fn anonymous() -> Self {
156        Self {
157            tag: None,
158            flags: FragmentFlags::empty(),
159        }
160    }
161
162    pub(crate) fn new_for_testing(id: usize) -> Self {
163        Self {
164            tag: Some(Tag {
165                node: OpaqueNode(id),
166                pseudo_element_chain: Default::default(),
167            }),
168            flags: FragmentFlags::empty(),
169        }
170    }
171
172    pub(crate) fn is_anonymous(&self) -> bool {
173        self.tag.is_none()
174    }
175}
176
177impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
178    fn from(info: &NodeAndStyleInfo) -> Self {
179        info.node.into()
180    }
181}
182
183impl From<ServoThreadSafeLayoutNode<'_>> for BaseFragmentInfo {
184    fn from(node: ServoThreadSafeLayoutNode) -> Self {
185        let pseudo_element_chain = node.pseudo_element_chain();
186        let mut flags = FragmentFlags::empty();
187
188        // Anonymous boxes should not have a tag, because they should not take part in hit testing.
189        //
190        // TODO(mrobinson): It seems that anonymous boxes should take part in hit testing in some
191        // cases, but currently this means that the order of hit test results isn't as expected for
192        // some WPT tests. This needs more investigation.
193        if matches!(
194            pseudo_element_chain.innermost(),
195            Some(PseudoElement::ServoAnonymousBox) |
196                Some(PseudoElement::ServoAnonymousTable) |
197                Some(PseudoElement::ServoAnonymousTableCell) |
198                Some(PseudoElement::ServoAnonymousTableRow)
199        ) {
200            return Self::anonymous();
201        }
202
203        if let Some(element) = node.as_html_element() {
204            if element.is_body_element_of_html_element_root() {
205                flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
206            }
207
208            match element.get_local_name() {
209                &local_name!("br") => {
210                    flags.insert(FragmentFlags::IS_BR_ELEMENT);
211                },
212                &local_name!("table") | &local_name!("th") | &local_name!("td") => {
213                    flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
214                },
215                _ => {},
216            }
217
218            if ThreadSafeLayoutElement::is_root(&element) {
219                flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
220            }
221        };
222
223        Self {
224            tag: Some(node.into()),
225            flags,
226        }
227    }
228}
229
230bitflags! {
231    /// Flags used to track various information about a DOM node during layout.
232    #[derive(Clone, Copy, Debug)]
233    pub(crate) struct FragmentFlags: u16 {
234        /// Whether or not the node that created this fragment is a `<body>` element on an HTML document.
235        const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
236        /// Whether or not the node that created this Fragment is a `<br>` element.
237        const IS_BR_ELEMENT = 1 << 1;
238        /// Whether or not the node that created this Fragment is a widget. Widgets behave similarly to
239        /// replaced elements, e.g. they are atomic when inline-level, and their automatic inline size
240        /// doesn't stretch when block-level.
241        /// <https://drafts.csswg.org/css-ui/#widget>
242        const IS_WIDGET = 1 << 2;
243        /// Whether or not this Fragment is a flex item or a grid item.
244        const IS_FLEX_OR_GRID_ITEM = 1 << 3;
245        /// Whether or not this Fragment was created to contain a replaced element or is
246        /// a replaced element.
247        const IS_REPLACED = 1 << 4;
248        /// Whether or not the node that created was a `<table>`, `<th>` or
249        /// `<td>` element. Note that this does *not* include elements with
250        /// `display: table` or `display: table-cell`.
251        const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 5;
252        /// Whether or not this Fragment was created to contain a list item marker
253        /// with a used value of `list-style-position: outside`.
254        const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 6;
255        /// Avoid painting the borders, backgrounds, and drop shadow for this fragment, this is used
256        /// for empty table cells when 'empty-cells' is 'hide' and also table wrappers.  This flag
257        /// doesn't avoid hit-testing nor does it prevent the painting outlines.
258        const DO_NOT_PAINT = 1 << 7;
259        /// Whether or not the size of this fragment depends on the block size of its container
260        /// and the fragment can be a flex item. This flag is used to cache items during flex
261        /// layout.
262        const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8;
263        /// Whether or not the node that created this fragment is the root element.
264        const IS_ROOT_ELEMENT = 1 << 9;
265        /// If element has propagated the overflow value to viewport.
266        const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10;
267        /// Whether or not this is a table cell that is part of a collapsed row or column.
268        /// In that case it should not be painted.
269        const IS_COLLAPSED = 1 << 11;
270
271    }
272}
273
274malloc_size_of_is_0!(FragmentFlags);
275
276/// A data structure used to hold DOM and pseudo-element information about
277/// a particular layout object.
278#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
279pub(crate) struct Tag {
280    pub(crate) node: OpaqueNode,
281    pub(crate) pseudo_element_chain: PseudoElementChain,
282}
283
284impl Tag {
285    pub(crate) fn to_display_list_fragment_id(self) -> u64 {
286        combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
287    }
288}
289
290impl From<ServoThreadSafeLayoutNode<'_>> for Tag {
291    fn from(node: ServoThreadSafeLayoutNode<'_>) -> Self {
292        Self {
293            node: node.opaque(),
294            pseudo_element_chain: node.pseudo_element_chain(),
295        }
296    }
297}