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 pub(crate) fn parent_box(&self) -> Option<LayoutBox> {
152 self.parent_box.as_ref().and_then(WeakLayoutBox::upgrade)
153 }
154
155 pub(crate) fn invalidate_caches(&self, damage_set: &ElementDamageSet) -> bool {
162 self.cached_layout_result_dirty
163 .store(true, Ordering::Relaxed);
164 if !damage_set.on_element.is_empty() ||
165 damage_set
166 .from_children
167 .contains(LayoutDamage::RecomputeInlineContentSizes)
168 {
169 *self.cached_inline_content_size.borrow_mut() = None;
170 }
171
172 !self.base_fragment_info.is_anonymous() &&
183 self.outer_inline_content_sizes_depend_on_content
184 .load(Ordering::Relaxed)
185 }
186
187 pub(crate) fn invalidate_caches_for_fragment_tree_layout(
194 &self,
195 damage_set: &ElementDamageSet,
196 ) -> bool {
197 self.clear_fragments();
198 self.invalidate_caches(damage_set)
199 }
200
201 pub(crate) fn cached_content_block_size_under_default_constraint_if_applicable(
202 &self,
203 inline_size: Au,
204 ) -> Option<Au> {
205 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
206 return None;
207 }
208
209 let cache = self.cached_layout_result.borrow();
210 let Some(LayoutResultAndInputs::IndependentFormattingContext(cache)) = &*cache else {
211 return None;
212 };
213
214 let cache = &**cache;
215 if !cache.applicable_for_inline_size(inline_size) {
216 return None;
217 }
218
219 cache.content_block_size_under_default_constraint
220 }
221
222 pub(crate) fn cached_independent_formatting_context_layout_if_applicable(
226 &self,
227 positioning_context: &mut PositioningContext,
228 containing_block_for_children: &ContainingBlock<'_>,
229 ) -> Result<IndependentFormattingContextLayoutResult, Option<Au>> {
230 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
231 return Err(None);
232 }
233
234 let cache = self.cached_layout_result.borrow();
235 let Some(LayoutResultAndInputs::IndependentFormattingContext(cache)) = &*cache else {
236 return Err(None);
237 };
238
239 let cache = &**cache;
240 if !cache.applicable_for_inline_size(containing_block_for_children.size.inline) {
241 return Err(None);
242 }
243 if !cache.applicable_for_block_size(containing_block_for_children.size.block) {
244 return Err(cache.content_block_size_under_default_constraint);
245 }
246
247 positioning_context.append(cache.positioning_context.clone());
248 Ok(cache.result.clone())
249 }
250
251 pub(crate) fn cache_independent_formatting_context_layout(
252 &self,
253 containing_block_for_children: &ContainingBlock<'_>,
254 child_positioning_context: &PositioningContext,
255 content_block_size_under_default_constraint: Option<Au>,
256 result: &IndependentFormattingContextLayoutResult,
257 ) {
258 self.cached_layout_result_dirty
259 .store(false, Ordering::Relaxed);
260 let mut result_and_inputs = IndependentFormattingContextLayoutResultAndInputs {
261 result: result.clone(),
262 positioning_context: child_positioning_context.clone(),
263 containing_block_for_children_size: containing_block_for_children.size.clone(),
264 content_block_size_under_default_constraint,
265 };
266 if content_block_size_under_default_constraint.is_none() &&
267 result_and_inputs.applicable_for_block_size(SizeConstraint::default())
268 {
269 result_and_inputs.content_block_size_under_default_constraint =
270 Some(result.content_block_size);
271 }
272 *self.cached_layout_result.borrow_mut() = Some(
273 LayoutResultAndInputs::IndependentFormattingContext(Box::new(result_and_inputs)),
274 );
275 }
276
277 pub(crate) fn cached_same_formatting_context_block_if_applicable(
278 &self,
279 containing_block: &ContainingBlock,
280 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
281 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
282 has_inline_parent: bool,
283 ) -> Option<Arc<BoxFragment>> {
284 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
285 return None;
286 }
287
288 let mut cached_layout_result = self.cached_layout_result.borrow_mut();
289 let Some(LayoutResultAndInputs::SameFormattingContextBlock(result)) =
290 &mut *cached_layout_result
291 else {
292 return None;
293 };
294
295 if result.containing_block_size != containing_block.size ||
296 result.containing_block_writing_mode != containing_block.style.writing_mode ||
297 result.containing_block_justify_items !=
298 containing_block.style.get_justify_items().computed.0.0 ||
299 result.collapsible_with_parent_start_margin != collapsible_with_parent_start_margin ||
300 result.ignore_block_margins_for_stretch != ignore_block_margins_for_stretch ||
301 result.has_inline_parent != has_inline_parent
302 {
303 return None;
304 }
305
306 let fragment = result.result.fragment.clone();
307 {
308 let mut origin = result.result.original_offset;
309 if self.style.get_box().position == Position::Relative {
310 origin += relative_adjustement(&self.style, containing_block)
311 .to_physical_vector(containing_block.style.writing_mode)
312 }
313 fragment.base.set_rect_origin(origin);
314 }
315
316 Some(fragment)
317 }
318
319 pub(crate) fn cache_same_formatting_context_block_layout(
320 &self,
321 containing_block: &ContainingBlock,
322 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
323 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
324 has_inline_parent: bool,
325 fragment: Arc<BoxFragment>,
326 ) {
327 let mut original_offset;
328 {
329 original_offset = fragment.content_rect().origin;
330 if self.style.get_box().position == Position::Relative {
331 original_offset -= relative_adjustement(&self.style, containing_block)
332 .to_physical_vector(containing_block.style.writing_mode)
333 }
334 }
335
336 self.cached_layout_result_dirty
337 .store(false, Ordering::Relaxed);
338 *self.cached_layout_result.borrow_mut() =
339 Some(LayoutResultAndInputs::SameFormattingContextBlock(Box::new(
340 SameFormattingContextBlockLayoutResultAndInputs {
341 result: SameFormattingContextBlockLayoutResult {
342 fragment,
343 original_offset,
344 },
345 containing_block_size: containing_block.size.clone(),
346 containing_block_writing_mode: containing_block.style.writing_mode,
347 containing_block_justify_items: containing_block
348 .style
349 .get_justify_items()
350 .computed
351 .0
352 .0,
353 collapsible_with_parent_start_margin,
354 ignore_block_margins_for_stretch,
355 has_inline_parent,
356 },
357 )));
358 }
359
360 pub(crate) fn clear_scrollable_overflow_all_on_fragments(&self) {
361 for fragment in self.fragments.borrow().iter() {
362 fragment.clear_scrollable_overflow();
363 }
364 }
365
366 pub(crate) fn mark_fragments_as_descendants_changed(&self) {
367 for fragment in self.fragments.borrow().iter() {
368 if let Some(base) = fragment.base() {
369 base.set_status(FragmentStatus::OnlyDescendantsChanged);
370 }
371 }
372 }
373}
374
375impl Debug for LayoutBoxBase {
376 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
377 f.debug_struct("LayoutBoxBase").finish()
378 }
379}
380
381#[derive(MallocSizeOf)]
382pub(crate) enum LayoutResultAndInputs {
383 IndependentFormattingContext(Box<IndependentFormattingContextLayoutResultAndInputs>),
384 SameFormattingContextBlock(Box<SameFormattingContextBlockLayoutResultAndInputs>),
385}
386
387#[derive(Clone, MallocSizeOf)]
388pub(crate) struct IndependentFormattingContextLayoutResult {
389 pub fragments: Vec<Fragment>,
390
391 pub content_block_size: Au,
393
394 pub collapsible_margins_in_children: CollapsedBlockMargins,
397
398 pub content_inline_size_for_table: Option<Au>,
402
403 pub baselines: Baselines,
407
408 pub depends_on_block_constraints: bool,
410
411 pub specific_layout_info: Option<SpecificLayoutInfo>,
413}
414
415#[derive(MallocSizeOf)]
418pub(crate) struct IndependentFormattingContextLayoutResultAndInputs {
419 pub result: IndependentFormattingContextLayoutResult,
421
422 pub containing_block_for_children_size: ContainingBlockSize,
425
426 pub positioning_context: PositioningContext,
429
430 content_block_size_under_default_constraint: Option<Au>,
437}
438
439impl IndependentFormattingContextLayoutResultAndInputs {
440 #[inline]
441 fn applicable_for_inline_size(&self, inline_size: Au) -> bool {
442 self.containing_block_for_children_size.inline == inline_size
443 }
444
445 #[inline]
446 fn applicable_for_block_size(&self, block_size: SizeConstraint) -> bool {
447 !self.result.depends_on_block_constraints ||
448 self.containing_block_for_children_size.block == block_size
449 }
450}
451
452#[derive(Clone, MallocSizeOf)]
453pub(crate) struct SameFormattingContextBlockLayoutResult {
454 #[conditional_malloc_size_of]
455 pub fragment: Arc<BoxFragment>,
456 original_offset: Point2D<Au, CSSPixel>,
457}
458
459#[derive(MallocSizeOf)]
462pub(crate) struct SameFormattingContextBlockLayoutResultAndInputs {
463 pub result: SameFormattingContextBlockLayoutResult,
464 pub containing_block_size: ContainingBlockSize,
466 pub containing_block_writing_mode: WritingMode,
468 pub containing_block_justify_items: AlignFlags,
470 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
473 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
475 has_inline_parent: bool,
477}