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;
6use std::rc::Rc;
7use std::sync::Arc;
8
9use app_units::Au;
10use embedder_traits::ViewportDetails;
11use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
12use malloc_size_of_derive::MallocSizeOf;
13use paint_api::display_list::{
14    AxesScrollSensitivity, PaintDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
15    SpatialTreeNodeInfo, StickyNodeInfo,
16};
17use servo_base::id::ScrollTreeNodeId;
18use servo_base::print_tree::PrintTree;
19use servo_config::opts::{DiagnosticsLogging, DiagnosticsLoggingOption};
20use servo_geometry::MaxRect;
21use style::Zero;
22use style::color::AbsoluteColor;
23use style::computed_values::overflow_x::T as ComputedOverflow;
24use style::computed_values::position::T as ComputedPosition;
25use style::computed_values::text_decoration_style::T as TextDecorationStyle;
26use style::values::computed::angle::Angle;
27use style::values::computed::{ClipRectOrAuto, Length, TextDecorationLine};
28use style::values::generics::box_::{OverflowClipMarginBox, Perspective};
29use style::values::generics::transform::{
30    self, GenericRotate, GenericScale, GenericTranslate, get_normalized_vector_and_angle,
31};
32use style_traits::CSSPixel;
33use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
34use webrender_api::{self as wr, BorderRadius};
35use wr::StickyOffsetBounds;
36use wr::units::{LayoutPixel, LayoutSize};
37
38use super::ClipId;
39use super::clip::StackingContextTreeClipStore;
40use crate::display_list::conversions::ToWebRender;
41use crate::display_list::{BuilderForBoxFragment, offset_radii};
42use crate::fragment_tree::{
43    BoxFragment, BoxFragmentWithStyle, ContainingBlockCalculation, ContainingBlockManager,
44    Fragment, FragmentFlags, FragmentTree, PositioningFragment,
45};
46use crate::geom::{
47    AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalVec,
48};
49use crate::style_ext::{ComputedValuesExt, TransformExt};
50
51#[derive(Clone)]
52pub(crate) struct ContainingBlock {
53    /// The SpatialId of the spatial node that contains the children
54    /// of this containing block.
55    scroll_node_id: ScrollTreeNodeId,
56
57    /// The size of the parent scroll frame of this containing block, used for resolving
58    /// sticky margins. If this is None, then this is a direct descendant of a reference
59    /// frame and sticky positioning isn't taken into account.
60    scroll_frame_size: Option<LayoutSize>,
61
62    /// The [`ClipId`] to use for the children of this containing block.
63    clip_id: ClipId,
64
65    /// The physical rect of this containing block.
66    rect: PhysicalRect<Au>,
67
68    /// Normally containing block offsets and display list items are positioned relative
69    /// to their parent reference frame, but cumulative containing block boundaries on
70    /// fragments need to disregard reference frames entirely. This value tracks the
71    /// accumulated offset from the origin of the parent reference frame of this
72    /// containing block.
73    accumulated_reference_frame_offset: PhysicalVec<Au>,
74}
75
76impl ContainingBlock {
77    pub(crate) fn new(
78        rect: PhysicalRect<Au>,
79        scroll_node_id: ScrollTreeNodeId,
80        scroll_frame_size: Option<LayoutSize>,
81        clip_id: ClipId,
82        accumulated_reference_frame_offset: PhysicalVec<Au>,
83    ) -> Self {
84        ContainingBlock {
85            scroll_node_id,
86            scroll_frame_size,
87            clip_id,
88            rect,
89            accumulated_reference_frame_offset,
90        }
91    }
92
93    pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Au>) -> Self {
94        ContainingBlock {
95            rect: *rect,
96            ..*self
97        }
98    }
99}
100
101pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
102
103#[derive(MallocSizeOf)]
104pub(crate) struct StackingContextTree {
105    /// The root [`StackingContext`] of this [`StackingContextTree`].
106    pub root_stacking_context: StackingContext,
107
108    /// The information about the WebRender display list that `Paint`
109    /// consumes. This currently contains the out-of-band hit testing information
110    /// data structure that `Paint` uses to map hit tests to information
111    /// about the item hit.
112    pub paint_info: PaintDisplayListInfo,
113
114    /// All of the clips collected for this [`StackingContextTree`]. These are added
115    /// for things like `overflow`. More clips may be created later during WebRender
116    /// display list construction, but they are never added here.
117    pub clip_store: StackingContextTreeClipStore,
118}
119
120impl StackingContextTree {
121    /// Create a new [DisplayList] given the dimensions of the layout and the WebRender
122    /// pipeline id.
123    pub fn new(
124        fragment_tree: &FragmentTree,
125        viewport_details: ViewportDetails,
126        pipeline_id: wr::PipelineId,
127        first_reflow: bool,
128        debug: &DiagnosticsLogging,
129    ) -> Self {
130        let scrollable_overflow = fragment_tree.scrollable_overflow();
131        let scroll_area = scrollable_overflow.union(&fragment_tree.initial_containing_block);
132        let scroll_area = LayoutSize::from_untyped(Size2D::new(
133            scroll_area.size.width.to_f32_px(),
134            scroll_area.size.height.to_f32_px(),
135        ));
136
137        let viewport_size = viewport_details.layout_size();
138        let paint_info = PaintDisplayListInfo::new(
139            viewport_details,
140            scroll_area,
141            pipeline_id,
142            // This epoch is set when the WebRender display list is built. For now use a dummy value.
143            Default::default(),
144            fragment_tree.viewport_scroll_sensitivity,
145            first_reflow,
146        );
147
148        let root_scroll_node_id = paint_info.root_scroll_node_id;
149        let cb_for_non_fixed_descendants = ContainingBlock::new(
150            fragment_tree.initial_containing_block,
151            root_scroll_node_id,
152            Some(viewport_size),
153            ClipId::INVALID,
154            PhysicalVec::zero(),
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            PhysicalVec::zero(),
162        );
163
164        // We need to specify all three containing blocks here, because absolute
165        // descendants of the root cannot share the containing block we specify
166        // for fixed descendants. In this case, they need to have the spatial
167        // id of the root scroll frame, whereas fixed descendants need the
168        // spatial id of the root reference frame so that they do not scroll with
169        // page content.
170        let containing_block_info = ContainingBlockInfo {
171            for_non_absolute_descendants: &cb_for_non_fixed_descendants,
172            for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
173            for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
174        };
175
176        let mut stacking_context_tree = Self {
177            // This is just a temporary value that will be replaced once we have finished
178            // building the tree.
179            root_stacking_context: StackingContext::root(root_scroll_node_id),
180            paint_info,
181            clip_store: Default::default(),
182        };
183
184        let text_decorations = Default::default();
185        let mut root_stacking_context = StackingContext::root(root_scroll_node_id);
186        if let Some(root_box_fragment) = fragment_tree.root_box_fragment() {
187            Fragment::Box(root_box_fragment).build_stacking_context_tree(
188                &mut stacking_context_tree,
189                &containing_block_info,
190                &mut root_stacking_context,
191                // The root element might be absolutely positioned and we still want
192                // to process it, if it is.
193                StackingContextBuildMode::IncludeHoisted,
194                &text_decorations,
195            );
196        }
197
198        root_stacking_context.sort();
199        stacking_context_tree.root_stacking_context = root_stacking_context;
200
201        if debug.is_enabled(DiagnosticsLoggingOption::StackingContextTree) {
202            stacking_context_tree
203                .root_stacking_context
204                .print(&mut PrintTree::new("Stacking Context Tree"));
205        }
206
207        stacking_context_tree
208    }
209
210    fn push_reference_frame(
211        &mut self,
212        origin: LayoutPoint,
213        frame_origin_for_query: LayoutPoint,
214        parent_scroll_node_id: ScrollTreeNodeId,
215        transform_style: wr::TransformStyle,
216        transform: LayoutTransform,
217        kind: wr::ReferenceFrameKind,
218    ) -> ScrollTreeNodeId {
219        self.paint_info.scroll_tree.add_scroll_tree_node(
220            Some(parent_scroll_node_id),
221            SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
222                origin,
223                frame_origin_for_query,
224                transform_style,
225                transform: transform.into(),
226                kind,
227            }),
228        )
229    }
230
231    fn define_scroll_frame(
232        &mut self,
233        parent_scroll_node_id: ScrollTreeNodeId,
234        external_id: wr::ExternalScrollId,
235        content_rect: LayoutRect,
236        clip_rect: LayoutRect,
237        scroll_sensitivity: AxesScrollSensitivity,
238    ) -> ScrollTreeNodeId {
239        self.paint_info.scroll_tree.add_scroll_tree_node(
240            Some(parent_scroll_node_id),
241            SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
242                external_id,
243                content_rect,
244                clip_rect,
245                scroll_sensitivity,
246                offset: LayoutVector2D::zero(),
247                offset_changed: Cell::new(false),
248            }),
249        )
250    }
251
252    fn define_sticky_frame(
253        &mut self,
254        parent_scroll_node_id: ScrollTreeNodeId,
255        frame_rect: LayoutRect,
256        margins: SideOffsets2D<Option<f32>, LayoutPixel>,
257        vertical_offset_bounds: StickyOffsetBounds,
258        horizontal_offset_bounds: StickyOffsetBounds,
259    ) -> ScrollTreeNodeId {
260        self.paint_info.scroll_tree.add_scroll_tree_node(
261            Some(parent_scroll_node_id),
262            SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
263                frame_rect,
264                margins,
265                vertical_offset_bounds,
266                horizontal_offset_bounds,
267            }),
268        )
269    }
270
271    /// Given a [`Fragment`] and a point in the viewport of the page, return the point in
272    /// the [`Fragment`]'s content rectangle in its transformed coordinate system
273    /// (untransformed CSS pixels). Note that the point may be outside the [`Fragment`]'s
274    /// boundaries.
275    ///
276    /// TODO: Currently, this only works for [`BoxFragment`], but we should extend it to
277    /// other types of [`Fragment`]s in the future.
278    pub(crate) fn offset_in_fragment(
279        &self,
280        fragment: &Fragment,
281        point_in_viewport: PhysicalPoint<Au>,
282    ) -> Option<Point2D<Au, CSSPixel>> {
283        let fragment = fragment.retrieve_box_fragment()?;
284        let spatial_tree_node = fragment.spatial_tree_node()?;
285        let transform = self
286            .paint_info
287            .scroll_tree
288            .cumulative_root_to_node_transform(spatial_tree_node)?;
289        let transformed_point = transform
290            .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
291            .map(Au::from_f32_px)
292            .cast_unit();
293
294        // Find the origin of the fragment relative to its reference frame in the same coordinate system.
295        let reference_frame_origin = self
296            .paint_info
297            .scroll_tree
298            .reference_frame_offset(spatial_tree_node)
299            .map(Au::from_f32_px);
300        let fragment_origin = fragment
301            .cumulative_content_box_rect(
302                ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
303            )
304            .origin -
305            reference_frame_origin.cast_unit();
306
307        // Use that to find the offset from the fragment origin.
308        Some(transformed_point - fragment_origin)
309    }
310}
311
312/// The text decorations for a Fragment, collecting during [`StackingContextTree`] construction.
313#[derive(Clone, Debug, MallocSizeOf)]
314pub(crate) struct FragmentTextDecoration {
315    pub line: TextDecorationLine,
316    pub color: AbsoluteColor,
317    pub style: TextDecorationStyle,
318}
319
320#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
321pub(crate) enum StackingContextType {
322    StackingContext,
323    StackingContainer,
324}
325
326#[derive(MallocSizeOf)]
327pub enum StackingContextFragments {
328    Root,
329    Fragment(#[conditional_malloc_size_of] Arc<BoxFragment>),
330}
331
332/// Either a stacking context or a stacking container, per the definitions in
333/// <https://drafts.csswg.org/css-position-4/#painting-order>.
334///
335/// We use the term “real stacking context” in situations that call for a stacking context
336/// but not a stacking container. Only positioned stacking containers every get a
337/// `StackingContext`. The rest are handled inline during the `PaintTraversal`.
338#[derive(MallocSizeOf)]
339pub struct StackingContext {
340    /// The [`BoxFragment`] that established this stacking context. This is used to paint this
341    /// [`StackingContext`] and traverse its descendants for painting.
342    ///
343    /// This is `None` for the root stacking context.
344    pub(crate) fragment: StackingContextFragments,
345
346    /// The [`StackingContextType`] of this [`StackingContet`], which determines if it
347    /// a stacking context or a stacking container.
348    pub(crate) context_type: StackingContextType,
349
350    /// Child [`StackingContext`]s of this [`StackingContext`].
351    pub(crate) children: Vec<StackingContext>,
352
353    /// The offset of the containing block, used to properly paint child fragments of this
354    /// stacking context or stacking container.
355    pub(crate) containing_block_origin: PhysicalPoint<Au>,
356
357    /// The spatial id of this [`StackingContext`].
358    pub(crate) scroll_tree_node_id: ScrollTreeNodeId,
359
360    /// The clip id of this [`StackingContext`] if it has one.
361    pub(crate) clip_id: ClipId,
362
363    /// The z-index of this [`StackingContext`]. Note that `auto` is represented as 0.
364    pub(crate) z_index: i32,
365
366    /// The text decorations that apply to this [`StackingContext`] propagated via the box tree.
367    #[conditional_malloc_size_of]
368    pub(crate) text_decorations: Rc<Vec<FragmentTextDecoration>>,
369}
370
371impl StackingContext {
372    fn root(scroll_tree_node_id: ScrollTreeNodeId) -> Self {
373        Self {
374            fragment: StackingContextFragments::Root,
375            context_type: StackingContextType::StackingContext,
376            children: Default::default(),
377            containing_block_origin: Default::default(),
378            scroll_tree_node_id,
379            clip_id: ClipId::INVALID,
380            z_index: 0,
381            text_decorations: Default::default(),
382        }
383    }
384
385    fn create_descendant(
386        &self,
387        context_type: StackingContextType,
388        containing_block_offset: PhysicalPoint<Au>,
389        spatial_id: ScrollTreeNodeId,
390        clip_id: ClipId,
391        initializing_fragment: Arc<BoxFragment>,
392        text_decorations: Rc<Vec<FragmentTextDecoration>>,
393    ) -> Self {
394        let z_index = initializing_fragment
395            .style()
396            .effective_z_index(initializing_fragment.base.flags);
397        Self {
398            fragment: StackingContextFragments::Fragment(initializing_fragment),
399            context_type,
400            containing_block_origin: containing_block_offset,
401            children: Default::default(),
402            scroll_tree_node_id: spatial_id,
403            clip_id,
404            z_index,
405            text_decorations,
406        }
407    }
408
409    pub(crate) fn fragment(&self) -> Option<&Arc<BoxFragment>> {
410        match &self.fragment {
411            StackingContextFragments::Root => None,
412            StackingContextFragments::Fragment(box_fragment) => Some(box_fragment),
413        }
414    }
415
416    fn sort(&mut self) {
417        self.children.sort_by_key(|child| child.z_index)
418    }
419
420    fn print(&self, tree: &mut PrintTree) {
421        let fragment_string = match &self.fragment {
422            StackingContextFragments::Root => "Root".into(),
423            StackingContextFragments::Fragment(box_fragment) => format!(
424                "{:?} rect={:?}",
425                box_fragment.base.tag,
426                box_fragment.content_rect()
427            ),
428        };
429
430        tree.new_level(format!(
431            "{fragment_string} z-index={:?} spatial={:?} clip={:?}",
432            self.z_index, self.scroll_tree_node_id, self.clip_id
433        ));
434
435        for child in self.children.iter() {
436            child.print(tree);
437        }
438
439        tree.end_level();
440    }
441}
442
443#[derive(Clone, Copy, PartialEq)]
444pub(crate) enum StackingContextBuildMode {
445    IncludeHoisted,
446    SkipHoisted,
447}
448
449impl Fragment {
450    pub(crate) fn build_stacking_context_tree(
451        &self,
452        stacking_context_tree: &mut StackingContextTree,
453        containing_block_info: &ContainingBlockInfo,
454        stacking_context: &mut StackingContext,
455        mode: StackingContextBuildMode,
456        text_decorations: &Rc<Vec<FragmentTextDecoration>>,
457    ) {
458        let containing_block = containing_block_info.get_containing_block_for_fragment(self);
459        let cumulative_containing_block = containing_block
460            .rect
461            .translate(containing_block.accumulated_reference_frame_offset);
462        self.set_containing_block(&cumulative_containing_block);
463
464        if self
465            .base()
466            .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
467        {
468            return;
469        }
470
471        let fragment_clone = self.clone();
472        match self {
473            Fragment::Box(fragment) | Fragment::Float(fragment) => {
474                if mode == StackingContextBuildMode::SkipHoisted &&
475                    fragment.style().clone_position().is_absolutely_positioned()
476                {
477                    return;
478                }
479
480                let text_decorations = match self {
481                    Fragment::Float(..) => &Default::default(),
482                    _ => text_decorations,
483                };
484
485                fragment.build_stacking_context_tree(
486                    fragment_clone,
487                    stacking_context_tree,
488                    containing_block,
489                    containing_block_info,
490                    stacking_context,
491                    text_decorations,
492                );
493            },
494            Fragment::LayoutRoot(..) => {
495                // These fragments are processed at their originating
496                // `Fragment::AbsoluteOrFixedPositionedPlaceholder` position.
497            },
498            Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
499                let shared_fragment = fragment.borrow();
500                let fragment_ref = match shared_fragment.fragment.as_ref() {
501                    Some(fragment_ref) => fragment_ref,
502                    None => unreachable!("Found hoisted box with missing fragment."),
503                };
504
505                fragment_ref.build_stacking_context_tree(
506                    stacking_context_tree,
507                    containing_block_info,
508                    stacking_context,
509                    StackingContextBuildMode::IncludeHoisted,
510                    &Default::default(),
511                );
512            },
513            Fragment::Positioning(fragment) => {
514                fragment.build_stacking_context_tree(
515                    stacking_context_tree,
516                    containing_block,
517                    containing_block_info,
518                    stacking_context,
519                    text_decorations,
520                );
521            },
522            Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {},
523        }
524    }
525}
526
527struct ReferenceFrameData {
528    origin: PhysicalPoint<Au>,
529    transform: LayoutTransform,
530    kind: wr::ReferenceFrameKind,
531}
532struct ScrollFrameData {
533    scroll_tree_node_id: ScrollTreeNodeId,
534    scroll_frame_rect: LayoutRect,
535}
536
537struct OverflowFrameData {
538    clip_id: ClipId,
539    scroll_frame_data: Option<ScrollFrameData>,
540}
541
542impl BoxFragment {
543    pub(crate) fn stacking_context_type(&self) -> Option<StackingContextType> {
544        let flags = self.base.flags;
545        let style = self.style();
546        if style.establishes_stacking_context(flags) {
547            return Some(StackingContextType::StackingContext);
548        }
549
550        let box_style = &style.get_box();
551        if box_style.position != ComputedPosition::Static {
552            return Some(StackingContextType::StackingContainer);
553        }
554
555        None
556    }
557
558    fn build_stacking_context_tree(
559        self: &Arc<Self>,
560        fragment: Fragment,
561        stacking_context_tree: &mut StackingContextTree,
562        containing_block: &ContainingBlock,
563        containing_block_info: &ContainingBlockInfo,
564        parent_stacking_context: &mut StackingContext,
565        text_decorations: &Rc<Vec<FragmentTextDecoration>>,
566    ) {
567        self.build_stacking_context_tree_maybe_creating_reference_frame(
568            fragment,
569            stacking_context_tree,
570            containing_block,
571            containing_block_info,
572            parent_stacking_context,
573            text_decorations,
574        );
575    }
576
577    fn build_stacking_context_tree_maybe_creating_reference_frame(
578        self: &Arc<Self>,
579        fragment: Fragment,
580        stacking_context_tree: &mut StackingContextTree,
581        containing_block: &ContainingBlock,
582        containing_block_info: &ContainingBlockInfo,
583        parent_stacking_context: &mut StackingContext,
584        text_decorations: &Rc<Vec<FragmentTextDecoration>>,
585    ) {
586        let reference_frame_data =
587            match self.reference_frame_data_if_necessary(&containing_block.rect) {
588                Some(reference_frame_data) => reference_frame_data,
589                None => {
590                    return self.build_stacking_context_tree_maybe_creating_stacking_context(
591                        fragment,
592                        stacking_context_tree,
593                        containing_block,
594                        containing_block_info,
595                        parent_stacking_context,
596                        text_decorations,
597                    );
598                },
599            };
600
601        // <https://drafts.csswg.org/css-transforms/#transform-function-lists>
602        // > If a transform function causes the current transformation matrix of an object
603        // > to be non-invertible, the object and its content do not get displayed.
604        if !reference_frame_data.transform.is_invertible() {
605            self.clear_spatial_tree_node_including_descendants();
606            return;
607        }
608
609        let style = self.style();
610        let frame_origin_for_query = self
611            .cumulative_border_box_rect(
612                ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
613            )
614            .origin
615            .to_webrender();
616        let new_spatial_id = stacking_context_tree.push_reference_frame(
617            reference_frame_data.origin.to_webrender(),
618            frame_origin_for_query,
619            containing_block.scroll_node_id,
620            style.used_transform_style(self.base.flags).to_webrender(),
621            reference_frame_data.transform,
622            reference_frame_data.kind,
623        );
624
625        // WebRender reference frames establish a new coordinate system at their
626        // origin (the border box of the fragment). We need to ensure that any
627        // coordinates we give to WebRender in this reference frame are relative
628        // to the fragment border box. We do this by adjusting the containing
629        // block origin. Note that the `for_absolute_descendants` and
630        // `for_all_absolute_and_fixed_descendants` properties are now bogus,
631        // but all fragments that establish reference frames also establish
632        // containing blocks for absolute and fixed descendants, so those
633        // properties will be replaced before recursing into children.
634        assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
635        let reference_frame_offset = reference_frame_data.origin.to_vector();
636        let adjusted_containing_block = ContainingBlock::new(
637            containing_block.rect.translate(-reference_frame_offset),
638            new_spatial_id,
639            None,
640            containing_block.clip_id,
641            containing_block.accumulated_reference_frame_offset + reference_frame_offset,
642        );
643        let new_containing_block_info =
644            containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
645
646        self.build_stacking_context_tree_maybe_creating_stacking_context(
647            fragment,
648            stacking_context_tree,
649            &adjusted_containing_block,
650            &new_containing_block_info,
651            parent_stacking_context,
652            text_decorations,
653        );
654    }
655
656    fn build_stacking_context_tree_maybe_creating_stacking_context(
657        self: &Arc<Self>,
658        fragment: Fragment,
659        stacking_context_tree: &mut StackingContextTree,
660        containing_block: &ContainingBlock,
661        containing_block_info: &ContainingBlockInfo,
662        parent_stacking_context: &mut StackingContext,
663        text_decorations: &Rc<Vec<FragmentTextDecoration>>,
664    ) {
665        let with_style = &self.with_style();
666        let style = with_style.style();
667        let Some(stacking_context_type) = self.stacking_context_type() else {
668            with_style.build_stacking_context_tree_for_children(
669                stacking_context_tree,
670                containing_block,
671                containing_block_info,
672                parent_stacking_context,
673                text_decorations,
674            );
675            return;
676        };
677
678        let new_scroll_frame_size = containing_block_info
679            .for_non_absolute_descendants
680            .scroll_frame_size;
681        let spatial_id = self.build_sticky_frame_if_necessary(
682            stacking_context_tree,
683            containing_block.scroll_node_id,
684            &containing_block.rect,
685            &new_scroll_frame_size,
686        );
687
688        let clip_id = with_style.build_clip_frame_if_necessary(
689            stacking_context_tree,
690            spatial_id.unwrap_or(containing_block.scroll_node_id),
691            containing_block.clip_id,
692            &containing_block.rect,
693        );
694
695        let clip_id = stacking_context_tree
696            .clip_store
697            .add_for_clip_path(
698                &style.get_svg().clip_path,
699                spatial_id.unwrap_or(containing_block.scroll_node_id),
700                clip_id.unwrap_or(containing_block.clip_id),
701                with_style,
702                containing_block.rect.origin,
703            )
704            .or(clip_id);
705
706        let containing_block = if clip_id.is_some() || spatial_id.is_some() {
707            if let Some(clip_id) = clip_id {
708                self.set_generated_clip_id(clip_id);
709            }
710            if let Some(spatial_id) = spatial_id {
711                self.set_generated_scroll_tree_node_id(spatial_id);
712            }
713            &ContainingBlock {
714                scroll_node_id: spatial_id.unwrap_or(containing_block.scroll_node_id),
715                clip_id: clip_id.unwrap_or(containing_block.clip_id),
716                ..*containing_block
717            }
718        } else {
719            containing_block
720        };
721
722        let box_fragment = fragment
723            .retrieve_box_fragment()
724            .expect("Should never try to make stacking context for non-BoxFragment")
725            .clone();
726        let mut child_stacking_context = parent_stacking_context.create_descendant(
727            stacking_context_type,
728            containing_block.rect.origin,
729            containing_block.scroll_node_id,
730            containing_block.clip_id,
731            box_fragment,
732            text_decorations.clone(),
733        );
734        with_style.build_stacking_context_tree_for_children(
735            stacking_context_tree,
736            containing_block,
737            containing_block_info,
738            &mut child_stacking_context,
739            text_decorations,
740        );
741
742        let mut stolen_children = vec![];
743        if stacking_context_type != StackingContextType::StackingContext {
744            stolen_children =
745                std::mem::replace(&mut child_stacking_context.children, stolen_children);
746        } else {
747            child_stacking_context.sort();
748        }
749
750        parent_stacking_context
751            .children
752            .push(child_stacking_context);
753        parent_stacking_context
754            .children
755            .append(&mut stolen_children);
756    }
757}
758
759impl BoxFragmentWithStyle<'_> {
760    fn build_stacking_context_tree_for_children(
761        &self,
762        stacking_context_tree: &mut StackingContextTree,
763        containing_block: &ContainingBlock,
764        containing_block_info: &ContainingBlockInfo,
765        stacking_context: &mut StackingContext,
766        text_decorations: &Rc<Vec<FragmentTextDecoration>>,
767    ) {
768        let style = self.style();
769        let establishes_containing_block_for_all_descendants =
770            style.establishes_containing_block_for_all_descendants(self.base.flags);
771        let establishes_containing_block_for_absolute_descendants =
772            style.establishes_containing_block_for_absolute_descendants(self.base.flags);
773
774        let mut new_scroll_node_id = containing_block.scroll_node_id;
775        self.spatial_tree_node.set(Some(new_scroll_node_id));
776
777        // We want to build the scroll frame after the background and border, because
778        // they shouldn't scroll with the rest of the box content.
779        let mut new_scroll_frame_size = containing_block_info
780            .for_non_absolute_descendants
781            .scroll_frame_size;
782        let mut new_clip_id = containing_block.clip_id;
783        if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
784            stacking_context_tree,
785            new_scroll_node_id,
786            new_clip_id,
787            &containing_block.rect,
788        ) {
789            new_clip_id = overflow_frame_data.clip_id;
790            self.set_generated_clip_id(new_clip_id);
791
792            if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
793                new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
794                new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
795                self.set_generated_scroll_tree_node_id(new_scroll_node_id);
796            }
797        }
798
799        let padding_rect = self
800            .padding_rect()
801            .translate(containing_block.rect.origin.to_vector());
802        let content_rect = self
803            .content_rect()
804            .translate(containing_block.rect.origin.to_vector());
805
806        let for_absolute_descendants = ContainingBlock::new(
807            padding_rect,
808            new_scroll_node_id,
809            new_scroll_frame_size,
810            new_clip_id,
811            containing_block.accumulated_reference_frame_offset,
812        );
813        let for_non_absolute_descendants = ContainingBlock::new(
814            content_rect,
815            new_scroll_node_id,
816            new_scroll_frame_size,
817            new_clip_id,
818            containing_block.accumulated_reference_frame_offset,
819        );
820
821        // Create a new `ContainingBlockInfo` for descendants depending on
822        // whether or not this fragment establishes a containing block for
823        // absolute and fixed descendants.
824        let new_containing_block_info = if establishes_containing_block_for_all_descendants {
825            containing_block_info.new_for_absolute_and_fixed_descendants(
826                &for_non_absolute_descendants,
827                &for_absolute_descendants,
828            )
829        } else if establishes_containing_block_for_absolute_descendants {
830            containing_block_info.new_for_absolute_descendants(
831                &for_non_absolute_descendants,
832                &for_absolute_descendants,
833            )
834        } else {
835            containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
836        };
837
838        // Text decorations are not propagated to atomic inline-level descendants.
839        // From https://drafts.csswg.org/css2/#lining-striking-props:
840        // > Note that text decorations are not propagated to floating and absolutely
841        // > positioned descendants, nor to the contents of atomic inline-level descendants
842        // > such as inline blocks and inline tables.
843        let text_decorations = match self.is_atomic_inline_level() ||
844            self.base
845                .flags
846                .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
847        {
848            true => &Default::default(),
849            false => text_decorations,
850        };
851
852        let new_text_decoration;
853        let text_decorations = match style.clone_text_decoration_line() {
854            TextDecorationLine::NONE => text_decorations,
855            line => {
856                let mut new_vector = (**text_decorations).clone();
857                let color = &style.get_inherited_text().color;
858                new_vector.push(FragmentTextDecoration {
859                    line,
860                    color: style
861                        .clone_text_decoration_color()
862                        .resolve_to_absolute(color),
863                    style: style.clone_text_decoration_style(),
864                });
865                new_text_decoration = Rc::new(new_vector);
866                &new_text_decoration
867            },
868        };
869
870        for child in &self.children {
871            child.build_stacking_context_tree(
872                stacking_context_tree,
873                &new_containing_block_info,
874                stacking_context,
875                StackingContextBuildMode::SkipHoisted,
876                text_decorations,
877            );
878        }
879    }
880
881    fn build_clip_frame_if_necessary(
882        &self,
883        stacking_context_tree: &mut StackingContextTree,
884        parent_scroll_node_id: ScrollTreeNodeId,
885        parent_clip_id: ClipId,
886        containing_block_rect: &PhysicalRect<Au>,
887    ) -> Option<ClipId> {
888        let style = self.style();
889        let position = style.get_box().position;
890        // https://drafts.csswg.org/css2/#clipping
891        // The clip property applies only to absolutely positioned elements
892        if !position.is_absolutely_positioned() {
893            return None;
894        }
895
896        // Only rectangles are supported for now.
897        let clip_rect = match style.get_effects().clip {
898            ClipRectOrAuto::Rect(rect) => rect,
899            _ => return None,
900        };
901
902        let border_rect = self.border_rect();
903        let clip_rect = clip_rect
904            .for_border_rect(border_rect)
905            .translate(containing_block_rect.origin.to_vector())
906            .to_webrender();
907        Some(stacking_context_tree.clip_store.add(
908            BorderRadius::zero(),
909            clip_rect,
910            parent_scroll_node_id,
911            parent_clip_id,
912        ))
913    }
914
915    fn build_overflow_frame_if_necessary(
916        &self,
917        stacking_context_tree: &mut StackingContextTree,
918        parent_scroll_node_id: ScrollTreeNodeId,
919        parent_clip_id: ClipId,
920        containing_block_rect: &PhysicalRect<Au>,
921    ) -> Option<OverflowFrameData> {
922        let style = self.style();
923        let overflow = style.effective_overflow(self.base.flags);
924
925        if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
926            return None;
927        }
928
929        // Non-scrollable overflow path
930        if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
931            let overflow_clip_margin = style.get_margin().overflow_clip_margin;
932            let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
933                OverflowClipMarginBox::ContentBox => self.content_rect(),
934                OverflowClipMarginBox::PaddingBox => self.padding_rect(),
935                OverflowClipMarginBox::BorderBox => self.border_rect(),
936            }
937            .translate(containing_block_rect.origin.to_vector())
938            .to_webrender();
939
940            // Adjust by the overflow clip margin.
941            // https://drafts.csswg.org/css-overflow-3/#overflow-clip-margin
942            let clip_margin_offset = overflow_clip_margin.offset.px();
943            overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
944
945            // The clipping region only gets rounded corners if both axes have `overflow: clip`.
946            // https://drafts.csswg.org/css-overflow-3/#corner-clipping
947            let radii;
948            if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
949                let builder = BuilderForBoxFragment::new(self, containing_block_rect.origin);
950                let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
951                match overflow_clip_margin.visual_box {
952                    OverflowClipMarginBox::ContentBox => {
953                        offsets_from_border -= (self.border + self.padding).to_webrender();
954                    },
955                    OverflowClipMarginBox::PaddingBox => {
956                        offsets_from_border -= self.border.to_webrender();
957                    },
958                    OverflowClipMarginBox::BorderBox => {},
959                };
960                radii = offset_radii(builder.border_radius(), offsets_from_border);
961            } else if overflow.x != ComputedOverflow::Clip {
962                let max = LayoutRect::max_rect();
963                overflow_clip_rect.min.x = max.min.x;
964                overflow_clip_rect.max.x = max.max.x;
965                radii = BorderRadius::zero();
966            } else {
967                let max = LayoutRect::max_rect();
968                overflow_clip_rect.min.y = max.min.y;
969                overflow_clip_rect.max.y = max.max.y;
970                radii = BorderRadius::zero();
971            }
972
973            let clip_id = stacking_context_tree.clip_store.add(
974                radii,
975                overflow_clip_rect,
976                parent_scroll_node_id,
977                parent_clip_id,
978            );
979
980            return Some(OverflowFrameData {
981                clip_id,
982                scroll_frame_data: None,
983            });
984        }
985
986        let scroll_frame_rect = self
987            .padding_rect()
988            .translate(containing_block_rect.origin.to_vector())
989            .to_webrender();
990
991        let clip_id = stacking_context_tree.clip_store.add(
992            BuilderForBoxFragment::new(self, containing_block_rect.origin).border_radius(),
993            scroll_frame_rect,
994            parent_scroll_node_id,
995            parent_clip_id,
996        );
997
998        let tag = self.base.tag?;
999        let external_scroll_id = wr::ExternalScrollId(
1000            tag.to_display_list_fragment_id(),
1001            stacking_context_tree.paint_info.pipeline_id,
1002        );
1003
1004        let sensitivity = AxesScrollSensitivity {
1005            x: overflow.x.into(),
1006            y: overflow.y.into(),
1007        };
1008
1009        let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1010            parent_scroll_node_id,
1011            external_scroll_id,
1012            self.scrollable_overflow().to_webrender(),
1013            scroll_frame_rect,
1014            sensitivity,
1015        );
1016
1017        Some(OverflowFrameData {
1018            clip_id,
1019            scroll_frame_data: Some(ScrollFrameData {
1020                scroll_tree_node_id,
1021                scroll_frame_rect,
1022            }),
1023        })
1024    }
1025}
1026
1027impl BoxFragment {
1028    fn build_sticky_frame_if_necessary(
1029        &self,
1030        stacking_context_tree: &mut StackingContextTree,
1031        parent_scroll_node_id: ScrollTreeNodeId,
1032        containing_block_rect: &PhysicalRect<Au>,
1033        scroll_frame_size: &Option<LayoutSize>,
1034    ) -> Option<ScrollTreeNodeId> {
1035        let style = self.style();
1036        if style.get_box().position != ComputedPosition::Sticky {
1037            return None;
1038        }
1039
1040        let scroll_frame_size_for_resolve = match scroll_frame_size {
1041            Some(size) => size,
1042            None => {
1043                // This is a direct descendant of a reference frame.
1044                &stacking_context_tree
1045                    .paint_info
1046                    .viewport_details
1047                    .layout_size()
1048            },
1049        };
1050
1051        // Percentages sticky positions offsets are resovled against the size of the
1052        // nearest scroll frame instead of the containing block like for other types
1053        // of positioning.
1054        let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1055        let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1056        let offsets = style.physical_box_offsets();
1057        let offsets = PhysicalSides::<AuOrAuto>::new(
1058            offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1059            offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1060            offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1061            offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1062        );
1063        self.set_resolved_sticky_insets(offsets);
1064
1065        if scroll_frame_size.is_none() {
1066            return None;
1067        }
1068
1069        if offsets.top.is_auto() &&
1070            offsets.right.is_auto() &&
1071            offsets.bottom.is_auto() &&
1072            offsets.left.is_auto()
1073        {
1074            return None;
1075        }
1076
1077        // https://drafts.csswg.org/css-position/#stickypos-insets
1078        // > For each side of the box, if the corresponding inset property is not `auto`, and the
1079        // > corresponding border edge of the box would be outside the corresponding edge of the
1080        // > sticky view rectangle, the box must be visually shifted (as for relative positioning)
1081        // > to be inward of that sticky view rectangle edge, insofar as it can while its position
1082        // > box remains contained within its containing block.
1083        // > The *position box* is its margin box, except that for any side for which the distance
1084        // > between its margin edge and the corresponding edge of its containing block is less
1085        // > than its corresponding margin, that distance is used in place of that margin.
1086        //
1087        // Amendments:
1088        // - Using the "margin edge" seems nonsensical, the spec must mean "border edge" instead:
1089        //   https://github.com/w3c/csswg-drafts/issues/12833
1090        // - `auto` margins need to be treated as zero:
1091        //   https://github.com/w3c/csswg-drafts/issues/12852
1092        //
1093        // We implement this by enforcing a minimum negative offset and a maximum positive offset.
1094        // The logic below is a simplified (but equivalent) version of the description above.
1095        let border_rect = self.border_rect();
1096        let computed_margin = style.physical_margin();
1097
1098        // Signed distance between each side of the border box to the corresponding side of the
1099        // containing block. Note that |border_rect| is already in the coordinate system of the
1100        // containing block.
1101        let distance_from_border_box_to_cb = PhysicalSides::new(
1102            border_rect.min_y(),
1103            containing_block_rect.width() - border_rect.max_x(),
1104            containing_block_rect.height() - border_rect.max_y(),
1105            border_rect.min_x(),
1106        );
1107
1108        // Shrinks the signed distance by the margin, producing a limit on how much we can shift
1109        // the sticky positioned box without forcing the margin to move outside of the containing
1110        // block.
1111        let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1112            let used_margin = if computed_margin.is_auto() {
1113                Au::zero()
1114            } else {
1115                used_margin
1116            };
1117            Au::zero().max(distance - used_margin).to_f32_px()
1118        };
1119
1120        // This is the minimum negative offset and then the maximum positive offset. We specify
1121        // all sides, but they will have no effect if the corresponding inset property is `auto`.
1122        let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1123            -offset_bound(
1124                distance_from_border_box_to_cb.top,
1125                self.margin.top,
1126                computed_margin.top,
1127            ),
1128            offset_bound(
1129                distance_from_border_box_to_cb.bottom,
1130                self.margin.bottom,
1131                computed_margin.bottom,
1132            ),
1133        );
1134        let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1135            -offset_bound(
1136                distance_from_border_box_to_cb.left,
1137                self.margin.left,
1138                computed_margin.left,
1139            ),
1140            offset_bound(
1141                distance_from_border_box_to_cb.right,
1142                self.margin.right,
1143                computed_margin.right,
1144            ),
1145        );
1146
1147        let frame_rect = border_rect
1148            .translate(containing_block_rect.origin.to_vector())
1149            .to_webrender();
1150
1151        // These are the "margins" between the scrollport and |frame_rect|. They are not the same
1152        // as CSS margins.
1153        let margins = SideOffsets2D::new(
1154            offsets.top.non_auto().map(|v| v.to_f32_px()),
1155            offsets.right.non_auto().map(|v| v.to_f32_px()),
1156            offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1157            offsets.left.non_auto().map(|v| v.to_f32_px()),
1158        );
1159
1160        let sticky_node_id = stacking_context_tree.define_sticky_frame(
1161            parent_scroll_node_id,
1162            frame_rect,
1163            margins,
1164            vertical_offset_bounds,
1165            horizontal_offset_bounds,
1166        );
1167
1168        Some(sticky_node_id)
1169    }
1170
1171    /// Optionally returns the data for building a reference frame, without yet building it.
1172    fn reference_frame_data_if_necessary(
1173        &self,
1174        containing_block_rect: &PhysicalRect<Au>,
1175    ) -> Option<ReferenceFrameData> {
1176        if !self
1177            .style()
1178            .has_effective_transform_or_perspective(self.base.flags)
1179        {
1180            return None;
1181        }
1182
1183        let relative_border_rect = self.border_rect();
1184        let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1185        let transform = self.calculate_transform_matrix(&border_rect);
1186        let perspective = self.calculate_perspective_matrix(&border_rect);
1187        let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1188            (None, Some(perspective)) => (
1189                perspective,
1190                wr::ReferenceFrameKind::Perspective {
1191                    scrolling_relative_to: None,
1192                },
1193            ),
1194            (Some(transform), None) => (
1195                transform,
1196                wr::ReferenceFrameKind::Transform {
1197                    is_2d_scale_translation: false,
1198                    should_snap: false,
1199                    paired_with_perspective: false,
1200                },
1201            ),
1202            (Some(transform), Some(perspective)) => (
1203                perspective.then(&transform),
1204                wr::ReferenceFrameKind::Perspective {
1205                    scrolling_relative_to: None,
1206                },
1207            ),
1208            (None, None) => unreachable!(),
1209        };
1210
1211        Some(ReferenceFrameData {
1212            origin: border_rect.origin,
1213            transform: reference_frame_transform,
1214            kind: reference_frame_kind,
1215        })
1216    }
1217
1218    /// Returns the 4D matrix representing this fragment's transform.
1219    pub fn calculate_transform_matrix(
1220        &self,
1221        border_rect: &Rect<Au, CSSPixel>,
1222    ) -> Option<LayoutTransform> {
1223        let style = self.style();
1224        let list = &style.get_box().transform;
1225        let length_rect = au_rect_to_length_rect(border_rect);
1226        // https://drafts.csswg.org/css-transforms-2/#individual-transforms
1227        let rotate = match style.clone_rotate() {
1228            GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1229            GenericRotate::Rotate3D(x, y, z, angle) => {
1230                // These are the raw, unormalized values from CSS, but euclid expects
1231                // rotation input to be normalized, so we must do that first.
1232                get_normalized_vector_and_angle(x, y, z, angle)
1233            },
1234            GenericRotate::None => (0., 0., 1., Angle::zero()),
1235        };
1236        let scale = match style.clone_scale() {
1237            GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1238            GenericScale::None => (1., 1., 1.),
1239        };
1240        let translation = match style.clone_translate() {
1241            GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1242                x.resolve(length_rect.size.width).px(),
1243                y.resolve(length_rect.size.height).px(),
1244                z.px(),
1245            ),
1246            GenericTranslate::None => LayoutTransform::identity(),
1247        };
1248
1249        let angle = euclid::Angle::radians(rotate.3.radians());
1250        let transform_base = list
1251            .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1252            .ok()?;
1253        let transform = LayoutTransform::from_untyped(&transform_base.0)
1254            .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1255            .then_scale(scale.0, scale.1, scale.2)
1256            .then(&translation);
1257
1258        let transform_origin = &style.get_box().transform_origin;
1259        let transform_origin_x = transform_origin
1260            .horizontal
1261            .to_used_value(border_rect.size.width)
1262            .to_f32_px();
1263        let transform_origin_y = transform_origin
1264            .vertical
1265            .to_used_value(border_rect.size.height)
1266            .to_f32_px();
1267        let transform_origin_z = transform_origin.depth.px();
1268
1269        Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1270    }
1271
1272    /// Returns the 4D matrix representing this fragment's perspective.
1273    pub fn calculate_perspective_matrix(
1274        &self,
1275        border_rect: &Rect<Au, CSSPixel>,
1276    ) -> Option<LayoutTransform> {
1277        let style = self.style();
1278        match style.get_box().perspective {
1279            Perspective::Length(length) => {
1280                let perspective_origin = &style.get_box().perspective_origin;
1281                let perspective_origin = LayoutPoint::new(
1282                    perspective_origin
1283                        .horizontal
1284                        .percentage_relative_to(border_rect.size.width.into())
1285                        .px(),
1286                    perspective_origin
1287                        .vertical
1288                        .percentage_relative_to(border_rect.size.height.into())
1289                        .px(),
1290                );
1291
1292                let perspective_matrix = LayoutTransform::from_untyped(
1293                    &transform::create_perspective_matrix(length.px()),
1294                );
1295
1296                Some(perspective_matrix.change_basis(
1297                    perspective_origin.x,
1298                    perspective_origin.y,
1299                    0.0,
1300                ))
1301            },
1302            Perspective::None => None,
1303        }
1304    }
1305
1306    fn clear_spatial_tree_node_including_descendants(&self) {
1307        fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1308            for fragment in fragments.iter() {
1309                match fragment {
1310                    Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
1311                        .inner_box_fragment()
1312                        .clear_spatial_tree_node_including_descendants(),
1313                    Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1314                        box_fragment.clear_spatial_tree_node_including_descendants();
1315                    },
1316                    Fragment::Positioning(positioning_fragment) => {
1317                        assign_spatial_tree_node_on_fragments(&positioning_fragment.children);
1318                    },
1319                    _ => {},
1320                }
1321            }
1322        }
1323
1324        self.spatial_tree_node.set(None);
1325        assign_spatial_tree_node_on_fragments(&self.children);
1326    }
1327}
1328
1329impl PositioningFragment {
1330    fn build_stacking_context_tree(
1331        &self,
1332        stacking_context_tree: &mut StackingContextTree,
1333        containing_block: &ContainingBlock,
1334        containing_block_info: &ContainingBlockInfo,
1335        stacking_context: &mut StackingContext,
1336        text_decorations: &Rc<Vec<FragmentTextDecoration>>,
1337    ) {
1338        let rect = self
1339            .base
1340            .rect()
1341            .translate(containing_block.rect.origin.to_vector());
1342        let new_containing_block = containing_block.new_replacing_rect(&rect);
1343        let new_containing_block_info =
1344            containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1345
1346        for child in &self.children {
1347            child.build_stacking_context_tree(
1348                stacking_context_tree,
1349                &new_containing_block_info,
1350                stacking_context,
1351                StackingContextBuildMode::SkipHoisted,
1352                text_decorations,
1353            );
1354        }
1355    }
1356}
1357
1358pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1359    Rect::new(
1360        Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1361        Size2D::new(rect.size.width.into(), rect.size.height.into()),
1362    )
1363}