layout/
layout_box_base.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::fmt::{Debug, Formatter};
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use app_units::Au;
9use atomic_refcell::AtomicRefCell;
10use layout_api::LayoutDamage;
11use malloc_size_of_derive::MallocSizeOf;
12use servo_arc::Arc;
13use style::properties::ComputedValues;
14
15use crate::context::LayoutContext;
16use crate::dom::{LayoutBox, WeakLayoutBox};
17use crate::formatting_contexts::Baselines;
18use crate::fragment_tree::{BaseFragmentInfo, CollapsedBlockMargins, Fragment, SpecificLayoutInfo};
19use crate::positioned::PositioningContext;
20use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult, SizeConstraint};
21use crate::{ConstraintSpace, ContainingBlockSize};
22
23/// A box tree node that handles containing information about style and the original DOM
24/// node or pseudo-element that it is based on. This also handles caching of layout values
25/// such as the inline content sizes to avoid recalculating these values during layout
26/// passes.
27///
28/// In the future, this will hold layout results to support incremental layout.
29#[derive(MallocSizeOf)]
30pub(crate) struct LayoutBoxBase {
31    pub base_fragment_info: BaseFragmentInfo,
32    pub style: Arc<ComputedValues>,
33    pub cached_inline_content_size:
34        AtomicRefCell<Option<Box<(SizeConstraint, InlineContentSizesResult)>>>,
35    pub outer_inline_content_sizes_depend_on_content: AtomicBool,
36    pub cached_layout_result: AtomicRefCell<Option<Box<CacheableLayoutResultAndInputs>>>,
37    pub fragments: AtomicRefCell<Vec<Fragment>>,
38    pub parent_box: Option<WeakLayoutBox>,
39}
40
41impl LayoutBoxBase {
42    pub(crate) fn new(base_fragment_info: BaseFragmentInfo, style: Arc<ComputedValues>) -> Self {
43        Self {
44            base_fragment_info,
45            style,
46            cached_inline_content_size: AtomicRefCell::default(),
47            outer_inline_content_sizes_depend_on_content: AtomicBool::new(true),
48            cached_layout_result: AtomicRefCell::default(),
49            fragments: AtomicRefCell::default(),
50            parent_box: None,
51        }
52    }
53
54    /// Get the inline content sizes of a box tree node that extends this [`LayoutBoxBase`], fetch
55    /// the result from a cache when possible.
56    pub(crate) fn inline_content_sizes(
57        &self,
58        layout_context: &LayoutContext,
59        constraint_space: &ConstraintSpace,
60        layout_box: &impl ComputeInlineContentSizes,
61    ) -> InlineContentSizesResult {
62        let mut cache = self.cached_inline_content_size.borrow_mut();
63        if let Some(cached_inline_content_size) = cache.as_ref() {
64            let (previous_cb_block_size, result) = **cached_inline_content_size;
65            if !result.depends_on_block_constraints ||
66                previous_cb_block_size == constraint_space.block_size
67            {
68                return result;
69            }
70            // TODO: Should we keep multiple caches for various block sizes?
71        }
72
73        let result =
74            layout_box.compute_inline_content_sizes_with_fixup(layout_context, constraint_space);
75        *cache = Some(Box::new((constraint_space.block_size, result)));
76        result
77    }
78
79    pub(crate) fn fragments(&self) -> Vec<Fragment> {
80        self.fragments.borrow().clone()
81    }
82
83    pub(crate) fn add_fragment(&self, fragment: Fragment) {
84        self.fragments.borrow_mut().push(fragment);
85    }
86
87    pub(crate) fn set_fragment(&self, fragment: Fragment) {
88        *self.fragments.borrow_mut() = vec![fragment];
89    }
90
91    pub(crate) fn clear_fragments(&self) {
92        self.fragments.borrow_mut().clear();
93    }
94
95    pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
96        self.style = new_style.clone();
97        for fragment in self.fragments.borrow_mut().iter_mut() {
98            if let Some(mut base) = fragment.base_mut() {
99                base.repair_style(new_style);
100            }
101        }
102    }
103
104    #[expect(unused)]
105    pub(crate) fn parent_box(&self) -> Option<LayoutBox> {
106        self.parent_box.as_ref().and_then(WeakLayoutBox::upgrade)
107    }
108
109    pub(crate) fn add_damage(
110        &self,
111        element_damage: LayoutDamage,
112        damage_from_children: LayoutDamage,
113    ) -> LayoutDamage {
114        self.clear_fragments();
115        *self.cached_layout_result.borrow_mut() = None;
116
117        if !element_damage.is_empty() ||
118            damage_from_children.contains(LayoutDamage::RECOMPUTE_INLINE_CONTENT_SIZES)
119        {
120            *self.cached_inline_content_size.borrow_mut() = None;
121        }
122
123        let mut damage_for_parent = element_damage | damage_from_children;
124
125        // When a block container has a mix of inline-level and block-level contents, the
126        // inline-level ones are wrapped inside an anonymous block associated with the
127        // block container. The anonymous block has an `auto` size, so its intrinsic
128        // contribution depends on content, but it can't affect the intrinsic size of
129        // ancestors if the block container is sized extrinsically.
130        //
131        // If the intrinsic contributions of this node depend on content, we will need to
132        // clear the cached intrinsic sizes of the parent. But if the contributions are
133        // purely extrinsic, then the intrinsic sizes of the ancestors won't be affected,
134        // and we can keep the cache.
135        damage_for_parent.set(
136            LayoutDamage::RECOMPUTE_INLINE_CONTENT_SIZES,
137            !element_damage.is_empty() ||
138                (!self.base_fragment_info.is_anonymous() &&
139                    self.outer_inline_content_sizes_depend_on_content
140                        .load(Ordering::Relaxed)),
141        );
142
143        damage_for_parent
144    }
145}
146
147impl Debug for LayoutBoxBase {
148    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
149        f.debug_struct("LayoutBoxBase").finish()
150    }
151}
152
153#[derive(Clone, MallocSizeOf)]
154pub(crate) struct CacheableLayoutResult {
155    pub fragments: Vec<Fragment>,
156
157    /// <https://drafts.csswg.org/css2/visudet.html#root-height>
158    pub content_block_size: Au,
159
160    /// If this layout is for a block container, this tracks the collapsable size
161    /// of start and end margins and whether or not the block container collapsed through.
162    pub collapsible_margins_in_children: CollapsedBlockMargins,
163
164    /// The contents of a table may force it to become wider than what we would expect
165    /// from 'width' and 'min-width'. This is the resulting inline content size,
166    /// or None for non-table layouts.
167    pub content_inline_size_for_table: Option<Au>,
168
169    /// The offset of the last inflow baseline of this layout in the content area, if
170    /// there was one. This is used to propagate baselines to the ancestors of `display:
171    /// inline-block`.
172    pub baselines: Baselines,
173
174    /// Whether or not this layout depends on the containing block size.
175    pub depends_on_block_constraints: bool,
176
177    /// Additional information of this layout that could be used by Javascripts and devtools.
178    pub specific_layout_info: Option<SpecificLayoutInfo>,
179}
180
181/// A collection of layout inputs and a cached layout result for a [`LayoutBoxBase`].
182#[derive(MallocSizeOf)]
183pub(crate) struct CacheableLayoutResultAndInputs {
184    /// The [`CacheableLayoutResult`] for this layout.
185    pub result: CacheableLayoutResult,
186
187    /// The [`ContainingBlockSize`] to use for this box's contents, but not
188    /// for the box itself.
189    pub containing_block_for_children_size: ContainingBlockSize,
190
191    /// A [`PositioningContext`] holding absolutely-positioned descendants
192    /// collected during the layout of this box.
193    pub positioning_context: PositioningContext,
194}