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