layout/fragment_tree/
base_fragment.rs1use std::sync::LazyLock;
6use std::sync::atomic::{AtomicU8, Ordering};
7
8use app_units::Au;
9use bitflags::bitflags;
10use layout_api::{LayoutElement, LayoutNode, PseudoElementChain, combine_id_with_fragment_type};
11use malloc_size_of::malloc_size_of_is_0;
12use malloc_size_of_derive::MallocSizeOf;
13use num_derive::FromPrimitive;
14use num_traits::FromPrimitive;
15use script::layout_dom::ServoLayoutNode;
16use style::dom::OpaqueNode;
17use style::selector_parser::PseudoElement;
18use stylo_atoms::atom;
19use web_atoms::{LocalName, local_name, ns};
20
21use crate::dom::NodeExt;
22use crate::dom_traversal::NodeAndStyleInfo;
23use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize, SyncPhysicalRectAu};
24
25static CONTAINERTIMING_ATTRIBUTE: LazyLock<LocalName> =
27 LazyLock::new(|| LocalName::from("containertiming"));
28static CONTAINERTIMINGIGNORE_ATTRIBUTE: LazyLock<LocalName> =
29 LazyLock::new(|| LocalName::from("containertimingignore"));
30
31#[derive(Clone, Debug, Default, FromPrimitive, MallocSizeOf, PartialEq)]
32#[repr(u8)]
33pub(crate) enum FragmentStatus {
34 #[default]
36 New,
37 StyleChanged,
39 OnlyDescendantsChanged,
42 Clean,
44}
45
46#[derive(MallocSizeOf)]
50pub(crate) struct BaseFragment {
51 pub tag: Option<Tag>,
55
56 pub flags: FragmentFlags,
59
60 rect: SyncPhysicalRectAu,
63
64 pub status: AtomicU8,
66}
67
68impl std::fmt::Debug for BaseFragment {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 let mut formatter = f.debug_struct("BaseFragment");
71 let mut formatter = formatter.field("tag", &self.tag);
72 if !self.flags.is_empty() {
73 formatter = formatter.field("flags", &self.flags);
74 }
75 formatter
76 .field("rect", &self.rect())
77 .field("status", &self.status())
78 .finish()
79 }
80}
81
82impl BaseFragment {
83 pub(crate) fn new(base_fragment_info: BaseFragmentInfo, rect: PhysicalRect<Au>) -> Self {
84 Self {
85 tag: base_fragment_info.tag,
86 flags: base_fragment_info.flags,
87 rect: SyncPhysicalRectAu::new(rect),
88 status: AtomicU8::new(FragmentStatus::New as u8),
89 }
90 }
91
92 #[inline]
93 pub(crate) fn rect(&self) -> PhysicalRect<Au> {
94 self.rect.get()
95 }
96
97 #[inline]
98 pub(crate) fn set_rect(&self, new_rect: PhysicalRect<Au>) {
99 self.rect.set(new_rect);
100 }
101
102 #[inline]
103 pub(crate) fn translate_rect(&self, offset: PhysicalSize<Au>) {
104 self.rect.translate(offset)
105 }
106
107 #[inline]
108 pub(crate) fn set_rect_origin(&self, offset: PhysicalPoint<Au>) {
109 self.rect.set_origin(offset)
110 }
111
112 pub(crate) fn is_anonymous(&self) -> bool {
113 self.tag.is_none()
114 }
115
116 pub(crate) fn status(&self) -> FragmentStatus {
117 FragmentStatus::from_u8(self.status.load(Ordering::Relaxed))
118 .expect("Unknown FragmentStatus value")
119 }
120
121 pub(crate) fn set_status(&self, new_status: FragmentStatus) {
122 self.status.store(new_status as u8, Ordering::Relaxed)
123 }
124}
125
126#[derive(Clone, Copy, Debug, MallocSizeOf)]
128pub(crate) struct BaseFragmentInfo {
129 pub tag: Option<Tag>,
131
132 pub flags: FragmentFlags,
134}
135
136impl BaseFragmentInfo {
137 pub(crate) fn anonymous() -> Self {
138 Self {
139 tag: None,
140 flags: FragmentFlags::empty(),
141 }
142 }
143
144 pub(crate) fn new_for_testing(id: usize) -> Self {
145 Self {
146 tag: Some(Tag {
147 node: OpaqueNode(id),
148 pseudo_element_chain: Default::default(),
149 }),
150 flags: FragmentFlags::empty(),
151 }
152 }
153
154 pub(crate) fn is_anonymous(&self) -> bool {
155 self.tag.is_none()
156 }
157}
158
159impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
160 fn from(info: &NodeAndStyleInfo) -> Self {
161 info.node.into()
162 }
163}
164
165impl From<ServoLayoutNode<'_>> for BaseFragmentInfo {
166 fn from(node: ServoLayoutNode) -> Self {
167 let pseudo_element_chain = node.pseudo_element_chain();
168 let mut flags = FragmentFlags::empty();
169
170 if let Some(innermost_pseudo) = pseudo_element_chain.innermost() {
171 match innermost_pseudo {
172 PseudoElement::ServoAnonymousBox |
178 PseudoElement::ServoAnonymousTable |
179 PseudoElement::ServoAnonymousTableCell |
180 PseudoElement::ServoAnonymousTableRow => return Self::anonymous(),
181 PseudoElement::Before if node.is_html_br_element() => {
184 flags.insert(FragmentFlags::IS_BR_ELEMENT);
185 },
186 _ => {},
187 }
188 return Self {
189 tag: Some(node.into()),
190 flags,
191 };
192 }
193
194 if node.as_element().is_some_and(|element| element.is_root()) {
195 flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
196 }
197
198 if let Some(element) = node.as_html_element() {
199 if element.is_body_element_of_html_element_root() {
200 flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
201 }
202 match element.local_name() {
203 &local_name!("br") => {
204 flags.insert(FragmentFlags::IS_BR_ELEMENT);
205 },
206 &local_name!("table") | &local_name!("th") | &local_name!("td") => {
207 flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
208 },
209 &local_name!("input") => {
210 flags.insert(FragmentFlags::IS_INPUT_ELEMENT);
211 if element
212 .attribute(&ns!(), &local_name!("type"))
213 .is_some_and(|attr| {
214 matches!(
215 attr.as_atom().to_ascii_lowercase(),
216 atom!("button") | atom!("color") | atom!("reset") | atom!("submit")
217 )
218 })
219 {
220 flags.insert(FragmentFlags::IS_BUTTON);
221 }
222 },
223 &local_name!("button") => {
224 flags.insert(FragmentFlags::IS_BUTTON);
225 },
226 _ => {},
227 }
228
229 if element
230 .attribute(&ns!(), &CONTAINERTIMING_ATTRIBUTE)
231 .is_some()
232 {
233 flags.insert(FragmentFlags::IS_CONTAINER_TIMING_ROOT);
234 }
235 if element
236 .attribute(&ns!(), &CONTAINERTIMINGIGNORE_ATTRIBUTE)
237 .is_some()
238 {
239 flags.insert(FragmentFlags::IS_CONTAINER_TIMING_IGNORE);
240 }
241 };
242
243 Self {
244 tag: Some(node.into()),
245 flags,
246 }
247 }
248}
249
250bitflags! {
251 #[derive(Clone, Copy, Debug)]
253 pub(crate) struct FragmentFlags: u16 {
254 const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
256 const IS_BR_ELEMENT = 1 << 1;
259 const IS_WIDGET = 1 << 2;
264 const IS_FLEX_OR_GRID_ITEM = 1 << 3;
266 const IS_REPLACED = 1 << 4;
269 const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 5;
273 const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 6;
276 const DO_NOT_PAINT = 1 << 7;
280 const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8;
284 const IS_ROOT_ELEMENT = 1 << 9;
286 const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10;
288 const IS_COLLAPSED = 1 << 11;
291 const IS_INPUT_ELEMENT = 1 << 12;
293 const IS_BUTTON = 1 << 13;
295 const IS_CONTAINER_TIMING_ROOT = 1 << 14;
298 const IS_CONTAINER_TIMING_IGNORE = 1 << 15;
302 }
303}
304
305impl FragmentFlags {
306 pub(crate) fn is_compressible_replaced(self) -> bool {
311 self.contains(Self::IS_REPLACED) ||
312 (self.contains(Self::IS_WIDGET) && !self.contains(Self::IS_BUTTON))
313 }
314}
315
316malloc_size_of_is_0!(FragmentFlags);
317
318#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
321pub(crate) struct Tag {
322 pub(crate) node: OpaqueNode,
323 pub(crate) pseudo_element_chain: PseudoElementChain,
324}
325
326impl std::fmt::Debug for Tag {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 f.write_fmt(format_args!("Tag({:?}", self.node))?;
329 if let Some(pseudo) = self.pseudo_element_chain.primary {
330 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
331 }
332 if let Some(pseudo) = self.pseudo_element_chain.secondary {
333 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
334 }
335 f.write_str(")")
336 }
337}
338
339impl Tag {
340 pub(crate) fn to_display_list_fragment_id(self) -> u64 {
341 combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
342 }
343}
344
345impl From<ServoLayoutNode<'_>> for Tag {
346 fn from(node: ServoLayoutNode<'_>) -> Self {
347 Self {
348 node: node.opaque(),
349 pseudo_element_chain: node.pseudo_element_chain(),
350 }
351 }
352}