1use std::fmt::{Debug, Formatter};
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, Ordering};
8
9use app_units::Au;
10use atomic_refcell::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::selector_parser::RestyleDamage;
19use style::values::specified::align::AlignFlags;
20use style_traits::CSSPixel;
21
22use crate::context::LayoutContext;
23use crate::dom::{LayoutBox, WeakLayoutBox};
24use crate::flow::CollapsibleWithParentStartMargin;
25use crate::formatting_contexts::Baselines;
26use crate::fragment_tree::{
27 BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, FragmentStatus,
28 SpecificLayoutInfo,
29};
30use crate::geom::LogicalSides1D;
31use crate::positioned::{PositioningContext, relative_adjustement};
32use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult, SizeConstraint};
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 pub fragments: AtomicRefCell<Vec<Fragment>>,
60 pub parent_box: Option<WeakLayoutBox>,
61 only_descendants_changed: AtomicBool,
62}
63
64impl LayoutBoxBase {
65 pub(crate) fn new(
66 base_fragment_info: BaseFragmentInfo,
67 style: ServoArc<ComputedValues>,
68 ) -> Self {
69 Self {
70 base_fragment_info,
71 style,
72 cached_inline_content_size: AtomicRefCell::default(),
73 outer_inline_content_sizes_depend_on_content: AtomicBool::new(true),
74 cached_layout_result: AtomicRefCell::default(),
75 cached_layout_result_dirty: AtomicBool::default(),
76 fragments: AtomicRefCell::default(),
77 parent_box: None,
78 only_descendants_changed: AtomicBool::default(),
79 }
80 }
81
82 pub(crate) fn inline_content_sizes(
85 &self,
86 layout_context: &LayoutContext,
87 constraint_space: &ConstraintSpace,
88 layout_box: &impl ComputeInlineContentSizes,
89 ) -> InlineContentSizesResult {
90 let mut cache = self.cached_inline_content_size.borrow_mut();
91 if let Some(cached_inline_content_size) = cache.as_ref() {
92 let (previous_cb_block_size, result) = **cached_inline_content_size;
93 if !result.depends_on_block_constraints ||
94 previous_cb_block_size == constraint_space.block_size
95 {
96 return result;
97 }
98 }
100
101 let result =
102 layout_box.compute_inline_content_sizes_with_fixup(layout_context, constraint_space);
103 *cache = Some(Box::new((constraint_space.block_size, result)));
104 result
105 }
106
107 pub(crate) fn fragments(&self) -> Vec<Fragment> {
108 self.fragments.borrow().clone()
109 }
110
111 pub(crate) fn add_fragment(&self, fragment: Fragment) {
112 if self.only_descendants_changed.load(Ordering::Relaxed) &&
113 let Some(base) = fragment.base()
114 {
115 base.set_status(FragmentStatus::OnlyDescendantsChanged)
116 }
117 self.fragments.borrow_mut().push(fragment);
118 }
119
120 pub(crate) fn set_fragment(&self, fragment: Fragment) {
121 if self.only_descendants_changed.load(Ordering::Relaxed) &&
122 let Some(base) = fragment.base()
123 {
124 base.set_status(FragmentStatus::OnlyDescendantsChanged)
125 }
126 *self.fragments.borrow_mut() = vec![fragment];
127 }
128
129 pub(crate) fn clear_fragments(&self) {
130 self.fragments.borrow_mut().clear();
131 }
132
133 pub(crate) fn clear_fragments_and_dirty_fragment_cache(&self) {
136 self.fragments.borrow_mut().clear();
137 self.cached_layout_result_dirty
138 .store(true, Ordering::Relaxed);
139 }
140
141 pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
142 self.style = new_style.clone();
143 for fragment in self.fragments.borrow_mut().iter_mut() {
144 if let Some(base) = fragment.base() {
145 base.repair_style(new_style);
146 }
147 }
148 }
149
150 #[expect(unused)]
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 add_damage(
156 &self,
157 element_damage: LayoutDamage,
158 damage_from_children: LayoutDamage,
159 damage_from_parent: RestyleDamage,
160 ) -> LayoutDamage {
161 let only_descendants_changed = !RestyleDamage::from(element_damage)
162 .contains(RestyleDamage::RELAYOUT) &&
163 !damage_from_parent.contains(RestyleDamage::RELAYOUT) &&
164 !damage_from_children.contains(LayoutDamage::LAYOUT_AFFECTED_BY_INFLOW_DESCENDANT) &&
165 self.fragments.borrow().iter().all(|fragment| {
166 fragment
167 .base()
168 .is_some_and(|base| base.status() == FragmentStatus::Clean)
169 });
170 self.only_descendants_changed
171 .store(only_descendants_changed, Ordering::Relaxed);
172 self.clear_fragments_and_dirty_fragment_cache();
173
174 if !element_damage.is_empty() ||
175 damage_from_children.contains(LayoutDamage::RECOMPUTE_INLINE_CONTENT_SIZES)
176 {
177 *self.cached_inline_content_size.borrow_mut() = None;
178 }
179
180 let mut damage_for_parent = element_damage | damage_from_children;
181
182 damage_for_parent.set(
193 LayoutDamage::RECOMPUTE_INLINE_CONTENT_SIZES,
194 !element_damage.is_empty() ||
195 (!self.base_fragment_info.is_anonymous() &&
196 self.outer_inline_content_sizes_depend_on_content
197 .load(Ordering::Relaxed)),
198 );
199
200 damage_for_parent
201 }
202
203 pub(crate) fn cached_independent_formatting_context_layout_if_applicable(
204 &self,
205 positioning_context: &mut PositioningContext,
206 containing_block_for_children: &ContainingBlock<'_>,
207 ) -> Option<IndependentFormattingContextLayoutResult> {
208 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
209 return None;
210 }
211
212 let cache = self.cached_layout_result.borrow();
213 let Some(LayoutResultAndInputs::IndependentFormattingContext(cache)) = &*cache else {
214 return None;
215 };
216
217 let cache = &**cache;
218 if cache.containing_block_for_children_size.inline !=
219 containing_block_for_children.size.inline
220 {
221 return None;
222 }
223 if cache.containing_block_for_children_size.block !=
224 containing_block_for_children.size.block &&
225 cache.result.depends_on_block_constraints
226 {
227 return None;
228 }
229
230 positioning_context.append(cache.positioning_context.clone());
231 Some(cache.result.clone())
232 }
233
234 pub(crate) fn cache_independent_formatting_context_layout(
235 &self,
236 containing_block_for_children: &ContainingBlock<'_>,
237 child_positioning_context: &PositioningContext,
238 result: &IndependentFormattingContextLayoutResult,
239 ) {
240 self.cached_layout_result_dirty
241 .store(false, Ordering::Relaxed);
242 *self.cached_layout_result.borrow_mut() =
243 Some(LayoutResultAndInputs::IndependentFormattingContext(
244 Box::new(IndependentFormattingContextLayoutResultAndInputs {
245 result: result.clone(),
246 positioning_context: child_positioning_context.clone(),
247 containing_block_for_children_size: containing_block_for_children.size.clone(),
248 }),
249 ));
250 }
251
252 pub(crate) fn cached_same_formatting_context_block_if_applicable(
253 &self,
254 containing_block: &ContainingBlock,
255 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
256 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
257 has_inline_parent: bool,
258 ) -> Option<Arc<BoxFragment>> {
259 if self.cached_layout_result_dirty.load(Ordering::Relaxed) {
260 return None;
261 }
262
263 let mut cached_layout_result = self.cached_layout_result.borrow_mut();
264 let Some(LayoutResultAndInputs::SameFormattingContextBlock(result)) =
265 &mut *cached_layout_result
266 else {
267 return None;
268 };
269
270 if result.containing_block_size != containing_block.size ||
271 result.containing_block_writing_mode != containing_block.style.writing_mode ||
272 result.containing_block_justify_items !=
273 containing_block.style.clone_justify_items().computed.0.0 ||
274 result.collapsible_with_parent_start_margin != collapsible_with_parent_start_margin ||
275 result.ignore_block_margins_for_stretch != ignore_block_margins_for_stretch ||
276 result.has_inline_parent != has_inline_parent
277 {
278 return None;
279 }
280
281 let fragment = result.result.fragment.clone();
282 {
283 let mut origin = result.result.original_offset;
284 if self.style.clone_position() == Position::Relative {
285 origin += relative_adjustement(&self.style, containing_block)
286 .to_physical_vector(containing_block.style.writing_mode)
287 }
288 fragment.base.set_rect_origin(origin);
289 }
290
291 Some(fragment)
292 }
293
294 pub(crate) fn cache_same_formatting_context_block_layout(
295 &self,
296 containing_block: &ContainingBlock,
297 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
298 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
299 has_inline_parent: bool,
300 fragment: Arc<BoxFragment>,
301 ) {
302 let mut original_offset;
303 {
304 original_offset = fragment.content_rect().origin;
305 if self.style.clone_position() == Position::Relative {
306 original_offset -= relative_adjustement(&self.style, containing_block)
307 .to_physical_vector(containing_block.style.writing_mode)
308 }
309 }
310
311 self.cached_layout_result_dirty
312 .store(false, Ordering::Relaxed);
313 *self.cached_layout_result.borrow_mut() =
314 Some(LayoutResultAndInputs::SameFormattingContextBlock(Box::new(
315 SameFormattingContextBlockLayoutResultAndInputs {
316 result: SameFormattingContextBlockLayoutResult {
317 fragment,
318 original_offset,
319 },
320 containing_block_size: containing_block.size.clone(),
321 containing_block_writing_mode: containing_block.style.writing_mode,
322 containing_block_justify_items: containing_block
323 .style
324 .clone_justify_items()
325 .computed
326 .0
327 .0,
328 collapsible_with_parent_start_margin,
329 ignore_block_margins_for_stretch,
330 has_inline_parent,
331 },
332 )));
333 }
334
335 pub(crate) fn clear_scrollable_overflow_all_on_fragments(&self) {
336 for fragment in self.fragments.borrow().iter() {
337 fragment.clear_scrollable_overflow();
338 }
339 }
340}
341
342impl Debug for LayoutBoxBase {
343 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
344 f.debug_struct("LayoutBoxBase").finish()
345 }
346}
347
348#[derive(MallocSizeOf)]
349pub(crate) enum LayoutResultAndInputs {
350 IndependentFormattingContext(Box<IndependentFormattingContextLayoutResultAndInputs>),
351 SameFormattingContextBlock(Box<SameFormattingContextBlockLayoutResultAndInputs>),
352}
353
354#[derive(Clone, MallocSizeOf)]
355pub(crate) struct IndependentFormattingContextLayoutResult {
356 pub fragments: Vec<Fragment>,
357
358 pub content_block_size: Au,
360
361 pub collapsible_margins_in_children: CollapsedBlockMargins,
364
365 pub content_inline_size_for_table: Option<Au>,
369
370 pub baselines: Baselines,
374
375 pub depends_on_block_constraints: bool,
377
378 pub specific_layout_info: Option<SpecificLayoutInfo>,
380}
381
382#[derive(MallocSizeOf)]
385pub(crate) struct IndependentFormattingContextLayoutResultAndInputs {
386 pub result: IndependentFormattingContextLayoutResult,
388
389 pub containing_block_for_children_size: ContainingBlockSize,
392
393 pub positioning_context: PositioningContext,
396}
397
398#[derive(Clone, MallocSizeOf)]
399pub(crate) struct SameFormattingContextBlockLayoutResult {
400 #[conditional_malloc_size_of]
401 pub fragment: Arc<BoxFragment>,
402 original_offset: Point2D<Au, CSSPixel>,
403}
404
405#[derive(MallocSizeOf)]
408pub(crate) struct SameFormattingContextBlockLayoutResultAndInputs {
409 pub result: SameFormattingContextBlockLayoutResult,
410 pub containing_block_size: ContainingBlockSize,
412 pub containing_block_writing_mode: WritingMode,
414 pub containing_block_justify_items: AlignFlags,
416 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
419 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
421 has_inline_parent: bool,
423}