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