/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/layout/dom.rs
829 строк
33 KB
Shubham Gupta
layout: Pass `poster_url` for `LargestContentfulPaint` Entry (#43376)
05 авг 2026, 23:29
Не верифицирован
05 авг 2026, 23:29
dce660c
Код
Авторство
О чём код?
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::marker::PhantomData; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use layout_api::{ GenericLayoutDataTrait, LayoutDataTrait, LayoutElement, LayoutElementType, LayoutNode, LayoutNodeType as ScriptLayoutNodeType, NodeRenderingType, SVGElementData, }; use malloc_size_of_derive::MallocSizeOf; use script::layout_dom::ServoLayoutNode; use servo_arc::Arc as ServoArc; use smallvec::SmallVec; use style::context::SharedStyleContext; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; use style::values::specified::box_::DisplayOutside as StyloDisplayOutside; use web_atoms::{local_name, ns}; use crate::cell::{ArcRefCell, WeakRefCell}; use crate::context::LayoutContext; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::flexbox::FlexLevelBox; use crate::flow::inline::text_run::TextRun; use crate::flow::inline::{InlineItem, SharedInlineStyles, WeakInlineItem}; use crate::flow::{BlockLevelBox, BlockLevelCreator}; use crate::fragment_tree::{Fragment, FragmentFlags}; use crate::geom::PhysicalSize; use crate::layout_box_base::LayoutBoxBase; use crate::replaced::{CanvasInfo, IFrameInfo, ImageInfo, VideoInfo}; use crate::style_ext::{ ComputedValuesExt, Display, DisplayGeneratingBox, DisplayLayoutInternal, DisplayOutside, }; use crate::table::{TableLevelBox, WeakTableLevelBox}; use crate::taffy::TaffyItemBox; #[derive(MallocSizeOf)] pub struct PseudoLayoutData { pseudo: PseudoElement, data: ArcRefCell<InnerDOMLayoutData>, } /// The data that is stored in each DOM node that is used by layout. #[derive(Default, MallocSizeOf)] pub struct InnerDOMLayoutData { pub(super) self_box: ArcRefCell<Option<LayoutBox>>, pub(super) pseudo_boxes: SmallVec<[PseudoLayoutData; 2]>, } impl InnerDOMLayoutData { fn pseudo_layout_data( &self, pseudo_element: PseudoElement, ) -> Option<ArcRefCell<InnerDOMLayoutData>> { for pseudo_layout_data in self.pseudo_boxes.iter() { if pseudo_element == pseudo_layout_data.pseudo { return Some(pseudo_layout_data.data.clone()); } } None } fn create_pseudo_layout_data( &mut self, pseudo_element: PseudoElement, ) -> ArcRefCell<InnerDOMLayoutData> { let data: ArcRefCell<InnerDOMLayoutData> = Default::default(); self.pseudo_boxes.push(PseudoLayoutData { pseudo: pseudo_element, data: data.clone(), }); data } fn fragments(&self) -> Vec<Fragment> { self.self_box .borrow() .as_ref() .and_then(|layout_box| layout_box.with_base(|base| base.fragments().clone())) .unwrap_or_default() } fn repair_style(&self, node: &ServoLayoutNode, context: &SharedStyleContext) { if let Some(layout_object) = &*self.self_box.borrow() { layout_object.repair_style(context, node, &node.style(context)); } for pseudo_layout_data in self.pseudo_boxes.iter() { let Some(node_with_pseudo) = node.with_pseudo(pseudo_layout_data.pseudo) else { continue; }; pseudo_layout_data .data .borrow() .repair_style(&node_with_pseudo, context); } } fn with_layout_box_base(&self, callback: impl FnOnce(&LayoutBoxBase)) { if let Some(data) = self.self_box.borrow().as_ref() { data.with_base(callback); } } fn with_layout_box_base_including_pseudos(&self, mut callback: impl FnMut(&LayoutBoxBase)) { self.with_layout_box_base(&mut callback); for pseudo_layout_data in self.pseudo_boxes.iter() { pseudo_layout_data .data .borrow() .with_layout_box_base(&mut callback); } } } /// A box that is stored in one of the `DOMLayoutData` slots. #[derive(Debug, MallocSizeOf)] pub(super) enum LayoutBox { DisplayContents(SharedInlineStyles), BlockLevel(ArcRefCell<BlockLevelBox>), InlineLevel(InlineItem), FlexLevel(ArcRefCell<FlexLevelBox>), TableLevelBox(TableLevelBox), TaffyItemBox(ArcRefCell<TaffyItemBox>), Text(ArcRefCell<TextRun>), } impl LayoutBox { pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> Option<T> { Some(match self { LayoutBox::DisplayContents(..) | LayoutBox::Text(..) => return None, LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback), LayoutBox::InlineLevel(inline_item) => inline_item.with_base(callback), LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback), LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback), LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback), }) } pub(crate) fn with_base_mut<T>( &mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T, ) -> Option<T> { Some(match self { LayoutBox::DisplayContents(..) | LayoutBox::Text(..) => return None, LayoutBox::BlockLevel(block_level_box) => { block_level_box.borrow_mut().with_base_mut(callback) }, LayoutBox::InlineLevel(inline_item) => inline_item.with_base_mut(callback), LayoutBox::FlexLevel(flex_level_box) => { flex_level_box.borrow_mut().with_base_mut(callback) }, LayoutBox::TaffyItemBox(taffy_item_box) => { taffy_item_box.borrow_mut().with_base_mut(callback) }, LayoutBox::TableLevelBox(table_box) => table_box.with_base_mut(callback), }) } fn repair_style( &self, context: &SharedStyleContext, node: &ServoLayoutNode, new_style: &ServoArc<ComputedValues>, ) { match self { LayoutBox::DisplayContents(inline_shared_styles) => { *inline_shared_styles.style.borrow_mut() = new_style.clone(); *inline_shared_styles.selected.borrow_mut() = node.selected_style(context); }, LayoutBox::BlockLevel(block_level_box) => { block_level_box .borrow_mut() .repair_style(context, node, new_style); }, LayoutBox::InlineLevel(inline_item) => { inline_item.repair_style(context, node, new_style); }, LayoutBox::FlexLevel(flex_level_box) => flex_level_box .borrow_mut() .repair_style(context, node, new_style), LayoutBox::TableLevelBox(table_level_box) => { table_level_box.repair_style(context, node, new_style) }, LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box .borrow_mut() .repair_style(context, node, new_style), LayoutBox::Text(..) => { // There is nothing to update in this case. }, } } fn attached_to_tree(&self, layout_box: WeakLayoutBox) { match self { Self::DisplayContents(_) => { // This box can't have children, its contents get reparented to its parent. // Therefore, no need to do anything. }, Self::BlockLevel(block_level_box) => { block_level_box.borrow().attached_to_tree(layout_box) }, Self::InlineLevel(inline_item) => inline_item.attached_to_tree(layout_box), Self::FlexLevel(flex_level_box) => flex_level_box.borrow().attached_to_tree(layout_box), Self::TableLevelBox(table_level_box) => table_level_box.attached_to_tree(layout_box), Self::TaffyItemBox(taffy_item_box) => { taffy_item_box.borrow().attached_to_tree(layout_box) }, Self::Text(..) => { // This kind of box cannot have children, so no need to do anything. }, } } fn downgrade(&self) -> WeakLayoutBox { match self { Self::DisplayContents(inline_shared_styles) => { WeakLayoutBox::DisplayContents(inline_shared_styles.clone()) }, Self::BlockLevel(block_level_box) => { WeakLayoutBox::BlockLevel(block_level_box.downgrade()) }, Self::InlineLevel(inline_item) => WeakLayoutBox::InlineLevel(inline_item.downgrade()), Self::FlexLevel(flex_level_box) => WeakLayoutBox::FlexLevel(flex_level_box.downgrade()), Self::TableLevelBox(table_level_box) => { WeakLayoutBox::TableLevelBox(table_level_box.downgrade()) }, Self::TaffyItemBox(taffy_item_box) => { WeakLayoutBox::TaffyItemBox(taffy_item_box.downgrade()) }, Self::Text(text_run) => WeakLayoutBox::Text(text_run.downgrade()), } } } #[derive(Clone, Debug, MallocSizeOf)] pub(super) enum WeakLayoutBox { DisplayContents(SharedInlineStyles), BlockLevel(WeakRefCell<BlockLevelBox>), InlineLevel(WeakInlineItem), FlexLevel(WeakRefCell<FlexLevelBox>), TableLevelBox(WeakTableLevelBox), TaffyItemBox(WeakRefCell<TaffyItemBox>), Text(WeakRefCell<TextRun>), } impl WeakLayoutBox { pub(crate) fn upgrade(&self) -> Option<LayoutBox> { Some(match self { Self::DisplayContents(inline_shared_styles) => { LayoutBox::DisplayContents(inline_shared_styles.clone()) }, Self::BlockLevel(block_level_box) => LayoutBox::BlockLevel(block_level_box.upgrade()?), Self::InlineLevel(inline_item) => LayoutBox::InlineLevel(inline_item.upgrade()?), Self::FlexLevel(flex_level_box) => LayoutBox::FlexLevel(flex_level_box.upgrade()?), Self::TableLevelBox(table_level_box) => { LayoutBox::TableLevelBox(table_level_box.upgrade()?) }, Self::TaffyItemBox(taffy_item_box) => { LayoutBox::TaffyItemBox(taffy_item_box.upgrade()?) }, Self::Text(text_run) => LayoutBox::Text(text_run.upgrade()?), }) } } /// A wrapper for [`InnerDOMLayoutData`]. This is necessary to give the entire data /// structure interior mutability, as we will need to mutate the layout data of /// non-mutable DOM nodes. #[derive(Default, MallocSizeOf)] pub struct DOMLayoutData(AtomicRefCell<InnerDOMLayoutData>); // The implementation of this trait allows the data to be stored in the DOM. impl LayoutDataTrait for DOMLayoutData {} impl GenericLayoutDataTrait for DOMLayoutData { fn as_any(&self) -> &dyn std::any::Any { self } } pub struct BoxSlot<'dom> { pub(crate) slot: ArcRefCell<Option<LayoutBox>>, pub(crate) marker: PhantomData<&'dom ()>, } impl From<ArcRefCell<Option<LayoutBox>>> for BoxSlot<'_> { fn from(slot: ArcRefCell<Option<LayoutBox>>) -> Self { Self { slot, marker: PhantomData, } } } /// A mutable reference to a `LayoutBox` stored in a DOM element. impl BoxSlot<'_> { pub(crate) fn set(self, layout_box: LayoutBox) { layout_box.attached_to_tree(layout_box.downgrade()); *self.slot.borrow_mut() = Some(layout_box); } pub(crate) fn take_layout_box(&self) -> Option<LayoutBox> { self.slot.borrow_mut().take() } /// Call [`Self::take_layout_box`] and try to unwrap it into a [`TextRun`], returning `None` if /// the slot is empty or it does not contain a [`TextRun`]. Note that this will *always* clear /// the [`BoxSlot`]. pub(crate) fn take_layout_box_as_text_run(&self) -> Option<ArcRefCell<TextRun>> { match self.take_layout_box()? { LayoutBox::Text(old_text_run) => Some(old_text_run), _ => None, } } } impl Drop for BoxSlot<'_> { fn drop(&mut self) { if !std::thread::panicking() { assert!(self.slot.borrow().is_some(), "failed to set a layout box"); } } } pub(crate) trait NodeExt<'dom> { /// Returns the relevant data wrapping into respective struct and its size in pixels. fn as_image(&self) -> Option<(ImageInfo, PhysicalSize<f64>)>; fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>; fn as_iframe(&self) -> Option<IFrameInfo>; fn as_video(&self) -> Option<(VideoInfo, Option<PhysicalSize<f64>>)>; fn as_svg(&self) -> Option<SVGElementData<'dom>>; fn as_typeless_object_with_data_attribute(&self) -> Option<String>; fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>; fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>; fn inner_layout_data_mut(&self) -> Option<AtomicRefMut<'dom, InnerDOMLayoutData>>; fn box_slot(&self) -> BoxSlot<'dom>; /// Remove boxes for the element itself, and all of its pseudo-element boxes. fn unset_all_boxes(&self); /// Clear laid out `Fragment`s and dirty `Fragment` caches for all of this node's boxes, /// ensuring that the next `FragmentTree` layout that happens at this node is complete. fn clear_fragments_and_dirty_fragment_caches(&self); /// Clear laid out `Fragment`s and dirty `Fragment` caches for all this node's boxes and /// descendant's boxes, ensuring that the next `FragmentTree` layout that happens at and /// below this node is complete. fn clear_fragments_and_dirty_fragment_caches_recursively(&self); /// Clear laid out `Fragment`s and dirty `Fragment` caches for all this node's descendant's /// boxes. fn clear_fragments_and_dirty_fragment_caches_of_descendants(&self); /// Returns the [`NodeRenderingType`] for this [`LayoutNode`] which describes whether /// the node is being rendered, delegating rendering, or not being rendered at all /// based on whether it has a [`LayoutBox`] and what kind. fn rendering_type(&self) -> NodeRenderingType; fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>; fn with_layout_box_base(&self, callback: impl FnMut(&LayoutBoxBase)); fn with_layout_box_base_including_pseudos(&self, callback: impl FnMut(&LayoutBoxBase)); fn repair_style(&self, context: &SharedStyleContext); /// Whether or not this node isolates downward flowing box tree rebuild damage and /// fragment tree layout cache damage. Roughly, this corresponds to independent /// formatting context boundaries. /// /// - The node's boxes themselves will be rebuilt, but not the descendant node's /// boxes. /// - The node's fragment tree layout will be rebuilt, not the descendent node's /// fragment tree layout cache. /// /// When this node has no box yet, `false` is returned. fn isolates_damage_for_damage_propagation(&self) -> bool; /// Try to re-run box tree reconstruction from this point. This can succeed if the /// node itself is still valid and isolates box tree damage from ancestors (for /// instance, if it starts an independent formatting context). **Note:** This assumes /// that no ancestors have box damage. /// /// Returns `true` if box tree reconstruction was sucessful and `false` otherwise. fn rebuild_box_tree_from_independent_formatting_context( &self, layout_context: &LayoutContext, ) -> bool; /// Whether or not the style of this node indicates that it should be absolutely /// positioned. fn is_absolutely_positioned(&self) -> bool; } impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> { fn as_image(&self) -> Option<(ImageInfo, PhysicalSize<f64>)> { let (resource, metadata) = self.image_data()?; let width = metadata.map(|metadata| metadata.width).unwrap_or_default(); let height = metadata.map(|metadata| metadata.height).unwrap_or_default(); let (mut width, mut height) = (width as f64, height as f64); // Take `image_density` into account for calculating the size in pixels for images. if let Some(density) = self.image_density().filter(|density| *density != 1.) { width /= density; height /= density; } Some(( ImageInfo { image: resource, showing_broken_image_icon: self.showing_broken_image_icon(), url: self.image_url(), }, PhysicalSize::new(width, height), )) } fn as_svg(&self) -> Option<SVGElementData<'dom>> { self.svg_data() } fn as_video(&self) -> Option<(VideoInfo, Option<PhysicalSize<f64>>)> { let data = self.media_data()?; let natural_size = if let Some(frame) = data.current_frame { Some(PhysicalSize::new(frame.width.into(), frame.height.into())) } else { data.metadata .map(|meta| PhysicalSize::new(meta.width.into(), meta.height.into())) }; Some(( VideoInfo { image_key: data.current_frame.map(|frame| frame.image_key), poster_url: data.poster_url, }, natural_size, )) } fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> { let canvas_data = self.canvas_data()?; let source = canvas_data.image_key; Some(( CanvasInfo { source }, PhysicalSize::new(canvas_data.width.into(), canvas_data.height.into()), )) } fn as_iframe(&self) -> Option<IFrameInfo> { match (self.iframe_pipeline_id(), self.iframe_browsing_context_id()) { (Some(pipeline_id), Some(browsing_context_id)) => Some(IFrameInfo { pipeline_id, browsing_context_id, }), _ => None, } } fn as_typeless_object_with_data_attribute(&self) -> Option<String> { if self.type_id() != Some(ScriptLayoutNodeType::Element( LayoutElementType::HTMLObjectElement, )) { return None; } // TODO: This is the what the legacy layout system did, but really if Servo // supports any `<object>` that's an image, it should support those with URLs // and `type` attributes with image mime types. let element = self.as_element()?; if element.attribute(&ns!(), &local_name!("type")).is_some() { return None; } element .attribute_as_str(&ns!(), &local_name!("data")) .map(|string| string.to_owned()) } fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> { if self.layout_data().is_none() { self.initialize_layout_data::<DOMLayoutData>(); } self.layout_data() .unwrap() .as_any() .downcast_ref::<DOMLayoutData>() .unwrap() .0 .borrow_mut() } fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> { self.layout_data().map(|data| { data.as_any() .downcast_ref::<DOMLayoutData>() .unwrap() .0 .borrow() }) } fn inner_layout_data_mut(&self) -> Option<AtomicRefMut<'dom, InnerDOMLayoutData>> { self.layout_data().map(|data| { data.as_any() .downcast_ref::<DOMLayoutData>() .unwrap() .0 .borrow_mut() }) } fn box_slot(&self) -> BoxSlot<'dom> { let pseudo_element_chain = self.pseudo_element_chain(); let Some(primary) = pseudo_element_chain.primary else { return self.ensure_inner_layout_data().self_box.clone().into(); }; let Some(secondary) = pseudo_element_chain.secondary else { let primary_layout_data = self .ensure_inner_layout_data() .create_pseudo_layout_data(primary); return primary_layout_data.borrow().self_box.clone().into(); }; // It's *very* important that this not borrow the element's main // `InnerLayoutData`. Primary pseudo-elements are processed at the same recursion // level as the main data, so the `BoxSlot` is created sequentially with other // primary pseudo-elements and the element itself. The secondary pseudo-element is // one level deep, so could be happening in parallel with the primary // pseudo-elements or main element layout. let primary_layout_data = self .inner_layout_data() .expect("Should already have element InnerLayoutData here.") .pseudo_layout_data(primary) .expect("Should already have primary pseudo-element InnerLayoutData here"); let secondary_layout_data = primary_layout_data .borrow_mut() .create_pseudo_layout_data(secondary); secondary_layout_data.borrow().self_box.clone().into() } fn unset_all_boxes(&self) { let mut layout_data = self.ensure_inner_layout_data(); *layout_data.self_box.borrow_mut() = None; layout_data.pseudo_boxes.clear(); // Stylo already takes care of removing all layout data // for DOM descendants of elements with `display: none`. } fn clear_fragments_and_dirty_fragment_caches_recursively(&self) { self.clear_fragments_and_dirty_fragment_caches(); self.clear_fragments_and_dirty_fragment_caches_of_descendants(); } fn clear_fragments_and_dirty_fragment_caches(&self) { self.with_layout_box_base_including_pseudos(|base| { base.clear_fragments_and_dirty_fragment_cache() }); } fn clear_fragments_and_dirty_fragment_caches_of_descendants(&self) { for child in self.flat_tree_children() { child.clear_fragments_and_dirty_fragment_caches_recursively(); } } fn rendering_type(&self) -> NodeRenderingType { let Some(layout_data) = self.inner_layout_data() else { return NodeRenderingType::NotRendered; }; match &*layout_data.self_box.borrow() { Some(LayoutBox::DisplayContents(..)) => NodeRenderingType::DelegatesRendering, Some(..) => NodeRenderingType::Rendered, None => NodeRenderingType::NotRendered, } } fn with_layout_box_base(&self, callback: impl FnMut(&LayoutBoxBase)) { if let Some(inner_layout_data) = self.inner_layout_data() { inner_layout_data.with_layout_box_base(callback); } } fn with_layout_box_base_including_pseudos(&self, callback: impl FnMut(&LayoutBoxBase)) { if let Some(inner_layout_data) = self.inner_layout_data() { inner_layout_data.with_layout_box_base_including_pseudos(callback); } } fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> { let Some(layout_data) = self.inner_layout_data() else { return vec![]; }; match pseudo_element { Some(pseudo_element) => layout_data .pseudo_layout_data(pseudo_element) .map(|pseudo_layout_data| pseudo_layout_data.borrow().fragments()) .unwrap_or_default(), None => layout_data.fragments(), } } fn repair_style(&self, context: &SharedStyleContext) { if let Some(layout_data) = self.inner_layout_data() { layout_data.repair_style(self, context); } } fn isolates_damage_for_damage_propagation(&self) -> bool { // Do not run incremental box and fragment tree layout at the `<body>` or root element as // there is some special processing that must happen for these elements and it currently // only happens when doing a full box tree construction traversal. if self.as_element().is_some_and(|element| { element.is_body_element_of_html_element_root() || element.is_root() }) { return false; } let Some(inner_layout_data) = self.inner_layout_data() else { return false; }; let self_box = inner_layout_data.self_box.borrow(); let Some(self_box) = &*self_box else { return false; }; match self_box { LayoutBox::DisplayContents(..) => false, LayoutBox::BlockLevel(block_level) => matches!( &*block_level.borrow(), BlockLevelBox::Independent(..) | BlockLevelBox::OutOfFlowFloatBox(..) | BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(..) ), LayoutBox::InlineLevel(inline_level) => matches!( inline_level, InlineItem::OutOfFlowAbsolutelyPositionedBox(..) | InlineItem::Atomic(..) ), LayoutBox::FlexLevel(..) => true, LayoutBox::TableLevelBox(table_level_box) => matches!( table_level_box, TableLevelBox::Cell(..) | TableLevelBox::Caption(..), ), LayoutBox::TaffyItemBox(..) => true, LayoutBox::Text(..) => unreachable!("An element should never be a text node"), } } fn rebuild_box_tree_from_independent_formatting_context( &self, layout_context: &LayoutContext, ) -> bool { // Do not run incremental box tree layout at the `<body>` or root element as there // is some special processing that must happen for these elements and it currently // only happens when doing a full box tree construction traversal. if self.as_element().is_some_and(|element| { element.is_body_element_of_html_element_root() || element.is_root() }) { return false; } let layout_box = { let Some(mut inner_layout_data) = self.inner_layout_data_mut() else { return false; }; inner_layout_data.pseudo_boxes.clear(); inner_layout_data.self_box.clone() }; let layout_box = layout_box.borrow(); let Some(layout_box) = &*layout_box else { return false; }; let info = NodeAndStyleInfo::new(*self, self.style(&layout_context.style_context)); let box_style = info.style.get_box(); let Display::GeneratingBox(display) = box_style.display.into() else { return false; }; let contents = || { assert!( self.pseudo_element_chain().is_empty(), "Shouldn't try to rebuild box tree from a pseudo-element" ); Contents::for_element(info.node, layout_context) }; match layout_box { LayoutBox::DisplayContents(..) => false, LayoutBox::BlockLevel(block_level) => { let mut block_level = block_level.borrow_mut(); match &mut *block_level { BlockLevelBox::Independent(independent_formatting_context) => { let DisplayGeneratingBox::OutsideInside { outside: DisplayOutside::Block, inside: display_inside, } = display else { return false; }; if !matches!( BlockLevelCreator::new_for_inflow_block_level_element( &info, display_inside, contents(), independent_formatting_context.propagated_data, ), BlockLevelCreator::Independent { .. } ) { return false; } independent_formatting_context.rebuild(layout_context, &info); true }, BlockLevelBox::OutOfFlowFloatBox(float_box) => { if !info.style.clone_float().is_floating() { return false; } float_box.contents.rebuild(layout_context, &info); true }, BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { // Even if absolute positioning blockifies the outer display type, if the // original display was inline-level, then the box needs to be handled as // an inline-level in order to compute the static position correctly. // See `BlockContainerBuilder::handle_absolutely_positioned_element()`. if !info.style.clone_position().is_absolutely_positioned() || box_style.original_display.outside() != StyloDisplayOutside::Block { return false; } positioned_box .borrow_mut() .context .rebuild(layout_context, &info); true }, _ => false, } }, LayoutBox::InlineLevel(inline_level) => match inline_level { InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => { if !info.style.clone_position().is_absolutely_positioned() { return false; } positioned_box .borrow_mut() .context .rebuild(layout_context, &info); true }, InlineItem::Atomic(atomic_box, _, _) => { let flags = match contents() { Contents::NonReplaced(_) => FragmentFlags::empty(), Contents::Replaced(_) => FragmentFlags::IS_REPLACED, Contents::Widget(_) => FragmentFlags::IS_WIDGET, }; if !info.style.is_atomic_inline_level(flags) { return false; } atomic_box.borrow_mut().rebuild(layout_context, &info); true }, _ => false, }, LayoutBox::FlexLevel(flex_level_box) => { let mut flex_level_box = flex_level_box.borrow_mut(); match &mut *flex_level_box { FlexLevelBox::FlexItem(flex_item_box) => { if info.style.clone_position().is_absolutely_positioned() || flex_item_box.style().clone_order() != info.style.clone_order() { return false; } flex_item_box .independent_formatting_context .rebuild(layout_context, &info) }, FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { if !info.style.clone_position().is_absolutely_positioned() { return false; } positioned_box .borrow_mut() .context .rebuild(layout_context, &info); }, } true }, LayoutBox::TableLevelBox(table_level_box) => match table_level_box { TableLevelBox::Caption(caption) => { if display != DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableCaption) { return false; } caption.borrow_mut().context.rebuild(layout_context, &info); true }, TableLevelBox::Cell(table_cell) => { if display != DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableCell) { return false; } table_cell .borrow_mut() .context .rebuild(layout_context, &info); true }, _ => false, }, LayoutBox::TaffyItemBox(..) => false, LayoutBox::Text(..) => unreachable!("An element should never be a text node"), } } fn is_absolutely_positioned(&self) -> bool { self.as_element().is_some_and(|element| { element .element_data() .styles .primary() .clone_position() .is_absolutely_positioned() }) } }