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 bitflags::bitflags;
6use html5ever::local_name;
7use layout_api::wrapper_traits::{
8    PseudoElementChain, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
9};
10use layout_api::{LayoutElementType, LayoutNodeType, combine_id_with_fragment_type};
11use malloc_size_of::malloc_size_of_is_0;
12use malloc_size_of_derive::MallocSizeOf;
13use script::layout_dom::ServoThreadSafeLayoutNode;
14use style::dom::OpaqueNode;
15use style::selector_parser::PseudoElement;
16
17use crate::dom_traversal::NodeAndStyleInfo;
18
19/// This data structure stores fields that are common to all non-base
20/// Fragment types and should generally be the first member of all
21/// concrete fragments.
22#[derive(Clone, Debug, MallocSizeOf)]
23pub(crate) struct BaseFragment {
24    /// A tag which identifies the DOM node and pseudo element of this
25    /// Fragment's content. If this fragment is for an anonymous box,
26    /// the tag will be None.
27    pub tag: Option<Tag>,
28
29    /// Flags which various information about this fragment used during
30    /// layout.
31    pub flags: FragmentFlags,
32}
33
34impl BaseFragment {
35    pub(crate) fn anonymous() -> Self {
36        BaseFragment {
37            tag: None,
38            flags: FragmentFlags::empty(),
39        }
40    }
41
42    pub(crate) fn is_anonymous(&self) -> bool {
43        self.tag.is_none()
44    }
45}
46
47/// Information necessary to construct a new BaseFragment.
48#[derive(Clone, Copy, Debug, MallocSizeOf)]
49pub(crate) struct BaseFragmentInfo {
50    /// The tag to use for the new BaseFragment, if it is not an anonymous Fragment.
51    pub tag: Option<Tag>,
52
53    /// The flags to use for the new BaseFragment.
54    pub flags: FragmentFlags,
55}
56
57impl BaseFragmentInfo {
58    pub(crate) fn anonymous() -> Self {
59        Self {
60            tag: None,
61            flags: FragmentFlags::empty(),
62        }
63    }
64
65    pub(crate) fn new_for_testing(id: usize) -> Self {
66        Self {
67            tag: Some(Tag {
68                node: OpaqueNode(id),
69                pseudo_element_chain: Default::default(),
70            }),
71            flags: FragmentFlags::empty(),
72        }
73    }
74}
75
76impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
77    fn from(info: &NodeAndStyleInfo) -> Self {
78        info.node.into()
79    }
80}
81
82impl From<ServoThreadSafeLayoutNode<'_>> for BaseFragmentInfo {
83    fn from(node: ServoThreadSafeLayoutNode) -> Self {
84        let pseudo_element_chain = node.pseudo_element_chain();
85        let mut flags = FragmentFlags::empty();
86
87        // Anonymous boxes should not have a tag, because they should not take part in hit testing.
88        //
89        // TODO(mrobinson): It seems that anonymous boxes should take part in hit testing in some
90        // cases, but currently this means that the order of hit test results isn't as expected for
91        // some WPT tests. This needs more investigation.
92        if matches!(
93            pseudo_element_chain.innermost(),
94            Some(PseudoElement::ServoAnonymousBox) |
95                Some(PseudoElement::ServoAnonymousTable) |
96                Some(PseudoElement::ServoAnonymousTableCell) |
97                Some(PseudoElement::ServoAnonymousTableRow)
98        ) {
99            return Self::anonymous();
100        }
101
102        if let Some(element) = node.as_html_element() {
103            if element.is_body_element_of_html_element_root() {
104                flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
105            }
106
107            match element.get_local_name() {
108                &local_name!("br") => {
109                    flags.insert(FragmentFlags::IS_BR_ELEMENT);
110                },
111                &local_name!("table") | &local_name!("th") | &local_name!("td") => {
112                    flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
113                },
114                _ => {},
115            }
116
117            if matches!(
118                element.type_id(),
119                Some(LayoutNodeType::Element(
120                    LayoutElementType::HTMLInputElement | LayoutElementType::HTMLTextAreaElement
121                ))
122            ) {
123                flags.insert(FragmentFlags::IS_TEXT_CONTROL);
124            }
125
126            if ThreadSafeLayoutElement::is_root(&element) {
127                flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
128            }
129        };
130
131        Self {
132            tag: Some(node.into()),
133            flags,
134        }
135    }
136}
137
138impl From<BaseFragmentInfo> for BaseFragment {
139    fn from(info: BaseFragmentInfo) -> Self {
140        Self {
141            tag: info.tag,
142            flags: info.flags,
143        }
144    }
145}
146
147bitflags! {
148    /// Flags used to track various information about a DOM node during layout.
149    #[derive(Clone, Copy, Debug)]
150    pub(crate) struct FragmentFlags: u16 {
151        /// Whether or not the node that created this fragment is a `<body>` element on an HTML document.
152        const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
153        /// Whether or not the node that created this Fragment is a `<br>` element.
154        const IS_BR_ELEMENT = 1 << 1;
155        /// Whether or not the node that created this Fragment is a `<input>` or `<textarea>` element.
156        const IS_TEXT_CONTROL = 1 << 2;
157        /// Whether or not this Fragment is a flex item or a grid item.
158        const IS_FLEX_OR_GRID_ITEM = 1 << 3;
159        /// Whether or not this Fragment was created to contain a replaced element or is
160        /// a replaced element.
161        const IS_REPLACED = 1 << 4;
162        /// Whether or not the node that created was a `<table>`, `<th>` or
163        /// `<td>` element. Note that this does *not* include elements with
164        /// `display: table` or `display: table-cell`.
165        const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 5;
166        /// Whether or not this Fragment was created to contain a list item marker
167        /// with a used value of `list-style-position: outside`.
168        const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 6;
169        /// Avoid painting the borders, backgrounds, and drop shadow for this fragment, this is used
170        /// for empty table cells when 'empty-cells' is 'hide' and also table wrappers.  This flag
171        /// doesn't avoid hit-testing nor does it prevent the painting outlines.
172        const DO_NOT_PAINT = 1 << 7;
173        /// Whether or not the size of this fragment depends on the block size of its container
174        /// and the fragment can be a flex item. This flag is used to cache items during flex
175        /// layout.
176        const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8;
177        /// Whether or not the node that created this fragment is the root element.
178        const IS_ROOT_ELEMENT = 1 << 9;
179        /// If element has propagated the overflow value to viewport.
180        const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10;
181        /// Whether or not this is a table cell that is part of a collapsed row or column.
182        /// In that case it should not be painted.
183        const IS_COLLAPSED = 1 << 11;
184
185    }
186}
187
188malloc_size_of_is_0!(FragmentFlags);
189
190/// A data structure used to hold DOM and pseudo-element information about
191/// a particular layout object.
192#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
193pub(crate) struct Tag {
194    pub(crate) node: OpaqueNode,
195    pub(crate) pseudo_element_chain: PseudoElementChain,
196}
197
198impl Tag {
199    pub(crate) fn to_display_list_fragment_id(self) -> u64 {
200        combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
201    }
202}
203
204impl From<ServoThreadSafeLayoutNode<'_>> for Tag {
205    fn from(node: ServoThreadSafeLayoutNode<'_>) -> Self {
206        Self {
207            node: node.opaque(),
208            pseudo_element_chain: node.pseudo_element_chain(),
209        }
210    }
211}