1use std::sync::atomic::{AtomicI32, AtomicU8, Ordering};
6
7use app_units::Au;
8use atomic_refcell::AtomicRef;
9use bitflags::bitflags;
10use euclid::{Point2D, Rect, Size2D};
11use layout_api::{LayoutElement, LayoutNode, PseudoElementChain, combine_id_with_fragment_type};
12use malloc_size_of::malloc_size_of_is_0;
13use malloc_size_of_derive::MallocSizeOf;
14use num_derive::FromPrimitive;
15use num_traits::FromPrimitive;
16use script::layout_dom::ServoLayoutNode;
17use servo_arc::Arc as ServoArc;
18use style::dom::OpaqueNode;
19use style::properties::ComputedValues;
20use style::selector_parser::PseudoElement;
21use style_traits::CSSPixel;
22use web_atoms::local_name;
23
24use crate::SharedStyle;
25use crate::dom_traversal::NodeAndStyleInfo;
26use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize};
27
28#[derive(Clone, Debug, Default, FromPrimitive, MallocSizeOf, PartialEq)]
29#[repr(u8)]
30pub(crate) enum FragmentStatus {
31 #[default]
33 New,
34 StyleChanged,
36 OnlyDescendantsChanged,
39 Clean,
41}
42
43#[derive(MallocSizeOf)]
47pub(crate) struct BaseFragment {
48 pub tag: Option<Tag>,
52
53 pub flags: FragmentFlags,
56
57 pub style: SharedStyle,
60
61 rect: Rect<AtomicI32, CSSPixel>,
65
66 pub status: AtomicU8,
68}
69
70impl std::fmt::Debug for BaseFragment {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 let mut formatter = f.debug_struct("BaseFragment");
73 let mut formatter = formatter.field("tag", &self.tag);
74 if !self.flags.is_empty() {
75 formatter = formatter.field("flags", &self.flags);
76 }
77 formatter
78 .field("rect", &self.rect())
79 .field("status", &self.status())
80 .finish()
81 }
82}
83
84impl BaseFragment {
85 pub(crate) fn new(
86 base_fragment_info: BaseFragmentInfo,
87 style: SharedStyle,
88 rect: PhysicalRect<Au>,
89 ) -> Self {
90 Self {
91 tag: base_fragment_info.tag,
92 flags: base_fragment_info.flags,
93 style,
94 rect: Rect::new(
95 Point2D::new(rect.origin.x.0.into(), rect.origin.y.0.into()),
96 Size2D::new(rect.size.width.0.into(), rect.size.height.0.into()),
97 ),
98 status: AtomicU8::new(FragmentStatus::New as u8),
99 }
100 }
101
102 #[inline]
103 pub(crate) fn rect(&self) -> PhysicalRect<Au> {
104 PhysicalRect::new(
105 Point2D::new(
106 Au::new(self.rect.origin.x.load(Ordering::Relaxed)),
107 Au::new(self.rect.origin.y.load(Ordering::Relaxed)),
108 ),
109 Size2D::new(
110 Au::new(self.rect.size.width.load(Ordering::Relaxed)),
111 Au::new(self.rect.size.height.load(Ordering::Relaxed)),
112 ),
113 )
114 }
115
116 #[inline]
117 pub(crate) fn set_rect(&self, new_rect: PhysicalRect<Au>) {
118 let origin = &self.rect.origin;
119 origin.x.store(new_rect.origin.x.0, Ordering::Relaxed);
120 origin.y.store(new_rect.origin.y.0, Ordering::Relaxed);
121
122 let size = &self.rect.size;
123 size.width.store(new_rect.size.width.0, Ordering::Relaxed);
124 size.height.store(new_rect.size.height.0, Ordering::Relaxed);
125 }
126
127 #[inline]
128 pub(crate) fn translate_rect(&self, offset: PhysicalSize<Au>) {
129 let origin = &self.rect.origin;
132 let new_x = Au::new(origin.x.load(Ordering::Relaxed)) + offset.width;
133 origin.x.store(new_x.0, Ordering::Relaxed);
134 let new_y = Au::new(origin.y.load(Ordering::Relaxed)) + offset.height;
135 origin.y.store(new_y.0, Ordering::Relaxed);
136 }
137
138 #[inline]
139 pub(crate) fn set_rect_origin(&self, offset: PhysicalPoint<Au>) {
140 let origin = &self.rect.origin;
141 origin.x.store(offset.x.0, Ordering::Relaxed);
142 origin.y.store(offset.y.0, Ordering::Relaxed);
143 }
144
145 pub(crate) fn is_anonymous(&self) -> bool {
146 self.tag.is_none()
147 }
148
149 pub(crate) fn status(&self) -> FragmentStatus {
150 FragmentStatus::from_u8(self.status.load(Ordering::Relaxed))
151 .expect("Unknown FragmentStatus value")
152 }
153
154 pub(crate) fn set_status(&self, new_status: FragmentStatus) {
155 self.status.store(new_status as u8, Ordering::Relaxed)
156 }
157
158 pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
159 *self.style.borrow_mut() = style.clone();
160 self.set_status(FragmentStatus::StyleChanged);
161 }
162
163 pub(crate) fn style<'a>(&'a self) -> AtomicRef<'a, ServoArc<ComputedValues>> {
164 self.style.borrow()
165 }
166}
167
168#[derive(Clone, Copy, Debug, MallocSizeOf)]
170pub(crate) struct BaseFragmentInfo {
171 pub tag: Option<Tag>,
173
174 pub flags: FragmentFlags,
176}
177
178impl BaseFragmentInfo {
179 pub(crate) fn anonymous() -> Self {
180 Self {
181 tag: None,
182 flags: FragmentFlags::empty(),
183 }
184 }
185
186 pub(crate) fn new_for_testing(id: usize) -> Self {
187 Self {
188 tag: Some(Tag {
189 node: OpaqueNode(id),
190 pseudo_element_chain: Default::default(),
191 }),
192 flags: FragmentFlags::empty(),
193 }
194 }
195
196 pub(crate) fn is_anonymous(&self) -> bool {
197 self.tag.is_none()
198 }
199}
200
201impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
202 fn from(info: &NodeAndStyleInfo) -> Self {
203 info.node.into()
204 }
205}
206
207impl From<ServoLayoutNode<'_>> for BaseFragmentInfo {
208 fn from(node: ServoLayoutNode) -> Self {
209 let pseudo_element_chain = node.pseudo_element_chain();
210 let mut flags = FragmentFlags::empty();
211
212 if matches!(
218 pseudo_element_chain.innermost(),
219 Some(PseudoElement::ServoAnonymousBox) |
220 Some(PseudoElement::ServoAnonymousTable) |
221 Some(PseudoElement::ServoAnonymousTableCell) |
222 Some(PseudoElement::ServoAnonymousTableRow)
223 ) {
224 return Self::anonymous();
225 }
226
227 if let Some(element) = node.as_html_element() {
228 if element.is_body_element_of_html_element_root() {
229 flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
230 }
231
232 match element.local_name() {
233 &local_name!("br") => {
234 flags.insert(FragmentFlags::IS_BR_ELEMENT);
235 },
236 &local_name!("table") | &local_name!("th") | &local_name!("td") => {
237 flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
238 },
239 &local_name!("input") => {
240 flags.insert(FragmentFlags::IS_INPUT_ELEMENT);
241 },
242 _ => {},
243 }
244
245 if element.is_root() {
246 flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
247 }
248 };
249
250 Self {
251 tag: Some(node.into()),
252 flags,
253 }
254 }
255}
256
257bitflags! {
258 #[derive(Clone, Copy, Debug)]
260 pub(crate) struct FragmentFlags: u16 {
261 const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
263 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
300 }
301}
302
303malloc_size_of_is_0!(FragmentFlags);
304
305#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
308pub(crate) struct Tag {
309 pub(crate) node: OpaqueNode,
310 pub(crate) pseudo_element_chain: PseudoElementChain,
311}
312
313impl std::fmt::Debug for Tag {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 f.write_fmt(format_args!("Tag({:?}", self.node))?;
316 if let Some(pseudo) = self.pseudo_element_chain.primary {
317 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
318 }
319 if let Some(pseudo) = self.pseudo_element_chain.secondary {
320 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
321 }
322 f.write_str(")")
323 }
324}
325
326impl Tag {
327 pub(crate) fn to_display_list_fragment_id(self) -> u64 {
328 combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
329 }
330}
331
332impl From<ServoLayoutNode<'_>> for Tag {
333 fn from(node: ServoLayoutNode<'_>) -> Self {
334 Self {
335 node: node.opaque(),
336 pseudo_element_chain: node.pseudo_element_chain(),
337 }
338 }
339}