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