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::display_list::conversions::{FilterToWebRender, ToWebRender};
45use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii};
46use crate::fragment_tree::{
47    BoxFragment, ContainingBlockCalculation, ContainingBlockManager, Fragment, FragmentFlags,
48    FragmentTree, PositioningFragment, SpecificLayoutInfo,
49};
50use crate::geom::{
51    AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalVec,
52};
53use crate::style_ext::{ComputedValuesExt, TransformExt};
54
55#[derive(Clone)]
56pub(crate) struct ContainingBlock {
57    /// The SpatialId of the spatial node that contains the children
58    /// of this containing block.
59    scroll_node_id: ScrollTreeNodeId,
60
61    /// The size of the parent scroll frame of this containing block, used for resolving
62    /// sticky margins. If this is None, then this is a direct descendant of a reference
63    /// frame and sticky positioning isn't taken into account.
64    scroll_frame_size: Option<LayoutSize>,
65
66    /// The [`ClipId`] to use for the children of this containing block.
67    clip_id: ClipId,
68
69    /// The physical rect of this containing block.
70    rect: PhysicalRect<Au>,
71
72    /// Normally containing block offsets and display list items are positioned relative
73    /// to their parent reference frame, but cumulative containing block boundaries on
74    /// fragments need to disregard reference frames entirely. This value tracks the
75    /// accumulated offset from the origin of the parent reference frame of this
76    /// containing block.
77    accumulated_reference_frame_offset: PhysicalVec<Au>,
78}
79
80impl ContainingBlock {
81    pub(crate) fn new(
82        rect: PhysicalRect<Au>,
83        scroll_node_id: ScrollTreeNodeId,
84        scroll_frame_size: Option<LayoutSize>,
85        clip_id: ClipId,
86        accumulated_reference_frame_offset: PhysicalVec<Au>,
87    ) -> Self {
88        ContainingBlock {
89            scroll_node_id,
90            scroll_frame_size,
91            clip_id,
92            rect,
93            accumulated_reference_frame_offset,
94        }
95    }
96
97    pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Au>) -> Self {
98        ContainingBlock {
99            rect: *rect,
100            ..*self
101        }
102    }
103}
104
105pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
106
107#[derive(Clone, Copy, Debug, Eq, Ord, MallocSizeOf, PartialEq, PartialOrd)]
108pub(crate) enum StackingContextSection {
109    OwnBackgroundsAndBorders,
110    DescendantBackgroundsAndBorders,
111    Foreground,
112    Outline,
113}
114
115#[derive(MallocSizeOf)]
116pub(crate) struct StackingContextTree {
117    /// The root stacking context of this [`StackingContextTree`].
118    pub root_stacking_context: StackingContext,
119
120    /// The information about the WebRender display list that `Paint`
121    /// consumes. This curerntly contains the out-of-band hit testing information
122    /// data structure that `Paint` uses to map hit tests to information
123    /// about the item hit.
124    pub paint_info: PaintDisplayListInfo,
125
126    /// All of the clips collected for this [`StackingContextTree`]. These are added
127    /// for things like `overflow`. More clips may be created later during WebRender
128    /// display list construction, but they are never added here.
129    pub clip_store: StackingContextTreeClipStore,
130}
131
132impl StackingContextTree {
133    /// Create a new [DisplayList] given the dimensions of the layout and the WebRender
134    /// pipeline id.
135    pub fn new(
136        fragment_tree: &FragmentTree,
137        viewport_details: ViewportDetails,
138        pipeline_id: wr::PipelineId,
139        first_reflow: bool,
140        debug: &DiagnosticsLogging,
141    ) -> Self {
142        let scrollable_overflow = fragment_tree.scrollable_overflow();
143        let scrollable_overflow = LayoutSize::from_untyped(Size2D::new(
144            scrollable_overflow.size.width.to_f32_px(),
145            scrollable_overflow.size.height.to_f32_px(),
146        ));
147
148        let viewport_size = viewport_details.layout_size();
149        let paint_info = PaintDisplayListInfo::new(
150            viewport_details,
151            scrollable_overflow,
152            pipeline_id,
153            // This epoch is set when the WebRender display list is built. For now use a dummy value.
154            Default::default(),
155            fragment_tree.viewport_scroll_sensitivity,
156            first_reflow,
157        );
158
159        let root_scroll_node_id = paint_info.root_scroll_node_id;
160        let cb_for_non_fixed_descendants = ContainingBlock::new(
161            fragment_tree.initial_containing_block,
162            root_scroll_node_id,
163            Some(viewport_size),
164            ClipId::INVALID,
165            PhysicalVec::zero(),
166        );
167        let cb_for_fixed_descendants = ContainingBlock::new(
168            fragment_tree.initial_containing_block,
169            paint_info.root_reference_frame_id,
170            None,
171            ClipId::INVALID,
172            PhysicalVec::zero(),
173        );
174
175        // We need to specify all three containing blocks here, because absolute
176        // descdendants of the root cannot share the containing block we specify
177        // for fixed descendants. In this case, they need to have the spatial
178        // id of the root scroll frame, whereas fixed descendants need the
179        // spatial id of the root reference frame so that they do not scroll with
180        // page content.
181        let containing_block_info = ContainingBlockInfo {
182            for_non_absolute_descendants: &cb_for_non_fixed_descendants,
183            for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
184            for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
185        };
186
187        let mut stacking_context_tree = Self {
188            // This is just a temporary value that will be replaced once we have finished building the tree.
189            root_stacking_context: StackingContext::create_root(root_scroll_node_id, debug),
190            paint_info,
191            clip_store: Default::default(),
192        };
193
194        let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug);
195        let text_decorations = Default::default();
196        for fragment in &fragment_tree.root_fragments {
197            fragment.build_stacking_context_tree(
198                &mut stacking_context_tree,
199                &containing_block_info,
200                &mut root_stacking_context,
201                StackingContextBuildMode::SkipHoisted,
202                &text_decorations,
203            );
204        }
205        root_stacking_context.sort();
206
207        if debug.is_enabled(DiagnosticsLoggingOption::StackingContextTree) {
208            root_stacking_context.debug_print();
209        }
210
211        stacking_context_tree.root_stacking_context = root_stacking_context;
212
213        stacking_context_tree
214    }
215
216    fn push_reference_frame(
217        &mut self,
218        origin: LayoutPoint,
219        frame_origin_for_query: LayoutPoint,
220        parent_scroll_node_id: ScrollTreeNodeId,
221        transform_style: wr::TransformStyle,
222        transform: LayoutTransform,
223        kind: wr::ReferenceFrameKind,
224    ) -> ScrollTreeNodeId {
225        self.paint_info.scroll_tree.add_scroll_tree_node(
226            Some(parent_scroll_node_id),
227            SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
228                origin,
229                frame_origin_for_query,
230                transform_style,
231                transform: transform.into(),
232                kind,
233            }),
234        )
235    }
236
237    fn define_scroll_frame(
238        &mut self,
239        parent_scroll_node_id: ScrollTreeNodeId,
240        external_id: wr::ExternalScrollId,
241        content_rect: LayoutRect,
242        clip_rect: LayoutRect,
243        scroll_sensitivity: AxesScrollSensitivity,
244    ) -> ScrollTreeNodeId {
245        self.paint_info.scroll_tree.add_scroll_tree_node(
246            Some(parent_scroll_node_id),
247            SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
248                external_id,
249                content_rect,
250                clip_rect,
251                scroll_sensitivity,
252                offset: LayoutVector2D::zero(),
253                offset_changed: Cell::new(false),
254            }),
255        )
256    }
257
258    fn define_sticky_frame(
259        &mut self,
260        parent_scroll_node_id: ScrollTreeNodeId,
261        frame_rect: LayoutRect,
262        margins: SideOffsets2D<Option<f32>, LayoutPixel>,
263        vertical_offset_bounds: StickyOffsetBounds,
264        horizontal_offset_bounds: StickyOffsetBounds,
265    ) -> ScrollTreeNodeId {
266        self.paint_info.scroll_tree.add_scroll_tree_node(
267            Some(parent_scroll_node_id),
268            SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
269                frame_rect,
270                margins,
271                vertical_offset_bounds,
272                horizontal_offset_bounds,
273            }),
274        )
275    }
276
277    /// Given a [`Fragment`] and a point in the viewport of the page, return the point in
278    /// the [`Fragment`]'s content rectangle in its transformed coordinate system
279    /// (untransformed CSS pixels). Note that the point may be outside the [`Fragment`]'s
280    /// boundaries.
281    ///
282    /// TODO: Currently, this only works for [`BoxFragment`], but we should extend it to
283    /// other types of [`Fragment`]s in the future.
284    pub(crate) fn offset_in_fragment(
285        &self,
286        fragment: &Fragment,
287        point_in_viewport: PhysicalPoint<Au>,
288    ) -> Option<Point2D<Au, CSSPixel>> {
289        let Fragment::Box(fragment) = fragment else {
290            return None;
291        };
292
293        let spatial_tree_node = fragment.spatial_tree_node()?;
294        let transform = self
295            .paint_info
296            .scroll_tree
297            .cumulative_root_to_node_transform(spatial_tree_node)?;
298        let transformed_point = transform
299            .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
300            .map(Au::from_f32_px)
301            .cast_unit();
302
303        // Find the origin of the fragment relative to its reference frame in the same coordinate system.
304        let reference_frame_origin = self
305            .paint_info
306            .scroll_tree
307            .reference_frame_offset(spatial_tree_node)
308            .map(Au::from_f32_px);
309        let fragment_origin = fragment
310            .cumulative_content_box_rect(
311                ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
312            )
313            .origin -
314            reference_frame_origin.cast_unit();
315
316        // Use that to find the offset from the fragment origin.
317        Some(transformed_point - fragment_origin)
318    }
319}
320
321/// The text decorations for a Fragment, collecting during [`StackingContextTree`] construction.
322#[derive(Clone, Debug, MallocSizeOf)]
323pub(crate) struct FragmentTextDecoration {
324    pub line: TextDecorationLine,
325    pub color: AbsoluteColor,
326    pub style: TextDecorationStyle,
327}
328
329/// A piece of content that directly belongs to a section of a stacking context.
330///
331/// This is generally part of a fragment, like its borders or foreground, but it
332/// can also be a stacking container that needs to be painted in fragment order.
333#[derive(MallocSizeOf)]
334pub(crate) enum StackingContextContent {
335    /// A fragment that does not generate a stacking context or stacking container.
336    Fragment {
337        scroll_node_id: ScrollTreeNodeId,
338        reference_frame_scroll_node_id: ScrollTreeNodeId,
339        clip_id: ClipId,
340        section: StackingContextSection,
341        containing_block: PhysicalRect<Au>,
342        fragment: Fragment,
343        is_hit_test_for_scrollable_overflow: bool,
344        is_collapsed_table_borders: bool,
345        #[conditional_malloc_size_of]
346        text_decorations: Arc<Vec<FragmentTextDecoration>>,
347    },
348
349    /// An index into [StackingContext::atomic_inline_stacking_containers].
350    ///
351    /// There is no section field, because these are always in [StackingContextSection::Foreground].
352    AtomicInlineStackingContainer { index: usize },
353}
354
355impl StackingContextContent {
356    pub(crate) fn section(&self) -> StackingContextSection {
357        match self {
358            Self::Fragment { section, .. } => *section,
359            Self::AtomicInlineStackingContainer { .. } => StackingContextSection::Foreground,
360        }
361    }
362
363    fn build_display_list_with_section_override(
364        &self,
365        builder: &mut DisplayListBuilder,
366        inline_stacking_containers: &[StackingContext],
367        section_override: Option<StackingContextSection>,
368    ) {
369        match self {
370            Self::Fragment {
371                scroll_node_id,
372                reference_frame_scroll_node_id,
373                clip_id,
374                section,
375                containing_block,
376                fragment,
377                is_hit_test_for_scrollable_overflow,
378                is_collapsed_table_borders,
379                text_decorations,
380            } => {
381                builder.current_scroll_node_id = *scroll_node_id;
382                builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
383                builder.current_clip_id = *clip_id;
384                fragment.build_display_list(
385                    builder,
386                    containing_block,
387                    section_override.unwrap_or(*section),
388                    *is_hit_test_for_scrollable_overflow,
389                    *is_collapsed_table_borders,
390                    text_decorations,
391                );
392            },
393            Self::AtomicInlineStackingContainer { index } => {
394                inline_stacking_containers[*index].build_display_list(builder);
395            },
396        }
397    }
398
399    fn build_display_list(
400        &self,
401        builder: &mut DisplayListBuilder,
402        inline_stacking_containers: &[StackingContext],
403    ) {
404        self.build_display_list_with_section_override(builder, inline_stacking_containers, None);
405    }
406
407    fn has_outline(&self) -> bool {
408        match self {
409            StackingContextContent::Fragment { fragment, .. } => match fragment {
410                Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
411                    let style = box_fragment.style();
412                    let outline = style.get_outline();
413                    !outline.outline_style.none_or_hidden() && !outline.outline_width.0.is_zero()
414                },
415                _ => false,
416            },
417            StackingContextContent::AtomicInlineStackingContainer { .. } => false,
418        }
419    }
420}
421
422#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
423pub(crate) enum StackingContextType {
424    RealStackingContext,
425    PositionedStackingContainer,
426    FloatStackingContainer,
427    AtomicInlineStackingContainer,
428}
429
430/// Either a stacking context or a stacking container, per the definitions in
431/// <https://drafts.csswg.org/css-position-4/#painting-order>.
432///
433/// We use the term “real stacking context” in situations that call for a
434/// stacking context but not a stacking container.
435#[derive(MallocSizeOf)]
436pub struct StackingContext {
437    /// The spatial id of this fragment. This is used to properly handle
438    /// things like preserve-3d.
439    scroll_tree_node_id: ScrollTreeNodeId,
440
441    /// The clip chain id of this stacking context if it has one. Used for filter clipping.
442    clip_id: Option<ClipId>,
443
444    /// The [`BoxFragment`] that established this stacking context. We store the fragment here
445    /// rather than just the style, so that incremental layout can automatically update the style.
446    #[conditional_malloc_size_of]
447    initializing_fragment: Option<Arc<BoxFragment>>,
448
449    /// The type of this stacking context. Used for collecting and sorting.
450    context_type: StackingContextType,
451
452    /// The contents that need to be painted in fragment order.
453    pub(super) contents: Vec<StackingContextContent>,
454
455    /// Stacking contexts that need to be stolen by the parent stacking context
456    /// if this is a stacking container, that is, real stacking contexts and
457    /// positioned stacking containers (where ‘z-index’ is auto).
458    /// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
459    /// > To paint a stacking container, given a box root and a canvas canvas:
460    /// >  1. Paint a stacking context given root and canvas, treating root as
461    /// >     if it created a new stacking context, but omitting any positioned
462    /// >     descendants or descendants that actually create a stacking context
463    /// >     (letting the parent stacking context paint them, instead).
464    pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
465
466    /// Float stacking containers.
467    /// Separate from real_stacking_contexts_or_positioned_stacking_containers
468    /// because they should never be stolen by the parent stacking context.
469    /// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
470    pub(super) float_stacking_containers: Vec<StackingContext>,
471
472    /// Atomic inline stacking containers.
473    /// Separate from real_stacking_contexts_or_positioned_stacking_containers
474    /// because they should never be stolen by the parent stacking context, and
475    /// separate from float_stacking_containers so that [StackingContextContent]
476    /// can index into this vec to paint them in fragment order.
477    /// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
478    /// <https://drafts.csswg.org/css-position-4/#paint-a-box-in-a-line-box>
479    pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
480
481    /// Information gathered about the painting order, for [Self::debug_print].
482    debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
483}
484
485/// Refers to one of the child contents or stacking contexts of a [StackingContext].
486#[derive(Clone, Copy, MallocSizeOf)]
487pub struct DebugPrintItem {
488    field: DebugPrintField,
489    index: usize,
490}
491
492/// Refers to one of the vecs of a [StackingContext].
493#[derive(Clone, Copy, MallocSizeOf)]
494pub enum DebugPrintField {
495    Contents,
496    RealStackingContextsAndPositionedStackingContainers,
497    FloatStackingContainers,
498}
499
500impl StackingContext {
501    fn create_descendant(
502        &self,
503        spatial_id: ScrollTreeNodeId,
504        clip_id: ClipId,
505        initializing_fragment: Arc<BoxFragment>,
506        context_type: StackingContextType,
507    ) -> Self {
508        // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be
509        // used for primitives, but `None` is used for stacking contexts and clip chains. We convert
510        // to the `Option<ClipId>` representation here. Just passing Some(ClipChainId::INVALID)
511        // leads to a crash.
512        let clip_id = match clip_id {
513            ClipId::INVALID => None,
514            clip_id => Some(clip_id),
515        };
516        Self {
517            scroll_tree_node_id: spatial_id,
518            clip_id,
519            initializing_fragment: Some(initializing_fragment),
520            context_type,
521            contents: vec![],
522            real_stacking_contexts_and_positioned_stacking_containers: vec![],
523            float_stacking_containers: vec![],
524            atomic_inline_stacking_containers: vec![],
525            debug_print_items: self.debug_print_items.is_some().then(|| vec![].into()),
526        }
527    }
528
529    fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DiagnosticsLogging) -> Self {
530        Self {
531            scroll_tree_node_id: root_scroll_node_id,
532            clip_id: None,
533            initializing_fragment: None,
534            context_type: StackingContextType::RealStackingContext,
535            contents: vec![],
536            real_stacking_contexts_and_positioned_stacking_containers: vec![],
537            float_stacking_containers: vec![],
538            atomic_inline_stacking_containers: vec![],
539            debug_print_items: debug
540                .is_enabled(DiagnosticsLoggingOption::StackingContextTree)
541                .then(|| vec![].into()),
542        }
543    }
544
545    /// Add a child stacking context to this stacking context.
546    fn add_stacking_context(&mut self, stacking_context: StackingContext) {
547        match stacking_context.context_type {
548            StackingContextType::RealStackingContext => {
549                &mut self.real_stacking_contexts_and_positioned_stacking_containers
550            },
551            StackingContextType::PositionedStackingContainer => {
552                &mut self.real_stacking_contexts_and_positioned_stacking_containers
553            },
554            StackingContextType::FloatStackingContainer => &mut self.float_stacking_containers,
555            StackingContextType::AtomicInlineStackingContainer => {
556                &mut self.atomic_inline_stacking_containers
557            },
558        }
559        .push(stacking_context)
560    }
561
562    pub(crate) fn z_index(&self) -> i32 {
563        self.initializing_fragment.as_ref().map_or(0, |fragment| {
564            fragment.style().effective_z_index(fragment.base.flags)
565        })
566    }
567
568    pub(crate) fn sort(&mut self) {
569        self.contents.sort_by_key(|a| a.section());
570        self.real_stacking_contexts_and_positioned_stacking_containers
571            .sort_by_key(|a| a.z_index());
572
573        debug_assert!(
574            self.real_stacking_contexts_and_positioned_stacking_containers
575                .iter()
576                .all(|c| matches!(
577                    c.context_type,
578                    StackingContextType::RealStackingContext |
579                        StackingContextType::PositionedStackingContainer
580                ))
581        );
582        debug_assert!(
583            self.float_stacking_containers
584                .iter()
585                .all(
586                    |c| c.context_type == StackingContextType::FloatStackingContainer &&
587                        c.z_index() == 0
588                )
589        );
590        debug_assert!(
591            self.atomic_inline_stacking_containers
592                .iter()
593                .all(
594                    |c| c.context_type == StackingContextType::AtomicInlineStackingContainer &&
595                        c.z_index() == 0
596                )
597        );
598    }
599
600    fn push_webrender_stacking_context_if_necessary(
601        &self,
602        builder: &mut DisplayListBuilder,
603    ) -> bool {
604        let Some(fragment) = self.initializing_fragment.as_ref() else {
605            return false;
606        };
607
608        // WebRender only uses the stacking context to apply certain effects. If we don't
609        // actually need to create a stacking context, just avoid creating one.
610        let style = fragment.style();
611        let effects = style.get_effects();
612        let transform_style = style.get_used_transform_style();
613        if effects.filter.0.is_empty() &&
614            effects.opacity == 1.0 &&
615            effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
616            !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
617            style.get_svg().clip_path == ClipPath::None &&
618            transform_style == TransformStyle::Flat
619        {
620            return false;
621        }
622
623        // Create the filter pipeline.
624        let current_color = style.clone_color();
625        let mut filters: Vec<wr::FilterOp> = effects
626            .filter
627            .0
628            .iter()
629            .map(|filter| FilterToWebRender::to_webrender(filter, &current_color))
630            .collect();
631        if effects.opacity != 1.0 {
632            filters.push(wr::FilterOp::Opacity(
633                effects.opacity.into(),
634                effects.opacity,
635            ));
636        }
637
638        // TODO(jdm): WebRender now requires us to create stacking context items
639        //            with the IS_BLEND_CONTAINER flag enabled if any children
640        //            of the stacking context have a blend mode applied.
641        //            This will require additional tracking during layout
642        //            before we start collecting stacking contexts so that
643        //            information will be available when we reach this point.
644        let spatial_id = builder.spatial_id(self.scroll_tree_node_id);
645        let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id));
646        builder.wr().push_stacking_context(
647            LayoutPoint::zero(), // origin
648            spatial_id,
649            style.get_webrender_primitive_flags(),
650            clip_chain_id,
651            transform_style.to_webrender(),
652            effects.mix_blend_mode.to_webrender(),
653            &filters,
654            &[], // filter_datas
655            &[], // filter_primitives
656            wr::RasterSpace::Screen,
657            wr::StackingContextFlags::empty(),
658            None, // snapshot
659        );
660
661        true
662    }
663
664    /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
665    ///
666    /// This is only called for the root `StackingContext`
667    pub(crate) fn build_canvas_background_display_list(
668        &self,
669        builder: &mut DisplayListBuilder,
670        fragment_tree: &crate::fragment_tree::FragmentTree,
671    ) {
672        let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
673            fragment
674                .base()
675                .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
676        }) else {
677            return;
678        };
679        let root_fragment = match root_fragment {
680            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
681            _ => return,
682        };
683
684        let source_style = {
685            // > For documents whose root element is an HTML HTML element or an XHTML html element
686            // > [HTML]: if the computed value of background-image on the root element is none and its
687            // > background-color is transparent, user agents must instead propagate the computed
688            // > values of the background properties from that element’s first HTML BODY or XHTML body
689            // > child element.
690            let root_fragment_style = root_fragment.style();
691            if root_fragment_style.background_is_transparent() {
692                let body_fragment = fragment_tree.body_fragment();
693                builder.paint_body_background = body_fragment.is_none();
694                body_fragment
695                    .map(|body_fragment| body_fragment.style().clone())
696                    .unwrap_or(root_fragment.style().clone())
697            } else {
698                root_fragment_style.clone()
699            }
700        };
701
702        // This can happen if the root fragment does not have a `<body>` child (either because it is
703        // `display: none` or `display: contents`) or if the `<body>`'s background is transparent.
704        if source_style.background_is_transparent() {
705            return;
706        }
707
708        // The painting area is theoretically the infinite 2D plane,
709        // but we need a rectangle with finite coordinates.
710        //
711        // If the document is smaller than the viewport (and doesn’t scroll),
712        // we still want to paint the rest of the viewport.
713        // If it’s larger, we also want to paint areas reachable after scrolling.
714        let painting_area = fragment_tree
715            .initial_containing_block
716            .union(&fragment_tree.scrollable_overflow())
717            .to_webrender();
718
719        let background_color =
720            source_style.resolve_color(&source_style.get_background().background_color);
721        if background_color.alpha > 0.0 {
722            let common = builder.common_properties(painting_area, &source_style);
723            let color = super::rgba(background_color);
724            builder.wr().push_rect(&common, painting_area, color);
725
726            // From <https://www.w3.org/TR/paint-timing/#sec-terminology>:
727            // First paint ... includes non-default background paint and the enclosing box of an iframe.
728            // The spec is vague. See also: https://github.com/w3c/paint-timing/issues/122
729            let default_background_color = servo_config::pref!(shell_background_color_rgba);
730            let default_background_color = AbsoluteColor::new(
731                ColorSpace::Srgb,
732                default_background_color[0] as f32,
733                default_background_color[1] as f32,
734                default_background_color[2] as f32,
735                default_background_color[3] as f32,
736            )
737            .into_srgb_legacy();
738            if background_color != default_background_color {
739                builder.mark_is_paintable();
740            }
741        }
742
743        let mut fragment_builder = BuilderForBoxFragment::new(
744            root_fragment,
745            &fragment_tree.initial_containing_block,
746            false, /* is_hit_test_for_scrollable_overflow */
747            false, /* is_collapsed_table_borders */
748        );
749        let painter = super::background::BackgroundPainter {
750            style: &source_style,
751            painting_area_override: Some(painting_area),
752            positioning_area_override: None,
753        };
754        fragment_builder.build_background_image(builder, &painter);
755    }
756
757    /// Build a display list from a a [`StackingContext`]. Note that this is the forward
758    /// version of the reversed stacking context walk algorithm in `hit_test.rs`. Any
759    /// changes made here should be reflected in the reverse version in that file.
760    pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
761        let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
762
763        // Properly order display items that make up a stacking context.
764        // “Steps” here refer to the steps in CSS 2.1 Appendix E.
765        // Note that “positioned descendants” is generalised to include all descendants that
766        // generate stacking contexts (csswg-drafts#2717), except in the phrase “any positioned
767        // descendants or descendants that actually create a stacking context”, where the term
768        // means positioned descendants that do not generate stacking contexts.
769
770        // Steps 1 and 2: Borders and background for the root
771        let mut content_with_outlines = Vec::new();
772        let mut contents = self.contents.iter().enumerate().peekable();
773        while contents.peek().is_some_and(|(_, child)| {
774            child.section() == StackingContextSection::OwnBackgroundsAndBorders
775        }) {
776            let (i, child) = contents.next().unwrap();
777            self.debug_push_print_item(DebugPrintField::Contents, i);
778            child.build_display_list(builder, &self.atomic_inline_stacking_containers);
779
780            if child.has_outline() {
781                content_with_outlines.push(child);
782            }
783        }
784
785        // Step 3: Stacking contexts with negative ‘z-index’
786        let mut real_stacking_contexts_and_positioned_stacking_containers = self
787            .real_stacking_contexts_and_positioned_stacking_containers
788            .iter()
789            .enumerate()
790            .peekable();
791        while real_stacking_contexts_and_positioned_stacking_containers
792            .peek()
793            .is_some_and(|(_, child)| child.z_index() < 0)
794        {
795            let (i, child) = real_stacking_contexts_and_positioned_stacking_containers
796                .next()
797                .unwrap();
798            self.debug_push_print_item(
799                DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
800                i,
801            );
802            child.build_display_list(builder);
803        }
804
805        // Step 4: Block backgrounds and borders
806        while contents.peek().is_some_and(|(_, child)| {
807            child.section() == StackingContextSection::DescendantBackgroundsAndBorders
808        }) {
809            let (i, child) = contents.next().unwrap();
810            self.debug_push_print_item(DebugPrintField::Contents, i);
811            child.build_display_list(builder, &self.atomic_inline_stacking_containers);
812
813            if child.has_outline() {
814                content_with_outlines.push(child);
815            }
816        }
817
818        // Step 5: Float stacking containers
819        for (i, child) in self.float_stacking_containers.iter().enumerate() {
820            self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
821            child.build_display_list(builder);
822        }
823
824        // Steps 6 and 7: Fragments and inline stacking containers
825        while contents
826            .peek()
827            .is_some_and(|(_, child)| child.section() == StackingContextSection::Foreground)
828        {
829            let (i, child) = contents.next().unwrap();
830            self.debug_push_print_item(DebugPrintField::Contents, i);
831            child.build_display_list(builder, &self.atomic_inline_stacking_containers);
832
833            if child.has_outline() {
834                content_with_outlines.push(child);
835            }
836        }
837
838        // Steps 8 and 9: Stacking contexts with non-negative ‘z-index’, and
839        // positioned stacking containers (where ‘z-index’ is auto)
840        for (i, child) in real_stacking_contexts_and_positioned_stacking_containers {
841            self.debug_push_print_item(
842                DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
843                i,
844            );
845            child.build_display_list(builder);
846        }
847
848        // Step 10: Outline
849        for content in content_with_outlines {
850            content.build_display_list_with_section_override(
851                builder,
852                &self.atomic_inline_stacking_containers,
853                Some(StackingContextSection::Outline),
854            );
855        }
856
857        if pushed_context {
858            builder.wr().pop_stacking_context();
859        }
860    }
861
862    /// Store the fact that something was painted, if [Self::debug_print_items] is not None.
863    ///
864    /// This is used to help reconstruct the original painting order in [Self::debug_print] without
865    /// duplicating our painting order logic, since that could fall out of sync with the real logic.
866    fn debug_push_print_item(&self, field: DebugPrintField, index: usize) {
867        if let Some(items) = self.debug_print_items.as_ref() {
868            items.borrow_mut().push(DebugPrintItem { field, index });
869        }
870    }
871
872    /// Print the stacking context tree.
873    pub fn debug_print(&self) {
874        if self.debug_print_items.is_none() {
875            warn!("failed to print stacking context tree: debug_print_items was None");
876            return;
877        }
878        let mut tree = PrintTree::new("Stacking context tree");
879        self.debug_print_with_tree(&mut tree);
880    }
881
882    /// Print a subtree with the given [PrintTree], or panic if [Self::debug_print_items] is None.
883    fn debug_print_with_tree(&self, tree: &mut PrintTree) {
884        match self.context_type {
885            StackingContextType::RealStackingContext => {
886                tree.new_level(format!("{:?} z={}", self.context_type, self.z_index()));
887            },
888            StackingContextType::AtomicInlineStackingContainer => {
889                // do nothing; we print the heading with its index in DebugPrintField::Contents
890            },
891            _ => {
892                tree.new_level(format!("{:?}", self.context_type));
893            },
894        }
895        for DebugPrintItem { field, index } in
896            self.debug_print_items.as_ref().unwrap().borrow().iter()
897        {
898            match field {
899                DebugPrintField::Contents => match self.contents[*index] {
900                    StackingContextContent::Fragment { section, .. } => {
901                        tree.add_item(format!("{section:?}"));
902                    },
903                    StackingContextContent::AtomicInlineStackingContainer { index } => {
904                        tree.new_level(format!("AtomicInlineStackingContainer #{index}"));
905                        self.atomic_inline_stacking_containers[index].debug_print_with_tree(tree);
906                        tree.end_level();
907                    },
908                },
909                DebugPrintField::RealStackingContextsAndPositionedStackingContainers => {
910                    self.real_stacking_contexts_and_positioned_stacking_containers[*index]
911                        .debug_print_with_tree(tree);
912                },
913                DebugPrintField::FloatStackingContainers => {
914                    self.float_stacking_containers[*index].debug_print_with_tree(tree);
915                },
916            }
917        }
918        match self.context_type {
919            StackingContextType::AtomicInlineStackingContainer => {
920                // do nothing; we print the heading with its index in DebugPrintField::Contents
921            },
922            _ => {
923                tree.end_level();
924            },
925        }
926    }
927}
928
929#[derive(PartialEq)]
930pub(crate) enum StackingContextBuildMode {
931    IncludeHoisted,
932    SkipHoisted,
933}
934
935impl Fragment {
936    pub(crate) fn build_stacking_context_tree(
937        &self,
938        stacking_context_tree: &mut StackingContextTree,
939        containing_block_info: &ContainingBlockInfo,
940        stacking_context: &mut StackingContext,
941        mode: StackingContextBuildMode,
942        text_decorations: &Arc<Vec<FragmentTextDecoration>>,
943    ) {
944        let containing_block = containing_block_info.get_containing_block_for_fragment(self);
945        let cumulative_containing_block = containing_block
946            .rect
947            .translate(containing_block.accumulated_reference_frame_offset);
948        self.set_containing_block(&cumulative_containing_block);
949
950        if self
951            .base()
952            .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
953        {
954            return;
955        }
956
957        let fragment_clone = self.clone();
958        match self {
959            Fragment::Box(fragment) | Fragment::Float(fragment) => {
960                if mode == StackingContextBuildMode::SkipHoisted &&
961                    fragment.style().clone_position().is_absolutely_positioned()
962                {
963                    return;
964                }
965
966                let text_decorations = match self {
967                    Fragment::Float(..) => &Default::default(),
968                    _ => text_decorations,
969                };
970
971                fragment.build_stacking_context_tree(
972                    fragment_clone,
973                    stacking_context_tree,
974                    containing_block,
975                    containing_block_info,
976                    stacking_context,
977                    text_decorations,
978                );
979            },
980            Fragment::AbsoluteOrFixedPositioned(fragment) => {
981                let shared_fragment = fragment.borrow();
982                let fragment_ref = match shared_fragment.fragment.as_ref() {
983                    Some(fragment_ref) => fragment_ref,
984                    None => unreachable!("Found hoisted box with missing fragment."),
985                };
986
987                fragment_ref.build_stacking_context_tree(
988                    stacking_context_tree,
989                    containing_block_info,
990                    stacking_context,
991                    StackingContextBuildMode::IncludeHoisted,
992                    &Default::default(),
993                );
994            },
995            Fragment::Positioning(fragment) => {
996                fragment.build_stacking_context_tree(
997                    stacking_context_tree,
998                    containing_block,
999                    containing_block_info,
1000                    stacking_context,
1001                    text_decorations,
1002                );
1003            },
1004            Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
1005                stacking_context
1006                    .contents
1007                    .push(StackingContextContent::Fragment {
1008                        section: StackingContextSection::Foreground,
1009                        scroll_node_id: containing_block.scroll_node_id,
1010                        reference_frame_scroll_node_id: containing_block_info
1011                            .for_absolute_and_fixed_descendants
1012                            .scroll_node_id,
1013                        clip_id: containing_block.clip_id,
1014                        containing_block: containing_block.rect,
1015                        fragment: fragment_clone,
1016                        is_hit_test_for_scrollable_overflow: false,
1017                        is_collapsed_table_borders: false,
1018                        text_decorations: text_decorations.clone(),
1019                    });
1020            },
1021        }
1022    }
1023}
1024
1025struct ReferenceFrameData {
1026    origin: PhysicalPoint<Au>,
1027    transform: LayoutTransform,
1028    kind: wr::ReferenceFrameKind,
1029}
1030struct ScrollFrameData {
1031    scroll_tree_node_id: ScrollTreeNodeId,
1032    scroll_frame_rect: LayoutRect,
1033}
1034
1035struct OverflowFrameData {
1036    clip_id: ClipId,
1037    scroll_frame_data: Option<ScrollFrameData>,
1038}
1039
1040impl BoxFragment {
1041    fn get_stacking_context_type(&self) -> Option<StackingContextType> {
1042        let flags = self.base.flags;
1043        let style = self.style();
1044        if style.establishes_stacking_context(flags) {
1045            return Some(StackingContextType::RealStackingContext);
1046        }
1047
1048        let box_style = &style.get_box();
1049        if box_style.position != ComputedPosition::Static {
1050            return Some(StackingContextType::PositionedStackingContainer);
1051        }
1052
1053        if box_style.float != ComputedFloat::None {
1054            return Some(StackingContextType::FloatStackingContainer);
1055        }
1056
1057        // Flex and grid items are painted like inline blocks.
1058        // <https://drafts.csswg.org/css-flexbox-1/#painting>
1059        // <https://drafts.csswg.org/css-grid/#z-order>
1060        if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
1061            return Some(StackingContextType::AtomicInlineStackingContainer);
1062        }
1063
1064        None
1065    }
1066
1067    fn get_stacking_context_section(&self) -> StackingContextSection {
1068        if self.get_stacking_context_type().is_some() {
1069            return StackingContextSection::OwnBackgroundsAndBorders;
1070        }
1071
1072        if self.style().get_box().display.outside() == DisplayOutside::Inline {
1073            return StackingContextSection::Foreground;
1074        }
1075
1076        StackingContextSection::DescendantBackgroundsAndBorders
1077    }
1078
1079    fn build_stacking_context_tree(
1080        &self,
1081        fragment: Fragment,
1082        stacking_context_tree: &mut StackingContextTree,
1083        containing_block: &ContainingBlock,
1084        containing_block_info: &ContainingBlockInfo,
1085        parent_stacking_context: &mut StackingContext,
1086        text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1087    ) {
1088        self.build_stacking_context_tree_maybe_creating_reference_frame(
1089            fragment,
1090            stacking_context_tree,
1091            containing_block,
1092            containing_block_info,
1093            parent_stacking_context,
1094            text_decorations,
1095        );
1096    }
1097
1098    fn build_stacking_context_tree_maybe_creating_reference_frame(
1099        &self,
1100        fragment: Fragment,
1101        stacking_context_tree: &mut StackingContextTree,
1102        containing_block: &ContainingBlock,
1103        containing_block_info: &ContainingBlockInfo,
1104        parent_stacking_context: &mut StackingContext,
1105        text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1106    ) {
1107        let reference_frame_data =
1108            match self.reference_frame_data_if_necessary(&containing_block.rect) {
1109                Some(reference_frame_data) => reference_frame_data,
1110                None => {
1111                    return self.build_stacking_context_tree_maybe_creating_stacking_context(
1112                        fragment,
1113                        stacking_context_tree,
1114                        containing_block,
1115                        containing_block_info,
1116                        parent_stacking_context,
1117                        text_decorations,
1118                    );
1119                },
1120            };
1121
1122        // <https://drafts.csswg.org/css-transforms/#transform-function-lists>
1123        // > If a transform function causes the current transformation matrix of an object
1124        // > to be non-invertible, the object and its content do not get displayed.
1125        if !reference_frame_data.transform.is_invertible() {
1126            self.clear_spatial_tree_node_including_descendants();
1127            return;
1128        }
1129
1130        let style = self.style();
1131        let frame_origin_for_query = self
1132            .cumulative_border_box_rect(
1133                ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
1134            )
1135            .origin
1136            .to_webrender();
1137        let new_spatial_id = stacking_context_tree.push_reference_frame(
1138            reference_frame_data.origin.to_webrender(),
1139            frame_origin_for_query,
1140            containing_block.scroll_node_id,
1141            style.get_box().transform_style.to_webrender(),
1142            reference_frame_data.transform,
1143            reference_frame_data.kind,
1144        );
1145
1146        // WebRender reference frames establish a new coordinate system at their
1147        // origin (the border box of the fragment). We need to ensure that any
1148        // coordinates we give to WebRender in this reference frame are relative
1149        // to the fragment border box. We do this by adjusting the containing
1150        // block origin. Note that the `for_absolute_descendants` and
1151        // `for_all_absolute_and_fixed_descendants` properties are now bogus,
1152        // but all fragments that establish reference frames also establish
1153        // containing blocks for absolute and fixed descendants, so those
1154        // properties will be replaced before recursing into children.
1155        assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
1156        let reference_frame_offset = reference_frame_data.origin.to_vector();
1157        let adjusted_containing_block = ContainingBlock::new(
1158            containing_block.rect.translate(-reference_frame_offset),
1159            new_spatial_id,
1160            None,
1161            containing_block.clip_id,
1162            containing_block.accumulated_reference_frame_offset + reference_frame_offset,
1163        );
1164        let new_containing_block_info =
1165            containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
1166
1167        self.build_stacking_context_tree_maybe_creating_stacking_context(
1168            fragment,
1169            stacking_context_tree,
1170            &adjusted_containing_block,
1171            &new_containing_block_info,
1172            parent_stacking_context,
1173            text_decorations,
1174        );
1175    }
1176
1177    fn build_stacking_context_tree_maybe_creating_stacking_context(
1178        &self,
1179        fragment: Fragment,
1180        stacking_context_tree: &mut StackingContextTree,
1181        containing_block: &ContainingBlock,
1182        containing_block_info: &ContainingBlockInfo,
1183        parent_stacking_context: &mut StackingContext,
1184        text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1185    ) {
1186        let context_type = match self.get_stacking_context_type() {
1187            Some(context_type) => context_type,
1188            None => {
1189                self.build_stacking_context_tree_for_children(
1190                    fragment,
1191                    stacking_context_tree,
1192                    containing_block,
1193                    containing_block_info,
1194                    parent_stacking_context,
1195                    text_decorations,
1196                );
1197                return;
1198            },
1199        };
1200
1201        if context_type == StackingContextType::AtomicInlineStackingContainer {
1202            // Push a dummy fragment that indicates when the new stacking context should be painted.
1203            parent_stacking_context.contents.push(
1204                StackingContextContent::AtomicInlineStackingContainer {
1205                    index: parent_stacking_context
1206                        .atomic_inline_stacking_containers
1207                        .len(),
1208                },
1209            );
1210        }
1211
1212        // `clip-path` needs to be applied before filters and creates a stacking context, so it can be
1213        // applied directly to the stacking context itself.
1214        // before
1215        let stacking_context_clip_id = stacking_context_tree
1216            .clip_store
1217            .add_for_clip_path(
1218                &self.style().get_svg().clip_path,
1219                containing_block.scroll_node_id,
1220                containing_block.clip_id,
1221                BuilderForBoxFragment::new(
1222                    self,
1223                    &containing_block.rect,
1224                    false, /* is_hit_test_for_scrollable_overflow */
1225                    false, /* is_collapsed_table_borders */
1226                ),
1227            )
1228            .unwrap_or(containing_block.clip_id);
1229
1230        let box_fragment = match fragment {
1231            Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
1232                box_fragment.clone()
1233            },
1234            _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
1235        };
1236
1237        let mut child_stacking_context = parent_stacking_context.create_descendant(
1238            containing_block.scroll_node_id,
1239            stacking_context_clip_id,
1240            box_fragment,
1241            context_type,
1242        );
1243        self.build_stacking_context_tree_for_children(
1244            fragment,
1245            stacking_context_tree,
1246            containing_block,
1247            containing_block_info,
1248            &mut child_stacking_context,
1249            text_decorations,
1250        );
1251
1252        let mut stolen_children = vec![];
1253        if context_type != StackingContextType::RealStackingContext {
1254            stolen_children = mem::replace(
1255                &mut child_stacking_context
1256                    .real_stacking_contexts_and_positioned_stacking_containers,
1257                stolen_children,
1258            );
1259        }
1260
1261        child_stacking_context.sort();
1262        parent_stacking_context.add_stacking_context(child_stacking_context);
1263        parent_stacking_context
1264            .real_stacking_contexts_and_positioned_stacking_containers
1265            .append(&mut stolen_children);
1266    }
1267
1268    fn build_stacking_context_tree_for_children(
1269        &self,
1270        fragment: Fragment,
1271        stacking_context_tree: &mut StackingContextTree,
1272        containing_block: &ContainingBlock,
1273        containing_block_info: &ContainingBlockInfo,
1274        stacking_context: &mut StackingContext,
1275        text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1276    ) {
1277        let mut new_scroll_node_id = containing_block.scroll_node_id;
1278        let mut new_clip_id = containing_block.clip_id;
1279        let mut new_scroll_frame_size = containing_block_info
1280            .for_non_absolute_descendants
1281            .scroll_frame_size;
1282
1283        if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
1284            stacking_context_tree,
1285            new_scroll_node_id,
1286            &containing_block.rect,
1287            &new_scroll_frame_size,
1288        ) {
1289            new_scroll_node_id = scroll_node_id;
1290        }
1291
1292        if let Some(clip_id) = self.build_clip_frame_if_necessary(
1293            stacking_context_tree,
1294            new_scroll_node_id,
1295            new_clip_id,
1296            &containing_block.rect,
1297        ) {
1298            new_clip_id = clip_id;
1299        }
1300
1301        let style = self.style();
1302        if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
1303            &style.get_svg().clip_path,
1304            new_scroll_node_id,
1305            new_clip_id,
1306            BuilderForBoxFragment::new(
1307                self,
1308                &containing_block.rect,
1309                false, /* is_hit_test_for_scrollable_overflow */
1310                false, /* is_collapsed_table_borders */
1311            ),
1312        ) {
1313            new_clip_id = clip_id;
1314        }
1315
1316        let establishes_containing_block_for_all_descendants =
1317            style.establishes_containing_block_for_all_descendants(self.base.flags);
1318        let establishes_containing_block_for_absolute_descendants =
1319            style.establishes_containing_block_for_absolute_descendants(self.base.flags);
1320
1321        let reference_frame_scroll_node_id_for_fragments =
1322            if establishes_containing_block_for_all_descendants {
1323                new_scroll_node_id
1324            } else {
1325                containing_block_info
1326                    .for_absolute_and_fixed_descendants
1327                    .scroll_node_id
1328            };
1329
1330        let mut add_fragment = |section| {
1331            stacking_context
1332                .contents
1333                .push(StackingContextContent::Fragment {
1334                    scroll_node_id: new_scroll_node_id,
1335                    reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1336                    clip_id: new_clip_id,
1337                    section,
1338                    containing_block: containing_block.rect,
1339                    fragment: fragment.clone(),
1340                    is_hit_test_for_scrollable_overflow: false,
1341                    is_collapsed_table_borders: false,
1342                    text_decorations: text_decorations.clone(),
1343                });
1344        };
1345
1346        let section = self.get_stacking_context_section();
1347        add_fragment(section);
1348
1349        // Spatial tree node that will affect the transform of the fragment. Note that the next frame,
1350        // scroll frame, does not affect the transform of the fragment but affect the transform of it
1351        // children.
1352        *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1353
1354        // We want to build the scroll frame after the background and border, because
1355        // they shouldn't scroll with the rest of the box content.
1356        if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
1357            stacking_context_tree,
1358            new_scroll_node_id,
1359            new_clip_id,
1360            &containing_block.rect,
1361        ) {
1362            new_clip_id = overflow_frame_data.clip_id;
1363            if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
1364                new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
1365                new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
1366                stacking_context
1367                    .contents
1368                    .push(StackingContextContent::Fragment {
1369                        scroll_node_id: new_scroll_node_id,
1370                        reference_frame_scroll_node_id:
1371                            reference_frame_scroll_node_id_for_fragments,
1372                        clip_id: new_clip_id,
1373                        section,
1374                        containing_block: containing_block.rect,
1375                        fragment: fragment.clone(),
1376                        is_hit_test_for_scrollable_overflow: true,
1377                        is_collapsed_table_borders: false,
1378                        text_decorations: text_decorations.clone(),
1379                    });
1380            }
1381        }
1382
1383        let padding_rect = self
1384            .padding_rect()
1385            .translate(containing_block.rect.origin.to_vector());
1386        let content_rect = self
1387            .content_rect()
1388            .translate(containing_block.rect.origin.to_vector());
1389
1390        let for_absolute_descendants = ContainingBlock::new(
1391            padding_rect,
1392            new_scroll_node_id,
1393            new_scroll_frame_size,
1394            new_clip_id,
1395            containing_block.accumulated_reference_frame_offset,
1396        );
1397        let for_non_absolute_descendants = ContainingBlock::new(
1398            content_rect,
1399            new_scroll_node_id,
1400            new_scroll_frame_size,
1401            new_clip_id,
1402            containing_block.accumulated_reference_frame_offset,
1403        );
1404
1405        // Create a new `ContainingBlockInfo` for descendants depending on
1406        // whether or not this fragment establishes a containing block for
1407        // absolute and fixed descendants.
1408        let new_containing_block_info = if establishes_containing_block_for_all_descendants {
1409            containing_block_info.new_for_absolute_and_fixed_descendants(
1410                &for_non_absolute_descendants,
1411                &for_absolute_descendants,
1412            )
1413        } else if establishes_containing_block_for_absolute_descendants {
1414            containing_block_info.new_for_absolute_descendants(
1415                &for_non_absolute_descendants,
1416                &for_absolute_descendants,
1417            )
1418        } else {
1419            containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
1420        };
1421
1422        // Text decorations are not propagated to atomic inline-level descendants.
1423        // From https://drafts.csswg.org/css2/#lining-striking-props:
1424        // > Note that text decorations are not propagated to floating and absolutely
1425        // > positioned descendants, nor to the contents of atomic inline-level descendants
1426        // > such as inline blocks and inline tables.
1427        let text_decorations = match self.is_atomic_inline_level() ||
1428            self.base
1429                .flags
1430                .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
1431        {
1432            true => &Default::default(),
1433            false => text_decorations,
1434        };
1435
1436        let new_text_decoration;
1437        let text_decorations = match style.clone_text_decoration_line() {
1438            TextDecorationLine::NONE => text_decorations,
1439            line => {
1440                let mut new_vector = (**text_decorations).clone();
1441                let color = &style.get_inherited_text().color;
1442                new_vector.push(FragmentTextDecoration {
1443                    line,
1444                    color: style
1445                        .clone_text_decoration_color()
1446                        .resolve_to_absolute(color),
1447                    style: style.clone_text_decoration_style(),
1448                });
1449                new_text_decoration = Arc::new(new_vector);
1450                &new_text_decoration
1451            },
1452        };
1453
1454        for child in &self.children {
1455            child.build_stacking_context_tree(
1456                stacking_context_tree,
1457                &new_containing_block_info,
1458                stacking_context,
1459                StackingContextBuildMode::SkipHoisted,
1460                text_decorations,
1461            );
1462        }
1463
1464        if matches!(
1465            self.specific_layout_info().as_deref(),
1466            Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
1467        ) {
1468            stacking_context
1469                .contents
1470                .push(StackingContextContent::Fragment {
1471                    scroll_node_id: new_scroll_node_id,
1472                    reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1473                    clip_id: new_clip_id,
1474                    section,
1475                    containing_block: containing_block.rect,
1476                    fragment: fragment.clone(),
1477                    is_hit_test_for_scrollable_overflow: false,
1478                    is_collapsed_table_borders: true,
1479                    text_decorations: text_decorations.clone(),
1480                });
1481        }
1482    }
1483
1484    fn build_clip_frame_if_necessary(
1485        &self,
1486        stacking_context_tree: &mut StackingContextTree,
1487        parent_scroll_node_id: ScrollTreeNodeId,
1488        parent_clip_id: ClipId,
1489        containing_block_rect: &PhysicalRect<Au>,
1490    ) -> Option<ClipId> {
1491        let style = self.style();
1492        let position = style.get_box().position;
1493        // https://drafts.csswg.org/css2/#clipping
1494        // The clip property applies only to absolutely positioned elements
1495        if !position.is_absolutely_positioned() {
1496            return None;
1497        }
1498
1499        // Only rectangles are supported for now.
1500        let clip_rect = match style.get_effects().clip {
1501            ClipRectOrAuto::Rect(rect) => rect,
1502            _ => return None,
1503        };
1504
1505        let border_rect = self.border_rect();
1506        let clip_rect = clip_rect
1507            .for_border_rect(border_rect)
1508            .translate(containing_block_rect.origin.to_vector())
1509            .to_webrender();
1510        Some(stacking_context_tree.clip_store.add(
1511            BorderRadius::zero(),
1512            clip_rect,
1513            parent_scroll_node_id,
1514            parent_clip_id,
1515        ))
1516    }
1517
1518    fn build_overflow_frame_if_necessary(
1519        &self,
1520        stacking_context_tree: &mut StackingContextTree,
1521        parent_scroll_node_id: ScrollTreeNodeId,
1522        parent_clip_id: ClipId,
1523        containing_block_rect: &PhysicalRect<Au>,
1524    ) -> Option<OverflowFrameData> {
1525        let style = self.style();
1526        let overflow = style.effective_overflow(self.base.flags);
1527
1528        if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1529            return None;
1530        }
1531
1532        // Non-scrollable overflow path
1533        if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1534            let overflow_clip_margin = style.get_margin().overflow_clip_margin;
1535            let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
1536                OverflowClipMarginBox::ContentBox => self.content_rect(),
1537                OverflowClipMarginBox::PaddingBox => self.padding_rect(),
1538                OverflowClipMarginBox::BorderBox => self.border_rect(),
1539            }
1540            .translate(containing_block_rect.origin.to_vector())
1541            .to_webrender();
1542
1543            // Adjust by the overflow clip margin.
1544            // https://drafts.csswg.org/css-overflow-3/#overflow-clip-margin
1545            let clip_margin_offset = overflow_clip_margin.offset.px();
1546            overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
1547
1548            // The clipping region only gets rounded corners if both axes have `overflow: clip`.
1549            // https://drafts.csswg.org/css-overflow-3/#corner-clipping
1550            let radii;
1551            if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1552                let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
1553                let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
1554                match overflow_clip_margin.visual_box {
1555                    OverflowClipMarginBox::ContentBox => {
1556                        offsets_from_border -= (self.border + self.padding).to_webrender();
1557                    },
1558                    OverflowClipMarginBox::PaddingBox => {
1559                        offsets_from_border -= self.border.to_webrender();
1560                    },
1561                    OverflowClipMarginBox::BorderBox => {},
1562                };
1563                radii = offset_radii(builder.border_radius, offsets_from_border);
1564            } else if overflow.x != ComputedOverflow::Clip {
1565                let max = LayoutRect::max_rect();
1566                overflow_clip_rect.min.x = max.min.x;
1567                overflow_clip_rect.max.x = max.max.x;
1568                radii = BorderRadius::zero();
1569            } else {
1570                let max = LayoutRect::max_rect();
1571                overflow_clip_rect.min.y = max.min.y;
1572                overflow_clip_rect.max.y = max.max.y;
1573                radii = BorderRadius::zero();
1574            }
1575
1576            let clip_id = stacking_context_tree.clip_store.add(
1577                radii,
1578                overflow_clip_rect,
1579                parent_scroll_node_id,
1580                parent_clip_id,
1581            );
1582
1583            return Some(OverflowFrameData {
1584                clip_id,
1585                scroll_frame_data: None,
1586            });
1587        }
1588
1589        let scroll_frame_rect = self
1590            .padding_rect()
1591            .translate(containing_block_rect.origin.to_vector())
1592            .to_webrender();
1593
1594        let clip_id = stacking_context_tree.clip_store.add(
1595            BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
1596            scroll_frame_rect,
1597            parent_scroll_node_id,
1598            parent_clip_id,
1599        );
1600
1601        let tag = self.base.tag?;
1602        let external_scroll_id = wr::ExternalScrollId(
1603            tag.to_display_list_fragment_id(),
1604            stacking_context_tree.paint_info.pipeline_id,
1605        );
1606
1607        let sensitivity = AxesScrollSensitivity {
1608            x: overflow.x.into(),
1609            y: overflow.y.into(),
1610        };
1611
1612        let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1613            parent_scroll_node_id,
1614            external_scroll_id,
1615            self.scrollable_overflow().to_webrender(),
1616            scroll_frame_rect,
1617            sensitivity,
1618        );
1619
1620        Some(OverflowFrameData {
1621            clip_id,
1622            scroll_frame_data: Some(ScrollFrameData {
1623                scroll_tree_node_id,
1624                scroll_frame_rect,
1625            }),
1626        })
1627    }
1628
1629    fn build_sticky_frame_if_necessary(
1630        &self,
1631        stacking_context_tree: &mut StackingContextTree,
1632        parent_scroll_node_id: ScrollTreeNodeId,
1633        containing_block_rect: &PhysicalRect<Au>,
1634        scroll_frame_size: &Option<LayoutSize>,
1635    ) -> Option<ScrollTreeNodeId> {
1636        let style = self.style();
1637        if style.get_box().position != ComputedPosition::Sticky {
1638            return None;
1639        }
1640
1641        let scroll_frame_size_for_resolve = match scroll_frame_size {
1642            Some(size) => size,
1643            None => {
1644                // This is a direct descendant of a reference frame.
1645                &stacking_context_tree
1646                    .paint_info
1647                    .viewport_details
1648                    .layout_size()
1649            },
1650        };
1651
1652        // Percentages sticky positions offsets are resovled against the size of the
1653        // nearest scroll frame instead of the containing block like for other types
1654        // of positioning.
1655        let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1656        let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1657        let offsets = style.physical_box_offsets();
1658        let offsets = PhysicalSides::<AuOrAuto>::new(
1659            offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1660            offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1661            offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1662            offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1663        );
1664        self.ensure_rare_data().resolved_sticky_insets = Some(Box::new(offsets));
1665
1666        if scroll_frame_size.is_none() {
1667            return None;
1668        }
1669
1670        if offsets.top.is_auto() &&
1671            offsets.right.is_auto() &&
1672            offsets.bottom.is_auto() &&
1673            offsets.left.is_auto()
1674        {
1675            return None;
1676        }
1677
1678        // https://drafts.csswg.org/css-position/#stickypos-insets
1679        // > For each side of the box, if the corresponding inset property is not `auto`, and the
1680        // > corresponding border edge of the box would be outside the corresponding edge of the
1681        // > sticky view rectangle, the box must be visually shifted (as for relative positioning)
1682        // > to be inward of that sticky view rectangle edge, insofar as it can while its position
1683        // > box remains contained within its containing block.
1684        // > The *position box* is its margin box, except that for any side for which the distance
1685        // > between its margin edge and the corresponding edge of its containing block is less
1686        // > than its corresponding margin, that distance is used in place of that margin.
1687        //
1688        // Amendments:
1689        // - Using the "margin edge" seems nonsensical, the spec must mean "border edge" instead:
1690        //   https://github.com/w3c/csswg-drafts/issues/12833
1691        // - `auto` margins need to be treated as zero:
1692        //   https://github.com/w3c/csswg-drafts/issues/12852
1693        //
1694        // We implement this by enforcing a minimum negative offset and a maximum positive offset.
1695        // The logic below is a simplified (but equivalent) version of the description above.
1696        let border_rect = self.border_rect();
1697        let computed_margin = style.physical_margin();
1698
1699        // Signed distance between each side of the border box to the corresponding side of the
1700        // containing block. Note that |border_rect| is already in the coordinate system of the
1701        // containing block.
1702        let distance_from_border_box_to_cb = PhysicalSides::new(
1703            border_rect.min_y(),
1704            containing_block_rect.width() - border_rect.max_x(),
1705            containing_block_rect.height() - border_rect.max_y(),
1706            border_rect.min_x(),
1707        );
1708
1709        // Shrinks the signed distance by the margin, producing a limit on how much we can shift
1710        // the sticky positioned box without forcing the margin to move outside of the containing
1711        // block.
1712        let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1713            let used_margin = if computed_margin.is_auto() {
1714                Au::zero()
1715            } else {
1716                used_margin
1717            };
1718            Au::zero().max(distance - used_margin).to_f32_px()
1719        };
1720
1721        // This is the minimum negative offset and then the maximum positive offset. We specify
1722        // all sides, but they will have no effect if the corresponding inset property is `auto`.
1723        let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1724            -offset_bound(
1725                distance_from_border_box_to_cb.top,
1726                self.margin.top,
1727                computed_margin.top,
1728            ),
1729            offset_bound(
1730                distance_from_border_box_to_cb.bottom,
1731                self.margin.bottom,
1732                computed_margin.bottom,
1733            ),
1734        );
1735        let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1736            -offset_bound(
1737                distance_from_border_box_to_cb.left,
1738                self.margin.left,
1739                computed_margin.left,
1740            ),
1741            offset_bound(
1742                distance_from_border_box_to_cb.right,
1743                self.margin.right,
1744                computed_margin.right,
1745            ),
1746        );
1747
1748        let frame_rect = border_rect
1749            .translate(containing_block_rect.origin.to_vector())
1750            .to_webrender();
1751
1752        // These are the "margins" between the scrollport and |frame_rect|. They are not the same
1753        // as CSS margins.
1754        let margins = SideOffsets2D::new(
1755            offsets.top.non_auto().map(|v| v.to_f32_px()),
1756            offsets.right.non_auto().map(|v| v.to_f32_px()),
1757            offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1758            offsets.left.non_auto().map(|v| v.to_f32_px()),
1759        );
1760
1761        let sticky_node_id = stacking_context_tree.define_sticky_frame(
1762            parent_scroll_node_id,
1763            frame_rect,
1764            margins,
1765            vertical_offset_bounds,
1766            horizontal_offset_bounds,
1767        );
1768
1769        Some(sticky_node_id)
1770    }
1771
1772    /// Optionally returns the data for building a reference frame, without yet building it.
1773    fn reference_frame_data_if_necessary(
1774        &self,
1775        containing_block_rect: &PhysicalRect<Au>,
1776    ) -> Option<ReferenceFrameData> {
1777        if !self
1778            .style()
1779            .has_effective_transform_or_perspective(self.base.flags)
1780        {
1781            return None;
1782        }
1783
1784        let relative_border_rect = self.border_rect();
1785        let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1786        let transform = self.calculate_transform_matrix(&border_rect);
1787        let perspective = self.calculate_perspective_matrix(&border_rect);
1788        let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1789            (None, Some(perspective)) => (
1790                perspective,
1791                wr::ReferenceFrameKind::Perspective {
1792                    scrolling_relative_to: None,
1793                },
1794            ),
1795            (Some(transform), None) => (
1796                transform,
1797                wr::ReferenceFrameKind::Transform {
1798                    is_2d_scale_translation: false,
1799                    should_snap: false,
1800                    paired_with_perspective: false,
1801                },
1802            ),
1803            (Some(transform), Some(perspective)) => (
1804                perspective.then(&transform),
1805                wr::ReferenceFrameKind::Perspective {
1806                    scrolling_relative_to: None,
1807                },
1808            ),
1809            (None, None) => unreachable!(),
1810        };
1811
1812        Some(ReferenceFrameData {
1813            origin: border_rect.origin,
1814            transform: reference_frame_transform,
1815            kind: reference_frame_kind,
1816        })
1817    }
1818
1819    /// Returns the 4D matrix representing this fragment's transform.
1820    pub fn calculate_transform_matrix(
1821        &self,
1822        border_rect: &Rect<Au, CSSPixel>,
1823    ) -> Option<LayoutTransform> {
1824        let style = self.style();
1825        let list = &style.get_box().transform;
1826        let length_rect = au_rect_to_length_rect(border_rect);
1827        // https://drafts.csswg.org/css-transforms-2/#individual-transforms
1828        let rotate = match style.clone_rotate() {
1829            GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1830            GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle),
1831            GenericRotate::None => (0., 0., 1., Angle::zero()),
1832        };
1833        let scale = match style.clone_scale() {
1834            GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1835            GenericScale::None => (1., 1., 1.),
1836        };
1837        let translation = match style.clone_translate() {
1838            GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1839                x.resolve(length_rect.size.width).px(),
1840                y.resolve(length_rect.size.height).px(),
1841                z.px(),
1842            ),
1843            GenericTranslate::None => LayoutTransform::identity(),
1844        };
1845
1846        let angle = euclid::Angle::radians(rotate.3.radians());
1847        let transform_base = list
1848            .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1849            .ok()?;
1850        let transform = LayoutTransform::from_untyped(&transform_base.0)
1851            .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1852            .then_scale(scale.0, scale.1, scale.2)
1853            .then(&translation);
1854
1855        let transform_origin = &style.get_box().transform_origin;
1856        let transform_origin_x = transform_origin
1857            .horizontal
1858            .to_used_value(border_rect.size.width)
1859            .to_f32_px();
1860        let transform_origin_y = transform_origin
1861            .vertical
1862            .to_used_value(border_rect.size.height)
1863            .to_f32_px();
1864        let transform_origin_z = transform_origin.depth.px();
1865
1866        Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1867    }
1868
1869    /// Returns the 4D matrix representing this fragment's perspective.
1870    pub fn calculate_perspective_matrix(
1871        &self,
1872        border_rect: &Rect<Au, CSSPixel>,
1873    ) -> Option<LayoutTransform> {
1874        let style = self.style();
1875        match style.get_box().perspective {
1876            Perspective::Length(length) => {
1877                let perspective_origin = &style.get_box().perspective_origin;
1878                let perspective_origin = LayoutPoint::new(
1879                    perspective_origin
1880                        .horizontal
1881                        .percentage_relative_to(border_rect.size.width.into())
1882                        .px(),
1883                    perspective_origin
1884                        .vertical
1885                        .percentage_relative_to(border_rect.size.height.into())
1886                        .px(),
1887                );
1888
1889                let perspective_matrix = LayoutTransform::from_untyped(
1890                    &transform::create_perspective_matrix(length.px()),
1891                );
1892
1893                Some(perspective_matrix.change_basis(
1894                    perspective_origin.x,
1895                    perspective_origin.y,
1896                    0.0,
1897                ))
1898            },
1899            Perspective::None => None,
1900        }
1901    }
1902
1903    fn clear_spatial_tree_node_including_descendants(&self) {
1904        fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1905            for fragment in fragments.iter() {
1906                match fragment {
1907                    Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1908                        box_fragment.clear_spatial_tree_node_including_descendants();
1909                    },
1910                    Fragment::Positioning(positioning_fragment) => {
1911                        assign_spatial_tree_node_on_fragments(&positioning_fragment.children);
1912                    },
1913                    _ => {},
1914                }
1915            }
1916        }
1917
1918        *self.spatial_tree_node.borrow_mut() = None;
1919        assign_spatial_tree_node_on_fragments(&self.children);
1920    }
1921}
1922
1923impl PositioningFragment {
1924    fn build_stacking_context_tree(
1925        &self,
1926        stacking_context_tree: &mut StackingContextTree,
1927        containing_block: &ContainingBlock,
1928        containing_block_info: &ContainingBlockInfo,
1929        stacking_context: &mut StackingContext,
1930        text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1931    ) {
1932        let rect = self
1933            .base
1934            .rect()
1935            .translate(containing_block.rect.origin.to_vector());
1936        let new_containing_block = containing_block.new_replacing_rect(&rect);
1937        let new_containing_block_info =
1938            containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1939
1940        for child in &self.children {
1941            child.build_stacking_context_tree(
1942                stacking_context_tree,
1943                &new_containing_block_info,
1944                stacking_context,
1945                StackingContextBuildMode::SkipHoisted,
1946                text_decorations,
1947            );
1948        }
1949    }
1950}
1951
1952pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1953    Rect::new(
1954        Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1955        Size2D::new(rect.size.width.into(), rect.size.height.into()),
1956    )
1957}