1use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use app_units::{Au, MAX_AU, MIN_AU};
9use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
10use euclid::Rect;
11use malloc_size_of_derive::MallocSizeOf;
12use once_cell::race::OnceBox;
13use servo_arc::Arc as ServoArc;
14use servo_base::id::{AtomicOptionScrollTreeNodeId, ScrollTreeNodeId};
15use servo_base::print_tree::PrintTree;
16use servo_geometry::f32_rect_to_au_rect;
17use style::Zero;
18use style::computed_values::border_collapse::T as BorderCollapse;
19use style::computed_values::overflow_x::T as ComputedOverflow;
20use style::logical_geometry::WritingMode;
21use style::properties::ComputedValues;
22
23use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment, FragmentFlags};
24use crate::SharedStyle;
25use crate::display_list::{ClipId, ToWebRender};
26use crate::formatting_contexts::Baselines;
27use crate::fragment_tree::CumulativeContentRectCalculation;
28use crate::geom::{
29 AuOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, SyncPhysicalRectAu, ToLogical,
30};
31use crate::style_ext::ComputedValuesExt;
32use crate::table::SpecificTableGridInfo;
33use crate::taffy::SpecificTaffyGridInfo;
34
35#[derive(Clone, MallocSizeOf)]
37pub(crate) enum BackgroundMode {
38 Extra(Vec<ExtraBackground>),
41 None,
45 Normal,
47}
48
49#[derive(Clone, MallocSizeOf)]
50pub(crate) struct ExtraBackground {
51 pub style: SharedStyle,
52 pub rect: PhysicalRect<Au>,
53}
54
55#[derive(Clone, Debug, MallocSizeOf)]
56pub(crate) enum SpecificLayoutInfo {
57 Grid(Box<SpecificTaffyGridInfo>),
58 TableCellWithCollapsedBorders,
59 TableGridWithCollapsedBorders(Box<SpecificTableGridInfo>),
60 TableWrapper,
61}
62
63#[derive(Clone, MallocSizeOf)]
64pub(crate) struct BlockLevelLayoutInfo {
65 pub clearance: Option<Au>,
72
73 pub block_margins_collapsed_with_children: CollapsedBlockMargins,
74}
75
76#[derive(Clone, Default, MallocSizeOf)]
77pub(crate) struct BoxFragmentRareData {
78 pub(crate) resolved_sticky_insets: Option<Box<PhysicalSides<AuOrAuto>>>,
82
83 pub specific_layout_info: Option<SpecificLayoutInfo>,
85
86 pub generated_clip_id: Option<ClipId>,
89
90 pub generated_scroll_tree_node_id: Option<ScrollTreeNodeId>,
93}
94
95impl BoxFragmentRareData {
96 fn new(specific_layout_info: Option<SpecificLayoutInfo>) -> OnceBox<AtomicRefCell<Self>> {
99 specific_layout_info
100 .map(|info| {
101 OnceBox::with_value(Box::new(AtomicRefCell::new(BoxFragmentRareData {
102 resolved_sticky_insets: None,
103 specific_layout_info: Some(info),
104 generated_clip_id: None,
105 generated_scroll_tree_node_id: None,
106 })))
107 })
108 .unwrap_or_default()
109 }
110}
111
112#[derive(MallocSizeOf)]
113pub(crate) struct BoxFragment {
114 pub base: BaseFragment,
115
116 pub style: SharedStyle,
118
119 pub children: Vec<Fragment>,
120
121 pub cumulative_parent_content_rect: SyncPhysicalRectAu,
125
126 pub padding: PhysicalSides<Au>,
127 pub border: PhysicalSides<Au>,
128 pub margin: PhysicalSides<Au>,
129
130 baselines: Baselines,
134
135 scrollable_overflow: SyncPhysicalRectAu,
140 scrollable_overflow_is_up_to_date: AtomicBool,
141
142 pub background_mode: BackgroundMode,
143
144 pub rare_data: OnceBox<AtomicRefCell<BoxFragmentRareData>>,
146
147 pub block_level_layout_info: Option<Box<BlockLevelLayoutInfo>>,
149
150 pub spatial_tree_node: AtomicOptionScrollTreeNodeId,
155}
156
157impl BoxFragment {
158 #[expect(clippy::too_many_arguments)]
159 pub(crate) fn new(
160 base_fragment_info: BaseFragmentInfo,
161 style: ServoArc<ComputedValues>,
162 children: Vec<Fragment>,
163 content_rect: PhysicalRect<Au>,
164 padding: PhysicalSides<Au>,
165 border: PhysicalSides<Au>,
166 margin: PhysicalSides<Au>,
167 specific_layout_info: Option<SpecificLayoutInfo>,
168 ) -> Self {
169 let rare_data = BoxFragmentRareData::new(specific_layout_info);
170 Self {
171 base: BaseFragment::new(base_fragment_info, content_rect),
172 style: style.into(),
173 children,
174 cumulative_parent_content_rect: Default::default(),
175 padding,
176 border,
177 margin,
178 baselines: Baselines::default(),
179 scrollable_overflow: Default::default(),
180 scrollable_overflow_is_up_to_date: AtomicBool::new(false),
181 background_mode: BackgroundMode::Normal,
182 rare_data,
183 block_level_layout_info: None,
184 spatial_tree_node: AtomicOptionScrollTreeNodeId::new(None),
185 }
186 }
187
188 pub(crate) fn with_baselines(mut self, baselines: Baselines) -> Self {
189 self.baselines = baselines;
190 self
191 }
192
193 pub(crate) fn style<'a>(&'a self) -> AtomicRef<'a, ServoArc<ComputedValues>> {
194 self.style.borrow()
195 }
196
197 pub(crate) fn with_style(self: &Arc<Self>) -> BoxFragmentWithStyle<'_> {
198 BoxFragmentWithStyle {
199 box_fragment: self,
200 style: self.style(),
201 }
202 }
203
204 pub(crate) fn baselines(&self, writing_mode: WritingMode) -> Baselines {
207 let style = self.style();
208 let mut baselines = if writing_mode.is_horizontal() == style.writing_mode.is_horizontal() {
209 self.baselines
210 } else {
211 Baselines::default()
215 };
216
217 if style.establishes_scroll_container(self.base.flags) {
226 let content_rect_size = self.content_rect().size.to_logical(writing_mode);
227 let padding = self.padding.to_logical(writing_mode);
228 let border = self.border.to_logical(writing_mode);
229 let margin = self.margin.to_logical(writing_mode);
230 baselines.last = Some(
231 content_rect_size.block + padding.block_end + border.block_end + margin.block_end,
232 )
233 }
234 baselines
235 }
236
237 pub(crate) fn add_extra_background(&mut self, extra_background: ExtraBackground) {
238 match self.background_mode {
239 BackgroundMode::Extra(ref mut backgrounds) => backgrounds.push(extra_background),
240 _ => self.background_mode = BackgroundMode::Extra(vec![extra_background]),
241 }
242 }
243
244 pub(crate) fn set_does_not_paint_background(&mut self) {
245 self.background_mode = BackgroundMode::None;
246 }
247
248 pub(crate) fn ensure_rare_data(&self) -> AtomicRefMut<'_, BoxFragmentRareData> {
249 self.rare_data.get_or_init(Default::default).borrow_mut()
250 }
251
252 pub(crate) fn specific_layout_info(&self) -> Option<AtomicRef<'_, SpecificLayoutInfo>> {
253 let rare_data = self.rare_data.get()?.borrow();
254
255 AtomicRef::filter_map(rare_data, |rare_data| {
256 rare_data.specific_layout_info.as_ref()
257 })
258 }
259
260 pub(crate) fn clear_stacking_context_tree_traversal_data(&self) {
261 if let Some(rare_data) = self.rare_data.get() {
262 let mut rare_data = rare_data.borrow_mut();
263 rare_data.generated_clip_id = None;
264 rare_data.generated_scroll_tree_node_id = None;
265 rare_data.resolved_sticky_insets = None;
266 }
267 }
268
269 pub(crate) fn resolved_sticky_insets(
270 &self,
271 ) -> Option<AtomicRef<'_, Box<PhysicalSides<AuOrAuto>>>> {
272 let rare_data = self.rare_data.get()?.borrow();
273
274 AtomicRef::filter_map(rare_data, |rare_data| {
275 rare_data.resolved_sticky_insets.as_ref()
276 })
277 }
278
279 pub(crate) fn set_resolved_sticky_insets(&self, sticky_insets: PhysicalSides<AuOrAuto>) {
280 self.ensure_rare_data().resolved_sticky_insets = Some(sticky_insets.into());
281 }
282
283 pub(crate) fn generated_clip_id(&self) -> Option<ClipId> {
284 self.rare_data.get()?.borrow().generated_clip_id
285 }
286
287 pub(crate) fn set_generated_clip_id(&self, generated_clip_id: ClipId) {
288 self.ensure_rare_data().generated_clip_id = Some(generated_clip_id);
289 }
290
291 pub(crate) fn generated_scroll_tree_node_id(&self) -> Option<ScrollTreeNodeId> {
292 self.rare_data.get()?.borrow().generated_scroll_tree_node_id
293 }
294
295 pub(crate) fn set_generated_scroll_tree_node_id(
296 &self,
297 generated_scroll_tree_node_id: ScrollTreeNodeId,
298 ) {
299 self.ensure_rare_data().generated_scroll_tree_node_id = Some(generated_scroll_tree_node_id);
300 }
301
302 pub(crate) fn with_block_level_layout_info(
303 mut self,
304 block_margins_collapsed_with_children: CollapsedBlockMargins,
305 clearance: Option<Au>,
306 ) -> Self {
307 self.block_level_layout_info = Some(Box::new(BlockLevelLayoutInfo {
308 block_margins_collapsed_with_children,
309 clearance,
310 }));
311 self
312 }
313
314 pub(crate) fn clear_scrollable_overflow(&self) {
318 self.scrollable_overflow_is_up_to_date
319 .store(false, Ordering::Release);
320 }
321
322 #[inline]
323 pub(crate) fn set_cumulative_content_rect(&self, content_rect: &PhysicalRect<Au>) {
324 self.cumulative_parent_content_rect.set(*content_rect);
325 }
326
327 pub(crate) fn offset_by_cumulative_content_offset(
328 &self,
329 rect: &PhysicalRect<Au>,
330 cumulative_content_rect_calculation: CumulativeContentRectCalculation<'_>,
331 ) -> PhysicalRect<Au> {
332 cumulative_content_rect_calculation.ensure();
333 rect.translate(self.cumulative_parent_content_rect.origin().to_vector())
334 }
335
336 pub(crate) fn cumulative_content_box_rect(
337 &self,
338 cumulative_content_rect_calculation: CumulativeContentRectCalculation<'_>,
339 ) -> PhysicalRect<Au> {
340 self.offset_by_cumulative_content_offset(
341 &self.base.rect(),
342 cumulative_content_rect_calculation,
343 )
344 }
345
346 pub(crate) fn cumulative_padding_box_rect(
347 &self,
348 cumulative_content_rect_calculation: CumulativeContentRectCalculation<'_>,
349 ) -> PhysicalRect<Au> {
350 self.offset_by_cumulative_content_offset(
351 &self.padding_rect(),
352 cumulative_content_rect_calculation,
353 )
354 }
355
356 pub(crate) fn cumulative_border_box_rect(
357 &self,
358 cumulative_content_rect_calculation: CumulativeContentRectCalculation<'_>,
359 ) -> PhysicalRect<Au> {
360 self.offset_by_cumulative_content_offset(
361 &self.border_rect(),
362 cumulative_content_rect_calculation,
363 )
364 }
365
366 pub(crate) fn content_rect(&self) -> PhysicalRect<Au> {
367 self.base.rect()
368 }
369
370 pub(crate) fn padding_rect(&self) -> PhysicalRect<Au> {
371 self.content_rect().outer_rect(self.padding)
372 }
373
374 pub(crate) fn border_rect(&self) -> PhysicalRect<Au> {
375 self.padding_rect().outer_rect(self.border)
376 }
377
378 pub(crate) fn margin_rect(&self) -> PhysicalRect<Au> {
379 self.border_rect().outer_rect(self.margin)
380 }
381
382 pub(crate) fn padding_border_margin(&self) -> PhysicalSides<Au> {
383 self.margin + self.border + self.padding
384 }
385
386 pub(crate) fn is_root_element(&self) -> bool {
387 self.base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT)
388 }
389
390 pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
391 self.base
392 .flags
393 .intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
394 }
395
396 pub(crate) fn print(self: &Arc<Self>, tree: &mut PrintTree) {
397 let with_style = self.with_style();
398 tree.new_level(format!(
399 "Box\
400 \nbase={:?}\
401 \ncontent={:?}\
402 \npadding rect={:?}\
403 \nborder rect={:?}\
404 \nmargin={:?}\
405 \nscrollable_overflow={:?}\
406 \nbaselines={:?}\
407 \noverflow={:?}",
408 self.base,
409 self.content_rect(),
410 self.padding_rect(),
411 self.border_rect(),
412 self.margin,
413 with_style.scrollable_overflow(),
414 self.baselines,
415 with_style.style().effective_overflow(self.base.flags),
416 ));
417
418 for child in &self.children {
419 child.print(tree);
420 }
421 tree.end_level();
422 }
423
424 pub(crate) fn is_flex_or_grid_item(&self) -> bool {
426 self.base
427 .flags
428 .contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
429 }
430
431 pub(crate) fn is_replaced(&self) -> bool {
433 self.base.flags.contains(FragmentFlags::IS_REPLACED)
434 }
435
436 pub(crate) fn is_table_wrapper(&self) -> bool {
439 matches!(
440 self.specific_layout_info().as_deref(),
441 Some(SpecificLayoutInfo::TableWrapper)
442 )
443 }
444
445 pub(crate) fn is_table_grid_with_collapsed_borders(&self) -> bool {
447 matches!(
448 self.specific_layout_info().as_deref(),
449 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
450 )
451 }
452
453 pub(crate) fn spatial_tree_node(&self) -> Option<ScrollTreeNodeId> {
454 self.spatial_tree_node.get()
455 }
456
457 pub(crate) fn containing_block_rect_for_absolute(
464 &self,
465 item_style: &ComputedValues,
466 ) -> PhysicalRect<Au> {
467 self.grid_info()
468 .map(|grid_info| {
469 grid_info.resolve_grid_area(self, item_style)
472 })
473 .unwrap_or_else(|| {
474 let mut padding_rect = self.padding_rect();
476 padding_rect.origin -= self.content_rect().origin.to_vector();
477 padding_rect
478 })
479 }
480
481 pub(crate) fn grid_info(&self) -> Option<AtomicRef<'_, SpecificTaffyGridInfo>> {
482 let rare_data = self.rare_data.get().map(|ref_cell| ref_cell.borrow());
483 rare_data.and_then(|rare_data| {
484 AtomicRef::filter_map(rare_data, |rare_data| {
485 match rare_data.specific_layout_info.as_ref() {
486 Some(SpecificLayoutInfo::Grid(grid_info)) => Some(&**grid_info),
487 _ => None,
488 }
489 })
490 })
491 }
492}
493
494pub(crate) struct BoxFragmentWithStyle<'a> {
501 pub(crate) box_fragment: &'a Arc<BoxFragment>,
502 pub(crate) style: AtomicRef<'a, ServoArc<ComputedValues>>,
503}
504
505impl std::ops::Deref for BoxFragmentWithStyle<'_> {
506 type Target = Arc<BoxFragment>;
507
508 fn deref(&self) -> &Self::Target {
509 self.box_fragment
510 }
511}
512
513impl<'a> BoxFragmentWithStyle<'a> {
514 pub(crate) fn style(&self) -> &ServoArc<ComputedValues> {
515 &self.style
516 }
517
518 pub(crate) fn clip_wholly_unreachable_scrollable_overflow(
523 &self,
524 scrollable_overflow_from_child: PhysicalRect<Au>,
525 clipping_rect: PhysicalRect<Au>,
526 ) -> PhysicalRect<Au> {
527 let scrolling_direction = self.style().overflow_direction();
536 let mut clipping_box = clipping_rect.to_box2d();
537 if scrolling_direction.rightward {
538 clipping_box.max.x = MAX_AU;
539 } else {
540 clipping_box.min.x = MIN_AU;
541 }
542
543 if scrolling_direction.downward {
544 clipping_box.max.y = MAX_AU;
545 } else {
546 clipping_box.min.y = MIN_AU;
547 }
548
549 let scrollable_overflow_box = scrollable_overflow_from_child
550 .to_box2d()
551 .intersection_unchecked(&clipping_box);
552
553 match scrollable_overflow_box.is_negative() {
554 false => scrollable_overflow_box.to_rect(),
555 true => Rect::zero(),
556 }
557 }
558
559 pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
560 let style = self.style();
561 let mut overflow = self.border_rect();
562 if !style.establishes_scroll_container(self.base.flags) {
563 let scrollable_overflow = self.scrollable_overflow();
566 let bottom_right = PhysicalPoint::new(
567 overflow.max_x().max(scrollable_overflow.max_x()),
568 overflow.max_y().max(scrollable_overflow.max_y()),
569 );
570
571 let overflow_style = style.effective_overflow(self.base.flags);
572 if overflow_style.y == ComputedOverflow::Visible {
573 overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
574 overflow.size.height = bottom_right.y - overflow.origin.y;
575 }
576
577 if overflow_style.x == ComputedOverflow::Visible {
578 overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
579 overflow.size.width = bottom_right.x - overflow.origin.x;
580 }
581 }
582
583 if !style.has_effective_transform_or_perspective(self.base.flags) {
584 return overflow;
585 }
586
587 self.calculate_transform_matrix(&self.border_rect())
595 .and_then(|transform| {
596 transform.outer_transformed_rect(&overflow.to_webrender().to_rect())
597 })
598 .map(|transformed_rect| f32_rect_to_au_rect(transformed_rect).cast_unit())
599 .unwrap_or(overflow)
600 }
601
602 pub(crate) fn scrollable_overflow(&self) -> PhysicalRect<Au> {
606 if self
607 .scrollable_overflow_is_up_to_date
608 .load(Ordering::Acquire)
609 {
610 self.scrollable_overflow.get()
611 } else {
612 let rect = self.calculate_scrollable_overflow();
613 self.scrollable_overflow.set(rect);
614 self.scrollable_overflow_is_up_to_date
615 .store(true, Ordering::Release);
616 rect
617 }
618 }
619 fn calculate_scrollable_overflow(&self) -> PhysicalRect<Au> {
623 if self.base.flags.contains(FragmentFlags::IS_COLLAPSED) {
628 return Rect::zero();
629 }
630
631 let physical_padding_rect = self.padding_rect();
632 let content_origin = self.base.rect().origin.to_vector();
633
634 let mut scrollable_overflow =
658 self.children
659 .iter()
660 .fold(physical_padding_rect, |acc, child| {
661 let scrollable_overflow_from_child = child
662 .scrollable_overflow_for_parent()
663 .translate(content_origin);
664
665 let scrollable_overflow_from_child = self
670 .clip_wholly_unreachable_scrollable_overflow(
671 scrollable_overflow_from_child,
672 physical_padding_rect,
673 );
674
675 acc.union(&scrollable_overflow_from_child)
676 });
677
678 let should_include_additional_padding =
691 self.style().establishes_scroll_container(self.base.flags) &&
692 !self.base.flags.intersects(FragmentFlags::IS_INPUT_ELEMENT);
693
694 if should_include_additional_padding {
695 scrollable_overflow = self
696 .children
697 .iter()
698 .fold(scrollable_overflow, |acc, child| {
699 let Some(padding_contribution) =
700 child.scrollable_overflow_padding_contribution_for_parent()
701 else {
702 return acc;
703 };
704
705 let padding_contribution = padding_contribution
706 .translate(content_origin)
707 .outer_rect(self.padding);
708
709 let padding_contribution = self.clip_wholly_unreachable_scrollable_overflow(
713 padding_contribution,
714 physical_padding_rect,
715 );
716
717 acc.union(&padding_contribution)
718 });
719 }
720 scrollable_overflow
721 }
722
723 pub(crate) fn is_inline_box(&self) -> bool {
726 self.style().is_inline_box(self.base.flags)
727 }
728
729 pub(crate) fn is_atomic_inline_level(&self) -> bool {
732 self.style().is_atomic_inline_level(self.base.flags)
733 }
734
735 pub(crate) fn has_collapsed_borders(&self) -> bool {
736 match self.specific_layout_info().as_deref() {
737 Some(SpecificLayoutInfo::TableCellWithCollapsedBorders) => true,
738 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_)) => true,
739 Some(SpecificLayoutInfo::TableWrapper) => {
740 self.style().get_inherited_table().border_collapse == BorderCollapse::Collapse
741 },
742 _ => false,
743 }
744 }
745
746 pub(crate) fn has_outline(&self) -> bool {
748 let style = self.style();
749 let outline = style.get_outline();
750 !outline.outline_style.none_or_hidden() && !outline.outline_width.0.is_zero()
751 }
752}