1use std::marker::PhantomData;
6
7use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
8use base::id::{BrowsingContextId, PipelineId};
9use html5ever::{local_name, ns};
10use layout_api::wrapper_traits::{LayoutDataTrait, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
11use layout_api::{
12 GenericLayoutDataTrait, LayoutDamage, LayoutElementType,
13 LayoutNodeType as ScriptLayoutNodeType, SVGElementData,
14};
15use malloc_size_of_derive::MallocSizeOf;
16use net_traits::image_cache::Image;
17use script::layout_dom::ServoThreadSafeLayoutNode;
18use servo_arc::Arc as ServoArc;
19use smallvec::SmallVec;
20use style::context::SharedStyleContext;
21use style::properties::ComputedValues;
22use style::selector_parser::{PseudoElement, RestyleDamage};
23
24use crate::cell::ArcRefCell;
25use crate::flexbox::FlexLevelBox;
26use crate::flow::BlockLevelBox;
27use crate::flow::inline::{InlineItem, SharedInlineStyles};
28use crate::fragment_tree::Fragment;
29use crate::geom::PhysicalSize;
30use crate::layout_box_base::LayoutBoxBase;
31use crate::replaced::CanvasInfo;
32use crate::table::TableLevelBox;
33use crate::taffy::TaffyItemBox;
34
35#[derive(MallocSizeOf)]
36pub struct PseudoLayoutData {
37 pseudo: PseudoElement,
38 data: ArcRefCell<InnerDOMLayoutData>,
39}
40
41#[derive(Default, MallocSizeOf)]
43pub struct InnerDOMLayoutData {
44 pub(super) self_box: ArcRefCell<Option<LayoutBox>>,
45 pub(super) pseudo_boxes: SmallVec<[PseudoLayoutData; 2]>,
46}
47
48impl InnerDOMLayoutData {
49 fn pseudo_layout_data(
50 &self,
51 pseudo_element: PseudoElement,
52 ) -> Option<ArcRefCell<InnerDOMLayoutData>> {
53 for pseudo_layout_data in self.pseudo_boxes.iter() {
54 if pseudo_element == pseudo_layout_data.pseudo {
55 return Some(pseudo_layout_data.data.clone());
56 }
57 }
58 None
59 }
60
61 fn create_pseudo_layout_data(
62 &mut self,
63 pseudo_element: PseudoElement,
64 ) -> ArcRefCell<InnerDOMLayoutData> {
65 let data: ArcRefCell<InnerDOMLayoutData> = Default::default();
66 self.pseudo_boxes.push(PseudoLayoutData {
67 pseudo: pseudo_element,
68 data: data.clone(),
69 });
70 data
71 }
72
73 fn fragments(&self) -> Vec<Fragment> {
74 self.self_box
75 .borrow()
76 .as_ref()
77 .map(|layout_box| layout_box.with_base_flat(LayoutBoxBase::fragments))
78 .unwrap_or_default()
79 }
80
81 fn repair_style(&self, node: &ServoThreadSafeLayoutNode, context: &SharedStyleContext) {
82 if let Some(layout_object) = &*self.self_box.borrow() {
83 layout_object.repair_style(context, node, &node.style(context));
84 }
85
86 for pseudo_layout_data in self.pseudo_boxes.iter() {
87 let Some(node_with_pseudo) = node.with_pseudo(pseudo_layout_data.pseudo) else {
88 continue;
89 };
90 pseudo_layout_data
91 .data
92 .borrow()
93 .repair_style(&node_with_pseudo, context);
94 }
95 }
96
97 fn with_each_layout_box_base(&self, callback: impl Fn(&LayoutBoxBase)) {
98 if let Some(data) = self.self_box.borrow().as_ref() {
99 data.with_each_base(callback);
100 }
101 }
102
103 fn with_each_layout_box_base_including_pseudos(&self, callback: impl Fn(&LayoutBoxBase)) {
104 self.with_each_layout_box_base(&callback);
105 for pseudo_layout_data in self.pseudo_boxes.iter() {
106 pseudo_layout_data
107 .data
108 .borrow()
109 .with_each_layout_box_base(&callback);
110 }
111 }
112}
113
114#[derive(MallocSizeOf)]
116pub(super) enum LayoutBox {
117 DisplayContents(SharedInlineStyles),
118 BlockLevel(ArcRefCell<BlockLevelBox>),
119 InlineLevel(Vec<ArcRefCell<InlineItem>>),
120 FlexLevel(ArcRefCell<FlexLevelBox>),
121 TableLevelBox(TableLevelBox),
122 TaffyItemBox(ArcRefCell<TaffyItemBox>),
123}
124
125impl LayoutBox {
126 pub(crate) fn with_each_base(&self, callback: impl Fn(&LayoutBoxBase)) {
127 self.with_base_fold((), |_, base| callback(base))
128 }
129
130 pub(crate) fn with_first_base<T>(
131 &self,
132 callback: impl FnOnce(&LayoutBoxBase) -> T,
133 ) -> Option<T> {
134 Some(match self {
135 LayoutBox::DisplayContents(..) => return None,
136 LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback),
137 LayoutBox::InlineLevel(inline_items) => {
138 inline_items.first()?.borrow().with_base(callback)
139 },
140 LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback),
141 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback),
142 LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback),
143 })
144 }
145
146 pub(crate) fn with_base_flat<T>(&self, callback: impl Fn(&LayoutBoxBase) -> Vec<T>) -> Vec<T> {
147 match self {
148 LayoutBox::DisplayContents(..) => vec![],
149 LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback),
150 LayoutBox::InlineLevel(inline_items) => inline_items
151 .iter()
152 .flat_map(|inline_item| inline_item.borrow().with_base(&callback))
153 .collect(),
154 LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback),
155 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback),
156 LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback),
157 }
158 }
159
160 pub(crate) fn with_base_fold<T>(
161 &self,
162 init: T,
163 callback: impl Fn(T, &LayoutBoxBase) -> T,
164 ) -> T {
165 match self {
166 LayoutBox::DisplayContents(..) => init,
167 LayoutBox::BlockLevel(block_level_box) => block_level_box
168 .borrow()
169 .with_base(|base| callback(init, base)),
170 LayoutBox::InlineLevel(inline_items) => inline_items.iter().fold(init, |acc, item| {
171 item.borrow().with_base(|base| callback(acc, base))
172 }),
173 LayoutBox::FlexLevel(flex_level_box) => flex_level_box
174 .borrow()
175 .with_base(|base| callback(init, base)),
176 LayoutBox::TableLevelBox(table_level_box) => {
177 table_level_box.with_base(|base| callback(init, base))
178 },
179 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box
180 .borrow()
181 .with_base(|base| callback(init, base)),
182 }
183 }
184
185 pub(crate) fn with_base_mut_fold<T>(
186 &mut self,
187 init: T,
188 callback: impl Fn(T, &mut LayoutBoxBase) -> T,
189 ) -> T {
190 match self {
191 LayoutBox::DisplayContents(..) => init,
192 LayoutBox::BlockLevel(block_level_box) => block_level_box
193 .borrow_mut()
194 .with_base_mut(|base| callback(init, base)),
195 LayoutBox::InlineLevel(inline_items) => inline_items.iter().fold(init, |acc, item| {
196 item.borrow_mut().with_base_mut(|base| callback(acc, base))
197 }),
198 LayoutBox::FlexLevel(flex_level_box) => flex_level_box
199 .borrow_mut()
200 .with_base_mut(|base| callback(init, base)),
201 LayoutBox::TableLevelBox(table_level_box) => {
202 table_level_box.with_base_mut(|base| callback(init, base))
203 },
204 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box
205 .borrow_mut()
206 .with_base_mut(|base| callback(init, base)),
207 }
208 }
209
210 fn repair_style(
211 &self,
212 context: &SharedStyleContext,
213 node: &ServoThreadSafeLayoutNode,
214 new_style: &ServoArc<ComputedValues>,
215 ) {
216 match self {
217 LayoutBox::DisplayContents(inline_shared_styles) => {
218 *inline_shared_styles.style.borrow_mut() = new_style.clone();
219 *inline_shared_styles.selected.borrow_mut() = node.selected_style();
220 },
221 LayoutBox::BlockLevel(block_level_box) => {
222 block_level_box
223 .borrow_mut()
224 .repair_style(context, node, new_style);
225 },
226 LayoutBox::InlineLevel(inline_items) => {
227 for inline_item in inline_items {
228 inline_item
229 .borrow_mut()
230 .repair_style(context, node, new_style);
231 }
232 },
233 LayoutBox::FlexLevel(flex_level_box) => flex_level_box
234 .borrow_mut()
235 .repair_style(context, node, new_style),
236 LayoutBox::TableLevelBox(table_level_box) => {
237 table_level_box.repair_style(context, node, new_style)
238 },
239 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box
240 .borrow_mut()
241 .repair_style(context, node, new_style),
242 }
243 }
244
245 pub(crate) fn unsplit_inline_level_layout_box(self) -> Option<ArcRefCell<InlineItem>> {
248 let LayoutBox::InlineLevel(inline_level_boxes) = self else {
249 return None;
250 };
251 if inline_level_boxes.len() != 1 {
254 return None;
255 }
256 inline_level_boxes.into_iter().next()
257 }
258}
259
260#[derive(Default, MallocSizeOf)]
264pub struct DOMLayoutData(AtomicRefCell<InnerDOMLayoutData>);
265
266impl LayoutDataTrait for DOMLayoutData {}
268impl GenericLayoutDataTrait for DOMLayoutData {
269 fn as_any(&self) -> &dyn std::any::Any {
270 self
271 }
272}
273
274pub struct BoxSlot<'dom> {
275 pub(crate) slot: Option<ArcRefCell<Option<LayoutBox>>>,
276 pub(crate) marker: PhantomData<&'dom ()>,
277}
278
279impl From<ArcRefCell<Option<LayoutBox>>> for BoxSlot<'_> {
280 fn from(layout_box_slot: ArcRefCell<Option<LayoutBox>>) -> Self {
281 let slot = Some(layout_box_slot);
282 Self {
283 slot,
284 marker: PhantomData,
285 }
286 }
287}
288
289impl BoxSlot<'_> {
291 pub(crate) fn set(mut self, box_: LayoutBox) {
292 if let Some(slot) = &mut self.slot {
293 *slot.borrow_mut() = Some(box_);
294 }
295 }
296
297 pub(crate) fn take_layout_box_if_undamaged(&self, damage: LayoutDamage) -> Option<LayoutBox> {
298 if damage.has_box_damage() {
299 return None;
300 }
301 self.slot.as_ref().and_then(|slot| slot.borrow_mut().take())
302 }
303}
304
305impl Drop for BoxSlot<'_> {
306 fn drop(&mut self) {
307 if !std::thread::panicking() {
308 if let Some(slot) = &mut self.slot {
309 assert!(slot.borrow().is_some(), "failed to set a layout box");
310 }
311 }
312 }
313}
314
315pub(crate) trait NodeExt<'dom> {
316 fn as_image(&self) -> Option<(Option<Image>, PhysicalSize<f64>)>;
319 fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
320 fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>;
321 fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
322 fn as_svg(&self) -> Option<SVGElementData>;
323 fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
324
325 fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
326 fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
327 fn box_slot(&self) -> BoxSlot<'dom>;
328
329 fn unset_all_boxes(&self);
331
332 fn unset_all_pseudo_boxes(&self);
334
335 fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>;
336 fn with_each_layout_box_base_including_pseudos(&self, callback: impl Fn(&LayoutBoxBase));
337
338 fn repair_style(&self, context: &SharedStyleContext);
339 fn take_restyle_damage(&self) -> LayoutDamage;
340}
341
342impl<'dom> NodeExt<'dom> for ServoThreadSafeLayoutNode<'dom> {
343 fn as_image(&self) -> Option<(Option<Image>, PhysicalSize<f64>)> {
344 let (resource, metadata) = self.image_data()?;
345 let width = metadata.map(|metadata| metadata.width).unwrap_or_default();
346 let height = metadata.map(|metadata| metadata.height).unwrap_or_default();
347 let (mut width, mut height) = (width as f64, height as f64);
348 if let Some(density) = self.image_density().filter(|density| *density != 1.) {
349 width /= density;
350 height /= density;
351 }
352 Some((resource, PhysicalSize::new(width, height)))
353 }
354
355 fn as_svg(&self) -> Option<SVGElementData> {
356 self.svg_data()
357 }
358
359 fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
360 let data = self.media_data()?;
361 let natural_size = if let Some(frame) = data.current_frame {
362 Some(PhysicalSize::new(frame.width.into(), frame.height.into()))
363 } else {
364 data.metadata
365 .map(|meta| PhysicalSize::new(meta.width.into(), meta.height.into()))
366 };
367 Some((
368 data.current_frame.map(|frame| frame.image_key),
369 natural_size,
370 ))
371 }
372
373 fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
374 let canvas_data = self.canvas_data()?;
375 let source = canvas_data.image_key;
376 Some((
377 CanvasInfo { source },
378 PhysicalSize::new(canvas_data.width.into(), canvas_data.height.into()),
379 ))
380 }
381
382 fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)> {
383 match (self.iframe_pipeline_id(), self.iframe_browsing_context_id()) {
384 (Some(pipeline_id), Some(browsing_context_id)) => {
385 Some((pipeline_id, browsing_context_id))
386 },
387 _ => None,
388 }
389 }
390
391 fn as_typeless_object_with_data_attribute(&self) -> Option<String> {
392 if self.type_id() !=
393 Some(ScriptLayoutNodeType::Element(
394 LayoutElementType::HTMLObjectElement,
395 ))
396 {
397 return None;
398 }
399
400 let element = self.as_element()?;
404 if element.get_attr(&ns!(), &local_name!("type")).is_some() {
405 return None;
406 }
407 element
408 .get_attr(&ns!(), &local_name!("data"))
409 .map(|string| string.to_owned())
410 }
411
412 fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
413 if self.layout_data().is_none() {
414 self.initialize_layout_data::<DOMLayoutData>();
415 }
416 self.layout_data()
417 .unwrap()
418 .as_any()
419 .downcast_ref::<DOMLayoutData>()
420 .unwrap()
421 .0
422 .borrow_mut()
423 }
424
425 fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
426 self.layout_data().map(|data| {
427 data.as_any()
428 .downcast_ref::<DOMLayoutData>()
429 .unwrap()
430 .0
431 .borrow()
432 })
433 }
434
435 fn box_slot(&self) -> BoxSlot<'dom> {
436 let pseudo_element_chain = self.pseudo_element_chain();
437 let Some(primary) = pseudo_element_chain.primary else {
438 return self.ensure_inner_layout_data().self_box.clone().into();
439 };
440
441 let Some(secondary) = pseudo_element_chain.secondary else {
442 let primary_layout_data = self
443 .ensure_inner_layout_data()
444 .create_pseudo_layout_data(primary);
445 return primary_layout_data.borrow().self_box.clone().into();
446 };
447
448 let primary_layout_data = self
455 .inner_layout_data()
456 .expect("Should already have element InnerLayoutData here.")
457 .pseudo_layout_data(primary)
458 .expect("Should already have primary pseudo-element InnerLayoutData here");
459 let secondary_layout_data = primary_layout_data
460 .borrow_mut()
461 .create_pseudo_layout_data(secondary);
462 secondary_layout_data.borrow().self_box.clone().into()
463 }
464
465 fn unset_all_boxes(&self) {
466 let mut layout_data = self.ensure_inner_layout_data();
467 *layout_data.self_box.borrow_mut() = None;
468 layout_data.pseudo_boxes.clear();
469
470 }
473
474 fn unset_all_pseudo_boxes(&self) {
475 self.ensure_inner_layout_data().pseudo_boxes.clear();
476 }
477
478 fn with_each_layout_box_base_including_pseudos(&self, callback: impl Fn(&LayoutBoxBase)) {
479 if let Some(inner_layout_data) = self.inner_layout_data() {
480 inner_layout_data.with_each_layout_box_base_including_pseudos(callback);
481 }
482 }
483
484 fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> {
485 let Some(layout_data) = self.inner_layout_data() else {
486 return vec![];
487 };
488 match pseudo_element {
489 Some(pseudo_element) => layout_data
490 .pseudo_layout_data(pseudo_element)
491 .map(|pseudo_layout_data| pseudo_layout_data.borrow().fragments())
492 .unwrap_or_default(),
493 None => layout_data.fragments(),
494 }
495 }
496
497 fn repair_style(&self, context: &SharedStyleContext) {
498 if let Some(layout_data) = self.inner_layout_data() {
499 layout_data.repair_style(self, context);
500 }
501 }
502
503 fn take_restyle_damage(&self) -> LayoutDamage {
504 let damage = self
505 .style_data()
506 .map(|style_data| std::mem::take(&mut style_data.element_data.borrow_mut().damage))
507 .unwrap_or_else(RestyleDamage::reconstruct);
508 LayoutDamage::from_bits_retain(damage.bits())
509 }
510}