layout/fragment_tree/
base_fragment.rs1use std::sync::atomic::{AtomicU8, Ordering};
6
7use app_units::Au;
8use bitflags::bitflags;
9use layout_api::{LayoutElement, LayoutNode, PseudoElementChain, combine_id_with_fragment_type};
10use malloc_size_of::malloc_size_of_is_0;
11use malloc_size_of_derive::MallocSizeOf;
12use num_derive::FromPrimitive;
13use num_traits::FromPrimitive;
14use script::layout_dom::ServoLayoutNode;
15use style::dom::OpaqueNode;
16use style::selector_parser::PseudoElement;
17use stylo_atoms::atom;
18use web_atoms::{local_name, ns};
19
20use crate::dom::NodeExt;
21use crate::dom_traversal::NodeAndStyleInfo;
22use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize, SyncPhysicalRectAu};
23
24#[derive(Clone, Debug, Default, FromPrimitive, MallocSizeOf, PartialEq)]
25#[repr(u8)]
26pub(crate) enum FragmentStatus {
27 #[default]
29 New,
30 StyleChanged,
32 OnlyDescendantsChanged,
35 Clean,
37}
38
39#[derive(MallocSizeOf)]
43pub(crate) struct BaseFragment {
44 pub tag: Option<Tag>,
48
49 pub flags: FragmentFlags,
52
53 rect: SyncPhysicalRectAu,
57
58 pub status: AtomicU8,
60}
61
62impl std::fmt::Debug for BaseFragment {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 let mut formatter = f.debug_struct("BaseFragment");
65 let mut formatter = formatter.field("tag", &self.tag);
66 if !self.flags.is_empty() {
67 formatter = formatter.field("flags", &self.flags);
68 }
69 formatter
70 .field("rect", &self.rect())
71 .field("status", &self.status())
72 .finish()
73 }
74}
75
76impl BaseFragment {
77 pub(crate) fn new(base_fragment_info: BaseFragmentInfo, rect: PhysicalRect<Au>) -> Self {
78 Self {
79 tag: base_fragment_info.tag,
80 flags: base_fragment_info.flags,
81 rect: SyncPhysicalRectAu::new(rect),
82 status: AtomicU8::new(FragmentStatus::New as u8),
83 }
84 }
85
86 #[inline]
87 pub(crate) fn rect(&self) -> PhysicalRect<Au> {
88 self.rect.get()
89 }
90
91 #[inline]
92 pub(crate) fn set_rect(&self, new_rect: PhysicalRect<Au>) {
93 self.rect.set(new_rect);
94 }
95
96 #[inline]
97 pub(crate) fn translate_rect(&self, offset: PhysicalSize<Au>) {
98 self.rect.translate(offset)
99 }
100
101 #[inline]
102 pub(crate) fn set_rect_origin(&self, offset: PhysicalPoint<Au>) {
103 self.rect.set_origin(offset)
104 }
105
106 pub(crate) fn is_anonymous(&self) -> bool {
107 self.tag.is_none()
108 }
109
110 pub(crate) fn status(&self) -> FragmentStatus {
111 FragmentStatus::from_u8(self.status.load(Ordering::Relaxed))
112 .expect("Unknown FragmentStatus value")
113 }
114
115 pub(crate) fn set_status(&self, new_status: FragmentStatus) {
116 self.status.store(new_status as u8, Ordering::Relaxed)
117 }
118}
119
120#[derive(Clone, Copy, Debug, MallocSizeOf)]
122pub(crate) struct BaseFragmentInfo {
123 pub tag: Option<Tag>,
125
126 pub flags: FragmentFlags,
128}
129
130impl BaseFragmentInfo {
131 pub(crate) fn anonymous() -> Self {
132 Self {
133 tag: None,
134 flags: FragmentFlags::empty(),
135 }
136 }
137
138 pub(crate) fn new_for_testing(id: usize) -> Self {
139 Self {
140 tag: Some(Tag {
141 node: OpaqueNode(id),
142 pseudo_element_chain: Default::default(),
143 }),
144 flags: FragmentFlags::empty(),
145 }
146 }
147
148 pub(crate) fn is_anonymous(&self) -> bool {
149 self.tag.is_none()
150 }
151}
152
153impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
154 fn from(info: &NodeAndStyleInfo) -> Self {
155 info.node.into()
156 }
157}
158
159impl From<ServoLayoutNode<'_>> for BaseFragmentInfo {
160 fn from(node: ServoLayoutNode) -> Self {
161 let pseudo_element_chain = node.pseudo_element_chain();
162 let mut flags = FragmentFlags::empty();
163
164 if let Some(innermost_pseudo) = pseudo_element_chain.innermost() {
165 match innermost_pseudo {
166 PseudoElement::ServoAnonymousBox |
172 PseudoElement::ServoAnonymousTable |
173 PseudoElement::ServoAnonymousTableCell |
174 PseudoElement::ServoAnonymousTableRow => return Self::anonymous(),
175 PseudoElement::Before if node.is_html_br_element() => {
178 flags.insert(FragmentFlags::IS_BR_ELEMENT);
179 },
180 _ => {},
181 }
182 return Self {
183 tag: Some(node.into()),
184 flags,
185 };
186 }
187
188 if node.as_element().is_some_and(|element| element.is_root()) {
189 flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
190 }
191
192 if let Some(element) = node.as_html_element() {
193 if element.is_body_element_of_html_element_root() {
194 flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
195 }
196 match element.local_name() {
197 &local_name!("br") => {
198 flags.insert(FragmentFlags::IS_BR_ELEMENT);
199 },
200 &local_name!("table") | &local_name!("th") | &local_name!("td") => {
201 flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
202 },
203 &local_name!("input") => {
204 flags.insert(FragmentFlags::IS_INPUT_ELEMENT);
205 if element
206 .attribute(&ns!(), &local_name!("type"))
207 .is_some_and(|attr| {
208 matches!(
209 attr.as_atom().to_ascii_lowercase(),
210 atom!("button") | atom!("color") | atom!("reset") | atom!("submit")
211 )
212 })
213 {
214 flags.insert(FragmentFlags::IS_BUTTON);
215 }
216 },
217 &local_name!("button") => {
218 flags.insert(FragmentFlags::IS_BUTTON);
219 },
220 _ => {},
221 }
222 };
223
224 Self {
225 tag: Some(node.into()),
226 flags,
227 }
228 }
229}
230
231bitflags! {
232 #[derive(Clone, Copy, Debug)]
234 pub(crate) struct FragmentFlags: u16 {
235 const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
237 const IS_BR_ELEMENT = 1 << 1;
240 const IS_WIDGET = 1 << 2;
245 const IS_FLEX_OR_GRID_ITEM = 1 << 3;
247 const IS_REPLACED = 1 << 4;
250 const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 5;
254 const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 6;
257 const DO_NOT_PAINT = 1 << 7;
261 const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8;
265 const IS_ROOT_ELEMENT = 1 << 9;
267 const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10;
269 const IS_COLLAPSED = 1 << 11;
272 const IS_INPUT_ELEMENT = 1 << 12;
274 const IS_BUTTON = 1 << 13;
276 }
277}
278
279malloc_size_of_is_0!(FragmentFlags);
280
281#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
284pub(crate) struct Tag {
285 pub(crate) node: OpaqueNode,
286 pub(crate) pseudo_element_chain: PseudoElementChain,
287}
288
289impl std::fmt::Debug for Tag {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 f.write_fmt(format_args!("Tag({:?}", self.node))?;
292 if let Some(pseudo) = self.pseudo_element_chain.primary {
293 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
294 }
295 if let Some(pseudo) = self.pseudo_element_chain.secondary {
296 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
297 }
298 f.write_str(")")
299 }
300}
301
302impl Tag {
303 pub(crate) fn to_display_list_fragment_id(self) -> u64 {
304 combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
305 }
306}
307
308impl From<ServoLayoutNode<'_>> for Tag {
309 fn from(node: ServoLayoutNode<'_>) -> Self {
310 Self {
311 node: node.opaque(),
312 pseudo_element_chain: node.pseudo_element_chain(),
313 }
314 }
315}