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