/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; use base::id::ScrollTreeNodeId; use compositing_traits::display_list::{ AxesScrollSensitivity, ScrollTree, ScrollType, ScrollableNodeInfo, SpatialTreeNodeInfo, }; use euclid::Size2D; use webrender_api::units::LayoutVector2D; use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation}; fn add_mock_scroll_node(tree: &mut ScrollTree) -> (ScrollTreeNodeId, ExternalScrollId) { let pipeline_id = PipelineId(0, 0); let num_nodes = tree.nodes.len(); let parent = if num_nodes > 0 { Some(ScrollTreeNodeId { index: num_nodes - 1, }) } else { None }; let external_id = ExternalScrollId(num_nodes as u64, pipeline_id); let scroll_node_id = tree.add_scroll_tree_node( parent, SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo { external_id, content_rect: Size2D::new(200.0, 200.0).into(), clip_rect: Size2D::new(100.0, 100.0).into(), scroll_sensitivity: AxesScrollSensitivity { x: ScrollType::Script | ScrollType::InputEvents, y: ScrollType::Script | ScrollType::InputEvents, }, offset: LayoutVector2D::zero(), offset_changed: Cell::new(false), }), ); (scroll_node_id, external_id) } #[test] fn test_scroll_tree_simple_scroll() { let mut scroll_tree = ScrollTree::default(); let (id, external_id) = add_mock_scroll_node(&mut scroll_tree); let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor( external_id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)), ScrollType::Script, ) .unwrap(); let expected_offset = LayoutVector2D::new(20.0, 40.0); assert_eq!(scrolled_id, external_id); assert_eq!(offset, expected_offset); assert_eq!(scroll_tree.get_node(id).offset(), Some(expected_offset)); let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor( external_id, ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)), ScrollType::Script, ) .unwrap(); let expected_offset = LayoutVector2D::new(0.0, 0.0); assert_eq!(scrolled_id, external_id); assert_eq!(offset, expected_offset); assert_eq!(scroll_tree.get_node(id).offset(), Some(expected_offset)); // Scroll offsets must be positive. let result = scroll_tree.scroll_node_or_ancestor( external_id, ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)), ScrollType::Script, ); assert!(result.is_none()); assert_eq!( scroll_tree.get_node(id).offset(), Some(LayoutVector2D::new(0.0, 0.0)) ); } #[test] fn test_scroll_tree_simple_scroll_chaining() { let mut scroll_tree = ScrollTree::default(); let pipeline_id = PipelineId(0, 0); let (parent_id, parent_external_id) = add_mock_scroll_node(&mut scroll_tree); let unscrollable_external_id = ExternalScrollId(100 as u64, pipeline_id); let unscrollable_child_id = scroll_tree.add_scroll_tree_node( Some(parent_id), SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo { external_id: unscrollable_external_id, content_rect: Size2D::new(100.0, 100.0).into(), clip_rect: Size2D::new(100.0, 100.0).into(), scroll_sensitivity: AxesScrollSensitivity { x: ScrollType::Script | ScrollType::InputEvents, y: ScrollType::Script | ScrollType::InputEvents, }, offset: LayoutVector2D::zero(), offset_changed: Cell::new(false), }), ); let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor( unscrollable_external_id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)), ScrollType::Script, ) .unwrap(); let expected_offset = LayoutVector2D::new(20.0, 40.0); assert_eq!(scrolled_id, parent_external_id); assert_eq!(offset, expected_offset); assert_eq!( scroll_tree.get_node(parent_id).offset(), Some(expected_offset) ); let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor( unscrollable_external_id, ScrollLocation::Delta(LayoutVector2D::new(10.0, 15.0)), ScrollType::Script, ) .unwrap(); let expected_offset = LayoutVector2D::new(30.0, 55.0); assert_eq!(scrolled_id, parent_external_id); assert_eq!(offset, expected_offset); assert_eq!( scroll_tree.get_node(parent_id).offset(), Some(expected_offset) ); assert_eq!( scroll_tree.get_node(unscrollable_child_id).offset(), Some(LayoutVector2D::zero()) ); } #[test] fn test_scroll_tree_chain_when_at_extent() { let mut scroll_tree = ScrollTree::default(); let (parent_id, parent_external_id) = add_mock_scroll_node(&mut scroll_tree); let (child_id, child_external_id) = add_mock_scroll_node(&mut scroll_tree); let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor(child_external_id, ScrollLocation::End, ScrollType::Script) .unwrap(); let expected_offset = LayoutVector2D::new(0.0, 100.0); assert_eq!(scrolled_id, child_external_id); assert_eq!(offset, expected_offset); assert_eq!( scroll_tree.get_node(child_id).offset(), Some(expected_offset) ); // The parent will have scrolled because the child is already at the extent // of its scroll area in the y axis. let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor( child_external_id, ScrollLocation::Delta(LayoutVector2D::new(0.0, 10.0)), ScrollType::Script, ) .unwrap(); let expected_offset = LayoutVector2D::new(0.0, 10.0); assert_eq!(scrolled_id, parent_external_id); assert_eq!(offset, expected_offset); assert_eq!( scroll_tree.get_node(parent_id).offset(), Some(expected_offset) ); } #[test] fn test_scroll_tree_chain_through_overflow_hidden() { let mut scroll_tree = ScrollTree::default(); // Create a tree with a scrollable leaf, but make its `scroll_sensitivity` // reflect `overflow: hidden` ie not responsive to non-script scroll events. let (parent_id, parent_external_id) = add_mock_scroll_node(&mut scroll_tree); let (overflow_hidden_id, overflow_hidden_external_id) = add_mock_scroll_node(&mut scroll_tree); let node = scroll_tree.get_node_mut(overflow_hidden_id); if let SpatialTreeNodeInfo::Scroll(ref mut scroll_node_info) = node.info { scroll_node_info.scroll_sensitivity = AxesScrollSensitivity { x: ScrollType::Script, y: ScrollType::Script, }; } let (scrolled_id, offset) = scroll_tree .scroll_node_or_ancestor( overflow_hidden_external_id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)), ScrollType::InputEvents, ) .unwrap(); let expected_offset = LayoutVector2D::new(20.0, 40.0); assert_eq!(scrolled_id, parent_external_id); assert_eq!(offset, expected_offset); assert_eq!( scroll_tree.get_node(parent_id).offset(), Some(expected_offset) ); assert_eq!( scroll_tree.get_node(overflow_hidden_id).offset(), Some(LayoutVector2D::new(0.0, 0.0)) ); }