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