1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint, LayerRect, LayerVector2D};
6 use api::{PipelineId, ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollNodeState};
7 use api::WorldPoint;
8 use clip::{ClipChain, ClipSourcesHandle, ClipStore};
9 use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo};
10 use gpu_cache::GpuCache;
11 use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData};
12 use internal_types::{FastHashMap, FastHashSet};
13 use print_tree::{PrintTree, PrintTreePrinter};
14 use resource_cache::ResourceCache;
15 use scene::SceneProperties;
16 use util::{LayerFastTransform, LayerToWorldFastTransform};
17 
18 pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
19 
20 /// An id that identifies coordinate systems in the ClipScrollTree. Each
21 /// coordinate system has an id and those ids will be shared when the coordinates
22 /// system are the same or are in the same axis-aligned space. This allows
23 /// for optimizing mask generation.
24 #[derive(Debug, Copy, Clone, PartialEq)]
25 #[cfg_attr(feature = "capture", derive(Serialize))]
26 #[cfg_attr(feature = "replay", derive(Deserialize))]
27 pub struct CoordinateSystemId(pub u32);
28 
29 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
30 pub struct ClipScrollNodeIndex(pub usize);
31 
32 const ROOT_REFERENCE_FRAME_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(0);
33 const TOPMOST_SCROLL_NODE_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(1);
34 
35 impl CoordinateSystemId {
root() -> Self36     pub fn root() -> Self {
37         CoordinateSystemId(0)
38     }
39 
next(&self) -> Self40     pub fn next(&self) -> Self {
41         let CoordinateSystemId(id) = *self;
42         CoordinateSystemId(id + 1)
43     }
44 
advance(&mut self)45     pub fn advance(&mut self) {
46         self.0 += 1;
47     }
48 }
49 
50 pub struct ClipChainDescriptor {
51     pub index: ClipChainIndex,
52     pub parent: Option<ClipChainIndex>,
53     pub clips: Vec<ClipScrollNodeIndex>,
54 }
55 
56 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
57 pub struct ClipChainIndex(pub usize);
58 
59 pub struct ClipScrollTree {
60     pub nodes: Vec<ClipScrollNode>,
61 
62     /// A Vec of all descriptors that describe ClipChains in the order in which they are
63     /// encountered during display list flattening. ClipChains are expected to never be
64     /// the children of ClipChains later in the list.
65     pub clip_chains_descriptors: Vec<ClipChainDescriptor>,
66 
67     /// A vector of all ClipChains in this ClipScrollTree including those from
68     /// ClipChainDescriptors and also those defined by the clipping node hierarchy.
69     pub clip_chains: Vec<ClipChain>,
70 
71     pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayerPoint, ScrollClamping)>,
72 
73     /// The ClipId of the currently scrolling node. Used to allow the same
74     /// node to scroll even if a touch operation leaves the boundaries of that node.
75     pub currently_scrolling_node_index: Option<ClipScrollNodeIndex>,
76 
77     /// The current frame id, used for giving a unique id to all new dynamically
78     /// added frames and clips. The ClipScrollTree increments this by one every
79     /// time a new dynamic frame is created.
80     current_new_node_item: u64,
81 
82     /// A set of pipelines which should be discarded the next time this
83     /// tree is drained.
84     pub pipelines_to_discard: FastHashSet<PipelineId>,
85 }
86 
87 #[derive(Clone)]
88 pub struct TransformUpdateState {
89     pub parent_reference_frame_transform: LayerToWorldFastTransform,
90     pub parent_accumulated_scroll_offset: LayerVector2D,
91     pub nearest_scrolling_ancestor_offset: LayerVector2D,
92     pub nearest_scrolling_ancestor_viewport: LayerRect,
93 
94     /// The index of the current parent's clip chain.
95     pub parent_clip_chain_index: ClipChainIndex,
96 
97     /// An id for keeping track of the axis-aligned space of this node. This is used in
98     /// order to to track what kinds of clip optimizations can be done for a particular
99     /// display list item, since optimizations can usually only be done among
100     /// coordinate systems which are relatively axis aligned.
101     pub current_coordinate_system_id: CoordinateSystemId,
102 
103     /// Transform from the coordinate system that started this compatible coordinate system.
104     pub coordinate_system_relative_transform: LayerFastTransform,
105 
106     /// True if this node is transformed by an invertible transform.  If not, display items
107     /// transformed by this node will not be displayed and display items not transformed by this
108     /// node will not be clipped by clips that are transformed by this node.
109     pub invertible: bool,
110 }
111 
112 impl ClipScrollTree {
new() -> Self113     pub fn new() -> Self {
114         ClipScrollTree {
115             nodes: Vec::new(),
116             clip_chains_descriptors: Vec::new(),
117             clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
118             pending_scroll_offsets: FastHashMap::default(),
119             currently_scrolling_node_index: None,
120             current_new_node_item: 1,
121             pipelines_to_discard: FastHashSet::default(),
122         }
123     }
124 
125     /// The root reference frame, which is the true root of the ClipScrollTree. Initially
126     /// this ID is not valid, which is indicated by ```nodes``` being empty.
root_reference_frame_index(&self) -> ClipScrollNodeIndex127     pub fn root_reference_frame_index(&self) -> ClipScrollNodeIndex {
128         // TODO(mrobinson): We should eventually make this impossible to misuse.
129         debug_assert!(!self.nodes.is_empty());
130         ROOT_REFERENCE_FRAME_INDEX
131     }
132 
133     /// The root scroll node which is the first child of the root reference frame.
134     /// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
topmost_scroll_node_index(&self) -> ClipScrollNodeIndex135     pub fn topmost_scroll_node_index(&self) -> ClipScrollNodeIndex {
136         // TODO(mrobinson): We should eventually make this impossible to misuse.
137         debug_assert!(self.nodes.len() >= 1);
138         TOPMOST_SCROLL_NODE_INDEX
139     }
140 
collect_nodes_bouncing_back(&self) -> FastHashSet<ClipScrollNodeIndex>141     pub fn collect_nodes_bouncing_back(&self) -> FastHashSet<ClipScrollNodeIndex> {
142         let mut nodes_bouncing_back = FastHashSet::default();
143         for (index, node) in self.nodes.iter().enumerate() {
144             if let NodeType::ScrollFrame(ref scrolling) = node.node_type {
145                 if scrolling.bouncing_back {
146                     nodes_bouncing_back.insert(ClipScrollNodeIndex(index));
147                 }
148             }
149         }
150         nodes_bouncing_back
151     }
152 
find_scrolling_node_at_point_in_node( &self, cursor: &WorldPoint, index: ClipScrollNodeIndex, ) -> Option<ClipScrollNodeIndex>153     fn find_scrolling_node_at_point_in_node(
154         &self,
155         cursor: &WorldPoint,
156         index: ClipScrollNodeIndex,
157     ) -> Option<ClipScrollNodeIndex> {
158         let node = &self.nodes[index.0];
159         for child_index in node.children.iter().rev() {
160             let found_index = self.find_scrolling_node_at_point_in_node(cursor, *child_index);
161             if found_index.is_some() {
162                 return found_index;
163             }
164         }
165 
166         match node.node_type {
167             NodeType::ScrollFrame(state) if state.sensitive_to_input_events() => {}
168             _ => return None,
169         }
170 
171         if node.ray_intersects_node(cursor) {
172             Some(index)
173         } else {
174             None
175         }
176     }
177 
find_scrolling_node_at_point(&self, cursor: &WorldPoint) -> ClipScrollNodeIndex178     pub fn find_scrolling_node_at_point(&self, cursor: &WorldPoint) -> ClipScrollNodeIndex {
179         self.find_scrolling_node_at_point_in_node(cursor, self.root_reference_frame_index())
180             .unwrap_or(self.topmost_scroll_node_index())
181     }
182 
get_scroll_node_state(&self) -> Vec<ScrollNodeState>183     pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
184         let mut result = vec![];
185         for node in &self.nodes {
186             if let NodeType::ScrollFrame(info) = node.node_type {
187                 if let Some(id) = info.external_id {
188                     result.push(ScrollNodeState { id, scroll_offset: info.offset })
189                 }
190             }
191         }
192         result
193     }
194 
drain(&mut self) -> ScrollStates195     pub fn drain(&mut self) -> ScrollStates {
196         self.current_new_node_item = 1;
197 
198         let mut scroll_states = FastHashMap::default();
199         for old_node in &mut self.nodes.drain(..) {
200             if self.pipelines_to_discard.contains(&old_node.pipeline_id) {
201                 continue;
202             }
203 
204             match old_node.node_type {
205                 NodeType::ScrollFrame(info) if info.external_id.is_some() => {
206                     scroll_states.insert(info.external_id.unwrap(), info);
207                 }
208                 _ => {}
209             }
210         }
211 
212         self.pipelines_to_discard.clear();
213         self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())];
214         self.clip_chains_descriptors.clear();
215         scroll_states
216     }
217 
scroll_node( &mut self, origin: LayerPoint, id: ExternalScrollId, clamp: ScrollClamping ) -> bool218     pub fn scroll_node(
219         &mut self,
220         origin: LayerPoint,
221         id: ExternalScrollId,
222         clamp: ScrollClamping
223     ) -> bool {
224         for node in &mut self.nodes {
225             if node.matches_external_id(id) {
226                 return node.set_scroll_origin(&origin, clamp);
227             }
228         }
229 
230         self.pending_scroll_offsets.insert(id, (origin, clamp));
231         false
232     }
233 
scroll( &mut self, scroll_location: ScrollLocation, cursor: WorldPoint, phase: ScrollEventPhase, ) -> bool234     pub fn scroll(
235         &mut self,
236         scroll_location: ScrollLocation,
237         cursor: WorldPoint,
238         phase: ScrollEventPhase,
239     ) -> bool {
240         if self.nodes.is_empty() {
241             return false;
242         }
243 
244         let node_index = match (
245             phase,
246             self.find_scrolling_node_at_point(&cursor),
247             self.currently_scrolling_node_index,
248         ) {
249             (ScrollEventPhase::Start, scroll_node_at_point_index, _) => {
250                 self.currently_scrolling_node_index = Some(scroll_node_at_point_index);
251                 scroll_node_at_point_index
252             }
253             (_, scroll_node_at_point_index, Some(cached_node_index)) => {
254                 let node_index = match self.nodes.get(cached_node_index.0) {
255                     Some(_) => cached_node_index,
256                     None => {
257                         self.currently_scrolling_node_index = Some(scroll_node_at_point_index);
258                         scroll_node_at_point_index
259                     }
260                 };
261                 node_index
262             }
263             (_, _, None) => return false,
264         };
265 
266         let topmost_scroll_node_index = self.topmost_scroll_node_index();
267         let non_root_overscroll = if node_index != topmost_scroll_node_index {
268             self.nodes[node_index.0].is_overscrolling()
269         } else {
270             false
271         };
272 
273         let mut switch_node = false;
274         {
275             let node = &mut self.nodes[node_index.0];
276             if let NodeType::ScrollFrame(ref mut scrolling) = node.node_type {
277                 match phase {
278                     ScrollEventPhase::Start => {
279                         // if this is a new gesture, we do not switch node,
280                         // however we do save the state of non_root_overscroll,
281                         // for use in the subsequent Move phase.
282                         scrolling.should_handoff_scroll = non_root_overscroll;
283                     }
284                     ScrollEventPhase::Move(_) => {
285                         // Switch node if movement originated in a new gesture,
286                         // from a non root node in overscroll.
287                         switch_node = scrolling.should_handoff_scroll && non_root_overscroll
288                     }
289                     ScrollEventPhase::End => {
290                         // clean-up when gesture ends.
291                         scrolling.should_handoff_scroll = false;
292                     }
293                 }
294             }
295         }
296 
297         let node_index = if switch_node {
298             topmost_scroll_node_index
299         } else {
300             node_index
301         };
302 
303         self.nodes[node_index.0].scroll(scroll_location, phase)
304     }
305 
update_tree( &mut self, screen_rect: &DeviceIntRect, device_pixel_scale: DevicePixelScale, clip_store: &mut ClipStore, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, pan: WorldPoint, node_data: &mut Vec<ClipScrollNodeData>, scene_properties: &SceneProperties, )306     pub fn update_tree(
307         &mut self,
308         screen_rect: &DeviceIntRect,
309         device_pixel_scale: DevicePixelScale,
310         clip_store: &mut ClipStore,
311         resource_cache: &mut ResourceCache,
312         gpu_cache: &mut GpuCache,
313         pan: WorldPoint,
314         node_data: &mut Vec<ClipScrollNodeData>,
315         scene_properties: &SceneProperties,
316     ) {
317         if self.nodes.is_empty() {
318             return;
319         }
320 
321         self.clip_chains[0] = ClipChain::empty(screen_rect);
322 
323         let root_reference_frame_index = self.root_reference_frame_index();
324         let mut state = TransformUpdateState {
325             parent_reference_frame_transform: LayerVector2D::new(pan.x, pan.y).into(),
326             parent_accumulated_scroll_offset: LayerVector2D::zero(),
327             nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
328             nearest_scrolling_ancestor_viewport: LayerRect::zero(),
329             parent_clip_chain_index: ClipChainIndex(0),
330             current_coordinate_system_id: CoordinateSystemId::root(),
331             coordinate_system_relative_transform: LayerFastTransform::identity(),
332             invertible: true,
333         };
334         let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
335         self.update_node(
336             root_reference_frame_index,
337             &mut state,
338             &mut next_coordinate_system_id,
339             device_pixel_scale,
340             clip_store,
341             resource_cache,
342             gpu_cache,
343             node_data,
344             scene_properties,
345         );
346 
347         self.build_clip_chains(screen_rect);
348     }
349 
update_node( &mut self, node_index: ClipScrollNodeIndex, state: &mut TransformUpdateState, next_coordinate_system_id: &mut CoordinateSystemId, device_pixel_scale: DevicePixelScale, clip_store: &mut ClipStore, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, gpu_node_data: &mut Vec<ClipScrollNodeData>, scene_properties: &SceneProperties, )350     fn update_node(
351         &mut self,
352         node_index: ClipScrollNodeIndex,
353         state: &mut TransformUpdateState,
354         next_coordinate_system_id: &mut CoordinateSystemId,
355         device_pixel_scale: DevicePixelScale,
356         clip_store: &mut ClipStore,
357         resource_cache: &mut ResourceCache,
358         gpu_cache: &mut GpuCache,
359         gpu_node_data: &mut Vec<ClipScrollNodeData>,
360         scene_properties: &SceneProperties,
361     ) {
362         // TODO(gw): This is an ugly borrow check workaround to clone these.
363         //           Restructure this to avoid the clones!
364         let mut state = state.clone();
365         let node_children = {
366             let node = match self.nodes.get_mut(node_index.0) {
367                 Some(node) => node,
368                 None => return,
369             };
370 
371             // We set this early so that we can use it to populate the ClipChain.
372             node.node_data_index = GPUClipScrollNodeIndex(gpu_node_data.len() as u32);
373 
374             node.update(
375                 &mut state,
376                 next_coordinate_system_id,
377                 device_pixel_scale,
378                 clip_store,
379                 resource_cache,
380                 gpu_cache,
381                 scene_properties,
382                 &mut self.clip_chains,
383             );
384 
385             node.push_gpu_node_data(gpu_node_data);
386 
387             if node.children.is_empty() {
388                 return;
389             }
390 
391             node.prepare_state_for_children(&mut state);
392             node.children.clone()
393         };
394 
395         for child_node_index in node_children {
396             self.update_node(
397                 child_node_index,
398                 &mut state,
399                 next_coordinate_system_id,
400                 device_pixel_scale,
401                 clip_store,
402                 resource_cache,
403                 gpu_cache,
404                 gpu_node_data,
405                 scene_properties,
406             );
407         }
408     }
409 
build_clip_chains(&mut self, screen_rect: &DeviceIntRect)410     pub fn build_clip_chains(&mut self, screen_rect: &DeviceIntRect) {
411         for descriptor in &self.clip_chains_descriptors {
412             // A ClipChain is an optional parent (which is another ClipChain) and a list of
413             // ClipScrollNode clipping nodes. Here we start the ClipChain with a clone of the
414             // parent's node, if necessary.
415             let mut chain = match descriptor.parent {
416                 Some(index) => self.clip_chains[index.0].clone(),
417                 None => ClipChain::empty(screen_rect),
418             };
419 
420             // Now we walk through each ClipScrollNode in the vector of clip nodes and
421             // extract their ClipChain nodes to construct the final list.
422             for clip_index in &descriptor.clips {
423                 match self.nodes[clip_index.0].node_type {
424                     NodeType::Clip { clip_chain_node: Some(ref node), .. } => {
425                         chain.add_node(node.clone());
426                     }
427                     NodeType::Clip { .. } => warn!("Found uninitialized clipping ClipScrollNode."),
428                     _ => warn!("Tried to create a clip chain with non-clipping node."),
429                 };
430             }
431 
432             chain.parent_index = descriptor.parent;
433             self.clip_chains[descriptor.index.0] = chain;
434         }
435     }
436 
tick_scrolling_bounce_animations(&mut self)437     pub fn tick_scrolling_bounce_animations(&mut self) {
438         for node in &mut self.nodes {
439             node.tick_scrolling_bounce_animation()
440         }
441     }
442 
finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates)443     pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
444         for node in &mut self.nodes {
445             let external_id = match node.node_type {
446                 NodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
447                 _ => continue,
448             };
449 
450             if let Some(scrolling_state) = old_states.get(&external_id) {
451                 node.apply_old_scrolling_state(scrolling_state);
452             }
453 
454             if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {
455                 node.set_scroll_origin(&offset, clamping);
456             }
457         }
458     }
459 
add_clip_node( &mut self, index: ClipScrollNodeIndex, parent_index: ClipScrollNodeIndex, handle: ClipSourcesHandle, clip_rect: LayerRect, pipeline_id: PipelineId, ) -> ClipChainIndex460     pub fn add_clip_node(
461         &mut self,
462         index: ClipScrollNodeIndex,
463         parent_index: ClipScrollNodeIndex,
464         handle: ClipSourcesHandle,
465         clip_rect: LayerRect,
466         pipeline_id: PipelineId,
467     )  -> ClipChainIndex {
468         let clip_chain_index = self.allocate_clip_chain();
469         let node_type = NodeType::Clip { handle, clip_chain_index, clip_chain_node: None };
470         let node = ClipScrollNode::new(pipeline_id, Some(parent_index), &clip_rect, node_type);
471         self.add_node(node, index);
472         clip_chain_index
473     }
474 
add_sticky_frame( &mut self, index: ClipScrollNodeIndex, parent_index: ClipScrollNodeIndex, frame_rect: LayerRect, sticky_frame_info: StickyFrameInfo, pipeline_id: PipelineId, )475     pub fn add_sticky_frame(
476         &mut self,
477         index: ClipScrollNodeIndex,
478         parent_index: ClipScrollNodeIndex,
479         frame_rect: LayerRect,
480         sticky_frame_info: StickyFrameInfo,
481         pipeline_id: PipelineId,
482     ) {
483         let node = ClipScrollNode::new_sticky_frame(
484             parent_index,
485             frame_rect,
486             sticky_frame_info,
487             pipeline_id,
488         );
489         self.add_node(node, index);
490     }
491 
add_clip_chain_descriptor( &mut self, parent: Option<ClipChainIndex>, clips: Vec<ClipScrollNodeIndex> ) -> ClipChainIndex492     pub fn add_clip_chain_descriptor(
493         &mut self,
494         parent: Option<ClipChainIndex>,
495         clips: Vec<ClipScrollNodeIndex>
496     ) -> ClipChainIndex {
497         let index = self.allocate_clip_chain();
498         self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips });
499         index
500     }
501 
add_node(&mut self, node: ClipScrollNode, index: ClipScrollNodeIndex)502     pub fn add_node(&mut self, node: ClipScrollNode, index: ClipScrollNodeIndex) {
503         // When the parent node is None this means we are adding the root.
504         if let Some(parent_index) = node.parent {
505             self.nodes[parent_index.0].add_child(index);
506         }
507 
508         if index.0 == self.nodes.len() {
509             self.nodes.push(node);
510             return;
511         }
512 
513 
514         if let Some(empty_node) = self.nodes.get_mut(index.0) {
515             *empty_node = node;
516             return
517         }
518 
519         let length_to_reserve = index.0 + 1 - self.nodes.len();
520         self.nodes.reserve_exact(length_to_reserve);
521 
522         // We would like to use `Vec::resize` here, but the Clone trait is not supported
523         // for ClipScrollNodes. We can fix this either by splitting the clip nodes out into
524         // their own tree or when support is added for something like `Vec::resize_default`.
525         let length_to_extend = self.nodes.len() .. index.0;
526         self.nodes.extend(length_to_extend.map(|_| ClipScrollNode::empty()));
527 
528         self.nodes.push(node);
529     }
530 
discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId)531     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
532         self.pipelines_to_discard.insert(pipeline_id);
533 
534         if let Some(index) = self.currently_scrolling_node_index {
535             if self.nodes[index.0].pipeline_id == pipeline_id {
536                 self.currently_scrolling_node_index = None;
537             }
538         }
539     }
540 
print_node<T: PrintTreePrinter>( &self, index: ClipScrollNodeIndex, pt: &mut T, clip_store: &ClipStore )541     fn print_node<T: PrintTreePrinter>(
542         &self,
543         index: ClipScrollNodeIndex,
544         pt: &mut T,
545         clip_store: &ClipStore
546     ) {
547         let node = &self.nodes[index.0];
548         match node.node_type {
549             NodeType::Clip { ref handle, .. } => {
550                 pt.new_level("Clip".to_owned());
551 
552                 pt.add_item(format!("index: {:?}", index));
553                 let clips = clip_store.get(&handle).clips();
554                 pt.new_level(format!("Clip Sources [{}]", clips.len()));
555                 for source in clips {
556                     pt.add_item(format!("{:?}", source));
557                 }
558                 pt.end_level();
559             }
560             NodeType::ReferenceFrame(ref info) => {
561                 pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
562                 pt.add_item(format!("index: {:?}", index));
563             }
564             NodeType::ScrollFrame(scrolling_info) => {
565                 pt.new_level(format!("ScrollFrame"));
566                 pt.add_item(format!("index: {:?}", index));
567                 pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
568                 pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset));
569             }
570             NodeType::StickyFrame(ref sticky_frame_info) => {
571                 pt.new_level(format!("StickyFrame"));
572                 pt.add_item(format!("index: {:?}", index));
573                 pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
574             }
575             NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
576         }
577 
578         pt.add_item(format!("local_viewport_rect: {:?}", node.local_viewport_rect));
579         pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
580         pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));
581         pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id));
582 
583         for child_index in &node.children {
584             self.print_node(*child_index, pt, clip_store);
585         }
586 
587         pt.end_level();
588     }
589 
590     #[allow(dead_code)]
print(&self, clip_store: &ClipStore)591     pub fn print(&self, clip_store: &ClipStore) {
592         if !self.nodes.is_empty() {
593             let mut pt = PrintTree::new("clip_scroll tree");
594             self.print_with(clip_store, &mut pt);
595         }
596     }
597 
print_with<T: PrintTreePrinter>(&self, clip_store: &ClipStore, pt: &mut T)598     pub fn print_with<T: PrintTreePrinter>(&self, clip_store: &ClipStore, pt: &mut T) {
599         if !self.nodes.is_empty() {
600             self.print_node(self.root_reference_frame_index(), pt, clip_store);
601         }
602     }
603 
allocate_clip_chain(&mut self) -> ClipChainIndex604     pub fn allocate_clip_chain(&mut self) -> ClipChainIndex {
605         debug_assert!(!self.clip_chains.is_empty());
606         let new_clip_chain =self.clip_chains[0].clone();
607         self.clip_chains.push(new_clip_chain);
608         ClipChainIndex(self.clip_chains.len() - 1)
609     }
610 
get_clip_chain(&self, index: ClipChainIndex) -> &ClipChain611     pub fn get_clip_chain(&self, index: ClipChainIndex) -> &ClipChain {
612         &self.clip_chains[index.0]
613     }
614 }
615