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