Skip to main content

layout/
dom.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::marker::PhantomData;
6
7use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
8use layout_api::{
9    GenericLayoutDataTrait, LayoutDataTrait, LayoutElement, LayoutElementType, LayoutNode,
10    LayoutNodeType as ScriptLayoutNodeType, NodeRenderingType, SVGElementData,
11};
12use malloc_size_of_derive::MallocSizeOf;
13use script::layout_dom::ServoLayoutNode;
14use servo_arc::Arc as ServoArc;
15use servo_base::text::{AssumeUnder4GB, RangeAny, Utf32CodeUnits};
16use smallvec::SmallVec;
17use style::context::SharedStyleContext;
18use style::properties::ComputedValues;
19use style::selector_parser::PseudoElement;
20use style::values::specified::box_::DisplayOutside as StyloDisplayOutside;
21use web_atoms::{local_name, ns};
22
23use crate::cell::{ArcRefCell, WeakRefCell};
24use crate::context::LayoutContext;
25use crate::dom_traversal::{Contents, NodeAndStyleInfo};
26use crate::flexbox::FlexLevelBox;
27use crate::flow::inline::text_run::TextRun;
28use crate::flow::inline::{InlineItem, SharedInlineStyles, WeakInlineItem};
29use crate::flow::{BlockLevelBox, BlockLevelCreator};
30use crate::fragment_tree::{Fragment, FragmentFlags};
31use crate::geom::PhysicalSize;
32use crate::layout_box_base::LayoutBoxBase;
33use crate::replaced::{CanvasInfo, IFrameInfo, ImageInfo, VideoInfo};
34use crate::style_ext::{
35    ComputedValuesExt, Display, DisplayGeneratingBox, DisplayLayoutInternal, DisplayOutside,
36};
37use crate::table::{TableLevelBox, WeakTableLevelBox};
38use crate::taffy::{TaffyItemBox, TaffyItemBoxInner};
39
40#[derive(MallocSizeOf)]
41pub struct PseudoLayoutData {
42    pseudo: PseudoElement,
43    data: ArcRefCell<InnerDOMLayoutData>,
44}
45
46/// The data that is stored in each DOM node that is used by layout.
47#[derive(Default, MallocSizeOf)]
48pub struct InnerDOMLayoutData {
49    pub(super) self_box: ArcRefCell<Option<LayoutBox>>,
50    pub(super) pseudo_boxes: SmallVec<[PseudoLayoutData; 2]>,
51}
52
53impl InnerDOMLayoutData {
54    fn pseudo_layout_data(
55        &self,
56        pseudo_element: PseudoElement,
57    ) -> Option<ArcRefCell<InnerDOMLayoutData>> {
58        for pseudo_layout_data in self.pseudo_boxes.iter() {
59            if pseudo_element == pseudo_layout_data.pseudo {
60                return Some(pseudo_layout_data.data.clone());
61            }
62        }
63        None
64    }
65
66    fn create_pseudo_layout_data(
67        &mut self,
68        pseudo_element: PseudoElement,
69    ) -> ArcRefCell<InnerDOMLayoutData> {
70        let data: ArcRefCell<InnerDOMLayoutData> = Default::default();
71        self.pseudo_boxes.push(PseudoLayoutData {
72            pseudo: pseudo_element,
73            data: data.clone(),
74        });
75        data
76    }
77
78    fn fragments(&self) -> Vec<Fragment> {
79        self.with_fragments(<[Fragment]>::to_vec)
80            .unwrap_or_default()
81    }
82
83    /// Run `callback` on the fragments without cloning them. Returns `None` if the node has no box.
84    fn with_fragments<T>(&self, callback: impl FnOnce(&[Fragment]) -> T) -> Option<T> {
85        let self_box = self.self_box.borrow();
86        let layout_box = self_box.as_ref()?;
87        layout_box.with_base(|base| callback(&base.fragments()))
88    }
89
90    fn repair_style(&self, node: &ServoLayoutNode, context: &SharedStyleContext) {
91        if let Some(layout_object) = &*self.self_box.borrow() {
92            layout_object.repair_style(context, node, &node.style(context));
93        }
94
95        for pseudo_layout_data in self.pseudo_boxes.iter() {
96            let Some(node_with_pseudo) = node.with_pseudo(pseudo_layout_data.pseudo) else {
97                continue;
98            };
99            pseudo_layout_data
100                .data
101                .borrow()
102                .repair_style(&node_with_pseudo, context);
103        }
104    }
105
106    fn with_layout_box_base(&self, callback: impl FnOnce(&LayoutBoxBase)) {
107        if let Some(data) = self.self_box.borrow().as_ref() {
108            data.with_base(callback);
109        }
110    }
111
112    fn with_layout_box_base_including_pseudos(&self, mut callback: impl FnMut(&LayoutBoxBase)) {
113        self.with_layout_box_base(&mut callback);
114        for pseudo_layout_data in self.pseudo_boxes.iter() {
115            pseudo_layout_data
116                .data
117                .borrow()
118                .with_layout_box_base(&mut callback);
119        }
120    }
121}
122
123/// A box that is stored in one of the `DOMLayoutData` slots.
124#[derive(Debug, MallocSizeOf)]
125pub(super) enum LayoutBox {
126    DisplayContents(SharedInlineStyles),
127    BlockLevel(ArcRefCell<BlockLevelBox>),
128    InlineLevel(InlineItem),
129    FlexLevel(ArcRefCell<FlexLevelBox>),
130    TableLevelBox(TableLevelBox),
131    TaffyItemBox(ArcRefCell<TaffyItemBox>),
132    Text(ArcRefCell<TextRun>),
133}
134
135impl LayoutBox {
136    pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> Option<T> {
137        Some(match self {
138            LayoutBox::DisplayContents(..) | LayoutBox::Text(..) => return None,
139            LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback),
140            LayoutBox::InlineLevel(inline_item) => inline_item.with_base(callback),
141            LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback),
142            LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback),
143            LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback),
144        })
145    }
146
147    pub(crate) fn with_base_mut<T>(
148        &mut self,
149        callback: impl FnOnce(&mut LayoutBoxBase) -> T,
150    ) -> Option<T> {
151        Some(match self {
152            LayoutBox::DisplayContents(..) | LayoutBox::Text(..) => return None,
153            LayoutBox::BlockLevel(block_level_box) => {
154                block_level_box.borrow_mut().with_base_mut(callback)
155            },
156            LayoutBox::InlineLevel(inline_item) => inline_item.with_base_mut(callback),
157            LayoutBox::FlexLevel(flex_level_box) => {
158                flex_level_box.borrow_mut().with_base_mut(callback)
159            },
160            LayoutBox::TaffyItemBox(taffy_item_box) => {
161                taffy_item_box.borrow_mut().with_base_mut(callback)
162            },
163            LayoutBox::TableLevelBox(table_box) => table_box.with_base_mut(callback),
164        })
165    }
166
167    fn repair_style(
168        &self,
169        context: &SharedStyleContext,
170        node: &ServoLayoutNode,
171        new_style: &ServoArc<ComputedValues>,
172    ) {
173        match self {
174            LayoutBox::DisplayContents(inline_shared_styles) => {
175                *inline_shared_styles.style.borrow_mut() = new_style.clone();
176                *inline_shared_styles.selected.borrow_mut() = node.selected_style(context);
177            },
178            LayoutBox::BlockLevel(block_level_box) => {
179                block_level_box
180                    .borrow_mut()
181                    .repair_style(context, node, new_style);
182            },
183            LayoutBox::InlineLevel(inline_item) => {
184                inline_item.repair_style(context, node, new_style);
185            },
186            LayoutBox::FlexLevel(flex_level_box) => flex_level_box
187                .borrow_mut()
188                .repair_style(context, node, new_style),
189            LayoutBox::TableLevelBox(table_level_box) => {
190                table_level_box.repair_style(context, node, new_style)
191            },
192            LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box
193                .borrow_mut()
194                .repair_style(context, node, new_style),
195            LayoutBox::Text(..) => {
196                // There is nothing to update in this case.
197            },
198        }
199    }
200
201    fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
202        match self {
203            Self::DisplayContents(_) => {
204                // This box can't have children, its contents get reparented to its parent.
205                // Therefore, no need to do anything.
206            },
207            Self::BlockLevel(block_level_box) => {
208                block_level_box.borrow().attached_to_tree(layout_box)
209            },
210            Self::InlineLevel(inline_item) => inline_item.attached_to_tree(layout_box),
211            Self::FlexLevel(flex_level_box) => flex_level_box.borrow().attached_to_tree(layout_box),
212            Self::TableLevelBox(table_level_box) => table_level_box.attached_to_tree(layout_box),
213            Self::TaffyItemBox(taffy_item_box) => {
214                taffy_item_box.borrow().attached_to_tree(layout_box)
215            },
216            Self::Text(..) => {
217                // This kind of box cannot have children, so no need to do anything.
218            },
219        }
220    }
221
222    fn downgrade(&self) -> WeakLayoutBox {
223        match self {
224            Self::DisplayContents(inline_shared_styles) => {
225                WeakLayoutBox::DisplayContents(inline_shared_styles.clone())
226            },
227            Self::BlockLevel(block_level_box) => {
228                WeakLayoutBox::BlockLevel(block_level_box.downgrade())
229            },
230            Self::InlineLevel(inline_item) => WeakLayoutBox::InlineLevel(inline_item.downgrade()),
231            Self::FlexLevel(flex_level_box) => WeakLayoutBox::FlexLevel(flex_level_box.downgrade()),
232            Self::TableLevelBox(table_level_box) => {
233                WeakLayoutBox::TableLevelBox(table_level_box.downgrade())
234            },
235            Self::TaffyItemBox(taffy_item_box) => {
236                WeakLayoutBox::TaffyItemBox(taffy_item_box.downgrade())
237            },
238            Self::Text(text_run) => WeakLayoutBox::Text(text_run.downgrade()),
239        }
240    }
241}
242
243#[derive(Clone, Debug, MallocSizeOf)]
244pub(super) enum WeakLayoutBox {
245    DisplayContents(SharedInlineStyles),
246    BlockLevel(WeakRefCell<BlockLevelBox>),
247    InlineLevel(WeakInlineItem),
248    FlexLevel(WeakRefCell<FlexLevelBox>),
249    TableLevelBox(WeakTableLevelBox),
250    TaffyItemBox(WeakRefCell<TaffyItemBox>),
251    Text(WeakRefCell<TextRun>),
252}
253
254impl WeakLayoutBox {
255    pub(crate) fn upgrade(&self) -> Option<LayoutBox> {
256        Some(match self {
257            Self::DisplayContents(inline_shared_styles) => {
258                LayoutBox::DisplayContents(inline_shared_styles.clone())
259            },
260            Self::BlockLevel(block_level_box) => LayoutBox::BlockLevel(block_level_box.upgrade()?),
261            Self::InlineLevel(inline_item) => LayoutBox::InlineLevel(inline_item.upgrade()?),
262            Self::FlexLevel(flex_level_box) => LayoutBox::FlexLevel(flex_level_box.upgrade()?),
263            Self::TableLevelBox(table_level_box) => {
264                LayoutBox::TableLevelBox(table_level_box.upgrade()?)
265            },
266            Self::TaffyItemBox(taffy_item_box) => {
267                LayoutBox::TaffyItemBox(taffy_item_box.upgrade()?)
268            },
269            Self::Text(text_run) => LayoutBox::Text(text_run.upgrade()?),
270        })
271    }
272}
273
274/// A wrapper for [`InnerDOMLayoutData`]. This is necessary to give the entire data
275/// structure interior mutability, as we will need to mutate the layout data of
276/// non-mutable DOM nodes.
277#[derive(Default, MallocSizeOf)]
278pub struct DOMLayoutData(AtomicRefCell<InnerDOMLayoutData>);
279
280// The implementation of this trait allows the data to be stored in the DOM.
281impl LayoutDataTrait for DOMLayoutData {}
282impl GenericLayoutDataTrait for DOMLayoutData {
283    fn as_any(&self) -> &dyn std::any::Any {
284        self
285    }
286
287    fn set_text_run_selection(&self, new_range: Option<RangeAny<Utf32CodeUnits>>) -> bool {
288        let inner = self.0.borrow();
289        // When `::first-letter` is used, one DOM text node can generate multiple text runs
290        // so there isn’t a single place to set the new range
291        if inner
292            .pseudo_boxes
293            .iter()
294            .any(|pseudo_box| matches!(pseudo_box.pseudo, PseudoElement::FirstLetter))
295        {
296            return false;
297        }
298        if let Some(LayoutBox::Text(text_run)) = &*inner.self_box.borrow() {
299            *text_run.borrow().run_data.selection.borrow_mut() = new_range;
300            true
301        } else {
302            false
303        }
304    }
305
306    fn set_element_selection(&self, selected: bool) -> bool {
307        let inner = self.0.borrow();
308        let inner_box = inner.self_box.borrow();
309        let Some(inner_box) = &*inner_box else {
310            return false;
311        };
312
313        match inner_box {
314            LayoutBox::DisplayContents(..) => false,
315            LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() {
316                BlockLevelBox::Independent(independent_formatting_context) => {
317                    independent_formatting_context.set_selection(selected)
318                },
319                _ => false,
320            },
321            LayoutBox::InlineLevel(inline_item) => match inline_item {
322                InlineItem::Atomic(atomic_item, ..) => atomic_item.borrow().set_selection(selected),
323                _ => false,
324            },
325            LayoutBox::FlexLevel(flex_level_box) => match &*flex_level_box.borrow() {
326                FlexLevelBox::FlexItem(flex_item) => flex_item
327                    .independent_formatting_context
328                    .set_selection(selected),
329                _ => false,
330            },
331            LayoutBox::TableLevelBox(table_level_box) => match table_level_box {
332                TableLevelBox::Caption(caption) => caption.borrow().context.set_selection(selected),
333                TableLevelBox::Cell(cell) => cell.borrow().context.set_selection(selected),
334                _ => false,
335            },
336            LayoutBox::TaffyItemBox(taffy_item_box) => {
337                match &taffy_item_box.borrow().taffy_level_box {
338                    TaffyItemBoxInner::InFlowBox(independent_formatting_context) => {
339                        independent_formatting_context.set_selection(selected)
340                    },
341                    _ => false,
342                }
343            },
344            LayoutBox::Text(..) => false,
345        }
346    }
347
348    fn rendered_text(&self, range: RangeAny<Utf32CodeUnits>) -> Option<String> {
349        let inner = self.0.borrow();
350        let self_box = inner.self_box.borrow();
351
352        let Some(LayoutBox::Text(text_run)) = self_box.as_ref() else {
353            return None;
354        };
355
356        let text_run = text_run.borrow();
357        let range_in_text_run = text_run.run_data.map_dom_range_to_transformed_range(range);
358        let start_in_ifc = text_run.run_data.character_range_in_ifc_text.start;
359
360        let string = text_run.run_data.text_content.get()?;
361        let start =
362            (range_in_text_run.start + start_in_ifc).to_utf8_code_units_in(AssumeUnder4GB, string);
363        let end =
364            (range_in_text_run.end + start_in_ifc).to_utf8_code_units_in(AssumeUnder4GB, string);
365        Some(string[start.0 as usize..end.0 as usize].to_owned())
366    }
367}
368
369pub struct BoxSlot<'dom> {
370    pub(crate) slot: ArcRefCell<Option<LayoutBox>>,
371    pub(crate) marker: PhantomData<&'dom ()>,
372}
373
374impl From<ArcRefCell<Option<LayoutBox>>> for BoxSlot<'_> {
375    fn from(slot: ArcRefCell<Option<LayoutBox>>) -> Self {
376        Self {
377            slot,
378            marker: PhantomData,
379        }
380    }
381}
382
383/// A mutable reference to a `LayoutBox` stored in a DOM element.
384impl BoxSlot<'_> {
385    pub(crate) fn set(self, layout_box: LayoutBox) {
386        layout_box.attached_to_tree(layout_box.downgrade());
387        *self.slot.borrow_mut() = Some(layout_box);
388    }
389
390    pub(crate) fn take_layout_box(&self) -> Option<LayoutBox> {
391        self.slot.borrow_mut().take()
392    }
393
394    /// Call [`Self::take_layout_box`] and try to unwrap it into a [`TextRun`], returning `None` if
395    /// the slot is empty or it does not contain a [`TextRun`]. Note that this will *always* clear
396    /// the [`BoxSlot`].
397    pub(crate) fn take_layout_box_as_text_run(&self) -> Option<ArcRefCell<TextRun>> {
398        match self.take_layout_box()? {
399            LayoutBox::Text(old_text_run) => Some(old_text_run),
400            _ => None,
401        }
402    }
403}
404
405impl Drop for BoxSlot<'_> {
406    fn drop(&mut self) {
407        if !std::thread::panicking() {
408            assert!(self.slot.borrow().is_some(), "failed to set a layout box");
409        }
410    }
411}
412
413pub(crate) trait NodeExt<'dom> {
414    /// Return true if the element is an HTML `<br>` element.
415    fn is_html_br_element(&self) -> bool;
416    /// Return true if the element is an HTML `<wbr>` element.
417    fn is_html_wbr_element(&self) -> bool;
418    /// Returns the relevant data wrapping into respective struct and its size in pixels.
419    fn as_image(&self) -> Option<(ImageInfo, PhysicalSize<f64>)>;
420    fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
421    fn as_iframe(&self) -> Option<IFrameInfo>;
422    fn as_video(&self) -> Option<(VideoInfo, Option<PhysicalSize<f64>>)>;
423    fn as_svg(&self) -> Option<SVGElementData<'dom>>;
424    fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
425
426    fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
427    fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
428    fn inner_layout_data_mut(&self) -> Option<AtomicRefMut<'dom, InnerDOMLayoutData>>;
429    fn box_slot(&self) -> BoxSlot<'dom>;
430
431    /// Remove boxes for the element itself, and all of its pseudo-element boxes.
432    fn unset_all_boxes(&self);
433
434    /// Clear laid out `Fragment`s and dirty `Fragment` caches for all of this node's boxes,
435    /// ensuring that the next `FragmentTree` layout that happens at this node is complete.
436    fn clear_fragments_and_dirty_fragment_caches(&self);
437
438    /// Clear laid out `Fragment`s and dirty `Fragment` caches for all this node's boxes and
439    /// descendant's boxes, ensuring that the next `FragmentTree` layout that happens at and
440    /// below this node is complete.
441    fn clear_fragments_and_dirty_fragment_caches_recursively(&self);
442
443    /// Clear laid out `Fragment`s and dirty `Fragment` caches for all this node's descendant's
444    /// boxes.
445    fn clear_fragments_and_dirty_fragment_caches_of_descendants(&self);
446
447    /// Returns the [`NodeRenderingType`] for this [`LayoutNode`] which describes whether
448    /// the node is being rendered, delegating rendering, or not being rendered at all
449    /// based on whether it has a [`LayoutBox`] and what kind.
450    fn rendering_type(&self) -> NodeRenderingType;
451
452    fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>;
453
454    /// Run `callback` on the fragments without cloning them. Returns `None` if the node has no box.
455    fn with_fragments<T>(&self, callback: impl FnOnce(&[Fragment]) -> T) -> Option<T>;
456    fn with_layout_box_base(&self, callback: impl FnMut(&LayoutBoxBase));
457    fn with_layout_box_base_including_pseudos(&self, callback: impl FnMut(&LayoutBoxBase));
458
459    fn repair_style(&self, context: &SharedStyleContext);
460
461    /// Whether or not this node isolates downward flowing box tree rebuild damage and
462    /// fragment tree layout cache damage. Roughly, this corresponds to independent
463    /// formatting context boundaries.
464    ///
465    /// - The node's boxes themselves will be rebuilt, but not the descendant node's
466    ///   boxes.
467    /// - The node's fragment tree layout will be rebuilt, not the descendent node's
468    ///   fragment tree layout cache.
469    ///
470    /// When this node has no box yet, `false` is returned.
471    fn isolates_damage_for_damage_propagation(&self) -> bool;
472
473    /// Try to re-run box tree reconstruction from this point. This can succeed if the
474    /// node itself is still valid and isolates box tree damage from ancestors (for
475    /// instance, if it starts an independent formatting context). **Note:** This assumes
476    /// that no ancestors have box damage.
477    ///
478    /// Returns `true` if box tree reconstruction was sucessful and `false` otherwise.
479    fn rebuild_box_tree_from_independent_formatting_context(
480        &self,
481        layout_context: &LayoutContext,
482    ) -> bool;
483
484    /// Whether or not the style of this node indicates that it should be absolutely
485    /// positioned.
486    fn is_absolutely_positioned(&self) -> bool;
487}
488
489impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
490    fn is_html_br_element(&self) -> bool {
491        self.as_html_element()
492            .is_some_and(|element| element.local_name() == &local_name!("br"))
493    }
494
495    fn is_html_wbr_element(&self) -> bool {
496        self.as_html_element()
497            .is_some_and(|element| element.local_name() == &local_name!("wbr"))
498    }
499
500    fn as_image(&self) -> Option<(ImageInfo, PhysicalSize<f64>)> {
501        let (resource, metadata) = self.image_data()?;
502        let width = metadata.map(|metadata| metadata.width).unwrap_or_default();
503        let height = metadata.map(|metadata| metadata.height).unwrap_or_default();
504        let (mut width, mut height) = (width as f64, height as f64);
505        // Take `image_density` into account for calculating the size in pixels for images.
506        if let Some(density) = self.image_density().filter(|density| *density != 1.) {
507            width /= density;
508            height /= density;
509        }
510        Some((
511            ImageInfo {
512                image: resource,
513                showing_broken_image_icon: self.showing_broken_image_icon(),
514                url: self.image_url(),
515            },
516            PhysicalSize::new(width, height),
517        ))
518    }
519
520    fn as_svg(&self) -> Option<SVGElementData<'dom>> {
521        self.svg_data()
522    }
523
524    fn as_video(&self) -> Option<(VideoInfo, Option<PhysicalSize<f64>>)> {
525        let data = self.media_data()?;
526        let natural_size = if let Some(frame) = data.current_frame {
527            Some(PhysicalSize::new(frame.width.into(), frame.height.into()))
528        } else {
529            data.metadata
530                .map(|meta| PhysicalSize::new(meta.width.into(), meta.height.into()))
531        };
532        Some((
533            VideoInfo {
534                image_key: data.current_frame.map(|frame| frame.image_key),
535                poster_url: data.poster_url,
536            },
537            natural_size,
538        ))
539    }
540
541    fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
542        let canvas_data = self.canvas_data()?;
543        let source = canvas_data.image_key;
544        Some((
545            CanvasInfo { source },
546            PhysicalSize::new(canvas_data.width.into(), canvas_data.height.into()),
547        ))
548    }
549
550    fn as_iframe(&self) -> Option<IFrameInfo> {
551        match (self.iframe_pipeline_id(), self.iframe_browsing_context_id()) {
552            (Some(pipeline_id), Some(browsing_context_id)) => Some(IFrameInfo {
553                pipeline_id,
554                browsing_context_id,
555            }),
556            _ => None,
557        }
558    }
559
560    fn as_typeless_object_with_data_attribute(&self) -> Option<String> {
561        if self.type_id() !=
562            Some(ScriptLayoutNodeType::Element(
563                LayoutElementType::HTMLObjectElement,
564            ))
565        {
566            return None;
567        }
568
569        // TODO: This is the what the legacy layout system did, but really if Servo
570        // supports any `<object>` that's an image, it should support those with URLs
571        // and `type` attributes with image mime types.
572        let element = self.as_element()?;
573        if element.attribute(&ns!(), &local_name!("type")).is_some() {
574            return None;
575        }
576        element
577            .attribute_as_str(&ns!(), &local_name!("data"))
578            .map(|string| string.to_owned())
579    }
580
581    fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
582        if self.layout_data().is_none() {
583            self.initialize_layout_data::<DOMLayoutData>();
584        }
585        self.layout_data()
586            .unwrap()
587            .as_any()
588            .downcast_ref::<DOMLayoutData>()
589            .unwrap()
590            .0
591            .borrow_mut()
592    }
593
594    fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
595        self.layout_data().map(|data| {
596            data.as_any()
597                .downcast_ref::<DOMLayoutData>()
598                .unwrap()
599                .0
600                .borrow()
601        })
602    }
603
604    fn inner_layout_data_mut(&self) -> Option<AtomicRefMut<'dom, InnerDOMLayoutData>> {
605        self.layout_data().map(|data| {
606            data.as_any()
607                .downcast_ref::<DOMLayoutData>()
608                .unwrap()
609                .0
610                .borrow_mut()
611        })
612    }
613
614    fn box_slot(&self) -> BoxSlot<'dom> {
615        let pseudo_element_chain = self.pseudo_element_chain();
616        let Some(primary) = pseudo_element_chain.primary else {
617            return self.ensure_inner_layout_data().self_box.clone().into();
618        };
619
620        let Some(secondary) = pseudo_element_chain.secondary else {
621            let primary_layout_data = self
622                .ensure_inner_layout_data()
623                .create_pseudo_layout_data(primary);
624            return primary_layout_data.borrow().self_box.clone().into();
625        };
626
627        // It's *very* important that this not borrow the element's main
628        // `InnerLayoutData`. Primary pseudo-elements are processed at the same recursion
629        // level as the main data, so the `BoxSlot` is created sequentially with other
630        // primary pseudo-elements and the element itself. The secondary pseudo-element is
631        // one level deep, so could be happening in parallel with the primary
632        // pseudo-elements or main element layout.
633        let primary_layout_data = self
634            .inner_layout_data()
635            .expect("Should already have element InnerLayoutData here.")
636            .pseudo_layout_data(primary)
637            .expect("Should already have primary pseudo-element InnerLayoutData here");
638        let secondary_layout_data = primary_layout_data
639            .borrow_mut()
640            .create_pseudo_layout_data(secondary);
641        secondary_layout_data.borrow().self_box.clone().into()
642    }
643
644    fn unset_all_boxes(&self) {
645        let mut layout_data = self.ensure_inner_layout_data();
646        *layout_data.self_box.borrow_mut() = None;
647        layout_data.pseudo_boxes.clear();
648
649        // Stylo already takes care of removing all layout data
650        // for DOM descendants of elements with `display: none`.
651    }
652
653    fn clear_fragments_and_dirty_fragment_caches_recursively(&self) {
654        self.clear_fragments_and_dirty_fragment_caches();
655        self.clear_fragments_and_dirty_fragment_caches_of_descendants();
656    }
657
658    fn clear_fragments_and_dirty_fragment_caches(&self) {
659        self.with_layout_box_base_including_pseudos(|base| {
660            base.clear_fragments_and_dirty_fragment_cache()
661        });
662    }
663
664    fn clear_fragments_and_dirty_fragment_caches_of_descendants(&self) {
665        for child in self.flat_tree_children() {
666            child.clear_fragments_and_dirty_fragment_caches_recursively();
667        }
668    }
669
670    fn rendering_type(&self) -> NodeRenderingType {
671        let Some(layout_data) = self.inner_layout_data() else {
672            return NodeRenderingType::NotRendered;
673        };
674        match &*layout_data.self_box.borrow() {
675            Some(LayoutBox::DisplayContents(..)) => NodeRenderingType::DelegatesRendering,
676            Some(..) => NodeRenderingType::Rendered,
677            None => NodeRenderingType::NotRendered,
678        }
679    }
680
681    fn with_layout_box_base(&self, callback: impl FnMut(&LayoutBoxBase)) {
682        if let Some(inner_layout_data) = self.inner_layout_data() {
683            inner_layout_data.with_layout_box_base(callback);
684        }
685    }
686
687    fn with_layout_box_base_including_pseudos(&self, callback: impl FnMut(&LayoutBoxBase)) {
688        if let Some(inner_layout_data) = self.inner_layout_data() {
689            inner_layout_data.with_layout_box_base_including_pseudos(callback);
690        }
691    }
692
693    fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> {
694        let Some(layout_data) = self.inner_layout_data() else {
695            return vec![];
696        };
697        match pseudo_element {
698            Some(pseudo_element) => layout_data
699                .pseudo_layout_data(pseudo_element)
700                .map(|pseudo_layout_data| pseudo_layout_data.borrow().fragments())
701                .unwrap_or_default(),
702            None => layout_data.fragments(),
703        }
704    }
705
706    fn with_fragments<T>(&self, callback: impl FnOnce(&[Fragment]) -> T) -> Option<T> {
707        self.inner_layout_data()?.with_fragments(callback)
708    }
709
710    fn repair_style(&self, context: &SharedStyleContext) {
711        if let Some(layout_data) = self.inner_layout_data() {
712            layout_data.repair_style(self, context);
713        }
714    }
715
716    fn isolates_damage_for_damage_propagation(&self) -> bool {
717        // Do not run incremental box and fragment tree layout at the `<body>` or root element as
718        // there is some special processing that must happen for these elements and it currently
719        // only happens when doing a full box tree construction traversal.
720        if self.as_element().is_some_and(|element| {
721            element.is_body_element_of_html_element_root() || element.is_root()
722        }) {
723            return false;
724        }
725
726        let Some(inner_layout_data) = self.inner_layout_data() else {
727            return false;
728        };
729        let self_box = inner_layout_data.self_box.borrow();
730        let Some(self_box) = &*self_box else {
731            return false;
732        };
733
734        match self_box {
735            LayoutBox::DisplayContents(..) => false,
736            LayoutBox::BlockLevel(block_level) => matches!(
737                &*block_level.borrow(),
738                BlockLevelBox::Independent(..) |
739                    BlockLevelBox::OutOfFlowFloatBox(..) |
740                    BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(..)
741            ),
742            LayoutBox::InlineLevel(inline_level) => matches!(
743                inline_level,
744                InlineItem::OutOfFlowAbsolutelyPositionedBox(..) | InlineItem::Atomic(..)
745            ),
746            LayoutBox::FlexLevel(..) => true,
747            LayoutBox::TableLevelBox(table_level_box) => matches!(
748                table_level_box,
749                TableLevelBox::Cell(..) | TableLevelBox::Caption(..),
750            ),
751            LayoutBox::TaffyItemBox(..) => true,
752            LayoutBox::Text(..) => unreachable!("An element should never be a text node"),
753        }
754    }
755
756    fn rebuild_box_tree_from_independent_formatting_context(
757        &self,
758        layout_context: &LayoutContext,
759    ) -> bool {
760        // Do not run incremental box tree layout at the `<body>` or root element as there
761        // is some special processing that must happen for these elements and it currently
762        // only happens when doing a full box tree construction traversal.
763        if self.as_element().is_some_and(|element| {
764            element.is_body_element_of_html_element_root() || element.is_root()
765        }) {
766            return false;
767        }
768
769        let layout_box = {
770            let Some(mut inner_layout_data) = self.inner_layout_data_mut() else {
771                return false;
772            };
773            inner_layout_data.pseudo_boxes.clear();
774            inner_layout_data.self_box.clone()
775        };
776
777        let layout_box = layout_box.borrow();
778        let Some(layout_box) = &*layout_box else {
779            return false;
780        };
781
782        let info = NodeAndStyleInfo::new(*self, self.style(&layout_context.style_context));
783        let box_style = info.style.get_box();
784        let Display::GeneratingBox(display) = box_style.display.into() else {
785            return false;
786        };
787        let contents = || {
788            assert!(
789                self.pseudo_element_chain().is_empty(),
790                "Shouldn't try to rebuild box tree from a pseudo-element"
791            );
792            Contents::for_element(info.node, layout_context)
793        };
794        match layout_box {
795            LayoutBox::DisplayContents(..) => false,
796            LayoutBox::BlockLevel(block_level) => {
797                let mut block_level = block_level.borrow_mut();
798                match &mut *block_level {
799                    BlockLevelBox::Independent(independent_formatting_context) => {
800                        if box_style.position.is_absolutely_positioned() ||
801                            box_style.float.is_floating()
802                        {
803                            return false;
804                        }
805                        let DisplayGeneratingBox::OutsideInside {
806                            outside: DisplayOutside::Block,
807                            inside: display_inside,
808                        } = display
809                        else {
810                            return false;
811                        };
812                        if !matches!(
813                            BlockLevelCreator::new_for_inflow_block_level_element(
814                                &info,
815                                display_inside,
816                                contents(),
817                                independent_formatting_context.propagated_data,
818                            ),
819                            BlockLevelCreator::Independent { .. }
820                        ) {
821                            return false;
822                        }
823                        independent_formatting_context.rebuild(layout_context, &info);
824                        true
825                    },
826                    BlockLevelBox::OutOfFlowFloatBox(float_box) => {
827                        if !info.style.get_float().is_floating() {
828                            return false;
829                        }
830                        float_box.contents.rebuild(layout_context, &info);
831                        true
832                    },
833                    BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
834                        // Even if absolute positioning blockifies the outer display type, if the
835                        // original display was inline-level, then the box needs to be handled as
836                        // an inline-level in order to compute the static position correctly.
837                        // See `BlockContainerBuilder::handle_absolutely_positioned_element()`.
838                        if !info.style.get_box().position.is_absolutely_positioned() ||
839                            box_style.original_display.outside() != StyloDisplayOutside::Block
840                        {
841                            return false;
842                        }
843                        positioned_box
844                            .borrow_mut()
845                            .context
846                            .rebuild(layout_context, &info);
847                        true
848                    },
849                    _ => false,
850                }
851            },
852            LayoutBox::InlineLevel(inline_level) => match inline_level {
853                InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
854                    if !info.style.get_box().position.is_absolutely_positioned() {
855                        return false;
856                    }
857                    positioned_box
858                        .borrow_mut()
859                        .context
860                        .rebuild(layout_context, &info);
861                    true
862                },
863                InlineItem::Atomic(atomic_box, _, _) => {
864                    let flags = match contents() {
865                        Contents::NonReplaced(_) => FragmentFlags::empty(),
866                        Contents::Replaced(_) => FragmentFlags::IS_REPLACED,
867                        Contents::Widget(_) => FragmentFlags::IS_WIDGET,
868                    };
869                    if !info.style.is_atomic_inline_level(flags) {
870                        return false;
871                    }
872                    atomic_box.borrow_mut().rebuild(layout_context, &info);
873                    true
874                },
875                _ => false,
876            },
877            LayoutBox::FlexLevel(flex_level_box) => {
878                let mut flex_level_box = flex_level_box.borrow_mut();
879                match &mut *flex_level_box {
880                    FlexLevelBox::FlexItem(flex_item_box) => {
881                        if info.style.get_box().position.is_absolutely_positioned() ||
882                            !flex_item_box.style().order_equals(&info.style)
883                        {
884                            return false;
885                        }
886                        flex_item_box
887                            .independent_formatting_context
888                            .rebuild(layout_context, &info)
889                    },
890                    FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
891                        if !info.style.get_box().position.is_absolutely_positioned() {
892                            return false;
893                        }
894                        positioned_box
895                            .borrow_mut()
896                            .context
897                            .rebuild(layout_context, &info);
898                    },
899                }
900                true
901            },
902            LayoutBox::TableLevelBox(table_level_box) => match table_level_box {
903                TableLevelBox::Caption(caption) => {
904                    if display !=
905                        DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableCaption)
906                    {
907                        return false;
908                    }
909                    caption.borrow_mut().context.rebuild(layout_context, &info);
910                    true
911                },
912                TableLevelBox::Cell(table_cell) => {
913                    if display !=
914                        DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableCell)
915                    {
916                        return false;
917                    }
918                    table_cell
919                        .borrow_mut()
920                        .context
921                        .rebuild(layout_context, &info);
922                    true
923                },
924                _ => false,
925            },
926            LayoutBox::TaffyItemBox(..) => false,
927            LayoutBox::Text(..) => unreachable!("An element should never be a text node"),
928        }
929    }
930
931    fn is_absolutely_positioned(&self) -> bool {
932        self.as_element().is_some_and(|element| {
933            element
934                .element_data()
935                .styles
936                .primary()
937                .get_box()
938                .position
939                .is_absolutely_positioned()
940        })
941    }
942}