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