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