Skip to main content

layout/display_list/
stacking_context.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    /// The SpatialId of the spatial node that contains the children
57    /// of this containing block.
58    scroll_node_id: ScrollTreeNodeId,
59
60    /// The size of the parent scroll frame of this containing block, used for resolving
61    /// sticky margins. If this is None, then this is a direct descendant of a reference
62    /// frame and sticky positioning isn't taken into account.
63    scroll_frame_size: Option<LayoutSize>,
64
65    /// The [`ClipId`] to use for the children of this containing block.
66    clip_id: ClipId,
67
68    /// The physical rect of this containing block.
69    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    /// The root stacking context of this [`StackingContextTree`].
108    pub root_stacking_context: StackingContext,
109
110    /// The information about the WebRender display list that `Paint`
111    /// consumes. This curerntly contains the out-of-band hit testing information
112    /// data structure that `Paint` uses to map hit tests to information
113    /// about the item hit.
114    pub paint_info: PaintDisplayListInfo,
115
116    /// All of the clips collected for this [`StackingContextTree`]. These are added
117    /// for things like `overflow`. More clips may be created later during WebRender
118    /// display list construction, but they are never added here.
119    pub clip_store: StackingContextTreeClipStore,
120}
121
122impl StackingContextTree {
123    /// Create a new [DisplayList] given the dimensions of the layout and the WebRender
124    /// pipeline id.
125    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            // This epoch is set when the WebRender display list is built. For now use a dummy value.
144            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        // We need to specify all three containing blocks here, because absolute
164        // descdendants of the root cannot share the containing block we specify
165        // for fixed descendants. In this case, they need to have the spatial
166        // id of the root scroll frame, whereas fixed descendants need the
167        // spatial id of the root reference frame so that they do not scroll with
168        // page content.
169        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            // This is just a temporary value that will be replaced once we have finished building the tree.
177            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    /// Given a [`Fragment`] and a point in the viewport of the page, return the point in
266    /// the [`Fragment`]'s content rectangle in its transformed coordinate system
267    /// (untransformed CSS pixels). Note that the point may be outside the [`Fragment`]'s
268    /// boundaries.
269    ///
270    /// TODO: Currently, this only works for [`BoxFragment`], but we should extend it to
271    /// other types of [`Fragment`]s in the future.
272    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        // Find the origin of the fragment relative to its reference frame in the same coordinate system.
293        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        // Use that to find the offset from the fragment origin.
302        Some(transformed_point - fragment_origin)
303    }
304}
305
306/// The text decorations for a Fragment, collecting during [`StackingContextTree`] construction.
307#[derive(Clone, Debug, MallocSizeOf)]
308pub(crate) struct FragmentTextDecoration {
309    pub line: TextDecorationLine,
310    pub color: AbsoluteColor,
311    pub style: TextDecorationStyle,
312}
313
314/// A piece of content that directly belongs to a section of a stacking context.
315///
316/// This is generally part of a fragment, like its borders or foreground, but it
317/// can also be a stacking container that needs to be painted in fragment order.
318#[derive(MallocSizeOf)]
319pub(crate) enum StackingContextContent {
320    /// A fragment that does not generate a stacking context or stacking container.
321    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    /// An index into [StackingContext::atomic_inline_stacking_containers].
335    ///
336    /// There is no section field, because these are always in [StackingContextSection::Foreground].
337    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/// Either a stacking context or a stacking container, per the definitions in
417/// <https://drafts.csswg.org/css-position-4/#painting-order>.
418///
419/// We use the term “real stacking context” in situations that call for a
420/// stacking context but not a stacking container.
421#[derive(MallocSizeOf)]
422pub struct StackingContext {
423    /// The spatial id of this fragment. This is used to properly handle
424    /// things like preserve-3d.
425    scroll_tree_node_id: ScrollTreeNodeId,
426
427    /// The clip chain id of this stacking context if it has one. Used for filter clipping.
428    clip_id: Option<ClipId>,
429
430    /// The [`BoxFragment`] that established this stacking context. We store the fragment here
431    /// rather than just the style, so that incremental layout can automatically update the style.
432    initializing_fragment: Option<ArcRefCell<BoxFragment>>,
433
434    /// The type of this stacking context. Used for collecting and sorting.
435    context_type: StackingContextType,
436
437    /// The contents that need to be painted in fragment order.
438    pub(super) contents: Vec<StackingContextContent>,
439
440    /// Stacking contexts that need to be stolen by the parent stacking context
441    /// if this is a stacking container, that is, real stacking contexts and
442    /// positioned stacking containers (where ‘z-index’ is auto).
443    /// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
444    /// > To paint a stacking container, given a box root and a canvas canvas:
445    /// >  1. Paint a stacking context given root and canvas, treating root as
446    /// >     if it created a new stacking context, but omitting any positioned
447    /// >     descendants or descendants that actually create a stacking context
448    /// >     (letting the parent stacking context paint them, instead).
449    pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
450
451    /// Float stacking containers.
452    /// Separate from real_stacking_contexts_or_positioned_stacking_containers
453    /// because they should never be stolen by the parent stacking context.
454    /// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
455    pub(super) float_stacking_containers: Vec<StackingContext>,
456
457    /// Atomic inline stacking containers.
458    /// Separate from real_stacking_contexts_or_positioned_stacking_containers
459    /// because they should never be stolen by the parent stacking context, and
460    /// separate from float_stacking_containers so that [StackingContextContent]
461    /// can index into this vec to paint them in fragment order.
462    /// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
463    /// <https://drafts.csswg.org/css-position-4/#paint-a-box-in-a-line-box>
464    pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
465
466    /// Information gathered about the painting order, for [Self::debug_print].
467    debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
468}
469
470/// Refers to one of the child contents or stacking contexts of a [StackingContext].
471#[derive(Clone, Copy, MallocSizeOf)]
472pub struct DebugPrintItem {
473    field: DebugPrintField,
474    index: usize,
475}
476
477/// Refers to one of the vecs of a [StackingContext].
478#[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        // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be
494        // used for primitives, but `None` is used for stacking contexts and clip chains. We convert
495        // to the `Option<ClipId>` representation here. Just passing Some(ClipChainId::INVALID)
496        // leads to a crash.
497        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    /// Add a child stacking context to this stacking context.
531    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        // WebRender only uses the stacking context to apply certain effects. If we don't
596        // actually need to create a stacking context, just avoid creating one.
597        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        // Create the filter pipeline.
611        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, &current_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        // TODO(jdm): WebRender now requires us to create stacking context items
626        //            with the IS_BLEND_CONTAINER flag enabled if any children
627        //            of the stacking context have a blend mode applied.
628        //            This will require additional tracking during layout
629        //            before we start collecting stacking contexts so that
630        //            information will be available when we reach this point.
631        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(), // origin
635            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            &[], // filter_datas
642            &[], // filter_primitives
643            wr::RasterSpace::Screen,
644            wr::StackingContextFlags::empty(),
645            None, // snapshot
646        );
647
648        true
649    }
650
651    /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
652    ///
653    /// This is only called for the root `StackingContext`
654    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            // > For documents whose root element is an HTML HTML element or an XHTML html element
674            // > [HTML]: if the computed value of background-image on the root element is none and its
675            // > background-color is transparent, user agents must instead propagate the computed
676            // > values of the background properties from that element’s first HTML BODY or XHTML body
677            // > child element.
678            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        // This can happen if the root fragment does not have a `<body>` child (either because it is
691        // `display: none` or `display: contents`) or if the `<body>`'s background is transparent.
692        if source_style.background_is_transparent() {
693            return;
694        }
695
696        // The painting area is theoretically the infinite 2D plane,
697        // but we need a rectangle with finite coordinates.
698        //
699        // If the document is smaller than the viewport (and doesn’t scroll),
700        // we still want to paint the rest of the viewport.
701        // If it’s larger, we also want to paint areas reachable after scrolling.
702        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            // From <https://www.w3.org/TR/paint-timing/#sec-terminology>:
715            // First paint ... includes non-default background paint and the enclosing box of an iframe.
716            // The spec is vague. See also: https://github.com/w3c/paint-timing/issues/122
717            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, /* is_hit_test_for_scrollable_overflow */
735            false, /* is_collapsed_table_borders */
736        );
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    /// Build a display list from a a [`StackingContext`]. Note that this is the forward
746    /// version of the reversed stacking context walk algorithm in `hit_test.rs`. Any
747    /// changes made here should be reflected in the reverse version in that file.
748    pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
749        let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
750
751        // Properly order display items that make up a stacking context.
752        // “Steps” here refer to the steps in CSS 2.1 Appendix E.
753        // Note that “positioned descendants” is generalised to include all descendants that
754        // generate stacking contexts (csswg-drafts#2717), except in the phrase “any positioned
755        // descendants or descendants that actually create a stacking context”, where the term
756        // means positioned descendants that do not generate stacking contexts.
757
758        // Steps 1 and 2: Borders and background for the root
759        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        // Step 3: Stacking contexts with negative ‘z-index’
774        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        // Step 4: Block backgrounds and borders
794        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        // Step 5: Float stacking containers
807        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        // Steps 6 and 7: Fragments and inline stacking containers
813        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        // Steps 8 and 9: Stacking contexts with non-negative ‘z-index’, and
827        // positioned stacking containers (where ‘z-index’ is auto)
828        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        // Step 10: Outline
837        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    /// Store the fact that something was painted, if [Self::debug_print_items] is not None.
851    ///
852    /// This is used to help reconstruct the original painting order in [Self::debug_print] without
853    /// duplicating our painting order logic, since that could fall out of sync with the real logic.
854    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    /// Print the stacking context tree.
861    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    /// Print a subtree with the given [PrintTree], or panic if [Self::debug_print_items] is None.
871    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                // do nothing; we print the heading with its index in DebugPrintField::Contents
878            },
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                // do nothing; we print the heading with its index in DebugPrintField::Contents
909            },
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        // Flex and grid items are painted like inline blocks.
1043        // <https://drafts.csswg.org/css-flexbox-1/#painting>
1044        // <https://drafts.csswg.org/css-grid/#z-order>
1045        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        // <https://drafts.csswg.org/css-transforms/#transform-function-lists>
1108        // > If a transform function causes the current transformation matrix of an object
1109        // > to be non-invertible, the object and its content do not get displayed.
1110        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        // WebRender reference frames establish a new coordinate system at their
1127        // origin (the border box of the fragment). We need to ensure that any
1128        // coordinates we give to WebRender in this reference frame are relative
1129        // to the fragment border box. We do this by adjusting the containing
1130        // block origin. Note that the `for_absolute_descendants` and
1131        // `for_all_absolute_and_fixed_descendants` properties are now bogus,
1132        // but all fragments that establish reference frames also establish
1133        // containing blocks for absolute and fixed descendants, so those
1134        // properties will be replaced before recursing into children.
1135        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            // Push a dummy fragment that indicates when the new stacking context should be painted.
1183            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        // `clip-path` needs to be applied before filters and creates a stacking context, so it can be
1193        // applied directly to the stacking context itself.
1194        // before
1195        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, /* is_hit_test_for_scrollable_overflow */
1205                    false, /* is_collapsed_table_borders */
1206                ),
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, /* is_hit_test_for_scrollable_overflow */
1290                false, /* is_collapsed_table_borders */
1291            ),
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        // Spatial tree node that will affect the transform of the fragment. Note that the next frame,
1330        // scroll frame, does not affect the transform of the fragment but affect the transform of it
1331        // children.
1332        *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1333
1334        // We want to build the scroll frame after the background and border, because
1335        // they shouldn't scroll with the rest of the box content.
1336        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        // Create a new `ContainingBlockInfo` for descendants depending on
1384        // whether or not this fragment establishes a containing block for
1385        // absolute and fixed descendants.
1386        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        // Text decorations are not propagated to atomic inline-level descendants.
1401        // From https://drafts.csswg.org/css2/#lining-striking-props:
1402        // > Note that text decorations are not propagated to floating and absolutely
1403        // > positioned descendants, nor to the contents of atomic inline-level descendants
1404        // > such as inline blocks and inline tables.
1405        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        // https://drafts.csswg.org/css2/#clipping
1472        // The clip property applies only to absolutely positioned elements
1473        if !position.is_absolutely_positioned() {
1474            return None;
1475        }
1476
1477        // Only rectangles are supported for now.
1478        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        // Non-scrollable overflow path
1511        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            // Adjust by the overflow clip margin.
1522            // https://drafts.csswg.org/css-overflow-3/#overflow-clip-margin
1523            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            // The clipping region only gets rounded corners if both axes have `overflow: clip`.
1527            // https://drafts.csswg.org/css-overflow-3/#corner-clipping
1528            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                // This is a direct descendant of a reference frame.
1623                &stacking_context_tree
1624                    .paint_info
1625                    .viewport_details
1626                    .layout_size()
1627            },
1628        };
1629
1630        // Percentages sticky positions offsets are resovled against the size of the
1631        // nearest scroll frame instead of the containing block like for other types
1632        // of positioning.
1633        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        // https://drafts.csswg.org/css-position/#stickypos-insets
1657        // > For each side of the box, if the corresponding inset property is not `auto`, and the
1658        // > corresponding border edge of the box would be outside the corresponding edge of the
1659        // > sticky view rectangle, the box must be visually shifted (as for relative positioning)
1660        // > to be inward of that sticky view rectangle edge, insofar as it can while its position
1661        // > box remains contained within its containing block.
1662        // > The *position box* is its margin box, except that for any side for which the distance
1663        // > between its margin edge and the corresponding edge of its containing block is less
1664        // > than its corresponding margin, that distance is used in place of that margin.
1665        //
1666        // Amendments:
1667        // - Using the "margin edge" seems nonsensical, the spec must mean "border edge" instead:
1668        //   https://github.com/w3c/csswg-drafts/issues/12833
1669        // - `auto` margins need to be treated as zero:
1670        //   https://github.com/w3c/csswg-drafts/issues/12852
1671        //
1672        // We implement this by enforcing a minimum negative offset and a maximum positive offset.
1673        // The logic below is a simplified (but equivalent) version of the description above.
1674        let border_rect = self.border_rect();
1675        let computed_margin = style.physical_margin();
1676
1677        // Signed distance between each side of the border box to the corresponding side of the
1678        // containing block. Note that |border_rect| is already in the coordinate system of the
1679        // containing block.
1680        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        // Shrinks the signed distance by the margin, producing a limit on how much we can shift
1688        // the sticky positioned box without forcing the margin to move outside of the containing
1689        // block.
1690        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        // This is the minimum negative offset and then the maximum positive offset. We specify
1700        // all sides, but they will have no effect if the corresponding inset property is `auto`.
1701        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        // These are the "margins" between the scrollport and |frame_rect|. They are not the same
1731        // as CSS margins.
1732        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    /// Optionally returns the data for building a reference frame, without yet building it.
1751    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    /// Returns the 4D matrix representing this fragment's transform.
1798    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        // https://drafts.csswg.org/css-transforms-2/#individual-transforms
1806        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    /// Returns the 4D matrix representing this fragment's perspective.
1848    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}