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 embedder_traits::ViewportDetails;
14use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
15use log::warn;
16use malloc_size_of_derive::MallocSizeOf;
17use paint_api::display_list::{
18 AxesScrollSensitivity, PaintDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
19 SpatialTreeNodeInfo, StickyNodeInfo,
20};
21use servo_config::opts::DiagnosticsLogging;
22use style::Zero;
23use style::color::AbsoluteColor;
24use style::computed_values::float::T as ComputedFloat;
25use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
26use style::computed_values::overflow_x::T as ComputedOverflow;
27use style::computed_values::position::T as ComputedPosition;
28use style::computed_values::text_decoration_style::T as TextDecorationStyle;
29use style::values::computed::angle::Angle;
30use style::values::computed::basic_shape::ClipPath;
31use style::values::computed::{ClipRectOrAuto, Length, TextDecorationLine};
32use style::values::generics::box_::{OverflowClipMarginBox, Perspective};
33use style::values::generics::transform::{self, GenericRotate, GenericScale, GenericTranslate};
34use style::values::specified::TransformStyle;
35use style::values::specified::box_::DisplayOutside;
36use style_traits::CSSPixel;
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, PhysicalPoint, 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 paint_info: PaintDisplayListInfo,
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: &DiagnosticsLogging,
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 paint_info = PaintDisplayListInfo::new(
140 viewport_details,
141 scrollable_overflow,
142 pipeline_id,
143 Default::default(),
145 fragment_tree.viewport_scroll_sensitivity,
146 first_reflow,
147 );
148
149 let root_scroll_node_id = paint_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 paint_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 paint_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.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.paint_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.paint_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.paint_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 pub(crate) fn offset_in_fragment(
273 &self,
274 fragment: &Fragment,
275 point_in_viewport: PhysicalPoint<Au>,
276 ) -> Option<Point2D<Au, CSSPixel>> {
277 let Fragment::Box(fragment) = fragment else {
278 return None;
279 };
280
281 let fragment = fragment.borrow();
282 let spatial_tree_node = fragment.spatial_tree_node()?;
283 let transform = self
284 .paint_info
285 .scroll_tree
286 .cumulative_root_to_node_transform(spatial_tree_node)?;
287 let transformed_point = transform
288 .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
289 .map(Au::from_f32_px)
290 .cast_unit();
291
292 let reference_frame_origin = self
294 .paint_info
295 .scroll_tree
296 .reference_frame_offset(spatial_tree_node)
297 .map(Au::from_f32_px);
298 let fragment_origin =
299 fragment.cumulative_content_box_rect().origin - reference_frame_origin.cast_unit();
300
301 Some(transformed_point - fragment_origin)
303 }
304}
305
306#[derive(Clone, Debug, MallocSizeOf)]
308pub(crate) struct FragmentTextDecoration {
309 pub line: TextDecorationLine,
310 pub color: AbsoluteColor,
311 pub style: TextDecorationStyle,
312}
313
314#[derive(MallocSizeOf)]
319pub(crate) enum StackingContextContent {
320 Fragment {
322 scroll_node_id: ScrollTreeNodeId,
323 reference_frame_scroll_node_id: ScrollTreeNodeId,
324 clip_id: ClipId,
325 section: StackingContextSection,
326 containing_block: PhysicalRect<Au>,
327 fragment: Fragment,
328 is_hit_test_for_scrollable_overflow: bool,
329 is_collapsed_table_borders: bool,
330 #[conditional_malloc_size_of]
331 text_decorations: Arc<Vec<FragmentTextDecoration>>,
332 },
333
334 AtomicInlineStackingContainer { index: usize },
338}
339
340impl StackingContextContent {
341 pub(crate) fn section(&self) -> StackingContextSection {
342 match self {
343 Self::Fragment { section, .. } => *section,
344 Self::AtomicInlineStackingContainer { .. } => StackingContextSection::Foreground,
345 }
346 }
347
348 fn build_display_list_with_section_override(
349 &self,
350 builder: &mut DisplayListBuilder,
351 inline_stacking_containers: &[StackingContext],
352 section_override: Option<StackingContextSection>,
353 ) {
354 match self {
355 Self::Fragment {
356 scroll_node_id,
357 reference_frame_scroll_node_id,
358 clip_id,
359 section,
360 containing_block,
361 fragment,
362 is_hit_test_for_scrollable_overflow,
363 is_collapsed_table_borders,
364 text_decorations,
365 } => {
366 builder.current_scroll_node_id = *scroll_node_id;
367 builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
368 builder.current_clip_id = *clip_id;
369 fragment.build_display_list(
370 builder,
371 containing_block,
372 section_override.unwrap_or(*section),
373 *is_hit_test_for_scrollable_overflow,
374 *is_collapsed_table_borders,
375 text_decorations,
376 );
377 },
378 Self::AtomicInlineStackingContainer { index } => {
379 inline_stacking_containers[*index].build_display_list(builder);
380 },
381 }
382 }
383
384 fn build_display_list(
385 &self,
386 builder: &mut DisplayListBuilder,
387 inline_stacking_containers: &[StackingContext],
388 ) {
389 self.build_display_list_with_section_override(builder, inline_stacking_containers, None);
390 }
391
392 fn has_outline(&self) -> bool {
393 match self {
394 StackingContextContent::Fragment { fragment, .. } => match fragment {
395 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
396 let box_fragment = box_fragment.borrow();
397 let style = box_fragment.style();
398 let outline = style.get_outline();
399 !outline.outline_style.none_or_hidden() && !outline.outline_width.0.is_zero()
400 },
401 _ => false,
402 },
403 StackingContextContent::AtomicInlineStackingContainer { .. } => false,
404 }
405 }
406}
407
408#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
409pub(crate) enum StackingContextType {
410 RealStackingContext,
411 PositionedStackingContainer,
412 FloatStackingContainer,
413 AtomicInlineStackingContainer,
414}
415
416#[derive(MallocSizeOf)]
422pub struct StackingContext {
423 scroll_tree_node_id: ScrollTreeNodeId,
426
427 clip_id: Option<ClipId>,
429
430 initializing_fragment: Option<ArcRefCell<BoxFragment>>,
433
434 context_type: StackingContextType,
436
437 pub(super) contents: Vec<StackingContextContent>,
439
440 pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
450
451 pub(super) float_stacking_containers: Vec<StackingContext>,
456
457 pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
465
466 debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
468}
469
470#[derive(Clone, Copy, MallocSizeOf)]
472pub struct DebugPrintItem {
473 field: DebugPrintField,
474 index: usize,
475}
476
477#[derive(Clone, Copy, MallocSizeOf)]
479pub enum DebugPrintField {
480 Contents,
481 RealStackingContextsAndPositionedStackingContainers,
482 FloatStackingContainers,
483}
484
485impl StackingContext {
486 fn create_descendant(
487 &self,
488 spatial_id: ScrollTreeNodeId,
489 clip_id: ClipId,
490 initializing_fragment: ArcRefCell<BoxFragment>,
491 context_type: StackingContextType,
492 ) -> Self {
493 let clip_id = match clip_id {
498 ClipId::INVALID => None,
499 clip_id => Some(clip_id),
500 };
501 Self {
502 scroll_tree_node_id: spatial_id,
503 clip_id,
504 initializing_fragment: Some(initializing_fragment),
505 context_type,
506 contents: vec![],
507 real_stacking_contexts_and_positioned_stacking_containers: vec![],
508 float_stacking_containers: vec![],
509 atomic_inline_stacking_containers: vec![],
510 debug_print_items: self.debug_print_items.is_some().then(|| vec![].into()),
511 }
512 }
513
514 fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DiagnosticsLogging) -> Self {
515 Self {
516 scroll_tree_node_id: root_scroll_node_id,
517 clip_id: None,
518 initializing_fragment: None,
519 context_type: StackingContextType::RealStackingContext,
520 contents: vec![],
521 real_stacking_contexts_and_positioned_stacking_containers: vec![],
522 float_stacking_containers: vec![],
523 atomic_inline_stacking_containers: vec![],
524 debug_print_items: debug.stacking_context_tree.then(|| vec![].into()),
525 }
526 }
527
528 fn add_stacking_context(&mut self, stacking_context: StackingContext) {
530 match stacking_context.context_type {
531 StackingContextType::RealStackingContext => {
532 &mut self.real_stacking_contexts_and_positioned_stacking_containers
533 },
534 StackingContextType::PositionedStackingContainer => {
535 &mut self.real_stacking_contexts_and_positioned_stacking_containers
536 },
537 StackingContextType::FloatStackingContainer => &mut self.float_stacking_containers,
538 StackingContextType::AtomicInlineStackingContainer => {
539 &mut self.atomic_inline_stacking_containers
540 },
541 }
542 .push(stacking_context)
543 }
544
545 pub(crate) fn z_index(&self) -> i32 {
546 self.initializing_fragment.as_ref().map_or(0, |fragment| {
547 let fragment = fragment.borrow();
548 fragment.style().effective_z_index(fragment.base.flags)
549 })
550 }
551
552 pub(crate) fn sort(&mut self) {
553 self.contents.sort_by_key(|a| a.section());
554 self.real_stacking_contexts_and_positioned_stacking_containers
555 .sort_by_key(|a| a.z_index());
556
557 debug_assert!(
558 self.real_stacking_contexts_and_positioned_stacking_containers
559 .iter()
560 .all(|c| matches!(
561 c.context_type,
562 StackingContextType::RealStackingContext |
563 StackingContextType::PositionedStackingContainer
564 ))
565 );
566 debug_assert!(
567 self.float_stacking_containers
568 .iter()
569 .all(
570 |c| c.context_type == StackingContextType::FloatStackingContainer &&
571 c.z_index() == 0
572 )
573 );
574 debug_assert!(
575 self.atomic_inline_stacking_containers
576 .iter()
577 .all(
578 |c| c.context_type == StackingContextType::AtomicInlineStackingContainer &&
579 c.z_index() == 0
580 )
581 );
582 }
583
584 fn push_webrender_stacking_context_if_necessary(
585 &self,
586 builder: &mut DisplayListBuilder,
587 ) -> bool {
588 let fragment = match self.initializing_fragment.as_ref() {
589 Some(fragment) => fragment.borrow(),
590 None => return false,
591 };
592
593 let style = fragment.style();
596 let effects = style.get_effects();
597 let transform_style = style.get_used_transform_style();
598 if effects.filter.0.is_empty() &&
599 effects.opacity == 1.0 &&
600 effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
601 !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
602 style.clone_clip_path() == ClipPath::None &&
603 transform_style == TransformStyle::Flat
604 {
605 return false;
606 }
607
608 let current_color = style.clone_color();
610 let mut filters: Vec<wr::FilterOp> = effects
611 .filter
612 .0
613 .iter()
614 .map(|filter| FilterToWebRender::to_webrender(filter, ¤t_color))
615 .collect();
616 if effects.opacity != 1.0 {
617 filters.push(wr::FilterOp::Opacity(
618 effects.opacity.into(),
619 effects.opacity,
620 ));
621 }
622
623 let spatial_id = builder.spatial_id(self.scroll_tree_node_id);
630 let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id));
631 builder.wr().push_stacking_context(
632 LayoutPoint::zero(), spatial_id,
634 style.get_webrender_primitive_flags(),
635 clip_chain_id,
636 transform_style.to_webrender(),
637 effects.mix_blend_mode.to_webrender(),
638 &filters,
639 &[], &[], wr::RasterSpace::Screen,
642 wr::StackingContextFlags::empty(),
643 None, );
645
646 true
647 }
648
649 pub(crate) fn build_canvas_background_display_list(
653 &self,
654 builder: &mut DisplayListBuilder,
655 fragment_tree: &crate::fragment_tree::FragmentTree,
656 ) {
657 let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
658 fragment
659 .base()
660 .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
661 }) else {
662 return;
663 };
664 let root_fragment = match root_fragment {
665 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
666 _ => return,
667 }
668 .borrow();
669
670 let source_style = {
671 let root_fragment_style = root_fragment.style();
677 if root_fragment_style.background_is_transparent() {
678 let body_fragment = fragment_tree.body_fragment();
679 builder.paint_body_background = body_fragment.is_none();
680 body_fragment
681 .map(|body_fragment| body_fragment.borrow().style().clone())
682 .unwrap_or(root_fragment.style().clone())
683 } else {
684 root_fragment_style.clone()
685 }
686 };
687
688 if source_style.background_is_transparent() {
691 return;
692 }
693
694 let painting_area = fragment_tree
701 .initial_containing_block
702 .union(&fragment_tree.scrollable_overflow())
703 .to_webrender();
704
705 let background_color =
706 source_style.resolve_color(&source_style.get_background().background_color);
707 if background_color.alpha > 0.0 {
708 let common = builder.common_properties(painting_area, &source_style);
709 let color = super::rgba(background_color);
710 builder.wr().push_rect(&common, painting_area, color)
711 }
712
713 let mut fragment_builder = BuilderForBoxFragment::new(
714 &root_fragment,
715 &fragment_tree.initial_containing_block,
716 false, false, );
719 let painter = super::background::BackgroundPainter {
720 style: &source_style,
721 painting_area_override: Some(painting_area),
722 positioning_area_override: None,
723 };
724 fragment_builder.build_background_image(builder, &painter);
725 }
726
727 pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
731 let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
732
733 let mut content_with_outlines = Vec::new();
742 let mut contents = self.contents.iter().enumerate().peekable();
743 while contents.peek().is_some_and(|(_, child)| {
744 child.section() == StackingContextSection::OwnBackgroundsAndBorders
745 }) {
746 let (i, child) = contents.next().unwrap();
747 self.debug_push_print_item(DebugPrintField::Contents, i);
748 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
749
750 if child.has_outline() {
751 content_with_outlines.push(child);
752 }
753 }
754
755 let mut real_stacking_contexts_and_positioned_stacking_containers = self
757 .real_stacking_contexts_and_positioned_stacking_containers
758 .iter()
759 .enumerate()
760 .peekable();
761 while real_stacking_contexts_and_positioned_stacking_containers
762 .peek()
763 .is_some_and(|(_, child)| child.z_index() < 0)
764 {
765 let (i, child) = real_stacking_contexts_and_positioned_stacking_containers
766 .next()
767 .unwrap();
768 self.debug_push_print_item(
769 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
770 i,
771 );
772 child.build_display_list(builder);
773 }
774
775 while contents.peek().is_some_and(|(_, child)| {
777 child.section() == StackingContextSection::DescendantBackgroundsAndBorders
778 }) {
779 let (i, child) = contents.next().unwrap();
780 self.debug_push_print_item(DebugPrintField::Contents, i);
781 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
782
783 if child.has_outline() {
784 content_with_outlines.push(child);
785 }
786 }
787
788 for (i, child) in self.float_stacking_containers.iter().enumerate() {
790 self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
791 child.build_display_list(builder);
792 }
793
794 while contents
796 .peek()
797 .is_some_and(|(_, child)| child.section() == StackingContextSection::Foreground)
798 {
799 let (i, child) = contents.next().unwrap();
800 self.debug_push_print_item(DebugPrintField::Contents, i);
801 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
802
803 if child.has_outline() {
804 content_with_outlines.push(child);
805 }
806 }
807
808 for (i, child) in real_stacking_contexts_and_positioned_stacking_containers {
811 self.debug_push_print_item(
812 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
813 i,
814 );
815 child.build_display_list(builder);
816 }
817
818 for content in content_with_outlines {
820 content.build_display_list_with_section_override(
821 builder,
822 &self.atomic_inline_stacking_containers,
823 Some(StackingContextSection::Outline),
824 );
825 }
826
827 if pushed_context {
828 builder.wr().pop_stacking_context();
829 }
830 }
831
832 fn debug_push_print_item(&self, field: DebugPrintField, index: usize) {
837 if let Some(items) = self.debug_print_items.as_ref() {
838 items.borrow_mut().push(DebugPrintItem { field, index });
839 }
840 }
841
842 pub fn debug_print(&self) {
844 if self.debug_print_items.is_none() {
845 warn!("failed to print stacking context tree: debug_print_items was None");
846 return;
847 }
848 let mut tree = PrintTree::new("Stacking context tree".to_owned());
849 self.debug_print_with_tree(&mut tree);
850 }
851
852 fn debug_print_with_tree(&self, tree: &mut PrintTree) {
854 match self.context_type {
855 StackingContextType::RealStackingContext => {
856 tree.new_level(format!("{:?} z={}", self.context_type, self.z_index()));
857 },
858 StackingContextType::AtomicInlineStackingContainer => {
859 },
861 _ => {
862 tree.new_level(format!("{:?}", self.context_type));
863 },
864 }
865 for DebugPrintItem { field, index } in
866 self.debug_print_items.as_ref().unwrap().borrow().iter()
867 {
868 match field {
869 DebugPrintField::Contents => match self.contents[*index] {
870 StackingContextContent::Fragment { section, .. } => {
871 tree.add_item(format!("{section:?}"));
872 },
873 StackingContextContent::AtomicInlineStackingContainer { index } => {
874 tree.new_level(format!("AtomicInlineStackingContainer #{index}"));
875 self.atomic_inline_stacking_containers[index].debug_print_with_tree(tree);
876 tree.end_level();
877 },
878 },
879 DebugPrintField::RealStackingContextsAndPositionedStackingContainers => {
880 self.real_stacking_contexts_and_positioned_stacking_containers[*index]
881 .debug_print_with_tree(tree);
882 },
883 DebugPrintField::FloatStackingContainers => {
884 self.float_stacking_containers[*index].debug_print_with_tree(tree);
885 },
886 }
887 }
888 match self.context_type {
889 StackingContextType::AtomicInlineStackingContainer => {
890 },
892 _ => {
893 tree.end_level();
894 },
895 }
896 }
897}
898
899#[derive(PartialEq)]
900pub(crate) enum StackingContextBuildMode {
901 IncludeHoisted,
902 SkipHoisted,
903}
904
905impl Fragment {
906 pub(crate) fn build_stacking_context_tree(
907 &self,
908 stacking_context_tree: &mut StackingContextTree,
909 containing_block_info: &ContainingBlockInfo,
910 stacking_context: &mut StackingContext,
911 mode: StackingContextBuildMode,
912 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
913 ) {
914 if self
915 .base()
916 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
917 {
918 return;
919 }
920
921 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
922 let fragment_clone = self.clone();
923 match self {
924 Fragment::Box(fragment) | Fragment::Float(fragment) => {
925 let fragment = fragment.borrow();
926 if mode == StackingContextBuildMode::SkipHoisted &&
927 fragment.style().clone_position().is_absolutely_positioned()
928 {
929 return;
930 }
931
932 let text_decorations = match self {
933 Fragment::Float(..) => &Default::default(),
934 _ => text_decorations,
935 };
936
937 fragment.build_stacking_context_tree(
938 fragment_clone,
939 stacking_context_tree,
940 containing_block,
941 containing_block_info,
942 stacking_context,
943 text_decorations,
944 );
945 },
946 Fragment::AbsoluteOrFixedPositioned(fragment) => {
947 let shared_fragment = fragment.borrow();
948 let fragment_ref = match shared_fragment.fragment.as_ref() {
949 Some(fragment_ref) => fragment_ref,
950 None => unreachable!("Found hoisted box with missing fragment."),
951 };
952
953 fragment_ref.build_stacking_context_tree(
954 stacking_context_tree,
955 containing_block_info,
956 stacking_context,
957 StackingContextBuildMode::IncludeHoisted,
958 &Default::default(),
959 );
960 },
961 Fragment::Positioning(fragment) => {
962 let fragment = fragment.borrow();
963 fragment.build_stacking_context_tree(
964 stacking_context_tree,
965 containing_block,
966 containing_block_info,
967 stacking_context,
968 text_decorations,
969 );
970 },
971 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
972 stacking_context
973 .contents
974 .push(StackingContextContent::Fragment {
975 section: StackingContextSection::Foreground,
976 scroll_node_id: containing_block.scroll_node_id,
977 reference_frame_scroll_node_id: containing_block_info
978 .for_absolute_and_fixed_descendants
979 .scroll_node_id,
980 clip_id: containing_block.clip_id,
981 containing_block: containing_block.rect,
982 fragment: fragment_clone,
983 is_hit_test_for_scrollable_overflow: false,
984 is_collapsed_table_borders: false,
985 text_decorations: text_decorations.clone(),
986 });
987 },
988 }
989 }
990}
991
992struct ReferenceFrameData {
993 origin: PhysicalPoint<Au>,
994 transform: LayoutTransform,
995 kind: wr::ReferenceFrameKind,
996}
997struct ScrollFrameData {
998 scroll_tree_node_id: ScrollTreeNodeId,
999 scroll_frame_rect: LayoutRect,
1000}
1001
1002struct OverflowFrameData {
1003 clip_id: ClipId,
1004 scroll_frame_data: Option<ScrollFrameData>,
1005}
1006
1007impl BoxFragment {
1008 fn get_stacking_context_type(&self) -> Option<StackingContextType> {
1009 let flags = self.base.flags;
1010 let style = self.style();
1011 if style.establishes_stacking_context(flags) {
1012 return Some(StackingContextType::RealStackingContext);
1013 }
1014
1015 let box_style = &style.get_box();
1016 if box_style.position != ComputedPosition::Static {
1017 return Some(StackingContextType::PositionedStackingContainer);
1018 }
1019
1020 if box_style.float != ComputedFloat::None {
1021 return Some(StackingContextType::FloatStackingContainer);
1022 }
1023
1024 if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
1028 return Some(StackingContextType::AtomicInlineStackingContainer);
1029 }
1030
1031 None
1032 }
1033
1034 fn get_stacking_context_section(&self) -> StackingContextSection {
1035 if self.get_stacking_context_type().is_some() {
1036 return StackingContextSection::OwnBackgroundsAndBorders;
1037 }
1038
1039 if self.style().get_box().display.outside() == DisplayOutside::Inline {
1040 return StackingContextSection::Foreground;
1041 }
1042
1043 StackingContextSection::DescendantBackgroundsAndBorders
1044 }
1045
1046 fn build_stacking_context_tree(
1047 &self,
1048 fragment: Fragment,
1049 stacking_context_tree: &mut StackingContextTree,
1050 containing_block: &ContainingBlock,
1051 containing_block_info: &ContainingBlockInfo,
1052 parent_stacking_context: &mut StackingContext,
1053 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1054 ) {
1055 self.build_stacking_context_tree_maybe_creating_reference_frame(
1056 fragment,
1057 stacking_context_tree,
1058 containing_block,
1059 containing_block_info,
1060 parent_stacking_context,
1061 text_decorations,
1062 );
1063 }
1064
1065 fn build_stacking_context_tree_maybe_creating_reference_frame(
1066 &self,
1067 fragment: Fragment,
1068 stacking_context_tree: &mut StackingContextTree,
1069 containing_block: &ContainingBlock,
1070 containing_block_info: &ContainingBlockInfo,
1071 parent_stacking_context: &mut StackingContext,
1072 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1073 ) {
1074 let reference_frame_data =
1075 match self.reference_frame_data_if_necessary(&containing_block.rect) {
1076 Some(reference_frame_data) => reference_frame_data,
1077 None => {
1078 return self.build_stacking_context_tree_maybe_creating_stacking_context(
1079 fragment,
1080 stacking_context_tree,
1081 containing_block,
1082 containing_block_info,
1083 parent_stacking_context,
1084 text_decorations,
1085 );
1086 },
1087 };
1088
1089 if !reference_frame_data.transform.is_invertible() {
1093 self.clear_spatial_tree_node_including_descendants();
1094 return;
1095 }
1096
1097 let style = self.style();
1098 let frame_origin_for_query = self.cumulative_border_box_rect().origin.to_webrender();
1099 let new_spatial_id = stacking_context_tree.push_reference_frame(
1100 reference_frame_data.origin.to_webrender(),
1101 frame_origin_for_query,
1102 containing_block.scroll_node_id,
1103 style.get_box().transform_style.to_webrender(),
1104 reference_frame_data.transform,
1105 reference_frame_data.kind,
1106 );
1107
1108 assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
1118 let adjusted_containing_block = ContainingBlock::new(
1119 containing_block
1120 .rect
1121 .translate(-reference_frame_data.origin.to_vector()),
1122 new_spatial_id,
1123 None,
1124 containing_block.clip_id,
1125 );
1126 let new_containing_block_info =
1127 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
1128
1129 self.build_stacking_context_tree_maybe_creating_stacking_context(
1130 fragment,
1131 stacking_context_tree,
1132 &adjusted_containing_block,
1133 &new_containing_block_info,
1134 parent_stacking_context,
1135 text_decorations,
1136 );
1137 }
1138
1139 fn build_stacking_context_tree_maybe_creating_stacking_context(
1140 &self,
1141 fragment: Fragment,
1142 stacking_context_tree: &mut StackingContextTree,
1143 containing_block: &ContainingBlock,
1144 containing_block_info: &ContainingBlockInfo,
1145 parent_stacking_context: &mut StackingContext,
1146 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1147 ) {
1148 let context_type = match self.get_stacking_context_type() {
1149 Some(context_type) => context_type,
1150 None => {
1151 self.build_stacking_context_tree_for_children(
1152 fragment,
1153 stacking_context_tree,
1154 containing_block,
1155 containing_block_info,
1156 parent_stacking_context,
1157 text_decorations,
1158 );
1159 return;
1160 },
1161 };
1162
1163 if context_type == StackingContextType::AtomicInlineStackingContainer {
1164 parent_stacking_context.contents.push(
1166 StackingContextContent::AtomicInlineStackingContainer {
1167 index: parent_stacking_context
1168 .atomic_inline_stacking_containers
1169 .len(),
1170 },
1171 );
1172 }
1173
1174 let stacking_context_clip_id = stacking_context_tree
1178 .clip_store
1179 .add_for_clip_path(
1180 self.style().clone_clip_path(),
1181 containing_block.scroll_node_id,
1182 containing_block.clip_id,
1183 BuilderForBoxFragment::new(
1184 self,
1185 &containing_block.rect,
1186 false, false, ),
1189 )
1190 .unwrap_or(containing_block.clip_id);
1191
1192 let box_fragment = match fragment {
1193 Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
1194 box_fragment.clone()
1195 },
1196 _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
1197 };
1198
1199 let mut child_stacking_context = parent_stacking_context.create_descendant(
1200 containing_block.scroll_node_id,
1201 stacking_context_clip_id,
1202 box_fragment,
1203 context_type,
1204 );
1205 self.build_stacking_context_tree_for_children(
1206 fragment,
1207 stacking_context_tree,
1208 containing_block,
1209 containing_block_info,
1210 &mut child_stacking_context,
1211 text_decorations,
1212 );
1213
1214 let mut stolen_children = vec![];
1215 if context_type != StackingContextType::RealStackingContext {
1216 stolen_children = mem::replace(
1217 &mut child_stacking_context
1218 .real_stacking_contexts_and_positioned_stacking_containers,
1219 stolen_children,
1220 );
1221 }
1222
1223 child_stacking_context.sort();
1224 parent_stacking_context.add_stacking_context(child_stacking_context);
1225 parent_stacking_context
1226 .real_stacking_contexts_and_positioned_stacking_containers
1227 .append(&mut stolen_children);
1228 }
1229
1230 fn build_stacking_context_tree_for_children(
1231 &self,
1232 fragment: Fragment,
1233 stacking_context_tree: &mut StackingContextTree,
1234 containing_block: &ContainingBlock,
1235 containing_block_info: &ContainingBlockInfo,
1236 stacking_context: &mut StackingContext,
1237 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1238 ) {
1239 let mut new_scroll_node_id = containing_block.scroll_node_id;
1240 let mut new_clip_id = containing_block.clip_id;
1241 let mut new_scroll_frame_size = containing_block_info
1242 .for_non_absolute_descendants
1243 .scroll_frame_size;
1244
1245 if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
1246 stacking_context_tree,
1247 new_scroll_node_id,
1248 &containing_block.rect,
1249 &new_scroll_frame_size,
1250 ) {
1251 new_scroll_node_id = scroll_node_id;
1252 }
1253
1254 if let Some(clip_id) = self.build_clip_frame_if_necessary(
1255 stacking_context_tree,
1256 new_scroll_node_id,
1257 new_clip_id,
1258 &containing_block.rect,
1259 ) {
1260 new_clip_id = clip_id;
1261 }
1262
1263 let style = self.style();
1264 if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
1265 style.clone_clip_path(),
1266 new_scroll_node_id,
1267 new_clip_id,
1268 BuilderForBoxFragment::new(
1269 self,
1270 &containing_block.rect,
1271 false, false, ),
1274 ) {
1275 new_clip_id = clip_id;
1276 }
1277
1278 let establishes_containing_block_for_all_descendants =
1279 style.establishes_containing_block_for_all_descendants(self.base.flags);
1280 let establishes_containing_block_for_absolute_descendants =
1281 style.establishes_containing_block_for_absolute_descendants(self.base.flags);
1282
1283 let reference_frame_scroll_node_id_for_fragments =
1284 if establishes_containing_block_for_all_descendants {
1285 new_scroll_node_id
1286 } else {
1287 containing_block_info
1288 .for_absolute_and_fixed_descendants
1289 .scroll_node_id
1290 };
1291
1292 let mut add_fragment = |section| {
1293 stacking_context
1294 .contents
1295 .push(StackingContextContent::Fragment {
1296 scroll_node_id: new_scroll_node_id,
1297 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1298 clip_id: new_clip_id,
1299 section,
1300 containing_block: containing_block.rect,
1301 fragment: fragment.clone(),
1302 is_hit_test_for_scrollable_overflow: false,
1303 is_collapsed_table_borders: false,
1304 text_decorations: text_decorations.clone(),
1305 });
1306 };
1307
1308 let section = self.get_stacking_context_section();
1309 add_fragment(section);
1310
1311 *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1315
1316 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
1319 stacking_context_tree,
1320 new_scroll_node_id,
1321 new_clip_id,
1322 &containing_block.rect,
1323 ) {
1324 new_clip_id = overflow_frame_data.clip_id;
1325 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
1326 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
1327 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
1328 stacking_context
1329 .contents
1330 .push(StackingContextContent::Fragment {
1331 scroll_node_id: new_scroll_node_id,
1332 reference_frame_scroll_node_id:
1333 reference_frame_scroll_node_id_for_fragments,
1334 clip_id: new_clip_id,
1335 section,
1336 containing_block: containing_block.rect,
1337 fragment: fragment.clone(),
1338 is_hit_test_for_scrollable_overflow: true,
1339 is_collapsed_table_borders: false,
1340 text_decorations: text_decorations.clone(),
1341 });
1342 }
1343 }
1344
1345 let padding_rect = self
1346 .padding_rect()
1347 .translate(containing_block.rect.origin.to_vector());
1348 let content_rect = self
1349 .content_rect()
1350 .translate(containing_block.rect.origin.to_vector());
1351
1352 let for_absolute_descendants = ContainingBlock::new(
1353 padding_rect,
1354 new_scroll_node_id,
1355 new_scroll_frame_size,
1356 new_clip_id,
1357 );
1358 let for_non_absolute_descendants = ContainingBlock::new(
1359 content_rect,
1360 new_scroll_node_id,
1361 new_scroll_frame_size,
1362 new_clip_id,
1363 );
1364
1365 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
1369 containing_block_info.new_for_absolute_and_fixed_descendants(
1370 &for_non_absolute_descendants,
1371 &for_absolute_descendants,
1372 )
1373 } else if establishes_containing_block_for_absolute_descendants {
1374 containing_block_info.new_for_absolute_descendants(
1375 &for_non_absolute_descendants,
1376 &for_absolute_descendants,
1377 )
1378 } else {
1379 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
1380 };
1381
1382 let text_decorations = match self.is_atomic_inline_level() ||
1388 self.base
1389 .flags
1390 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
1391 {
1392 true => &Default::default(),
1393 false => text_decorations,
1394 };
1395
1396 let new_text_decoration;
1397 let text_decorations = match style.clone_text_decoration_line() {
1398 TextDecorationLine::NONE => text_decorations,
1399 line => {
1400 let mut new_vector = (**text_decorations).clone();
1401 let color = &style.get_inherited_text().color;
1402 new_vector.push(FragmentTextDecoration {
1403 line,
1404 color: style
1405 .clone_text_decoration_color()
1406 .resolve_to_absolute(color),
1407 style: style.clone_text_decoration_style(),
1408 });
1409 new_text_decoration = Arc::new(new_vector);
1410 &new_text_decoration
1411 },
1412 };
1413
1414 for child in &self.children {
1415 child.build_stacking_context_tree(
1416 stacking_context_tree,
1417 &new_containing_block_info,
1418 stacking_context,
1419 StackingContextBuildMode::SkipHoisted,
1420 text_decorations,
1421 );
1422 }
1423
1424 if matches!(
1425 self.specific_layout_info(),
1426 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
1427 ) {
1428 stacking_context
1429 .contents
1430 .push(StackingContextContent::Fragment {
1431 scroll_node_id: new_scroll_node_id,
1432 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1433 clip_id: new_clip_id,
1434 section,
1435 containing_block: containing_block.rect,
1436 fragment: fragment.clone(),
1437 is_hit_test_for_scrollable_overflow: false,
1438 is_collapsed_table_borders: true,
1439 text_decorations: text_decorations.clone(),
1440 });
1441 }
1442 }
1443
1444 fn build_clip_frame_if_necessary(
1445 &self,
1446 stacking_context_tree: &mut StackingContextTree,
1447 parent_scroll_node_id: ScrollTreeNodeId,
1448 parent_clip_id: ClipId,
1449 containing_block_rect: &PhysicalRect<Au>,
1450 ) -> Option<ClipId> {
1451 let style = self.style();
1452 let position = style.get_box().position;
1453 if !position.is_absolutely_positioned() {
1456 return None;
1457 }
1458
1459 let clip_rect = match style.get_effects().clip {
1461 ClipRectOrAuto::Rect(rect) => rect,
1462 _ => return None,
1463 };
1464
1465 let border_rect = self.border_rect();
1466 let clip_rect = clip_rect
1467 .for_border_rect(border_rect)
1468 .translate(containing_block_rect.origin.to_vector())
1469 .to_webrender();
1470 Some(stacking_context_tree.clip_store.add(
1471 BorderRadius::zero(),
1472 clip_rect,
1473 parent_scroll_node_id,
1474 parent_clip_id,
1475 ))
1476 }
1477
1478 fn build_overflow_frame_if_necessary(
1479 &self,
1480 stacking_context_tree: &mut StackingContextTree,
1481 parent_scroll_node_id: ScrollTreeNodeId,
1482 parent_clip_id: ClipId,
1483 containing_block_rect: &PhysicalRect<Au>,
1484 ) -> Option<OverflowFrameData> {
1485 let style = self.style();
1486 let overflow = style.effective_overflow(self.base.flags);
1487
1488 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1489 return None;
1490 }
1491
1492 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1494 let overflow_clip_margin = style.get_margin().overflow_clip_margin;
1495 let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
1496 OverflowClipMarginBox::ContentBox => self.content_rect(),
1497 OverflowClipMarginBox::PaddingBox => self.padding_rect(),
1498 OverflowClipMarginBox::BorderBox => self.border_rect(),
1499 }
1500 .translate(containing_block_rect.origin.to_vector())
1501 .to_webrender();
1502
1503 let clip_margin_offset = overflow_clip_margin.offset.px();
1506 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
1507
1508 let radii;
1511 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1512 let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
1513 let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
1514 match overflow_clip_margin.visual_box {
1515 OverflowClipMarginBox::ContentBox => {
1516 offsets_from_border -= (self.border + self.padding).to_webrender();
1517 },
1518 OverflowClipMarginBox::PaddingBox => {
1519 offsets_from_border -= self.border.to_webrender();
1520 },
1521 OverflowClipMarginBox::BorderBox => {},
1522 };
1523 radii = offset_radii(builder.border_radius, offsets_from_border);
1524 } else if overflow.x != ComputedOverflow::Clip {
1525 overflow_clip_rect.min.x = f32::MIN;
1526 overflow_clip_rect.max.x = f32::MAX;
1527 radii = BorderRadius::zero();
1528 } else {
1529 overflow_clip_rect.min.y = f32::MIN;
1530 overflow_clip_rect.max.y = f32::MAX;
1531 radii = BorderRadius::zero();
1532 }
1533
1534 let clip_id = stacking_context_tree.clip_store.add(
1535 radii,
1536 overflow_clip_rect,
1537 parent_scroll_node_id,
1538 parent_clip_id,
1539 );
1540
1541 return Some(OverflowFrameData {
1542 clip_id,
1543 scroll_frame_data: None,
1544 });
1545 }
1546
1547 let scroll_frame_rect = self
1548 .padding_rect()
1549 .translate(containing_block_rect.origin.to_vector())
1550 .to_webrender();
1551
1552 let clip_id = stacking_context_tree.clip_store.add(
1553 BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
1554 scroll_frame_rect,
1555 parent_scroll_node_id,
1556 parent_clip_id,
1557 );
1558
1559 let tag = self.base.tag?;
1560 let external_scroll_id = wr::ExternalScrollId(
1561 tag.to_display_list_fragment_id(),
1562 stacking_context_tree.paint_info.pipeline_id,
1563 );
1564
1565 let sensitivity = AxesScrollSensitivity {
1566 x: overflow.x.into(),
1567 y: overflow.y.into(),
1568 };
1569
1570 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1571 parent_scroll_node_id,
1572 external_scroll_id,
1573 self.scrollable_overflow().to_webrender(),
1574 scroll_frame_rect,
1575 sensitivity,
1576 );
1577
1578 Some(OverflowFrameData {
1579 clip_id,
1580 scroll_frame_data: Some(ScrollFrameData {
1581 scroll_tree_node_id,
1582 scroll_frame_rect,
1583 }),
1584 })
1585 }
1586
1587 fn build_sticky_frame_if_necessary(
1588 &self,
1589 stacking_context_tree: &mut StackingContextTree,
1590 parent_scroll_node_id: ScrollTreeNodeId,
1591 containing_block_rect: &PhysicalRect<Au>,
1592 scroll_frame_size: &Option<LayoutSize>,
1593 ) -> Option<ScrollTreeNodeId> {
1594 let style = self.style();
1595 if style.get_box().position != ComputedPosition::Sticky {
1596 return None;
1597 }
1598
1599 let scroll_frame_size_for_resolve = match scroll_frame_size {
1600 Some(size) => size,
1601 None => {
1602 &stacking_context_tree
1604 .paint_info
1605 .viewport_details
1606 .layout_size()
1607 },
1608 };
1609
1610 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1614 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1615 let offsets = style.physical_box_offsets();
1616 let offsets = PhysicalSides::<AuOrAuto>::new(
1617 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1618 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1619 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1620 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1621 );
1622 *self.resolved_sticky_insets.borrow_mut() = Some(offsets);
1623
1624 if scroll_frame_size.is_none() {
1625 return None;
1626 }
1627
1628 if offsets.top.is_auto() &&
1629 offsets.right.is_auto() &&
1630 offsets.bottom.is_auto() &&
1631 offsets.left.is_auto()
1632 {
1633 return None;
1634 }
1635
1636 let border_rect = self.border_rect();
1655 let computed_margin = style.physical_margin();
1656
1657 let distance_from_border_box_to_cb = PhysicalSides::new(
1661 border_rect.min_y(),
1662 containing_block_rect.width() - border_rect.max_x(),
1663 containing_block_rect.height() - border_rect.max_y(),
1664 border_rect.min_x(),
1665 );
1666
1667 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1671 let used_margin = if computed_margin.is_auto() {
1672 Au::zero()
1673 } else {
1674 used_margin
1675 };
1676 Au::zero().max(distance - used_margin).to_f32_px()
1677 };
1678
1679 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1682 -offset_bound(
1683 distance_from_border_box_to_cb.top,
1684 self.margin.top,
1685 computed_margin.top,
1686 ),
1687 offset_bound(
1688 distance_from_border_box_to_cb.bottom,
1689 self.margin.bottom,
1690 computed_margin.bottom,
1691 ),
1692 );
1693 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1694 -offset_bound(
1695 distance_from_border_box_to_cb.left,
1696 self.margin.left,
1697 computed_margin.left,
1698 ),
1699 offset_bound(
1700 distance_from_border_box_to_cb.right,
1701 self.margin.right,
1702 computed_margin.right,
1703 ),
1704 );
1705
1706 let frame_rect = border_rect
1707 .translate(containing_block_rect.origin.to_vector())
1708 .to_webrender();
1709
1710 let margins = SideOffsets2D::new(
1713 offsets.top.non_auto().map(|v| v.to_f32_px()),
1714 offsets.right.non_auto().map(|v| v.to_f32_px()),
1715 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1716 offsets.left.non_auto().map(|v| v.to_f32_px()),
1717 );
1718
1719 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1720 parent_scroll_node_id,
1721 frame_rect,
1722 margins,
1723 vertical_offset_bounds,
1724 horizontal_offset_bounds,
1725 );
1726
1727 Some(sticky_node_id)
1728 }
1729
1730 fn reference_frame_data_if_necessary(
1732 &self,
1733 containing_block_rect: &PhysicalRect<Au>,
1734 ) -> Option<ReferenceFrameData> {
1735 if !self
1736 .style()
1737 .has_effective_transform_or_perspective(self.base.flags)
1738 {
1739 return None;
1740 }
1741
1742 let relative_border_rect = self.border_rect();
1743 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1744 let transform = self.calculate_transform_matrix(&border_rect);
1745 let perspective = self.calculate_perspective_matrix(&border_rect);
1746 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1747 (None, Some(perspective)) => (
1748 perspective,
1749 wr::ReferenceFrameKind::Perspective {
1750 scrolling_relative_to: None,
1751 },
1752 ),
1753 (Some(transform), None) => (
1754 transform,
1755 wr::ReferenceFrameKind::Transform {
1756 is_2d_scale_translation: false,
1757 should_snap: false,
1758 paired_with_perspective: false,
1759 },
1760 ),
1761 (Some(transform), Some(perspective)) => (
1762 perspective.then(&transform),
1763 wr::ReferenceFrameKind::Perspective {
1764 scrolling_relative_to: None,
1765 },
1766 ),
1767 (None, None) => unreachable!(),
1768 };
1769
1770 Some(ReferenceFrameData {
1771 origin: border_rect.origin,
1772 transform: reference_frame_transform,
1773 kind: reference_frame_kind,
1774 })
1775 }
1776
1777 pub fn calculate_transform_matrix(
1779 &self,
1780 border_rect: &Rect<Au, CSSPixel>,
1781 ) -> Option<LayoutTransform> {
1782 let style = self.style();
1783 let list = &style.get_box().transform;
1784 let length_rect = au_rect_to_length_rect(border_rect);
1785 let rotate = match style.clone_rotate() {
1787 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1788 GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle),
1789 GenericRotate::None => (0., 0., 1., Angle::zero()),
1790 };
1791 let scale = match style.clone_scale() {
1792 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1793 GenericScale::None => (1., 1., 1.),
1794 };
1795 let translation = match style.clone_translate() {
1796 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1797 x.resolve(length_rect.size.width).px(),
1798 y.resolve(length_rect.size.height).px(),
1799 z.px(),
1800 ),
1801 GenericTranslate::None => LayoutTransform::identity(),
1802 };
1803
1804 let angle = euclid::Angle::radians(rotate.3.radians());
1805 let transform_base = list
1806 .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1807 .ok()?;
1808 let transform = LayoutTransform::from_untyped(&transform_base.0)
1809 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1810 .then_scale(scale.0, scale.1, scale.2)
1811 .then(&translation);
1812
1813 let transform_origin = &style.get_box().transform_origin;
1814 let transform_origin_x = transform_origin
1815 .horizontal
1816 .to_used_value(border_rect.size.width)
1817 .to_f32_px();
1818 let transform_origin_y = transform_origin
1819 .vertical
1820 .to_used_value(border_rect.size.height)
1821 .to_f32_px();
1822 let transform_origin_z = transform_origin.depth.px();
1823
1824 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1825 }
1826
1827 pub fn calculate_perspective_matrix(
1829 &self,
1830 border_rect: &Rect<Au, CSSPixel>,
1831 ) -> Option<LayoutTransform> {
1832 let style = self.style();
1833 match style.get_box().perspective {
1834 Perspective::Length(length) => {
1835 let perspective_origin = &style.get_box().perspective_origin;
1836 let perspective_origin = LayoutPoint::new(
1837 perspective_origin
1838 .horizontal
1839 .percentage_relative_to(border_rect.size.width.into())
1840 .px(),
1841 perspective_origin
1842 .vertical
1843 .percentage_relative_to(border_rect.size.height.into())
1844 .px(),
1845 );
1846
1847 let perspective_matrix = LayoutTransform::from_untyped(
1848 &transform::create_perspective_matrix(length.px()),
1849 );
1850
1851 Some(perspective_matrix.change_basis(
1852 perspective_origin.x,
1853 perspective_origin.y,
1854 0.0,
1855 ))
1856 },
1857 Perspective::None => None,
1858 }
1859 }
1860
1861 fn clear_spatial_tree_node_including_descendants(&self) {
1862 fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1863 for fragment in fragments.iter() {
1864 match fragment {
1865 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1866 box_fragment
1867 .borrow()
1868 .clear_spatial_tree_node_including_descendants();
1869 },
1870 Fragment::Positioning(positioning_fragment) => {
1871 assign_spatial_tree_node_on_fragments(
1872 &positioning_fragment.borrow().children,
1873 );
1874 },
1875 _ => {},
1876 }
1877 }
1878 }
1879
1880 *self.spatial_tree_node.borrow_mut() = None;
1881 assign_spatial_tree_node_on_fragments(&self.children);
1882 }
1883}
1884
1885impl PositioningFragment {
1886 fn build_stacking_context_tree(
1887 &self,
1888 stacking_context_tree: &mut StackingContextTree,
1889 containing_block: &ContainingBlock,
1890 containing_block_info: &ContainingBlockInfo,
1891 stacking_context: &mut StackingContext,
1892 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1893 ) {
1894 let rect = self
1895 .base
1896 .rect
1897 .translate(containing_block.rect.origin.to_vector());
1898 let new_containing_block = containing_block.new_replacing_rect(&rect);
1899 let new_containing_block_info =
1900 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1901
1902 for child in &self.children {
1903 child.build_stacking_context_tree(
1904 stacking_context_tree,
1905 &new_containing_block_info,
1906 stacking_context,
1907 StackingContextBuildMode::SkipHoisted,
1908 text_decorations,
1909 );
1910 }
1911 }
1912}
1913
1914pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1915 Rect::new(
1916 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1917 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1918 )
1919}