mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement scroll event listener
Signed-off-by: PotatoCP <kenzieradityatirtarahardja.18@gmail.com> Co-authored-by: Asun0204 <asun0204@163.com>
This commit is contained in:
parent
7f0cebd442
commit
2d3fa482d7
5 changed files with 88 additions and 20 deletions
|
@ -12,8 +12,8 @@ use compositing_traits::{SendableFrameTree, WebViewTrait};
|
||||||
use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType};
|
use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType};
|
||||||
use embedder_traits::{
|
use embedder_traits::{
|
||||||
AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction,
|
AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction,
|
||||||
MouseButtonEvent, MouseMoveEvent, ShutdownState, TouchEvent, TouchEventResult, TouchEventType,
|
MouseButtonEvent, MouseMoveEvent, ScrollEvent as EmbedderScrollEvent, ShutdownState,
|
||||||
TouchId, ViewportDetails,
|
TouchEvent, TouchEventResult, TouchEventType, TouchId, ViewportDetails,
|
||||||
};
|
};
|
||||||
use euclid::{Box2D, Point2D, Scale, Size2D, Vector2D};
|
use euclid::{Box2D, Point2D, Scale, Size2D, Vector2D};
|
||||||
use fnv::FnvHashSet;
|
use fnv::FnvHashSet;
|
||||||
|
@ -51,9 +51,9 @@ enum ScrollZoomEvent {
|
||||||
Scroll(ScrollEvent),
|
Scroll(ScrollEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct ScrollResult {
|
pub(crate) struct ScrollResult {
|
||||||
pub pipeline_id: PipelineId,
|
pub hit_test_result: CompositorHitTestResult,
|
||||||
pub external_scroll_id: ExternalScrollId,
|
pub external_scroll_id: ExternalScrollId,
|
||||||
pub offset: LayoutVector2D,
|
pub offset: LayoutVector2D,
|
||||||
}
|
}
|
||||||
|
@ -813,8 +813,14 @@ impl WebViewRenderer {
|
||||||
combined_event.scroll_location,
|
combined_event.scroll_location,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
if let Some(scroll_result) = scroll_result {
|
if let Some(scroll_result) = scroll_result.clone() {
|
||||||
self.send_scroll_positions_to_layout_for_pipeline(scroll_result.pipeline_id);
|
self.send_scroll_positions_to_layout_for_pipeline(
|
||||||
|
scroll_result.hit_test_result.pipeline_id,
|
||||||
|
);
|
||||||
|
self.dispatch_scroll_event(
|
||||||
|
scroll_result.external_scroll_id,
|
||||||
|
scroll_result.hit_test_result,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pinch_zoom_result = match self
|
let pinch_zoom_result = match self
|
||||||
|
@ -829,8 +835,8 @@ impl WebViewRenderer {
|
||||||
|
|
||||||
/// Perform a hit test at the given [`DevicePoint`] and apply the [`ScrollLocation`]
|
/// Perform a hit test at the given [`DevicePoint`] and apply the [`ScrollLocation`]
|
||||||
/// scrolling to the applicable scroll node under that point. If a scroll was
|
/// scrolling to the applicable scroll node under that point. If a scroll was
|
||||||
/// performed, returns the [`PipelineId`] of the node scrolled, the id, and the final
|
/// performed, returns the hit test result contains [`PipelineId`] of the node
|
||||||
/// scroll delta.
|
/// scrolled, the id, and the final scroll delta.
|
||||||
fn scroll_node_at_device_point(
|
fn scroll_node_at_device_point(
|
||||||
&mut self,
|
&mut self,
|
||||||
cursor: DevicePoint,
|
cursor: DevicePoint,
|
||||||
|
@ -864,20 +870,17 @@ impl WebViewRenderer {
|
||||||
// This is needed to propagate the scroll events from a pipeline representing an iframe to
|
// This is needed to propagate the scroll events from a pipeline representing an iframe to
|
||||||
// its ancestor pipelines.
|
// its ancestor pipelines.
|
||||||
let mut previous_pipeline_id = None;
|
let mut previous_pipeline_id = None;
|
||||||
for CompositorHitTestResult {
|
for hit_test_result in hit_test_results.iter() {
|
||||||
pipeline_id,
|
let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
|
||||||
scroll_tree_node,
|
if previous_pipeline_id.replace(&hit_test_result.pipeline_id) !=
|
||||||
..
|
Some(&hit_test_result.pipeline_id)
|
||||||
} in hit_test_results.iter()
|
{
|
||||||
{
|
|
||||||
let pipeline_details = self.pipelines.get_mut(pipeline_id)?;
|
|
||||||
if previous_pipeline_id.replace(pipeline_id) != Some(pipeline_id) {
|
|
||||||
let scroll_result = pipeline_details
|
let scroll_result = pipeline_details
|
||||||
.scroll_tree
|
.scroll_tree
|
||||||
.scroll_node_or_ancestor(scroll_tree_node, scroll_location);
|
.scroll_node_or_ancestor(&hit_test_result.scroll_tree_node, scroll_location);
|
||||||
if let Some((external_scroll_id, offset)) = scroll_result {
|
if let Some((external_scroll_id, offset)) = scroll_result {
|
||||||
return Some(ScrollResult {
|
return Some(ScrollResult {
|
||||||
pipeline_id: *pipeline_id,
|
hit_test_result: hit_test_result.clone(),
|
||||||
external_scroll_id,
|
external_scroll_id,
|
||||||
offset,
|
offset,
|
||||||
});
|
});
|
||||||
|
@ -887,6 +890,22 @@ impl WebViewRenderer {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dispatch_scroll_event(
|
||||||
|
&self,
|
||||||
|
external_id: ExternalScrollId,
|
||||||
|
hit_test_result: CompositorHitTestResult,
|
||||||
|
) {
|
||||||
|
let event = InputEvent::Scroll(EmbedderScrollEvent { external_id });
|
||||||
|
let msg = EmbedderToConstellationMessage::ForwardInputEvent(
|
||||||
|
self.id,
|
||||||
|
event,
|
||||||
|
Some(hit_test_result),
|
||||||
|
);
|
||||||
|
if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
|
||||||
|
warn!("Sending scroll event to constellation failed ({:?}).", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
|
pub(crate) fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
|
||||||
Scale::new(self.viewport_zoom.get())
|
Scale::new(self.viewport_zoom.get())
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ mod from_compositor {
|
||||||
InputEvent::MouseMove(..) => target_variant!("MouseMove"),
|
InputEvent::MouseMove(..) => target_variant!("MouseMove"),
|
||||||
InputEvent::Touch(..) => target_variant!("Touch"),
|
InputEvent::Touch(..) => target_variant!("Touch"),
|
||||||
InputEvent::Wheel(..) => target_variant!("Wheel"),
|
InputEvent::Wheel(..) => target_variant!("Wheel"),
|
||||||
|
InputEvent::Scroll(..) => target_variant!("Scroll"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ use dom_struct::dom_struct;
|
||||||
use embedder_traits::{
|
use embedder_traits::{
|
||||||
AllowOrDeny, AnimationState, CompositorHitTestResult, ContextMenuResult, EditingActionEvent,
|
AllowOrDeny, AnimationState, CompositorHitTestResult, ContextMenuResult, EditingActionEvent,
|
||||||
EmbedderMsg, FocusSequenceNumber, ImeEvent, InputEvent, LoadStatus, MouseButton,
|
EmbedderMsg, FocusSequenceNumber, ImeEvent, InputEvent, LoadStatus, MouseButton,
|
||||||
MouseButtonAction, MouseButtonEvent, TouchEvent, TouchEventType, TouchId, WheelEvent,
|
MouseButtonAction, MouseButtonEvent, ScrollEvent, TouchEvent, TouchEventType, TouchId,
|
||||||
|
UntrustedNodeAddress, WheelEvent,
|
||||||
};
|
};
|
||||||
use encoding_rs::{Encoding, UTF_8};
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use euclid::default::{Point2D, Rect, Size2D};
|
use euclid::default::{Point2D, Rect, Size2D};
|
||||||
|
@ -54,7 +55,7 @@ use profile_traits::ipc as profile_ipc;
|
||||||
use profile_traits::time::TimerMetadataFrameType;
|
use profile_traits::time::TimerMetadataFrameType;
|
||||||
use regex::bytes::Regex;
|
use regex::bytes::Regex;
|
||||||
use script_bindings::interfaces::DocumentHelpers;
|
use script_bindings::interfaces::DocumentHelpers;
|
||||||
use script_layout_interface::{PendingRestyle, TrustedNodeAddress};
|
use script_layout_interface::{PendingRestyle, TrustedNodeAddress, node_id_from_scroll_id};
|
||||||
use script_traits::{ConstellationInputEvent, DocumentActivity, ProgressiveWebMetricType};
|
use script_traits::{ConstellationInputEvent, DocumentActivity, ProgressiveWebMetricType};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use servo_config::pref;
|
use servo_config::pref;
|
||||||
|
@ -2337,6 +2338,40 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub(crate) fn handle_scroll_event(&self, event: ScrollEvent, can_gc: CanGc) {
|
||||||
|
// <https://drafts.csswg.org/cssom-view/#scrolling-events>
|
||||||
|
// If target is a Document, fire an event named scroll that bubbles at target.
|
||||||
|
if event.external_id.is_root() {
|
||||||
|
let Some(document) = self
|
||||||
|
.node
|
||||||
|
.inclusive_ancestors(ShadowIncluding::No)
|
||||||
|
.filter_map(DomRoot::downcast::<Document>)
|
||||||
|
.next()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
DomRoot::upcast::<EventTarget>(document)
|
||||||
|
.fire_bubbling_event(Atom::from("scroll"), can_gc);
|
||||||
|
} else {
|
||||||
|
// Otherwise, fire an event named scroll at target.
|
||||||
|
let Some(node_id) = node_id_from_scroll_id(event.external_id.0 as usize) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let node = unsafe {
|
||||||
|
node::from_untrusted_node_address(UntrustedNodeAddress::from_id(node_id))
|
||||||
|
};
|
||||||
|
let Some(element) = node
|
||||||
|
.inclusive_ancestors(ShadowIncluding::No)
|
||||||
|
.filter_map(DomRoot::downcast::<Element>)
|
||||||
|
.next()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
DomRoot::upcast::<EventTarget>(element).fire_event(Atom::from("scroll"), can_gc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The entry point for all key processing for web content
|
/// The entry point for all key processing for web content
|
||||||
pub(crate) fn dispatch_key_event(
|
pub(crate) fn dispatch_key_event(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -1143,6 +1143,9 @@ impl ScriptThread {
|
||||||
InputEvent::EditingAction(editing_action_event) => {
|
InputEvent::EditingAction(editing_action_event) => {
|
||||||
document.handle_editing_action(editing_action_event, can_gc);
|
document.handle_editing_action(editing_action_event, can_gc);
|
||||||
},
|
},
|
||||||
|
InputEvent::Scroll(scroll_event) => {
|
||||||
|
document.handle_scroll_event(scroll_event, can_gc);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScriptThread::set_user_interacting(false);
|
ScriptThread::set_user_interacting(false);
|
||||||
|
|
|
@ -6,6 +6,7 @@ use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||||
use log::error;
|
use log::error;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use webrender_api::ExternalScrollId;
|
||||||
use webrender_api::units::DevicePoint;
|
use webrender_api::units::DevicePoint;
|
||||||
|
|
||||||
use crate::WebDriverMessageId;
|
use crate::WebDriverMessageId;
|
||||||
|
@ -21,6 +22,7 @@ pub enum InputEvent {
|
||||||
MouseMove(MouseMoveEvent),
|
MouseMove(MouseMoveEvent),
|
||||||
Touch(TouchEvent),
|
Touch(TouchEvent),
|
||||||
Wheel(WheelEvent),
|
Wheel(WheelEvent),
|
||||||
|
Scroll(ScrollEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An editing action that should be performed on a `WebView`.
|
/// An editing action that should be performed on a `WebView`.
|
||||||
|
@ -42,6 +44,7 @@ impl InputEvent {
|
||||||
InputEvent::MouseMove(event) => Some(event.point),
|
InputEvent::MouseMove(event) => Some(event.point),
|
||||||
InputEvent::Touch(event) => Some(event.point),
|
InputEvent::Touch(event) => Some(event.point),
|
||||||
InputEvent::Wheel(event) => Some(event.point),
|
InputEvent::Wheel(event) => Some(event.point),
|
||||||
|
InputEvent::Scroll(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ impl InputEvent {
|
||||||
InputEvent::MouseMove(event) => event.webdriver_id,
|
InputEvent::MouseMove(event) => event.webdriver_id,
|
||||||
InputEvent::Touch(..) => None,
|
InputEvent::Touch(..) => None,
|
||||||
InputEvent::Wheel(..) => None,
|
InputEvent::Wheel(..) => None,
|
||||||
|
InputEvent::Scroll(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +76,7 @@ impl InputEvent {
|
||||||
},
|
},
|
||||||
InputEvent::Touch(..) => {},
|
InputEvent::Touch(..) => {},
|
||||||
InputEvent::Wheel(..) => {},
|
InputEvent::Wheel(..) => {},
|
||||||
|
InputEvent::Scroll(..) => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -277,6 +282,11 @@ pub struct WheelEvent {
|
||||||
pub point: DevicePoint,
|
pub point: DevicePoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct ScrollEvent {
|
||||||
|
pub external_id: ExternalScrollId,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub enum ImeEvent {
|
pub enum ImeEvent {
|
||||||
Composition(CompositionEvent),
|
Composition(CompositionEvent),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue