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