layout/fragment_tree/
base_fragment.rs1use std::sync::atomic::{AtomicU8, Ordering};
6
7use app_units::Au;
8use atomic_refcell::AtomicRef;
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 servo_arc::Arc as ServoArc;
17use style::dom::OpaqueNode;
18use style::properties::ComputedValues;
19use style::selector_parser::PseudoElement;
20use stylo_atoms::atom;
21use web_atoms::{local_name, ns};
22
23use crate::SharedStyle;
24use crate::dom_traversal::NodeAndStyleInfo;
25use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize, SyncPhysicalRectAu};
26
27#[derive(Clone, Debug, Default, FromPrimitive, MallocSizeOf, PartialEq)]
28#[repr(u8)]
29pub(crate) enum FragmentStatus {
30 #[default]
32 New,
33 StyleChanged,
35 OnlyDescendantsChanged,
38 Clean,
40}
41
42#[derive(MallocSizeOf)]
46pub(crate) struct BaseFragment {
47 pub tag: Option<Tag>,
51
52 pub flags: FragmentFlags,
55
56 pub style: SharedStyle,
59
60 rect: SyncPhysicalRectAu,
64
65 pub status: AtomicU8,
67}
68
69impl std::fmt::Debug for BaseFragment {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 let mut formatter = f.debug_struct("BaseFragment");
72 let mut formatter = formatter.field("tag", &self.tag);
73 if !self.flags.is_empty() {
74 formatter = formatter.field("flags", &self.flags);
75 }
76 formatter
77 .field("rect", &self.rect())
78 .field("status", &self.status())
79 .finish()
80 }
81}
82
83impl BaseFragment {
84 pub(crate) fn new(
85 base_fragment_info: BaseFragmentInfo,
86 style: SharedStyle,
87 rect: PhysicalRect<Au>,
88 ) -> Self {
89 Self {
90 tag: base_fragment_info.tag,
91 flags: base_fragment_info.flags,
92 style,
93 rect: SyncPhysicalRectAu::new(rect),
94 status: AtomicU8::new(FragmentStatus::New as u8),
95 }
96 }
97
98 #[inline]
99 pub(crate) fn rect(&self) -> PhysicalRect<Au> {
100 self.rect.get()
101 }
102
103 #[inline]
104 pub(crate) fn set_rect(&self, new_rect: PhysicalRect<Au>) {
105 self.rect.set(new_rect);
106 }
107
108 #[inline]
109 pub(crate) fn translate_rect(&self, offset: PhysicalSize<Au>) {
110 self.rect.translate(offset)
111 }
112
113 #[inline]
114 pub(crate) fn set_rect_origin(&self, offset: PhysicalPoint<Au>) {
115 self.rect.set_origin(offset)
116 }
117
118 pub(crate) fn is_anonymous(&self) -> bool {
119 self.tag.is_none()
120 }
121
122 pub(crate) fn status(&self) -> FragmentStatus {
123 FragmentStatus::from_u8(self.status.load(Ordering::Relaxed))
124 .expect("Unknown FragmentStatus value")
125 }
126
127 pub(crate) fn set_status(&self, new_status: FragmentStatus) {
128 self.status.store(new_status as u8, Ordering::Relaxed)
129 }
130
131 pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
132 *self.style.borrow_mut() = style.clone();
133 self.set_status(FragmentStatus::StyleChanged);
134 }
135
136 pub(crate) fn style<'a>(&'a self) -> AtomicRef<'a, ServoArc<ComputedValues>> {
137 self.style.borrow()
138 }
139}
140
141#[derive(Clone, Copy, Debug, MallocSizeOf)]
143pub(crate) struct BaseFragmentInfo {
144 pub tag: Option<Tag>,
146
147 pub flags: FragmentFlags,
149}
150
151impl BaseFragmentInfo {
152 pub(crate) fn anonymous() -> Self {
153 Self {
154 tag: None,
155 flags: FragmentFlags::empty(),
156 }
157 }
158
159 pub(crate) fn new_for_testing(id: usize) -> Self {
160 Self {
161 tag: Some(Tag {
162 node: OpaqueNode(id),
163 pseudo_element_chain: Default::default(),
164 }),
165 flags: FragmentFlags::empty(),
166 }
167 }
168
169 pub(crate) fn is_anonymous(&self) -> bool {
170 self.tag.is_none()
171 }
172}
173
174impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
175 fn from(info: &NodeAndStyleInfo) -> Self {
176 info.node.into()
177 }
178}
179
180impl From<ServoLayoutNode<'_>> for BaseFragmentInfo {
181 fn from(node: ServoLayoutNode) -> Self {
182 let pseudo_element_chain = node.pseudo_element_chain();
183 let mut flags = FragmentFlags::empty();
184
185 if let Some(innermost_pseudo) = pseudo_element_chain.innermost() {
186 match innermost_pseudo {
187 PseudoElement::ServoAnonymousBox |
193 PseudoElement::ServoAnonymousTable |
194 PseudoElement::ServoAnonymousTableCell |
195 PseudoElement::ServoAnonymousTableRow => return Self::anonymous(),
196 PseudoElement::Before
199 if node
200 .as_html_element()
201 .is_some_and(|element| element.local_name() == &local_name!("br")) =>
202 {
203 flags.insert(FragmentFlags::IS_BR_ELEMENT);
204 },
205 _ => {},
206 }
207 return Self {
208 tag: Some(node.into()),
209 flags,
210 };
211 }
212
213 if node.as_element().is_some_and(|element| element.is_root()) {
214 flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
215 }
216
217 if let Some(element) = node.as_html_element() {
218 if element.is_body_element_of_html_element_root() {
219 flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
220 }
221 match element.local_name() {
222 &local_name!("br") => {
223 flags.insert(FragmentFlags::IS_BR_ELEMENT);
224 },
225 &local_name!("table") | &local_name!("th") | &local_name!("td") => {
226 flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
227 },
228 &local_name!("input") => {
229 flags.insert(FragmentFlags::IS_INPUT_ELEMENT);
230 if element
231 .attribute(&ns!(), &local_name!("type"))
232 .is_some_and(|attr| {
233 matches!(
234 attr.as_atom().to_ascii_lowercase(),
235 atom!("button") | atom!("color") | atom!("reset") | atom!("submit")
236 )
237 })
238 {
239 flags.insert(FragmentFlags::IS_BUTTON);
240 }
241 },
242 &local_name!("button") => {
243 flags.insert(FragmentFlags::IS_BUTTON);
244 },
245 _ => {},
246 }
247 };
248
249 Self {
250 tag: Some(node.into()),
251 flags,
252 }
253 }
254}
255
256bitflags! {
257 #[derive(Clone, Copy, Debug)]
259 pub(crate) struct FragmentFlags: u16 {
260 const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
262 const IS_BR_ELEMENT = 1 << 1;
265 const IS_WIDGET = 1 << 2;
270 const IS_FLEX_OR_GRID_ITEM = 1 << 3;
272 const IS_REPLACED = 1 << 4;
275 const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 5;
279 const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 6;
282 const DO_NOT_PAINT = 1 << 7;
286 const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8;
290 const IS_ROOT_ELEMENT = 1 << 9;
292 const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10;
294 const IS_COLLAPSED = 1 << 11;
297 const IS_INPUT_ELEMENT = 1 << 12;
299 const IS_BUTTON = 1 << 13;
301 }
302}
303
304malloc_size_of_is_0!(FragmentFlags);
305
306#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
309pub(crate) struct Tag {
310 pub(crate) node: OpaqueNode,
311 pub(crate) pseudo_element_chain: PseudoElementChain,
312}
313
314impl std::fmt::Debug for Tag {
315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316 f.write_fmt(format_args!("Tag({:?}", self.node))?;
317 if let Some(pseudo) = self.pseudo_element_chain.primary {
318 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
319 }
320 if let Some(pseudo) = self.pseudo_element_chain.secondary {
321 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
322 }
323 f.write_str(")")
324 }
325}
326
327impl Tag {
328 pub(crate) fn to_display_list_fragment_id(self) -> u64 {
329 combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
330 }
331}
332
333impl From<ServoLayoutNode<'_>> for Tag {
334 fn from(node: ServoLayoutNode<'_>) -> Self {
335 Self {
336 node: node.opaque(),
337 pseudo_element_chain: node.pseudo_element_chain(),
338 }
339 }
340}