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