1use app_units::{Au, MAX_AU, MIN_AU};
6use atomic_refcell::AtomicRefCell;
7use base::id::ScrollTreeNodeId;
8use base::print_tree::PrintTree;
9use euclid::Rect;
10use malloc_size_of_derive::MallocSizeOf;
11use servo_arc::Arc as ServoArc;
12use servo_geometry::f32_rect_to_au_rect;
13use style::Zero;
14use style::computed_values::border_collapse::T as BorderCollapse;
15use style::computed_values::overflow_x::T as ComputedOverflow;
16use style::computed_values::position::T as ComputedPosition;
17use style::logical_geometry::WritingMode;
18use style::properties::ComputedValues;
19
20use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment, FragmentFlags};
21use crate::SharedStyle;
22use crate::display_list::ToWebRender;
23use crate::formatting_contexts::Baselines;
24use crate::fragment_tree::BaseFragmentStyleRef;
25use crate::geom::{
26 AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, ToLogical,
27};
28use crate::style_ext::ComputedValuesExt;
29use crate::table::SpecificTableGridInfo;
30use crate::taffy::SpecificTaffyGridInfo;
31
32#[derive(MallocSizeOf)]
34pub(crate) enum BackgroundMode {
35 Extra(Vec<ExtraBackground>),
38 None,
42 Normal,
44}
45#[derive(MallocSizeOf)]
46pub(crate) struct ExtraBackground {
47 pub style: SharedStyle,
48 pub rect: PhysicalRect<Au>,
49}
50
51#[derive(Clone, Debug, MallocSizeOf)]
52pub(crate) enum SpecificLayoutInfo {
53 Grid(Box<SpecificTaffyGridInfo>),
54 TableCellWithCollapsedBorders,
55 TableGridWithCollapsedBorders(Box<SpecificTableGridInfo>),
56 TableWrapper,
57}
58
59#[derive(MallocSizeOf)]
60pub(crate) struct BlockLevelLayoutInfo {
61 pub clearance: Option<Au>,
68
69 pub block_margins_collapsed_with_children: CollapsedBlockMargins,
70}
71
72#[derive(MallocSizeOf)]
73pub(crate) struct BoxFragmentRareData {
74 pub specific_layout_info: Option<SpecificLayoutInfo>,
76}
77
78impl BoxFragmentRareData {
79 fn try_boxed_from(specific_layout_info: Option<SpecificLayoutInfo>) -> Option<Box<Self>> {
82 specific_layout_info.map(|info| {
83 Box::new(BoxFragmentRareData {
84 specific_layout_info: Some(info),
85 })
86 })
87 }
88}
89
90#[derive(MallocSizeOf)]
91pub(crate) struct BoxFragment {
92 pub base: BaseFragment,
93
94 pub children: Vec<Fragment>,
95
96 pub cumulative_containing_block_rect: PhysicalRect<Au>,
99
100 pub padding: PhysicalSides<Au>,
101 pub border: PhysicalSides<Au>,
102 pub margin: PhysicalSides<Au>,
103
104 baselines: Baselines,
108
109 scrollable_overflow: Option<PhysicalRect<Au>>,
114
115 pub(crate) resolved_sticky_insets: AtomicRefCell<Option<PhysicalSides<AuOrAuto>>>,
119
120 pub background_mode: BackgroundMode,
121
122 pub rare_data: Option<Box<BoxFragmentRareData>>,
124
125 pub block_level_layout_info: Option<Box<BlockLevelLayoutInfo>>,
127
128 pub spatial_tree_node: AtomicRefCell<Option<ScrollTreeNodeId>>,
133}
134
135impl BoxFragment {
136 #[allow(clippy::too_many_arguments)]
137 pub fn new(
138 base_fragment_info: BaseFragmentInfo,
139 style: ServoArc<ComputedValues>,
140 children: Vec<Fragment>,
141 content_rect: PhysicalRect<Au>,
142 padding: PhysicalSides<Au>,
143 border: PhysicalSides<Au>,
144 margin: PhysicalSides<Au>,
145 specific_layout_info: Option<SpecificLayoutInfo>,
146 ) -> BoxFragment {
147 let rare_data = BoxFragmentRareData::try_boxed_from(specific_layout_info);
148
149 BoxFragment {
150 base: BaseFragment::new(base_fragment_info, style.into(), content_rect),
151 children,
152 cumulative_containing_block_rect: Default::default(),
153 padding,
154 border,
155 margin,
156 baselines: Baselines::default(),
157 scrollable_overflow: None,
158 resolved_sticky_insets: AtomicRefCell::default(),
159 background_mode: BackgroundMode::Normal,
160 rare_data,
161 block_level_layout_info: None,
162 spatial_tree_node: AtomicRefCell::default(),
163 }
164 }
165
166 pub fn with_baselines(mut self, baselines: Baselines) -> Self {
167 self.baselines = baselines;
168 self
169 }
170
171 pub(crate) fn style<'a>(&'a self) -> BaseFragmentStyleRef<'a> {
172 self.base.style()
173 }
174
175 pub fn baselines(&self, writing_mode: WritingMode) -> Baselines {
178 let style = self.style();
179 let mut baselines = if writing_mode.is_horizontal() == style.writing_mode.is_horizontal() {
180 self.baselines
181 } else {
182 Baselines::default()
186 };
187
188 if style.establishes_scroll_container(self.base.flags) {
197 let content_rect_size = self.content_rect().size.to_logical(writing_mode);
198 let padding = self.padding.to_logical(writing_mode);
199 let border = self.border.to_logical(writing_mode);
200 let margin = self.margin.to_logical(writing_mode);
201 baselines.last = Some(
202 content_rect_size.block + padding.block_end + border.block_end + margin.block_end,
203 )
204 }
205 baselines
206 }
207
208 pub fn add_extra_background(&mut self, extra_background: ExtraBackground) {
209 match self.background_mode {
210 BackgroundMode::Extra(ref mut backgrounds) => backgrounds.push(extra_background),
211 _ => self.background_mode = BackgroundMode::Extra(vec![extra_background]),
212 }
213 }
214
215 pub fn set_does_not_paint_background(&mut self) {
216 self.background_mode = BackgroundMode::None;
217 }
218
219 pub fn specific_layout_info(&self) -> Option<&SpecificLayoutInfo> {
220 self.rare_data.as_ref()?.specific_layout_info.as_ref()
221 }
222
223 pub fn with_block_level_layout_info(
224 mut self,
225 block_margins_collapsed_with_children: CollapsedBlockMargins,
226 clearance: Option<Au>,
227 ) -> Self {
228 self.block_level_layout_info = Some(Box::new(BlockLevelLayoutInfo {
229 block_margins_collapsed_with_children,
230 clearance,
231 }));
232 self
233 }
234
235 pub fn scrollable_overflow(&self) -> PhysicalRect<Au> {
238 self.scrollable_overflow
239 .expect("Should only call `scrollable_overflow()` after calculating overflow")
240 }
241
242 pub(crate) fn calculate_scrollable_overflow(&mut self) {
246 let physical_padding_rect = self.padding_rect();
247 let content_origin = self.base.rect.origin.to_vector();
248
249 let scrollable_overflow = self
273 .children
274 .iter()
275 .fold(physical_padding_rect, |acc, child| {
276 let scrollable_overflow_from_child = child
277 .calculate_scrollable_overflow_for_parent()
278 .translate(content_origin);
279
280 let scrollable_overflow_from_child = self
285 .clip_wholly_unreachable_scrollable_overflow(
286 scrollable_overflow_from_child,
287 physical_padding_rect,
288 );
289 acc.union(&scrollable_overflow_from_child)
290 });
291
292 if self.base.flags.contains(FragmentFlags::IS_COLLAPSED) {
297 self.scrollable_overflow = Some(Rect::zero());
298 return;
299 }
300
301 self.scrollable_overflow = Some(scrollable_overflow)
302 }
303
304 pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect<Au>) {
305 self.cumulative_containing_block_rect = *containing_block;
306 }
307
308 pub fn offset_by_containing_block(&self, rect: &PhysicalRect<Au>) -> PhysicalRect<Au> {
309 rect.translate(self.cumulative_containing_block_rect.origin.to_vector())
310 }
311
312 pub(crate) fn cumulative_content_box_rect(&self) -> PhysicalRect<Au> {
313 self.offset_by_containing_block(&self.base.rect)
314 }
315
316 pub(crate) fn cumulative_padding_box_rect(&self) -> PhysicalRect<Au> {
317 self.offset_by_containing_block(&self.padding_rect())
318 }
319
320 pub(crate) fn cumulative_border_box_rect(&self) -> PhysicalRect<Au> {
321 self.offset_by_containing_block(&self.border_rect())
322 }
323
324 pub(crate) fn content_rect(&self) -> PhysicalRect<Au> {
325 self.base.rect
326 }
327
328 pub(crate) fn padding_rect(&self) -> PhysicalRect<Au> {
329 self.content_rect().outer_rect(self.padding)
330 }
331
332 pub(crate) fn border_rect(&self) -> PhysicalRect<Au> {
333 self.padding_rect().outer_rect(self.border)
334 }
335
336 pub(crate) fn margin_rect(&self) -> PhysicalRect<Au> {
337 self.border_rect().outer_rect(self.margin)
338 }
339
340 pub(crate) fn padding_border_margin(&self) -> PhysicalSides<Au> {
341 self.margin + self.border + self.padding
342 }
343
344 pub(crate) fn is_root_element(&self) -> bool {
345 self.base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT)
346 }
347
348 pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
349 self.base
350 .flags
351 .intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
352 }
353
354 pub fn print(&self, tree: &mut PrintTree) {
355 tree.new_level(format!(
356 "Box\
357 \nbase={:?}\
358 \ncontent={:?}\
359 \npadding rect={:?}\
360 \nborder rect={:?}\
361 \nmargin={:?}\
362 \nscrollable_overflow={:?}\
363 \nbaselines={:?}\
364 \noverflow={:?}",
365 self.base,
366 self.content_rect(),
367 self.padding_rect(),
368 self.border_rect(),
369 self.margin,
370 self.scrollable_overflow(),
371 self.baselines,
372 self.style().effective_overflow(self.base.flags),
373 ));
374
375 for child in &self.children {
376 child.print(tree);
377 }
378 tree.end_level();
379 }
380
381 pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
382 let style = self.style();
383 let mut overflow = self.border_rect();
384 if !style.establishes_scroll_container(self.base.flags) {
385 let scrollable_overflow = self.scrollable_overflow();
388 let bottom_right = PhysicalPoint::new(
389 overflow.max_x().max(scrollable_overflow.max_x()),
390 overflow.max_y().max(scrollable_overflow.max_y()),
391 );
392
393 let overflow_style = style.effective_overflow(self.base.flags);
394 if overflow_style.y == ComputedOverflow::Visible {
395 overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
396 overflow.size.height = bottom_right.y - overflow.origin.y;
397 }
398
399 if overflow_style.x == ComputedOverflow::Visible {
400 overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
401 overflow.size.width = bottom_right.x - overflow.origin.x;
402 }
403 }
404
405 if !style.has_effective_transform_or_perspective(self.base.flags) {
406 return overflow;
407 }
408
409 self.calculate_transform_matrix(&self.border_rect())
417 .and_then(|transform| {
418 transform.outer_transformed_rect(&overflow.to_webrender().to_rect())
419 })
420 .map(|transformed_rect| f32_rect_to_au_rect(transformed_rect).cast_unit())
421 .unwrap_or(overflow)
422 }
423
424 pub(crate) fn clip_wholly_unreachable_scrollable_overflow(
428 &self,
429 scrollable_overflow: PhysicalRect<Au>,
430 clipping_rect: PhysicalRect<Au>,
431 ) -> PhysicalRect<Au> {
432 let scrolling_direction = self.style().overflow_direction();
441 let mut clipping_box = clipping_rect.to_box2d();
442 if scrolling_direction.rightward {
443 clipping_box.max.x = MAX_AU;
444 } else {
445 clipping_box.min.x = MIN_AU;
446 }
447
448 if scrolling_direction.downward {
449 clipping_box.max.y = MAX_AU;
450 } else {
451 clipping_box.min.y = MIN_AU;
452 }
453
454 let scrollable_overflow_box = scrollable_overflow
455 .to_box2d()
456 .intersection_unchecked(&clipping_box);
457
458 match scrollable_overflow_box.is_negative() {
459 true => PhysicalRect::zero(),
460 false => scrollable_overflow_box.to_rect(),
461 }
462 }
463
464 pub(crate) fn calculate_resolved_insets_if_positioned(&self) -> PhysicalSides<AuOrAuto> {
465 let style = self.style();
466 let position = style.get_box().position;
467 debug_assert_ne!(
468 position,
469 ComputedPosition::Static,
470 "Should not call this method on statically positioned box."
471 );
472
473 if let Some(resolved_sticky_insets) = *self.resolved_sticky_insets.borrow() {
474 return resolved_sticky_insets;
475 }
476
477 let convert_to_au_or_auto = |sides: PhysicalSides<Au>| {
478 PhysicalSides::new(
479 AuOrAuto::LengthPercentage(sides.top),
480 AuOrAuto::LengthPercentage(sides.right),
481 AuOrAuto::LengthPercentage(sides.bottom),
482 AuOrAuto::LengthPercentage(sides.left),
483 )
484 };
485
486 let insets = style.physical_box_offsets();
493 let (cb_width, cb_height) = (
494 self.cumulative_containing_block_rect.width(),
495 self.cumulative_containing_block_rect.height(),
496 );
497 if position == ComputedPosition::Relative {
498 let get_resolved_axis = |start: &LengthPercentageOrAuto,
499 end: &LengthPercentageOrAuto,
500 container_length: Au| {
501 let start = start.map(|value| value.to_used_value(container_length));
502 let end = end.map(|value| value.to_used_value(container_length));
503 match (start.non_auto(), end.non_auto()) {
504 (None, None) => (Au::zero(), Au::zero()),
505 (None, Some(end)) => (-end, end),
506 (Some(start), None) => (start, -start),
507 (Some(start), Some(end)) => (start, end),
510 }
511 };
512 let (left, right) = get_resolved_axis(&insets.left, &insets.right, cb_width);
513 let (top, bottom) = get_resolved_axis(&insets.top, &insets.bottom, cb_height);
514 return convert_to_au_or_auto(PhysicalSides::new(top, right, bottom, left));
515 }
516
517 debug_assert!(position.is_absolutely_positioned());
518
519 let margin_rect = self.margin_rect();
520 let (top, bottom) = match (&insets.top, &insets.bottom) {
521 (
522 LengthPercentageOrAuto::LengthPercentage(top),
523 LengthPercentageOrAuto::LengthPercentage(bottom),
524 ) => (
525 top.to_used_value(cb_height),
526 bottom.to_used_value(cb_height),
527 ),
528 _ => (margin_rect.origin.y, cb_height - margin_rect.max_y()),
529 };
530 let (left, right) = match (&insets.left, &insets.right) {
531 (
532 LengthPercentageOrAuto::LengthPercentage(left),
533 LengthPercentageOrAuto::LengthPercentage(right),
534 ) => (left.to_used_value(cb_width), right.to_used_value(cb_width)),
535 _ => (margin_rect.origin.x, cb_width - margin_rect.max_x()),
536 };
537
538 convert_to_au_or_auto(PhysicalSides::new(top, right, bottom, left))
539 }
540
541 pub(crate) fn is_inline_box(&self) -> bool {
544 self.style().is_inline_box(self.base.flags)
545 }
546
547 pub(crate) fn is_atomic_inline_level(&self) -> bool {
550 self.style().is_atomic_inline_level(self.base.flags)
551 }
552
553 pub(crate) fn is_table_wrapper(&self) -> bool {
556 matches!(
557 self.specific_layout_info(),
558 Some(SpecificLayoutInfo::TableWrapper)
559 )
560 }
561
562 pub(crate) fn has_collapsed_borders(&self) -> bool {
563 match self.specific_layout_info() {
564 Some(SpecificLayoutInfo::TableCellWithCollapsedBorders) => true,
565 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_)) => true,
566 Some(SpecificLayoutInfo::TableWrapper) => {
567 self.style().get_inherited_table().border_collapse == BorderCollapse::Collapse
568 },
569 _ => false,
570 }
571 }
572
573 pub(crate) fn spatial_tree_node(&self) -> Option<ScrollTreeNodeId> {
574 *self.spatial_tree_node.borrow()
575 }
576}