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::DebugOptions;
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, 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: &DebugOptions,
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 wr::Epoch(0),
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.dump_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(
308 &self,
309 builder: &mut DisplayListBuilder,
310 inline_stacking_containers: &[StackingContext],
311 ) {
312 match self {
313 Self::Fragment {
314 scroll_node_id,
315 reference_frame_scroll_node_id,
316 clip_id,
317 section,
318 containing_block,
319 fragment,
320 is_hit_test_for_scrollable_overflow,
321 is_collapsed_table_borders,
322 text_decorations,
323 } => {
324 builder.current_scroll_node_id = *scroll_node_id;
325 builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
326 builder.current_clip_id = *clip_id;
327 fragment.build_display_list(
328 builder,
329 containing_block,
330 *section,
331 *is_hit_test_for_scrollable_overflow,
332 *is_collapsed_table_borders,
333 text_decorations,
334 );
335 },
336 Self::AtomicInlineStackingContainer { index } => {
337 inline_stacking_containers[*index].build_display_list(builder);
338 },
339 }
340 }
341}
342
343#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
344pub(crate) enum StackingContextType {
345 RealStackingContext,
346 PositionedStackingContainer,
347 FloatStackingContainer,
348 AtomicInlineStackingContainer,
349}
350
351#[derive(MallocSizeOf)]
357pub struct StackingContext {
358 scroll_tree_node_id: ScrollTreeNodeId,
361
362 clip_id: Option<ClipId>,
364
365 initializing_fragment: Option<ArcRefCell<BoxFragment>>,
368
369 context_type: StackingContextType,
371
372 pub(super) contents: Vec<StackingContextContent>,
374
375 pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
385
386 pub(super) float_stacking_containers: Vec<StackingContext>,
391
392 pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
400
401 debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
403}
404
405#[derive(Clone, Copy, MallocSizeOf)]
407pub struct DebugPrintItem {
408 field: DebugPrintField,
409 index: usize,
410}
411
412#[derive(Clone, Copy, MallocSizeOf)]
414pub enum DebugPrintField {
415 Contents,
416 RealStackingContextsAndPositionedStackingContainers,
417 FloatStackingContainers,
418}
419
420impl StackingContext {
421 fn create_descendant(
422 &self,
423 spatial_id: ScrollTreeNodeId,
424 clip_id: ClipId,
425 initializing_fragment: ArcRefCell<BoxFragment>,
426 context_type: StackingContextType,
427 ) -> Self {
428 let clip_id = match clip_id {
433 ClipId::INVALID => None,
434 clip_id => Some(clip_id),
435 };
436 Self {
437 scroll_tree_node_id: spatial_id,
438 clip_id,
439 initializing_fragment: Some(initializing_fragment),
440 context_type,
441 contents: vec![],
442 real_stacking_contexts_and_positioned_stacking_containers: vec![],
443 float_stacking_containers: vec![],
444 atomic_inline_stacking_containers: vec![],
445 debug_print_items: self.debug_print_items.is_some().then(|| vec![].into()),
446 }
447 }
448
449 fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DebugOptions) -> Self {
450 Self {
451 scroll_tree_node_id: root_scroll_node_id,
452 clip_id: None,
453 initializing_fragment: None,
454 context_type: StackingContextType::RealStackingContext,
455 contents: vec![],
456 real_stacking_contexts_and_positioned_stacking_containers: vec![],
457 float_stacking_containers: vec![],
458 atomic_inline_stacking_containers: vec![],
459 debug_print_items: debug.dump_stacking_context_tree.then(|| vec![].into()),
460 }
461 }
462
463 fn add_stacking_context(&mut self, stacking_context: StackingContext) {
465 match stacking_context.context_type {
466 StackingContextType::RealStackingContext => {
467 &mut self.real_stacking_contexts_and_positioned_stacking_containers
468 },
469 StackingContextType::PositionedStackingContainer => {
470 &mut self.real_stacking_contexts_and_positioned_stacking_containers
471 },
472 StackingContextType::FloatStackingContainer => &mut self.float_stacking_containers,
473 StackingContextType::AtomicInlineStackingContainer => {
474 &mut self.atomic_inline_stacking_containers
475 },
476 }
477 .push(stacking_context)
478 }
479
480 pub(crate) fn z_index(&self) -> i32 {
481 self.initializing_fragment.as_ref().map_or(0, |fragment| {
482 let fragment = fragment.borrow();
483 fragment.style.effective_z_index(fragment.base.flags)
484 })
485 }
486
487 pub(crate) fn sort(&mut self) {
488 self.contents.sort_by_key(|a| a.section());
489 self.real_stacking_contexts_and_positioned_stacking_containers
490 .sort_by_key(|a| a.z_index());
491
492 debug_assert!(
493 self.real_stacking_contexts_and_positioned_stacking_containers
494 .iter()
495 .all(|c| matches!(
496 c.context_type,
497 StackingContextType::RealStackingContext |
498 StackingContextType::PositionedStackingContainer
499 ))
500 );
501 debug_assert!(
502 self.float_stacking_containers
503 .iter()
504 .all(
505 |c| c.context_type == StackingContextType::FloatStackingContainer &&
506 c.z_index() == 0
507 )
508 );
509 debug_assert!(
510 self.atomic_inline_stacking_containers
511 .iter()
512 .all(
513 |c| c.context_type == StackingContextType::AtomicInlineStackingContainer &&
514 c.z_index() == 0
515 )
516 );
517 }
518
519 fn push_webrender_stacking_context_if_necessary(
520 &self,
521 builder: &mut DisplayListBuilder,
522 ) -> bool {
523 let fragment = match self.initializing_fragment.as_ref() {
524 Some(fragment) => fragment.borrow(),
525 None => return false,
526 };
527
528 let style = &fragment.style;
531 let effects = style.get_effects();
532 if effects.filter.0.is_empty() &&
533 effects.opacity == 1.0 &&
534 effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
535 !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
536 style.clone_clip_path() == ClipPath::None
537 {
538 return false;
539 }
540
541 let current_color = style.clone_color();
543 let mut filters: Vec<wr::FilterOp> = effects
544 .filter
545 .0
546 .iter()
547 .map(|filter| FilterToWebRender::to_webrender(filter, ¤t_color))
548 .collect();
549 if effects.opacity != 1.0 {
550 filters.push(wr::FilterOp::Opacity(
551 effects.opacity.into(),
552 effects.opacity,
553 ));
554 }
555
556 let spatial_id = builder.spatial_id(self.scroll_tree_node_id);
563 let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id));
564 builder.wr().push_stacking_context(
565 LayoutPoint::zero(), spatial_id,
567 style.get_webrender_primitive_flags(),
568 clip_chain_id,
569 style.get_used_transform_style().to_webrender(),
570 effects.mix_blend_mode.to_webrender(),
571 &filters,
572 &[], &[], wr::RasterSpace::Screen,
575 wr::StackingContextFlags::empty(),
576 None, );
578
579 true
580 }
581
582 pub(crate) fn build_canvas_background_display_list(
586 &self,
587 builder: &mut DisplayListBuilder,
588 fragment_tree: &crate::fragment_tree::FragmentTree,
589 ) {
590 let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
591 fragment
592 .base()
593 .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
594 }) else {
595 return;
596 };
597 let root_fragment = match root_fragment {
598 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
599 _ => return,
600 }
601 .borrow();
602
603 let source_style = {
604 if root_fragment.style.background_is_transparent() {
610 let body_fragment = fragment_tree.body_fragment();
611 builder.paint_body_background = body_fragment.is_none();
612 body_fragment
613 .map(|body_fragment| body_fragment.borrow().style.clone())
614 .unwrap_or(root_fragment.style.clone())
615 } else {
616 root_fragment.style.clone()
617 }
618 };
619
620 if source_style.background_is_transparent() {
623 return;
624 }
625
626 let painting_area = fragment_tree
633 .initial_containing_block
634 .union(&fragment_tree.scrollable_overflow())
635 .to_webrender();
636
637 let background_color =
638 source_style.resolve_color(&source_style.get_background().background_color);
639 if background_color.alpha > 0.0 {
640 let common = builder.common_properties(painting_area, &source_style);
641 let color = super::rgba(background_color);
642 builder.wr().push_rect(&common, painting_area, color)
643 }
644
645 let mut fragment_builder = BuilderForBoxFragment::new(
646 &root_fragment,
647 &fragment_tree.initial_containing_block,
648 false, false, );
651 let painter = super::background::BackgroundPainter {
652 style: &source_style,
653 painting_area_override: Some(painting_area),
654 positioning_area_override: None,
655 };
656 fragment_builder.build_background_image(builder, &painter);
657 }
658
659 pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
663 let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
664
665 let mut contents = self.contents.iter().enumerate().peekable();
674 while contents.peek().is_some_and(|(_, child)| {
675 child.section() == StackingContextSection::OwnBackgroundsAndBorders
676 }) {
677 let (i, child) = contents.next().unwrap();
678 self.debug_push_print_item(DebugPrintField::Contents, i);
679 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
680 }
681
682 let mut real_stacking_contexts_and_positioned_stacking_containers = self
684 .real_stacking_contexts_and_positioned_stacking_containers
685 .iter()
686 .enumerate()
687 .peekable();
688 while real_stacking_contexts_and_positioned_stacking_containers
689 .peek()
690 .is_some_and(|(_, child)| child.z_index() < 0)
691 {
692 let (i, child) = real_stacking_contexts_and_positioned_stacking_containers
693 .next()
694 .unwrap();
695 self.debug_push_print_item(
696 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
697 i,
698 );
699 child.build_display_list(builder);
700 }
701
702 while contents.peek().is_some_and(|(_, child)| {
704 child.section() == StackingContextSection::DescendantBackgroundsAndBorders
705 }) {
706 let (i, child) = contents.next().unwrap();
707 self.debug_push_print_item(DebugPrintField::Contents, i);
708 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
709 }
710
711 for (i, child) in self.float_stacking_containers.iter().enumerate() {
713 self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
714 child.build_display_list(builder);
715 }
716
717 while contents
719 .peek()
720 .is_some_and(|(_, child)| child.section() == StackingContextSection::Foreground)
721 {
722 let (i, child) = contents.next().unwrap();
723 self.debug_push_print_item(DebugPrintField::Contents, i);
724 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
725 }
726
727 for (i, child) in real_stacking_contexts_and_positioned_stacking_containers {
730 self.debug_push_print_item(
731 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
732 i,
733 );
734 child.build_display_list(builder);
735 }
736
737 while contents
739 .peek()
740 .is_some_and(|(_, child)| child.section() == StackingContextSection::Outline)
741 {
742 let (i, child) = contents.next().unwrap();
743 self.debug_push_print_item(DebugPrintField::Contents, i);
744 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
745 }
746
747 if pushed_context {
748 builder.wr().pop_stacking_context();
749 }
750 }
751
752 fn debug_push_print_item(&self, field: DebugPrintField, index: usize) {
757 if let Some(items) = self.debug_print_items.as_ref() {
758 items.borrow_mut().push(DebugPrintItem { field, index });
759 }
760 }
761
762 pub fn debug_print(&self) {
764 if self.debug_print_items.is_none() {
765 warn!("failed to print stacking context tree: debug_print_items was None");
766 return;
767 }
768 let mut tree = PrintTree::new("Stacking context tree".to_owned());
769 self.debug_print_with_tree(&mut tree);
770 }
771
772 fn debug_print_with_tree(&self, tree: &mut PrintTree) {
774 match self.context_type {
775 StackingContextType::RealStackingContext => {
776 tree.new_level(format!("{:?} z={}", self.context_type, self.z_index()));
777 },
778 StackingContextType::AtomicInlineStackingContainer => {
779 },
781 _ => {
782 tree.new_level(format!("{:?}", self.context_type));
783 },
784 }
785 for DebugPrintItem { field, index } in
786 self.debug_print_items.as_ref().unwrap().borrow().iter()
787 {
788 match field {
789 DebugPrintField::Contents => match self.contents[*index] {
790 StackingContextContent::Fragment { section, .. } => {
791 tree.add_item(format!("{section:?}"));
792 },
793 StackingContextContent::AtomicInlineStackingContainer { index } => {
794 tree.new_level(format!("AtomicInlineStackingContainer #{index}"));
795 self.atomic_inline_stacking_containers[index].debug_print_with_tree(tree);
796 tree.end_level();
797 },
798 },
799 DebugPrintField::RealStackingContextsAndPositionedStackingContainers => {
800 self.real_stacking_contexts_and_positioned_stacking_containers[*index]
801 .debug_print_with_tree(tree);
802 },
803 DebugPrintField::FloatStackingContainers => {
804 self.float_stacking_containers[*index].debug_print_with_tree(tree);
805 },
806 }
807 }
808 match self.context_type {
809 StackingContextType::AtomicInlineStackingContainer => {
810 },
812 _ => {
813 tree.end_level();
814 },
815 }
816 }
817}
818
819#[derive(PartialEq)]
820pub(crate) enum StackingContextBuildMode {
821 IncludeHoisted,
822 SkipHoisted,
823}
824
825impl Fragment {
826 pub(crate) fn build_stacking_context_tree(
827 &self,
828 stacking_context_tree: &mut StackingContextTree,
829 containing_block_info: &ContainingBlockInfo,
830 stacking_context: &mut StackingContext,
831 mode: StackingContextBuildMode,
832 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
833 ) {
834 if self
835 .base()
836 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
837 {
838 return;
839 }
840
841 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
842 let fragment_clone = self.clone();
843 match self {
844 Fragment::Box(fragment) | Fragment::Float(fragment) => {
845 let fragment = fragment.borrow();
846 if mode == StackingContextBuildMode::SkipHoisted &&
847 fragment.style.clone_position().is_absolutely_positioned()
848 {
849 return;
850 }
851
852 let text_decorations = match self {
853 Fragment::Float(..) => &Default::default(),
854 _ => text_decorations,
855 };
856
857 fragment.build_stacking_context_tree(
858 fragment_clone,
859 stacking_context_tree,
860 containing_block,
861 containing_block_info,
862 stacking_context,
863 text_decorations,
864 );
865 },
866 Fragment::AbsoluteOrFixedPositioned(fragment) => {
867 let shared_fragment = fragment.borrow();
868 let fragment_ref = match shared_fragment.fragment.as_ref() {
869 Some(fragment_ref) => fragment_ref,
870 None => unreachable!("Found hoisted box with missing fragment."),
871 };
872
873 fragment_ref.build_stacking_context_tree(
874 stacking_context_tree,
875 containing_block_info,
876 stacking_context,
877 StackingContextBuildMode::IncludeHoisted,
878 &Default::default(),
879 );
880 },
881 Fragment::Positioning(fragment) => {
882 let fragment = fragment.borrow();
883 fragment.build_stacking_context_tree(
884 stacking_context_tree,
885 containing_block,
886 containing_block_info,
887 stacking_context,
888 text_decorations,
889 );
890 },
891 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
892 stacking_context
893 .contents
894 .push(StackingContextContent::Fragment {
895 section: StackingContextSection::Foreground,
896 scroll_node_id: containing_block.scroll_node_id,
897 reference_frame_scroll_node_id: containing_block_info
898 .for_absolute_and_fixed_descendants
899 .scroll_node_id,
900 clip_id: containing_block.clip_id,
901 containing_block: containing_block.rect,
902 fragment: fragment_clone,
903 is_hit_test_for_scrollable_overflow: false,
904 is_collapsed_table_borders: false,
905 text_decorations: text_decorations.clone(),
906 });
907 },
908 }
909 }
910}
911
912struct ReferenceFrameData {
913 origin: crate::geom::PhysicalPoint<Au>,
914 transform: LayoutTransform,
915 kind: wr::ReferenceFrameKind,
916}
917struct ScrollFrameData {
918 scroll_tree_node_id: ScrollTreeNodeId,
919 scroll_frame_rect: LayoutRect,
920}
921
922struct OverflowFrameData {
923 clip_id: ClipId,
924 scroll_frame_data: Option<ScrollFrameData>,
925}
926
927impl BoxFragment {
928 fn get_stacking_context_type(&self) -> Option<StackingContextType> {
929 let flags = self.base.flags;
930 if self.style.establishes_stacking_context(flags) {
931 return Some(StackingContextType::RealStackingContext);
932 }
933
934 let box_style = &self.style.get_box();
935 if box_style.position != ComputedPosition::Static {
936 return Some(StackingContextType::PositionedStackingContainer);
937 }
938
939 if box_style.float != ComputedFloat::None {
940 return Some(StackingContextType::FloatStackingContainer);
941 }
942
943 if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
947 return Some(StackingContextType::AtomicInlineStackingContainer);
948 }
949
950 None
951 }
952
953 fn get_stacking_context_section(&self) -> StackingContextSection {
954 if self.get_stacking_context_type().is_some() {
955 return StackingContextSection::OwnBackgroundsAndBorders;
956 }
957
958 if self.style.get_box().display.outside() == DisplayOutside::Inline {
959 return StackingContextSection::Foreground;
960 }
961
962 StackingContextSection::DescendantBackgroundsAndBorders
963 }
964
965 fn build_stacking_context_tree(
966 &self,
967 fragment: Fragment,
968 stacking_context_tree: &mut StackingContextTree,
969 containing_block: &ContainingBlock,
970 containing_block_info: &ContainingBlockInfo,
971 parent_stacking_context: &mut StackingContext,
972 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
973 ) {
974 self.build_stacking_context_tree_maybe_creating_reference_frame(
975 fragment,
976 stacking_context_tree,
977 containing_block,
978 containing_block_info,
979 parent_stacking_context,
980 text_decorations,
981 );
982 }
983
984 fn build_stacking_context_tree_maybe_creating_reference_frame(
985 &self,
986 fragment: Fragment,
987 stacking_context_tree: &mut StackingContextTree,
988 containing_block: &ContainingBlock,
989 containing_block_info: &ContainingBlockInfo,
990 parent_stacking_context: &mut StackingContext,
991 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
992 ) {
993 let reference_frame_data =
994 match self.reference_frame_data_if_necessary(&containing_block.rect) {
995 Some(reference_frame_data) => reference_frame_data,
996 None => {
997 return self.build_stacking_context_tree_maybe_creating_stacking_context(
998 fragment,
999 stacking_context_tree,
1000 containing_block,
1001 containing_block_info,
1002 parent_stacking_context,
1003 text_decorations,
1004 );
1005 },
1006 };
1007
1008 if !reference_frame_data.transform.is_invertible() {
1012 self.clear_spatial_tree_node_including_descendants();
1013 return;
1014 }
1015
1016 let frame_origin_for_query = self.cumulative_border_box_rect().origin.to_webrender();
1017
1018 let new_spatial_id = stacking_context_tree.push_reference_frame(
1019 reference_frame_data.origin.to_webrender(),
1020 frame_origin_for_query,
1021 &containing_block.scroll_node_id,
1022 self.style.get_box().transform_style.to_webrender(),
1023 reference_frame_data.transform,
1024 reference_frame_data.kind,
1025 );
1026
1027 assert!(
1037 self.style
1038 .establishes_containing_block_for_all_descendants(self.base.flags)
1039 );
1040 let adjusted_containing_block = ContainingBlock::new(
1041 containing_block
1042 .rect
1043 .translate(-reference_frame_data.origin.to_vector()),
1044 new_spatial_id,
1045 None,
1046 containing_block.clip_id,
1047 );
1048 let new_containing_block_info =
1049 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
1050
1051 self.build_stacking_context_tree_maybe_creating_stacking_context(
1052 fragment,
1053 stacking_context_tree,
1054 &adjusted_containing_block,
1055 &new_containing_block_info,
1056 parent_stacking_context,
1057 text_decorations,
1058 );
1059 }
1060
1061 fn build_stacking_context_tree_maybe_creating_stacking_context(
1062 &self,
1063 fragment: Fragment,
1064 stacking_context_tree: &mut StackingContextTree,
1065 containing_block: &ContainingBlock,
1066 containing_block_info: &ContainingBlockInfo,
1067 parent_stacking_context: &mut StackingContext,
1068 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1069 ) {
1070 let context_type = match self.get_stacking_context_type() {
1071 Some(context_type) => context_type,
1072 None => {
1073 self.build_stacking_context_tree_for_children(
1074 fragment,
1075 stacking_context_tree,
1076 containing_block,
1077 containing_block_info,
1078 parent_stacking_context,
1079 text_decorations,
1080 );
1081 return;
1082 },
1083 };
1084
1085 if context_type == StackingContextType::AtomicInlineStackingContainer {
1086 parent_stacking_context.contents.push(
1088 StackingContextContent::AtomicInlineStackingContainer {
1089 index: parent_stacking_context
1090 .atomic_inline_stacking_containers
1091 .len(),
1092 },
1093 );
1094 }
1095
1096 let stacking_context_clip_id = stacking_context_tree
1100 .clip_store
1101 .add_for_clip_path(
1102 self.style.clone_clip_path(),
1103 &containing_block.scroll_node_id,
1104 &containing_block.clip_id,
1105 BuilderForBoxFragment::new(
1106 self,
1107 &containing_block.rect,
1108 false, false, ),
1111 )
1112 .unwrap_or(containing_block.clip_id);
1113
1114 let box_fragment = match fragment {
1115 Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
1116 box_fragment.clone()
1117 },
1118 _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
1119 };
1120
1121 let mut child_stacking_context = parent_stacking_context.create_descendant(
1122 containing_block.scroll_node_id,
1123 stacking_context_clip_id,
1124 box_fragment,
1125 context_type,
1126 );
1127 self.build_stacking_context_tree_for_children(
1128 fragment,
1129 stacking_context_tree,
1130 containing_block,
1131 containing_block_info,
1132 &mut child_stacking_context,
1133 text_decorations,
1134 );
1135
1136 let mut stolen_children = vec![];
1137 if context_type != StackingContextType::RealStackingContext {
1138 stolen_children = mem::replace(
1139 &mut child_stacking_context
1140 .real_stacking_contexts_and_positioned_stacking_containers,
1141 stolen_children,
1142 );
1143 }
1144
1145 child_stacking_context.sort();
1146 parent_stacking_context.add_stacking_context(child_stacking_context);
1147 parent_stacking_context
1148 .real_stacking_contexts_and_positioned_stacking_containers
1149 .append(&mut stolen_children);
1150 }
1151
1152 fn build_stacking_context_tree_for_children(
1153 &self,
1154 fragment: Fragment,
1155 stacking_context_tree: &mut StackingContextTree,
1156 containing_block: &ContainingBlock,
1157 containing_block_info: &ContainingBlockInfo,
1158 stacking_context: &mut StackingContext,
1159 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1160 ) {
1161 let mut new_scroll_node_id = containing_block.scroll_node_id;
1162 let mut new_clip_id = containing_block.clip_id;
1163 let mut new_scroll_frame_size = containing_block_info
1164 .for_non_absolute_descendants
1165 .scroll_frame_size;
1166
1167 if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
1168 stacking_context_tree,
1169 &new_scroll_node_id,
1170 &containing_block.rect,
1171 &new_scroll_frame_size,
1172 ) {
1173 new_scroll_node_id = scroll_node_id;
1174 }
1175
1176 if let Some(clip_id) = self.build_clip_frame_if_necessary(
1177 stacking_context_tree,
1178 &new_scroll_node_id,
1179 new_clip_id,
1180 &containing_block.rect,
1181 ) {
1182 new_clip_id = clip_id;
1183 }
1184
1185 if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
1186 self.style.clone_clip_path(),
1187 &new_scroll_node_id,
1188 &new_clip_id,
1189 BuilderForBoxFragment::new(
1190 self,
1191 &containing_block.rect,
1192 false, false, ),
1195 ) {
1196 new_clip_id = clip_id;
1197 }
1198
1199 let establishes_containing_block_for_all_descendants = self
1200 .style
1201 .establishes_containing_block_for_all_descendants(self.base.flags);
1202 let establishes_containing_block_for_absolute_descendants = self
1203 .style
1204 .establishes_containing_block_for_absolute_descendants(self.base.flags);
1205
1206 let reference_frame_scroll_node_id_for_fragments =
1207 if establishes_containing_block_for_all_descendants {
1208 new_scroll_node_id
1209 } else {
1210 containing_block_info
1211 .for_absolute_and_fixed_descendants
1212 .scroll_node_id
1213 };
1214
1215 let mut add_fragment = |section| {
1216 stacking_context
1217 .contents
1218 .push(StackingContextContent::Fragment {
1219 scroll_node_id: new_scroll_node_id,
1220 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1221 clip_id: new_clip_id,
1222 section,
1223 containing_block: containing_block.rect,
1224 fragment: fragment.clone(),
1225 is_hit_test_for_scrollable_overflow: false,
1226 is_collapsed_table_borders: false,
1227 text_decorations: text_decorations.clone(),
1228 });
1229 };
1230
1231 let section = self.get_stacking_context_section();
1232 add_fragment(section);
1233 if !self.style.get_outline().outline_width.is_zero() {
1234 add_fragment(StackingContextSection::Outline);
1235 }
1236
1237 *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1241
1242 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
1245 stacking_context_tree,
1246 &new_scroll_node_id,
1247 new_clip_id,
1248 &containing_block.rect,
1249 ) {
1250 new_clip_id = overflow_frame_data.clip_id;
1251 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
1252 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
1253 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
1254 stacking_context
1255 .contents
1256 .push(StackingContextContent::Fragment {
1257 scroll_node_id: new_scroll_node_id,
1258 reference_frame_scroll_node_id:
1259 reference_frame_scroll_node_id_for_fragments,
1260 clip_id: new_clip_id,
1261 section,
1262 containing_block: containing_block.rect,
1263 fragment: fragment.clone(),
1264 is_hit_test_for_scrollable_overflow: true,
1265 is_collapsed_table_borders: false,
1266 text_decorations: text_decorations.clone(),
1267 });
1268 }
1269 }
1270
1271 let padding_rect = self
1272 .padding_rect()
1273 .translate(containing_block.rect.origin.to_vector());
1274 let content_rect = self
1275 .content_rect
1276 .translate(containing_block.rect.origin.to_vector());
1277
1278 let for_absolute_descendants = ContainingBlock::new(
1279 padding_rect,
1280 new_scroll_node_id,
1281 new_scroll_frame_size,
1282 new_clip_id,
1283 );
1284 let for_non_absolute_descendants = ContainingBlock::new(
1285 content_rect,
1286 new_scroll_node_id,
1287 new_scroll_frame_size,
1288 new_clip_id,
1289 );
1290
1291 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
1295 containing_block_info.new_for_absolute_and_fixed_descendants(
1296 &for_non_absolute_descendants,
1297 &for_absolute_descendants,
1298 )
1299 } else if establishes_containing_block_for_absolute_descendants {
1300 containing_block_info.new_for_absolute_descendants(
1301 &for_non_absolute_descendants,
1302 &for_absolute_descendants,
1303 )
1304 } else {
1305 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
1306 };
1307
1308 let text_decorations = match self.is_atomic_inline_level() ||
1314 self.base
1315 .flags
1316 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
1317 {
1318 true => &Default::default(),
1319 false => text_decorations,
1320 };
1321
1322 let new_text_decoration;
1323 let text_decorations = match self.style.clone_text_decoration_line() {
1324 TextDecorationLine::NONE => text_decorations,
1325 line => {
1326 let mut new_vector = (**text_decorations).clone();
1327 let color = &self.style.get_inherited_text().color;
1328 new_vector.push(FragmentTextDecoration {
1329 line,
1330 color: self
1331 .style
1332 .clone_text_decoration_color()
1333 .resolve_to_absolute(color),
1334 style: self.style.clone_text_decoration_style(),
1335 });
1336 new_text_decoration = Arc::new(new_vector);
1337 &new_text_decoration
1338 },
1339 };
1340
1341 for child in &self.children {
1342 child.build_stacking_context_tree(
1343 stacking_context_tree,
1344 &new_containing_block_info,
1345 stacking_context,
1346 StackingContextBuildMode::SkipHoisted,
1347 text_decorations,
1348 );
1349 }
1350
1351 if matches!(&fragment, Fragment::Box(box_fragment) if matches!(
1352 box_fragment.borrow().specific_layout_info(),
1353 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
1354 )) {
1355 stacking_context
1356 .contents
1357 .push(StackingContextContent::Fragment {
1358 scroll_node_id: new_scroll_node_id,
1359 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1360 clip_id: new_clip_id,
1361 section,
1362 containing_block: containing_block.rect,
1363 fragment: fragment.clone(),
1364 is_hit_test_for_scrollable_overflow: false,
1365 is_collapsed_table_borders: true,
1366 text_decorations: text_decorations.clone(),
1367 });
1368 }
1369 }
1370
1371 fn build_clip_frame_if_necessary(
1372 &self,
1373 stacking_context_tree: &mut StackingContextTree,
1374 parent_scroll_node_id: &ScrollTreeNodeId,
1375 parent_clip_id: ClipId,
1376 containing_block_rect: &PhysicalRect<Au>,
1377 ) -> Option<ClipId> {
1378 let position = self.style.get_box().position;
1379 if !position.is_absolutely_positioned() {
1382 return None;
1383 }
1384
1385 let clip_rect = match self.style.get_effects().clip {
1387 ClipRectOrAuto::Rect(rect) => rect,
1388 _ => return None,
1389 };
1390
1391 let border_rect = self.border_rect();
1392 let clip_rect = clip_rect
1393 .for_border_rect(border_rect)
1394 .translate(containing_block_rect.origin.to_vector())
1395 .to_webrender();
1396 Some(stacking_context_tree.clip_store.add(
1397 BorderRadius::zero(),
1398 clip_rect,
1399 *parent_scroll_node_id,
1400 parent_clip_id,
1401 ))
1402 }
1403
1404 fn build_overflow_frame_if_necessary(
1405 &self,
1406 stacking_context_tree: &mut StackingContextTree,
1407 parent_scroll_node_id: &ScrollTreeNodeId,
1408 parent_clip_id: ClipId,
1409 containing_block_rect: &PhysicalRect<Au>,
1410 ) -> Option<OverflowFrameData> {
1411 let overflow = self.style.effective_overflow(self.base.flags);
1412
1413 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1414 return None;
1415 }
1416
1417 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1419 let mut overflow_clip_rect = self
1423 .padding_rect()
1424 .translate(containing_block_rect.origin.to_vector())
1425 .to_webrender();
1426
1427 let clip_margin = self.style.get_margin().overflow_clip_margin.px();
1430 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin, clip_margin);
1431
1432 let radii;
1435 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1436 let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
1437 radii = offset_radii(builder.border_radius, clip_margin);
1438 } else if overflow.x != ComputedOverflow::Clip {
1439 overflow_clip_rect.min.x = f32::MIN;
1440 overflow_clip_rect.max.x = f32::MAX;
1441 radii = BorderRadius::zero();
1442 } else {
1443 overflow_clip_rect.min.y = f32::MIN;
1444 overflow_clip_rect.max.y = f32::MAX;
1445 radii = BorderRadius::zero();
1446 }
1447
1448 let clip_id = stacking_context_tree.clip_store.add(
1449 radii,
1450 overflow_clip_rect,
1451 *parent_scroll_node_id,
1452 parent_clip_id,
1453 );
1454
1455 return Some(OverflowFrameData {
1456 clip_id,
1457 scroll_frame_data: None,
1458 });
1459 }
1460
1461 let scroll_frame_rect = self
1462 .padding_rect()
1463 .translate(containing_block_rect.origin.to_vector())
1464 .to_webrender();
1465
1466 let clip_id = stacking_context_tree.clip_store.add(
1467 BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
1468 scroll_frame_rect,
1469 *parent_scroll_node_id,
1470 parent_clip_id,
1471 );
1472
1473 let tag = self.base.tag?;
1474 let external_scroll_id = wr::ExternalScrollId(
1475 tag.to_display_list_fragment_id(),
1476 stacking_context_tree.compositor_info.pipeline_id,
1477 );
1478
1479 let sensitivity = AxesScrollSensitivity {
1480 x: overflow.x.into(),
1481 y: overflow.y.into(),
1482 };
1483
1484 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1485 parent_scroll_node_id,
1486 external_scroll_id,
1487 self.scrollable_overflow().to_webrender(),
1488 scroll_frame_rect,
1489 sensitivity,
1490 );
1491
1492 Some(OverflowFrameData {
1493 clip_id,
1494 scroll_frame_data: Some(ScrollFrameData {
1495 scroll_tree_node_id,
1496 scroll_frame_rect,
1497 }),
1498 })
1499 }
1500
1501 fn build_sticky_frame_if_necessary(
1502 &self,
1503 stacking_context_tree: &mut StackingContextTree,
1504 parent_scroll_node_id: &ScrollTreeNodeId,
1505 containing_block_rect: &PhysicalRect<Au>,
1506 scroll_frame_size: &Option<LayoutSize>,
1507 ) -> Option<ScrollTreeNodeId> {
1508 if self.style.get_box().position != ComputedPosition::Sticky {
1509 return None;
1510 }
1511
1512 let scroll_frame_size_for_resolve = match scroll_frame_size {
1513 Some(size) => size,
1514 None => {
1515 &stacking_context_tree
1517 .compositor_info
1518 .viewport_details
1519 .layout_size()
1520 },
1521 };
1522
1523 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1527 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1528 let offsets = self.style.physical_box_offsets();
1529 let offsets = PhysicalSides::<AuOrAuto>::new(
1530 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1531 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1532 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1533 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1534 );
1535 *self.resolved_sticky_insets.borrow_mut() = Some(offsets);
1536
1537 if scroll_frame_size.is_none() {
1538 return None;
1539 }
1540
1541 if offsets.top.is_auto() &&
1542 offsets.right.is_auto() &&
1543 offsets.bottom.is_auto() &&
1544 offsets.left.is_auto()
1545 {
1546 return None;
1547 }
1548
1549 let frame_rect = self
1550 .border_rect()
1551 .translate(containing_block_rect.origin.to_vector())
1552 .to_webrender();
1553
1554 let containing_block_rect = containing_block_rect.to_webrender();
1557
1558 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1561 containing_block_rect.min.y - frame_rect.min.y,
1562 containing_block_rect.max.y - frame_rect.max.y,
1563 );
1564 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1565 containing_block_rect.min.x - frame_rect.min.x,
1566 containing_block_rect.max.x - frame_rect.max.x,
1567 );
1568
1569 let margins = SideOffsets2D::new(
1570 offsets.top.non_auto().map(|v| v.to_f32_px()),
1571 offsets.right.non_auto().map(|v| v.to_f32_px()),
1572 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1573 offsets.left.non_auto().map(|v| v.to_f32_px()),
1574 );
1575
1576 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1577 parent_scroll_node_id,
1578 frame_rect,
1579 margins,
1580 vertical_offset_bounds,
1581 horizontal_offset_bounds,
1582 );
1583
1584 Some(sticky_node_id)
1585 }
1586
1587 fn reference_frame_data_if_necessary(
1589 &self,
1590 containing_block_rect: &PhysicalRect<Au>,
1591 ) -> Option<ReferenceFrameData> {
1592 if !self
1593 .style
1594 .has_effective_transform_or_perspective(self.base.flags)
1595 {
1596 return None;
1597 }
1598
1599 let relative_border_rect = self.border_rect();
1600 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1601 let untyped_border_rect = border_rect.to_untyped();
1602
1603 let transform = self.calculate_transform_matrix(&untyped_border_rect);
1604 let perspective = self.calculate_perspective_matrix(&untyped_border_rect);
1605 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1606 (None, Some(perspective)) => (
1607 perspective,
1608 wr::ReferenceFrameKind::Perspective {
1609 scrolling_relative_to: None,
1610 },
1611 ),
1612 (Some(transform), None) => (
1613 transform,
1614 wr::ReferenceFrameKind::Transform {
1615 is_2d_scale_translation: false,
1616 should_snap: false,
1617 paired_with_perspective: false,
1618 },
1619 ),
1620 (Some(transform), Some(perspective)) => (
1621 perspective.then(&transform),
1622 wr::ReferenceFrameKind::Perspective {
1623 scrolling_relative_to: None,
1624 },
1625 ),
1626 (None, None) => unreachable!(),
1627 };
1628
1629 Some(ReferenceFrameData {
1630 origin: border_rect.origin,
1631 transform: reference_frame_transform,
1632 kind: reference_frame_kind,
1633 })
1634 }
1635
1636 pub fn calculate_transform_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> {
1638 let list = &self.style.get_box().transform;
1639 let length_rect = au_rect_to_length_rect(border_rect);
1640 let rotate = match self.style.clone_rotate() {
1642 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1643 GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle),
1644 GenericRotate::None => (0., 0., 1., Angle::zero()),
1645 };
1646 let scale = match self.style.clone_scale() {
1647 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1648 GenericScale::None => (1., 1., 1.),
1649 };
1650 let translation = match self.style.clone_translate() {
1651 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1652 x.resolve(length_rect.size.width).px(),
1653 y.resolve(length_rect.size.height).px(),
1654 z.px(),
1655 ),
1656 GenericTranslate::None => LayoutTransform::identity(),
1657 };
1658
1659 let angle = euclid::Angle::radians(rotate.3.radians());
1660 let transform_base = list.to_transform_3d_matrix(Some(&length_rect)).ok()?;
1661 let transform = LayoutTransform::from_untyped(&transform_base.0)
1662 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1663 .then_scale(scale.0, scale.1, scale.2)
1664 .then(&translation);
1665
1666 let transform_origin = &self.style.get_box().transform_origin;
1667 let transform_origin_x = transform_origin
1668 .horizontal
1669 .to_used_value(border_rect.size.width)
1670 .to_f32_px();
1671 let transform_origin_y = transform_origin
1672 .vertical
1673 .to_used_value(border_rect.size.height)
1674 .to_f32_px();
1675 let transform_origin_z = transform_origin.depth.px();
1676
1677 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1678 }
1679
1680 pub fn calculate_perspective_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> {
1682 match self.style.get_box().perspective {
1683 Perspective::Length(length) => {
1684 let perspective_origin = &self.style.get_box().perspective_origin;
1685 let perspective_origin = LayoutPoint::new(
1686 perspective_origin
1687 .horizontal
1688 .percentage_relative_to(border_rect.size.width.into())
1689 .px(),
1690 perspective_origin
1691 .vertical
1692 .percentage_relative_to(border_rect.size.height.into())
1693 .px(),
1694 );
1695
1696 let perspective_matrix = LayoutTransform::from_untyped(
1697 &transform::create_perspective_matrix(length.px()),
1698 );
1699
1700 Some(perspective_matrix.change_basis(
1701 perspective_origin.x,
1702 perspective_origin.y,
1703 0.0,
1704 ))
1705 },
1706 Perspective::None => None,
1707 }
1708 }
1709
1710 fn clear_spatial_tree_node_including_descendants(&self) {
1711 fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1712 for fragment in fragments.iter() {
1713 match fragment {
1714 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1715 box_fragment
1716 .borrow()
1717 .clear_spatial_tree_node_including_descendants();
1718 },
1719 Fragment::Positioning(positioning_fragment) => {
1720 assign_spatial_tree_node_on_fragments(
1721 &positioning_fragment.borrow().children,
1722 );
1723 },
1724 _ => {},
1725 }
1726 }
1727 }
1728
1729 *self.spatial_tree_node.borrow_mut() = None;
1730 assign_spatial_tree_node_on_fragments(&self.children);
1731 }
1732}
1733
1734impl PositioningFragment {
1735 fn build_stacking_context_tree(
1736 &self,
1737 stacking_context_tree: &mut StackingContextTree,
1738 containing_block: &ContainingBlock,
1739 containing_block_info: &ContainingBlockInfo,
1740 stacking_context: &mut StackingContext,
1741 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1742 ) {
1743 let rect = self
1744 .rect
1745 .translate(containing_block.rect.origin.to_vector());
1746 let new_containing_block = containing_block.new_replacing_rect(&rect);
1747 let new_containing_block_info =
1748 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1749
1750 for child in &self.children {
1751 child.build_stacking_context_tree(
1752 stacking_context_tree,
1753 &new_containing_block_info,
1754 stacking_context,
1755 StackingContextBuildMode::SkipHoisted,
1756 text_decorations,
1757 );
1758 }
1759 }
1760}
1761
1762pub fn au_rect_to_length_rect(rect: &Rect<Au>) -> Rect<Length> {
1763 Rect::new(
1764 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1765 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1766 )
1767}