1use std::cell::{Cell, RefCell};
6use std::mem;
7use std::sync::Arc;
8
9use app_units::Au;
10use embedder_traits::ViewportDetails;
11use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
12use log::warn;
13use malloc_size_of_derive::MallocSizeOf;
14use paint_api::display_list::{
15 AxesScrollSensitivity, PaintDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
16 SpatialTreeNodeInfo, StickyNodeInfo,
17};
18use servo_base::id::ScrollTreeNodeId;
19use servo_base::print_tree::PrintTree;
20use servo_config::opts::{DiagnosticsLogging, DiagnosticsLoggingOption};
21use servo_geometry::MaxRect;
22use style::Zero;
23use style::color::{AbsoluteColor, ColorSpace};
24use style::computed_values::float::T as ComputedFloat;
25use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
26use style::computed_values::overflow_x::T as ComputedOverflow;
27use style::computed_values::position::T as ComputedPosition;
28use style::computed_values::text_decoration_style::T as TextDecorationStyle;
29use style::values::computed::angle::Angle;
30use style::values::computed::basic_shape::ClipPath;
31use style::values::computed::{ClipRectOrAuto, Length, TextDecorationLine};
32use style::values::generics::box_::{OverflowClipMarginBox, Perspective};
33use style::values::generics::transform::{self, GenericRotate, GenericScale, GenericTranslate};
34use style::values::specified::TransformStyle;
35use style::values::specified::box_::DisplayOutside;
36use style_traits::CSSPixel;
37use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
38use webrender_api::{self as wr, BorderRadius};
39use wr::StickyOffsetBounds;
40use wr::units::{LayoutPixel, LayoutSize};
41
42use super::ClipId;
43use super::clip::StackingContextTreeClipStore;
44use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
45use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii};
46use crate::fragment_tree::{
47 BoxFragment, ContainingBlockCalculation, ContainingBlockManager, Fragment, FragmentFlags,
48 FragmentTree, PositioningFragment, SpecificLayoutInfo,
49};
50use crate::geom::{
51 AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalVec,
52};
53use crate::style_ext::{ComputedValuesExt, TransformExt};
54
55#[derive(Clone)]
56pub(crate) struct ContainingBlock {
57 scroll_node_id: ScrollTreeNodeId,
60
61 scroll_frame_size: Option<LayoutSize>,
65
66 clip_id: ClipId,
68
69 rect: PhysicalRect<Au>,
71
72 accumulated_reference_frame_offset: PhysicalVec<Au>,
78}
79
80impl ContainingBlock {
81 pub(crate) fn new(
82 rect: PhysicalRect<Au>,
83 scroll_node_id: ScrollTreeNodeId,
84 scroll_frame_size: Option<LayoutSize>,
85 clip_id: ClipId,
86 accumulated_reference_frame_offset: PhysicalVec<Au>,
87 ) -> Self {
88 ContainingBlock {
89 scroll_node_id,
90 scroll_frame_size,
91 clip_id,
92 rect,
93 accumulated_reference_frame_offset,
94 }
95 }
96
97 pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Au>) -> Self {
98 ContainingBlock {
99 rect: *rect,
100 ..*self
101 }
102 }
103}
104
105pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
106
107#[derive(Clone, Copy, Debug, Eq, Ord, MallocSizeOf, PartialEq, PartialOrd)]
108pub(crate) enum StackingContextSection {
109 OwnBackgroundsAndBorders,
110 DescendantBackgroundsAndBorders,
111 Foreground,
112 Outline,
113}
114
115#[derive(MallocSizeOf)]
116pub(crate) struct StackingContextTree {
117 pub root_stacking_context: StackingContext,
119
120 pub paint_info: PaintDisplayListInfo,
125
126 pub clip_store: StackingContextTreeClipStore,
130}
131
132impl StackingContextTree {
133 pub fn new(
136 fragment_tree: &FragmentTree,
137 viewport_details: ViewportDetails,
138 pipeline_id: wr::PipelineId,
139 first_reflow: bool,
140 debug: &DiagnosticsLogging,
141 ) -> Self {
142 let scrollable_overflow = fragment_tree.scrollable_overflow();
143 let scrollable_overflow = LayoutSize::from_untyped(Size2D::new(
144 scrollable_overflow.size.width.to_f32_px(),
145 scrollable_overflow.size.height.to_f32_px(),
146 ));
147
148 let viewport_size = viewport_details.layout_size();
149 let paint_info = PaintDisplayListInfo::new(
150 viewport_details,
151 scrollable_overflow,
152 pipeline_id,
153 Default::default(),
155 fragment_tree.viewport_scroll_sensitivity,
156 first_reflow,
157 );
158
159 let root_scroll_node_id = paint_info.root_scroll_node_id;
160 let cb_for_non_fixed_descendants = ContainingBlock::new(
161 fragment_tree.initial_containing_block,
162 root_scroll_node_id,
163 Some(viewport_size),
164 ClipId::INVALID,
165 PhysicalVec::zero(),
166 );
167 let cb_for_fixed_descendants = ContainingBlock::new(
168 fragment_tree.initial_containing_block,
169 paint_info.root_reference_frame_id,
170 None,
171 ClipId::INVALID,
172 PhysicalVec::zero(),
173 );
174
175 let containing_block_info = ContainingBlockInfo {
182 for_non_absolute_descendants: &cb_for_non_fixed_descendants,
183 for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
184 for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
185 };
186
187 let mut stacking_context_tree = Self {
188 root_stacking_context: StackingContext::create_root(root_scroll_node_id, debug),
190 paint_info,
191 clip_store: Default::default(),
192 };
193
194 let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug);
195 let text_decorations = Default::default();
196 for fragment in &fragment_tree.root_fragments {
197 fragment.build_stacking_context_tree(
198 &mut stacking_context_tree,
199 &containing_block_info,
200 &mut root_stacking_context,
201 StackingContextBuildMode::SkipHoisted,
202 &text_decorations,
203 );
204 }
205 root_stacking_context.sort();
206
207 if debug.is_enabled(DiagnosticsLoggingOption::StackingContextTree) {
208 root_stacking_context.debug_print();
209 }
210
211 stacking_context_tree.root_stacking_context = root_stacking_context;
212
213 stacking_context_tree
214 }
215
216 fn push_reference_frame(
217 &mut self,
218 origin: LayoutPoint,
219 frame_origin_for_query: LayoutPoint,
220 parent_scroll_node_id: ScrollTreeNodeId,
221 transform_style: wr::TransformStyle,
222 transform: LayoutTransform,
223 kind: wr::ReferenceFrameKind,
224 ) -> ScrollTreeNodeId {
225 self.paint_info.scroll_tree.add_scroll_tree_node(
226 Some(parent_scroll_node_id),
227 SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
228 origin,
229 frame_origin_for_query,
230 transform_style,
231 transform: transform.into(),
232 kind,
233 }),
234 )
235 }
236
237 fn define_scroll_frame(
238 &mut self,
239 parent_scroll_node_id: ScrollTreeNodeId,
240 external_id: wr::ExternalScrollId,
241 content_rect: LayoutRect,
242 clip_rect: LayoutRect,
243 scroll_sensitivity: AxesScrollSensitivity,
244 ) -> ScrollTreeNodeId {
245 self.paint_info.scroll_tree.add_scroll_tree_node(
246 Some(parent_scroll_node_id),
247 SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
248 external_id,
249 content_rect,
250 clip_rect,
251 scroll_sensitivity,
252 offset: LayoutVector2D::zero(),
253 offset_changed: Cell::new(false),
254 }),
255 )
256 }
257
258 fn define_sticky_frame(
259 &mut self,
260 parent_scroll_node_id: ScrollTreeNodeId,
261 frame_rect: LayoutRect,
262 margins: SideOffsets2D<Option<f32>, LayoutPixel>,
263 vertical_offset_bounds: StickyOffsetBounds,
264 horizontal_offset_bounds: StickyOffsetBounds,
265 ) -> ScrollTreeNodeId {
266 self.paint_info.scroll_tree.add_scroll_tree_node(
267 Some(parent_scroll_node_id),
268 SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
269 frame_rect,
270 margins,
271 vertical_offset_bounds,
272 horizontal_offset_bounds,
273 }),
274 )
275 }
276
277 pub(crate) fn offset_in_fragment(
285 &self,
286 fragment: &Fragment,
287 point_in_viewport: PhysicalPoint<Au>,
288 ) -> Option<Point2D<Au, CSSPixel>> {
289 let Fragment::Box(fragment) = fragment else {
290 return None;
291 };
292
293 let spatial_tree_node = fragment.spatial_tree_node()?;
294 let transform = self
295 .paint_info
296 .scroll_tree
297 .cumulative_root_to_node_transform(spatial_tree_node)?;
298 let transformed_point = transform
299 .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
300 .map(Au::from_f32_px)
301 .cast_unit();
302
303 let reference_frame_origin = self
305 .paint_info
306 .scroll_tree
307 .reference_frame_offset(spatial_tree_node)
308 .map(Au::from_f32_px);
309 let fragment_origin = fragment
310 .cumulative_content_box_rect(
311 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
312 )
313 .origin -
314 reference_frame_origin.cast_unit();
315
316 Some(transformed_point - fragment_origin)
318 }
319}
320
321#[derive(Clone, Debug, MallocSizeOf)]
323pub(crate) struct FragmentTextDecoration {
324 pub line: TextDecorationLine,
325 pub color: AbsoluteColor,
326 pub style: TextDecorationStyle,
327}
328
329#[derive(MallocSizeOf)]
334pub(crate) enum StackingContextContent {
335 Fragment {
337 scroll_node_id: ScrollTreeNodeId,
338 reference_frame_scroll_node_id: ScrollTreeNodeId,
339 clip_id: ClipId,
340 section: StackingContextSection,
341 containing_block: PhysicalRect<Au>,
342 fragment: Fragment,
343 is_hit_test_for_scrollable_overflow: bool,
344 is_collapsed_table_borders: bool,
345 #[conditional_malloc_size_of]
346 text_decorations: Arc<Vec<FragmentTextDecoration>>,
347 },
348
349 AtomicInlineStackingContainer { index: usize },
353}
354
355impl StackingContextContent {
356 pub(crate) fn section(&self) -> StackingContextSection {
357 match self {
358 Self::Fragment { section, .. } => *section,
359 Self::AtomicInlineStackingContainer { .. } => StackingContextSection::Foreground,
360 }
361 }
362
363 fn build_display_list_with_section_override(
364 &self,
365 builder: &mut DisplayListBuilder,
366 inline_stacking_containers: &[StackingContext],
367 section_override: Option<StackingContextSection>,
368 ) {
369 match self {
370 Self::Fragment {
371 scroll_node_id,
372 reference_frame_scroll_node_id,
373 clip_id,
374 section,
375 containing_block,
376 fragment,
377 is_hit_test_for_scrollable_overflow,
378 is_collapsed_table_borders,
379 text_decorations,
380 } => {
381 builder.current_scroll_node_id = *scroll_node_id;
382 builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
383 builder.current_clip_id = *clip_id;
384 fragment.build_display_list(
385 builder,
386 containing_block,
387 section_override.unwrap_or(*section),
388 *is_hit_test_for_scrollable_overflow,
389 *is_collapsed_table_borders,
390 text_decorations,
391 );
392 },
393 Self::AtomicInlineStackingContainer { index } => {
394 inline_stacking_containers[*index].build_display_list(builder);
395 },
396 }
397 }
398
399 fn build_display_list(
400 &self,
401 builder: &mut DisplayListBuilder,
402 inline_stacking_containers: &[StackingContext],
403 ) {
404 self.build_display_list_with_section_override(builder, inline_stacking_containers, None);
405 }
406
407 fn has_outline(&self) -> bool {
408 match self {
409 StackingContextContent::Fragment { fragment, .. } => match fragment {
410 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
411 let style = box_fragment.style();
412 let outline = style.get_outline();
413 !outline.outline_style.none_or_hidden() && !outline.outline_width.0.is_zero()
414 },
415 _ => false,
416 },
417 StackingContextContent::AtomicInlineStackingContainer { .. } => false,
418 }
419 }
420}
421
422#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
423pub(crate) enum StackingContextType {
424 RealStackingContext,
425 PositionedStackingContainer,
426 FloatStackingContainer,
427 AtomicInlineStackingContainer,
428}
429
430#[derive(MallocSizeOf)]
436pub struct StackingContext {
437 scroll_tree_node_id: ScrollTreeNodeId,
440
441 clip_id: Option<ClipId>,
443
444 #[conditional_malloc_size_of]
447 initializing_fragment: Option<Arc<BoxFragment>>,
448
449 context_type: StackingContextType,
451
452 pub(super) contents: Vec<StackingContextContent>,
454
455 pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
465
466 pub(super) float_stacking_containers: Vec<StackingContext>,
471
472 pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
480
481 debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
483}
484
485#[derive(Clone, Copy, MallocSizeOf)]
487pub struct DebugPrintItem {
488 field: DebugPrintField,
489 index: usize,
490}
491
492#[derive(Clone, Copy, MallocSizeOf)]
494pub enum DebugPrintField {
495 Contents,
496 RealStackingContextsAndPositionedStackingContainers,
497 FloatStackingContainers,
498}
499
500impl StackingContext {
501 fn create_descendant(
502 &self,
503 spatial_id: ScrollTreeNodeId,
504 clip_id: ClipId,
505 initializing_fragment: Arc<BoxFragment>,
506 context_type: StackingContextType,
507 ) -> Self {
508 let clip_id = match clip_id {
513 ClipId::INVALID => None,
514 clip_id => Some(clip_id),
515 };
516 Self {
517 scroll_tree_node_id: spatial_id,
518 clip_id,
519 initializing_fragment: Some(initializing_fragment),
520 context_type,
521 contents: vec![],
522 real_stacking_contexts_and_positioned_stacking_containers: vec![],
523 float_stacking_containers: vec![],
524 atomic_inline_stacking_containers: vec![],
525 debug_print_items: self.debug_print_items.is_some().then(|| vec![].into()),
526 }
527 }
528
529 fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DiagnosticsLogging) -> Self {
530 Self {
531 scroll_tree_node_id: root_scroll_node_id,
532 clip_id: None,
533 initializing_fragment: None,
534 context_type: StackingContextType::RealStackingContext,
535 contents: vec![],
536 real_stacking_contexts_and_positioned_stacking_containers: vec![],
537 float_stacking_containers: vec![],
538 atomic_inline_stacking_containers: vec![],
539 debug_print_items: debug
540 .is_enabled(DiagnosticsLoggingOption::StackingContextTree)
541 .then(|| vec![].into()),
542 }
543 }
544
545 fn add_stacking_context(&mut self, stacking_context: StackingContext) {
547 match stacking_context.context_type {
548 StackingContextType::RealStackingContext => {
549 &mut self.real_stacking_contexts_and_positioned_stacking_containers
550 },
551 StackingContextType::PositionedStackingContainer => {
552 &mut self.real_stacking_contexts_and_positioned_stacking_containers
553 },
554 StackingContextType::FloatStackingContainer => &mut self.float_stacking_containers,
555 StackingContextType::AtomicInlineStackingContainer => {
556 &mut self.atomic_inline_stacking_containers
557 },
558 }
559 .push(stacking_context)
560 }
561
562 pub(crate) fn z_index(&self) -> i32 {
563 self.initializing_fragment.as_ref().map_or(0, |fragment| {
564 fragment.style().effective_z_index(fragment.base.flags)
565 })
566 }
567
568 pub(crate) fn sort(&mut self) {
569 self.contents.sort_by_key(|a| a.section());
570 self.real_stacking_contexts_and_positioned_stacking_containers
571 .sort_by_key(|a| a.z_index());
572
573 debug_assert!(
574 self.real_stacking_contexts_and_positioned_stacking_containers
575 .iter()
576 .all(|c| matches!(
577 c.context_type,
578 StackingContextType::RealStackingContext |
579 StackingContextType::PositionedStackingContainer
580 ))
581 );
582 debug_assert!(
583 self.float_stacking_containers
584 .iter()
585 .all(
586 |c| c.context_type == StackingContextType::FloatStackingContainer &&
587 c.z_index() == 0
588 )
589 );
590 debug_assert!(
591 self.atomic_inline_stacking_containers
592 .iter()
593 .all(
594 |c| c.context_type == StackingContextType::AtomicInlineStackingContainer &&
595 c.z_index() == 0
596 )
597 );
598 }
599
600 fn push_webrender_stacking_context_if_necessary(
601 &self,
602 builder: &mut DisplayListBuilder,
603 ) -> bool {
604 let Some(fragment) = self.initializing_fragment.as_ref() else {
605 return false;
606 };
607
608 let style = fragment.style();
611 let effects = style.get_effects();
612 let transform_style = style.get_used_transform_style();
613 if effects.filter.0.is_empty() &&
614 effects.opacity == 1.0 &&
615 effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
616 !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
617 style.get_svg().clip_path == ClipPath::None &&
618 transform_style == TransformStyle::Flat
619 {
620 return false;
621 }
622
623 let current_color = style.clone_color();
625 let mut filters: Vec<wr::FilterOp> = effects
626 .filter
627 .0
628 .iter()
629 .map(|filter| FilterToWebRender::to_webrender(filter, ¤t_color))
630 .collect();
631 if effects.opacity != 1.0 {
632 filters.push(wr::FilterOp::Opacity(
633 effects.opacity.into(),
634 effects.opacity,
635 ));
636 }
637
638 let spatial_id = builder.spatial_id(self.scroll_tree_node_id);
645 let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id));
646 builder.wr().push_stacking_context(
647 LayoutPoint::zero(), spatial_id,
649 style.get_webrender_primitive_flags(),
650 clip_chain_id,
651 transform_style.to_webrender(),
652 effects.mix_blend_mode.to_webrender(),
653 &filters,
654 &[], &[], wr::RasterSpace::Screen,
657 wr::StackingContextFlags::empty(),
658 None, );
660
661 true
662 }
663
664 pub(crate) fn build_canvas_background_display_list(
668 &self,
669 builder: &mut DisplayListBuilder,
670 fragment_tree: &crate::fragment_tree::FragmentTree,
671 ) {
672 let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
673 fragment
674 .base()
675 .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
676 }) else {
677 return;
678 };
679 let root_fragment = match root_fragment {
680 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
681 _ => return,
682 };
683
684 let source_style = {
685 let root_fragment_style = root_fragment.style();
691 if root_fragment_style.background_is_transparent() {
692 let body_fragment = fragment_tree.body_fragment();
693 builder.paint_body_background = body_fragment.is_none();
694 body_fragment
695 .map(|body_fragment| body_fragment.style().clone())
696 .unwrap_or(root_fragment.style().clone())
697 } else {
698 root_fragment_style.clone()
699 }
700 };
701
702 if source_style.background_is_transparent() {
705 return;
706 }
707
708 let painting_area = fragment_tree
715 .initial_containing_block
716 .union(&fragment_tree.scrollable_overflow())
717 .to_webrender();
718
719 let background_color =
720 source_style.resolve_color(&source_style.get_background().background_color);
721 if background_color.alpha > 0.0 {
722 let common = builder.common_properties(painting_area, &source_style);
723 let color = super::rgba(background_color);
724 builder.wr().push_rect(&common, painting_area, color);
725
726 let default_background_color = servo_config::pref!(shell_background_color_rgba);
730 let default_background_color = AbsoluteColor::new(
731 ColorSpace::Srgb,
732 default_background_color[0] as f32,
733 default_background_color[1] as f32,
734 default_background_color[2] as f32,
735 default_background_color[3] as f32,
736 )
737 .into_srgb_legacy();
738 if background_color != default_background_color {
739 builder.mark_is_paintable();
740 }
741 }
742
743 let mut fragment_builder = BuilderForBoxFragment::new(
744 root_fragment,
745 &fragment_tree.initial_containing_block,
746 false, false, );
749 let painter = super::background::BackgroundPainter {
750 style: &source_style,
751 painting_area_override: Some(painting_area),
752 positioning_area_override: None,
753 };
754 fragment_builder.build_background_image(builder, &painter);
755 }
756
757 pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
761 let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
762
763 let mut content_with_outlines = Vec::new();
772 let mut contents = self.contents.iter().enumerate().peekable();
773 while contents.peek().is_some_and(|(_, child)| {
774 child.section() == StackingContextSection::OwnBackgroundsAndBorders
775 }) {
776 let (i, child) = contents.next().unwrap();
777 self.debug_push_print_item(DebugPrintField::Contents, i);
778 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
779
780 if child.has_outline() {
781 content_with_outlines.push(child);
782 }
783 }
784
785 let mut real_stacking_contexts_and_positioned_stacking_containers = self
787 .real_stacking_contexts_and_positioned_stacking_containers
788 .iter()
789 .enumerate()
790 .peekable();
791 while real_stacking_contexts_and_positioned_stacking_containers
792 .peek()
793 .is_some_and(|(_, child)| child.z_index() < 0)
794 {
795 let (i, child) = real_stacking_contexts_and_positioned_stacking_containers
796 .next()
797 .unwrap();
798 self.debug_push_print_item(
799 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
800 i,
801 );
802 child.build_display_list(builder);
803 }
804
805 while contents.peek().is_some_and(|(_, child)| {
807 child.section() == StackingContextSection::DescendantBackgroundsAndBorders
808 }) {
809 let (i, child) = contents.next().unwrap();
810 self.debug_push_print_item(DebugPrintField::Contents, i);
811 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
812
813 if child.has_outline() {
814 content_with_outlines.push(child);
815 }
816 }
817
818 for (i, child) in self.float_stacking_containers.iter().enumerate() {
820 self.debug_push_print_item(DebugPrintField::FloatStackingContainers, i);
821 child.build_display_list(builder);
822 }
823
824 while contents
826 .peek()
827 .is_some_and(|(_, child)| child.section() == StackingContextSection::Foreground)
828 {
829 let (i, child) = contents.next().unwrap();
830 self.debug_push_print_item(DebugPrintField::Contents, i);
831 child.build_display_list(builder, &self.atomic_inline_stacking_containers);
832
833 if child.has_outline() {
834 content_with_outlines.push(child);
835 }
836 }
837
838 for (i, child) in real_stacking_contexts_and_positioned_stacking_containers {
841 self.debug_push_print_item(
842 DebugPrintField::RealStackingContextsAndPositionedStackingContainers,
843 i,
844 );
845 child.build_display_list(builder);
846 }
847
848 for content in content_with_outlines {
850 content.build_display_list_with_section_override(
851 builder,
852 &self.atomic_inline_stacking_containers,
853 Some(StackingContextSection::Outline),
854 );
855 }
856
857 if pushed_context {
858 builder.wr().pop_stacking_context();
859 }
860 }
861
862 fn debug_push_print_item(&self, field: DebugPrintField, index: usize) {
867 if let Some(items) = self.debug_print_items.as_ref() {
868 items.borrow_mut().push(DebugPrintItem { field, index });
869 }
870 }
871
872 pub fn debug_print(&self) {
874 if self.debug_print_items.is_none() {
875 warn!("failed to print stacking context tree: debug_print_items was None");
876 return;
877 }
878 let mut tree = PrintTree::new("Stacking context tree");
879 self.debug_print_with_tree(&mut tree);
880 }
881
882 fn debug_print_with_tree(&self, tree: &mut PrintTree) {
884 match self.context_type {
885 StackingContextType::RealStackingContext => {
886 tree.new_level(format!("{:?} z={}", self.context_type, self.z_index()));
887 },
888 StackingContextType::AtomicInlineStackingContainer => {
889 },
891 _ => {
892 tree.new_level(format!("{:?}", self.context_type));
893 },
894 }
895 for DebugPrintItem { field, index } in
896 self.debug_print_items.as_ref().unwrap().borrow().iter()
897 {
898 match field {
899 DebugPrintField::Contents => match self.contents[*index] {
900 StackingContextContent::Fragment { section, .. } => {
901 tree.add_item(format!("{section:?}"));
902 },
903 StackingContextContent::AtomicInlineStackingContainer { index } => {
904 tree.new_level(format!("AtomicInlineStackingContainer #{index}"));
905 self.atomic_inline_stacking_containers[index].debug_print_with_tree(tree);
906 tree.end_level();
907 },
908 },
909 DebugPrintField::RealStackingContextsAndPositionedStackingContainers => {
910 self.real_stacking_contexts_and_positioned_stacking_containers[*index]
911 .debug_print_with_tree(tree);
912 },
913 DebugPrintField::FloatStackingContainers => {
914 self.float_stacking_containers[*index].debug_print_with_tree(tree);
915 },
916 }
917 }
918 match self.context_type {
919 StackingContextType::AtomicInlineStackingContainer => {
920 },
922 _ => {
923 tree.end_level();
924 },
925 }
926 }
927}
928
929#[derive(PartialEq)]
930pub(crate) enum StackingContextBuildMode {
931 IncludeHoisted,
932 SkipHoisted,
933}
934
935impl Fragment {
936 pub(crate) fn build_stacking_context_tree(
937 &self,
938 stacking_context_tree: &mut StackingContextTree,
939 containing_block_info: &ContainingBlockInfo,
940 stacking_context: &mut StackingContext,
941 mode: StackingContextBuildMode,
942 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
943 ) {
944 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
945 let cumulative_containing_block = containing_block
946 .rect
947 .translate(containing_block.accumulated_reference_frame_offset);
948 self.set_containing_block(&cumulative_containing_block);
949
950 if self
951 .base()
952 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
953 {
954 return;
955 }
956
957 let fragment_clone = self.clone();
958 match self {
959 Fragment::Box(fragment) | Fragment::Float(fragment) => {
960 if mode == StackingContextBuildMode::SkipHoisted &&
961 fragment.style().clone_position().is_absolutely_positioned()
962 {
963 return;
964 }
965
966 let text_decorations = match self {
967 Fragment::Float(..) => &Default::default(),
968 _ => text_decorations,
969 };
970
971 fragment.build_stacking_context_tree(
972 fragment_clone,
973 stacking_context_tree,
974 containing_block,
975 containing_block_info,
976 stacking_context,
977 text_decorations,
978 );
979 },
980 Fragment::AbsoluteOrFixedPositioned(fragment) => {
981 let shared_fragment = fragment.borrow();
982 let fragment_ref = match shared_fragment.fragment.as_ref() {
983 Some(fragment_ref) => fragment_ref,
984 None => unreachable!("Found hoisted box with missing fragment."),
985 };
986
987 fragment_ref.build_stacking_context_tree(
988 stacking_context_tree,
989 containing_block_info,
990 stacking_context,
991 StackingContextBuildMode::IncludeHoisted,
992 &Default::default(),
993 );
994 },
995 Fragment::Positioning(fragment) => {
996 fragment.build_stacking_context_tree(
997 stacking_context_tree,
998 containing_block,
999 containing_block_info,
1000 stacking_context,
1001 text_decorations,
1002 );
1003 },
1004 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
1005 stacking_context
1006 .contents
1007 .push(StackingContextContent::Fragment {
1008 section: StackingContextSection::Foreground,
1009 scroll_node_id: containing_block.scroll_node_id,
1010 reference_frame_scroll_node_id: containing_block_info
1011 .for_absolute_and_fixed_descendants
1012 .scroll_node_id,
1013 clip_id: containing_block.clip_id,
1014 containing_block: containing_block.rect,
1015 fragment: fragment_clone,
1016 is_hit_test_for_scrollable_overflow: false,
1017 is_collapsed_table_borders: false,
1018 text_decorations: text_decorations.clone(),
1019 });
1020 },
1021 }
1022 }
1023}
1024
1025struct ReferenceFrameData {
1026 origin: PhysicalPoint<Au>,
1027 transform: LayoutTransform,
1028 kind: wr::ReferenceFrameKind,
1029}
1030struct ScrollFrameData {
1031 scroll_tree_node_id: ScrollTreeNodeId,
1032 scroll_frame_rect: LayoutRect,
1033}
1034
1035struct OverflowFrameData {
1036 clip_id: ClipId,
1037 scroll_frame_data: Option<ScrollFrameData>,
1038}
1039
1040impl BoxFragment {
1041 fn get_stacking_context_type(&self) -> Option<StackingContextType> {
1042 let flags = self.base.flags;
1043 let style = self.style();
1044 if style.establishes_stacking_context(flags) {
1045 return Some(StackingContextType::RealStackingContext);
1046 }
1047
1048 let box_style = &style.get_box();
1049 if box_style.position != ComputedPosition::Static {
1050 return Some(StackingContextType::PositionedStackingContainer);
1051 }
1052
1053 if box_style.float != ComputedFloat::None {
1054 return Some(StackingContextType::FloatStackingContainer);
1055 }
1056
1057 if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
1061 return Some(StackingContextType::AtomicInlineStackingContainer);
1062 }
1063
1064 None
1065 }
1066
1067 fn get_stacking_context_section(&self) -> StackingContextSection {
1068 if self.get_stacking_context_type().is_some() {
1069 return StackingContextSection::OwnBackgroundsAndBorders;
1070 }
1071
1072 if self.style().get_box().display.outside() == DisplayOutside::Inline {
1073 return StackingContextSection::Foreground;
1074 }
1075
1076 StackingContextSection::DescendantBackgroundsAndBorders
1077 }
1078
1079 fn build_stacking_context_tree(
1080 &self,
1081 fragment: Fragment,
1082 stacking_context_tree: &mut StackingContextTree,
1083 containing_block: &ContainingBlock,
1084 containing_block_info: &ContainingBlockInfo,
1085 parent_stacking_context: &mut StackingContext,
1086 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1087 ) {
1088 self.build_stacking_context_tree_maybe_creating_reference_frame(
1089 fragment,
1090 stacking_context_tree,
1091 containing_block,
1092 containing_block_info,
1093 parent_stacking_context,
1094 text_decorations,
1095 );
1096 }
1097
1098 fn build_stacking_context_tree_maybe_creating_reference_frame(
1099 &self,
1100 fragment: Fragment,
1101 stacking_context_tree: &mut StackingContextTree,
1102 containing_block: &ContainingBlock,
1103 containing_block_info: &ContainingBlockInfo,
1104 parent_stacking_context: &mut StackingContext,
1105 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1106 ) {
1107 let reference_frame_data =
1108 match self.reference_frame_data_if_necessary(&containing_block.rect) {
1109 Some(reference_frame_data) => reference_frame_data,
1110 None => {
1111 return self.build_stacking_context_tree_maybe_creating_stacking_context(
1112 fragment,
1113 stacking_context_tree,
1114 containing_block,
1115 containing_block_info,
1116 parent_stacking_context,
1117 text_decorations,
1118 );
1119 },
1120 };
1121
1122 if !reference_frame_data.transform.is_invertible() {
1126 self.clear_spatial_tree_node_including_descendants();
1127 return;
1128 }
1129
1130 let style = self.style();
1131 let frame_origin_for_query = self
1132 .cumulative_border_box_rect(
1133 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
1134 )
1135 .origin
1136 .to_webrender();
1137 let new_spatial_id = stacking_context_tree.push_reference_frame(
1138 reference_frame_data.origin.to_webrender(),
1139 frame_origin_for_query,
1140 containing_block.scroll_node_id,
1141 style.get_box().transform_style.to_webrender(),
1142 reference_frame_data.transform,
1143 reference_frame_data.kind,
1144 );
1145
1146 assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
1156 let reference_frame_offset = reference_frame_data.origin.to_vector();
1157 let adjusted_containing_block = ContainingBlock::new(
1158 containing_block.rect.translate(-reference_frame_offset),
1159 new_spatial_id,
1160 None,
1161 containing_block.clip_id,
1162 containing_block.accumulated_reference_frame_offset + reference_frame_offset,
1163 );
1164 let new_containing_block_info =
1165 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
1166
1167 self.build_stacking_context_tree_maybe_creating_stacking_context(
1168 fragment,
1169 stacking_context_tree,
1170 &adjusted_containing_block,
1171 &new_containing_block_info,
1172 parent_stacking_context,
1173 text_decorations,
1174 );
1175 }
1176
1177 fn build_stacking_context_tree_maybe_creating_stacking_context(
1178 &self,
1179 fragment: Fragment,
1180 stacking_context_tree: &mut StackingContextTree,
1181 containing_block: &ContainingBlock,
1182 containing_block_info: &ContainingBlockInfo,
1183 parent_stacking_context: &mut StackingContext,
1184 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1185 ) {
1186 let context_type = match self.get_stacking_context_type() {
1187 Some(context_type) => context_type,
1188 None => {
1189 self.build_stacking_context_tree_for_children(
1190 fragment,
1191 stacking_context_tree,
1192 containing_block,
1193 containing_block_info,
1194 parent_stacking_context,
1195 text_decorations,
1196 );
1197 return;
1198 },
1199 };
1200
1201 if context_type == StackingContextType::AtomicInlineStackingContainer {
1202 parent_stacking_context.contents.push(
1204 StackingContextContent::AtomicInlineStackingContainer {
1205 index: parent_stacking_context
1206 .atomic_inline_stacking_containers
1207 .len(),
1208 },
1209 );
1210 }
1211
1212 let stacking_context_clip_id = stacking_context_tree
1216 .clip_store
1217 .add_for_clip_path(
1218 &self.style().get_svg().clip_path,
1219 containing_block.scroll_node_id,
1220 containing_block.clip_id,
1221 BuilderForBoxFragment::new(
1222 self,
1223 &containing_block.rect,
1224 false, false, ),
1227 )
1228 .unwrap_or(containing_block.clip_id);
1229
1230 let box_fragment = match fragment {
1231 Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
1232 box_fragment.clone()
1233 },
1234 _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
1235 };
1236
1237 let mut child_stacking_context = parent_stacking_context.create_descendant(
1238 containing_block.scroll_node_id,
1239 stacking_context_clip_id,
1240 box_fragment,
1241 context_type,
1242 );
1243 self.build_stacking_context_tree_for_children(
1244 fragment,
1245 stacking_context_tree,
1246 containing_block,
1247 containing_block_info,
1248 &mut child_stacking_context,
1249 text_decorations,
1250 );
1251
1252 let mut stolen_children = vec![];
1253 if context_type != StackingContextType::RealStackingContext {
1254 stolen_children = mem::replace(
1255 &mut child_stacking_context
1256 .real_stacking_contexts_and_positioned_stacking_containers,
1257 stolen_children,
1258 );
1259 }
1260
1261 child_stacking_context.sort();
1262 parent_stacking_context.add_stacking_context(child_stacking_context);
1263 parent_stacking_context
1264 .real_stacking_contexts_and_positioned_stacking_containers
1265 .append(&mut stolen_children);
1266 }
1267
1268 fn build_stacking_context_tree_for_children(
1269 &self,
1270 fragment: Fragment,
1271 stacking_context_tree: &mut StackingContextTree,
1272 containing_block: &ContainingBlock,
1273 containing_block_info: &ContainingBlockInfo,
1274 stacking_context: &mut StackingContext,
1275 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1276 ) {
1277 let mut new_scroll_node_id = containing_block.scroll_node_id;
1278 let mut new_clip_id = containing_block.clip_id;
1279 let mut new_scroll_frame_size = containing_block_info
1280 .for_non_absolute_descendants
1281 .scroll_frame_size;
1282
1283 if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
1284 stacking_context_tree,
1285 new_scroll_node_id,
1286 &containing_block.rect,
1287 &new_scroll_frame_size,
1288 ) {
1289 new_scroll_node_id = scroll_node_id;
1290 }
1291
1292 if let Some(clip_id) = self.build_clip_frame_if_necessary(
1293 stacking_context_tree,
1294 new_scroll_node_id,
1295 new_clip_id,
1296 &containing_block.rect,
1297 ) {
1298 new_clip_id = clip_id;
1299 }
1300
1301 let style = self.style();
1302 if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
1303 &style.get_svg().clip_path,
1304 new_scroll_node_id,
1305 new_clip_id,
1306 BuilderForBoxFragment::new(
1307 self,
1308 &containing_block.rect,
1309 false, false, ),
1312 ) {
1313 new_clip_id = clip_id;
1314 }
1315
1316 let establishes_containing_block_for_all_descendants =
1317 style.establishes_containing_block_for_all_descendants(self.base.flags);
1318 let establishes_containing_block_for_absolute_descendants =
1319 style.establishes_containing_block_for_absolute_descendants(self.base.flags);
1320
1321 let reference_frame_scroll_node_id_for_fragments =
1322 if establishes_containing_block_for_all_descendants {
1323 new_scroll_node_id
1324 } else {
1325 containing_block_info
1326 .for_absolute_and_fixed_descendants
1327 .scroll_node_id
1328 };
1329
1330 let mut add_fragment = |section| {
1331 stacking_context
1332 .contents
1333 .push(StackingContextContent::Fragment {
1334 scroll_node_id: new_scroll_node_id,
1335 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1336 clip_id: new_clip_id,
1337 section,
1338 containing_block: containing_block.rect,
1339 fragment: fragment.clone(),
1340 is_hit_test_for_scrollable_overflow: false,
1341 is_collapsed_table_borders: false,
1342 text_decorations: text_decorations.clone(),
1343 });
1344 };
1345
1346 let section = self.get_stacking_context_section();
1347 add_fragment(section);
1348
1349 *self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
1353
1354 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
1357 stacking_context_tree,
1358 new_scroll_node_id,
1359 new_clip_id,
1360 &containing_block.rect,
1361 ) {
1362 new_clip_id = overflow_frame_data.clip_id;
1363 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
1364 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
1365 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
1366 stacking_context
1367 .contents
1368 .push(StackingContextContent::Fragment {
1369 scroll_node_id: new_scroll_node_id,
1370 reference_frame_scroll_node_id:
1371 reference_frame_scroll_node_id_for_fragments,
1372 clip_id: new_clip_id,
1373 section,
1374 containing_block: containing_block.rect,
1375 fragment: fragment.clone(),
1376 is_hit_test_for_scrollable_overflow: true,
1377 is_collapsed_table_borders: false,
1378 text_decorations: text_decorations.clone(),
1379 });
1380 }
1381 }
1382
1383 let padding_rect = self
1384 .padding_rect()
1385 .translate(containing_block.rect.origin.to_vector());
1386 let content_rect = self
1387 .content_rect()
1388 .translate(containing_block.rect.origin.to_vector());
1389
1390 let for_absolute_descendants = ContainingBlock::new(
1391 padding_rect,
1392 new_scroll_node_id,
1393 new_scroll_frame_size,
1394 new_clip_id,
1395 containing_block.accumulated_reference_frame_offset,
1396 );
1397 let for_non_absolute_descendants = ContainingBlock::new(
1398 content_rect,
1399 new_scroll_node_id,
1400 new_scroll_frame_size,
1401 new_clip_id,
1402 containing_block.accumulated_reference_frame_offset,
1403 );
1404
1405 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
1409 containing_block_info.new_for_absolute_and_fixed_descendants(
1410 &for_non_absolute_descendants,
1411 &for_absolute_descendants,
1412 )
1413 } else if establishes_containing_block_for_absolute_descendants {
1414 containing_block_info.new_for_absolute_descendants(
1415 &for_non_absolute_descendants,
1416 &for_absolute_descendants,
1417 )
1418 } else {
1419 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
1420 };
1421
1422 let text_decorations = match self.is_atomic_inline_level() ||
1428 self.base
1429 .flags
1430 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
1431 {
1432 true => &Default::default(),
1433 false => text_decorations,
1434 };
1435
1436 let new_text_decoration;
1437 let text_decorations = match style.clone_text_decoration_line() {
1438 TextDecorationLine::NONE => text_decorations,
1439 line => {
1440 let mut new_vector = (**text_decorations).clone();
1441 let color = &style.get_inherited_text().color;
1442 new_vector.push(FragmentTextDecoration {
1443 line,
1444 color: style
1445 .clone_text_decoration_color()
1446 .resolve_to_absolute(color),
1447 style: style.clone_text_decoration_style(),
1448 });
1449 new_text_decoration = Arc::new(new_vector);
1450 &new_text_decoration
1451 },
1452 };
1453
1454 for child in &self.children {
1455 child.build_stacking_context_tree(
1456 stacking_context_tree,
1457 &new_containing_block_info,
1458 stacking_context,
1459 StackingContextBuildMode::SkipHoisted,
1460 text_decorations,
1461 );
1462 }
1463
1464 if matches!(
1465 self.specific_layout_info().as_deref(),
1466 Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
1467 ) {
1468 stacking_context
1469 .contents
1470 .push(StackingContextContent::Fragment {
1471 scroll_node_id: new_scroll_node_id,
1472 reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
1473 clip_id: new_clip_id,
1474 section,
1475 containing_block: containing_block.rect,
1476 fragment: fragment.clone(),
1477 is_hit_test_for_scrollable_overflow: false,
1478 is_collapsed_table_borders: true,
1479 text_decorations: text_decorations.clone(),
1480 });
1481 }
1482 }
1483
1484 fn build_clip_frame_if_necessary(
1485 &self,
1486 stacking_context_tree: &mut StackingContextTree,
1487 parent_scroll_node_id: ScrollTreeNodeId,
1488 parent_clip_id: ClipId,
1489 containing_block_rect: &PhysicalRect<Au>,
1490 ) -> Option<ClipId> {
1491 let style = self.style();
1492 let position = style.get_box().position;
1493 if !position.is_absolutely_positioned() {
1496 return None;
1497 }
1498
1499 let clip_rect = match style.get_effects().clip {
1501 ClipRectOrAuto::Rect(rect) => rect,
1502 _ => return None,
1503 };
1504
1505 let border_rect = self.border_rect();
1506 let clip_rect = clip_rect
1507 .for_border_rect(border_rect)
1508 .translate(containing_block_rect.origin.to_vector())
1509 .to_webrender();
1510 Some(stacking_context_tree.clip_store.add(
1511 BorderRadius::zero(),
1512 clip_rect,
1513 parent_scroll_node_id,
1514 parent_clip_id,
1515 ))
1516 }
1517
1518 fn build_overflow_frame_if_necessary(
1519 &self,
1520 stacking_context_tree: &mut StackingContextTree,
1521 parent_scroll_node_id: ScrollTreeNodeId,
1522 parent_clip_id: ClipId,
1523 containing_block_rect: &PhysicalRect<Au>,
1524 ) -> Option<OverflowFrameData> {
1525 let style = self.style();
1526 let overflow = style.effective_overflow(self.base.flags);
1527
1528 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1529 return None;
1530 }
1531
1532 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1534 let overflow_clip_margin = style.get_margin().overflow_clip_margin;
1535 let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
1536 OverflowClipMarginBox::ContentBox => self.content_rect(),
1537 OverflowClipMarginBox::PaddingBox => self.padding_rect(),
1538 OverflowClipMarginBox::BorderBox => self.border_rect(),
1539 }
1540 .translate(containing_block_rect.origin.to_vector())
1541 .to_webrender();
1542
1543 let clip_margin_offset = overflow_clip_margin.offset.px();
1546 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
1547
1548 let radii;
1551 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1552 let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
1553 let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
1554 match overflow_clip_margin.visual_box {
1555 OverflowClipMarginBox::ContentBox => {
1556 offsets_from_border -= (self.border + self.padding).to_webrender();
1557 },
1558 OverflowClipMarginBox::PaddingBox => {
1559 offsets_from_border -= self.border.to_webrender();
1560 },
1561 OverflowClipMarginBox::BorderBox => {},
1562 };
1563 radii = offset_radii(builder.border_radius, offsets_from_border);
1564 } else if overflow.x != ComputedOverflow::Clip {
1565 let max = LayoutRect::max_rect();
1566 overflow_clip_rect.min.x = max.min.x;
1567 overflow_clip_rect.max.x = max.max.x;
1568 radii = BorderRadius::zero();
1569 } else {
1570 let max = LayoutRect::max_rect();
1571 overflow_clip_rect.min.y = max.min.y;
1572 overflow_clip_rect.max.y = max.max.y;
1573 radii = BorderRadius::zero();
1574 }
1575
1576 let clip_id = stacking_context_tree.clip_store.add(
1577 radii,
1578 overflow_clip_rect,
1579 parent_scroll_node_id,
1580 parent_clip_id,
1581 );
1582
1583 return Some(OverflowFrameData {
1584 clip_id,
1585 scroll_frame_data: None,
1586 });
1587 }
1588
1589 let scroll_frame_rect = self
1590 .padding_rect()
1591 .translate(containing_block_rect.origin.to_vector())
1592 .to_webrender();
1593
1594 let clip_id = stacking_context_tree.clip_store.add(
1595 BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
1596 scroll_frame_rect,
1597 parent_scroll_node_id,
1598 parent_clip_id,
1599 );
1600
1601 let tag = self.base.tag?;
1602 let external_scroll_id = wr::ExternalScrollId(
1603 tag.to_display_list_fragment_id(),
1604 stacking_context_tree.paint_info.pipeline_id,
1605 );
1606
1607 let sensitivity = AxesScrollSensitivity {
1608 x: overflow.x.into(),
1609 y: overflow.y.into(),
1610 };
1611
1612 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1613 parent_scroll_node_id,
1614 external_scroll_id,
1615 self.scrollable_overflow().to_webrender(),
1616 scroll_frame_rect,
1617 sensitivity,
1618 );
1619
1620 Some(OverflowFrameData {
1621 clip_id,
1622 scroll_frame_data: Some(ScrollFrameData {
1623 scroll_tree_node_id,
1624 scroll_frame_rect,
1625 }),
1626 })
1627 }
1628
1629 fn build_sticky_frame_if_necessary(
1630 &self,
1631 stacking_context_tree: &mut StackingContextTree,
1632 parent_scroll_node_id: ScrollTreeNodeId,
1633 containing_block_rect: &PhysicalRect<Au>,
1634 scroll_frame_size: &Option<LayoutSize>,
1635 ) -> Option<ScrollTreeNodeId> {
1636 let style = self.style();
1637 if style.get_box().position != ComputedPosition::Sticky {
1638 return None;
1639 }
1640
1641 let scroll_frame_size_for_resolve = match scroll_frame_size {
1642 Some(size) => size,
1643 None => {
1644 &stacking_context_tree
1646 .paint_info
1647 .viewport_details
1648 .layout_size()
1649 },
1650 };
1651
1652 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1656 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1657 let offsets = style.physical_box_offsets();
1658 let offsets = PhysicalSides::<AuOrAuto>::new(
1659 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1660 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1661 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1662 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1663 );
1664 self.ensure_rare_data().resolved_sticky_insets = Some(Box::new(offsets));
1665
1666 if scroll_frame_size.is_none() {
1667 return None;
1668 }
1669
1670 if offsets.top.is_auto() &&
1671 offsets.right.is_auto() &&
1672 offsets.bottom.is_auto() &&
1673 offsets.left.is_auto()
1674 {
1675 return None;
1676 }
1677
1678 let border_rect = self.border_rect();
1697 let computed_margin = style.physical_margin();
1698
1699 let distance_from_border_box_to_cb = PhysicalSides::new(
1703 border_rect.min_y(),
1704 containing_block_rect.width() - border_rect.max_x(),
1705 containing_block_rect.height() - border_rect.max_y(),
1706 border_rect.min_x(),
1707 );
1708
1709 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1713 let used_margin = if computed_margin.is_auto() {
1714 Au::zero()
1715 } else {
1716 used_margin
1717 };
1718 Au::zero().max(distance - used_margin).to_f32_px()
1719 };
1720
1721 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1724 -offset_bound(
1725 distance_from_border_box_to_cb.top,
1726 self.margin.top,
1727 computed_margin.top,
1728 ),
1729 offset_bound(
1730 distance_from_border_box_to_cb.bottom,
1731 self.margin.bottom,
1732 computed_margin.bottom,
1733 ),
1734 );
1735 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1736 -offset_bound(
1737 distance_from_border_box_to_cb.left,
1738 self.margin.left,
1739 computed_margin.left,
1740 ),
1741 offset_bound(
1742 distance_from_border_box_to_cb.right,
1743 self.margin.right,
1744 computed_margin.right,
1745 ),
1746 );
1747
1748 let frame_rect = border_rect
1749 .translate(containing_block_rect.origin.to_vector())
1750 .to_webrender();
1751
1752 let margins = SideOffsets2D::new(
1755 offsets.top.non_auto().map(|v| v.to_f32_px()),
1756 offsets.right.non_auto().map(|v| v.to_f32_px()),
1757 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1758 offsets.left.non_auto().map(|v| v.to_f32_px()),
1759 );
1760
1761 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1762 parent_scroll_node_id,
1763 frame_rect,
1764 margins,
1765 vertical_offset_bounds,
1766 horizontal_offset_bounds,
1767 );
1768
1769 Some(sticky_node_id)
1770 }
1771
1772 fn reference_frame_data_if_necessary(
1774 &self,
1775 containing_block_rect: &PhysicalRect<Au>,
1776 ) -> Option<ReferenceFrameData> {
1777 if !self
1778 .style()
1779 .has_effective_transform_or_perspective(self.base.flags)
1780 {
1781 return None;
1782 }
1783
1784 let relative_border_rect = self.border_rect();
1785 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1786 let transform = self.calculate_transform_matrix(&border_rect);
1787 let perspective = self.calculate_perspective_matrix(&border_rect);
1788 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1789 (None, Some(perspective)) => (
1790 perspective,
1791 wr::ReferenceFrameKind::Perspective {
1792 scrolling_relative_to: None,
1793 },
1794 ),
1795 (Some(transform), None) => (
1796 transform,
1797 wr::ReferenceFrameKind::Transform {
1798 is_2d_scale_translation: false,
1799 should_snap: false,
1800 paired_with_perspective: false,
1801 },
1802 ),
1803 (Some(transform), Some(perspective)) => (
1804 perspective.then(&transform),
1805 wr::ReferenceFrameKind::Perspective {
1806 scrolling_relative_to: None,
1807 },
1808 ),
1809 (None, None) => unreachable!(),
1810 };
1811
1812 Some(ReferenceFrameData {
1813 origin: border_rect.origin,
1814 transform: reference_frame_transform,
1815 kind: reference_frame_kind,
1816 })
1817 }
1818
1819 pub fn calculate_transform_matrix(
1821 &self,
1822 border_rect: &Rect<Au, CSSPixel>,
1823 ) -> Option<LayoutTransform> {
1824 let style = self.style();
1825 let list = &style.get_box().transform;
1826 let length_rect = au_rect_to_length_rect(border_rect);
1827 let rotate = match style.clone_rotate() {
1829 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1830 GenericRotate::Rotate3D(x, y, z, angle) => (x, y, z, angle),
1831 GenericRotate::None => (0., 0., 1., Angle::zero()),
1832 };
1833 let scale = match style.clone_scale() {
1834 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1835 GenericScale::None => (1., 1., 1.),
1836 };
1837 let translation = match style.clone_translate() {
1838 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1839 x.resolve(length_rect.size.width).px(),
1840 y.resolve(length_rect.size.height).px(),
1841 z.px(),
1842 ),
1843 GenericTranslate::None => LayoutTransform::identity(),
1844 };
1845
1846 let angle = euclid::Angle::radians(rotate.3.radians());
1847 let transform_base = list
1848 .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1849 .ok()?;
1850 let transform = LayoutTransform::from_untyped(&transform_base.0)
1851 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1852 .then_scale(scale.0, scale.1, scale.2)
1853 .then(&translation);
1854
1855 let transform_origin = &style.get_box().transform_origin;
1856 let transform_origin_x = transform_origin
1857 .horizontal
1858 .to_used_value(border_rect.size.width)
1859 .to_f32_px();
1860 let transform_origin_y = transform_origin
1861 .vertical
1862 .to_used_value(border_rect.size.height)
1863 .to_f32_px();
1864 let transform_origin_z = transform_origin.depth.px();
1865
1866 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1867 }
1868
1869 pub fn calculate_perspective_matrix(
1871 &self,
1872 border_rect: &Rect<Au, CSSPixel>,
1873 ) -> Option<LayoutTransform> {
1874 let style = self.style();
1875 match style.get_box().perspective {
1876 Perspective::Length(length) => {
1877 let perspective_origin = &style.get_box().perspective_origin;
1878 let perspective_origin = LayoutPoint::new(
1879 perspective_origin
1880 .horizontal
1881 .percentage_relative_to(border_rect.size.width.into())
1882 .px(),
1883 perspective_origin
1884 .vertical
1885 .percentage_relative_to(border_rect.size.height.into())
1886 .px(),
1887 );
1888
1889 let perspective_matrix = LayoutTransform::from_untyped(
1890 &transform::create_perspective_matrix(length.px()),
1891 );
1892
1893 Some(perspective_matrix.change_basis(
1894 perspective_origin.x,
1895 perspective_origin.y,
1896 0.0,
1897 ))
1898 },
1899 Perspective::None => None,
1900 }
1901 }
1902
1903 fn clear_spatial_tree_node_including_descendants(&self) {
1904 fn assign_spatial_tree_node_on_fragments(fragments: &[Fragment]) {
1905 for fragment in fragments.iter() {
1906 match fragment {
1907 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1908 box_fragment.clear_spatial_tree_node_including_descendants();
1909 },
1910 Fragment::Positioning(positioning_fragment) => {
1911 assign_spatial_tree_node_on_fragments(&positioning_fragment.children);
1912 },
1913 _ => {},
1914 }
1915 }
1916 }
1917
1918 *self.spatial_tree_node.borrow_mut() = None;
1919 assign_spatial_tree_node_on_fragments(&self.children);
1920 }
1921}
1922
1923impl PositioningFragment {
1924 fn build_stacking_context_tree(
1925 &self,
1926 stacking_context_tree: &mut StackingContextTree,
1927 containing_block: &ContainingBlock,
1928 containing_block_info: &ContainingBlockInfo,
1929 stacking_context: &mut StackingContext,
1930 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
1931 ) {
1932 let rect = self
1933 .base
1934 .rect()
1935 .translate(containing_block.rect.origin.to_vector());
1936 let new_containing_block = containing_block.new_replacing_rect(&rect);
1937 let new_containing_block_info =
1938 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1939
1940 for child in &self.children {
1941 child.build_stacking_context_tree(
1942 stacking_context_tree,
1943 &new_containing_block_info,
1944 stacking_context,
1945 StackingContextBuildMode::SkipHoisted,
1946 text_decorations,
1947 );
1948 }
1949 }
1950}
1951
1952pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1953 Rect::new(
1954 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1955 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1956 )
1957}