1use std::cell::Cell;
8use std::collections::HashMap;
9
10use base::Epoch;
11use base::id::ScrollTreeNodeId;
12use base::print_tree::PrintTree;
13use bitflags::bitflags;
14use embedder_traits::ViewportDetails;
15use euclid::SideOffsets2D;
16use malloc_size_of_derive::MallocSizeOf;
17use rustc_hash::FxHashMap;
18use serde::{Deserialize, Serialize};
19use servo_geometry::FastLayoutTransform;
20use style::values::specified::Overflow;
21use webrender_api::units::{LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D};
22use webrender_api::{
23 ExternalScrollId, PipelineId, ReferenceFrameKind, ScrollLocation, SpatialId,
24 StickyOffsetBounds, TransformStyle,
25};
26
27#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
31pub struct ScrollType(u8);
32
33bitflags! {
34 impl ScrollType: u8 {
35 const InputEvents = 1 << 0;
38 const Script = 1 << 1;
40 }
41}
42
43impl From<Overflow> for ScrollType {
45 fn from(overflow: Overflow) -> Self {
46 match overflow {
47 Overflow::Hidden => ScrollType::Script,
48 Overflow::Scroll | Overflow::Auto => ScrollType::Script | ScrollType::InputEvents,
49 Overflow::Visible | Overflow::Clip => ScrollType::empty(),
50 }
51 }
52}
53
54#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
56pub struct AxesScrollSensitivity {
57 pub x: ScrollType,
58 pub y: ScrollType,
59}
60
61#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
62pub enum SpatialTreeNodeInfo {
63 ReferenceFrame(ReferenceFrameNodeInfo),
64 Scroll(ScrollableNodeInfo),
65 Sticky(StickyNodeInfo),
66}
67
68#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
69pub struct StickyNodeInfo {
70 pub frame_rect: LayoutRect,
71 pub margins: SideOffsets2D<Option<f32>, LayoutPixel>,
72 pub vertical_offset_bounds: StickyOffsetBounds,
73 pub horizontal_offset_bounds: StickyOffsetBounds,
74}
75
76impl StickyNodeInfo {
77 fn calculate_sticky_offset(
82 &self,
83 viewport_scroll_offset: &LayoutVector2D,
84 viewport_rect: &LayoutRect,
85 ) -> LayoutVector2D {
86 if self.margins.top.is_none() &&
87 self.margins.bottom.is_none() &&
88 self.margins.left.is_none() &&
89 self.margins.right.is_none()
90 {
91 return LayoutVector2D::zero();
92 }
93
94 let mut sticky_rect = self.frame_rect.translate(*viewport_scroll_offset);
100
101 let mut sticky_offset = LayoutVector2D::zero();
102 if let Some(margin) = self.margins.top {
103 let top_viewport_edge = viewport_rect.min.y + margin;
104 if sticky_rect.min.y < top_viewport_edge {
105 sticky_offset.y = top_viewport_edge - sticky_rect.min.y;
108 }
109 }
110
111 if sticky_offset.y <= 0.0 {
116 if let Some(margin) = self.margins.bottom {
117 sticky_rect.min.y += sticky_offset.y;
123 sticky_rect.max.y += sticky_offset.y;
124
125 let bottom_viewport_edge = viewport_rect.max.y - margin;
130 if sticky_rect.max.y > bottom_viewport_edge {
131 sticky_offset.y += bottom_viewport_edge - sticky_rect.max.y;
132 }
133 }
134 }
135
136 if let Some(margin) = self.margins.left {
138 let left_viewport_edge = viewport_rect.min.x + margin;
139 if sticky_rect.min.x < left_viewport_edge {
140 sticky_offset.x = left_viewport_edge - sticky_rect.min.x;
141 }
142 }
143
144 if sticky_offset.x <= 0.0 {
145 if let Some(margin) = self.margins.right {
146 sticky_rect.min.x += sticky_offset.x;
147 sticky_rect.max.x += sticky_offset.x;
148 let right_viewport_edge = viewport_rect.max.x - margin;
149 if sticky_rect.max.x > right_viewport_edge {
150 sticky_offset.x += right_viewport_edge - sticky_rect.max.x;
151 }
152 }
153 }
154
155 let clamp =
158 |value: f32, bounds: &StickyOffsetBounds| (value).max(bounds.min).min(bounds.max);
159 sticky_offset.y = clamp(sticky_offset.y, &self.vertical_offset_bounds);
160 sticky_offset.x = clamp(sticky_offset.x, &self.horizontal_offset_bounds);
161
162 sticky_offset
163 }
164}
165
166#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
167pub struct ReferenceFrameNodeInfo {
168 pub origin: LayoutPoint,
169 pub frame_origin_for_query: LayoutPoint,
171 pub transform_style: TransformStyle,
172 pub transform: FastLayoutTransform,
173 pub kind: ReferenceFrameKind,
174}
175
176#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
179pub struct ScrollableNodeInfo {
180 pub external_id: ExternalScrollId,
183
184 pub content_rect: LayoutRect,
186
187 pub clip_rect: LayoutRect,
189
190 pub scroll_sensitivity: AxesScrollSensitivity,
192
193 pub offset: LayoutVector2D,
195
196 pub offset_changed: Cell<bool>,
199}
200
201impl ScrollableNodeInfo {
202 fn scroll_to_offset(
203 &mut self,
204 new_offset: LayoutVector2D,
205 context: ScrollType,
206 ) -> Option<LayoutVector2D> {
207 if !self.scroll_sensitivity.x.contains(context) &&
208 !self.scroll_sensitivity.y.contains(context)
209 {
210 return None;
211 }
212
213 let scrollable_size = self.scrollable_size();
214 let original_layer_scroll_offset = self.offset;
215
216 if scrollable_size.width > 0. && self.scroll_sensitivity.x.contains(context) {
217 self.offset.x = new_offset.x.clamp(0.0, scrollable_size.width);
218 }
219
220 if scrollable_size.height > 0. && self.scroll_sensitivity.y.contains(context) {
221 self.offset.y = new_offset.y.clamp(0.0, scrollable_size.height);
222 }
223
224 if self.offset != original_layer_scroll_offset {
225 self.offset_changed.set(true);
226 Some(self.offset)
227 } else {
228 None
229 }
230 }
231
232 fn scroll_to_webrender_location(
233 &mut self,
234 scroll_location: ScrollLocation,
235 context: ScrollType,
236 ) -> Option<LayoutVector2D> {
237 if !self.scroll_sensitivity.x.contains(context) &&
238 !self.scroll_sensitivity.y.contains(context)
239 {
240 return None;
241 }
242
243 let delta = match scroll_location {
244 ScrollLocation::Delta(delta) => delta,
245 ScrollLocation::Start => {
246 if self.offset.y.round() <= 0.0 {
247 return None;
249 }
250
251 self.offset.y = 0.0;
252 self.offset_changed.set(true);
253 return Some(self.offset);
254 },
255 ScrollLocation::End => {
256 let end_pos = self.scrollable_size().height;
257 if self.offset.y.round() >= end_pos {
258 return None;
260 }
261
262 self.offset.y = end_pos;
263 self.offset_changed.set(true);
264 return Some(self.offset);
265 },
266 };
267
268 self.scroll_to_offset(self.offset + delta, context)
269 }
270}
271
272impl ScrollableNodeInfo {
273 fn scrollable_size(&self) -> LayoutSize {
274 self.content_rect.size() - self.clip_rect.size()
275 }
276}
277
278#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
285pub struct ScrollTreeNodeTransformationCache {
286 node_to_root_transform: FastLayoutTransform,
287 root_to_node_transform: Option<FastLayoutTransform>,
288 nearest_scrolling_ancestor_offset: LayoutVector2D,
289 nearest_scrolling_ancestor_viewport: LayoutRect,
290 cumulative_sticky_offsets: LayoutVector2D,
291}
292
293#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
294pub struct ScrollTreeNode {
297 pub parent: Option<ScrollTreeNodeId>,
300
301 pub children: Vec<ScrollTreeNodeId>,
303
304 pub webrender_id: Option<SpatialId>,
307
308 pub info: SpatialTreeNodeInfo,
311
312 transformation_cache: Cell<Option<ScrollTreeNodeTransformationCache>>,
315}
316
317impl ScrollTreeNode {
318 pub fn webrender_id(&self) -> SpatialId {
321 self.webrender_id
322 .expect("Should have called ScrollTree::build_display_list before querying SpatialId")
323 }
324
325 pub fn external_id(&self) -> Option<ExternalScrollId> {
327 match self.info {
328 SpatialTreeNodeInfo::Scroll(ref info) => Some(info.external_id),
329 _ => None,
330 }
331 }
332
333 pub fn offset(&self) -> Option<LayoutVector2D> {
335 match self.info {
336 SpatialTreeNodeInfo::Scroll(ref info) => Some(info.offset),
337 _ => None,
338 }
339 }
340
341 fn scroll(
345 &mut self,
346 scroll_location: ScrollLocation,
347 context: ScrollType,
348 ) -> Option<(ExternalScrollId, LayoutVector2D)> {
349 let SpatialTreeNodeInfo::Scroll(ref mut info) = self.info else {
350 return None;
351 };
352
353 info.scroll_to_webrender_location(scroll_location, context)
354 .map(|location| (info.external_id, location))
355 }
356
357 pub fn debug_print(&self, print_tree: &mut PrintTree, node_index: usize) {
358 match &self.info {
359 SpatialTreeNodeInfo::ReferenceFrame(info) => {
360 print_tree.new_level(format!(
361 "Reference Frame({node_index}): webrender_id={:?}\
362 \norigin: {:?}\
363 \ntransform_style: {:?}\
364 \ntransform: {:?}\
365 \nkind: {:?}",
366 self.webrender_id, info.origin, info.transform_style, info.transform, info.kind,
367 ));
368 },
369 SpatialTreeNodeInfo::Scroll(info) => {
370 print_tree.new_level(format!(
371 "Scroll Frame({node_index}): webrender_id={:?}\
372 \nexternal_id: {:?}\
373 \ncontent_rect: {:?}\
374 \nclip_rect: {:?}\
375 \nscroll_sensitivity: {:?}\
376 \noffset: {:?}",
377 self.webrender_id,
378 info.external_id,
379 info.content_rect,
380 info.clip_rect,
381 info.scroll_sensitivity,
382 info.offset,
383 ));
384 },
385 SpatialTreeNodeInfo::Sticky(info) => {
386 print_tree.new_level(format!(
387 "Sticky Frame({node_index}): webrender_id={:?}\
388 \nframe_rect: {:?}\
389 \nmargins: {:?}\
390 \nhorizontal_offset_bounds: {:?}\
391 \nvertical_offset_bounds: {:?}",
392 self.webrender_id,
393 info.frame_rect,
394 info.margins,
395 info.horizontal_offset_bounds,
396 info.vertical_offset_bounds,
397 ));
398 },
399 };
400 }
401
402 fn invalidate_cached_transforms(&self, scroll_tree: &ScrollTree, ancestors_invalid: bool) {
403 let node_invalid = match &self.info {
404 SpatialTreeNodeInfo::Scroll(info) => info.offset_changed.take(),
405 _ => false,
406 };
407
408 let invalid = node_invalid || ancestors_invalid;
409 if invalid {
410 self.transformation_cache.set(None);
411 }
412
413 for child_id in &self.children {
414 scroll_tree
415 .get_node(*child_id)
416 .invalidate_cached_transforms(scroll_tree, invalid);
417 }
418 }
419}
420
421#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)]
425pub struct ScrollTree {
426 pub nodes: Vec<ScrollTreeNode>,
430}
431
432impl ScrollTree {
433 pub fn add_scroll_tree_node(
435 &mut self,
436 parent: Option<ScrollTreeNodeId>,
437 info: SpatialTreeNodeInfo,
438 ) -> ScrollTreeNodeId {
439 self.nodes.push(ScrollTreeNode {
440 parent,
441 children: Vec::new(),
442 webrender_id: None,
443 info,
444 transformation_cache: Cell::default(),
445 });
446
447 let new_node_id = ScrollTreeNodeId {
448 index: self.nodes.len() - 1,
449 };
450
451 if let Some(parent_id) = parent {
452 self.get_node_mut(parent_id).children.push(new_node_id);
453 }
454
455 new_node_id
456 }
457
458 pub fn update_mapping(&mut self, mapping: Vec<SpatialId>) {
461 for (spatial_id, node) in mapping.into_iter().zip(self.nodes.iter_mut()) {
462 node.webrender_id = Some(spatial_id);
463 }
464 }
465
466 pub fn get_node_mut(&mut self, id: ScrollTreeNodeId) -> &mut ScrollTreeNode {
468 &mut self.nodes[id.index]
469 }
470
471 pub fn get_node(&self, id: ScrollTreeNodeId) -> &ScrollTreeNode {
473 &self.nodes[id.index]
474 }
475
476 pub fn webrender_id(&self, id: ScrollTreeNodeId) -> SpatialId {
479 self.get_node(id).webrender_id()
480 }
481
482 pub fn scroll_node_or_ancestor_inner(
483 &mut self,
484 scroll_node_id: ScrollTreeNodeId,
485 scroll_location: ScrollLocation,
486 context: ScrollType,
487 ) -> Option<(ExternalScrollId, LayoutVector2D)> {
488 let parent = {
489 let node = &mut self.get_node_mut(scroll_node_id);
490 let result = node.scroll(scroll_location, context);
491 if result.is_some() {
492 return result;
493 }
494 node.parent
495 };
496
497 parent
498 .and_then(|parent| self.scroll_node_or_ancestor_inner(parent, scroll_location, context))
499 }
500
501 fn node_with_external_scroll_node_id(
502 &self,
503 external_id: ExternalScrollId,
504 ) -> Option<ScrollTreeNodeId> {
505 self.nodes
506 .iter()
507 .enumerate()
508 .find_map(|(index, node)| match &node.info {
509 SpatialTreeNodeInfo::Scroll(info) if info.external_id == external_id => {
510 Some(ScrollTreeNodeId { index })
511 },
512 _ => None,
513 })
514 }
515
516 pub fn scroll_node_or_ancestor(
521 &mut self,
522 external_id: ExternalScrollId,
523 scroll_location: ScrollLocation,
524 context: ScrollType,
525 ) -> Option<(ExternalScrollId, LayoutVector2D)> {
526 let scroll_node_id = self.node_with_external_scroll_node_id(external_id)?;
527 let result = self.scroll_node_or_ancestor_inner(scroll_node_id, scroll_location, context);
528 if result.is_some() {
529 self.invalidate_cached_transforms();
530 }
531 result
532 }
533
534 pub fn set_scroll_offset_for_node_with_external_scroll_id(
537 &mut self,
538 external_scroll_id: ExternalScrollId,
539 offset: LayoutVector2D,
540 context: ScrollType,
541 ) -> Option<LayoutVector2D> {
542 let result = self.nodes.iter_mut().find_map(|node| match node.info {
543 SpatialTreeNodeInfo::Scroll(ref mut scroll_info)
544 if scroll_info.external_id == external_scroll_id =>
545 {
546 scroll_info.scroll_to_offset(offset, context)
547 },
548 _ => None,
549 });
550
551 if result.is_some() {
552 self.invalidate_cached_transforms();
553 }
554
555 result
556 }
557
558 pub fn set_all_scroll_offsets(
561 &mut self,
562 offsets: &FxHashMap<ExternalScrollId, LayoutVector2D>,
563 ) {
564 for node in self.nodes.iter_mut() {
565 if let SpatialTreeNodeInfo::Scroll(ref mut scroll_info) = node.info {
566 if let Some(offset) = offsets.get(&scroll_info.external_id) {
567 scroll_info.scroll_to_offset(*offset, ScrollType::Script);
568 }
569 }
570 }
571
572 self.invalidate_cached_transforms();
573 }
574
575 pub fn reset_all_scroll_offsets(&mut self) {
577 for node in self.nodes.iter_mut() {
578 if let SpatialTreeNodeInfo::Scroll(ref mut scroll_info) = node.info {
579 scroll_info.scroll_to_offset(LayoutVector2D::zero(), ScrollType::Script);
580 }
581 }
582
583 self.invalidate_cached_transforms();
584 }
585
586 pub fn scroll_offsets(&self) -> FxHashMap<ExternalScrollId, LayoutVector2D> {
589 HashMap::from_iter(self.nodes.iter().filter_map(|node| match node.info {
590 SpatialTreeNodeInfo::Scroll(ref scroll_info) => {
591 Some((scroll_info.external_id, scroll_info.offset))
592 },
593 _ => None,
594 }))
595 }
596
597 pub fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
600 self.nodes.iter().find_map(|node| match node.info {
601 SpatialTreeNodeInfo::Scroll(ref info) if info.external_id == id => Some(info.offset),
602 _ => None,
603 })
604 }
605
606 pub fn cumulative_node_to_root_transform(
609 &self,
610 node_id: ScrollTreeNodeId,
611 ) -> FastLayoutTransform {
612 self.cumulative_node_transform(node_id)
613 .node_to_root_transform
614 }
615
616 pub fn cumulative_root_to_node_transform(
620 &self,
621 node_id: ScrollTreeNodeId,
622 ) -> Option<FastLayoutTransform> {
623 self.cumulative_node_transform(node_id)
624 .root_to_node_transform
625 }
626
627 pub fn reference_frame_offset(&self, node_id: ScrollTreeNodeId) -> LayoutPoint {
630 let mut maybe_node_id = Some(node_id);
631 while let Some(node_id) = maybe_node_id {
632 let node = self.get_node(node_id);
633 if let SpatialTreeNodeInfo::ReferenceFrame(reference_frame) = &node.info {
634 return reference_frame.frame_origin_for_query;
635 }
636 maybe_node_id = node.parent;
637 }
638 Default::default()
639 }
640
641 pub fn cumulative_sticky_offsets(&self, node_id: ScrollTreeNodeId) -> LayoutVector2D {
644 self.cumulative_node_transform(node_id)
645 .cumulative_sticky_offsets
646 }
647
648 fn cumulative_node_transform(
649 &self,
650 node_id: ScrollTreeNodeId,
651 ) -> ScrollTreeNodeTransformationCache {
652 let node = self.get_node(node_id);
653 if let Some(cached_transforms) = node.transformation_cache.get() {
654 return cached_transforms;
655 }
656
657 let transforms = self.cumulative_node_transform_inner(node);
658 node.transformation_cache.set(Some(transforms));
659 transforms
660 }
661
662 fn cumulative_node_transform_inner(
664 &self,
665 node: &ScrollTreeNode,
666 ) -> ScrollTreeNodeTransformationCache {
667 let parent_transforms = node
668 .parent
669 .map(|parent_id| self.cumulative_node_transform(parent_id))
670 .unwrap_or_default();
671
672 let node_to_root_transform = |node_to_parent_transform: FastLayoutTransform| {
673 node_to_parent_transform.then(&parent_transforms.node_to_root_transform)
674 };
675 let root_to_node_transform = |parent_to_node_transform: FastLayoutTransform| {
676 parent_transforms
677 .root_to_node_transform
678 .map_or(parent_to_node_transform, |parent_transform| {
679 parent_transform.then(&parent_to_node_transform)
680 })
681 };
682
683 match &node.info {
684 SpatialTreeNodeInfo::ReferenceFrame(info) => {
685 let offset = info.frame_origin_for_query.to_vector();
688 let node_to_parent_transform =
689 info.transform.pre_translate(-offset).then_translate(offset);
690 let parent_to_node_transform = info.transform.inverse().map(|inverse_transform| {
691 FastLayoutTransform::Offset(-info.origin.to_vector()).then(&inverse_transform)
692 });
693 ScrollTreeNodeTransformationCache {
694 node_to_root_transform: node_to_root_transform(node_to_parent_transform),
695 root_to_node_transform: parent_to_node_transform.map(root_to_node_transform),
696 nearest_scrolling_ancestor_viewport: parent_transforms
697 .nearest_scrolling_ancestor_viewport
698 .translate(-info.origin.to_vector()),
699 nearest_scrolling_ancestor_offset: parent_transforms
700 .nearest_scrolling_ancestor_offset,
701 cumulative_sticky_offsets: parent_transforms.cumulative_sticky_offsets,
702 }
703 },
704 SpatialTreeNodeInfo::Scroll(info) => {
705 let node_to_parent_transform = FastLayoutTransform::Offset(-info.offset);
706 let parent_to_node_transform = node_to_parent_transform.inverse();
707 ScrollTreeNodeTransformationCache {
708 node_to_root_transform: node_to_root_transform(node_to_parent_transform),
709 root_to_node_transform: parent_to_node_transform.map(root_to_node_transform),
710 nearest_scrolling_ancestor_viewport: info.clip_rect,
711 nearest_scrolling_ancestor_offset: -info.offset,
712 cumulative_sticky_offsets: parent_transforms.cumulative_sticky_offsets,
713 }
714 },
715
716 SpatialTreeNodeInfo::Sticky(info) => {
717 let offset = info.calculate_sticky_offset(
718 &parent_transforms.nearest_scrolling_ancestor_offset,
719 &parent_transforms.nearest_scrolling_ancestor_viewport,
720 );
721 let node_to_parent_transform = FastLayoutTransform::Offset(offset);
722 let parent_to_node_transform = node_to_parent_transform.inverse();
723 ScrollTreeNodeTransformationCache {
724 node_to_root_transform: node_to_root_transform(node_to_parent_transform),
725 root_to_node_transform: parent_to_node_transform.map(root_to_node_transform),
726 nearest_scrolling_ancestor_viewport: parent_transforms
727 .nearest_scrolling_ancestor_viewport,
728 nearest_scrolling_ancestor_offset: parent_transforms
729 .nearest_scrolling_ancestor_offset +
730 offset,
731 cumulative_sticky_offsets: parent_transforms.cumulative_sticky_offsets + offset,
732 }
733 },
734 }
735 }
736
737 fn invalidate_cached_transforms(&self) {
738 let Some(root_node) = self.nodes.first() else {
739 return;
740 };
741 root_node.invalidate_cached_transforms(self, false );
742 }
743
744 fn external_scroll_id_for_scroll_tree_node(
745 &self,
746 id: ScrollTreeNodeId,
747 ) -> Option<ExternalScrollId> {
748 let mut maybe_node = Some(self.get_node(id));
749
750 while let Some(node) = maybe_node {
751 if let Some(external_scroll_id) = node.external_id() {
752 return Some(external_scroll_id);
753 }
754 maybe_node = node.parent.map(|id| self.get_node(id));
755 }
756
757 None
758 }
759}
760
761type AdjacencyListForPrint = Vec<Vec<ScrollTreeNodeId>>;
767
768impl ScrollTree {
772 fn nodes_in_adjacency_list(&self) -> AdjacencyListForPrint {
773 let mut adjacency_list: AdjacencyListForPrint = vec![Default::default(); self.nodes.len()];
774
775 for (node_index, node) in self.nodes.iter().enumerate() {
776 let current_id = ScrollTreeNodeId { index: node_index };
777 if let Some(parent_id) = node.parent {
778 adjacency_list[parent_id.index].push(current_id);
779 }
780 }
781
782 adjacency_list
783 }
784
785 fn debug_print_traversal(
786 &self,
787 print_tree: &mut PrintTree,
788 current_id: ScrollTreeNodeId,
789 adjacency_list: &[Vec<ScrollTreeNodeId>],
790 ) {
791 for node_id in &adjacency_list[current_id.index] {
792 self.nodes[node_id.index].debug_print(print_tree, node_id.index);
793 self.debug_print_traversal(print_tree, *node_id, adjacency_list);
794 }
795 print_tree.end_level();
796 }
797
798 pub fn debug_print(&self) {
805 let mut print_tree = PrintTree::new("Scroll Tree".to_owned());
806
807 let adj_list = self.nodes_in_adjacency_list();
808 let root_id = ScrollTreeNodeId { index: 0 };
809
810 self.nodes[root_id.index].debug_print(&mut print_tree, root_id.index);
811 self.debug_print_traversal(&mut print_tree, root_id, &adj_list);
812 print_tree.end_level();
813 }
814}
815
816#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
819pub struct PaintDisplayListInfo {
820 pub pipeline_id: PipelineId,
822
823 pub viewport_details: ViewportDetails,
826
827 pub content_size: LayoutSize,
829
830 pub epoch: Epoch,
832
833 pub scroll_tree: ScrollTree,
836
837 pub root_reference_frame_id: ScrollTreeNodeId,
840
841 pub root_scroll_node_id: ScrollTreeNodeId,
844
845 pub is_contentful: bool,
849
850 pub first_reflow: bool,
853}
854
855impl PaintDisplayListInfo {
856 pub fn new(
859 viewport_details: ViewportDetails,
860 content_size: LayoutSize,
861 pipeline_id: PipelineId,
862 epoch: Epoch,
863 viewport_scroll_sensitivity: AxesScrollSensitivity,
864 first_reflow: bool,
865 ) -> Self {
866 let mut scroll_tree = ScrollTree::default();
867 let root_reference_frame_id = scroll_tree.add_scroll_tree_node(
868 None,
869 SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
870 origin: Default::default(),
871 frame_origin_for_query: Default::default(),
872 transform_style: TransformStyle::Flat,
873 transform: FastLayoutTransform::identity(),
874 kind: ReferenceFrameKind::default(),
875 }),
876 );
877 let root_scroll_node_id = scroll_tree.add_scroll_tree_node(
878 Some(root_reference_frame_id),
879 SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
880 external_id: ExternalScrollId(0, pipeline_id),
881 content_rect: LayoutRect::from_origin_and_size(LayoutPoint::zero(), content_size),
882 clip_rect: LayoutRect::from_origin_and_size(
883 LayoutPoint::zero(),
884 viewport_details.layout_size(),
885 ),
886 scroll_sensitivity: viewport_scroll_sensitivity,
887 offset: LayoutVector2D::zero(),
888 offset_changed: Cell::new(false),
889 }),
890 );
891
892 PaintDisplayListInfo {
893 pipeline_id,
894 viewport_details,
895 content_size,
896 epoch,
897 scroll_tree,
898 root_reference_frame_id,
899 root_scroll_node_id,
900 is_contentful: false,
901 first_reflow,
902 }
903 }
904
905 pub fn external_scroll_id_for_scroll_tree_node(
906 &self,
907 id: ScrollTreeNodeId,
908 ) -> ExternalScrollId {
909 self.scroll_tree
910 .external_scroll_id_for_scroll_tree_node(id)
911 .unwrap_or(ExternalScrollId(0, self.pipeline_id))
912 }
913}