1use core::f32;
6use std::cell::{Cell, RefCell};
7use std::mem;
8use std::sync::Arc;
9
10use app_units::Au;
11use embedder_traits::ViewportDetails;
12use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
13use log::warn;
14use malloc_size_of_derive::MallocSizeOf;
15use paint_api::display_list::{
16 AxesScrollSensitivity, PaintDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
17 SpatialTreeNodeInfo, StickyNodeInfo,
18};
19use servo_base::id::ScrollTreeNodeId;
20use servo_base::print_tree::PrintTree;
21use servo_config::opts::DiagnosticsLogging;
22use style::Zero;
23use style::color::{AbsoluteColor, ColorSpace};
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 let default_background_color = servo_config::pref!(shell_background_color_rgba);
716 let default_background_color = AbsoluteColor::new(
717 ColorSpace::Srgb,
718 default_background_color[0] as f32,
719 default_background_color[1] as f32,
720 default_background_color[2] as f32,
721 default_background_color[3] as f32,
722 )
723 .into_srgb_legacy();
724 if background_color != default_background_color {
725 builder.mark_is_paintable();
726 }
727 }
728
729 let mut fragment_builder = BuilderForBoxFragment::new(
730 &root_fragment,
731 &fragment_tree.initial_containing_block,
732 false, false, );
735 let painter = super::background::BackgroundPainter {
736 style: &source_style,
737 painting_area_override: Some(painting_area),
738 positioning_area_override: None,
739 };
740 fragment_builder.build_background_image(builder, &painter);
741 }
742
743 pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
747 let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
748
749 let mut content_with_outlines = Vec::new();
758 let mut contents = self.contents.iter().enumerate().peekable();
759 while contents.peek().is_some_and(|(_, child)| {
760 child.section() == StackingContextSection::OwnBackgroundsAndBorders
761 }) {
762 let (i, child) = contents.next().unwrap();
763 self.debug_push_print_item(DebugPrintField::Contents, i);
764 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
765
766 if child.has_outline() {
767 content_with_outlines.push(child);
768 }
769 }
770
771 let mut real_stacking_contexts_and_positioned_stacking_containers = self
773 .real_stacking_contexts_and_positioned_stacking_containers
774 .iter()
775 .enumerate()
776 .peekable();
777 while real_stacking_contexts_and_positioned_stacking_containers
778 .peek()
779 .is_some_and(|(_, child)| child.z_index() < 0)
780 {
781 let (i, child) = real_stacking_contexts_and_positioned_stacking_containers
782 .next()
783 .unwrap();
784 self.debug_push_print_item(
785 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
786 i,
787 );
788 child.build_display_list(builder);
789 }
790
791 while contents.peek().is_some_and(|(_, child)| {
793 child.section() == StackingContextSection::DescendantBackgroundsAndBorders
794 }) {
795 let (i, child) = contents.next().unwrap();
796 self.debug_push_print_item(DebugPrintField::Contents, i);
797 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
798
799 if child.has_outline() {
800 content_with_outlines.push(child);
801 }
802 }
803
804 for (i, child) in self.float_stacking_containers.iter().enumerate() {
806 self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
807 child.build_display_list(builder);
808 }
809
810 while contents
812 .peek()
813 .is_some_and(|(_, child)| child.section() == StackingContextSection::Foreground)
814 {
815 let (i, child) = contents.next().unwrap();
816 self.debug_push_print_item(DebugPrintField::Contents, i);
817 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
818
819 if child.has_outline() {
820 content_with_outlines.push(child);
821 }
822 }
823
824 for (i, child) in real_stacking_contexts_and_positioned_stacking_containers {
827 self.debug_push_print_item(
828 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
829 i,
830 );
831 child.build_display_list(builder);
832 }
833
834 for content in content_with_outlines {
836 content.build_display_list_with_section_override(
837 builder,
838 &self.atomic_inline_stacking_containers,
839 Some(StackingContextSection::Outline),
840 );
841 }
842
843 if pushed_context {
844 builder.wr().pop_stacking_context();
845 }
846 }
847
848 fn debug_push_print_item(&self, field: DebugPrintField, index: usize) {
853 if let Some(items) = self.debug_print_items.as_ref() {
854 items.borrow_mut().push(DebugPrintItem { field, index });
855 }
856 }
857
858 pub fn debug_print(&self) {
860 if self.debug_print_items.is_none() {
861 warn!("failed to print stacking context tree: debug_print_items was None");
862 return;
863 }
864 let mut tree = PrintTree::new("Stacking context tree".to_owned());
865 self.debug_print_with_tree(&mut tree);
866 }
867
868 fn debug_print_with_tree(&self, tree: &mut PrintTree) {
870 match self.context_type {
871 StackingContextType::RealStackingContext => {
872 tree.new_level(format!("{:?} z={}", self.context_type, self.z_index()));
873 },
874 StackingContextType::AtomicInlineStackingContainer => {
875 },
877 _ => {
878 tree.new_level(format!("{:?}", self.context_type));
879 },
880 }
881 for DebugPrintItem { field, index } in
882 self.debug_print_items.as_ref().unwrap().borrow().iter()
883 {
884 match field {
885 DebugPrintField::Contents => match self.contents[*index] {
886 StackingContextContent::Fragment { section, .. } => {
887 tree.add_item(format!("{section:?}"));
888 },
889 StackingContextContent::AtomicInlineStackingContainer { index } => {
890 tree.new_level(format!("AtomicInlineStackingContainer #{index}"));
891 self.atomic_inline_stacking_containers[index].debug_print_with_tree(tree);
892 tree.end_level();
893 },
894 },
895 DebugPrintField::RealStackingContextsAndPositionedStackingContainers => {
896 self.real_stacking_contexts_and_positioned_stacking_containers[*index]
897 .debug_print_with_tree(tree);
898 },
899 DebugPrintField::FloatStackingContainers => {
900 self.float_stacking_containers[*index].debug_print_with_tree(tree);
901 },
902 }
903 }
904 match self.context_type {
905 StackingContextType::AtomicInlineStackingContainer => {
906 },
908 _ => {
909 tree.end_level();
910 },
911 }
912 }
913}
914
915#[derive(PartialEq)]
916pub(crate) enum StackingContextBuildMode {
917 IncludeHoisted,
918 SkipHoisted,
919}
920
921impl Fragment {
922 pub(crate) fn build_stacking_context_tree(
923 &self,
924 stacking_context_tree: &mut StackingContextTree,
925 containing_block_info: &ContainingBlockInfo,
926 stacking_context: &mut StackingContext,
927 mode: StackingContextBuildMode,
928 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
929 ) {
930 if self
931 .base()
932 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
933 {
934 return;
935 }
936
937 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
938 let fragment_clone = self.clone();
939 match self {
940 Fragment::Box(fragment) | Fragment::Float(fragment) => {
941 let fragment = fragment.borrow();
942 if mode == StackingContextBuildMode::SkipHoisted &&
943 fragment.style().clone_position().is_absolutely_positioned()
944 {
945 return;
946 }
947
948 let text_decorations = match self {
949 Fragment::Float(..) => &Default::default(),
950 _ => text_decorations,
951 };
952
953 fragment.build_stacking_context_tree(
954 fragment_clone,
955 stacking_context_tree,
956 containing_block,
957 containing_block_info,
958 stacking_context,
959 text_decorations,
960 );
961 },
962 Fragment::AbsoluteOrFixedPositioned(fragment) => {
963 let shared_fragment = fragment.borrow();
964 let fragment_ref = match shared_fragment.fragment.as_ref() {
965 Some(fragment_ref) => fragment_ref,
966 None => unreachable!("Found hoisted box with missing fragment."),
967 };
968
969 fragment_ref.build_stacking_context_tree(
970 stacking_context_tree,
971 containing_block_info,
972 stacking_context,
973 StackingContextBuildMode::IncludeHoisted,
974 &Default::default(),
975 );
976 },
977 Fragment::Positioning(fragment) => {
978 let fragment = fragment.borrow();
979 fragment.build_stacking_context_tree(
980 stacking_context_tree,
981 containing_block,
982 containing_block_info,
983 stacking_context,
984 text_decorations,
985 );
986 },
987 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
988 stacking_context
989 .contents
990 .push(StackingContextContent::Fragment {
991 section: StackingContextSection::Foreground,
992 scroll_node_id: containing_block.scroll_node_id,
993 reference_frame_scroll_node_id: containing_block_info
994 .for_absolute_and_fixed_descendants
995 .scroll_node_id,
996 clip_id: containing_block.clip_id,
997 containing_block: containing_block.rect,
998 fragment: fragment_clone,
999 is_hit_test_for_scrollable_overflow: false,
1000 is_collapsed_table_borders: false,
1001 text_decorations: text_decorations.clone(),
1002 });
1003 },
1004 }
1005 }
1006}
1007
1008struct ReferenceFrameData {
1009 origin: PhysicalPoint<Au>,
1010 transform: LayoutTransform,
1011 kind: wr::ReferenceFrameKind,
1012}
1013struct ScrollFrameData {
1014 scroll_tree_node_id: ScrollTreeNodeId,
1015 scroll_frame_rect: LayoutRect,
1016}
1017
1018struct OverflowFrameData {
1019 clip_id: ClipId,
1020 scroll_frame_data: Option<ScrollFrameData>,
1021}
1022
1023impl BoxFragment {
1024 fn get_stacking_context_type(&self) -> Option<StackingContextType> {
1025 let flags = self.base.flags;
1026 let style = self.style();
1027 if style.establishes_stacking_context(flags) {
1028 return Some(StackingContextType::RealStackingContext);
1029 }
1030
1031 let box_style = &style.get_box();
1032 if box_style.position != ComputedPosition::Static {
1033 return Some(StackingContextType::PositionedStackingContainer);
1034 }
1035
1036 if box_style.float != ComputedFloat::None {
1037 return Some(StackingContextType::FloatStackingContainer);
1038 }
1039
1040 if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
1044 return Some(StackingContextType::AtomicInlineStackingContainer);
1045 }
1046
1047 None
1048 }
1049
1050 fn get_stacking_context_section(&self) -> StackingContextSection {
1051 if self.get_stacking_context_type().is_some() {
1052 return StackingContextSection::OwnBackgroundsAndBorders;
1053 }
1054
1055 if self.style().get_box().display.outside() == DisplayOutside::Inline {
1056 return StackingContextSection::Foreground;
1057 }
1058
1059 StackingContextSection::DescendantBackgroundsAndBorders
1060 }
1061
1062 fn build_stacking_context_tree(
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 self.build_stacking_context_tree_maybe_creating_reference_frame(
1072 fragment,
1073 stacking_context_tree,
1074 containing_block,
1075 containing_block_info,
1076 parent_stacking_context,
1077 text_decorations,
1078 );
1079 }
1080
1081 fn build_stacking_context_tree_maybe_creating_reference_frame(
1082 &self,
1083 fragment: Fragment,
1084 stacking_context_tree: &mut StackingContextTree,
1085 containing_block: &ContainingBlock,
1086 containing_block_info: &ContainingBlockInfo,
1087 parent_stacking_context: &mut StackingContext,
1088 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1089 ) {
1090 let reference_frame_data =
1091 match self.reference_frame_data_if_necessary(&containing_block.rect) {
1092 Some(reference_frame_data) => reference_frame_data,
1093 None => {
1094 return self.build_stacking_context_tree_maybe_creating_stacking_context(
1095 fragment,
1096 stacking_context_tree,
1097 containing_block,
1098 containing_block_info,
1099 parent_stacking_context,
1100 text_decorations,
1101 );
1102 },
1103 };
1104
1105 if !reference_frame_data.transform.is_invertible() {
1109 self.clear_spatial_tree_node_including_descendants();
1110 return;
1111 }
1112
1113 let style = self.style();
1114 let frame_origin_for_query = self.cumulative_border_box_rect().origin.to_webrender();
1115 let new_spatial_id = stacking_context_tree.push_reference_frame(
1116 reference_frame_data.origin.to_webrender(),
1117 frame_origin_for_query,
1118 containing_block.scroll_node_id,
1119 style.get_box().transform_style.to_webrender(),
1120 reference_frame_data.transform,
1121 reference_frame_data.kind,
1122 );
1123
1124 assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
1134 let adjusted_containing_block = ContainingBlock::new(
1135 containing_block
1136 .rect
1137 .translate(-reference_frame_data.origin.to_vector()),
1138 new_spatial_id,
1139 None,
1140 containing_block.clip_id,
1141 );
1142 let new_containing_block_info =
1143 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
1144
1145 self.build_stacking_context_tree_maybe_creating_stacking_context(
1146 fragment,
1147 stacking_context_tree,
1148 &adjusted_containing_block,
1149 &new_containing_block_info,
1150 parent_stacking_context,
1151 text_decorations,
1152 );
1153 }
1154
1155 fn build_stacking_context_tree_maybe_creating_stacking_context(
1156 &self,
1157 fragment: Fragment,
1158 stacking_context_tree: &mut StackingContextTree,
1159 containing_block: &ContainingBlock,
1160 containing_block_info: &ContainingBlockInfo,
1161 parent_stacking_context: &mut StackingContext,
1162 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1163 ) {
1164 let context_type = match self.get_stacking_context_type() {
1165 Some(context_type) => context_type,
1166 None => {
1167 self.build_stacking_context_tree_for_children(
1168 fragment,
1169 stacking_context_tree,
1170 containing_block,
1171 containing_block_info,
1172 parent_stacking_context,
1173 text_decorations,
1174 );
1175 return;
1176 },
1177 };
1178
1179 if context_type == StackingContextType::AtomicInlineStackingContainer {
1180 parent_stacking_context.contents.push(
1182 StackingContextContent::AtomicInlineStackingContainer {
1183 index: parent_stacking_context
1184 .atomic_inline_stacking_containers
1185 .len(),
1186 },
1187 );
1188 }
1189
1190 let stacking_context_clip_id = stacking_context_tree
1194 .clip_store
1195 .add_for_clip_path(
1196 self.style().clone_clip_path(),
1197 containing_block.scroll_node_id,
1198 containing_block.clip_id,
1199 BuilderForBoxFragment::new(
1200 self,
1201 &containing_block.rect,
1202 false, false, ),
1205 )
1206 .unwrap_or(containing_block.clip_id);
1207
1208 let box_fragment = match fragment {
1209 Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
1210 box_fragment.clone()
1211 },
1212 _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
1213 };
1214
1215 let mut child_stacking_context = parent_stacking_context.create_descendant(
1216 containing_block.scroll_node_id,
1217 stacking_context_clip_id,
1218 box_fragment,
1219 context_type,
1220 );
1221 self.build_stacking_context_tree_for_children(
1222 fragment,
1223 stacking_context_tree,
1224 containing_block,
1225 containing_block_info,
1226 &mut child_stacking_context,
1227 text_decorations,
1228 );
1229
1230 let mut stolen_children = vec![];
1231 if context_type != StackingContextType::RealStackingContext {
1232 stolen_children = mem::replace(
1233 &mut child_stacking_context
1234 .real_stacking_contexts_and_positioned_stacking_containers,
1235 stolen_children,
1236 );
1237 }
1238
1239 child_stacking_context.sort();
1240 parent_stacking_context.add_stacking_context(child_stacking_context);
1241 parent_stacking_context
1242 .real_stacking_contexts_and_positioned_stacking_containers
1243 .append(&mut stolen_children);
1244 }
1245
1246 fn build_stacking_context_tree_for_children(
1247 &self,
1248 fragment: Fragment,
1249 stacking_context_tree: &mut StackingContextTree,
1250 containing_block: &ContainingBlock,
1251 containing_block_info: &ContainingBlockInfo,
1252 stacking_context: &mut StackingContext,
1253 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1254 ) {
1255 let mut new_scroll_node_id = containing_block.scroll_node_id;
1256 let mut new_clip_id = containing_block.clip_id;
1257 let mut new_scroll_frame_size = containing_block_info
1258 .for_non_absolute_descendants
1259 .scroll_frame_size;
1260
1261 if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
1262 stacking_context_tree,
1263 new_scroll_node_id,
1264 &containing_block.rect,
1265 &new_scroll_frame_size,
1266 ) {
1267 new_scroll_node_id = scroll_node_id;
1268 }
1269
1270 if let Some(clip_id) = self.build_clip_frame_if_necessary(
1271 stacking_context_tree,
1272 new_scroll_node_id,
1273 new_clip_id,
1274 &containing_block.rect,
1275 ) {
1276 new_clip_id = clip_id;
1277 }
1278
1279 let style = self.style();
1280 if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
1281 style.clone_clip_path(),
1282 new_scroll_node_id,
1283 new_clip_id,
1284 BuilderForBoxFragment::new(
1285 self,
1286 &containing_block.rect,
1287 false, false, ),
1290 ) {
1291 new_clip_id = clip_id;
1292 }
1293
1294 let establishes_containing_block_for_all_descendants =
1295 style.establishes_containing_block_for_all_descendants(self.base.flags);
1296 let establishes_containing_block_for_absolute_descendants =
1297 style.establishes_containing_block_for_absolute_descendants(self.base.flags);
1298
1299 let reference_frame_scroll_node_id_for_fragments =
1300 if establishes_containing_block_for_all_descendants {
1301 new_scroll_node_id
1302 } else {
1303 containing_block_info
1304 .for_absolute_and_fixed_descendants
1305 .scroll_node_id
1306 };
1307
1308 let mut add_fragment = |section| {
1309 stacking_context
1310 .contents
1311 .push(StackingContextContent::Fragment {
1312 scroll_node_id: new_scroll_node_id,
1313 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1314 clip_id: new_clip_id,
1315 section,
1316 containing_block: containing_block.rect,
1317 fragment: fragment.clone(),
1318 is_hit_test_for_scrollable_overflow: false,
1319 is_collapsed_table_borders: false,
1320 text_decorations: text_decorations.clone(),
1321 });
1322 };
1323
1324 let section = self.get_stacking_context_section();
1325 add_fragment(section);
1326
1327 *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1331
1332 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
1335 stacking_context_tree,
1336 new_scroll_node_id,
1337 new_clip_id,
1338 &containing_block.rect,
1339 ) {
1340 new_clip_id = overflow_frame_data.clip_id;
1341 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
1342 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
1343 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
1344 stacking_context
1345 .contents
1346 .push(StackingContextContent::Fragment {
1347 scroll_node_id: new_scroll_node_id,
1348 reference_frame_scroll_node_id:
1349 reference_frame_scroll_node_id_for_fragments,
1350 clip_id: new_clip_id,
1351 section,
1352 containing_block: containing_block.rect,
1353 fragment: fragment.clone(),
1354 is_hit_test_for_scrollable_overflow: true,
1355 is_collapsed_table_borders: false,
1356 text_decorations: text_decorations.clone(),
1357 });
1358 }
1359 }
1360
1361 let padding_rect = self
1362 .padding_rect()
1363 .translate(containing_block.rect.origin.to_vector());
1364 let content_rect = self
1365 .content_rect()
1366 .translate(containing_block.rect.origin.to_vector());
1367
1368 let for_absolute_descendants = ContainingBlock::new(
1369 padding_rect,
1370 new_scroll_node_id,
1371 new_scroll_frame_size,
1372 new_clip_id,
1373 );
1374 let for_non_absolute_descendants = ContainingBlock::new(
1375 content_rect,
1376 new_scroll_node_id,
1377 new_scroll_frame_size,
1378 new_clip_id,
1379 );
1380
1381 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
1385 containing_block_info.new_for_absolute_and_fixed_descendants(
1386 &for_non_absolute_descendants,
1387 &for_absolute_descendants,
1388 )
1389 } else if establishes_containing_block_for_absolute_descendants {
1390 containing_block_info.new_for_absolute_descendants(
1391 &for_non_absolute_descendants,
1392 &for_absolute_descendants,
1393 )
1394 } else {
1395 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
1396 };
1397
1398 let text_decorations = match self.is_atomic_inline_level() ||
1404 self.base
1405 .flags
1406 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
1407 {
1408 true => &Default::default(),
1409 false => text_decorations,
1410 };
1411
1412 let new_text_decoration;
1413 let text_decorations = match style.clone_text_decoration_line() {
1414 TextDecorationLine::NONE => text_decorations,
1415 line => {
1416 let mut new_vector = (**text_decorations).clone();
1417 let color = &style.get_inherited_text().color;
1418 new_vector.push(FragmentTextDecoration {
1419 line,
1420 color: style
1421 .clone_text_decoration_color()
1422 .resolve_to_absolute(color),
1423 style: style.clone_text_decoration_style(),
1424 });
1425 new_text_decoration = Arc::new(new_vector);
1426 &new_text_decoration
1427 },
1428 };
1429
1430 for child in &self.children {
1431 child.build_stacking_context_tree(
1432 stacking_context_tree,
1433 &new_containing_block_info,
1434 stacking_context,
1435 StackingContextBuildMode::SkipHoisted,
1436 text_decorations,
1437 );
1438 }
1439
1440 if matches!(
1441 self.specific_layout_info().as_deref(),
1442 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
1443 ) {
1444 stacking_context
1445 .contents
1446 .push(StackingContextContent::Fragment {
1447 scroll_node_id: new_scroll_node_id,
1448 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1449 clip_id: new_clip_id,
1450 section,
1451 containing_block: containing_block.rect,
1452 fragment: fragment.clone(),
1453 is_hit_test_for_scrollable_overflow: false,
1454 is_collapsed_table_borders: true,
1455 text_decorations: text_decorations.clone(),
1456 });
1457 }
1458 }
1459
1460 fn build_clip_frame_if_necessary(
1461 &self,
1462 stacking_context_tree: &mut StackingContextTree,
1463 parent_scroll_node_id: ScrollTreeNodeId,
1464 parent_clip_id: ClipId,
1465 containing_block_rect: &PhysicalRect<Au>,
1466 ) -> Option<ClipId> {
1467 let style = self.style();
1468 let position = style.get_box().position;
1469 if !position.is_absolutely_positioned() {
1472 return None;
1473 }
1474
1475 let clip_rect = match style.get_effects().clip {
1477 ClipRectOrAuto::Rect(rect) => rect,
1478 _ => return None,
1479 };
1480
1481 let border_rect = self.border_rect();
1482 let clip_rect = clip_rect
1483 .for_border_rect(border_rect)
1484 .translate(containing_block_rect.origin.to_vector())
1485 .to_webrender();
1486 Some(stacking_context_tree.clip_store.add(
1487 BorderRadius::zero(),
1488 clip_rect,
1489 parent_scroll_node_id,
1490 parent_clip_id,
1491 ))
1492 }
1493
1494 fn build_overflow_frame_if_necessary(
1495 &self,
1496 stacking_context_tree: &mut StackingContextTree,
1497 parent_scroll_node_id: ScrollTreeNodeId,
1498 parent_clip_id: ClipId,
1499 containing_block_rect: &PhysicalRect<Au>,
1500 ) -> Option<OverflowFrameData> {
1501 let style = self.style();
1502 let overflow = style.effective_overflow(self.base.flags);
1503
1504 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1505 return None;
1506 }
1507
1508 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1510 let overflow_clip_margin = style.get_margin().overflow_clip_margin;
1511 let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
1512 OverflowClipMarginBox::ContentBox => self.content_rect(),
1513 OverflowClipMarginBox::PaddingBox => self.padding_rect(),
1514 OverflowClipMarginBox::BorderBox => self.border_rect(),
1515 }
1516 .translate(containing_block_rect.origin.to_vector())
1517 .to_webrender();
1518
1519 let clip_margin_offset = overflow_clip_margin.offset.px();
1522 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
1523
1524 let radii;
1527 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1528 let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
1529 let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
1530 match overflow_clip_margin.visual_box {
1531 OverflowClipMarginBox::ContentBox => {
1532 offsets_from_border -= (self.border + self.padding).to_webrender();
1533 },
1534 OverflowClipMarginBox::PaddingBox => {
1535 offsets_from_border -= self.border.to_webrender();
1536 },
1537 OverflowClipMarginBox::BorderBox => {},
1538 };
1539 radii = offset_radii(builder.border_radius, offsets_from_border);
1540 } else if overflow.x != ComputedOverflow::Clip {
1541 overflow_clip_rect.min.x = f32::MIN;
1542 overflow_clip_rect.max.x = f32::MAX;
1543 radii = BorderRadius::zero();
1544 } else {
1545 overflow_clip_rect.min.y = f32::MIN;
1546 overflow_clip_rect.max.y = f32::MAX;
1547 radii = BorderRadius::zero();
1548 }
1549
1550 let clip_id = stacking_context_tree.clip_store.add(
1551 radii,
1552 overflow_clip_rect,
1553 parent_scroll_node_id,
1554 parent_clip_id,
1555 );
1556
1557 return Some(OverflowFrameData {
1558 clip_id,
1559 scroll_frame_data: None,
1560 });
1561 }
1562
1563 let scroll_frame_rect = self
1564 .padding_rect()
1565 .translate(containing_block_rect.origin.to_vector())
1566 .to_webrender();
1567
1568 let clip_id = stacking_context_tree.clip_store.add(
1569 BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
1570 scroll_frame_rect,
1571 parent_scroll_node_id,
1572 parent_clip_id,
1573 );
1574
1575 let tag = self.base.tag?;
1576 let external_scroll_id = wr::ExternalScrollId(
1577 tag.to_display_list_fragment_id(),
1578 stacking_context_tree.paint_info.pipeline_id,
1579 );
1580
1581 let sensitivity = AxesScrollSensitivity {
1582 x: overflow.x.into(),
1583 y: overflow.y.into(),
1584 };
1585
1586 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1587 parent_scroll_node_id,
1588 external_scroll_id,
1589 self.scrollable_overflow().to_webrender(),
1590 scroll_frame_rect,
1591 sensitivity,
1592 );
1593
1594 Some(OverflowFrameData {
1595 clip_id,
1596 scroll_frame_data: Some(ScrollFrameData {
1597 scroll_tree_node_id,
1598 scroll_frame_rect,
1599 }),
1600 })
1601 }
1602
1603 fn build_sticky_frame_if_necessary(
1604 &self,
1605 stacking_context_tree: &mut StackingContextTree,
1606 parent_scroll_node_id: ScrollTreeNodeId,
1607 containing_block_rect: &PhysicalRect<Au>,
1608 scroll_frame_size: &Option<LayoutSize>,
1609 ) -> Option<ScrollTreeNodeId> {
1610 let style = self.style();
1611 if style.get_box().position != ComputedPosition::Sticky {
1612 return None;
1613 }
1614
1615 let scroll_frame_size_for_resolve = match scroll_frame_size {
1616 Some(size) => size,
1617 None => {
1618 &stacking_context_tree
1620 .paint_info
1621 .viewport_details
1622 .layout_size()
1623 },
1624 };
1625
1626 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1630 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1631 let offsets = style.physical_box_offsets();
1632 let offsets = PhysicalSides::<AuOrAuto>::new(
1633 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1634 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1635 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1636 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1637 );
1638 self.ensure_rare_data().resolved_sticky_insets = Some(Box::new(offsets));
1639
1640 if scroll_frame_size.is_none() {
1641 return None;
1642 }
1643
1644 if offsets.top.is_auto() &&
1645 offsets.right.is_auto() &&
1646 offsets.bottom.is_auto() &&
1647 offsets.left.is_auto()
1648 {
1649 return None;
1650 }
1651
1652 let border_rect = self.border_rect();
1671 let computed_margin = style.physical_margin();
1672
1673 let distance_from_border_box_to_cb = PhysicalSides::new(
1677 border_rect.min_y(),
1678 containing_block_rect.width() - border_rect.max_x(),
1679 containing_block_rect.height() - border_rect.max_y(),
1680 border_rect.min_x(),
1681 );
1682
1683 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1687 let used_margin = if computed_margin.is_auto() {
1688 Au::zero()
1689 } else {
1690 used_margin
1691 };
1692 Au::zero().max(distance - used_margin).to_f32_px()
1693 };
1694
1695 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1698 -offset_bound(
1699 distance_from_border_box_to_cb.top,
1700 self.margin.top,
1701 computed_margin.top,
1702 ),
1703 offset_bound(
1704 distance_from_border_box_to_cb.bottom,
1705 self.margin.bottom,
1706 computed_margin.bottom,
1707 ),
1708 );
1709 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1710 -offset_bound(
1711 distance_from_border_box_to_cb.left,
1712 self.margin.left,
1713 computed_margin.left,
1714 ),
1715 offset_bound(
1716 distance_from_border_box_to_cb.right,
1717 self.margin.right,
1718 computed_margin.right,
1719 ),
1720 );
1721
1722 let frame_rect = border_rect
1723 .translate(containing_block_rect.origin.to_vector())
1724 .to_webrender();
1725
1726 let margins = SideOffsets2D::new(
1729 offsets.top.non_auto().map(|v| v.to_f32_px()),
1730 offsets.right.non_auto().map(|v| v.to_f32_px()),
1731 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1732 offsets.left.non_auto().map(|v| v.to_f32_px()),
1733 );
1734
1735 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1736 parent_scroll_node_id,
1737 frame_rect,
1738 margins,
1739 vertical_offset_bounds,
1740 horizontal_offset_bounds,
1741 );
1742
1743 Some(sticky_node_id)
1744 }
1745
1746 fn reference_frame_data_if_necessary(
1748 &self,
1749 containing_block_rect: &PhysicalRect<Au>,
1750 ) -> Option<ReferenceFrameData> {
1751 if !self
1752 .style()
1753 .has_effective_transform_or_perspective(self.base.flags)
1754 {
1755 return None;
1756 }
1757
1758 let relative_border_rect = self.border_rect();
1759 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1760 let transform = self.calculate_transform_matrix(&border_rect);
1761 let perspective = self.calculate_perspective_matrix(&border_rect);
1762 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1763 (None, Some(perspective)) => (
1764 perspective,
1765 wr::ReferenceFrameKind::Perspective {
1766 scrolling_relative_to: None,
1767 },
1768 ),
1769 (Some(transform), None) => (
1770 transform,
1771 wr::ReferenceFrameKind::Transform {
1772 is_2d_scale_translation: false,
1773 should_snap: false,
1774 paired_with_perspective: false,
1775 },
1776 ),
1777 (Some(transform), Some(perspective)) => (
1778 perspective.then(&transform),
1779 wr::ReferenceFrameKind::Perspective {
1780 scrolling_relative_to: None,
1781 },
1782 ),
1783 (None, None) => unreachable!(),
1784 };
1785
1786 Some(ReferenceFrameData {
1787 origin: border_rect.origin,
1788 transform: reference_frame_transform,
1789 kind: reference_frame_kind,
1790 })
1791 }
1792
1793 pub fn calculate_transform_matrix(
1795 &self,
1796 border_rect: &Rect<Au, CSSPixel>,
1797 ) -> Option<LayoutTransform> {
1798 let style = self.style();
1799 let list = &style.get_box().transform;
1800 let length_rect = au_rect_to_length_rect(border_rect);
1801 let rotate = match style.clone_rotate() {
1803 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1804 GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle),
1805 GenericRotate::None => (0., 0., 1., Angle::zero()),
1806 };
1807 let scale = match style.clone_scale() {
1808 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1809 GenericScale::None => (1., 1., 1.),
1810 };
1811 let translation = match style.clone_translate() {
1812 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1813 x.resolve(length_rect.size.width).px(),
1814 y.resolve(length_rect.size.height).px(),
1815 z.px(),
1816 ),
1817 GenericTranslate::None => LayoutTransform::identity(),
1818 };
1819
1820 let angle = euclid::Angle::radians(rotate.3.radians());
1821 let transform_base = list
1822 .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1823 .ok()?;
1824 let transform = LayoutTransform::from_untyped(&transform_base.0)
1825 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1826 .then_scale(scale.0, scale.1, scale.2)
1827 .then(&translation);
1828
1829 let transform_origin = &style.get_box().transform_origin;
1830 let transform_origin_x = transform_origin
1831 .horizontal
1832 .to_used_value(border_rect.size.width)
1833 .to_f32_px();
1834 let transform_origin_y = transform_origin
1835 .vertical
1836 .to_used_value(border_rect.size.height)
1837 .to_f32_px();
1838 let transform_origin_z = transform_origin.depth.px();
1839
1840 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1841 }
1842
1843 pub fn calculate_perspective_matrix(
1845 &self,
1846 border_rect: &Rect<Au, CSSPixel>,
1847 ) -> Option<LayoutTransform> {
1848 let style = self.style();
1849 match style.get_box().perspective {
1850 Perspective::Length(length) => {
1851 let perspective_origin = &style.get_box().perspective_origin;
1852 let perspective_origin = LayoutPoint::new(
1853 perspective_origin
1854 .horizontal
1855 .percentage_relative_to(border_rect.size.width.into())
1856 .px(),
1857 perspective_origin
1858 .vertical
1859 .percentage_relative_to(border_rect.size.height.into())
1860 .px(),
1861 );
1862
1863 let perspective_matrix = LayoutTransform::from_untyped(
1864 &transform::create_perspective_matrix(length.px()),
1865 );
1866
1867 Some(perspective_matrix.change_basis(
1868 perspective_origin.x,
1869 perspective_origin.y,
1870 0.0,
1871 ))
1872 },
1873 Perspective::None => None,
1874 }
1875 }
1876
1877 fn clear_spatial_tree_node_including_descendants(&self) {
1878 fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1879 for fragment in fragments.iter() {
1880 match fragment {
1881 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1882 box_fragment
1883 .borrow()
1884 .clear_spatial_tree_node_including_descendants();
1885 },
1886 Fragment::Positioning(positioning_fragment) => {
1887 assign_spatial_tree_node_on_fragments(
1888 &positioning_fragment.borrow().children,
1889 );
1890 },
1891 _ => {},
1892 }
1893 }
1894 }
1895
1896 *self.spatial_tree_node.borrow_mut() = None;
1897 assign_spatial_tree_node_on_fragments(&self.children);
1898 }
1899}
1900
1901impl PositioningFragment {
1902 fn build_stacking_context_tree(
1903 &self,
1904 stacking_context_tree: &mut StackingContextTree,
1905 containing_block: &ContainingBlock,
1906 containing_block_info: &ContainingBlockInfo,
1907 stacking_context: &mut StackingContext,
1908 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1909 ) {
1910 let rect = self
1911 .base
1912 .rect
1913 .translate(containing_block.rect.origin.to_vector());
1914 let new_containing_block = containing_block.new_replacing_rect(&rect);
1915 let new_containing_block_info =
1916 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1917
1918 for child in &self.children {
1919 child.build_stacking_context_tree(
1920 stacking_context_tree,
1921 &new_containing_block_info,
1922 stacking_context,
1923 StackingContextBuildMode::SkipHoisted,
1924 text_decorations,
1925 );
1926 }
1927 }
1928}
1929
1930pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1931 Rect::new(
1932 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1933 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1934 )
1935}