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 web_atoms::local_name;
21
22use crate::SharedStyle;
23use crate::dom_traversal::NodeAndStyleInfo;
24use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize, SyncPhysicalRectAu};
25
26#[derive(Clone, Debug, Default, FromPrimitive, MallocSizeOf, PartialEq)]
27#[repr(u8)]
28pub(crate) enum FragmentStatus {
29 #[default]
31 New,
32 StyleChanged,
34 OnlyDescendantsChanged,
37 Clean,
39}
40
41#[derive(MallocSizeOf)]
45pub(crate) struct BaseFragment {
46 pub tag: Option<Tag>,
50
51 pub flags: FragmentFlags,
54
55 pub style: SharedStyle,
58
59 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(
84 base_fragment_info: BaseFragmentInfo,
85 style: SharedStyle,
86 rect: PhysicalRect<Au>,
87 ) -> Self {
88 Self {
89 tag: base_fragment_info.tag,
90 flags: base_fragment_info.flags,
91 style,
92 rect: SyncPhysicalRectAu::new(rect),
93 status: AtomicU8::new(FragmentStatus::New as u8),
94 }
95 }
96
97 #[inline]
98 pub(crate) fn rect(&self) -> PhysicalRect<Au> {
99 self.rect.get()
100 }
101
102 #[inline]
103 pub(crate) fn set_rect(&self, new_rect: PhysicalRect<Au>) {
104 self.rect.set(new_rect);
105 }
106
107 #[inline]
108 pub(crate) fn translate_rect(&self, offset: PhysicalSize<Au>) {
109 self.rect.translate(offset)
110 }
111
112 #[inline]
113 pub(crate) fn set_rect_origin(&self, offset: PhysicalPoint<Au>) {
114 self.rect.set_origin(offset)
115 }
116
117 pub(crate) fn is_anonymous(&self) -> bool {
118 self.tag.is_none()
119 }
120
121 pub(crate) fn status(&self) -> FragmentStatus {
122 FragmentStatus::from_u8(self.status.load(Ordering::Relaxed))
123 .expect("Unknown FragmentStatus value")
124 }
125
126 pub(crate) fn set_status(&self, new_status: FragmentStatus) {
127 self.status.store(new_status as u8, Ordering::Relaxed)
128 }
129
130 pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
131 *self.style.borrow_mut() = style.clone();
132 self.set_status(FragmentStatus::StyleChanged);
133 }
134
135 pub(crate) fn style<'a>(&'a self) -> AtomicRef<'a, ServoArc<ComputedValues>> {
136 self.style.borrow()
137 }
138}
139
140#[derive(Clone, Copy, Debug, MallocSizeOf)]
142pub(crate) struct BaseFragmentInfo {
143 pub tag: Option<Tag>,
145
146 pub flags: FragmentFlags,
148}
149
150impl BaseFragmentInfo {
151 pub(crate) fn anonymous() -> Self {
152 Self {
153 tag: None,
154 flags: FragmentFlags::empty(),
155 }
156 }
157
158 pub(crate) fn new_for_testing(id: usize) -> Self {
159 Self {
160 tag: Some(Tag {
161 node: OpaqueNode(id),
162 pseudo_element_chain: Default::default(),
163 }),
164 flags: FragmentFlags::empty(),
165 }
166 }
167
168 pub(crate) fn is_anonymous(&self) -> bool {
169 self.tag.is_none()
170 }
171}
172
173impl From<&NodeAndStyleInfo<'_>> for BaseFragmentInfo {
174 fn from(info: &NodeAndStyleInfo) -> Self {
175 info.node.into()
176 }
177}
178
179impl From<ServoLayoutNode<'_>> for BaseFragmentInfo {
180 fn from(node: ServoLayoutNode) -> Self {
181 let pseudo_element_chain = node.pseudo_element_chain();
182 let mut flags = FragmentFlags::empty();
183
184 if matches!(
190 pseudo_element_chain.innermost(),
191 Some(PseudoElement::ServoAnonymousBox) |
192 Some(PseudoElement::ServoAnonymousTable) |
193 Some(PseudoElement::ServoAnonymousTableCell) |
194 Some(PseudoElement::ServoAnonymousTableRow)
195 ) {
196 return Self::anonymous();
197 }
198
199 if node.as_element().is_some_and(|element| element.is_root()) {
200 flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
201 }
202
203 if let Some(element) = node.as_html_element() {
204 if element.is_body_element_of_html_element_root() {
205 flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
206 }
207 match element.local_name() {
208 &local_name!("br") => {
209 flags.insert(FragmentFlags::IS_BR_ELEMENT);
210 },
211 &local_name!("table") | &local_name!("th") | &local_name!("td") => {
212 flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
213 },
214 &local_name!("input") => {
215 flags.insert(FragmentFlags::IS_INPUT_ELEMENT);
216 },
217 _ => {},
218 }
219 };
220
221 Self {
222 tag: Some(node.into()),
223 flags,
224 }
225 }
226}
227
228bitflags! {
229 #[derive(Clone, Copy, Debug)]
231 pub(crate) struct FragmentFlags: u16 {
232 const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
234 const IS_BR_ELEMENT = 1 << 1;
236 const IS_WIDGET = 1 << 2;
241 const IS_FLEX_OR_GRID_ITEM = 1 << 3;
243 const IS_REPLACED = 1 << 4;
246 const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 5;
250 const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 6;
253 const DO_NOT_PAINT = 1 << 7;
257 const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8;
261 const IS_ROOT_ELEMENT = 1 << 9;
263 const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10;
265 const IS_COLLAPSED = 1 << 11;
268 const IS_INPUT_ELEMENT = 1 << 12;
270
271 }
272}
273
274malloc_size_of_is_0!(FragmentFlags);
275
276#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
279pub(crate) struct Tag {
280 pub(crate) node: OpaqueNode,
281 pub(crate) pseudo_element_chain: PseudoElementChain,
282}
283
284impl std::fmt::Debug for Tag {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 f.write_fmt(format_args!("Tag({:?}", self.node))?;
287 if let Some(pseudo) = self.pseudo_element_chain.primary {
288 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
289 }
290 if let Some(pseudo) = self.pseudo_element_chain.secondary {
291 f.write_fmt(format_args!(", PseudoElement::{pseudo:?}"))?;
292 }
293 f.write_str(")")
294 }
295}
296
297impl Tag {
298 pub(crate) fn to_display_list_fragment_id(self) -> u64 {
299 combine_id_with_fragment_type(self.node.id(), self.pseudo_element_chain.primary.into())
300 }
301}
302
303impl From<ServoLayoutNode<'_>> for Tag {
304 fn from(node: ServoLayoutNode<'_>) -> Self {
305 Self {
306 node: node.opaque(),
307 pseudo_element_chain: node.pseudo_element_chain(),
308 }
309 }
310}