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