servo/components/shared/script/tests/compositor.rs
Euclid Ye 03fc54e682
layout: Implement overflow scroll support for different axes (#35414)
* layout: Add AxesScrollSensitivity to enable control of scroll in axis

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout_2013: Be compatible with AxesScrollSensitivity

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout: update struct AxesScrollSensitivity to euclid::Vector2D

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* display_list: implement From<Overflow> for ScrollSensitivity

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout: simplify and reuse scroll related logic

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout_2013: simplify and reuse scroll related logic

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout, layout_2013: revert AxesScrollSensitivity to pair struct

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout: Reimport ComputedOverflow as #35103 depends on it

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout: Add AxesOverflow to replace PhysicalVec

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout: implement scroll of viewport for different axes

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* layout: explicitly handle overflow match

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

* Update components/shared/webrender/Cargo.toml

Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-02-14 08:14:57 +00:00

187 lines
6.5 KiB
Rust

/* 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 euclid::Size2D;
use webrender_api::units::LayoutVector2D;
use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
use webrender_traits::display_list::{
AxesScrollSensitivity, ScrollSensitivity, ScrollTree, ScrollTreeNodeId, ScrollableNodeInfo,
};
fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
let pipeline_id = PipelineId(0, 0);
let num_nodes = tree.nodes.len();
let parent = if num_nodes > 0 {
Some(ScrollTreeNodeId {
index: num_nodes - 1,
spatial_id: SpatialId::new(num_nodes - 1, pipeline_id),
})
} else {
None
};
tree.add_scroll_tree_node(
parent.as_ref(),
SpatialId::new(num_nodes, pipeline_id),
Some(ScrollableNodeInfo {
external_id: ExternalScrollId(num_nodes as u64, pipeline_id),
scrollable_size: Size2D::new(100.0, 100.0),
scroll_sensitivity: AxesScrollSensitivity {
x: ScrollSensitivity::ScriptAndInputEvents,
y: ScrollSensitivity::ScriptAndInputEvents,
},
offset: LayoutVector2D::zero(),
}),
)
}
#[test]
fn test_scroll_tree_simple_scroll() {
let mut scroll_tree = ScrollTree::default();
let pipeline_id = PipelineId(0, 0);
let id = add_mock_scroll_node(&mut scroll_tree);
let (scrolled_id, offset) = scroll_tree
.scroll_node_or_ancestor(
&id,
ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)),
)
.unwrap();
let expected_offset = LayoutVector2D::new(-20.0, -40.0);
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_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(&id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)))
.unwrap();
let expected_offset = LayoutVector2D::new(0.0, 0.0);
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
assert_eq!(offset, expected_offset);
assert_eq!(scroll_tree.get_node(&id).offset(), Some(expected_offset));
// Scroll offsets must be negative.
let result = scroll_tree
.scroll_node_or_ancestor(&id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)));
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 = add_mock_scroll_node(&mut scroll_tree);
let unscrollable_child_id =
scroll_tree.add_scroll_tree_node(Some(&parent_id), SpatialId::new(1, pipeline_id), None);
let (scrolled_id, offset) = scroll_tree
.scroll_node_or_ancestor(
&unscrollable_child_id,
ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)),
)
.unwrap();
let expected_offset = LayoutVector2D::new(-20.0, -40.0);
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_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_child_id,
ScrollLocation::Delta(LayoutVector2D::new(-10.0, -15.0)),
)
.unwrap();
let expected_offset = LayoutVector2D::new(-30.0, -55.0);
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_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(), None);
}
#[test]
fn test_scroll_tree_chain_when_at_extent() {
let mut scroll_tree = ScrollTree::default();
let pipeline_id = PipelineId(0, 0);
let parent_id = add_mock_scroll_node(&mut scroll_tree);
let child_id = add_mock_scroll_node(&mut scroll_tree);
let (scrolled_id, offset) = scroll_tree
.scroll_node_or_ancestor(&child_id, ScrollLocation::End)
.unwrap();
let expected_offset = LayoutVector2D::new(0.0, -100.0);
assert_eq!(scrolled_id, ExternalScrollId(1, pipeline_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_id,
ScrollLocation::Delta(LayoutVector2D::new(0.0, -10.0)),
)
.unwrap();
let expected_offset = LayoutVector2D::new(0.0, -10.0);
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_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 pipeline_id = PipelineId(0, 0);
let parent_id = add_mock_scroll_node(&mut scroll_tree);
let overflow_hidden_id = add_mock_scroll_node(&mut scroll_tree);
scroll_tree
.get_node_mut(&overflow_hidden_id)
.scroll_info
.as_mut()
.map(|info| {
info.scroll_sensitivity = AxesScrollSensitivity {
x: ScrollSensitivity::Script,
y: ScrollSensitivity::Script,
};
});
let (scrolled_id, offset) = scroll_tree
.scroll_node_or_ancestor(
&overflow_hidden_id,
ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)),
)
.unwrap();
let expected_offset = LayoutVector2D::new(-20.0, -40.0);
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_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))
);
}