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().iter() {
147 fragment.repair_style(new_style);
148 }
149 }
150
151 #[expect(unused)]
152 pub(crate) fn parent_box(&self) -> Option<LayoutBox> {
153 self.parent_box.as_ref().and_then(WeakLayoutBox::upgrade)
154 }
155
156 pub(crate) fn invalidate_caches(&self, damage_set: &ElementDamageSet) -> bool {
163 self.cached_layout_result_dirty
164 .store(true, Ordering::Relaxed);
165 if !damage_set.on_element.is_empty() ||
166 damage_set
167 .from_children
168 .contains(LayoutDamage::RecomputeInlineContentSizes)
169 {
170 *self.cached_inline_content_size.borrow_mut() = None;
171 }
172
173 !self.base_fragment_info.is_anonymous() &&
184 self.outer_inline_content_sizes_depend_on_content
185 .load(Ordering::Relaxed)
186 }
187
188 pub(crate) fn invalidate_caches_for_fragment_tree_layout(
195 &self,
196 damage_set: &ElementDamageSet,
197 ) -> bool {
198 self.clear_fragments();
199 self.invalidate_caches(damage_set)
200 }
201
202 pub(crate) fn cached_independent_formatting_context_layout_if_applicable(
203 &self,
204 positioning_context: &mut PositioningContext,
205 containing_block_for_children: &ContainingBlock<'_>,
206 ) -> Option<IndependentFormattingContextLayoutResult> {
207 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
208 return None;
209 }
210
211 let cache = self.cached_layout_result.borrow();
212 let Some(LayoutResultAndInputs::IndependentFormattingContext(cache)) = &*cache else {
213 return None;
214 };
215
216 let cache = &**cache;
217 if cache.containing_block_for_children_size.inline !=
218 containing_block_for_children.size.inline
219 {
220 return None;
221 }
222 if cache.containing_block_for_children_size.block !=
223 containing_block_for_children.size.block &&
224 cache.result.depends_on_block_constraints
225 {
226 return None;
227 }
228
229 positioning_context.append(cache.positioning_context.clone());
230 Some(cache.result.clone())
231 }
232
233 pub(crate) fn cache_independent_formatting_context_layout(
234 &self,
235 containing_block_for_children: &ContainingBlock<'_>,
236 child_positioning_context: &PositioningContext,
237 result: &IndependentFormattingContextLayoutResult,
238 ) {
239 self.cached_layout_result_dirty
240 .store(false, Ordering::Relaxed);
241 *self.cached_layout_result.borrow_mut() =
242 Some(LayoutResultAndInputs::IndependentFormattingContext(
243 Box::new(IndependentFormattingContextLayoutResultAndInputs {
244 result: result.clone(),
245 positioning_context: child_positioning_context.clone(),
246 containing_block_for_children_size: containing_block_for_children.size.clone(),
247 }),
248 ));
249 }
250
251 pub(crate) fn cached_same_formatting_context_block_if_applicable(
252 &self,
253 containing_block: &ContainingBlock,
254 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
255 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
256 has_inline_parent: bool,
257 ) -> Option<Arc<BoxFragment>> {
258 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
259 return None;
260 }
261
262 let mut cached_layout_result = self.cached_layout_result.borrow_mut();
263 let Some(LayoutResultAndInputs::SameFormattingContextBlock(result)) =
264 &mut *cached_layout_result
265 else {
266 return None;
267 };
268
269 if result.containing_block_size != containing_block.size ||
270 result.containing_block_writing_mode != containing_block.style.writing_mode ||
271 result.containing_block_justify_items !=
272 containing_block.style.clone_justify_items().computed.0.0 ||
273 result.collapsible_with_parent_start_margin != collapsible_with_parent_start_margin ||
274 result.ignore_block_margins_for_stretch != ignore_block_margins_for_stretch ||
275 result.has_inline_parent != has_inline_parent
276 {
277 return None;
278 }
279
280 let fragment = result.result.fragment.clone();
281 {
282 let mut origin = result.result.original_offset;
283 if self.style.clone_position() == Position::Relative {
284 origin += relative_adjustement(&self.style, containing_block)
285 .to_physical_vector(containing_block.style.writing_mode)
286 }
287 fragment.base.set_rect_origin(origin);
288 }
289
290 Some(fragment)
291 }
292
293 pub(crate) fn cache_same_formatting_context_block_layout(
294 &self,
295 containing_block: &ContainingBlock,
296 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
297 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
298 has_inline_parent: bool,
299 fragment: Arc<BoxFragment>,
300 ) {
301 let mut original_offset;
302 {
303 original_offset = fragment.content_rect().origin;
304 if self.style.clone_position() == Position::Relative {
305 original_offset -= relative_adjustement(&self.style, containing_block)
306 .to_physical_vector(containing_block.style.writing_mode)
307 }
308 }
309
310 self.cached_layout_result_dirty
311 .store(false, Ordering::Relaxed);
312 *self.cached_layout_result.borrow_mut() =
313 Some(LayoutResultAndInputs::SameFormattingContextBlock(Box::new(
314 SameFormattingContextBlockLayoutResultAndInputs {
315 result: SameFormattingContextBlockLayoutResult {
316 fragment,
317 original_offset,
318 },
319 containing_block_size: containing_block.size.clone(),
320 containing_block_writing_mode: containing_block.style.writing_mode,
321 containing_block_justify_items: containing_block
322 .style
323 .clone_justify_items()
324 .computed
325 .0
326 .0,
327 collapsible_with_parent_start_margin,
328 ignore_block_margins_for_stretch,
329 has_inline_parent,
330 },
331 )));
332 }
333
334 pub(crate) fn clear_scrollable_overflow_all_on_fragments(&self) {
335 for fragment in self.fragments.borrow().iter() {
336 fragment.clear_scrollable_overflow();
337 }
338 }
339
340 pub(crate) fn mark_fragments_as_descendants_changed(&self) {
341 for fragment in self.fragments.borrow().iter() {
342 if let Some(base) = fragment.base() {
343 base.set_status(FragmentStatus::OnlyDescendantsChanged);
344 }
345 }
346 }
347}
348
349impl Debug for LayoutBoxBase {
350 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
351 f.debug_struct("LayoutBoxBase").finish()
352 }
353}
354
355#[derive(MallocSizeOf)]
356pub(crate) enum LayoutResultAndInputs {
357 IndependentFormattingContext(Box<IndependentFormattingContextLayoutResultAndInputs>),
358 SameFormattingContextBlock(Box<SameFormattingContextBlockLayoutResultAndInputs>),
359}
360
361#[derive(Clone, MallocSizeOf)]
362pub(crate) struct IndependentFormattingContextLayoutResult {
363 pub fragments: Vec<Fragment>,
364
365 pub content_block_size: Au,
367
368 pub collapsible_margins_in_children: CollapsedBlockMargins,
371
372 pub content_inline_size_for_table: Option<Au>,
376
377 pub baselines: Baselines,
381
382 pub depends_on_block_constraints: bool,
384
385 pub specific_layout_info: Option<SpecificLayoutInfo>,
387}
388
389#[derive(MallocSizeOf)]
392pub(crate) struct IndependentFormattingContextLayoutResultAndInputs {
393 pub result: IndependentFormattingContextLayoutResult,
395
396 pub containing_block_for_children_size: ContainingBlockSize,
399
400 pub positioning_context: PositioningContext,
403}
404
405#[derive(Clone, MallocSizeOf)]
406pub(crate) struct SameFormattingContextBlockLayoutResult {
407 #[conditional_malloc_size_of]
408 pub fragment: Arc<BoxFragment>,
409 original_offset: Point2D<Au, CSSPixel>,
410}
411
412#[derive(MallocSizeOf)]
415pub(crate) struct SameFormattingContextBlockLayoutResultAndInputs {
416 pub result: SameFormattingContextBlockLayoutResult,
417 pub containing_block_size: ContainingBlockSize,
419 pub containing_block_writing_mode: WritingMode,
421 pub containing_block_justify_items: AlignFlags,
423 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
426 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
428 has_inline_parent: bool,
430}