1use std::fmt::{Debug, Formatter};
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
8
9use app_units::Au;
10use atomic_refcell::{AtomicRef, AtomicRefCell};
11use euclid::Point2D;
12use layout_api::LayoutDamage;
13use malloc_size_of_derive::MallocSizeOf;
14use servo_arc::Arc as ServoArc;
15use style::computed_values::position::T as Position;
16use style::logical_geometry::WritingMode;
17use style::properties::ComputedValues;
18use style::values::specified::align::AlignFlags;
19use style_traits::CSSPixel;
20
21use crate::context::LayoutContext;
22use crate::dom::{LayoutBox, WeakLayoutBox};
23use crate::flow::CollapsibleWithParentStartMargin;
24use crate::formatting_contexts::Baselines;
25use crate::fragment_tree::{
26 BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, FragmentStatus,
27 SpecificLayoutInfo,
28};
29use crate::geom::LogicalSides1D;
30use crate::positioned::{PositioningContext, relative_adjustement};
31use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult, SizeConstraint};
32use crate::traversal::ElementDamageSet;
33use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize};
34
35#[derive(MallocSizeOf)]
42pub(crate) struct LayoutBoxBase {
43 pub base_fragment_info: BaseFragmentInfo,
44 pub style: ServoArc<ComputedValues>,
45 pub cached_inline_content_size:
46 AtomicRefCell<Option<Box<(SizeConstraint, InlineContentSizesResult)>>>,
47 pub outer_inline_content_sizes_depend_on_content: AtomicBool,
48
49 cached_layout_result: AtomicRefCell<Option<LayoutResultAndInputs>>,
53
54 cached_layout_result_dirty: AtomicBool,
58
59 subtree_size: AtomicUsize,
62
63 pub fragments: AtomicRefCell<Vec<Fragment>>,
64 pub parent_box: Option<WeakLayoutBox>,
65}
66
67impl LayoutBoxBase {
68 pub(crate) fn new(
69 base_fragment_info: BaseFragmentInfo,
70 style: ServoArc<ComputedValues>,
71 ) -> Self {
72 Self {
73 base_fragment_info,
74 style,
75 cached_inline_content_size: AtomicRefCell::default(),
76 outer_inline_content_sizes_depend_on_content: AtomicBool::new(true),
77 cached_layout_result: AtomicRefCell::default(),
78 cached_layout_result_dirty: AtomicBool::default(),
79 subtree_size: AtomicUsize::default(),
80 fragments: AtomicRefCell::default(),
81 parent_box: None,
82 }
83 }
84
85 pub(crate) fn set_subtree_size(&self, size: usize) {
88 self.subtree_size.store(size, Ordering::Relaxed);
89 }
90
91 pub(crate) fn subtree_size(&self) -> usize {
92 self.subtree_size.load(Ordering::Relaxed)
93 }
94
95 pub(crate) fn inline_content_sizes(
98 &self,
99 layout_context: &LayoutContext,
100 constraint_space: &ConstraintSpace,
101 layout_box: &impl ComputeInlineContentSizes,
102 ) -> InlineContentSizesResult {
103 let mut cache = self.cached_inline_content_size.borrow_mut();
104 if let Some(cached_inline_content_size) = cache.as_ref() {
105 let (previous_cb_block_size, result) = **cached_inline_content_size;
106 if !result.depends_on_block_constraints ||
107 previous_cb_block_size == constraint_space.block_size
108 {
109 return result;
110 }
111 }
113
114 let result =
115 layout_box.compute_inline_content_sizes_with_fixup(layout_context, constraint_space);
116 *cache = Some(Box::new((constraint_space.block_size, result)));
117 result
118 }
119
120 pub(crate) fn fragments(&self) -> AtomicRef<'_, Vec<Fragment>> {
121 self.fragments.borrow()
122 }
123
124 pub(crate) fn add_fragment(&self, fragment: Fragment) {
125 self.fragments.borrow_mut().push(fragment);
126 }
127
128 pub(crate) fn set_fragment(&self, fragment: Fragment) {
129 *self.fragments.borrow_mut() = vec![fragment];
130 }
131
132 pub(crate) fn clear_fragments(&self) {
133 self.fragments.borrow_mut().clear();
134 }
135
136 pub(crate) fn clear_fragments_and_dirty_fragment_cache(&self) {
139 self.clear_fragments();
140 self.cached_layout_result_dirty
141 .store(true, Ordering::Relaxed);
142 }
143
144 pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
145 self.style = new_style.clone();
146 for fragment in self.fragments.borrow_mut().iter_mut() {
147 if let Some(base) = fragment.base() {
148 base.repair_style(new_style);
149 }
150 }
151 }
152
153 #[expect(unused)]
154 pub(crate) fn parent_box(&self) -> Option<LayoutBox> {
155 self.parent_box.as_ref().and_then(WeakLayoutBox::upgrade)
156 }
157
158 pub(crate) fn invalidate_caches(&self, damage_set: &ElementDamageSet) -> bool {
165 self.cached_layout_result_dirty
166 .store(true, Ordering::Relaxed);
167 if !damage_set.on_element.is_empty() ||
168 damage_set
169 .from_children
170 .contains(LayoutDamage::RecomputeInlineContentSizes)
171 {
172 *self.cached_inline_content_size.borrow_mut() = None;
173 }
174
175 !self.base_fragment_info.is_anonymous() &&
186 self.outer_inline_content_sizes_depend_on_content
187 .load(Ordering::Relaxed)
188 }
189
190 pub(crate) fn invalidate_caches_for_fragment_tree_layout(
197 &self,
198 damage_set: &ElementDamageSet,
199 ) -> bool {
200 self.clear_fragments();
201 self.invalidate_caches(damage_set)
202 }
203
204 pub(crate) fn cached_independent_formatting_context_layout_if_applicable(
205 &self,
206 positioning_context: &mut PositioningContext,
207 containing_block_for_children: &ContainingBlock<'_>,
208 ) -> Option<IndependentFormattingContextLayoutResult> {
209 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
210 return None;
211 }
212
213 let cache = self.cached_layout_result.borrow();
214 let Some(LayoutResultAndInputs::IndependentFormattingContext(cache)) = &*cache else {
215 return None;
216 };
217
218 let cache = &**cache;
219 if cache.containing_block_for_children_size.inline !=
220 containing_block_for_children.size.inline
221 {
222 return None;
223 }
224 if cache.containing_block_for_children_size.block !=
225 containing_block_for_children.size.block &&
226 cache.result.depends_on_block_constraints
227 {
228 return None;
229 }
230
231 positioning_context.append(cache.positioning_context.clone());
232 Some(cache.result.clone())
233 }
234
235 pub(crate) fn cache_independent_formatting_context_layout(
236 &self,
237 containing_block_for_children: &ContainingBlock<'_>,
238 child_positioning_context: &PositioningContext,
239 result: &IndependentFormattingContextLayoutResult,
240 ) {
241 self.cached_layout_result_dirty
242 .store(false, Ordering::Relaxed);
243 *self.cached_layout_result.borrow_mut() =
244 Some(LayoutResultAndInputs::IndependentFormattingContext(
245 Box::new(IndependentFormattingContextLayoutResultAndInputs {
246 result: result.clone(),
247 positioning_context: child_positioning_context.clone(),
248 containing_block_for_children_size: containing_block_for_children.size.clone(),
249 }),
250 ));
251 }
252
253 pub(crate) fn cached_same_formatting_context_block_if_applicable(
254 &self,
255 containing_block: &ContainingBlock,
256 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
257 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
258 has_inline_parent: bool,
259 ) -> Option<Arc<BoxFragment>> {
260 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
261 return None;
262 }
263
264 let mut cached_layout_result = self.cached_layout_result.borrow_mut();
265 let Some(LayoutResultAndInputs::SameFormattingContextBlock(result)) =
266 &mut *cached_layout_result
267 else {
268 return None;
269 };
270
271 if result.containing_block_size != containing_block.size ||
272 result.containing_block_writing_mode != containing_block.style.writing_mode ||
273 result.containing_block_justify_items !=
274 containing_block.style.clone_justify_items().computed.0.0 ||
275 result.collapsible_with_parent_start_margin != collapsible_with_parent_start_margin ||
276 result.ignore_block_margins_for_stretch != ignore_block_margins_for_stretch ||
277 result.has_inline_parent != has_inline_parent
278 {
279 return None;
280 }
281
282 let fragment = result.result.fragment.clone();
283 {
284 let mut origin = result.result.original_offset;
285 if self.style.clone_position() == Position::Relative {
286 origin += relative_adjustement(&self.style, containing_block)
287 .to_physical_vector(containing_block.style.writing_mode)
288 }
289 fragment.base.set_rect_origin(origin);
290 }
291
292 Some(fragment)
293 }
294
295 pub(crate) fn cache_same_formatting_context_block_layout(
296 &self,
297 containing_block: &ContainingBlock,
298 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
299 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
300 has_inline_parent: bool,
301 fragment: Arc<BoxFragment>,
302 ) {
303 let mut original_offset;
304 {
305 original_offset = fragment.content_rect().origin;
306 if self.style.clone_position() == Position::Relative {
307 original_offset -= relative_adjustement(&self.style, containing_block)
308 .to_physical_vector(containing_block.style.writing_mode)
309 }
310 }
311
312 self.cached_layout_result_dirty
313 .store(false, Ordering::Relaxed);
314 *self.cached_layout_result.borrow_mut() =
315 Some(LayoutResultAndInputs::SameFormattingContextBlock(Box::new(
316 SameFormattingContextBlockLayoutResultAndInputs {
317 result: SameFormattingContextBlockLayoutResult {
318 fragment,
319 original_offset,
320 },
321 containing_block_size: containing_block.size.clone(),
322 containing_block_writing_mode: containing_block.style.writing_mode,
323 containing_block_justify_items: containing_block
324 .style
325 .clone_justify_items()
326 .computed
327 .0
328 .0,
329 collapsible_with_parent_start_margin,
330 ignore_block_margins_for_stretch,
331 has_inline_parent,
332 },
333 )));
334 }
335
336 pub(crate) fn clear_scrollable_overflow_all_on_fragments(&self) {
337 for fragment in self.fragments.borrow().iter() {
338 fragment.clear_scrollable_overflow();
339 }
340 }
341
342 pub(crate) fn mark_fragments_as_descendants_changed(&self) {
343 for fragment in self.fragments.borrow().iter() {
344 if let Some(base) = fragment.base() {
345 base.set_status(FragmentStatus::OnlyDescendantsChanged);
346 }
347 }
348 }
349}
350
351impl Debug for LayoutBoxBase {
352 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
353 f.debug_struct("LayoutBoxBase").finish()
354 }
355}
356
357#[derive(MallocSizeOf)]
358pub(crate) enum LayoutResultAndInputs {
359 IndependentFormattingContext(Box<IndependentFormattingContextLayoutResultAndInputs>),
360 SameFormattingContextBlock(Box<SameFormattingContextBlockLayoutResultAndInputs>),
361}
362
363#[derive(Clone, MallocSizeOf)]
364pub(crate) struct IndependentFormattingContextLayoutResult {
365 pub fragments: Vec<Fragment>,
366
367 pub content_block_size: Au,
369
370 pub collapsible_margins_in_children: CollapsedBlockMargins,
373
374 pub content_inline_size_for_table: Option<Au>,
378
379 pub baselines: Baselines,
383
384 pub depends_on_block_constraints: bool,
386
387 pub specific_layout_info: Option<SpecificLayoutInfo>,
389}
390
391#[derive(MallocSizeOf)]
394pub(crate) struct IndependentFormattingContextLayoutResultAndInputs {
395 pub result: IndependentFormattingContextLayoutResult,
397
398 pub containing_block_for_children_size: ContainingBlockSize,
401
402 pub positioning_context: PositioningContext,
405}
406
407#[derive(Clone, MallocSizeOf)]
408pub(crate) struct SameFormattingContextBlockLayoutResult {
409 #[conditional_malloc_size_of]
410 pub fragment: Arc<BoxFragment>,
411 original_offset: Point2D<Au, CSSPixel>,
412}
413
414#[derive(MallocSizeOf)]
417pub(crate) struct SameFormattingContextBlockLayoutResultAndInputs {
418 pub result: SameFormattingContextBlockLayoutResult,
419 pub containing_block_size: ContainingBlockSize,
421 pub containing_block_writing_mode: WritingMode,
423 pub containing_block_justify_items: AlignFlags,
425 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
428 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
430 has_inline_parent: bool,
432}