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