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, WeakRefCell};
25use crate::flexbox::FlexLevelBox;
26use crate::flow::BlockLevelBox;
27use crate::flow::inline::{InlineItem, SharedInlineStyles, WeakInlineItem};
28use crate::fragment_tree::Fragment;
29use crate::geom::PhysicalSize;
30use crate::layout_box_base::LayoutBoxBase;
31use crate::replaced::CanvasInfo;
32use crate::table::{TableLevelBox, WeakTableLevelBox};
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 .and_then(|layout_box| layout_box.with_base(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_layout_box_base(&self, callback: impl Fn(&LayoutBoxBase)) {
98 if let Some(data) = self.self_box.borrow().as_ref() {
99 data.with_base(callback);
100 }
101 }
102
103 fn with_layout_box_base_including_pseudos(&self, callback: impl Fn(&LayoutBoxBase)) {
104 self.with_layout_box_base(&callback);
105 for pseudo_layout_data in self.pseudo_boxes.iter() {
106 pseudo_layout_data
107 .data
108 .borrow()
109 .with_layout_box_base(&callback);
110 }
111 }
112}
113
114#[derive(Debug, MallocSizeOf)]
116pub(super) enum LayoutBox {
117 DisplayContents(SharedInlineStyles),
118 BlockLevel(ArcRefCell<BlockLevelBox>),
119 InlineLevel(InlineItem),
120 FlexLevel(ArcRefCell<FlexLevelBox>),
121 TableLevelBox(TableLevelBox),
122 TaffyItemBox(ArcRefCell<TaffyItemBox>),
123}
124
125impl LayoutBox {
126 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> Option<T> {
127 Some(match self {
128 LayoutBox::DisplayContents(..) => return None,
129 LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback),
130 LayoutBox::InlineLevel(inline_item) => inline_item.with_base(callback),
131 LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback),
132 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback),
133 LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback),
134 })
135 }
136
137 pub(crate) fn with_base_mut<T>(
138 &mut self,
139 callback: impl FnOnce(&mut LayoutBoxBase) -> T,
140 ) -> Option<T> {
141 Some(match self {
142 LayoutBox::DisplayContents(..) => return None,
143 LayoutBox::BlockLevel(block_level_box) => {
144 block_level_box.borrow_mut().with_base_mut(callback)
145 },
146 LayoutBox::InlineLevel(inline_item) => inline_item.with_base_mut(callback),
147 LayoutBox::FlexLevel(flex_level_box) => {
148 flex_level_box.borrow_mut().with_base_mut(callback)
149 },
150 LayoutBox::TaffyItemBox(taffy_item_box) => {
151 taffy_item_box.borrow_mut().with_base_mut(callback)
152 },
153 LayoutBox::TableLevelBox(table_box) => table_box.with_base_mut(callback),
154 })
155 }
156
157 fn repair_style(
158 &self,
159 context: &SharedStyleContext,
160 node: &ServoThreadSafeLayoutNode,
161 new_style: &ServoArc<ComputedValues>,
162 ) {
163 match self {
164 LayoutBox::DisplayContents(inline_shared_styles) => {
165 *inline_shared_styles.style.borrow_mut() = new_style.clone();
166 *inline_shared_styles.selected.borrow_mut() = node.selected_style();
167 },
168 LayoutBox::BlockLevel(block_level_box) => {
169 block_level_box
170 .borrow_mut()
171 .repair_style(context, node, new_style);
172 },
173 LayoutBox::InlineLevel(inline_item) => {
174 inline_item.repair_style(context, node, new_style);
175 },
176 LayoutBox::FlexLevel(flex_level_box) => flex_level_box
177 .borrow_mut()
178 .repair_style(context, node, new_style),
179 LayoutBox::TableLevelBox(table_level_box) => {
180 table_level_box.repair_style(context, node, new_style)
181 },
182 LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box
183 .borrow_mut()
184 .repair_style(context, node, new_style),
185 }
186 }
187
188 fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
189 match self {
190 Self::DisplayContents(_) => {
191 },
194 Self::BlockLevel(block_level_box) => {
195 block_level_box.borrow().attached_to_tree(layout_box)
196 },
197 Self::InlineLevel(inline_item) => inline_item.attached_to_tree(layout_box),
198 Self::FlexLevel(flex_level_box) => flex_level_box.borrow().attached_to_tree(layout_box),
199 Self::TableLevelBox(table_level_box) => table_level_box.attached_to_tree(layout_box),
200 Self::TaffyItemBox(taffy_item_box) => {
201 taffy_item_box.borrow().attached_to_tree(layout_box)
202 },
203 }
204 }
205
206 fn downgrade(&self) -> WeakLayoutBox {
207 match self {
208 Self::DisplayContents(inline_shared_styles) => {
209 WeakLayoutBox::DisplayContents(inline_shared_styles.clone())
210 },
211 Self::BlockLevel(block_level_box) => {
212 WeakLayoutBox::BlockLevel(block_level_box.downgrade())
213 },
214 Self::InlineLevel(inline_item) => WeakLayoutBox::InlineLevel(inline_item.downgrade()),
215 Self::FlexLevel(flex_level_box) => WeakLayoutBox::FlexLevel(flex_level_box.downgrade()),
216 Self::TableLevelBox(table_level_box) => {
217 WeakLayoutBox::TableLevelBox(table_level_box.downgrade())
218 },
219 Self::TaffyItemBox(taffy_item_box) => {
220 WeakLayoutBox::TaffyItemBox(taffy_item_box.downgrade())
221 },
222 }
223 }
224}
225
226#[derive(Clone, Debug, MallocSizeOf)]
227pub(super) enum WeakLayoutBox {
228 DisplayContents(SharedInlineStyles),
229 BlockLevel(WeakRefCell<BlockLevelBox>),
230 InlineLevel(WeakInlineItem),
231 FlexLevel(WeakRefCell<FlexLevelBox>),
232 TableLevelBox(WeakTableLevelBox),
233 TaffyItemBox(WeakRefCell<TaffyItemBox>),
234}
235
236impl WeakLayoutBox {
237 pub(crate) fn upgrade(&self) -> Option<LayoutBox> {
238 Some(match self {
239 Self::DisplayContents(inline_shared_styles) => {
240 LayoutBox::DisplayContents(inline_shared_styles.clone())
241 },
242 Self::BlockLevel(block_level_box) => LayoutBox::BlockLevel(block_level_box.upgrade()?),
243 Self::InlineLevel(inline_item) => LayoutBox::InlineLevel(inline_item.upgrade()?),
244 Self::FlexLevel(flex_level_box) => LayoutBox::FlexLevel(flex_level_box.upgrade()?),
245 Self::TableLevelBox(table_level_box) => {
246 LayoutBox::TableLevelBox(table_level_box.upgrade()?)
247 },
248 Self::TaffyItemBox(taffy_item_box) => {
249 LayoutBox::TaffyItemBox(taffy_item_box.upgrade()?)
250 },
251 })
252 }
253}
254
255#[derive(Default, MallocSizeOf)]
259pub struct DOMLayoutData(AtomicRefCell<InnerDOMLayoutData>);
260
261impl LayoutDataTrait for DOMLayoutData {}
263impl GenericLayoutDataTrait for DOMLayoutData {
264 fn as_any(&self) -> &dyn std::any::Any {
265 self
266 }
267}
268
269pub struct BoxSlot<'dom> {
270 pub(crate) slot: ArcRefCell<Option<LayoutBox>>,
271 pub(crate) marker: PhantomData<&'dom ()>,
272}
273
274impl From<ArcRefCell<Option<LayoutBox>>> for BoxSlot<'_> {
275 fn from(slot: ArcRefCell<Option<LayoutBox>>) -> Self {
276 Self {
277 slot,
278 marker: PhantomData,
279 }
280 }
281}
282
283impl BoxSlot<'_> {
285 pub(crate) fn set(self, layout_box: LayoutBox) {
286 layout_box.attached_to_tree(layout_box.downgrade());
287 *self.slot.borrow_mut() = Some(layout_box);
288 }
289
290 pub(crate) fn take_layout_box_if_undamaged(&self, damage: LayoutDamage) -> Option<LayoutBox> {
291 if damage.has_box_damage() {
292 return None;
293 }
294 self.slot.borrow_mut().take()
295 }
296}
297
298impl Drop for BoxSlot<'_> {
299 fn drop(&mut self) {
300 if !std::thread::panicking() {
301 assert!(self.slot.borrow().is_some(), "failed to set a layout box");
302 }
303 }
304}
305
306pub(crate) trait NodeExt<'dom> {
307 fn as_image(&self) -> Option<(Option<Image>, PhysicalSize<f64>)>;
310 fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
311 fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>;
312 fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
313 fn as_svg(&self) -> Option<SVGElementData<'dom>>;
314 fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
315
316 fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
317 fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
318 fn box_slot(&self) -> BoxSlot<'dom>;
319
320 fn unset_all_boxes(&self);
322
323 fn unset_all_pseudo_boxes(&self);
325
326 fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>;
327 fn with_layout_box_base_including_pseudos(&self, callback: impl Fn(&LayoutBoxBase));
328
329 fn repair_style(&self, context: &SharedStyleContext);
330 fn take_restyle_damage(&self) -> LayoutDamage;
331}
332
333impl<'dom> NodeExt<'dom> for ServoThreadSafeLayoutNode<'dom> {
334 fn as_image(&self) -> Option<(Option<Image>, PhysicalSize<f64>)> {
335 let (resource, metadata) = self.image_data()?;
336 let width = metadata.map(|metadata| metadata.width).unwrap_or_default();
337 let height = metadata.map(|metadata| metadata.height).unwrap_or_default();
338 let (mut width, mut height) = (width as f64, height as f64);
339 if let Some(density) = self.image_density().filter(|density| *density != 1.) {
340 width /= density;
341 height /= density;
342 }
343 Some((resource, PhysicalSize::new(width, height)))
344 }
345
346 fn as_svg(&self) -> Option<SVGElementData<'dom>> {
347 self.svg_data()
348 }
349
350 fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
351 let data = self.media_data()?;
352 let natural_size = if let Some(frame) = data.current_frame {
353 Some(PhysicalSize::new(frame.width.into(), frame.height.into()))
354 } else {
355 data.metadata
356 .map(|meta| PhysicalSize::new(meta.width.into(), meta.height.into()))
357 };
358 Some((
359 data.current_frame.map(|frame| frame.image_key),
360 natural_size,
361 ))
362 }
363
364 fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
365 let canvas_data = self.canvas_data()?;
366 let source = canvas_data.image_key;
367 Some((
368 CanvasInfo { source },
369 PhysicalSize::new(canvas_data.width.into(), canvas_data.height.into()),
370 ))
371 }
372
373 fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)> {
374 match (self.iframe_pipeline_id(), self.iframe_browsing_context_id()) {
375 (Some(pipeline_id), Some(browsing_context_id)) => {
376 Some((pipeline_id, browsing_context_id))
377 },
378 _ => None,
379 }
380 }
381
382 fn as_typeless_object_with_data_attribute(&self) -> Option<String> {
383 if self.type_id() !=
384 Some(ScriptLayoutNodeType::Element(
385 LayoutElementType::HTMLObjectElement,
386 ))
387 {
388 return None;
389 }
390
391 let element = self.as_element()?;
395 if element.get_attr(&ns!(), &local_name!("type")).is_some() {
396 return None;
397 }
398 element
399 .get_attr(&ns!(), &local_name!("data"))
400 .map(|string| string.to_owned())
401 }
402
403 fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
404 if self.layout_data().is_none() {
405 self.initialize_layout_data::<DOMLayoutData>();
406 }
407 self.layout_data()
408 .unwrap()
409 .as_any()
410 .downcast_ref::<DOMLayoutData>()
411 .unwrap()
412 .0
413 .borrow_mut()
414 }
415
416 fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
417 self.layout_data().map(|data| {
418 data.as_any()
419 .downcast_ref::<DOMLayoutData>()
420 .unwrap()
421 .0
422 .borrow()
423 })
424 }
425
426 fn box_slot(&self) -> BoxSlot<'dom> {
427 let pseudo_element_chain = self.pseudo_element_chain();
428 let Some(primary) = pseudo_element_chain.primary else {
429 return self.ensure_inner_layout_data().self_box.clone().into();
430 };
431
432 let Some(secondary) = pseudo_element_chain.secondary else {
433 let primary_layout_data = self
434 .ensure_inner_layout_data()
435 .create_pseudo_layout_data(primary);
436 return primary_layout_data.borrow().self_box.clone().into();
437 };
438
439 let primary_layout_data = self
446 .inner_layout_data()
447 .expect("Should already have element InnerLayoutData here.")
448 .pseudo_layout_data(primary)
449 .expect("Should already have primary pseudo-element InnerLayoutData here");
450 let secondary_layout_data = primary_layout_data
451 .borrow_mut()
452 .create_pseudo_layout_data(secondary);
453 secondary_layout_data.borrow().self_box.clone().into()
454 }
455
456 fn unset_all_boxes(&self) {
457 let mut layout_data = self.ensure_inner_layout_data();
458 *layout_data.self_box.borrow_mut() = None;
459 layout_data.pseudo_boxes.clear();
460
461 }
464
465 fn unset_all_pseudo_boxes(&self) {
466 self.ensure_inner_layout_data().pseudo_boxes.clear();
467 }
468
469 fn with_layout_box_base_including_pseudos(&self, callback: impl Fn(&LayoutBoxBase)) {
470 if let Some(inner_layout_data) = self.inner_layout_data() {
471 inner_layout_data.with_layout_box_base_including_pseudos(callback);
472 }
473 }
474
475 fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> {
476 let Some(layout_data) = self.inner_layout_data() else {
477 return vec![];
478 };
479 match pseudo_element {
480 Some(pseudo_element) => layout_data
481 .pseudo_layout_data(pseudo_element)
482 .map(|pseudo_layout_data| pseudo_layout_data.borrow().fragments())
483 .unwrap_or_default(),
484 None => layout_data.fragments(),
485 }
486 }
487
488 fn repair_style(&self, context: &SharedStyleContext) {
489 if let Some(layout_data) = self.inner_layout_data() {
490 layout_data.repair_style(self, context);
491 }
492 }
493
494 fn take_restyle_damage(&self) -> LayoutDamage {
495 let damage = self
496 .style_data()
497 .map(|style_data| std::mem::take(&mut style_data.element_data.borrow_mut().damage))
498 .unwrap_or_else(RestyleDamage::reconstruct);
499 LayoutDamage::from_bits_retain(damage.bits())
500 }
501}