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>
This commit is contained in:
Euclid Ye 2025-02-14 16:14:57 +08:00 committed by GitHub
parent 9b3e23633d
commit 03fc54e682
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 111 additions and 63 deletions

1
Cargo.lock generated
View file

@ -8611,6 +8611,7 @@ dependencies = [
"serde", "serde",
"servo-media", "servo-media",
"servo_geometry", "servo_geometry",
"style",
"surfman", "surfman",
"webrender_api", "webrender_api",
] ]

View file

@ -51,7 +51,7 @@ use webrender_api::{
FilterOp, GlyphInstance, ImageRendering, LineStyle, NinePatchBorder, NinePatchBorderSource, FilterOp, GlyphInstance, ImageRendering, LineStyle, NinePatchBorder, NinePatchBorderSource,
NormalBorder, PropertyBinding, StickyOffsetBounds, NormalBorder, PropertyBinding, StickyOffsetBounds,
}; };
use webrender_traits::display_list::ScrollSensitivity; use webrender_traits::display_list::AxesScrollSensitivity;
use super::StackingContextId; use super::StackingContextId;
use crate::block::BlockFlow; use crate::block::BlockFlow;
@ -2535,14 +2535,6 @@ impl BlockFlow {
return; return;
} }
let sensitivity = if StyleOverflow::Hidden == self.fragment.style.get_box().overflow_x &&
StyleOverflow::Hidden == self.fragment.style.get_box().overflow_y
{
ScrollSensitivity::Script
} else {
ScrollSensitivity::ScriptAndInputEvents
};
let border_widths = self let border_widths = self
.fragment .fragment
.style .style
@ -2572,7 +2564,13 @@ impl BlockFlow {
parent_index: self.clipping_and_scrolling().scrolling, parent_index: self.clipping_and_scrolling().scrolling,
clip, clip,
content_rect: Rect::new(content_box.origin, content_size).to_layout(), content_rect: Rect::new(content_box.origin, content_size).to_layout(),
node_type: ClipScrollNodeType::ScrollFrame(sensitivity, external_id), node_type: ClipScrollNodeType::ScrollFrame(
AxesScrollSensitivity {
x: self.fragment.style.get_box().overflow_x.into(),
y: self.fragment.style.get_box().overflow_y.into(),
},
external_id,
),
scroll_node_id: None, scroll_node_id: None,
clip_chain_id: None, clip_chain_id: None,
}); });

View file

@ -32,7 +32,7 @@ use webrender_api::{
FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags, Shadow, FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags, Shadow,
SpatialId, StickyOffsetBounds, TransformStyle, SpatialId, StickyOffsetBounds, TransformStyle,
}; };
use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId}; use webrender_traits::display_list::{AxesScrollSensitivity, ScrollTreeNodeId};
use super::StackingContextId; use super::StackingContextId;
@ -353,7 +353,7 @@ pub enum ClipType {
#[derive(Clone, Debug, PartialEq, Serialize)] #[derive(Clone, Debug, PartialEq, Serialize)]
pub enum ClipScrollNodeType { pub enum ClipScrollNodeType {
Placeholder, Placeholder,
ScrollFrame(ScrollSensitivity, ExternalScrollId), ScrollFrame(AxesScrollSensitivity, ExternalScrollId),
StickyFrame(StickyFrameData), StickyFrame(StickyFrameData),
Clip(ClipType), Clip(ClipType),
} }

View file

@ -17,7 +17,8 @@ use webrender_api::{
ReferenceFrameKind, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, ReferenceFrameKind, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey,
}; };
use webrender_traits::display_list::{ use webrender_traits::display_list::{
CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo, AxesScrollSensitivity, CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId,
ScrollableNodeInfo,
}; };
use crate::display_list::items::{ use crate::display_list::items::{
@ -167,7 +168,10 @@ impl DisplayList {
content_size, content_size,
webrender_pipeline, webrender_pipeline,
epoch, epoch,
ScrollSensitivity::ScriptAndInputEvents, AxesScrollSensitivity {
x: ScrollSensitivity::ScriptAndInputEvents,
y: ScrollSensitivity::ScriptAndInputEvents,
},
), ),
); );

View file

@ -36,7 +36,7 @@ use webrender_api::{
ImageRendering, NinePatchBorder, NinePatchBorderSource, ImageRendering, NinePatchBorder, NinePatchBorderSource,
}; };
use webrender_traits::display_list::{ use webrender_traits::display_list::{
CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId, AxesScrollSensitivity, CompositorDisplayListInfo, ScrollTreeNodeId,
}; };
use wr::units::LayoutVector2D; use wr::units::LayoutVector2D;
@ -101,7 +101,7 @@ impl DisplayList {
content_size: units::LayoutSize, content_size: units::LayoutSize,
pipeline_id: wr::PipelineId, pipeline_id: wr::PipelineId,
epoch: wr::Epoch, epoch: wr::Epoch,
root_scroll_sensitivity: ScrollSensitivity, root_scroll_sensitivity: AxesScrollSensitivity,
) -> Self { ) -> Self {
Self { Self {
wr: wr::DisplayListBuilder::new(pipeline_id), wr: wr::DisplayListBuilder::new(pipeline_id),

View file

@ -26,7 +26,7 @@ use style::values::specified::box_::DisplayOutside;
use style::Zero; use style::Zero;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D}; use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::{self as wr, BorderRadius}; use webrender_api::{self as wr, BorderRadius};
use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo}; use webrender_traits::display_list::{AxesScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo};
use wr::units::{LayoutPixel, LayoutSize}; use wr::units::{LayoutPixel, LayoutSize};
use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds}; use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds};
@ -38,8 +38,8 @@ use crate::fragment_tree::{
BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree, BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree,
PositioningFragment, SpecificLayoutInfo, PositioningFragment, SpecificLayoutInfo,
}; };
use crate::geom::{AuOrAuto, PhysicalRect, PhysicalSides, PhysicalVec}; use crate::geom::{AuOrAuto, PhysicalRect, PhysicalSides};
use crate::style_ext::ComputedValuesExt; use crate::style_ext::{AxesOverflow, ComputedValuesExt};
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct ContainingBlock { pub(crate) struct ContainingBlock {
@ -212,7 +212,7 @@ impl DisplayList {
external_id: wr::ExternalScrollId, external_id: wr::ExternalScrollId,
content_rect: LayoutRect, content_rect: LayoutRect,
clip_rect: LayoutRect, clip_rect: LayoutRect,
scroll_sensitivity: ScrollSensitivity, scroll_sensitivity: AxesScrollSensitivity,
) -> ScrollTreeNodeId { ) -> ScrollTreeNodeId {
let spatial_tree_item_key = self.get_next_spatial_tree_item_key(); let spatial_tree_item_key = self.get_next_spatial_tree_item_key();
@ -1373,7 +1373,7 @@ impl BoxFragment {
} }
// TODO: merge this function with style.effective_overflow() // TODO: merge this function with style.effective_overflow()
fn used_overflow(&self) -> PhysicalVec<ComputedOverflow> { fn used_overflow(&self) -> AxesOverflow {
let mut overflow = self.style.effective_overflow(); let mut overflow = self.style.effective_overflow();
let is_replaced_element = self.base.flags.contains(FragmentFlags::IS_REPLACED); let is_replaced_element = self.base.flags.contains(FragmentFlags::IS_REPLACED);
@ -1483,12 +1483,12 @@ impl BoxFragment {
display_list.wr.pipeline_id, display_list.wr.pipeline_id,
); );
let sensitivity = let overflow = self.style.effective_overflow();
if ComputedOverflow::Hidden == overflow.x && ComputedOverflow::Hidden == overflow.y {
ScrollSensitivity::Script let sensitivity = AxesScrollSensitivity {
} else { x: overflow.x.into(),
ScrollSensitivity::ScriptAndInputEvents y: overflow.y.into(),
}; };
let content_rect = self.scrollable_overflow().to_webrender(); let content_rect = self.scrollable_overflow().to_webrender();

View file

@ -13,7 +13,7 @@ use style::dom::OpaqueNode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::Overflow; use style::values::computed::Overflow;
use style_traits::CSSPixel; use style_traits::CSSPixel;
use webrender_traits::display_list::ScrollSensitivity; use webrender_traits::display_list::AxesScrollSensitivity;
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
use crate::context::LayoutContext; use crate::context::LayoutContext;
@ -40,8 +40,8 @@ pub struct BoxTree {
/// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds> /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
canvas_background: CanvasBackground, canvas_background: CanvasBackground,
/// Whether or not the root element should be sensitive to scrolling input events. /// Whether or not the viewport should be sensitive to scrolling input events in two axes
sensitive_to_scroll_input: bool, scroll_sensitivity: AxesScrollSensitivity,
} }
impl BoxTree { impl BoxTree {
@ -54,7 +54,7 @@ impl BoxTree {
// Zero box for `:root { display: none }`, one for the root element otherwise. // Zero box for `:root { display: none }`, one for the root element otherwise.
assert!(boxes.len() <= 1); assert!(boxes.len() <= 1);
// From https://drafts.csswg.org/css-overflow/#propdef-overflow: // From https://drafts.csswg.org/css-overflow/#overflow-propagation:
// > UAs must apply the overflow-* values set on the root element to the viewport when the // > UAs must apply the overflow-* values set on the root element to the viewport when the
// > root elements display value is not none. However, when the root element is an [HTML] // > root elements display value is not none. However, when the root element is an [HTML]
// > html element (including XML syntax for HTML) whose overflow value is visible (in both // > html element (including XML syntax for HTML) whose overflow value is visible (in both
@ -62,12 +62,13 @@ impl BoxTree {
// > none, user agents must instead apply the overflow-* values of the first such child // > none, user agents must instead apply the overflow-* values of the first such child
// > element to the viewport. The element from which the value is propagated must then have a // > element to the viewport. The element from which the value is propagated must then have a
// > used overflow value of visible. // > used overflow value of visible.
//
// TODO: This should handle when different overflow is set multiple axes, which requires the
// compositor scroll tree to allow setting a value per axis.
let root_style = root_element.style(context); let root_style = root_element.style(context);
let mut root_overflow = root_style.effective_overflow().y; let root_overflow = root_style.effective_overflow();
if root_overflow == Overflow::Visible && !root_style.get_box().display.is_none() { let mut viewport_overflow = root_overflow;
if root_overflow.x == Overflow::Visible &&
root_overflow.y == Overflow::Visible &&
!root_style.get_box().display.is_none()
{
for child in iter_child_nodes(root_element) { for child in iter_child_nodes(root_element) {
if !child if !child
.to_threadsafe() .to_threadsafe()
@ -79,7 +80,7 @@ impl BoxTree {
let style = child.style(context); let style = child.style(context);
if !style.get_box().display.is_none() { if !style.get_box().display.is_none() {
root_overflow = style.effective_overflow().y; viewport_overflow = style.effective_overflow();
break; break;
} }
} }
@ -93,7 +94,10 @@ impl BoxTree {
contains_floats, contains_floats,
}, },
canvas_background: CanvasBackground::for_root_element(context, root_element), canvas_background: CanvasBackground::for_root_element(context, root_element),
sensitive_to_scroll_input: root_overflow != Overflow::Hidden, scroll_sensitivity: AxesScrollSensitivity {
x: viewport_overflow.x.to_scrollable().into(),
y: viewport_overflow.y.to_scrollable().into(),
},
} }
} }
@ -392,18 +396,12 @@ impl BoxTree {
acc.union(&child_overflow) acc.union(&child_overflow)
}); });
let root_scroll_sensitivity = if self.sensitive_to_scroll_input {
ScrollSensitivity::ScriptAndInputEvents
} else {
ScrollSensitivity::Script
};
FragmentTree { FragmentTree {
root_fragments, root_fragments,
scrollable_overflow, scrollable_overflow,
initial_containing_block: physical_containing_block, initial_containing_block: physical_containing_block,
canvas_background: self.canvas_background.clone(), canvas_background: self.canvas_background.clone(),
root_scroll_sensitivity, root_scroll_sensitivity: self.scroll_sensitivity,
} }
} }
} }

View file

@ -9,7 +9,7 @@ use fxhash::FxHashSet;
use style::animation::AnimationSetKey; use style::animation::AnimationSetKey;
use style::dom::OpaqueNode; use style::dom::OpaqueNode;
use webrender_api::units; use webrender_api::units;
use webrender_traits::display_list::ScrollSensitivity; use webrender_traits::display_list::AxesScrollSensitivity;
use super::{ContainingBlockManager, Fragment, Tag}; use super::{ContainingBlockManager, Fragment, Tag};
use crate::display_list::StackingContext; use crate::display_list::StackingContext;
@ -38,7 +38,7 @@ pub struct FragmentTree {
pub(crate) canvas_background: CanvasBackground, pub(crate) canvas_background: CanvasBackground,
/// Whether or not the root element is sensitive to scroll input events. /// Whether or not the root element is sensitive to scroll input events.
pub root_scroll_sensitivity: ScrollSensitivity, pub root_scroll_sensitivity: AxesScrollSensitivity,
} }
impl FragmentTree { impl FragmentTree {

View file

@ -30,8 +30,8 @@ use webrender_api as wr;
use crate::dom_traversal::Contents; use crate::dom_traversal::Contents;
use crate::fragment_tree::FragmentFlags; use crate::fragment_tree::FragmentFlags;
use crate::geom::{ use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize, AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize, Size,
PhysicalVec, Size, Sizes, Sizes,
}; };
use crate::table::TableLayoutStyle; use crate::table::TableLayoutStyle;
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -52,6 +52,11 @@ pub(crate) enum DisplayGeneratingBox {
/// <https://drafts.csswg.org/css-display-3/#layout-specific-display> /// <https://drafts.csswg.org/css-display-3/#layout-specific-display>
LayoutInternal(DisplayLayoutInternal), LayoutInternal(DisplayLayoutInternal),
} }
#[derive(Clone, Copy, Debug)]
pub struct AxesOverflow {
pub x: Overflow,
pub y: Overflow,
}
impl DisplayGeneratingBox { impl DisplayGeneratingBox {
pub(crate) fn display_inside(&self) -> DisplayInside { pub(crate) fn display_inside(&self) -> DisplayInside {
@ -301,7 +306,7 @@ pub(crate) trait ComputedValuesExt {
) -> LogicalSides<LengthPercentageOrAuto<'_>>; ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool; fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32; fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
fn effective_overflow(&self) -> PhysicalVec<Overflow>; fn effective_overflow(&self) -> AxesOverflow;
fn establishes_block_formatting_context(&self) -> bool; fn establishes_block_formatting_context(&self) -> bool;
fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool; fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
fn establishes_scroll_container(&self) -> bool; fn establishes_scroll_container(&self) -> bool;
@ -517,7 +522,7 @@ impl ComputedValuesExt for ComputedValues {
/// Get the effective overflow of this box. The property only applies to block containers, /// Get the effective overflow of this box. The property only applies to block containers,
/// flex containers, and grid containers. And some box types only accept a few values. /// flex containers, and grid containers. And some box types only accept a few values.
/// <https://www.w3.org/TR/css-overflow-3/#overflow-control> /// <https://www.w3.org/TR/css-overflow-3/#overflow-control>
fn effective_overflow(&self) -> PhysicalVec<Overflow> { fn effective_overflow(&self) -> AxesOverflow {
let style_box = self.get_box(); let style_box = self.get_box();
let overflow_x = style_box.overflow_x; let overflow_x = style_box.overflow_x;
let overflow_y = style_box.overflow_y; let overflow_y = style_box.overflow_y;
@ -547,9 +552,15 @@ impl ComputedValuesExt for ComputedValues {
_ => false, _ => false,
}; };
if ignores_overflow { if ignores_overflow {
PhysicalVec::new(Overflow::Visible, Overflow::Visible) AxesOverflow {
x: Overflow::Visible,
y: Overflow::Visible,
}
} else { } else {
PhysicalVec::new(overflow_x, overflow_y) AxesOverflow {
x: overflow_x,
y: overflow_y,
}
} }
} }
@ -583,6 +594,8 @@ impl ComputedValuesExt for ComputedValues {
/// Whether or not the `overflow` value of this style establishes a scroll container. /// Whether or not the `overflow` value of this style establishes a scroll container.
fn establishes_scroll_container(&self) -> bool { fn establishes_scroll_container(&self) -> bool {
// Checking one axis suffices, because the computed value ensures that
// either both axes are scrollable, or none is scrollable.
self.effective_overflow().x.is_scrollable() self.effective_overflow().x.is_scrollable()
} }

View file

@ -6,7 +6,7 @@ use euclid::Size2D;
use webrender_api::units::LayoutVector2D; use webrender_api::units::LayoutVector2D;
use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, SpatialId}; use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
use webrender_traits::display_list::{ use webrender_traits::display_list::{
ScrollSensitivity, ScrollTree, ScrollTreeNodeId, ScrollableNodeInfo, AxesScrollSensitivity, ScrollSensitivity, ScrollTree, ScrollTreeNodeId, ScrollableNodeInfo,
}; };
fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId { fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
@ -27,7 +27,10 @@ fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
Some(ScrollableNodeInfo { Some(ScrollableNodeInfo {
external_id: ExternalScrollId(num_nodes as u64, pipeline_id), external_id: ExternalScrollId(num_nodes as u64, pipeline_id),
scrollable_size: Size2D::new(100.0, 100.0), scrollable_size: Size2D::new(100.0, 100.0),
scroll_sensitivity: ScrollSensitivity::ScriptAndInputEvents, scroll_sensitivity: AxesScrollSensitivity {
x: ScrollSensitivity::ScriptAndInputEvents,
y: ScrollSensitivity::ScriptAndInputEvents,
},
offset: LayoutVector2D::zero(), offset: LayoutVector2D::zero(),
}), }),
) )
@ -158,7 +161,10 @@ fn test_scroll_tree_chain_through_overflow_hidden() {
.scroll_info .scroll_info
.as_mut() .as_mut()
.map(|info| { .map(|info| {
info.scroll_sensitivity = ScrollSensitivity::Script; info.scroll_sensitivity = AxesScrollSensitivity {
x: ScrollSensitivity::Script,
y: ScrollSensitivity::Script,
};
}); });
let (scrolled_id, offset) = scroll_tree let (scrolled_id, offset) = scroll_tree

View file

@ -26,4 +26,5 @@ webrender_api = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
servo_geometry = { path = "../../geometry" } servo_geometry = { path = "../../geometry" }
servo-media = { workspace = true } servo-media = { workspace = true }
style = { workspace = true }
surfman = { workspace = true, features = ["sm-x11"] } surfman = { workspace = true, features = ["sm-x11"] }

View file

@ -6,17 +6,38 @@
use embedder_traits::Cursor; use embedder_traits::Cursor;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use style::values::specified::Overflow;
use webrender_api::units::{LayoutSize, LayoutVector2D}; use webrender_api::units::{LayoutSize, LayoutVector2D};
use webrender_api::{Epoch, ExternalScrollId, PipelineId, ScrollLocation, SpatialId}; use webrender_api::{Epoch, ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
/// The scroll sensitivity of a scroll node ie whether it can be scrolled due to input event and /// The scroll sensitivity of a scroll node in a particular axis ie whether it can be scrolled due to
/// script events or only script events. /// input events and script events or only script events.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum ScrollSensitivity { pub enum ScrollSensitivity {
/// This node can be scrolled by input and script events. /// This node can be scrolled by input and script events.
ScriptAndInputEvents, ScriptAndInputEvents,
/// This node can only be scrolled by script events. /// This node can only be scrolled by script events.
Script, Script,
/// This node cannot be scrolled.
None,
}
/// Convert [Overflow] to [ScrollSensitivity].
impl From<Overflow> for ScrollSensitivity {
fn from(overflow: Overflow) -> Self {
match overflow {
Overflow::Hidden => ScrollSensitivity::Script,
Overflow::Scroll | Overflow::Auto => ScrollSensitivity::ScriptAndInputEvents,
Overflow::Visible | Overflow::Clip => ScrollSensitivity::None,
}
}
}
/// The [ScrollSensitivity] of particular node in the vertical and horizontal axes.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct AxesScrollSensitivity {
pub x: ScrollSensitivity,
pub y: ScrollSensitivity,
} }
/// Information that Servo keeps alongside WebRender display items /// Information that Servo keeps alongside WebRender display items
@ -57,7 +78,7 @@ pub struct ScrollableNodeInfo {
pub scrollable_size: LayoutSize, pub scrollable_size: LayoutSize,
/// Whether this `ScrollableNode` is sensitive to input events. /// Whether this `ScrollableNode` is sensitive to input events.
pub scroll_sensitivity: ScrollSensitivity, pub scroll_sensitivity: AxesScrollSensitivity,
/// The current offset of this scroll node. /// The current offset of this scroll node.
pub offset: LayoutVector2D, pub offset: LayoutVector2D,
@ -119,7 +140,9 @@ impl ScrollTreeNode {
None => return None, None => return None,
}; };
if info.scroll_sensitivity != ScrollSensitivity::ScriptAndInputEvents { if info.scroll_sensitivity.x != ScrollSensitivity::ScriptAndInputEvents &&
info.scroll_sensitivity.y != ScrollSensitivity::ScriptAndInputEvents
{
return None; return None;
} }
@ -150,11 +173,15 @@ impl ScrollTreeNode {
let scrollable_height = info.scrollable_size.height; let scrollable_height = info.scrollable_size.height;
let original_layer_scroll_offset = info.offset; let original_layer_scroll_offset = info.offset;
if scrollable_width > 0. { if scrollable_width > 0. &&
info.scroll_sensitivity.x == ScrollSensitivity::ScriptAndInputEvents
{
info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width); info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width);
} }
if scrollable_height > 0. { if scrollable_height > 0. &&
info.scroll_sensitivity.y == ScrollSensitivity::ScriptAndInputEvents
{
info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height); info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height);
} }
@ -288,7 +315,7 @@ impl CompositorDisplayListInfo {
content_size: LayoutSize, content_size: LayoutSize,
pipeline_id: PipelineId, pipeline_id: PipelineId,
epoch: Epoch, epoch: Epoch,
root_scroll_sensitivity: ScrollSensitivity, root_scroll_sensitivity: AxesScrollSensitivity,
) -> Self { ) -> Self {
let mut scroll_tree = ScrollTree::default(); let mut scroll_tree = ScrollTree::default();
let root_reference_frame_id = scroll_tree.add_scroll_tree_node( let root_reference_frame_id = scroll_tree.add_scroll_tree_node(