1use core::f32;
6use std::cell::{Cell, RefCell};
7use std::mem;
8use std::sync::Arc;
9
10use app_units::Au;
11use base::id::ScrollTreeNodeId;
12use base::print_tree::PrintTree;
13use compositing_traits::display_list::{
14 AxesScrollSensitivity, CompositorDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
15 SpatialTreeNodeInfo, StickyNodeInfo,
16};
17use embedder_traits::ViewportDetails;
18use euclid::SideOffsets2D;
19use euclid::default::{Point2D, Rect, Size2D};
20use log::warn;
21use malloc_size_of_derive::MallocSizeOf;
22use servo_config::opts::DiagnosticsLogging;
23use style::Zero;
24use style::color::AbsoluteColor;
25use style::computed_values::float::T as ComputedFloat;
26use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
27use style::computed_values::overflow_x::T as ComputedOverflow;
28use style::computed_values::position::T as ComputedPosition;
29use style::computed_values::text_decoration_style::T as TextDecorationStyle;
30use style::values::computed::angle::Angle;
31use style::values::computed::basic_shape::ClipPath;
32use style::values::computed::{ClipRectOrAuto, Length, TextDecorationLine};
33use style::values::generics::box_::Perspective;
34use style::values::generics::transform::{self, GenericRotate, GenericScale, GenericTranslate};
35use style::values::specified::box_::DisplayOutside;
36use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
37use webrender_api::{self as wr, BorderRadius};
38use wr::StickyOffsetBounds;
39use wr::units::{LayoutPixel, LayoutSize};
40
41use super::ClipId;
42use super::clip::StackingContextTreeClipStore;
43use crate::ArcRefCell;
44use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
45use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii};
46use crate::fragment_tree::{
47 BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree,
48 PositioningFragment, SpecificLayoutInfo,
49};
50use crate::geom::{AuOrAuto, LengthPercentageOrAuto, PhysicalRect, PhysicalSides};
51use crate::style_ext::{ComputedValuesExt, TransformExt};
52
53#[derive(Clone)]
54pub(crate) struct ContainingBlock {
55 scroll_node_id: ScrollTreeNodeId,
58
59 scroll_frame_size: Option<LayoutSize>,
63
64 clip_id: ClipId,
66
67 rect: PhysicalRect<Au>,
69}
70
71impl ContainingBlock {
72 pub(crate) fn new(
73 rect: PhysicalRect<Au>,
74 scroll_node_id: ScrollTreeNodeId,
75 scroll_frame_size: Option<LayoutSize>,
76 clip_id: ClipId,
77 ) -> Self {
78 ContainingBlock {
79 scroll_node_id,
80 scroll_frame_size,
81 clip_id,
82 rect,
83 }
84 }
85
86 pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Au>) -> Self {
87 ContainingBlock {
88 rect: *rect,
89 ..*self
90 }
91 }
92}
93
94pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
95
96#[derive(Clone, Copy, Debug, Eq, Ord, MallocSizeOf, PartialEq, PartialOrd)]
97pub(crate) enum StackingContextSection {
98 OwnBackgroundsAndBorders,
99 DescendantBackgroundsAndBorders,
100 Foreground,
101 Outline,
102}
103
104#[derive(MallocSizeOf)]
105pub(crate) struct StackingContextTree {
106 pub root_stacking_context: StackingContext,
108
109 pub compositor_info: CompositorDisplayListInfo,
114
115 pub clip_store: StackingContextTreeClipStore,
119}
120
121impl StackingContextTree {
122 pub fn new(
125 fragment_tree: &FragmentTree,
126 viewport_details: ViewportDetails,
127 pipeline_id: wr::PipelineId,
128 first_reflow: bool,
129 debug: &DiagnosticsLogging,
130 ) -> Self {
131 let scrollable_overflow = fragment_tree.scrollable_overflow();
132 let scrollable_overflow = LayoutSize::from_untyped(Size2D::new(
133 scrollable_overflow.size.width.to_f32_px(),
134 scrollable_overflow.size.height.to_f32_px(),
135 ));
136
137 let viewport_size = viewport_details.layout_size();
138 let compositor_info = CompositorDisplayListInfo::new(
139 viewport_details,
140 scrollable_overflow,
141 pipeline_id,
142 Default::default(),
144 fragment_tree.viewport_scroll_sensitivity,
145 first_reflow,
146 );
147
148 let root_scroll_node_id = compositor_info.root_scroll_node_id;
149 let cb_for_non_fixed_descendants = ContainingBlock::new(
150 fragment_tree.initial_containing_block,
151 root_scroll_node_id,
152 Some(viewport_size),
153 ClipId::INVALID,
154 );
155 let cb_for_fixed_descendants = ContainingBlock::new(
156 fragment_tree.initial_containing_block,
157 compositor_info.root_reference_frame_id,
158 None,
159 ClipId::INVALID,
160 );
161
162 let containing_block_info = ContainingBlockInfo {
169 for_non_absolute_descendants: &cb_for_non_fixed_descendants,
170 for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
171 for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
172 };
173
174 let mut stacking_context_tree = Self {
175 root_stacking_context: StackingContext::create_root(root_scroll_node_id, debug),
177 compositor_info,
178 clip_store: Default::default(),
179 };
180
181 let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug);
182 let text_decorations = Default::default();
183 for fragment in &fragment_tree.root_fragments {
184 fragment.build_stacking_context_tree(
185 &mut stacking_context_tree,
186 &containing_block_info,
187 &mut root_stacking_context,
188 StackingContextBuildMode::SkipHoisted,
189 &text_decorations,
190 );
191 }
192 root_stacking_context.sort();
193
194 if debug.stacking_context_tree {
195 root_stacking_context.debug_print();
196 }
197
198 stacking_context_tree.root_stacking_context = root_stacking_context;
199
200 stacking_context_tree
201 }
202
203 fn push_reference_frame(
204 &mut self,
205 origin: LayoutPoint,
206 frame_origin_for_query: LayoutPoint,
207 parent_scroll_node_id: ScrollTreeNodeId,
208 transform_style: wr::TransformStyle,
209 transform: LayoutTransform,
210 kind: wr::ReferenceFrameKind,
211 ) -> ScrollTreeNodeId {
212 self.compositor_info.scroll_tree.add_scroll_tree_node(
213 Some(parent_scroll_node_id),
214 SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
215 origin,
216 frame_origin_for_query,
217 transform_style,
218 transform: transform.into(),
219 kind,
220 }),
221 )
222 }
223
224 fn define_scroll_frame(
225 &mut self,
226 parent_scroll_node_id: ScrollTreeNodeId,
227 external_id: wr::ExternalScrollId,
228 content_rect: LayoutRect,
229 clip_rect: LayoutRect,
230 scroll_sensitivity: AxesScrollSensitivity,
231 ) -> ScrollTreeNodeId {
232 self.compositor_info.scroll_tree.add_scroll_tree_node(
233 Some(parent_scroll_node_id),
234 SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
235 external_id,
236 content_rect,
237 clip_rect,
238 scroll_sensitivity,
239 offset: LayoutVector2D::zero(),
240 offset_changed: Cell::new(false),
241 }),
242 )
243 }
244
245 fn define_sticky_frame(
246 &mut self,
247 parent_scroll_node_id: ScrollTreeNodeId,
248 frame_rect: LayoutRect,
249 margins: SideOffsets2D<Option<f32>, LayoutPixel>,
250 vertical_offset_bounds: StickyOffsetBounds,
251 horizontal_offset_bounds: StickyOffsetBounds,
252 ) -> ScrollTreeNodeId {
253 self.compositor_info.scroll_tree.add_scroll_tree_node(
254 Some(parent_scroll_node_id),
255 SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
256 frame_rect,
257 margins,
258 vertical_offset_bounds,
259 horizontal_offset_bounds,
260 }),
261 )
262 }
263}
264
265#[derive(Clone, Debug, MallocSizeOf)]
267pub(crate) struct FragmentTextDecoration {
268 pub line: TextDecorationLine,
269 pub color: AbsoluteColor,
270 pub style: TextDecorationStyle,
271}
272
273#[derive(MallocSizeOf)]
278pub(crate) enum StackingContextContent {
279 Fragment {
281 scroll_node_id: ScrollTreeNodeId,
282 reference_frame_scroll_node_id: ScrollTreeNodeId,
283 clip_id: ClipId,
284 section: StackingContextSection,
285 containing_block: PhysicalRect<Au>,
286 fragment: Fragment,
287 is_hit_test_for_scrollable_overflow: bool,
288 is_collapsed_table_borders: bool,
289 #[conditional_malloc_size_of]
290 text_decorations: Arc<Vec<FragmentTextDecoration>>,
291 },
292
293 AtomicInlineStackingContainer { index: usize },
297}
298
299impl StackingContextContent {
300 pub(crate) fn section(&self) -> StackingContextSection {
301 match self {
302 Self::Fragment { section, .. } => *section,
303 Self::AtomicInlineStackingContainer { .. } => StackingContextSection::Foreground,
304 }
305 }
306
307 fn build_display_list_with_section_override(
308 &self,
309 builder: &mut DisplayListBuilder,
310 inline_stacking_containers: &[StackingContext],
311 section_override: Option<StackingContextSection>,
312 ) {
313 match self {
314 Self::Fragment {
315 scroll_node_id,
316 reference_frame_scroll_node_id,
317 clip_id,
318 section,
319 containing_block,
320 fragment,
321 is_hit_test_for_scrollable_overflow,
322 is_collapsed_table_borders,
323 text_decorations,
324 } => {
325 builder.current_scroll_node_id = *scroll_node_id;
326 builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
327 builder.current_clip_id = *clip_id;
328 fragment.build_display_list(
329 builder,
330 containing_block,
331 section_override.unwrap_or(*section),
332 *is_hit_test_for_scrollable_overflow,
333 *is_collapsed_table_borders,
334 text_decorations,
335 );
336 },
337 Self::AtomicInlineStackingContainer { index } => {
338 inline_stacking_containers[*index].build_display_list(builder);
339 },
340 }
341 }
342
343 fn build_display_list(
344 &self,
345 builder: &mut DisplayListBuilder,
346 inline_stacking_containers: &[StackingContext],
347 ) {
348 self.build_display_list_with_section_override(builder, inline_stacking_containers, None);
349 }
350
351 fn has_outline(&self) -> bool {
352 match self {
353 StackingContextContent::Fragment { fragment, .. } => match fragment {
354 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment
355 .borrow()
356 .style
357 .get_outline()
358 .outline_has_nonzero_width(),
359 _ => false,
360 },
361 StackingContextContent::AtomicInlineStackingContainer { .. } => false,
362 }
363 }
364}
365
366#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
367pub(crate) enum StackingContextType {
368 RealStackingContext,
369 PositionedStackingContainer,
370 FloatStackingContainer,
371 AtomicInlineStackingContainer,
372}
373
374#[derive(MallocSizeOf)]
380pub struct StackingContext {
381 scroll_tree_node_id: ScrollTreeNodeId,
384
385 clip_id: Option<ClipId>,
387
388 initializing_fragment: Option<ArcRefCell<BoxFragment>>,
391
392 context_type: StackingContextType,
394
395 pub(super) contents: Vec<StackingContextContent>,
397
398 pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
408
409 pub(super) float_stacking_containers: Vec<StackingContext>,
414
415 pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
423
424 debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
426}
427
428#[derive(Clone, Copy, MallocSizeOf)]
430pub struct DebugPrintItem {
431 field: DebugPrintField,
432 index: usize,
433}
434
435#[derive(Clone, Copy, MallocSizeOf)]
437pub enum DebugPrintField {
438 Contents,
439 RealStackingContextsAndPositionedStackingContainers,
440 FloatStackingContainers,
441}
442
443impl StackingContext {
444 fn create_descendant(
445 &self,
446 spatial_id: ScrollTreeNodeId,
447 clip_id: ClipId,
448 initializing_fragment: ArcRefCell<BoxFragment>,
449 context_type: StackingContextType,
450 ) -> Self {
451 let clip_id = match clip_id {
456 ClipId::INVALID => None,
457 clip_id => Some(clip_id),
458 };
459 Self {
460 scroll_tree_node_id: spatial_id,
461 clip_id,
462 initializing_fragment: Some(initializing_fragment),
463 context_type,
464 contents: vec![],
465 real_stacking_contexts_and_positioned_stacking_containers: vec![],
466 float_stacking_containers: vec![],
467 atomic_inline_stacking_containers: vec![],
468 debug_print_items: self.debug_print_items.is_some().then(|| vec![].into()),
469 }
470 }
471
472 fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DiagnosticsLogging) -> Self {
473 Self {
474 scroll_tree_node_id: root_scroll_node_id,
475 clip_id: None,
476 initializing_fragment: None,
477 context_type: StackingContextType::RealStackingContext,
478 contents: vec![],
479 real_stacking_contexts_and_positioned_stacking_containers: vec![],
480 float_stacking_containers: vec![],
481 atomic_inline_stacking_containers: vec![],
482 debug_print_items: debug.stacking_context_tree.then(|| vec![].into()),
483 }
484 }
485
486 fn add_stacking_context(&mut self, stacking_context: StackingContext) {
488 match stacking_context.context_type {
489 StackingContextType::RealStackingContext => {
490 &mut self.real_stacking_contexts_and_positioned_stacking_containers
491 },
492 StackingContextType::PositionedStackingContainer => {
493 &mut self.real_stacking_contexts_and_positioned_stacking_containers
494 },
495 StackingContextType::FloatStackingContainer => &mut self.float_stacking_containers,
496 StackingContextType::AtomicInlineStackingContainer => {
497 &mut self.atomic_inline_stacking_containers
498 },
499 }
500 .push(stacking_context)
501 }
502
503 pub(crate) fn z_index(&self) -> i32 {
504 self.initializing_fragment.as_ref().map_or(0, |fragment| {
505 let fragment = fragment.borrow();
506 fragment.style.effective_z_index(fragment.base.flags)
507 })
508 }
509
510 pub(crate) fn sort(&mut self) {
511 self.contents.sort_by_key(|a| a.section());
512 self.real_stacking_contexts_and_positioned_stacking_containers
513 .sort_by_key(|a| a.z_index());
514
515 debug_assert!(
516 self.real_stacking_contexts_and_positioned_stacking_containers
517 .iter()
518 .all(|c| matches!(
519 c.context_type,
520 StackingContextType::RealStackingContext |
521 StackingContextType::PositionedStackingContainer
522 ))
523 );
524 debug_assert!(
525 self.float_stacking_containers
526 .iter()
527 .all(
528 |c| c.context_type == StackingContextType::FloatStackingContainer &&
529 c.z_index() == 0
530 )
531 );
532 debug_assert!(
533 self.atomic_inline_stacking_containers
534 .iter()
535 .all(
536 |c| c.context_type == StackingContextType::AtomicInlineStackingContainer &&
537 c.z_index() == 0
538 )
539 );
540 }
541
542 fn push_webrender_stacking_context_if_necessary(
543 &self,
544 builder: &mut DisplayListBuilder,
545 ) -> bool {
546 let fragment = match self.initializing_fragment.as_ref() {
547 Some(fragment) => fragment.borrow(),
548 None => return false,
549 };
550
551 let style = &fragment.style;
554 let effects = style.get_effects();
555 if effects.filter.0.is_empty() &&
556 effects.opacity == 1.0 &&
557 effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
558 !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
559 style.clone_clip_path() == ClipPath::None
560 {
561 return false;
562 }
563
564 let current_color = style.clone_color();
566 let mut filters: Vec<wr::FilterOp> = effects
567 .filter
568 .0
569 .iter()
570 .map(|filter| FilterToWebRender::to_webrender(filter, ¤t_color))
571 .collect();
572 if effects.opacity != 1.0 {
573 filters.push(wr::FilterOp::Opacity(
574 effects.opacity.into(),
575 effects.opacity,
576 ));
577 }
578
579 let spatial_id = builder.spatial_id(self.scroll_tree_node_id);
586 let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id));
587 builder.wr().push_stacking_context(
588 LayoutPoint::zero(), spatial_id,
590 style.get_webrender_primitive_flags(),
591 clip_chain_id,
592 style.get_used_transform_style().to_webrender(),
593 effects.mix_blend_mode.to_webrender(),
594 &filters,
595 &[], &[], wr::RasterSpace::Screen,
598 wr::StackingContextFlags::empty(),
599 None, );
601
602 true
603 }
604
605 pub(crate) fn build_canvas_background_display_list(
609 &self,
610 builder: &mut DisplayListBuilder,
611 fragment_tree: &crate::fragment_tree::FragmentTree,
612 ) {
613 let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
614 fragment
615 .base()
616 .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
617 }) else {
618 return;
619 };
620 let root_fragment = match root_fragment {
621 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
622 _ => return,
623 }
624 .borrow();
625
626 let source_style = {
627 if root_fragment.style.background_is_transparent() {
633 let body_fragment = fragment_tree.body_fragment();
634 builder.paint_body_background = body_fragment.is_none();
635 body_fragment
636 .map(|body_fragment| body_fragment.borrow().style.clone())
637 .unwrap_or(root_fragment.style.clone())
638 } else {
639 root_fragment.style.clone()
640 }
641 };
642
643 if source_style.background_is_transparent() {
646 return;
647 }
648
649 let painting_area = fragment_tree
656 .initial_containing_block
657 .union(&fragment_tree.scrollable_overflow())
658 .to_webrender();
659
660 let background_color =
661 source_style.resolve_color(&source_style.get_background().background_color);
662 if background_color.alpha > 0.0 {
663 let common = builder.common_properties(painting_area, &source_style);
664 let color = super::rgba(background_color);
665 builder.wr().push_rect(&common, painting_area, color)
666 }
667
668 let mut fragment_builder = BuilderForBoxFragment::new(
669 &root_fragment,
670 &fragment_tree.initial_containing_block,
671 false, false, );
674 let painter = super::background::BackgroundPainter {
675 style: &source_style,
676 painting_area_override: Some(painting_area),
677 positioning_area_override: None,
678 };
679 fragment_builder.build_background_image(builder, &painter);
680 }
681
682 pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
686 let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
687
688 let mut content_with_outlines = Vec::new();
697 let mut contents = self.contents.iter().enumerate().peekable();
698 while contents.peek().is_some_and(|(_, child)| {
699 child.section() == StackingContextSection::OwnBackgroundsAndBorders
700 }) {
701 let (i, child) = contents.next().unwrap();
702 self.debug_push_print_item(DebugPrintField::Contents, i);
703 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
704
705 if child.has_outline() {
706 content_with_outlines.push(child);
707 }
708 }
709
710 let mut real_stacking_contexts_and_positioned_stacking_containers = self
712 .real_stacking_contexts_and_positioned_stacking_containers
713 .iter()
714 .enumerate()
715 .peekable();
716 while real_stacking_contexts_and_positioned_stacking_containers
717 .peek()
718 .is_some_and(|(_, child)| child.z_index() < 0)
719 {
720 let (i, child) = real_stacking_contexts_and_positioned_stacking_containers
721 .next()
722 .unwrap();
723 self.debug_push_print_item(
724 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
725 i,
726 );
727 child.build_display_list(builder);
728 }
729
730 while contents.peek().is_some_and(|(_, child)| {
732 child.section() == StackingContextSection::DescendantBackgroundsAndBorders
733 }) {
734 let (i, child) = contents.next().unwrap();
735 self.debug_push_print_item(DebugPrintField::Contents, i);
736 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
737
738 if child.has_outline() {
739 content_with_outlines.push(child);
740 }
741 }
742
743 for (i, child) in self.float_stacking_containers.iter().enumerate() {
745 self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
746 child.build_display_list(builder);
747 }
748
749 while contents
751 .peek()
752 .is_some_and(|(_, child)| child.section() == StackingContextSection::Foreground)
753 {
754 let (i, child) = contents.next().unwrap();
755 self.debug_push_print_item(DebugPrintField::Contents, i);
756 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
757
758 if child.has_outline() {
759 content_with_outlines.push(child);
760 }
761 }
762
763 for (i, child) in real_stacking_contexts_and_positioned_stacking_containers {
766 self.debug_push_print_item(
767 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
768 i,
769 );
770 child.build_display_list(builder);
771 }
772
773 for content in content_with_outlines {
775 content.build_display_list_with_section_override(
776 builder,
777 &self.atomic_inline_stacking_containers,
778 Some(StackingContextSection::Outline),
779 );
780 }
781
782 if pushed_context {
783 builder.wr().pop_stacking_context();
784 }
785 }
786
787 fn debug_push_print_item(&self, field: DebugPrintField, index: usize) {
792 if let Some(items) = self.debug_print_items.as_ref() {
793 items.borrow_mut().push(DebugPrintItem { field, index });
794 }
795 }
796
797 pub fn debug_print(&self) {
799 if self.debug_print_items.is_none() {
800 warn!("failed to print stacking context tree: debug_print_items was None");
801 return;
802 }
803 let mut tree = PrintTree::new("Stacking context tree".to_owned());
804 self.debug_print_with_tree(&mut tree);
805 }
806
807 fn debug_print_with_tree(&self, tree: &mut PrintTree) {
809 match self.context_type {
810 StackingContextType::RealStackingContext => {
811 tree.new_level(format!("{:?} z={}", self.context_type, self.z_index()));
812 },
813 StackingContextType::AtomicInlineStackingContainer => {
814 },
816 _ => {
817 tree.new_level(format!("{:?}", self.context_type));
818 },
819 }
820 for DebugPrintItem { field, index } in
821 self.debug_print_items.as_ref().unwrap().borrow().iter()
822 {
823 match field {
824 DebugPrintField::Contents => match self.contents[*index] {
825 StackingContextContent::Fragment { section, .. } => {
826 tree.add_item(format!("{section:?}"));
827 },
828 StackingContextContent::AtomicInlineStackingContainer { index } => {
829 tree.new_level(format!("AtomicInlineStackingContainer #{index}"));
830 self.atomic_inline_stacking_containers[index].debug_print_with_tree(tree);
831 tree.end_level();
832 },
833 },
834 DebugPrintField::RealStackingContextsAndPositionedStackingContainers => {
835 self.real_stacking_contexts_and_positioned_stacking_containers[*index]
836 .debug_print_with_tree(tree);
837 },
838 DebugPrintField::FloatStackingContainers => {
839 self.float_stacking_containers[*index].debug_print_with_tree(tree);
840 },
841 }
842 }
843 match self.context_type {
844 StackingContextType::AtomicInlineStackingContainer => {
845 },
847 _ => {
848 tree.end_level();
849 },
850 }
851 }
852}
853
854#[derive(PartialEq)]
855pub(crate) enum StackingContextBuildMode {
856 IncludeHoisted,
857 SkipHoisted,
858}
859
860impl Fragment {
861 pub(crate) fn build_stacking_context_tree(
862 &self,
863 stacking_context_tree: &mut StackingContextTree,
864 containing_block_info: &ContainingBlockInfo,
865 stacking_context: &mut StackingContext,
866 mode: StackingContextBuildMode,
867 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
868 ) {
869 if self
870 .base()
871 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
872 {
873 return;
874 }
875
876 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
877 let fragment_clone = self.clone();
878 match self {
879 Fragment::Box(fragment) | Fragment::Float(fragment) => {
880 let fragment = fragment.borrow();
881 if mode == StackingContextBuildMode::SkipHoisted &&
882 fragment.style.clone_position().is_absolutely_positioned()
883 {
884 return;
885 }
886
887 let text_decorations = match self {
888 Fragment::Float(..) => &Default::default(),
889 _ => text_decorations,
890 };
891
892 fragment.build_stacking_context_tree(
893 fragment_clone,
894 stacking_context_tree,
895 containing_block,
896 containing_block_info,
897 stacking_context,
898 text_decorations,
899 );
900 },
901 Fragment::AbsoluteOrFixedPositioned(fragment) => {
902 let shared_fragment = fragment.borrow();
903 let fragment_ref = match shared_fragment.fragment.as_ref() {
904 Some(fragment_ref) => fragment_ref,
905 None => unreachable!("Found hoisted box with missing fragment."),
906 };
907
908 fragment_ref.build_stacking_context_tree(
909 stacking_context_tree,
910 containing_block_info,
911 stacking_context,
912 StackingContextBuildMode::IncludeHoisted,
913 &Default::default(),
914 );
915 },
916 Fragment::Positioning(fragment) => {
917 let fragment = fragment.borrow();
918 fragment.build_stacking_context_tree(
919 stacking_context_tree,
920 containing_block,
921 containing_block_info,
922 stacking_context,
923 text_decorations,
924 );
925 },
926 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
927 stacking_context
928 .contents
929 .push(StackingContextContent::Fragment {
930 section: StackingContextSection::Foreground,
931 scroll_node_id: containing_block.scroll_node_id,
932 reference_frame_scroll_node_id: containing_block_info
933 .for_absolute_and_fixed_descendants
934 .scroll_node_id,
935 clip_id: containing_block.clip_id,
936 containing_block: containing_block.rect,
937 fragment: fragment_clone,
938 is_hit_test_for_scrollable_overflow: false,
939 is_collapsed_table_borders: false,
940 text_decorations: text_decorations.clone(),
941 });
942 },
943 }
944 }
945}
946
947struct ReferenceFrameData {
948 origin: crate::geom::PhysicalPoint<Au>,
949 transform: LayoutTransform,
950 kind: wr::ReferenceFrameKind,
951}
952struct ScrollFrameData {
953 scroll_tree_node_id: ScrollTreeNodeId,
954 scroll_frame_rect: LayoutRect,
955}
956
957struct OverflowFrameData {
958 clip_id: ClipId,
959 scroll_frame_data: Option<ScrollFrameData>,
960}
961
962impl BoxFragment {
963 fn get_stacking_context_type(&self) -> Option<StackingContextType> {
964 let flags = self.base.flags;
965 if self.style.establishes_stacking_context(flags) {
966 return Some(StackingContextType::RealStackingContext);
967 }
968
969 let box_style = &self.style.get_box();
970 if box_style.position != ComputedPosition::Static {
971 return Some(StackingContextType::PositionedStackingContainer);
972 }
973
974 if box_style.float != ComputedFloat::None {
975 return Some(StackingContextType::FloatStackingContainer);
976 }
977
978 if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
982 return Some(StackingContextType::AtomicInlineStackingContainer);
983 }
984
985 None
986 }
987
988 fn get_stacking_context_section(&self) -> StackingContextSection {
989 if self.get_stacking_context_type().is_some() {
990 return StackingContextSection::OwnBackgroundsAndBorders;
991 }
992
993 if self.style.get_box().display.outside() == DisplayOutside::Inline {
994 return StackingContextSection::Foreground;
995 }
996
997 StackingContextSection::DescendantBackgroundsAndBorders
998 }
999
1000 fn build_stacking_context_tree(
1001 &self,
1002 fragment: Fragment,
1003 stacking_context_tree: &mut StackingContextTree,
1004 containing_block: &ContainingBlock,
1005 containing_block_info: &ContainingBlockInfo,
1006 parent_stacking_context: &mut StackingContext,
1007 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1008 ) {
1009 self.build_stacking_context_tree_maybe_creating_reference_frame(
1010 fragment,
1011 stacking_context_tree,
1012 containing_block,
1013 containing_block_info,
1014 parent_stacking_context,
1015 text_decorations,
1016 );
1017 }
1018
1019 fn build_stacking_context_tree_maybe_creating_reference_frame(
1020 &self,
1021 fragment: Fragment,
1022 stacking_context_tree: &mut StackingContextTree,
1023 containing_block: &ContainingBlock,
1024 containing_block_info: &ContainingBlockInfo,
1025 parent_stacking_context: &mut StackingContext,
1026 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1027 ) {
1028 let reference_frame_data =
1029 match self.reference_frame_data_if_necessary(&containing_block.rect) {
1030 Some(reference_frame_data) => reference_frame_data,
1031 None => {
1032 return self.build_stacking_context_tree_maybe_creating_stacking_context(
1033 fragment,
1034 stacking_context_tree,
1035 containing_block,
1036 containing_block_info,
1037 parent_stacking_context,
1038 text_decorations,
1039 );
1040 },
1041 };
1042
1043 if !reference_frame_data.transform.is_invertible() {
1047 self.clear_spatial_tree_node_including_descendants();
1048 return;
1049 }
1050
1051 let frame_origin_for_query = self.cumulative_border_box_rect().origin.to_webrender();
1052
1053 let new_spatial_id = stacking_context_tree.push_reference_frame(
1054 reference_frame_data.origin.to_webrender(),
1055 frame_origin_for_query,
1056 containing_block.scroll_node_id,
1057 self.style.get_box().transform_style.to_webrender(),
1058 reference_frame_data.transform,
1059 reference_frame_data.kind,
1060 );
1061
1062 assert!(
1072 self.style
1073 .establishes_containing_block_for_all_descendants(self.base.flags)
1074 );
1075 let adjusted_containing_block = ContainingBlock::new(
1076 containing_block
1077 .rect
1078 .translate(-reference_frame_data.origin.to_vector()),
1079 new_spatial_id,
1080 None,
1081 containing_block.clip_id,
1082 );
1083 let new_containing_block_info =
1084 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
1085
1086 self.build_stacking_context_tree_maybe_creating_stacking_context(
1087 fragment,
1088 stacking_context_tree,
1089 &adjusted_containing_block,
1090 &new_containing_block_info,
1091 parent_stacking_context,
1092 text_decorations,
1093 );
1094 }
1095
1096 fn build_stacking_context_tree_maybe_creating_stacking_context(
1097 &self,
1098 fragment: Fragment,
1099 stacking_context_tree: &mut StackingContextTree,
1100 containing_block: &ContainingBlock,
1101 containing_block_info: &ContainingBlockInfo,
1102 parent_stacking_context: &mut StackingContext,
1103 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1104 ) {
1105 let context_type = match self.get_stacking_context_type() {
1106 Some(context_type) => context_type,
1107 None => {
1108 self.build_stacking_context_tree_for_children(
1109 fragment,
1110 stacking_context_tree,
1111 containing_block,
1112 containing_block_info,
1113 parent_stacking_context,
1114 text_decorations,
1115 );
1116 return;
1117 },
1118 };
1119
1120 if context_type == StackingContextType::AtomicInlineStackingContainer {
1121 parent_stacking_context.contents.push(
1123 StackingContextContent::AtomicInlineStackingContainer {
1124 index: parent_stacking_context
1125 .atomic_inline_stacking_containers
1126 .len(),
1127 },
1128 );
1129 }
1130
1131 let stacking_context_clip_id = stacking_context_tree
1135 .clip_store
1136 .add_for_clip_path(
1137 self.style.clone_clip_path(),
1138 containing_block.scroll_node_id,
1139 containing_block.clip_id,
1140 BuilderForBoxFragment::new(
1141 self,
1142 &containing_block.rect,
1143 false, false, ),
1146 )
1147 .unwrap_or(containing_block.clip_id);
1148
1149 let box_fragment = match fragment {
1150 Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
1151 box_fragment.clone()
1152 },
1153 _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
1154 };
1155
1156 let mut child_stacking_context = parent_stacking_context.create_descendant(
1157 containing_block.scroll_node_id,
1158 stacking_context_clip_id,
1159 box_fragment,
1160 context_type,
1161 );
1162 self.build_stacking_context_tree_for_children(
1163 fragment,
1164 stacking_context_tree,
1165 containing_block,
1166 containing_block_info,
1167 &mut child_stacking_context,
1168 text_decorations,
1169 );
1170
1171 let mut stolen_children = vec![];
1172 if context_type != StackingContextType::RealStackingContext {
1173 stolen_children = mem::replace(
1174 &mut child_stacking_context
1175 .real_stacking_contexts_and_positioned_stacking_containers,
1176 stolen_children,
1177 );
1178 }
1179
1180 child_stacking_context.sort();
1181 parent_stacking_context.add_stacking_context(child_stacking_context);
1182 parent_stacking_context
1183 .real_stacking_contexts_and_positioned_stacking_containers
1184 .append(&mut stolen_children);
1185 }
1186
1187 fn build_stacking_context_tree_for_children(
1188 &self,
1189 fragment: Fragment,
1190 stacking_context_tree: &mut StackingContextTree,
1191 containing_block: &ContainingBlock,
1192 containing_block_info: &ContainingBlockInfo,
1193 stacking_context: &mut StackingContext,
1194 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1195 ) {
1196 let mut new_scroll_node_id = containing_block.scroll_node_id;
1197 let mut new_clip_id = containing_block.clip_id;
1198 let mut new_scroll_frame_size = containing_block_info
1199 .for_non_absolute_descendants
1200 .scroll_frame_size;
1201
1202 if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
1203 stacking_context_tree,
1204 new_scroll_node_id,
1205 &containing_block.rect,
1206 &new_scroll_frame_size,
1207 ) {
1208 new_scroll_node_id = scroll_node_id;
1209 }
1210
1211 if let Some(clip_id) = self.build_clip_frame_if_necessary(
1212 stacking_context_tree,
1213 new_scroll_node_id,
1214 new_clip_id,
1215 &containing_block.rect,
1216 ) {
1217 new_clip_id = clip_id;
1218 }
1219
1220 if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
1221 self.style.clone_clip_path(),
1222 new_scroll_node_id,
1223 new_clip_id,
1224 BuilderForBoxFragment::new(
1225 self,
1226 &containing_block.rect,
1227 false, false, ),
1230 ) {
1231 new_clip_id = clip_id;
1232 }
1233
1234 let establishes_containing_block_for_all_descendants = self
1235 .style
1236 .establishes_containing_block_for_all_descendants(self.base.flags);
1237 let establishes_containing_block_for_absolute_descendants = self
1238 .style
1239 .establishes_containing_block_for_absolute_descendants(self.base.flags);
1240
1241 let reference_frame_scroll_node_id_for_fragments =
1242 if establishes_containing_block_for_all_descendants {
1243 new_scroll_node_id
1244 } else {
1245 containing_block_info
1246 .for_absolute_and_fixed_descendants
1247 .scroll_node_id
1248 };
1249
1250 let mut add_fragment = |section| {
1251 stacking_context
1252 .contents
1253 .push(StackingContextContent::Fragment {
1254 scroll_node_id: new_scroll_node_id,
1255 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1256 clip_id: new_clip_id,
1257 section,
1258 containing_block: containing_block.rect,
1259 fragment: fragment.clone(),
1260 is_hit_test_for_scrollable_overflow: false,
1261 is_collapsed_table_borders: false,
1262 text_decorations: text_decorations.clone(),
1263 });
1264 };
1265
1266 let section = self.get_stacking_context_section();
1267 add_fragment(section);
1268
1269 *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1273
1274 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
1277 stacking_context_tree,
1278 new_scroll_node_id,
1279 new_clip_id,
1280 &containing_block.rect,
1281 ) {
1282 new_clip_id = overflow_frame_data.clip_id;
1283 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
1284 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
1285 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
1286 stacking_context
1287 .contents
1288 .push(StackingContextContent::Fragment {
1289 scroll_node_id: new_scroll_node_id,
1290 reference_frame_scroll_node_id:
1291 reference_frame_scroll_node_id_for_fragments,
1292 clip_id: new_clip_id,
1293 section,
1294 containing_block: containing_block.rect,
1295 fragment: fragment.clone(),
1296 is_hit_test_for_scrollable_overflow: true,
1297 is_collapsed_table_borders: false,
1298 text_decorations: text_decorations.clone(),
1299 });
1300 }
1301 }
1302
1303 let padding_rect = self
1304 .padding_rect()
1305 .translate(containing_block.rect.origin.to_vector());
1306 let content_rect = self
1307 .content_rect
1308 .translate(containing_block.rect.origin.to_vector());
1309
1310 let for_absolute_descendants = ContainingBlock::new(
1311 padding_rect,
1312 new_scroll_node_id,
1313 new_scroll_frame_size,
1314 new_clip_id,
1315 );
1316 let for_non_absolute_descendants = ContainingBlock::new(
1317 content_rect,
1318 new_scroll_node_id,
1319 new_scroll_frame_size,
1320 new_clip_id,
1321 );
1322
1323 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
1327 containing_block_info.new_for_absolute_and_fixed_descendants(
1328 &for_non_absolute_descendants,
1329 &for_absolute_descendants,
1330 )
1331 } else if establishes_containing_block_for_absolute_descendants {
1332 containing_block_info.new_for_absolute_descendants(
1333 &for_non_absolute_descendants,
1334 &for_absolute_descendants,
1335 )
1336 } else {
1337 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
1338 };
1339
1340 let text_decorations = match self.is_atomic_inline_level() ||
1346 self.base
1347 .flags
1348 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
1349 {
1350 true => &Default::default(),
1351 false => text_decorations,
1352 };
1353
1354 let new_text_decoration;
1355 let text_decorations = match self.style.clone_text_decoration_line() {
1356 TextDecorationLine::NONE => text_decorations,
1357 line => {
1358 let mut new_vector = (**text_decorations).clone();
1359 let color = &self.style.get_inherited_text().color;
1360 new_vector.push(FragmentTextDecoration {
1361 line,
1362 color: self
1363 .style
1364 .clone_text_decoration_color()
1365 .resolve_to_absolute(color),
1366 style: self.style.clone_text_decoration_style(),
1367 });
1368 new_text_decoration = Arc::new(new_vector);
1369 &new_text_decoration
1370 },
1371 };
1372
1373 for child in &self.children {
1374 child.build_stacking_context_tree(
1375 stacking_context_tree,
1376 &new_containing_block_info,
1377 stacking_context,
1378 StackingContextBuildMode::SkipHoisted,
1379 text_decorations,
1380 );
1381 }
1382
1383 if matches!(&fragment, Fragment::Box(box_fragment) if matches!(
1384 box_fragment.borrow().specific_layout_info(),
1385 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
1386 )) {
1387 stacking_context
1388 .contents
1389 .push(StackingContextContent::Fragment {
1390 scroll_node_id: new_scroll_node_id,
1391 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1392 clip_id: new_clip_id,
1393 section,
1394 containing_block: containing_block.rect,
1395 fragment: fragment.clone(),
1396 is_hit_test_for_scrollable_overflow: false,
1397 is_collapsed_table_borders: true,
1398 text_decorations: text_decorations.clone(),
1399 });
1400 }
1401 }
1402
1403 fn build_clip_frame_if_necessary(
1404 &self,
1405 stacking_context_tree: &mut StackingContextTree,
1406 parent_scroll_node_id: ScrollTreeNodeId,
1407 parent_clip_id: ClipId,
1408 containing_block_rect: &PhysicalRect<Au>,
1409 ) -> Option<ClipId> {
1410 let position = self.style.get_box().position;
1411 if !position.is_absolutely_positioned() {
1414 return None;
1415 }
1416
1417 let clip_rect = match self.style.get_effects().clip {
1419 ClipRectOrAuto::Rect(rect) => rect,
1420 _ => return None,
1421 };
1422
1423 let border_rect = self.border_rect();
1424 let clip_rect = clip_rect
1425 .for_border_rect(border_rect)
1426 .translate(containing_block_rect.origin.to_vector())
1427 .to_webrender();
1428 Some(stacking_context_tree.clip_store.add(
1429 BorderRadius::zero(),
1430 clip_rect,
1431 parent_scroll_node_id,
1432 parent_clip_id,
1433 ))
1434 }
1435
1436 fn build_overflow_frame_if_necessary(
1437 &self,
1438 stacking_context_tree: &mut StackingContextTree,
1439 parent_scroll_node_id: ScrollTreeNodeId,
1440 parent_clip_id: ClipId,
1441 containing_block_rect: &PhysicalRect<Au>,
1442 ) -> Option<OverflowFrameData> {
1443 let overflow = self.style.effective_overflow(self.base.flags);
1444
1445 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1446 return None;
1447 }
1448
1449 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1451 let mut overflow_clip_rect = self
1455 .padding_rect()
1456 .translate(containing_block_rect.origin.to_vector())
1457 .to_webrender();
1458
1459 let clip_margin = self.style.get_margin().overflow_clip_margin.px();
1462 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin, clip_margin);
1463
1464 let radii;
1467 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1468 let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
1469 radii = offset_radii(builder.border_radius, clip_margin);
1470 } else if overflow.x != ComputedOverflow::Clip {
1471 overflow_clip_rect.min.x = f32::MIN;
1472 overflow_clip_rect.max.x = f32::MAX;
1473 radii = BorderRadius::zero();
1474 } else {
1475 overflow_clip_rect.min.y = f32::MIN;
1476 overflow_clip_rect.max.y = f32::MAX;
1477 radii = BorderRadius::zero();
1478 }
1479
1480 let clip_id = stacking_context_tree.clip_store.add(
1481 radii,
1482 overflow_clip_rect,
1483 parent_scroll_node_id,
1484 parent_clip_id,
1485 );
1486
1487 return Some(OverflowFrameData {
1488 clip_id,
1489 scroll_frame_data: None,
1490 });
1491 }
1492
1493 let scroll_frame_rect = self
1494 .padding_rect()
1495 .translate(containing_block_rect.origin.to_vector())
1496 .to_webrender();
1497
1498 let clip_id = stacking_context_tree.clip_store.add(
1499 BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
1500 scroll_frame_rect,
1501 parent_scroll_node_id,
1502 parent_clip_id,
1503 );
1504
1505 let tag = self.base.tag?;
1506 let external_scroll_id = wr::ExternalScrollId(
1507 tag.to_display_list_fragment_id(),
1508 stacking_context_tree.compositor_info.pipeline_id,
1509 );
1510
1511 let sensitivity = AxesScrollSensitivity {
1512 x: overflow.x.into(),
1513 y: overflow.y.into(),
1514 };
1515
1516 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1517 parent_scroll_node_id,
1518 external_scroll_id,
1519 self.scrollable_overflow().to_webrender(),
1520 scroll_frame_rect,
1521 sensitivity,
1522 );
1523
1524 Some(OverflowFrameData {
1525 clip_id,
1526 scroll_frame_data: Some(ScrollFrameData {
1527 scroll_tree_node_id,
1528 scroll_frame_rect,
1529 }),
1530 })
1531 }
1532
1533 fn build_sticky_frame_if_necessary(
1534 &self,
1535 stacking_context_tree: &mut StackingContextTree,
1536 parent_scroll_node_id: ScrollTreeNodeId,
1537 containing_block_rect: &PhysicalRect<Au>,
1538 scroll_frame_size: &Option<LayoutSize>,
1539 ) -> Option<ScrollTreeNodeId> {
1540 if self.style.get_box().position != ComputedPosition::Sticky {
1541 return None;
1542 }
1543
1544 let scroll_frame_size_for_resolve = match scroll_frame_size {
1545 Some(size) => size,
1546 None => {
1547 &stacking_context_tree
1549 .compositor_info
1550 .viewport_details
1551 .layout_size()
1552 },
1553 };
1554
1555 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1559 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1560 let offsets = self.style.physical_box_offsets();
1561 let offsets = PhysicalSides::<AuOrAuto>::new(
1562 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1563 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1564 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1565 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1566 );
1567 *self.resolved_sticky_insets.borrow_mut() = Some(offsets);
1568
1569 if scroll_frame_size.is_none() {
1570 return None;
1571 }
1572
1573 if offsets.top.is_auto() &&
1574 offsets.right.is_auto() &&
1575 offsets.bottom.is_auto() &&
1576 offsets.left.is_auto()
1577 {
1578 return None;
1579 }
1580
1581 let border_rect = self.border_rect();
1600 let computed_margin = self.style.physical_margin();
1601
1602 let distance_from_border_box_to_cb = PhysicalSides::new(
1606 border_rect.min_y(),
1607 containing_block_rect.width() - border_rect.max_x(),
1608 containing_block_rect.height() - border_rect.max_y(),
1609 border_rect.min_x(),
1610 );
1611
1612 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1616 let used_margin = if computed_margin.is_auto() {
1617 Au::zero()
1618 } else {
1619 used_margin
1620 };
1621 Au::zero().max(distance - used_margin).to_f32_px()
1622 };
1623
1624 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1627 -offset_bound(
1628 distance_from_border_box_to_cb.top,
1629 self.margin.top,
1630 computed_margin.top,
1631 ),
1632 offset_bound(
1633 distance_from_border_box_to_cb.bottom,
1634 self.margin.bottom,
1635 computed_margin.bottom,
1636 ),
1637 );
1638 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1639 -offset_bound(
1640 distance_from_border_box_to_cb.left,
1641 self.margin.left,
1642 computed_margin.left,
1643 ),
1644 offset_bound(
1645 distance_from_border_box_to_cb.right,
1646 self.margin.right,
1647 computed_margin.right,
1648 ),
1649 );
1650
1651 let frame_rect = border_rect
1652 .translate(containing_block_rect.origin.to_vector())
1653 .to_webrender();
1654
1655 let margins = SideOffsets2D::new(
1658 offsets.top.non_auto().map(|v| v.to_f32_px()),
1659 offsets.right.non_auto().map(|v| v.to_f32_px()),
1660 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1661 offsets.left.non_auto().map(|v| v.to_f32_px()),
1662 );
1663
1664 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1665 parent_scroll_node_id,
1666 frame_rect,
1667 margins,
1668 vertical_offset_bounds,
1669 horizontal_offset_bounds,
1670 );
1671
1672 Some(sticky_node_id)
1673 }
1674
1675 fn reference_frame_data_if_necessary(
1677 &self,
1678 containing_block_rect: &PhysicalRect<Au>,
1679 ) -> Option<ReferenceFrameData> {
1680 if !self
1681 .style
1682 .has_effective_transform_or_perspective(self.base.flags)
1683 {
1684 return None;
1685 }
1686
1687 let relative_border_rect = self.border_rect();
1688 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1689 let untyped_border_rect = border_rect.to_untyped();
1690
1691 let transform = self.calculate_transform_matrix(&untyped_border_rect);
1692 let perspective = self.calculate_perspective_matrix(&untyped_border_rect);
1693 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1694 (None, Some(perspective)) => (
1695 perspective,
1696 wr::ReferenceFrameKind::Perspective {
1697 scrolling_relative_to: None,
1698 },
1699 ),
1700 (Some(transform), None) => (
1701 transform,
1702 wr::ReferenceFrameKind::Transform {
1703 is_2d_scale_translation: false,
1704 should_snap: false,
1705 paired_with_perspective: false,
1706 },
1707 ),
1708 (Some(transform), Some(perspective)) => (
1709 perspective.then(&transform),
1710 wr::ReferenceFrameKind::Perspective {
1711 scrolling_relative_to: None,
1712 },
1713 ),
1714 (None, None) => unreachable!(),
1715 };
1716
1717 Some(ReferenceFrameData {
1718 origin: border_rect.origin,
1719 transform: reference_frame_transform,
1720 kind: reference_frame_kind,
1721 })
1722 }
1723
1724 pub fn calculate_transform_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> {
1726 let list = &self.style.get_box().transform;
1727 let length_rect = au_rect_to_length_rect(border_rect);
1728 let rotate = match self.style.clone_rotate() {
1730 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1731 GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle),
1732 GenericRotate::None => (0., 0., 1., Angle::zero()),
1733 };
1734 let scale = match self.style.clone_scale() {
1735 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1736 GenericScale::None => (1., 1., 1.),
1737 };
1738 let translation = match self.style.clone_translate() {
1739 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1740 x.resolve(length_rect.size.width).px(),
1741 y.resolve(length_rect.size.height).px(),
1742 z.px(),
1743 ),
1744 GenericTranslate::None => LayoutTransform::identity(),
1745 };
1746
1747 let angle = euclid::Angle::radians(rotate.3.radians());
1748 let transform_base = list.to_transform_3d_matrix(Some(&length_rect)).ok()?;
1749 let transform = LayoutTransform::from_untyped(&transform_base.0)
1750 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1751 .then_scale(scale.0, scale.1, scale.2)
1752 .then(&translation);
1753
1754 let transform_origin = &self.style.get_box().transform_origin;
1755 let transform_origin_x = transform_origin
1756 .horizontal
1757 .to_used_value(border_rect.size.width)
1758 .to_f32_px();
1759 let transform_origin_y = transform_origin
1760 .vertical
1761 .to_used_value(border_rect.size.height)
1762 .to_f32_px();
1763 let transform_origin_z = transform_origin.depth.px();
1764
1765 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1766 }
1767
1768 pub fn calculate_perspective_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> {
1770 match self.style.get_box().perspective {
1771 Perspective::Length(length) => {
1772 let perspective_origin = &self.style.get_box().perspective_origin;
1773 let perspective_origin = LayoutPoint::new(
1774 perspective_origin
1775 .horizontal
1776 .percentage_relative_to(border_rect.size.width.into())
1777 .px(),
1778 perspective_origin
1779 .vertical
1780 .percentage_relative_to(border_rect.size.height.into())
1781 .px(),
1782 );
1783
1784 let perspective_matrix = LayoutTransform::from_untyped(
1785 &transform::create_perspective_matrix(length.px()),
1786 );
1787
1788 Some(perspective_matrix.change_basis(
1789 perspective_origin.x,
1790 perspective_origin.y,
1791 0.0,
1792 ))
1793 },
1794 Perspective::None => None,
1795 }
1796 }
1797
1798 fn clear_spatial_tree_node_including_descendants(&self) {
1799 fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1800 for fragment in fragments.iter() {
1801 match fragment {
1802 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1803 box_fragment
1804 .borrow()
1805 .clear_spatial_tree_node_including_descendants();
1806 },
1807 Fragment::Positioning(positioning_fragment) => {
1808 assign_spatial_tree_node_on_fragments(
1809 &positioning_fragment.borrow().children,
1810 );
1811 },
1812 _ => {},
1813 }
1814 }
1815 }
1816
1817 *self.spatial_tree_node.borrow_mut() = None;
1818 assign_spatial_tree_node_on_fragments(&self.children);
1819 }
1820}
1821
1822impl PositioningFragment {
1823 fn build_stacking_context_tree(
1824 &self,
1825 stacking_context_tree: &mut StackingContextTree,
1826 containing_block: &ContainingBlock,
1827 containing_block_info: &ContainingBlockInfo,
1828 stacking_context: &mut StackingContext,
1829 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1830 ) {
1831 let rect = self
1832 .rect
1833 .translate(containing_block.rect.origin.to_vector());
1834 let new_containing_block = containing_block.new_replacing_rect(&rect);
1835 let new_containing_block_info =
1836 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1837
1838 for child in &self.children {
1839 child.build_stacking_context_tree(
1840 stacking_context_tree,
1841 &new_containing_block_info,
1842 stacking_context,
1843 StackingContextBuildMode::SkipHoisted,
1844 text_decorations,
1845 );
1846 }
1847 }
1848}
1849
1850pub fn au_rect_to_length_rect(rect: &Rect<Au>) -> Rect<Length> {
1851 Rect::new(
1852 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1853 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1854 )
1855}