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