1use std::cell::Cell;
6use std::rc::Rc;
7use std::sync::Arc;
8
9use app_units::Au;
10use embedder_traits::ViewportDetails;
11use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
12use malloc_size_of_derive::MallocSizeOf;
13use paint_api::display_list::{
14 AxesScrollSensitivity, PaintDisplayListInfo, ReferenceFrameNodeInfo, ScrollType,
15 ScrollableNodeInfo, SpatialTreeNodeInfo, StickyNodeInfo, TouchAction,
16};
17use servo_base::id::ScrollTreeNodeId;
18use servo_base::print_tree::PrintTree;
19use servo_config::opts::{DiagnosticsLogging, DiagnosticsLoggingOption};
20use servo_geometry::MaxRect;
21use style::Zero;
22use style::color::AbsoluteColor;
23use style::computed_values::overflow_x::T as ComputedOverflow;
24use style::computed_values::position::T as ComputedPosition;
25use style::computed_values::text_decoration_style::T as TextDecorationStyle;
26use style::computed_values::text_decoration_thickness::T as TextDecorationThickness;
27use style::values::computed::angle::Angle;
28use style::values::computed::{ClipRectOrAuto, Length, TextDecorationLine};
29use style::values::generics::box_::{OverflowClipMarginBox, Perspective};
30use style::values::generics::transform::{
31 self, GenericRotate, GenericScale, GenericTranslate, get_normalized_vector_and_angle,
32};
33use style_traits::CSSPixel;
34use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
35use webrender_api::{self as wr, BorderRadius};
36use wr::StickyOffsetBounds;
37use wr::units::{LayoutPixel, LayoutSize};
38
39use super::ClipId;
40use super::clip::StackingContextTreeClipStore;
41use crate::display_list::conversions::ToWebRender;
42use crate::display_list::{BuilderForBoxFragment, offset_radii};
43use crate::fragment_tree::{
44 BoxFragment, BoxFragmentWithStyle, ContainingBlockCalculation, ContainingBlockManager,
45 Fragment, FragmentFlags, FragmentTree, PositioningFragment,
46};
47use crate::geom::{
48 AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize,
49 PhysicalVec,
50};
51use crate::style_ext::{ComputedValuesExt, TransformExt};
52
53#[derive(Clone)]
54pub(crate) struct ContainingBlock {
55 scroll_node_id: ScrollTreeNodeId,
58
59 scroll_frame_size: Option<LayoutSize>,
63
64 clip_id: ClipId,
66
67 rect: PhysicalRect<Au>,
69
70 accumulated_reference_frame_offset: PhysicalVec<Au>,
76
77 established_scroll_frame: bool,
79}
80
81impl ContainingBlock {
82 pub(crate) fn new(
83 rect: PhysicalRect<Au>,
84 scroll_node_id: ScrollTreeNodeId,
85 scroll_frame_size: Option<LayoutSize>,
86 clip_id: ClipId,
87 accumulated_reference_frame_offset: PhysicalVec<Au>,
88 established_scroll_frame: bool,
89 ) -> Self {
90 ContainingBlock {
91 scroll_node_id,
92 scroll_frame_size,
93 clip_id,
94 rect,
95 accumulated_reference_frame_offset,
96 established_scroll_frame,
97 }
98 }
99
100 pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Au>) -> Self {
101 ContainingBlock {
102 rect: *rect,
103 ..*self
104 }
105 }
106}
107
108pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
109
110#[derive(MallocSizeOf)]
111pub(crate) struct StackingContextTree {
112 pub root_stacking_context: StackingContext,
114
115 pub paint_info: PaintDisplayListInfo,
120
121 pub clip_store: StackingContextTreeClipStore,
125}
126
127impl StackingContextTree {
128 pub fn new(
131 fragment_tree: &FragmentTree,
132 viewport_details: ViewportDetails,
133 pipeline_id: wr::PipelineId,
134 first_reflow: bool,
135 debug: &DiagnosticsLogging,
136 ) -> Self {
137 let scrollable_overflow = fragment_tree.scrollable_overflow();
138 let scroll_area = scrollable_overflow.union(&fragment_tree.initial_containing_block);
139 let scroll_area = LayoutSize::from_untyped(Size2D::new(
140 scroll_area.size.width.to_f32_px(),
141 scroll_area.size.height.to_f32_px(),
142 ));
143
144 let viewport_size = viewport_details.layout_size();
145 let paint_info = PaintDisplayListInfo::new(
146 viewport_details,
147 scroll_area,
148 pipeline_id,
149 Default::default(),
151 fragment_tree.viewport_scroll_sensitivity,
152 first_reflow,
153 );
154
155 let root_scroll_node_id = paint_info.root_scroll_node_id;
156 let cb_for_non_fixed_descendants = ContainingBlock::new(
157 fragment_tree.initial_containing_block,
158 root_scroll_node_id,
159 Some(viewport_size),
160 ClipId::INVALID,
161 PhysicalVec::zero(),
162 true,
163 );
164 let cb_for_fixed_descendants = ContainingBlock::new(
165 fragment_tree.initial_containing_block,
166 paint_info.root_reference_frame_id,
167 None,
168 ClipId::INVALID,
169 PhysicalVec::zero(),
170 false,
171 );
172
173 let containing_block_info = ContainingBlockInfo {
180 for_non_absolute_descendants: &cb_for_non_fixed_descendants,
181 for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
182 for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
183 };
184
185 let mut stacking_context_tree = Self {
186 root_stacking_context: StackingContext::root(root_scroll_node_id),
189 paint_info,
190 clip_store: Default::default(),
191 };
192
193 let text_decorations = Default::default();
194 let mut root_stacking_context = StackingContext::root(root_scroll_node_id);
195 if let Some(root_box_fragment) = fragment_tree.root_box_fragment() {
196 Fragment::Box(root_box_fragment).build_stacking_context_tree(
197 &mut stacking_context_tree,
198 &containing_block_info,
199 &mut root_stacking_context,
200 StackingContextBuildMode::IncludeHoisted,
203 &text_decorations,
204 false, );
206 }
207
208 root_stacking_context.sort();
209 stacking_context_tree.root_stacking_context = root_stacking_context;
210
211 if debug.is_enabled(DiagnosticsLoggingOption::StackingContextTree) {
212 stacking_context_tree
213 .root_stacking_context
214 .print(&mut PrintTree::new("Stacking Context Tree"));
215 }
216
217 stacking_context_tree
218 }
219
220 fn push_reference_frame(
221 &mut self,
222 origin: LayoutPoint,
223 frame_origin_for_query: LayoutPoint,
224 parent_scroll_node_id: ScrollTreeNodeId,
225 transform_style: wr::TransformStyle,
226 transform: LayoutTransform,
227 kind: wr::ReferenceFrameKind,
228 ) -> ScrollTreeNodeId {
229 self.paint_info.scroll_tree.add_scroll_tree_node(
230 Some(parent_scroll_node_id),
231 SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
232 origin,
233 frame_origin_for_query,
234 transform_style,
235 transform: transform.into(),
236 kind,
237 }),
238 )
239 }
240
241 fn define_scroll_frame(
242 &mut self,
243 parent_scroll_node_id: ScrollTreeNodeId,
244 external_id: wr::ExternalScrollId,
245 content_rect: LayoutRect,
246 clip_rect: LayoutRect,
247 scroll_sensitivity: AxesScrollSensitivity,
248 touch_action: TouchAction,
249 ) -> ScrollTreeNodeId {
250 self.paint_info.scroll_tree.add_scroll_tree_node(
251 Some(parent_scroll_node_id),
252 SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
253 external_id,
254 content_rect,
255 clip_rect,
256 scroll_sensitivity,
257 touch_action,
258 offset: LayoutVector2D::zero(),
259 offset_changed: Cell::new(false),
260 }),
261 )
262 }
263
264 fn define_sticky_frame(
265 &mut self,
266 parent_scroll_node_id: ScrollTreeNodeId,
267 frame_rect: LayoutRect,
268 margins: SideOffsets2D<Option<f32>, LayoutPixel>,
269 vertical_offset_bounds: StickyOffsetBounds,
270 horizontal_offset_bounds: StickyOffsetBounds,
271 ) -> ScrollTreeNodeId {
272 self.paint_info.scroll_tree.add_scroll_tree_node(
273 Some(parent_scroll_node_id),
274 SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
275 frame_rect,
276 margins,
277 vertical_offset_bounds,
278 horizontal_offset_bounds,
279 }),
280 )
281 }
282
283 pub(crate) fn offset_in_fragment(
291 &self,
292 fragment: &Fragment,
293 point_in_viewport: PhysicalPoint<Au>,
294 ) -> Option<Point2D<Au, CSSPixel>> {
295 let fragment = fragment.retrieve_box_fragment()?;
296 let spatial_tree_node = fragment.spatial_tree_node()?;
297 let transform = self
298 .paint_info
299 .scroll_tree
300 .cumulative_root_to_node_transform(spatial_tree_node)?;
301 let transformed_point = transform
302 .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
303 .map(Au::from_f32_px)
304 .cast_unit();
305
306 let reference_frame_origin = self
308 .paint_info
309 .scroll_tree
310 .reference_frame_offset(spatial_tree_node)
311 .map(Au::from_f32_px);
312 let fragment_origin = fragment
313 .cumulative_content_box_rect(
314 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
315 )
316 .origin -
317 reference_frame_origin.cast_unit();
318
319 Some(transformed_point - fragment_origin)
321 }
322}
323
324#[derive(Clone, Debug, MallocSizeOf)]
326pub(crate) struct FragmentTextDecoration {
327 pub line: TextDecorationLine,
328 pub color: AbsoluteColor,
329 pub style: TextDecorationStyle,
330 pub thickness: TextDecorationThickness,
331}
332
333#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
334pub(crate) enum StackingContextType {
335 StackingContext,
336 StackingContainer,
337}
338
339#[derive(MallocSizeOf)]
340pub(crate) enum StackingContextFragments {
341 Root,
342 Fragment(#[conditional_malloc_size_of] Arc<BoxFragment>),
343}
344
345#[derive(MallocSizeOf)]
346pub(crate) struct StackingContextReferenceFrameInfo {
347 pub(crate) parent_spatial_node_id: ScrollTreeNodeId,
348 pub(crate) captured_clip_id: ClipId,
349}
350
351#[derive(MallocSizeOf)]
358pub struct StackingContext {
359 pub(crate) fragment: StackingContextFragments,
364
365 pub(crate) context_type: StackingContextType,
368
369 pub(crate) children: Vec<StackingContext>,
371
372 pub(crate) containing_block_origin: PhysicalPoint<Au>,
375
376 pub(crate) scroll_tree_node_id: ScrollTreeNodeId,
378
379 pub(crate) clip_id: ClipId,
381
382 pub(crate) z_index: i32,
384
385 #[conditional_malloc_size_of]
387 pub(crate) text_decorations: Rc<Vec<FragmentTextDecoration>>,
388
389 pub(crate) reference_frame_info: Option<StackingContextReferenceFrameInfo>,
392
393 pub(crate) participates_in_a_3d_rendering_context: bool,
396}
397
398impl StackingContext {
399 fn root(scroll_tree_node_id: ScrollTreeNodeId) -> Self {
400 Self {
401 fragment: StackingContextFragments::Root,
402 context_type: StackingContextType::StackingContext,
403 children: Default::default(),
404 containing_block_origin: Default::default(),
405 scroll_tree_node_id,
406 clip_id: ClipId::INVALID,
407 z_index: 0,
408 text_decorations: Default::default(),
409 reference_frame_info: None,
410 participates_in_a_3d_rendering_context: false,
411 }
412 }
413
414 #[expect(clippy::too_many_arguments)]
415 fn create_descendant(
416 &self,
417 context_type: StackingContextType,
418 containing_block_offset: PhysicalPoint<Au>,
419 spatial_id: ScrollTreeNodeId,
420 clip_id: ClipId,
421 initializing_fragment: Arc<BoxFragment>,
422 text_decorations: Rc<Vec<FragmentTextDecoration>>,
423 reference_frame_info: Option<StackingContextReferenceFrameInfo>,
424 participates_in_a_3d_rendering_context: bool,
425 ) -> Self {
426 let z_index = initializing_fragment
427 .style()
428 .effective_z_index(initializing_fragment.base.flags);
429 Self {
430 fragment: StackingContextFragments::Fragment(initializing_fragment),
431 context_type,
432 containing_block_origin: containing_block_offset,
433 children: Default::default(),
434 scroll_tree_node_id: spatial_id,
435 clip_id,
436 z_index,
437 text_decorations,
438 reference_frame_info,
439 participates_in_a_3d_rendering_context,
440 }
441 }
442
443 pub(crate) fn fragment(&self) -> Option<&Arc<BoxFragment>> {
444 match &self.fragment {
445 StackingContextFragments::Root => None,
446 StackingContextFragments::Fragment(box_fragment) => Some(box_fragment),
447 }
448 }
449
450 fn sort(&mut self) {
451 self.children.sort_by_key(|child| child.z_index)
452 }
453
454 fn print(&self, tree: &mut PrintTree) {
455 let fragment_string = match &self.fragment {
456 StackingContextFragments::Root => "Root".into(),
457 StackingContextFragments::Fragment(box_fragment) => format!(
458 "{:?} rect={:?}",
459 box_fragment.base.tag,
460 box_fragment.content_rect()
461 ),
462 };
463
464 tree.new_level(format!(
465 "{fragment_string} z-index={:?} spatial={:?} clip={:?}",
466 self.z_index, self.scroll_tree_node_id, self.clip_id
467 ));
468
469 for child in self.children.iter() {
470 child.print(tree);
471 }
472
473 tree.end_level();
474 }
475}
476
477#[derive(Clone, Copy, PartialEq)]
478pub(crate) enum StackingContextBuildMode {
479 IncludeHoisted,
480 SkipHoisted,
481}
482
483impl Fragment {
484 pub(crate) fn build_stacking_context_tree(
485 &self,
486 stacking_context_tree: &mut StackingContextTree,
487 containing_block_info: &ContainingBlockInfo,
488 stacking_context: &mut StackingContext,
489 mode: StackingContextBuildMode,
490 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
491 participates_in_3d_rendering_context: bool,
492 ) {
493 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
494 let cumulative_containing_block = containing_block
495 .rect
496 .translate(containing_block.accumulated_reference_frame_offset);
497 self.set_containing_block(&cumulative_containing_block);
498
499 if self
500 .base()
501 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
502 {
503 return;
504 }
505
506 let fragment_clone = self.clone();
507 match self {
508 Fragment::Box(fragment) | Fragment::Float(fragment) => {
509 if mode == StackingContextBuildMode::SkipHoisted &&
510 fragment
511 .style()
512 .get_box()
513 .position
514 .is_absolutely_positioned()
515 {
516 return;
517 }
518
519 let text_decorations = match self {
520 Fragment::Float(..) => &Default::default(),
521 _ => text_decorations,
522 };
523
524 fragment.build_stacking_context_tree(
525 fragment_clone,
526 stacking_context_tree,
527 containing_block,
528 containing_block_info,
529 stacking_context,
530 text_decorations,
531 participates_in_3d_rendering_context,
532 );
533 },
534 Fragment::LayoutRoot(..) => {
535 },
538 Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
539 let shared_fragment = fragment.borrow();
540 let fragment_ref = match shared_fragment.fragment.as_ref() {
541 Some(fragment_ref) => fragment_ref,
542 None => unreachable!("Found hoisted box with missing fragment."),
543 };
544
545 fragment_ref.build_stacking_context_tree(
546 stacking_context_tree,
547 containing_block_info,
548 stacking_context,
549 StackingContextBuildMode::IncludeHoisted,
550 &Default::default(),
551 participates_in_3d_rendering_context,
552 );
553 },
554 Fragment::Positioning(fragment) => {
555 fragment.build_stacking_context_tree(
556 stacking_context_tree,
557 containing_block,
558 containing_block_info,
559 stacking_context,
560 text_decorations,
561 participates_in_3d_rendering_context,
562 );
563 },
564 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {},
565 }
566 }
567}
568
569struct ReferenceFrameData {
570 origin: PhysicalPoint<Au>,
571 transform: LayoutTransform,
572 kind: wr::ReferenceFrameKind,
573}
574struct ScrollFrameData {
575 scroll_tree_node_id: ScrollTreeNodeId,
576 scroll_frame_rect: LayoutRect,
577}
578
579struct OverflowFrameData {
580 clip_id: ClipId,
581 scroll_frame_data: Option<ScrollFrameData>,
582}
583
584impl BoxFragment {
585 pub(crate) fn stacking_context_type(&self) -> Option<StackingContextType> {
586 let flags = self.base.flags;
587 let style = self.style();
588 if style.establishes_stacking_context(flags) {
589 return Some(StackingContextType::StackingContext);
590 }
591
592 let box_style = &style.get_box();
593 if box_style.position != ComputedPosition::Static {
594 return Some(StackingContextType::StackingContainer);
595 }
596
597 None
598 }
599
600 #[expect(clippy::too_many_arguments)]
601 fn build_stacking_context_tree(
602 self: &Arc<Self>,
603 fragment: Fragment,
604 stacking_context_tree: &mut StackingContextTree,
605 containing_block: &ContainingBlock,
606 containing_block_info: &ContainingBlockInfo,
607 parent_stacking_context: &mut StackingContext,
608 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
609 participates_in_3d_rendering_context: bool,
610 ) {
611 self.clear_stacking_context_tree_traversal_data();
612 self.build_stacking_context_tree_maybe_creating_reference_frame(
613 fragment,
614 stacking_context_tree,
615 containing_block,
616 containing_block_info,
617 parent_stacking_context,
618 text_decorations,
619 participates_in_3d_rendering_context,
620 );
621 }
622
623 #[expect(clippy::too_many_arguments)]
624 fn build_stacking_context_tree_maybe_creating_reference_frame(
625 self: &Arc<Self>,
626 fragment: Fragment,
627 stacking_context_tree: &mut StackingContextTree,
628 containing_block: &ContainingBlock,
629 containing_block_info: &ContainingBlockInfo,
630 parent_stacking_context: &mut StackingContext,
631 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
632 participates_in_3d_rendering_context: bool,
633 ) {
634 let reference_frame_data =
635 match self.reference_frame_data_if_necessary(&containing_block.rect) {
636 Some(reference_frame_data) => reference_frame_data,
637 None => {
638 return self.build_stacking_context_tree_maybe_creating_stacking_context(
639 fragment,
640 stacking_context_tree,
641 containing_block,
642 containing_block_info,
643 parent_stacking_context,
644 text_decorations,
645 None, participates_in_3d_rendering_context,
647 );
648 },
649 };
650
651 if !reference_frame_data.transform.is_invertible() {
655 self.clear_stacking_context_tree_traversal_data_recursively();
656 return;
657 }
658
659 let style = self.style();
660 let frame_origin_for_query = self
661 .cumulative_border_box_rect(
662 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
663 )
664 .origin
665 .to_webrender();
666 let new_spatial_id = stacking_context_tree.push_reference_frame(
667 reference_frame_data.origin.to_webrender(),
668 frame_origin_for_query,
669 containing_block.scroll_node_id,
670 style.used_transform_style(self.base.flags).to_webrender(),
671 reference_frame_data.transform,
672 reference_frame_data.kind,
673 );
674
675 assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
685 let reference_frame_offset = reference_frame_data.origin.to_vector();
686 let adjusted_containing_block = ContainingBlock::new(
687 containing_block.rect.translate(-reference_frame_offset),
688 new_spatial_id,
689 None,
690 ClipId::INVALID,
691 containing_block.accumulated_reference_frame_offset + reference_frame_offset,
692 false,
693 );
694 let new_containing_block_info =
695 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
696
697 let reference_frame_info = StackingContextReferenceFrameInfo {
698 parent_spatial_node_id: containing_block.scroll_node_id,
699 captured_clip_id: containing_block.clip_id,
700 };
701
702 self.build_stacking_context_tree_maybe_creating_stacking_context(
703 fragment,
704 stacking_context_tree,
705 &adjusted_containing_block,
706 &new_containing_block_info,
707 parent_stacking_context,
708 text_decorations,
709 Some(reference_frame_info),
710 participates_in_3d_rendering_context,
711 );
712 }
713
714 #[expect(clippy::too_many_arguments)]
715 fn build_stacking_context_tree_maybe_creating_stacking_context(
716 self: &Arc<Self>,
717 fragment: Fragment,
718 stacking_context_tree: &mut StackingContextTree,
719 containing_block: &ContainingBlock,
720 containing_block_info: &ContainingBlockInfo,
721 parent_stacking_context: &mut StackingContext,
722 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
723 reference_frame_info: Option<StackingContextReferenceFrameInfo>,
724 participates_in_3d_rendering_context: bool,
725 ) {
726 let with_style = &self.with_style();
727 let style = with_style.style();
728 let Some(stacking_context_type) = self.stacking_context_type() else {
729 with_style.build_stacking_context_tree_for_children(
730 stacking_context_tree,
731 containing_block,
732 containing_block_info,
733 parent_stacking_context,
734 text_decorations,
735 participates_in_3d_rendering_context,
736 );
737 return;
738 };
739
740 let new_scroll_frame_size = containing_block_info
741 .for_non_absolute_descendants
742 .scroll_frame_size;
743 let spatial_id = self.build_sticky_frame_if_necessary(
744 stacking_context_tree,
745 containing_block.scroll_node_id,
746 &containing_block.rect,
747 &new_scroll_frame_size,
748 containing_block.established_scroll_frame,
749 );
750
751 let clip_id = with_style.build_clip_frame_if_necessary(
752 stacking_context_tree,
753 spatial_id.unwrap_or(containing_block.scroll_node_id),
754 containing_block.clip_id,
755 &containing_block.rect,
756 );
757
758 let clip_id = stacking_context_tree
759 .clip_store
760 .add_for_clip_path(
761 &style.get_svg().clip_path,
762 spatial_id.unwrap_or(containing_block.scroll_node_id),
763 clip_id.unwrap_or(containing_block.clip_id),
764 with_style,
765 containing_block.rect.origin,
766 )
767 .or(clip_id);
768
769 let containing_block = if clip_id.is_some() || spatial_id.is_some() {
770 if let Some(clip_id) = clip_id {
771 self.set_generated_clip_id(clip_id);
772 }
773 if let Some(spatial_id) = spatial_id {
774 self.set_generated_scroll_tree_node_id(spatial_id);
775 }
776 &ContainingBlock {
777 scroll_node_id: spatial_id.unwrap_or(containing_block.scroll_node_id),
778 clip_id: clip_id.unwrap_or(containing_block.clip_id),
779 ..*containing_block
780 }
781 } else {
782 containing_block
783 };
784
785 let box_fragment = fragment
786 .retrieve_box_fragment()
787 .expect("Should never try to make stacking context for non-BoxFragment")
788 .clone();
789
790 let mut child_stacking_context = parent_stacking_context.create_descendant(
791 stacking_context_type,
792 containing_block.rect.origin,
793 containing_block.scroll_node_id,
794 containing_block.clip_id,
795 box_fragment,
796 text_decorations.clone(),
797 reference_frame_info,
798 participates_in_3d_rendering_context,
799 );
800 with_style.build_stacking_context_tree_for_children(
801 stacking_context_tree,
802 containing_block,
803 containing_block_info,
804 &mut child_stacking_context,
805 text_decorations,
806 participates_in_3d_rendering_context,
807 );
808
809 let mut stolen_children = vec![];
810 if stacking_context_type != StackingContextType::StackingContext {
811 stolen_children =
812 std::mem::replace(&mut child_stacking_context.children, stolen_children);
813 } else {
814 child_stacking_context.sort();
815 }
816
817 parent_stacking_context
818 .children
819 .push(child_stacking_context);
820 parent_stacking_context
821 .children
822 .append(&mut stolen_children);
823 }
824}
825
826impl BoxFragmentWithStyle<'_> {
827 fn build_stacking_context_tree_for_children(
828 &self,
829 stacking_context_tree: &mut StackingContextTree,
830 containing_block: &ContainingBlock,
831 containing_block_info: &ContainingBlockInfo,
832 stacking_context: &mut StackingContext,
833 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
834 participates_in_3d_rendering_context: bool,
835 ) {
836 let style = self.style();
837 let establishes_containing_block_for_all_descendants =
838 style.establishes_containing_block_for_all_descendants(self.base.flags);
839 let establishes_containing_block_for_absolute_descendants =
840 style.establishes_containing_block_for_absolute_descendants(self.base.flags);
841
842 let mut new_scroll_node_id = containing_block.scroll_node_id;
843 self.spatial_tree_node.set(Some(new_scroll_node_id));
844
845 let mut new_scroll_frame_size = containing_block_info
848 .for_non_absolute_descendants
849 .scroll_frame_size;
850
851 let mut established_scroll_frame = containing_block.established_scroll_frame;
852 if established_scroll_frame && !self.base.is_anonymous() {
855 established_scroll_frame = false;
856 }
857 let mut new_clip_id = containing_block.clip_id;
858 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
859 stacking_context_tree,
860 new_scroll_node_id,
861 new_clip_id,
862 &containing_block.rect,
863 ) {
864 new_clip_id = overflow_frame_data.clip_id;
865 self.set_generated_clip_id(new_clip_id);
866
867 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
868 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
869 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
870 established_scroll_frame = true;
871 self.set_generated_scroll_tree_node_id(new_scroll_node_id);
872 }
873 }
874
875 let padding_rect = self
876 .padding_rect()
877 .translate(containing_block.rect.origin.to_vector());
878 let content_rect = self
879 .content_rect()
880 .translate(containing_block.rect.origin.to_vector());
881
882 let for_absolute_descendants = ContainingBlock::new(
883 padding_rect,
884 new_scroll_node_id,
885 new_scroll_frame_size,
886 new_clip_id,
887 containing_block.accumulated_reference_frame_offset,
888 established_scroll_frame,
889 );
890 let for_non_absolute_descendants = ContainingBlock::new(
891 content_rect,
892 new_scroll_node_id,
893 new_scroll_frame_size,
894 new_clip_id,
895 containing_block.accumulated_reference_frame_offset,
896 established_scroll_frame,
897 );
898
899 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
903 containing_block_info.new_for_absolute_and_fixed_descendants(
904 &for_non_absolute_descendants,
905 &for_absolute_descendants,
906 )
907 } else if establishes_containing_block_for_absolute_descendants {
908 containing_block_info.new_for_absolute_descendants(
909 &for_non_absolute_descendants,
910 &for_absolute_descendants,
911 )
912 } else {
913 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
914 };
915
916 let text_decorations = match self.is_atomic_inline_level() ||
922 self.base
923 .flags
924 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
925 {
926 true => &Default::default(),
927 false => text_decorations,
928 };
929
930 let new_text_decoration;
931 let text_decorations = match style.get_text_decoration_line() {
932 &TextDecorationLine::NONE => text_decorations,
933 line => {
934 let mut new_vector = (**text_decorations).clone();
935 let color = style.get_color();
936 new_vector.push(FragmentTextDecoration {
937 line: *line,
938 color: style.get_text_decoration_color().resolve_to_absolute(color),
939 style: style.slow_clone_text_decoration_style(),
940 thickness: style.slow_clone_text_decoration_thickness(),
941 });
942 new_text_decoration = Rc::new(new_vector);
943 &new_text_decoration
944 },
945 };
946
947 let participates_in_3d_rendering_context = if self.base.is_anonymous() {
948 participates_in_3d_rendering_context
949 } else {
950 self.style()
951 .establishes_or_extends_3d_rendering_context(self.base.flags)
952 };
953
954 for child in &self.children {
955 child.build_stacking_context_tree(
956 stacking_context_tree,
957 &new_containing_block_info,
958 stacking_context,
959 StackingContextBuildMode::SkipHoisted,
960 text_decorations,
961 participates_in_3d_rendering_context,
962 );
963 }
964 }
965
966 fn build_clip_frame_if_necessary(
967 &self,
968 stacking_context_tree: &mut StackingContextTree,
969 parent_scroll_node_id: ScrollTreeNodeId,
970 parent_clip_id: ClipId,
971 containing_block_rect: &PhysicalRect<Au>,
972 ) -> Option<ClipId> {
973 let style = self.style();
974 let position = style.get_box().position;
975 if !position.is_absolutely_positioned() {
978 return None;
979 }
980
981 let clip_rect = match style.get_effects().clip {
983 ClipRectOrAuto::Rect(rect) => rect,
984 _ => return None,
985 };
986
987 let border_rect = self.border_rect();
988 let clip_rect = clip_rect
989 .for_border_rect(border_rect)
990 .translate(containing_block_rect.origin.to_vector())
991 .to_webrender();
992 Some(stacking_context_tree.clip_store.add(
993 BorderRadius::zero(),
994 clip_rect,
995 parent_scroll_node_id,
996 parent_clip_id,
997 ))
998 }
999
1000 fn build_overflow_frame_if_necessary(
1001 &self,
1002 stacking_context_tree: &mut StackingContextTree,
1003 parent_scroll_node_id: ScrollTreeNodeId,
1004 parent_clip_id: ClipId,
1005 containing_block_rect: &PhysicalRect<Au>,
1006 ) -> Option<OverflowFrameData> {
1007 let style = self.style();
1008 let overflow = style.effective_overflow(self.base.flags);
1009
1010 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
1011 return None;
1012 }
1013
1014 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
1016 let overflow_clip_margin = style.get_margin().overflow_clip_margin;
1017 let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
1018 OverflowClipMarginBox::ContentBox => self.content_rect(),
1019 OverflowClipMarginBox::PaddingBox => self.padding_rect(),
1020 OverflowClipMarginBox::BorderBox => self.border_rect(),
1021 }
1022 .translate(containing_block_rect.origin.to_vector())
1023 .to_webrender();
1024
1025 let clip_margin_offset = overflow_clip_margin.offset.px();
1028 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
1029
1030 let radii;
1033 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
1034 let builder = BuilderForBoxFragment::new(self, containing_block_rect.origin);
1035 let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
1036 match overflow_clip_margin.visual_box {
1037 OverflowClipMarginBox::ContentBox => {
1038 offsets_from_border -= (self.border + self.padding).to_webrender();
1039 },
1040 OverflowClipMarginBox::PaddingBox => {
1041 offsets_from_border -= self.border.to_webrender();
1042 },
1043 OverflowClipMarginBox::BorderBox => {},
1044 };
1045 radii = offset_radii(builder.border_radius(), offsets_from_border);
1046 } else if overflow.x != ComputedOverflow::Clip {
1047 let max = LayoutRect::max_rect();
1048 overflow_clip_rect.min.x = max.min.x;
1049 overflow_clip_rect.max.x = max.max.x;
1050 radii = BorderRadius::zero();
1051 } else {
1052 let max = LayoutRect::max_rect();
1053 overflow_clip_rect.min.y = max.min.y;
1054 overflow_clip_rect.max.y = max.max.y;
1055 radii = BorderRadius::zero();
1056 }
1057
1058 let clip_id = stacking_context_tree.clip_store.add(
1059 radii,
1060 overflow_clip_rect,
1061 parent_scroll_node_id,
1062 parent_clip_id,
1063 );
1064
1065 return Some(OverflowFrameData {
1066 clip_id,
1067 scroll_frame_data: None,
1068 });
1069 }
1070
1071 let scroll_frame_rect = self
1072 .padding_rect()
1073 .translate(containing_block_rect.origin.to_vector())
1074 .to_webrender();
1075
1076 let clip_id = stacking_context_tree.clip_store.add(
1077 BuilderForBoxFragment::new(self, containing_block_rect.origin).border_radius(),
1078 scroll_frame_rect,
1079 parent_scroll_node_id,
1080 parent_clip_id,
1081 );
1082
1083 let tag = self.base.tag?;
1084 let external_scroll_id = wr::ExternalScrollId(
1085 tag.to_display_list_fragment_id(),
1086 stacking_context_tree.paint_info.pipeline_id,
1087 );
1088
1089 let mut x_sensitivity: ScrollType = overflow.x.into();
1090 let mut y_sensitivity: ScrollType = overflow.y.into();
1091 let touch_action = TouchAction::from(style.get_box().touch_action);
1092 match touch_action {
1096 TouchAction::PanX => {
1097 y_sensitivity.remove(ScrollType::Touch);
1098 },
1099 TouchAction::PanY => {
1100 x_sensitivity.remove(ScrollType::Touch);
1101 },
1102 TouchAction::None => {
1103 x_sensitivity.remove(ScrollType::Touch);
1104 y_sensitivity.remove(ScrollType::Touch);
1105 },
1106 TouchAction::Auto => {},
1107 }
1108 let sensitivity = AxesScrollSensitivity {
1109 x: x_sensitivity,
1110 y: y_sensitivity,
1111 };
1112
1113 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1114 parent_scroll_node_id,
1115 external_scroll_id,
1116 self.scrollable_overflow().to_webrender(),
1117 scroll_frame_rect,
1118 sensitivity,
1119 touch_action,
1120 );
1121
1122 Some(OverflowFrameData {
1123 clip_id,
1124 scroll_frame_data: Some(ScrollFrameData {
1125 scroll_tree_node_id,
1126 scroll_frame_rect,
1127 }),
1128 })
1129 }
1130}
1131
1132impl BoxFragment {
1133 fn build_sticky_frame_if_necessary(
1134 &self,
1135 stacking_context_tree: &mut StackingContextTree,
1136 parent_scroll_node_id: ScrollTreeNodeId,
1137 containing_block_rect: &PhysicalRect<Au>,
1138 scroll_frame_size: &Option<LayoutSize>,
1139 established_scroll_frame: bool,
1140 ) -> Option<ScrollTreeNodeId> {
1141 let style = self.style();
1142 if style.get_box().position != ComputedPosition::Sticky {
1143 return None;
1144 }
1145
1146 let scroll_frame_size_for_resolve = match scroll_frame_size {
1147 Some(size) => size,
1148 None => {
1149 &stacking_context_tree
1151 .paint_info
1152 .viewport_details
1153 .layout_size()
1154 },
1155 };
1156
1157 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1161 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1162 let offsets = style.physical_box_offsets();
1163 let offsets = PhysicalSides::<AuOrAuto>::new(
1164 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1165 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1166 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1167 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1168 );
1169 self.set_resolved_sticky_insets(offsets);
1170
1171 if scroll_frame_size.is_none() {
1172 return None;
1173 }
1174
1175 if offsets.top.is_auto() &&
1176 offsets.right.is_auto() &&
1177 offsets.bottom.is_auto() &&
1178 offsets.left.is_auto()
1179 {
1180 return None;
1181 }
1182
1183 let border_rect = self.border_rect();
1202 let computed_margin = style.physical_margin();
1203 let parent_scroll_node = stacking_context_tree
1204 .paint_info
1205 .scroll_tree
1206 .get_node(parent_scroll_node_id);
1207 let sticky_offset_boundary = match parent_scroll_node.info {
1208 SpatialTreeNodeInfo::Scroll(ref scrollable_node_info) if established_scroll_frame => {
1209 let content_rect = &scrollable_node_info.content_rect;
1210 &PhysicalRect::new(
1211 PhysicalPoint::new(
1212 Au::from_f32_px(content_rect.min.x),
1213 Au::from_f32_px(content_rect.min.y),
1214 ),
1215 PhysicalSize::new(
1216 Au::from_f32_px(content_rect.max.x - content_rect.min.x),
1217 Au::from_f32_px(content_rect.max.y - content_rect.min.y),
1218 ),
1219 )
1220 },
1221 _ => containing_block_rect,
1222 };
1223 let distance_from_border_box_to_cb = PhysicalSides::new(
1227 border_rect.min_y(),
1228 sticky_offset_boundary.width() - border_rect.max_x(),
1229 sticky_offset_boundary.height() - border_rect.max_y(),
1230 border_rect.min_x(),
1231 );
1232 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1236 let used_margin = if computed_margin.is_auto() {
1237 Au::zero()
1238 } else {
1239 used_margin
1240 };
1241 Au::zero().max(distance - used_margin).to_f32_px()
1242 };
1243
1244 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1247 -offset_bound(
1248 distance_from_border_box_to_cb.top,
1249 self.margin.top,
1250 computed_margin.top,
1251 ),
1252 offset_bound(
1253 distance_from_border_box_to_cb.bottom,
1254 self.margin.bottom,
1255 computed_margin.bottom,
1256 ),
1257 );
1258 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1259 -offset_bound(
1260 distance_from_border_box_to_cb.left,
1261 self.margin.left,
1262 computed_margin.left,
1263 ),
1264 offset_bound(
1265 distance_from_border_box_to_cb.right,
1266 self.margin.right,
1267 computed_margin.right,
1268 ),
1269 );
1270
1271 let frame_rect = border_rect
1272 .translate(containing_block_rect.origin.to_vector())
1273 .to_webrender();
1274
1275 let margins = SideOffsets2D::new(
1278 offsets.top.non_auto().map(|v| v.to_f32_px()),
1279 offsets.right.non_auto().map(|v| v.to_f32_px()),
1280 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1281 offsets.left.non_auto().map(|v| v.to_f32_px()),
1282 );
1283
1284 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1285 parent_scroll_node_id,
1286 frame_rect,
1287 margins,
1288 vertical_offset_bounds,
1289 horizontal_offset_bounds,
1290 );
1291
1292 Some(sticky_node_id)
1293 }
1294
1295 fn reference_frame_data_if_necessary(
1297 &self,
1298 containing_block_rect: &PhysicalRect<Au>,
1299 ) -> Option<ReferenceFrameData> {
1300 if !self
1301 .style()
1302 .has_effective_transform_or_perspective(self.base.flags)
1303 {
1304 return None;
1305 }
1306
1307 let relative_border_rect = self.border_rect();
1308 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1309 let transform = self.calculate_transform_matrix(&border_rect);
1310 let perspective = self.calculate_perspective_matrix(&border_rect);
1311 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1312 (None, Some(perspective)) => (
1313 perspective,
1314 wr::ReferenceFrameKind::Perspective {
1315 scrolling_relative_to: None,
1316 },
1317 ),
1318 (Some(transform), None) => (
1319 transform,
1320 wr::ReferenceFrameKind::Transform {
1321 is_2d_scale_translation: false,
1322 should_snap: false,
1323 paired_with_perspective: false,
1324 },
1325 ),
1326 (Some(transform), Some(perspective)) => (
1327 perspective.then(&transform),
1328 wr::ReferenceFrameKind::Perspective {
1329 scrolling_relative_to: None,
1330 },
1331 ),
1332 (None, None) => unreachable!(),
1333 };
1334
1335 Some(ReferenceFrameData {
1336 origin: border_rect.origin,
1337 transform: reference_frame_transform,
1338 kind: reference_frame_kind,
1339 })
1340 }
1341
1342 pub fn calculate_transform_matrix(
1344 &self,
1345 border_rect: &Rect<Au, CSSPixel>,
1346 ) -> Option<LayoutTransform> {
1347 let style = self.style();
1348 let list = &style.get_box().transform;
1349 let length_rect = au_rect_to_length_rect(border_rect);
1350 let rotate = match style.get_rotate() {
1352 GenericRotate::Rotate(angle) => (0., 0., 1., *angle),
1353 GenericRotate::Rotate3D(x, y, z, angle) => {
1354 get_normalized_vector_and_angle(*x, *y, *z, *angle)
1357 },
1358 GenericRotate::None => (0., 0., 1., Angle::zero()),
1359 };
1360 let scale = match style.get_scale() {
1361 GenericScale::Scale(sx, sy, sz) => (*sx, *sy, *sz),
1362 GenericScale::None => (1., 1., 1.),
1363 };
1364 let translation = match style.get_translate() {
1365 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1366 x.resolve(length_rect.size.width).px(),
1367 y.resolve(length_rect.size.height).px(),
1368 z.px(),
1369 ),
1370 GenericTranslate::None => LayoutTransform::identity(),
1371 };
1372
1373 let angle = euclid::Angle::radians(rotate.3.radians());
1374 let transform_base = list
1375 .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1376 .ok()?;
1377 let transform = LayoutTransform::from_untyped(&transform_base.0)
1378 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1379 .then_scale(scale.0, scale.1, scale.2)
1380 .then(&translation);
1381
1382 let transform_origin = &style.get_box().transform_origin;
1383 let transform_origin_x = transform_origin
1384 .horizontal
1385 .to_used_value(border_rect.size.width)
1386 .to_f32_px();
1387 let transform_origin_y = transform_origin
1388 .vertical
1389 .to_used_value(border_rect.size.height)
1390 .to_f32_px();
1391 let transform_origin_z = transform_origin.depth.px();
1392
1393 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1394 }
1395
1396 pub fn calculate_perspective_matrix(
1398 &self,
1399 border_rect: &Rect<Au, CSSPixel>,
1400 ) -> Option<LayoutTransform> {
1401 let style = self.style();
1402 match style.get_box().perspective {
1403 Perspective::Length(length) => {
1404 let perspective_origin = &style.get_box().perspective_origin;
1405 let perspective_origin = LayoutPoint::new(
1406 perspective_origin
1407 .horizontal
1408 .percentage_relative_to(border_rect.size.width.into())
1409 .px(),
1410 perspective_origin
1411 .vertical
1412 .percentage_relative_to(border_rect.size.height.into())
1413 .px(),
1414 );
1415
1416 let perspective_matrix = LayoutTransform::from_untyped(
1417 &transform::create_perspective_matrix(length.px()),
1418 );
1419
1420 Some(perspective_matrix.change_basis(
1421 perspective_origin.x,
1422 perspective_origin.y,
1423 0.0,
1424 ))
1425 },
1426 Perspective::None => None,
1427 }
1428 }
1429
1430 fn clear_stacking_context_tree_traversal_data_recursively(&self) {
1431 fn clear_stacking_context_tree_traversal_data_on_fragments(fragments: &[Fragment]) {
1432 for fragment in fragments.iter() {
1433 match fragment {
1434 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
1435 .inner_box_fragment()
1436 .clear_stacking_context_tree_traversal_data_recursively(),
1437 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1438 box_fragment.clear_stacking_context_tree_traversal_data_recursively();
1439 },
1440 Fragment::Positioning(positioning_fragment) => {
1441 clear_stacking_context_tree_traversal_data_on_fragments(
1442 &positioning_fragment.children,
1443 );
1444 },
1445 _ => {},
1446 }
1447 }
1448 }
1449
1450 self.spatial_tree_node.set(None);
1451 self.clear_stacking_context_tree_traversal_data();
1452 clear_stacking_context_tree_traversal_data_on_fragments(&self.children);
1453 }
1454}
1455
1456impl PositioningFragment {
1457 fn build_stacking_context_tree(
1458 &self,
1459 stacking_context_tree: &mut StackingContextTree,
1460 containing_block: &ContainingBlock,
1461 containing_block_info: &ContainingBlockInfo,
1462 stacking_context: &mut StackingContext,
1463 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
1464 participates_in_3d_rendering_context: bool,
1465 ) {
1466 let rect = self
1467 .base
1468 .rect()
1469 .translate(containing_block.rect.origin.to_vector());
1470 let new_containing_block = containing_block.new_replacing_rect(&rect);
1471 let new_containing_block_info =
1472 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1473
1474 for child in &self.children {
1475 child.build_stacking_context_tree(
1476 stacking_context_tree,
1477 &new_containing_block_info,
1478 stacking_context,
1479 StackingContextBuildMode::SkipHoisted,
1480 text_decorations,
1481 participates_in_3d_rendering_context,
1482 );
1483 }
1484 }
1485}
1486
1487pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1488 Rect::new(
1489 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1490 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1491 )
1492}