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, ScrollableNodeInfo,
15 SpatialTreeNodeInfo, StickyNodeInfo,
16};
17use servo_base::id::ScrollTreeNodeId;
18use servo_base::print_tree::PrintTree;
19use servo_config::opts::{DiagnosticsLogging, DiagnosticsLoggingOption};
20use servo_geometry::MaxRect;
21use style::Zero;
22use style::color::AbsoluteColor;
23use style::computed_values::overflow_x::T as ComputedOverflow;
24use style::computed_values::position::T as ComputedPosition;
25use style::computed_values::text_decoration_style::T as TextDecorationStyle;
26use style::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, PhysicalVec,
49};
50use crate::style_ext::{ComputedValuesExt, TransformExt};
51
52#[derive(Clone)]
53pub(crate) struct ContainingBlock {
54 scroll_node_id: ScrollTreeNodeId,
57
58 scroll_frame_size: Option<LayoutSize>,
62
63 clip_id: ClipId,
65
66 rect: PhysicalRect<Au>,
68
69 accumulated_reference_frame_offset: PhysicalVec<Au>,
75}
76
77impl ContainingBlock {
78 pub(crate) fn new(
79 rect: PhysicalRect<Au>,
80 scroll_node_id: ScrollTreeNodeId,
81 scroll_frame_size: Option<LayoutSize>,
82 clip_id: ClipId,
83 accumulated_reference_frame_offset: PhysicalVec<Au>,
84 ) -> Self {
85 ContainingBlock {
86 scroll_node_id,
87 scroll_frame_size,
88 clip_id,
89 rect,
90 accumulated_reference_frame_offset,
91 }
92 }
93
94 pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Au>) -> Self {
95 ContainingBlock {
96 rect: *rect,
97 ..*self
98 }
99 }
100}
101
102pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
103
104#[derive(MallocSizeOf)]
105pub(crate) struct StackingContextTree {
106 pub root_stacking_context: StackingContext,
108
109 pub paint_info: PaintDisplayListInfo,
114
115 pub clip_store: StackingContextTreeClipStore,
119}
120
121impl StackingContextTree {
122 pub fn new(
125 fragment_tree: &FragmentTree,
126 viewport_details: ViewportDetails,
127 pipeline_id: wr::PipelineId,
128 first_reflow: bool,
129 debug: &DiagnosticsLogging,
130 ) -> Self {
131 let scrollable_overflow = fragment_tree.scrollable_overflow();
132 let scroll_area = scrollable_overflow.union(&fragment_tree.initial_containing_block);
133 let scroll_area = LayoutSize::from_untyped(Size2D::new(
134 scroll_area.size.width.to_f32_px(),
135 scroll_area.size.height.to_f32_px(),
136 ));
137
138 let viewport_size = viewport_details.layout_size();
139 let paint_info = PaintDisplayListInfo::new(
140 viewport_details,
141 scroll_area,
142 pipeline_id,
143 Default::default(),
145 fragment_tree.viewport_scroll_sensitivity,
146 first_reflow,
147 );
148
149 let root_scroll_node_id = paint_info.root_scroll_node_id;
150 let cb_for_non_fixed_descendants = ContainingBlock::new(
151 fragment_tree.initial_containing_block,
152 root_scroll_node_id,
153 Some(viewport_size),
154 ClipId::INVALID,
155 PhysicalVec::zero(),
156 );
157 let cb_for_fixed_descendants = ContainingBlock::new(
158 fragment_tree.initial_containing_block,
159 paint_info.root_reference_frame_id,
160 None,
161 ClipId::INVALID,
162 PhysicalVec::zero(),
163 );
164
165 let containing_block_info = ContainingBlockInfo {
172 for_non_absolute_descendants: &cb_for_non_fixed_descendants,
173 for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
174 for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
175 };
176
177 let mut stacking_context_tree = Self {
178 root_stacking_context: StackingContext::root(root_scroll_node_id),
181 paint_info,
182 clip_store: Default::default(),
183 };
184
185 let text_decorations = Default::default();
186 let mut root_stacking_context = StackingContext::root(root_scroll_node_id);
187 if let Some(root_box_fragment) = fragment_tree.root_box_fragment() {
188 Fragment::Box(root_box_fragment).build_stacking_context_tree(
189 &mut stacking_context_tree,
190 &containing_block_info,
191 &mut root_stacking_context,
192 StackingContextBuildMode::IncludeHoisted,
195 &text_decorations,
196 );
197 }
198
199 root_stacking_context.sort();
200 stacking_context_tree.root_stacking_context = root_stacking_context;
201
202 if debug.is_enabled(DiagnosticsLoggingOption::StackingContextTree) {
203 stacking_context_tree
204 .root_stacking_context
205 .print(&mut PrintTree::new("Stacking Context Tree"));
206 }
207
208 stacking_context_tree
209 }
210
211 fn push_reference_frame(
212 &mut self,
213 origin: LayoutPoint,
214 frame_origin_for_query: LayoutPoint,
215 parent_scroll_node_id: ScrollTreeNodeId,
216 transform_style: wr::TransformStyle,
217 transform: LayoutTransform,
218 kind: wr::ReferenceFrameKind,
219 ) -> ScrollTreeNodeId {
220 self.paint_info.scroll_tree.add_scroll_tree_node(
221 Some(parent_scroll_node_id),
222 SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
223 origin,
224 frame_origin_for_query,
225 transform_style,
226 transform: transform.into(),
227 kind,
228 }),
229 )
230 }
231
232 fn define_scroll_frame(
233 &mut self,
234 parent_scroll_node_id: ScrollTreeNodeId,
235 external_id: wr::ExternalScrollId,
236 content_rect: LayoutRect,
237 clip_rect: LayoutRect,
238 scroll_sensitivity: AxesScrollSensitivity,
239 ) -> ScrollTreeNodeId {
240 self.paint_info.scroll_tree.add_scroll_tree_node(
241 Some(parent_scroll_node_id),
242 SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
243 external_id,
244 content_rect,
245 clip_rect,
246 scroll_sensitivity,
247 offset: LayoutVector2D::zero(),
248 offset_changed: Cell::new(false),
249 }),
250 )
251 }
252
253 fn define_sticky_frame(
254 &mut self,
255 parent_scroll_node_id: ScrollTreeNodeId,
256 frame_rect: LayoutRect,
257 margins: SideOffsets2D<Option<f32>, LayoutPixel>,
258 vertical_offset_bounds: StickyOffsetBounds,
259 horizontal_offset_bounds: StickyOffsetBounds,
260 ) -> ScrollTreeNodeId {
261 self.paint_info.scroll_tree.add_scroll_tree_node(
262 Some(parent_scroll_node_id),
263 SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
264 frame_rect,
265 margins,
266 vertical_offset_bounds,
267 horizontal_offset_bounds,
268 }),
269 )
270 }
271
272 pub(crate) fn offset_in_fragment(
280 &self,
281 fragment: &Fragment,
282 point_in_viewport: PhysicalPoint<Au>,
283 ) -> Option<Point2D<Au, CSSPixel>> {
284 let fragment = fragment.retrieve_box_fragment()?;
285 let spatial_tree_node = fragment.spatial_tree_node()?;
286 let transform = self
287 .paint_info
288 .scroll_tree
289 .cumulative_root_to_node_transform(spatial_tree_node)?;
290 let transformed_point = transform
291 .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
292 .map(Au::from_f32_px)
293 .cast_unit();
294
295 let reference_frame_origin = self
297 .paint_info
298 .scroll_tree
299 .reference_frame_offset(spatial_tree_node)
300 .map(Au::from_f32_px);
301 let fragment_origin = fragment
302 .cumulative_content_box_rect(
303 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
304 )
305 .origin -
306 reference_frame_origin.cast_unit();
307
308 Some(transformed_point - fragment_origin)
310 }
311}
312
313#[derive(Clone, Debug, MallocSizeOf)]
315pub(crate) struct FragmentTextDecoration {
316 pub line: TextDecorationLine,
317 pub color: AbsoluteColor,
318 pub style: TextDecorationStyle,
319 pub thickness: TextDecorationThickness,
320}
321
322#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
323pub(crate) enum StackingContextType {
324 StackingContext,
325 StackingContainer,
326}
327
328#[derive(MallocSizeOf)]
329pub(crate) enum StackingContextFragments {
330 Root,
331 Fragment(#[conditional_malloc_size_of] Arc<BoxFragment>),
332}
333
334#[derive(MallocSizeOf)]
335pub(crate) struct StackingContextReferenceFrameInfo {
336 pub(crate) parent_spatial_node_id: ScrollTreeNodeId,
337 pub(crate) captured_clip_id: ClipId,
338}
339
340#[derive(MallocSizeOf)]
347pub struct StackingContext {
348 pub(crate) fragment: StackingContextFragments,
353
354 pub(crate) context_type: StackingContextType,
357
358 pub(crate) children: Vec<StackingContext>,
360
361 pub(crate) containing_block_origin: PhysicalPoint<Au>,
364
365 pub(crate) scroll_tree_node_id: ScrollTreeNodeId,
367
368 pub(crate) clip_id: ClipId,
370
371 pub(crate) z_index: i32,
373
374 #[conditional_malloc_size_of]
376 pub(crate) text_decorations: Rc<Vec<FragmentTextDecoration>>,
377
378 pub(crate) reference_frame_info: Option<StackingContextReferenceFrameInfo>,
381}
382
383impl StackingContext {
384 fn root(scroll_tree_node_id: ScrollTreeNodeId) -> Self {
385 Self {
386 fragment: StackingContextFragments::Root,
387 context_type: StackingContextType::StackingContext,
388 children: Default::default(),
389 containing_block_origin: Default::default(),
390 scroll_tree_node_id,
391 clip_id: ClipId::INVALID,
392 z_index: 0,
393 text_decorations: Default::default(),
394 reference_frame_info: None,
395 }
396 }
397
398 #[expect(clippy::too_many_arguments)]
399 fn create_descendant(
400 &self,
401 context_type: StackingContextType,
402 containing_block_offset: PhysicalPoint<Au>,
403 spatial_id: ScrollTreeNodeId,
404 clip_id: ClipId,
405 initializing_fragment: Arc<BoxFragment>,
406 text_decorations: Rc<Vec<FragmentTextDecoration>>,
407 reference_frame_info: Option<StackingContextReferenceFrameInfo>,
408 ) -> Self {
409 let z_index = initializing_fragment
410 .style()
411 .effective_z_index(initializing_fragment.base.flags);
412 Self {
413 fragment: StackingContextFragments::Fragment(initializing_fragment),
414 context_type,
415 containing_block_origin: containing_block_offset,
416 children: Default::default(),
417 scroll_tree_node_id: spatial_id,
418 clip_id,
419 z_index,
420 text_decorations,
421 reference_frame_info,
422 }
423 }
424
425 pub(crate) fn fragment(&self) -> Option<&Arc<BoxFragment>> {
426 match &self.fragment {
427 StackingContextFragments::Root => None,
428 StackingContextFragments::Fragment(box_fragment) => Some(box_fragment),
429 }
430 }
431
432 fn sort(&mut self) {
433 self.children.sort_by_key(|child| child.z_index)
434 }
435
436 fn print(&self, tree: &mut PrintTree) {
437 let fragment_string = match &self.fragment {
438 StackingContextFragments::Root => "Root".into(),
439 StackingContextFragments::Fragment(box_fragment) => format!(
440 "{:?} rect={:?}",
441 box_fragment.base.tag,
442 box_fragment.content_rect()
443 ),
444 };
445
446 tree.new_level(format!(
447 "{fragment_string} z-index={:?} spatial={:?} clip={:?}",
448 self.z_index, self.scroll_tree_node_id, self.clip_id
449 ));
450
451 for child in self.children.iter() {
452 child.print(tree);
453 }
454
455 tree.end_level();
456 }
457}
458
459#[derive(Clone, Copy, PartialEq)]
460pub(crate) enum StackingContextBuildMode {
461 IncludeHoisted,
462 SkipHoisted,
463}
464
465impl Fragment {
466 pub(crate) fn build_stacking_context_tree(
467 &self,
468 stacking_context_tree: &mut StackingContextTree,
469 containing_block_info: &ContainingBlockInfo,
470 stacking_context: &mut StackingContext,
471 mode: StackingContextBuildMode,
472 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
473 ) {
474 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
475 let cumulative_containing_block = containing_block
476 .rect
477 .translate(containing_block.accumulated_reference_frame_offset);
478 self.set_containing_block(&cumulative_containing_block);
479
480 if self
481 .base()
482 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
483 {
484 return;
485 }
486
487 let fragment_clone = self.clone();
488 match self {
489 Fragment::Box(fragment) | Fragment::Float(fragment) => {
490 if mode == StackingContextBuildMode::SkipHoisted &&
491 fragment.style().clone_position().is_absolutely_positioned()
492 {
493 return;
494 }
495
496 let text_decorations = match self {
497 Fragment::Float(..) => &Default::default(),
498 _ => text_decorations,
499 };
500
501 fragment.build_stacking_context_tree(
502 fragment_clone,
503 stacking_context_tree,
504 containing_block,
505 containing_block_info,
506 stacking_context,
507 text_decorations,
508 );
509 },
510 Fragment::LayoutRoot(..) => {
511 },
514 Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
515 let shared_fragment = fragment.borrow();
516 let fragment_ref = match shared_fragment.fragment.as_ref() {
517 Some(fragment_ref) => fragment_ref,
518 None => unreachable!("Found hoisted box with missing fragment."),
519 };
520
521 fragment_ref.build_stacking_context_tree(
522 stacking_context_tree,
523 containing_block_info,
524 stacking_context,
525 StackingContextBuildMode::IncludeHoisted,
526 &Default::default(),
527 );
528 },
529 Fragment::Positioning(fragment) => {
530 fragment.build_stacking_context_tree(
531 stacking_context_tree,
532 containing_block,
533 containing_block_info,
534 stacking_context,
535 text_decorations,
536 );
537 },
538 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {},
539 }
540 }
541}
542
543struct ReferenceFrameData {
544 origin: PhysicalPoint<Au>,
545 transform: LayoutTransform,
546 kind: wr::ReferenceFrameKind,
547}
548struct ScrollFrameData {
549 scroll_tree_node_id: ScrollTreeNodeId,
550 scroll_frame_rect: LayoutRect,
551}
552
553struct OverflowFrameData {
554 clip_id: ClipId,
555 scroll_frame_data: Option<ScrollFrameData>,
556}
557
558impl BoxFragment {
559 pub(crate) fn stacking_context_type(&self) -> Option<StackingContextType> {
560 let flags = self.base.flags;
561 let style = self.style();
562 if style.establishes_stacking_context(flags) {
563 return Some(StackingContextType::StackingContext);
564 }
565
566 let box_style = &style.get_box();
567 if box_style.position != ComputedPosition::Static {
568 return Some(StackingContextType::StackingContainer);
569 }
570
571 None
572 }
573
574 fn build_stacking_context_tree(
575 self: &Arc<Self>,
576 fragment: Fragment,
577 stacking_context_tree: &mut StackingContextTree,
578 containing_block: &ContainingBlock,
579 containing_block_info: &ContainingBlockInfo,
580 parent_stacking_context: &mut StackingContext,
581 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
582 ) {
583 self.clear_stacking_context_tree_traversal_data();
584 self.build_stacking_context_tree_maybe_creating_reference_frame(
585 fragment,
586 stacking_context_tree,
587 containing_block,
588 containing_block_info,
589 parent_stacking_context,
590 text_decorations,
591 );
592 }
593
594 fn build_stacking_context_tree_maybe_creating_reference_frame(
595 self: &Arc<Self>,
596 fragment: Fragment,
597 stacking_context_tree: &mut StackingContextTree,
598 containing_block: &ContainingBlock,
599 containing_block_info: &ContainingBlockInfo,
600 parent_stacking_context: &mut StackingContext,
601 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
602 ) {
603 let reference_frame_data =
604 match self.reference_frame_data_if_necessary(&containing_block.rect) {
605 Some(reference_frame_data) => reference_frame_data,
606 None => {
607 return self.build_stacking_context_tree_maybe_creating_stacking_context(
608 fragment,
609 stacking_context_tree,
610 containing_block,
611 containing_block_info,
612 parent_stacking_context,
613 text_decorations,
614 None, );
616 },
617 };
618
619 if !reference_frame_data.transform.is_invertible() {
623 self.clear_stacking_context_tree_traversal_data_recursively();
624 return;
625 }
626
627 let style = self.style();
628 let frame_origin_for_query = self
629 .cumulative_border_box_rect(
630 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
631 )
632 .origin
633 .to_webrender();
634 let new_spatial_id = stacking_context_tree.push_reference_frame(
635 reference_frame_data.origin.to_webrender(),
636 frame_origin_for_query,
637 containing_block.scroll_node_id,
638 style.used_transform_style(self.base.flags).to_webrender(),
639 reference_frame_data.transform,
640 reference_frame_data.kind,
641 );
642
643 assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
653 let reference_frame_offset = reference_frame_data.origin.to_vector();
654 let adjusted_containing_block = ContainingBlock::new(
655 containing_block.rect.translate(-reference_frame_offset),
656 new_spatial_id,
657 None,
658 ClipId::INVALID,
659 containing_block.accumulated_reference_frame_offset + reference_frame_offset,
660 );
661 let new_containing_block_info =
662 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
663
664 let reference_frame_info = StackingContextReferenceFrameInfo {
665 parent_spatial_node_id: containing_block.scroll_node_id,
666 captured_clip_id: containing_block.clip_id,
667 };
668
669 self.build_stacking_context_tree_maybe_creating_stacking_context(
670 fragment,
671 stacking_context_tree,
672 &adjusted_containing_block,
673 &new_containing_block_info,
674 parent_stacking_context,
675 text_decorations,
676 Some(reference_frame_info),
677 );
678 }
679
680 #[expect(clippy::too_many_arguments)]
681 fn build_stacking_context_tree_maybe_creating_stacking_context(
682 self: &Arc<Self>,
683 fragment: Fragment,
684 stacking_context_tree: &mut StackingContextTree,
685 containing_block: &ContainingBlock,
686 containing_block_info: &ContainingBlockInfo,
687 parent_stacking_context: &mut StackingContext,
688 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
689 reference_frame_info: Option<StackingContextReferenceFrameInfo>,
690 ) {
691 let with_style = &self.with_style();
692 let style = with_style.style();
693 let Some(stacking_context_type) = self.stacking_context_type() else {
694 with_style.build_stacking_context_tree_for_children(
695 stacking_context_tree,
696 containing_block,
697 containing_block_info,
698 parent_stacking_context,
699 text_decorations,
700 );
701 return;
702 };
703
704 let new_scroll_frame_size = containing_block_info
705 .for_non_absolute_descendants
706 .scroll_frame_size;
707 let spatial_id = self.build_sticky_frame_if_necessary(
708 stacking_context_tree,
709 containing_block.scroll_node_id,
710 &containing_block.rect,
711 &new_scroll_frame_size,
712 );
713
714 let clip_id = with_style.build_clip_frame_if_necessary(
715 stacking_context_tree,
716 spatial_id.unwrap_or(containing_block.scroll_node_id),
717 containing_block.clip_id,
718 &containing_block.rect,
719 );
720
721 let clip_id = stacking_context_tree
722 .clip_store
723 .add_for_clip_path(
724 &style.get_svg().clip_path,
725 spatial_id.unwrap_or(containing_block.scroll_node_id),
726 clip_id.unwrap_or(containing_block.clip_id),
727 with_style,
728 containing_block.rect.origin,
729 )
730 .or(clip_id);
731
732 let containing_block = if clip_id.is_some() || spatial_id.is_some() {
733 if let Some(clip_id) = clip_id {
734 self.set_generated_clip_id(clip_id);
735 }
736 if let Some(spatial_id) = spatial_id {
737 self.set_generated_scroll_tree_node_id(spatial_id);
738 }
739 &ContainingBlock {
740 scroll_node_id: spatial_id.unwrap_or(containing_block.scroll_node_id),
741 clip_id: clip_id.unwrap_or(containing_block.clip_id),
742 ..*containing_block
743 }
744 } else {
745 containing_block
746 };
747
748 let box_fragment = fragment
749 .retrieve_box_fragment()
750 .expect("Should never try to make stacking context for non-BoxFragment")
751 .clone();
752 let mut child_stacking_context = parent_stacking_context.create_descendant(
753 stacking_context_type,
754 containing_block.rect.origin,
755 containing_block.scroll_node_id,
756 containing_block.clip_id,
757 box_fragment,
758 text_decorations.clone(),
759 reference_frame_info,
760 );
761 with_style.build_stacking_context_tree_for_children(
762 stacking_context_tree,
763 containing_block,
764 containing_block_info,
765 &mut child_stacking_context,
766 text_decorations,
767 );
768
769 let mut stolen_children = vec![];
770 if stacking_context_type != StackingContextType::StackingContext {
771 stolen_children =
772 std::mem::replace(&mut child_stacking_context.children, stolen_children);
773 } else {
774 child_stacking_context.sort();
775 }
776
777 parent_stacking_context
778 .children
779 .push(child_stacking_context);
780 parent_stacking_context
781 .children
782 .append(&mut stolen_children);
783 }
784}
785
786impl BoxFragmentWithStyle<'_> {
787 fn build_stacking_context_tree_for_children(
788 &self,
789 stacking_context_tree: &mut StackingContextTree,
790 containing_block: &ContainingBlock,
791 containing_block_info: &ContainingBlockInfo,
792 stacking_context: &mut StackingContext,
793 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
794 ) {
795 let style = self.style();
796 let establishes_containing_block_for_all_descendants =
797 style.establishes_containing_block_for_all_descendants(self.base.flags);
798 let establishes_containing_block_for_absolute_descendants =
799 style.establishes_containing_block_for_absolute_descendants(self.base.flags);
800
801 let mut new_scroll_node_id = containing_block.scroll_node_id;
802 self.spatial_tree_node.set(Some(new_scroll_node_id));
803
804 let mut new_scroll_frame_size = containing_block_info
807 .for_non_absolute_descendants
808 .scroll_frame_size;
809 let mut new_clip_id = containing_block.clip_id;
810 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
811 stacking_context_tree,
812 new_scroll_node_id,
813 new_clip_id,
814 &containing_block.rect,
815 ) {
816 new_clip_id = overflow_frame_data.clip_id;
817 self.set_generated_clip_id(new_clip_id);
818
819 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
820 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
821 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
822 self.set_generated_scroll_tree_node_id(new_scroll_node_id);
823 }
824 }
825
826 let padding_rect = self
827 .padding_rect()
828 .translate(containing_block.rect.origin.to_vector());
829 let content_rect = self
830 .content_rect()
831 .translate(containing_block.rect.origin.to_vector());
832
833 let for_absolute_descendants = ContainingBlock::new(
834 padding_rect,
835 new_scroll_node_id,
836 new_scroll_frame_size,
837 new_clip_id,
838 containing_block.accumulated_reference_frame_offset,
839 );
840 let for_non_absolute_descendants = ContainingBlock::new(
841 content_rect,
842 new_scroll_node_id,
843 new_scroll_frame_size,
844 new_clip_id,
845 containing_block.accumulated_reference_frame_offset,
846 );
847
848 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
852 containing_block_info.new_for_absolute_and_fixed_descendants(
853 &for_non_absolute_descendants,
854 &for_absolute_descendants,
855 )
856 } else if establishes_containing_block_for_absolute_descendants {
857 containing_block_info.new_for_absolute_descendants(
858 &for_non_absolute_descendants,
859 &for_absolute_descendants,
860 )
861 } else {
862 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
863 };
864
865 let text_decorations = match self.is_atomic_inline_level() ||
871 self.base
872 .flags
873 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
874 {
875 true => &Default::default(),
876 false => text_decorations,
877 };
878
879 let new_text_decoration;
880 let text_decorations = match style.clone_text_decoration_line() {
881 TextDecorationLine::NONE => text_decorations,
882 line => {
883 let mut new_vector = (**text_decorations).clone();
884 let color = &style.get_inherited_text().color;
885 new_vector.push(FragmentTextDecoration {
886 line,
887 color: style
888 .clone_text_decoration_color()
889 .resolve_to_absolute(color),
890 style: style.clone_text_decoration_style(),
891 thickness: style.clone_text_decoration_thickness(),
892 });
893 new_text_decoration = Rc::new(new_vector);
894 &new_text_decoration
895 },
896 };
897
898 for child in &self.children {
899 child.build_stacking_context_tree(
900 stacking_context_tree,
901 &new_containing_block_info,
902 stacking_context,
903 StackingContextBuildMode::SkipHoisted,
904 text_decorations,
905 );
906 }
907 }
908
909 fn build_clip_frame_if_necessary(
910 &self,
911 stacking_context_tree: &mut StackingContextTree,
912 parent_scroll_node_id: ScrollTreeNodeId,
913 parent_clip_id: ClipId,
914 containing_block_rect: &PhysicalRect<Au>,
915 ) -> Option<ClipId> {
916 let style = self.style();
917 let position = style.get_box().position;
918 if !position.is_absolutely_positioned() {
921 return None;
922 }
923
924 let clip_rect = match style.get_effects().clip {
926 ClipRectOrAuto::Rect(rect) => rect,
927 _ => return None,
928 };
929
930 let border_rect = self.border_rect();
931 let clip_rect = clip_rect
932 .for_border_rect(border_rect)
933 .translate(containing_block_rect.origin.to_vector())
934 .to_webrender();
935 Some(stacking_context_tree.clip_store.add(
936 BorderRadius::zero(),
937 clip_rect,
938 parent_scroll_node_id,
939 parent_clip_id,
940 ))
941 }
942
943 fn build_overflow_frame_if_necessary(
944 &self,
945 stacking_context_tree: &mut StackingContextTree,
946 parent_scroll_node_id: ScrollTreeNodeId,
947 parent_clip_id: ClipId,
948 containing_block_rect: &PhysicalRect<Au>,
949 ) -> Option<OverflowFrameData> {
950 let style = self.style();
951 let overflow = style.effective_overflow(self.base.flags);
952
953 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
954 return None;
955 }
956
957 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
959 let overflow_clip_margin = style.get_margin().overflow_clip_margin;
960 let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
961 OverflowClipMarginBox::ContentBox => self.content_rect(),
962 OverflowClipMarginBox::PaddingBox => self.padding_rect(),
963 OverflowClipMarginBox::BorderBox => self.border_rect(),
964 }
965 .translate(containing_block_rect.origin.to_vector())
966 .to_webrender();
967
968 let clip_margin_offset = overflow_clip_margin.offset.px();
971 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
972
973 let radii;
976 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
977 let builder = BuilderForBoxFragment::new(self, containing_block_rect.origin);
978 let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
979 match overflow_clip_margin.visual_box {
980 OverflowClipMarginBox::ContentBox => {
981 offsets_from_border -= (self.border + self.padding).to_webrender();
982 },
983 OverflowClipMarginBox::PaddingBox => {
984 offsets_from_border -= self.border.to_webrender();
985 },
986 OverflowClipMarginBox::BorderBox => {},
987 };
988 radii = offset_radii(builder.border_radius(), offsets_from_border);
989 } else if overflow.x != ComputedOverflow::Clip {
990 let max = LayoutRect::max_rect();
991 overflow_clip_rect.min.x = max.min.x;
992 overflow_clip_rect.max.x = max.max.x;
993 radii = BorderRadius::zero();
994 } else {
995 let max = LayoutRect::max_rect();
996 overflow_clip_rect.min.y = max.min.y;
997 overflow_clip_rect.max.y = max.max.y;
998 radii = BorderRadius::zero();
999 }
1000
1001 let clip_id = stacking_context_tree.clip_store.add(
1002 radii,
1003 overflow_clip_rect,
1004 parent_scroll_node_id,
1005 parent_clip_id,
1006 );
1007
1008 return Some(OverflowFrameData {
1009 clip_id,
1010 scroll_frame_data: None,
1011 });
1012 }
1013
1014 let scroll_frame_rect = self
1015 .padding_rect()
1016 .translate(containing_block_rect.origin.to_vector())
1017 .to_webrender();
1018
1019 let clip_id = stacking_context_tree.clip_store.add(
1020 BuilderForBoxFragment::new(self, containing_block_rect.origin).border_radius(),
1021 scroll_frame_rect,
1022 parent_scroll_node_id,
1023 parent_clip_id,
1024 );
1025
1026 let tag = self.base.tag?;
1027 let external_scroll_id = wr::ExternalScrollId(
1028 tag.to_display_list_fragment_id(),
1029 stacking_context_tree.paint_info.pipeline_id,
1030 );
1031
1032 let sensitivity = AxesScrollSensitivity {
1033 x: overflow.x.into(),
1034 y: overflow.y.into(),
1035 };
1036
1037 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1038 parent_scroll_node_id,
1039 external_scroll_id,
1040 self.scrollable_overflow().to_webrender(),
1041 scroll_frame_rect,
1042 sensitivity,
1043 );
1044
1045 Some(OverflowFrameData {
1046 clip_id,
1047 scroll_frame_data: Some(ScrollFrameData {
1048 scroll_tree_node_id,
1049 scroll_frame_rect,
1050 }),
1051 })
1052 }
1053}
1054
1055impl BoxFragment {
1056 fn build_sticky_frame_if_necessary(
1057 &self,
1058 stacking_context_tree: &mut StackingContextTree,
1059 parent_scroll_node_id: ScrollTreeNodeId,
1060 containing_block_rect: &PhysicalRect<Au>,
1061 scroll_frame_size: &Option<LayoutSize>,
1062 ) -> Option<ScrollTreeNodeId> {
1063 let style = self.style();
1064 if style.get_box().position != ComputedPosition::Sticky {
1065 return None;
1066 }
1067
1068 let scroll_frame_size_for_resolve = match scroll_frame_size {
1069 Some(size) => size,
1070 None => {
1071 &stacking_context_tree
1073 .paint_info
1074 .viewport_details
1075 .layout_size()
1076 },
1077 };
1078
1079 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1083 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1084 let offsets = style.physical_box_offsets();
1085 let offsets = PhysicalSides::<AuOrAuto>::new(
1086 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1087 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1088 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1089 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1090 );
1091 self.set_resolved_sticky_insets(offsets);
1092
1093 if scroll_frame_size.is_none() {
1094 return None;
1095 }
1096
1097 if offsets.top.is_auto() &&
1098 offsets.right.is_auto() &&
1099 offsets.bottom.is_auto() &&
1100 offsets.left.is_auto()
1101 {
1102 return None;
1103 }
1104
1105 let border_rect = self.border_rect();
1124 let computed_margin = style.physical_margin();
1125
1126 let distance_from_border_box_to_cb = PhysicalSides::new(
1130 border_rect.min_y(),
1131 containing_block_rect.width() - border_rect.max_x(),
1132 containing_block_rect.height() - border_rect.max_y(),
1133 border_rect.min_x(),
1134 );
1135
1136 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1140 let used_margin = if computed_margin.is_auto() {
1141 Au::zero()
1142 } else {
1143 used_margin
1144 };
1145 Au::zero().max(distance - used_margin).to_f32_px()
1146 };
1147
1148 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1151 -offset_bound(
1152 distance_from_border_box_to_cb.top,
1153 self.margin.top,
1154 computed_margin.top,
1155 ),
1156 offset_bound(
1157 distance_from_border_box_to_cb.bottom,
1158 self.margin.bottom,
1159 computed_margin.bottom,
1160 ),
1161 );
1162 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1163 -offset_bound(
1164 distance_from_border_box_to_cb.left,
1165 self.margin.left,
1166 computed_margin.left,
1167 ),
1168 offset_bound(
1169 distance_from_border_box_to_cb.right,
1170 self.margin.right,
1171 computed_margin.right,
1172 ),
1173 );
1174
1175 let frame_rect = border_rect
1176 .translate(containing_block_rect.origin.to_vector())
1177 .to_webrender();
1178
1179 let margins = SideOffsets2D::new(
1182 offsets.top.non_auto().map(|v| v.to_f32_px()),
1183 offsets.right.non_auto().map(|v| v.to_f32_px()),
1184 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1185 offsets.left.non_auto().map(|v| v.to_f32_px()),
1186 );
1187
1188 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1189 parent_scroll_node_id,
1190 frame_rect,
1191 margins,
1192 vertical_offset_bounds,
1193 horizontal_offset_bounds,
1194 );
1195
1196 Some(sticky_node_id)
1197 }
1198
1199 fn reference_frame_data_if_necessary(
1201 &self,
1202 containing_block_rect: &PhysicalRect<Au>,
1203 ) -> Option<ReferenceFrameData> {
1204 if !self
1205 .style()
1206 .has_effective_transform_or_perspective(self.base.flags)
1207 {
1208 return None;
1209 }
1210
1211 let relative_border_rect = self.border_rect();
1212 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1213 let transform = self.calculate_transform_matrix(&border_rect);
1214 let perspective = self.calculate_perspective_matrix(&border_rect);
1215 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1216 (None, Some(perspective)) => (
1217 perspective,
1218 wr::ReferenceFrameKind::Perspective {
1219 scrolling_relative_to: None,
1220 },
1221 ),
1222 (Some(transform), None) => (
1223 transform,
1224 wr::ReferenceFrameKind::Transform {
1225 is_2d_scale_translation: false,
1226 should_snap: false,
1227 paired_with_perspective: false,
1228 },
1229 ),
1230 (Some(transform), Some(perspective)) => (
1231 perspective.then(&transform),
1232 wr::ReferenceFrameKind::Perspective {
1233 scrolling_relative_to: None,
1234 },
1235 ),
1236 (None, None) => unreachable!(),
1237 };
1238
1239 Some(ReferenceFrameData {
1240 origin: border_rect.origin,
1241 transform: reference_frame_transform,
1242 kind: reference_frame_kind,
1243 })
1244 }
1245
1246 pub fn calculate_transform_matrix(
1248 &self,
1249 border_rect: &Rect<Au, CSSPixel>,
1250 ) -> Option<LayoutTransform> {
1251 let style = self.style();
1252 let list = &style.get_box().transform;
1253 let length_rect = au_rect_to_length_rect(border_rect);
1254 let rotate = match style.clone_rotate() {
1256 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1257 GenericRotate::Rotate3D(x, y, z, angle) => {
1258 get_normalized_vector_and_angle(x, y, z, angle)
1261 },
1262 GenericRotate::None => (0., 0., 1., Angle::zero()),
1263 };
1264 let scale = match style.clone_scale() {
1265 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1266 GenericScale::None => (1., 1., 1.),
1267 };
1268 let translation = match style.clone_translate() {
1269 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1270 x.resolve(length_rect.size.width).px(),
1271 y.resolve(length_rect.size.height).px(),
1272 z.px(),
1273 ),
1274 GenericTranslate::None => LayoutTransform::identity(),
1275 };
1276
1277 let angle = euclid::Angle::radians(rotate.3.radians());
1278 let transform_base = list
1279 .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1280 .ok()?;
1281 let transform = LayoutTransform::from_untyped(&transform_base.0)
1282 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1283 .then_scale(scale.0, scale.1, scale.2)
1284 .then(&translation);
1285
1286 let transform_origin = &style.get_box().transform_origin;
1287 let transform_origin_x = transform_origin
1288 .horizontal
1289 .to_used_value(border_rect.size.width)
1290 .to_f32_px();
1291 let transform_origin_y = transform_origin
1292 .vertical
1293 .to_used_value(border_rect.size.height)
1294 .to_f32_px();
1295 let transform_origin_z = transform_origin.depth.px();
1296
1297 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1298 }
1299
1300 pub fn calculate_perspective_matrix(
1302 &self,
1303 border_rect: &Rect<Au, CSSPixel>,
1304 ) -> Option<LayoutTransform> {
1305 let style = self.style();
1306 match style.get_box().perspective {
1307 Perspective::Length(length) => {
1308 let perspective_origin = &style.get_box().perspective_origin;
1309 let perspective_origin = LayoutPoint::new(
1310 perspective_origin
1311 .horizontal
1312 .percentage_relative_to(border_rect.size.width.into())
1313 .px(),
1314 perspective_origin
1315 .vertical
1316 .percentage_relative_to(border_rect.size.height.into())
1317 .px(),
1318 );
1319
1320 let perspective_matrix = LayoutTransform::from_untyped(
1321 &transform::create_perspective_matrix(length.px()),
1322 );
1323
1324 Some(perspective_matrix.change_basis(
1325 perspective_origin.x,
1326 perspective_origin.y,
1327 0.0,
1328 ))
1329 },
1330 Perspective::None => None,
1331 }
1332 }
1333
1334 fn clear_stacking_context_tree_traversal_data_recursively(&self) {
1335 fn clear_stacking_context_tree_traversal_data_on_fragments(fragments: &[Fragment]) {
1336 for fragment in fragments.iter() {
1337 match fragment {
1338 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
1339 .inner_box_fragment()
1340 .clear_stacking_context_tree_traversal_data_recursively(),
1341 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1342 box_fragment.clear_stacking_context_tree_traversal_data_recursively();
1343 },
1344 Fragment::Positioning(positioning_fragment) => {
1345 clear_stacking_context_tree_traversal_data_on_fragments(
1346 &positioning_fragment.children,
1347 );
1348 },
1349 _ => {},
1350 }
1351 }
1352 }
1353
1354 self.spatial_tree_node.set(None);
1355 self.clear_stacking_context_tree_traversal_data();
1356 clear_stacking_context_tree_traversal_data_on_fragments(&self.children);
1357 }
1358}
1359
1360impl PositioningFragment {
1361 fn build_stacking_context_tree(
1362 &self,
1363 stacking_context_tree: &mut StackingContextTree,
1364 containing_block: &ContainingBlock,
1365 containing_block_info: &ContainingBlockInfo,
1366 stacking_context: &mut StackingContext,
1367 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
1368 ) {
1369 let rect = self
1370 .base
1371 .rect()
1372 .translate(containing_block.rect.origin.to_vector());
1373 let new_containing_block = containing_block.new_replacing_rect(&rect);
1374 let new_containing_block_info =
1375 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1376
1377 for child in &self.children {
1378 child.build_stacking_context_tree(
1379 stacking_context_tree,
1380 &new_containing_block_info,
1381 stacking_context,
1382 StackingContextBuildMode::SkipHoisted,
1383 text_decorations,
1384 );
1385 }
1386 }
1387}
1388
1389pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1390 Rect::new(
1391 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1392 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1393 )
1394}