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