summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender/src/spatial_tree.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/webrender/src/spatial_tree.rs')
-rw-r--r--gfx/wr/webrender/src/spatial_tree.rs122
1 files changed, 97 insertions, 25 deletions
diff --git a/gfx/wr/webrender/src/spatial_tree.rs b/gfx/wr/webrender/src/spatial_tree.rs
index 0aa6bb5296..94e934ca53 100644
--- a/gfx/wr/webrender/src/spatial_tree.rs
+++ b/gfx/wr/webrender/src/spatial_tree.rs
@@ -335,9 +335,11 @@ impl SceneSpatialTree {
pub fn find_scroll_root(
&self,
spatial_node_index: SpatialNodeIndex,
+ allow_sticky_frames: bool,
) -> SpatialNodeIndex {
let mut real_scroll_root = self.root_reference_frame_index;
let mut outermost_scroll_root = self.root_reference_frame_index;
+ let mut current_scroll_root_is_sticky = false;
let mut node_index = spatial_node_index;
while node_index != self.root_reference_frame_index {
@@ -354,10 +356,21 @@ impl SceneSpatialTree {
// we have encountered, as they may end up with a non-axis-aligned transform.
real_scroll_root = self.root_reference_frame_index;
outermost_scroll_root = self.root_reference_frame_index;
+ current_scroll_root_is_sticky = false;
}
}
}
- SpatialNodeType::StickyFrame(..) => {}
+ SpatialNodeType::StickyFrame(..) => {
+ // Though not a scroll frame, we optionally treat sticky frames as scroll roots
+ // to ensure they are given a separate picture cache slice.
+ if allow_sticky_frames {
+ outermost_scroll_root = node_index;
+ real_scroll_root = node_index;
+ // Set this true so that we don't select an ancestor scroll frame as the scroll root
+ // on a subsequent iteration.
+ current_scroll_root_is_sticky = true;
+ }
+ }
SpatialNodeType::ScrollFrame(ref info) => {
match info.frame_kind {
ScrollFrameKind::PipelineRoot { is_root_pipeline } => {
@@ -371,24 +384,29 @@ impl SceneSpatialTree {
// later on, even if it's not actually scrollable.
outermost_scroll_root = node_index;
- // If the scroll root has no scrollable area, we don't want to
- // consider it. This helps pages that have a nested scroll root
- // within a redundant scroll root to avoid selecting the wrong
- // reference spatial node for a picture cache.
- if info.scrollable_size.width > MIN_SCROLLABLE_AMOUNT ||
- info.scrollable_size.height > MIN_SCROLLABLE_AMOUNT {
- // Since we are skipping redundant scroll roots, we may end up
- // selecting inner scroll roots that are very small. There is
- // no performance benefit to creating a slice for these roots,
- // as they are cheap to rasterize. The size comparison is in
- // local-space, but makes for a reasonable estimate. The value
- // is arbitrary, but is generally small enough to ignore things
- // like scroll roots around text input elements.
- if info.viewport_rect.width() > MIN_SCROLL_ROOT_SIZE &&
- info.viewport_rect.height() > MIN_SCROLL_ROOT_SIZE {
- // If we've found a root that is scrollable, and a reasonable
- // size, select that as the current root for this node
- real_scroll_root = node_index;
+ // If the previously identified scroll root is sticky then we don't
+ // want to choose an ancestor scroll root, as we want the sticky item
+ // to have its own picture cache slice.
+ if !current_scroll_root_is_sticky {
+ // If the scroll root has no scrollable area, we don't want to
+ // consider it. This helps pages that have a nested scroll root
+ // within a redundant scroll root to avoid selecting the wrong
+ // reference spatial node for a picture cache.
+ if info.scrollable_size.width > MIN_SCROLLABLE_AMOUNT ||
+ info.scrollable_size.height > MIN_SCROLLABLE_AMOUNT {
+ // Since we are skipping redundant scroll roots, we may end up
+ // selecting inner scroll roots that are very small. There is
+ // no performance benefit to creating a slice for these roots,
+ // as they are cheap to rasterize. The size comparison is in
+ // local-space, but makes for a reasonable estimate. The value
+ // is arbitrary, but is generally small enough to ignore things
+ // like scroll roots around text input elements.
+ if info.viewport_rect.width() > MIN_SCROLL_ROOT_SIZE &&
+ info.viewport_rect.height() > MIN_SCROLL_ROOT_SIZE {
+ // If we've found a root that is scrollable, and a reasonable
+ // size, select that as the current root for this node
+ real_scroll_root = node_index;
+ }
}
}
}
@@ -1732,7 +1750,7 @@ fn test_find_scroll_root_simple() {
SpatialNodeUid::external(SpatialTreeItemKey::new(0, 1), PipelineId::dummy(), pid),
);
- assert_eq!(st.find_scroll_root(scroll), scroll);
+ assert_eq!(st.find_scroll_root(scroll, true), scroll);
}
/// Tests that we select the root scroll frame rather than the subframe if both are scrollable.
@@ -1781,7 +1799,7 @@ fn test_find_scroll_root_sub_scroll_frame() {
SpatialNodeUid::external(SpatialTreeItemKey::new(0, 2), PipelineId::dummy(), pid),
);
- assert_eq!(st.find_scroll_root(sub_scroll), root_scroll);
+ assert_eq!(st.find_scroll_root(sub_scroll, true), root_scroll);
}
/// Tests that we select the sub scroll frame when the root scroll frame is not scrollable.
@@ -1830,7 +1848,7 @@ fn test_find_scroll_root_not_scrollable() {
SpatialNodeUid::external(SpatialTreeItemKey::new(0, 2), PipelineId::dummy(), pid),
);
- assert_eq!(st.find_scroll_root(sub_scroll), sub_scroll);
+ assert_eq!(st.find_scroll_root(sub_scroll, true), sub_scroll);
}
/// Tests that we select the sub scroll frame when the root scroll frame is too small.
@@ -1879,7 +1897,7 @@ fn test_find_scroll_root_too_small() {
SpatialNodeUid::external(SpatialTreeItemKey::new(0, 2), PipelineId::dummy(), pid),
);
- assert_eq!(st.find_scroll_root(sub_scroll), sub_scroll);
+ assert_eq!(st.find_scroll_root(sub_scroll, true), sub_scroll);
}
/// Tests that we select the root scroll node, even if it is not scrollable,
@@ -1941,7 +1959,7 @@ fn test_find_scroll_root_perspective() {
SpatialNodeUid::external(SpatialTreeItemKey::new(0, 3), PipelineId::dummy(), pid),
);
- assert_eq!(st.find_scroll_root(sub_scroll), root_scroll);
+ assert_eq!(st.find_scroll_root(sub_scroll, true), root_scroll);
}
/// Tests that encountering a 2D scale or translation transform does not prevent
@@ -2005,7 +2023,61 @@ fn test_find_scroll_root_2d_scale() {
SpatialNodeUid::external(SpatialTreeItemKey::new(0, 3), PipelineId::dummy(), pid),
);
- assert_eq!(st.find_scroll_root(sub_scroll), sub_scroll);
+ assert_eq!(st.find_scroll_root(sub_scroll, true), sub_scroll);
+}
+
+/// Tests that a sticky spatial node is chosen as the scroll root rather than
+/// its parent scroll frame
+#[test]
+fn test_find_scroll_root_sticky() {
+ let mut st = SceneSpatialTree::new();
+ let pid = PipelineInstanceId::new(0);
+
+ let root = st.add_reference_frame(
+ st.root_reference_frame_index(),
+ TransformStyle::Flat,
+ PropertyBinding::Value(LayoutTransform::identity()),
+ ReferenceFrameKind::Transform {
+ is_2d_scale_translation: true,
+ should_snap: true,
+ paired_with_perspective: false,
+ },
+ LayoutVector2D::new(0.0, 0.0),
+ PipelineId::dummy(),
+ SpatialNodeUid::external(SpatialTreeItemKey::new(0, 0), PipelineId::dummy(), pid),
+ );
+
+ let scroll = st.add_scroll_frame(
+ root,
+ ExternalScrollId(1, PipelineId::dummy()),
+ PipelineId::dummy(),
+ &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
+ &LayoutSize::new(400.0, 800.0),
+ ScrollFrameKind::Explicit,
+ LayoutVector2D::new(0.0, 0.0),
+ APZScrollGeneration::default(),
+ HasScrollLinkedEffect::No,
+ SpatialNodeUid::external(SpatialTreeItemKey::new(0, 1), PipelineId::dummy(), pid),
+ );
+
+ let sticky = st.add_sticky_frame(
+ scroll,
+ StickyFrameInfo {
+ frame_rect: LayoutRect::from_size(LayoutSize::new(400.0, 100.0)),
+ margins: euclid::SideOffsets2D::new(Some(0.0), None, None, None),
+ vertical_offset_bounds: api::StickyOffsetBounds::new(0.0, 0.0),
+ horizontal_offset_bounds: api::StickyOffsetBounds::new(0.0, 0.0),
+ previously_applied_offset: LayoutVector2D::zero(),
+ current_offset: LayoutVector2D::zero(),
+ transform: None
+ },
+ PipelineId::dummy(),
+ SpatialTreeItemKey::new(0, 2),
+ pid,
+ );
+
+ assert_eq!(st.find_scroll_root(sticky, true), sticky);
+ assert_eq!(st.find_scroll_root(sticky, false), scroll);
}
#[test]