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 );
205 }
206
207 root_stacking_context.sort();
208 stacking_context_tree.root_stacking_context = root_stacking_context;
209
210 if debug.is_enabled(DiagnosticsLoggingOption::StackingContextTree) {
211 stacking_context_tree
212 .root_stacking_context
213 .print(&mut PrintTree::new("Stacking Context Tree"));
214 }
215
216 stacking_context_tree
217 }
218
219 fn push_reference_frame(
220 &mut self,
221 origin: LayoutPoint,
222 frame_origin_for_query: LayoutPoint,
223 parent_scroll_node_id: ScrollTreeNodeId,
224 transform_style: wr::TransformStyle,
225 transform: LayoutTransform,
226 kind: wr::ReferenceFrameKind,
227 ) -> ScrollTreeNodeId {
228 self.paint_info.scroll_tree.add_scroll_tree_node(
229 Some(parent_scroll_node_id),
230 SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
231 origin,
232 frame_origin_for_query,
233 transform_style,
234 transform: transform.into(),
235 kind,
236 }),
237 )
238 }
239
240 fn define_scroll_frame(
241 &mut self,
242 parent_scroll_node_id: ScrollTreeNodeId,
243 external_id: wr::ExternalScrollId,
244 content_rect: LayoutRect,
245 clip_rect: LayoutRect,
246 scroll_sensitivity: AxesScrollSensitivity,
247 touch_action: TouchAction,
248 ) -> ScrollTreeNodeId {
249 self.paint_info.scroll_tree.add_scroll_tree_node(
250 Some(parent_scroll_node_id),
251 SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
252 external_id,
253 content_rect,
254 clip_rect,
255 scroll_sensitivity,
256 touch_action,
257 offset: LayoutVector2D::zero(),
258 offset_changed: Cell::new(false),
259 }),
260 )
261 }
262
263 fn define_sticky_frame(
264 &mut self,
265 parent_scroll_node_id: ScrollTreeNodeId,
266 frame_rect: LayoutRect,
267 margins: SideOffsets2D<Option<f32>, LayoutPixel>,
268 vertical_offset_bounds: StickyOffsetBounds,
269 horizontal_offset_bounds: StickyOffsetBounds,
270 ) -> ScrollTreeNodeId {
271 self.paint_info.scroll_tree.add_scroll_tree_node(
272 Some(parent_scroll_node_id),
273 SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
274 frame_rect,
275 margins,
276 vertical_offset_bounds,
277 horizontal_offset_bounds,
278 }),
279 )
280 }
281
282 pub(crate) fn offset_in_fragment(
290 &self,
291 fragment: &Fragment,
292 point_in_viewport: PhysicalPoint<Au>,
293 ) -> Option<Point2D<Au, CSSPixel>> {
294 let fragment = fragment.retrieve_box_fragment()?;
295 let spatial_tree_node = fragment.spatial_tree_node()?;
296 let transform = self
297 .paint_info
298 .scroll_tree
299 .cumulative_root_to_node_transform(spatial_tree_node)?;
300 let transformed_point = transform
301 .project_point2d(point_in_viewport.map(Au::to_f32_px).cast_unit())?
302 .map(Au::from_f32_px)
303 .cast_unit();
304
305 let reference_frame_origin = self
307 .paint_info
308 .scroll_tree
309 .reference_frame_offset(spatial_tree_node)
310 .map(Au::from_f32_px);
311 let fragment_origin = fragment
312 .cumulative_content_box_rect(
313 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
314 )
315 .origin -
316 reference_frame_origin.cast_unit();
317
318 Some(transformed_point - fragment_origin)
320 }
321}
322
323#[derive(Clone, Debug, MallocSizeOf)]
325pub(crate) struct FragmentTextDecoration {
326 pub line: TextDecorationLine,
327 pub color: AbsoluteColor,
328 pub style: TextDecorationStyle,
329 pub thickness: TextDecorationThickness,
330}
331
332#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
333pub(crate) enum StackingContextType {
334 StackingContext,
335 StackingContainer,
336}
337
338#[derive(MallocSizeOf)]
339pub(crate) enum StackingContextFragments {
340 Root,
341 Fragment(#[conditional_malloc_size_of] Arc<BoxFragment>),
342}
343
344#[derive(MallocSizeOf)]
345pub(crate) struct StackingContextReferenceFrameInfo {
346 pub(crate) parent_spatial_node_id: ScrollTreeNodeId,
347 pub(crate) captured_clip_id: ClipId,
348}
349
350#[derive(MallocSizeOf)]
357pub struct StackingContext {
358 pub(crate) fragment: StackingContextFragments,
363
364 pub(crate) context_type: StackingContextType,
367
368 pub(crate) children: Vec<StackingContext>,
370
371 pub(crate) containing_block_origin: PhysicalPoint<Au>,
374
375 pub(crate) scroll_tree_node_id: ScrollTreeNodeId,
377
378 pub(crate) clip_id: ClipId,
380
381 pub(crate) z_index: i32,
383
384 #[conditional_malloc_size_of]
386 pub(crate) text_decorations: Rc<Vec<FragmentTextDecoration>>,
387
388 pub(crate) reference_frame_info: Option<StackingContextReferenceFrameInfo>,
391}
392
393impl StackingContext {
394 fn root(scroll_tree_node_id: ScrollTreeNodeId) -> Self {
395 Self {
396 fragment: StackingContextFragments::Root,
397 context_type: StackingContextType::StackingContext,
398 children: Default::default(),
399 containing_block_origin: Default::default(),
400 scroll_tree_node_id,
401 clip_id: ClipId::INVALID,
402 z_index: 0,
403 text_decorations: Default::default(),
404 reference_frame_info: None,
405 }
406 }
407
408 #[expect(clippy::too_many_arguments)]
409 fn create_descendant(
410 &self,
411 context_type: StackingContextType,
412 containing_block_offset: PhysicalPoint<Au>,
413 spatial_id: ScrollTreeNodeId,
414 clip_id: ClipId,
415 initializing_fragment: Arc<BoxFragment>,
416 text_decorations: Rc<Vec<FragmentTextDecoration>>,
417 reference_frame_info: Option<StackingContextReferenceFrameInfo>,
418 ) -> Self {
419 let z_index = initializing_fragment
420 .style()
421 .effective_z_index(initializing_fragment.base.flags);
422 Self {
423 fragment: StackingContextFragments::Fragment(initializing_fragment),
424 context_type,
425 containing_block_origin: containing_block_offset,
426 children: Default::default(),
427 scroll_tree_node_id: spatial_id,
428 clip_id,
429 z_index,
430 text_decorations,
431 reference_frame_info,
432 }
433 }
434
435 pub(crate) fn fragment(&self) -> Option<&Arc<BoxFragment>> {
436 match &self.fragment {
437 StackingContextFragments::Root => None,
438 StackingContextFragments::Fragment(box_fragment) => Some(box_fragment),
439 }
440 }
441
442 fn sort(&mut self) {
443 self.children.sort_by_key(|child| child.z_index)
444 }
445
446 fn print(&self, tree: &mut PrintTree) {
447 let fragment_string = match &self.fragment {
448 StackingContextFragments::Root => "Root".into(),
449 StackingContextFragments::Fragment(box_fragment) => format!(
450 "{:?} rect={:?}",
451 box_fragment.base.tag,
452 box_fragment.content_rect()
453 ),
454 };
455
456 tree.new_level(format!(
457 "{fragment_string} z-index={:?} spatial={:?} clip={:?}",
458 self.z_index, self.scroll_tree_node_id, self.clip_id
459 ));
460
461 for child in self.children.iter() {
462 child.print(tree);
463 }
464
465 tree.end_level();
466 }
467}
468
469#[derive(Clone, Copy, PartialEq)]
470pub(crate) enum StackingContextBuildMode {
471 IncludeHoisted,
472 SkipHoisted,
473}
474
475impl Fragment {
476 pub(crate) fn build_stacking_context_tree(
477 &self,
478 stacking_context_tree: &mut StackingContextTree,
479 containing_block_info: &ContainingBlockInfo,
480 stacking_context: &mut StackingContext,
481 mode: StackingContextBuildMode,
482 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
483 ) {
484 let containing_block = containing_block_info.get_containing_block_for_fragment(self);
485 let cumulative_containing_block = containing_block
486 .rect
487 .translate(containing_block.accumulated_reference_frame_offset);
488 self.set_containing_block(&cumulative_containing_block);
489
490 if self
491 .base()
492 .is_some_and(|base| base.flags.contains(FragmentFlags::IS_COLLAPSED))
493 {
494 return;
495 }
496
497 let fragment_clone = self.clone();
498 match self {
499 Fragment::Box(fragment) | Fragment::Float(fragment) => {
500 if mode == StackingContextBuildMode::SkipHoisted &&
501 fragment.style().clone_position().is_absolutely_positioned()
502 {
503 return;
504 }
505
506 let text_decorations = match self {
507 Fragment::Float(..) => &Default::default(),
508 _ => text_decorations,
509 };
510
511 fragment.build_stacking_context_tree(
512 fragment_clone,
513 stacking_context_tree,
514 containing_block,
515 containing_block_info,
516 stacking_context,
517 text_decorations,
518 );
519 },
520 Fragment::LayoutRoot(..) => {
521 },
524 Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
525 let shared_fragment = fragment.borrow();
526 let fragment_ref = match shared_fragment.fragment.as_ref() {
527 Some(fragment_ref) => fragment_ref,
528 None => unreachable!("Found hoisted box with missing fragment."),
529 };
530
531 fragment_ref.build_stacking_context_tree(
532 stacking_context_tree,
533 containing_block_info,
534 stacking_context,
535 StackingContextBuildMode::IncludeHoisted,
536 &Default::default(),
537 );
538 },
539 Fragment::Positioning(fragment) => {
540 fragment.build_stacking_context_tree(
541 stacking_context_tree,
542 containing_block,
543 containing_block_info,
544 stacking_context,
545 text_decorations,
546 );
547 },
548 Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {},
549 }
550 }
551}
552
553struct ReferenceFrameData {
554 origin: PhysicalPoint<Au>,
555 transform: LayoutTransform,
556 kind: wr::ReferenceFrameKind,
557}
558struct ScrollFrameData {
559 scroll_tree_node_id: ScrollTreeNodeId,
560 scroll_frame_rect: LayoutRect,
561}
562
563struct OverflowFrameData {
564 clip_id: ClipId,
565 scroll_frame_data: Option<ScrollFrameData>,
566}
567
568impl BoxFragment {
569 pub(crate) fn stacking_context_type(&self) -> Option<StackingContextType> {
570 let flags = self.base.flags;
571 let style = self.style();
572 if style.establishes_stacking_context(flags) {
573 return Some(StackingContextType::StackingContext);
574 }
575
576 let box_style = &style.get_box();
577 if box_style.position != ComputedPosition::Static {
578 return Some(StackingContextType::StackingContainer);
579 }
580
581 None
582 }
583
584 fn build_stacking_context_tree(
585 self: &Arc<Self>,
586 fragment: Fragment,
587 stacking_context_tree: &mut StackingContextTree,
588 containing_block: &ContainingBlock,
589 containing_block_info: &ContainingBlockInfo,
590 parent_stacking_context: &mut StackingContext,
591 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
592 ) {
593 self.clear_stacking_context_tree_traversal_data();
594 self.build_stacking_context_tree_maybe_creating_reference_frame(
595 fragment,
596 stacking_context_tree,
597 containing_block,
598 containing_block_info,
599 parent_stacking_context,
600 text_decorations,
601 );
602 }
603
604 fn build_stacking_context_tree_maybe_creating_reference_frame(
605 self: &Arc<Self>,
606 fragment: Fragment,
607 stacking_context_tree: &mut StackingContextTree,
608 containing_block: &ContainingBlock,
609 containing_block_info: &ContainingBlockInfo,
610 parent_stacking_context: &mut StackingContext,
611 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
612 ) {
613 let reference_frame_data =
614 match self.reference_frame_data_if_necessary(&containing_block.rect) {
615 Some(reference_frame_data) => reference_frame_data,
616 None => {
617 return self.build_stacking_context_tree_maybe_creating_stacking_context(
618 fragment,
619 stacking_context_tree,
620 containing_block,
621 containing_block_info,
622 parent_stacking_context,
623 text_decorations,
624 None, );
626 },
627 };
628
629 if !reference_frame_data.transform.is_invertible() {
633 self.clear_stacking_context_tree_traversal_data_recursively();
634 return;
635 }
636
637 let style = self.style();
638 let frame_origin_for_query = self
639 .cumulative_border_box_rect(
640 ContainingBlockCalculation::AlreadyDoneWithStackingContextTree,
641 )
642 .origin
643 .to_webrender();
644 let new_spatial_id = stacking_context_tree.push_reference_frame(
645 reference_frame_data.origin.to_webrender(),
646 frame_origin_for_query,
647 containing_block.scroll_node_id,
648 style.used_transform_style(self.base.flags).to_webrender(),
649 reference_frame_data.transform,
650 reference_frame_data.kind,
651 );
652
653 assert!(style.establishes_containing_block_for_all_descendants(self.base.flags));
663 let reference_frame_offset = reference_frame_data.origin.to_vector();
664 let adjusted_containing_block = ContainingBlock::new(
665 containing_block.rect.translate(-reference_frame_offset),
666 new_spatial_id,
667 None,
668 ClipId::INVALID,
669 containing_block.accumulated_reference_frame_offset + reference_frame_offset,
670 false,
671 );
672 let new_containing_block_info =
673 containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
674
675 let reference_frame_info = StackingContextReferenceFrameInfo {
676 parent_spatial_node_id: containing_block.scroll_node_id,
677 captured_clip_id: containing_block.clip_id,
678 };
679
680 self.build_stacking_context_tree_maybe_creating_stacking_context(
681 fragment,
682 stacking_context_tree,
683 &adjusted_containing_block,
684 &new_containing_block_info,
685 parent_stacking_context,
686 text_decorations,
687 Some(reference_frame_info),
688 );
689 }
690
691 #[expect(clippy::too_many_arguments)]
692 fn build_stacking_context_tree_maybe_creating_stacking_context(
693 self: &Arc<Self>,
694 fragment: Fragment,
695 stacking_context_tree: &mut StackingContextTree,
696 containing_block: &ContainingBlock,
697 containing_block_info: &ContainingBlockInfo,
698 parent_stacking_context: &mut StackingContext,
699 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
700 reference_frame_info: Option<StackingContextReferenceFrameInfo>,
701 ) {
702 let with_style = &self.with_style();
703 let style = with_style.style();
704 let Some(stacking_context_type) = self.stacking_context_type() else {
705 with_style.build_stacking_context_tree_for_children(
706 stacking_context_tree,
707 containing_block,
708 containing_block_info,
709 parent_stacking_context,
710 text_decorations,
711 );
712 return;
713 };
714
715 let new_scroll_frame_size = containing_block_info
716 .for_non_absolute_descendants
717 .scroll_frame_size;
718 let spatial_id = self.build_sticky_frame_if_necessary(
719 stacking_context_tree,
720 containing_block.scroll_node_id,
721 &containing_block.rect,
722 &new_scroll_frame_size,
723 containing_block.established_scroll_frame,
724 );
725
726 let clip_id = with_style.build_clip_frame_if_necessary(
727 stacking_context_tree,
728 spatial_id.unwrap_or(containing_block.scroll_node_id),
729 containing_block.clip_id,
730 &containing_block.rect,
731 );
732
733 let clip_id = stacking_context_tree
734 .clip_store
735 .add_for_clip_path(
736 &style.get_svg().clip_path,
737 spatial_id.unwrap_or(containing_block.scroll_node_id),
738 clip_id.unwrap_or(containing_block.clip_id),
739 with_style,
740 containing_block.rect.origin,
741 )
742 .or(clip_id);
743
744 let containing_block = if clip_id.is_some() || spatial_id.is_some() {
745 if let Some(clip_id) = clip_id {
746 self.set_generated_clip_id(clip_id);
747 }
748 if let Some(spatial_id) = spatial_id {
749 self.set_generated_scroll_tree_node_id(spatial_id);
750 }
751 &ContainingBlock {
752 scroll_node_id: spatial_id.unwrap_or(containing_block.scroll_node_id),
753 clip_id: clip_id.unwrap_or(containing_block.clip_id),
754 ..*containing_block
755 }
756 } else {
757 containing_block
758 };
759
760 let box_fragment = fragment
761 .retrieve_box_fragment()
762 .expect("Should never try to make stacking context for non-BoxFragment")
763 .clone();
764 let mut child_stacking_context = parent_stacking_context.create_descendant(
765 stacking_context_type,
766 containing_block.rect.origin,
767 containing_block.scroll_node_id,
768 containing_block.clip_id,
769 box_fragment,
770 text_decorations.clone(),
771 reference_frame_info,
772 );
773 with_style.build_stacking_context_tree_for_children(
774 stacking_context_tree,
775 containing_block,
776 containing_block_info,
777 &mut child_stacking_context,
778 text_decorations,
779 );
780
781 let mut stolen_children = vec![];
782 if stacking_context_type != StackingContextType::StackingContext {
783 stolen_children =
784 std::mem::replace(&mut child_stacking_context.children, stolen_children);
785 } else {
786 child_stacking_context.sort();
787 }
788
789 parent_stacking_context
790 .children
791 .push(child_stacking_context);
792 parent_stacking_context
793 .children
794 .append(&mut stolen_children);
795 }
796}
797
798impl BoxFragmentWithStyle<'_> {
799 fn build_stacking_context_tree_for_children(
800 &self,
801 stacking_context_tree: &mut StackingContextTree,
802 containing_block: &ContainingBlock,
803 containing_block_info: &ContainingBlockInfo,
804 stacking_context: &mut StackingContext,
805 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
806 ) {
807 let style = self.style();
808 let establishes_containing_block_for_all_descendants =
809 style.establishes_containing_block_for_all_descendants(self.base.flags);
810 let establishes_containing_block_for_absolute_descendants =
811 style.establishes_containing_block_for_absolute_descendants(self.base.flags);
812
813 let mut new_scroll_node_id = containing_block.scroll_node_id;
814 self.spatial_tree_node.set(Some(new_scroll_node_id));
815
816 let mut new_scroll_frame_size = containing_block_info
819 .for_non_absolute_descendants
820 .scroll_frame_size;
821
822 let mut established_scroll_frame = containing_block.established_scroll_frame;
823 if established_scroll_frame && !self.base.is_anonymous() {
826 established_scroll_frame = false;
827 }
828 let mut new_clip_id = containing_block.clip_id;
829 if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
830 stacking_context_tree,
831 new_scroll_node_id,
832 new_clip_id,
833 &containing_block.rect,
834 ) {
835 new_clip_id = overflow_frame_data.clip_id;
836 self.set_generated_clip_id(new_clip_id);
837
838 if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
839 new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
840 new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
841 established_scroll_frame = true;
842 self.set_generated_scroll_tree_node_id(new_scroll_node_id);
843 }
844 }
845
846 let padding_rect = self
847 .padding_rect()
848 .translate(containing_block.rect.origin.to_vector());
849 let content_rect = self
850 .content_rect()
851 .translate(containing_block.rect.origin.to_vector());
852
853 let for_absolute_descendants = ContainingBlock::new(
854 padding_rect,
855 new_scroll_node_id,
856 new_scroll_frame_size,
857 new_clip_id,
858 containing_block.accumulated_reference_frame_offset,
859 established_scroll_frame,
860 );
861 let for_non_absolute_descendants = ContainingBlock::new(
862 content_rect,
863 new_scroll_node_id,
864 new_scroll_frame_size,
865 new_clip_id,
866 containing_block.accumulated_reference_frame_offset,
867 established_scroll_frame,
868 );
869
870 let new_containing_block_info = if establishes_containing_block_for_all_descendants {
874 containing_block_info.new_for_absolute_and_fixed_descendants(
875 &for_non_absolute_descendants,
876 &for_absolute_descendants,
877 )
878 } else if establishes_containing_block_for_absolute_descendants {
879 containing_block_info.new_for_absolute_descendants(
880 &for_non_absolute_descendants,
881 &for_absolute_descendants,
882 )
883 } else {
884 containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
885 };
886
887 let text_decorations = match self.is_atomic_inline_level() ||
893 self.base
894 .flags
895 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
896 {
897 true => &Default::default(),
898 false => text_decorations,
899 };
900
901 let new_text_decoration;
902 let text_decorations = match style.clone_text_decoration_line() {
903 TextDecorationLine::NONE => text_decorations,
904 line => {
905 let mut new_vector = (**text_decorations).clone();
906 let color = &style.get_inherited_text().color;
907 new_vector.push(FragmentTextDecoration {
908 line,
909 color: style
910 .clone_text_decoration_color()
911 .resolve_to_absolute(color),
912 style: style.clone_text_decoration_style(),
913 thickness: style.clone_text_decoration_thickness(),
914 });
915 new_text_decoration = Rc::new(new_vector);
916 &new_text_decoration
917 },
918 };
919
920 for child in &self.children {
921 child.build_stacking_context_tree(
922 stacking_context_tree,
923 &new_containing_block_info,
924 stacking_context,
925 StackingContextBuildMode::SkipHoisted,
926 text_decorations,
927 );
928 }
929 }
930
931 fn build_clip_frame_if_necessary(
932 &self,
933 stacking_context_tree: &mut StackingContextTree,
934 parent_scroll_node_id: ScrollTreeNodeId,
935 parent_clip_id: ClipId,
936 containing_block_rect: &PhysicalRect<Au>,
937 ) -> Option<ClipId> {
938 let style = self.style();
939 let position = style.get_box().position;
940 if !position.is_absolutely_positioned() {
943 return None;
944 }
945
946 let clip_rect = match style.get_effects().clip {
948 ClipRectOrAuto::Rect(rect) => rect,
949 _ => return None,
950 };
951
952 let border_rect = self.border_rect();
953 let clip_rect = clip_rect
954 .for_border_rect(border_rect)
955 .translate(containing_block_rect.origin.to_vector())
956 .to_webrender();
957 Some(stacking_context_tree.clip_store.add(
958 BorderRadius::zero(),
959 clip_rect,
960 parent_scroll_node_id,
961 parent_clip_id,
962 ))
963 }
964
965 fn build_overflow_frame_if_necessary(
966 &self,
967 stacking_context_tree: &mut StackingContextTree,
968 parent_scroll_node_id: ScrollTreeNodeId,
969 parent_clip_id: ClipId,
970 containing_block_rect: &PhysicalRect<Au>,
971 ) -> Option<OverflowFrameData> {
972 let style = self.style();
973 let overflow = style.effective_overflow(self.base.flags);
974
975 if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
976 return None;
977 }
978
979 if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
981 let overflow_clip_margin = style.get_margin().overflow_clip_margin;
982 let mut overflow_clip_rect = match overflow_clip_margin.visual_box {
983 OverflowClipMarginBox::ContentBox => self.content_rect(),
984 OverflowClipMarginBox::PaddingBox => self.padding_rect(),
985 OverflowClipMarginBox::BorderBox => self.border_rect(),
986 }
987 .translate(containing_block_rect.origin.to_vector())
988 .to_webrender();
989
990 let clip_margin_offset = overflow_clip_margin.offset.px();
993 overflow_clip_rect = overflow_clip_rect.inflate(clip_margin_offset, clip_margin_offset);
994
995 let radii;
998 if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
999 let builder = BuilderForBoxFragment::new(self, containing_block_rect.origin);
1000 let mut offsets_from_border = SideOffsets2D::new_all_same(clip_margin_offset);
1001 match overflow_clip_margin.visual_box {
1002 OverflowClipMarginBox::ContentBox => {
1003 offsets_from_border -= (self.border + self.padding).to_webrender();
1004 },
1005 OverflowClipMarginBox::PaddingBox => {
1006 offsets_from_border -= self.border.to_webrender();
1007 },
1008 OverflowClipMarginBox::BorderBox => {},
1009 };
1010 radii = offset_radii(builder.border_radius(), offsets_from_border);
1011 } else if overflow.x != ComputedOverflow::Clip {
1012 let max = LayoutRect::max_rect();
1013 overflow_clip_rect.min.x = max.min.x;
1014 overflow_clip_rect.max.x = max.max.x;
1015 radii = BorderRadius::zero();
1016 } else {
1017 let max = LayoutRect::max_rect();
1018 overflow_clip_rect.min.y = max.min.y;
1019 overflow_clip_rect.max.y = max.max.y;
1020 radii = BorderRadius::zero();
1021 }
1022
1023 let clip_id = stacking_context_tree.clip_store.add(
1024 radii,
1025 overflow_clip_rect,
1026 parent_scroll_node_id,
1027 parent_clip_id,
1028 );
1029
1030 return Some(OverflowFrameData {
1031 clip_id,
1032 scroll_frame_data: None,
1033 });
1034 }
1035
1036 let scroll_frame_rect = self
1037 .padding_rect()
1038 .translate(containing_block_rect.origin.to_vector())
1039 .to_webrender();
1040
1041 let clip_id = stacking_context_tree.clip_store.add(
1042 BuilderForBoxFragment::new(self, containing_block_rect.origin).border_radius(),
1043 scroll_frame_rect,
1044 parent_scroll_node_id,
1045 parent_clip_id,
1046 );
1047
1048 let tag = self.base.tag?;
1049 let external_scroll_id = wr::ExternalScrollId(
1050 tag.to_display_list_fragment_id(),
1051 stacking_context_tree.paint_info.pipeline_id,
1052 );
1053
1054 let mut x_sensitivity: ScrollType = overflow.x.into();
1055 let mut y_sensitivity: ScrollType = overflow.y.into();
1056 let touch_action = TouchAction::from(style.get_box().touch_action);
1057 match touch_action {
1061 TouchAction::PanX => {
1062 y_sensitivity.remove(ScrollType::Touch);
1063 },
1064 TouchAction::PanY => {
1065 x_sensitivity.remove(ScrollType::Touch);
1066 },
1067 TouchAction::None => {
1068 x_sensitivity.remove(ScrollType::Touch);
1069 y_sensitivity.remove(ScrollType::Touch);
1070 },
1071 TouchAction::Auto => {},
1072 }
1073 let sensitivity = AxesScrollSensitivity {
1074 x: x_sensitivity,
1075 y: y_sensitivity,
1076 };
1077
1078 let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
1079 parent_scroll_node_id,
1080 external_scroll_id,
1081 self.scrollable_overflow().to_webrender(),
1082 scroll_frame_rect,
1083 sensitivity,
1084 touch_action,
1085 );
1086
1087 Some(OverflowFrameData {
1088 clip_id,
1089 scroll_frame_data: Some(ScrollFrameData {
1090 scroll_tree_node_id,
1091 scroll_frame_rect,
1092 }),
1093 })
1094 }
1095}
1096
1097impl BoxFragment {
1098 fn build_sticky_frame_if_necessary(
1099 &self,
1100 stacking_context_tree: &mut StackingContextTree,
1101 parent_scroll_node_id: ScrollTreeNodeId,
1102 containing_block_rect: &PhysicalRect<Au>,
1103 scroll_frame_size: &Option<LayoutSize>,
1104 established_scroll_frame: bool,
1105 ) -> Option<ScrollTreeNodeId> {
1106 let style = self.style();
1107 if style.get_box().position != ComputedPosition::Sticky {
1108 return None;
1109 }
1110
1111 let scroll_frame_size_for_resolve = match scroll_frame_size {
1112 Some(size) => size,
1113 None => {
1114 &stacking_context_tree
1116 .paint_info
1117 .viewport_details
1118 .layout_size()
1119 },
1120 };
1121
1122 let scroll_frame_height = Au::from_f32_px(scroll_frame_size_for_resolve.height);
1126 let scroll_frame_width = Au::from_f32_px(scroll_frame_size_for_resolve.width);
1127 let offsets = style.physical_box_offsets();
1128 let offsets = PhysicalSides::<AuOrAuto>::new(
1129 offsets.top.map(|v| v.to_used_value(scroll_frame_height)),
1130 offsets.right.map(|v| v.to_used_value(scroll_frame_width)),
1131 offsets.bottom.map(|v| v.to_used_value(scroll_frame_height)),
1132 offsets.left.map(|v| v.to_used_value(scroll_frame_width)),
1133 );
1134 self.set_resolved_sticky_insets(offsets);
1135
1136 if scroll_frame_size.is_none() {
1137 return None;
1138 }
1139
1140 if offsets.top.is_auto() &&
1141 offsets.right.is_auto() &&
1142 offsets.bottom.is_auto() &&
1143 offsets.left.is_auto()
1144 {
1145 return None;
1146 }
1147
1148 let border_rect = self.border_rect();
1167 let computed_margin = style.physical_margin();
1168 let parent_scroll_node = stacking_context_tree
1169 .paint_info
1170 .scroll_tree
1171 .get_node(parent_scroll_node_id);
1172 let sticky_offset_boundary = match parent_scroll_node.info {
1173 SpatialTreeNodeInfo::Scroll(ref scrollable_node_info) if established_scroll_frame => {
1174 let content_rect = &scrollable_node_info.content_rect;
1175 &PhysicalRect::new(
1176 PhysicalPoint::new(
1177 Au::from_f32_px(content_rect.min.x),
1178 Au::from_f32_px(content_rect.min.y),
1179 ),
1180 PhysicalSize::new(
1181 Au::from_f32_px(content_rect.max.x - content_rect.min.x),
1182 Au::from_f32_px(content_rect.max.y - content_rect.min.y),
1183 ),
1184 )
1185 },
1186 _ => containing_block_rect,
1187 };
1188 let distance_from_border_box_to_cb = PhysicalSides::new(
1192 border_rect.min_y(),
1193 sticky_offset_boundary.width() - border_rect.max_x(),
1194 sticky_offset_boundary.height() - border_rect.max_y(),
1195 border_rect.min_x(),
1196 );
1197 let offset_bound = |distance, used_margin, computed_margin: LengthPercentageOrAuto| {
1201 let used_margin = if computed_margin.is_auto() {
1202 Au::zero()
1203 } else {
1204 used_margin
1205 };
1206 Au::zero().max(distance - used_margin).to_f32_px()
1207 };
1208
1209 let vertical_offset_bounds = wr::StickyOffsetBounds::new(
1212 -offset_bound(
1213 distance_from_border_box_to_cb.top,
1214 self.margin.top,
1215 computed_margin.top,
1216 ),
1217 offset_bound(
1218 distance_from_border_box_to_cb.bottom,
1219 self.margin.bottom,
1220 computed_margin.bottom,
1221 ),
1222 );
1223 let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
1224 -offset_bound(
1225 distance_from_border_box_to_cb.left,
1226 self.margin.left,
1227 computed_margin.left,
1228 ),
1229 offset_bound(
1230 distance_from_border_box_to_cb.right,
1231 self.margin.right,
1232 computed_margin.right,
1233 ),
1234 );
1235
1236 let frame_rect = border_rect
1237 .translate(containing_block_rect.origin.to_vector())
1238 .to_webrender();
1239
1240 let margins = SideOffsets2D::new(
1243 offsets.top.non_auto().map(|v| v.to_f32_px()),
1244 offsets.right.non_auto().map(|v| v.to_f32_px()),
1245 offsets.bottom.non_auto().map(|v| v.to_f32_px()),
1246 offsets.left.non_auto().map(|v| v.to_f32_px()),
1247 );
1248
1249 let sticky_node_id = stacking_context_tree.define_sticky_frame(
1250 parent_scroll_node_id,
1251 frame_rect,
1252 margins,
1253 vertical_offset_bounds,
1254 horizontal_offset_bounds,
1255 );
1256
1257 Some(sticky_node_id)
1258 }
1259
1260 fn reference_frame_data_if_necessary(
1262 &self,
1263 containing_block_rect: &PhysicalRect<Au>,
1264 ) -> Option<ReferenceFrameData> {
1265 if !self
1266 .style()
1267 .has_effective_transform_or_perspective(self.base.flags)
1268 {
1269 return None;
1270 }
1271
1272 let relative_border_rect = self.border_rect();
1273 let border_rect = relative_border_rect.translate(containing_block_rect.origin.to_vector());
1274 let transform = self.calculate_transform_matrix(&border_rect);
1275 let perspective = self.calculate_perspective_matrix(&border_rect);
1276 let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
1277 (None, Some(perspective)) => (
1278 perspective,
1279 wr::ReferenceFrameKind::Perspective {
1280 scrolling_relative_to: None,
1281 },
1282 ),
1283 (Some(transform), None) => (
1284 transform,
1285 wr::ReferenceFrameKind::Transform {
1286 is_2d_scale_translation: false,
1287 should_snap: false,
1288 paired_with_perspective: false,
1289 },
1290 ),
1291 (Some(transform), Some(perspective)) => (
1292 perspective.then(&transform),
1293 wr::ReferenceFrameKind::Perspective {
1294 scrolling_relative_to: None,
1295 },
1296 ),
1297 (None, None) => unreachable!(),
1298 };
1299
1300 Some(ReferenceFrameData {
1301 origin: border_rect.origin,
1302 transform: reference_frame_transform,
1303 kind: reference_frame_kind,
1304 })
1305 }
1306
1307 pub fn calculate_transform_matrix(
1309 &self,
1310 border_rect: &Rect<Au, CSSPixel>,
1311 ) -> Option<LayoutTransform> {
1312 let style = self.style();
1313 let list = &style.get_box().transform;
1314 let length_rect = au_rect_to_length_rect(border_rect);
1315 let rotate = match style.clone_rotate() {
1317 GenericRotate::Rotate(angle) => (0., 0., 1., angle),
1318 GenericRotate::Rotate3D(x, y, z, angle) => {
1319 get_normalized_vector_and_angle(x, y, z, angle)
1322 },
1323 GenericRotate::None => (0., 0., 1., Angle::zero()),
1324 };
1325 let scale = match style.clone_scale() {
1326 GenericScale::Scale(sx, sy, sz) => (sx, sy, sz),
1327 GenericScale::None => (1., 1., 1.),
1328 };
1329 let translation = match style.clone_translate() {
1330 GenericTranslate::Translate(x, y, z) => LayoutTransform::translation(
1331 x.resolve(length_rect.size.width).px(),
1332 y.resolve(length_rect.size.height).px(),
1333 z.px(),
1334 ),
1335 GenericTranslate::None => LayoutTransform::identity(),
1336 };
1337
1338 let angle = euclid::Angle::radians(rotate.3.radians());
1339 let transform_base = list
1340 .to_transform_3d_matrix(Some(&length_rect.to_untyped()))
1341 .ok()?;
1342 let transform = LayoutTransform::from_untyped(&transform_base.0)
1343 .then_rotate(rotate.0, rotate.1, rotate.2, angle)
1344 .then_scale(scale.0, scale.1, scale.2)
1345 .then(&translation);
1346
1347 let transform_origin = &style.get_box().transform_origin;
1348 let transform_origin_x = transform_origin
1349 .horizontal
1350 .to_used_value(border_rect.size.width)
1351 .to_f32_px();
1352 let transform_origin_y = transform_origin
1353 .vertical
1354 .to_used_value(border_rect.size.height)
1355 .to_f32_px();
1356 let transform_origin_z = transform_origin.depth.px();
1357
1358 Some(transform.change_basis(transform_origin_x, transform_origin_y, transform_origin_z))
1359 }
1360
1361 pub fn calculate_perspective_matrix(
1363 &self,
1364 border_rect: &Rect<Au, CSSPixel>,
1365 ) -> Option<LayoutTransform> {
1366 let style = self.style();
1367 match style.get_box().perspective {
1368 Perspective::Length(length) => {
1369 let perspective_origin = &style.get_box().perspective_origin;
1370 let perspective_origin = LayoutPoint::new(
1371 perspective_origin
1372 .horizontal
1373 .percentage_relative_to(border_rect.size.width.into())
1374 .px(),
1375 perspective_origin
1376 .vertical
1377 .percentage_relative_to(border_rect.size.height.into())
1378 .px(),
1379 );
1380
1381 let perspective_matrix = LayoutTransform::from_untyped(
1382 &transform::create_perspective_matrix(length.px()),
1383 );
1384
1385 Some(perspective_matrix.change_basis(
1386 perspective_origin.x,
1387 perspective_origin.y,
1388 0.0,
1389 ))
1390 },
1391 Perspective::None => None,
1392 }
1393 }
1394
1395 fn clear_stacking_context_tree_traversal_data_recursively(&self) {
1396 fn clear_stacking_context_tree_traversal_data_on_fragments(fragments: &[Fragment]) {
1397 for fragment in fragments.iter() {
1398 match fragment {
1399 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
1400 .inner_box_fragment()
1401 .clear_stacking_context_tree_traversal_data_recursively(),
1402 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
1403 box_fragment.clear_stacking_context_tree_traversal_data_recursively();
1404 },
1405 Fragment::Positioning(positioning_fragment) => {
1406 clear_stacking_context_tree_traversal_data_on_fragments(
1407 &positioning_fragment.children,
1408 );
1409 },
1410 _ => {},
1411 }
1412 }
1413 }
1414
1415 self.spatial_tree_node.set(None);
1416 self.clear_stacking_context_tree_traversal_data();
1417 clear_stacking_context_tree_traversal_data_on_fragments(&self.children);
1418 }
1419}
1420
1421impl PositioningFragment {
1422 fn build_stacking_context_tree(
1423 &self,
1424 stacking_context_tree: &mut StackingContextTree,
1425 containing_block: &ContainingBlock,
1426 containing_block_info: &ContainingBlockInfo,
1427 stacking_context: &mut StackingContext,
1428 text_decorations: &Rc<Vec<FragmentTextDecoration>>,
1429 ) {
1430 let rect = self
1431 .base
1432 .rect()
1433 .translate(containing_block.rect.origin.to_vector());
1434 let new_containing_block = containing_block.new_replacing_rect(&rect);
1435 let new_containing_block_info =
1436 containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
1437
1438 for child in &self.children {
1439 child.build_stacking_context_tree(
1440 stacking_context_tree,
1441 &new_containing_block_info,
1442 stacking_context,
1443 StackingContextBuildMode::SkipHoisted,
1444 text_decorations,
1445 );
1446 }
1447 }
1448}
1449
1450pub(crate) fn au_rect_to_length_rect(rect: &Rect<Au, CSSPixel>) -> Rect<Length, CSSPixel> {
1451 Rect::new(
1452 Point2D::new(rect.origin.x.into(), rect.origin.y.into()),
1453 Size2D::new(rect.size.width.into(), rect.size.height.into()),
1454 )
1455}