implement Touchevent prevent default behavior (#35031)

* implement Touchevent prevent default behavior

* The status change logic of the `TouchHandler` is changed.
> The `WaitingForScript` state is canceled. TouchAction can be identified
  based on the current touch type and numbers if touch points.
* Sends current event to script thread along with recognized `TouchAction`.
> After dispatch event, script thread sends a `TouchEventProcess(EventResult)`
  message to main thread. If the event is set to `DefaultAllowed`, the
  corresponding `TouchAction` information is added.
* After receiving `DefaultAllowed(TouchAction)` message, main thread executes corresponding action.
> `DefaultPrevented(TouchEventType)` is received. Use `prevent_click` to mark
  that the default `Click` is blocked, and `prevent_move` to mark that the
  default `Scroll` and `Zoom` are blocked. In this way, all TouchActions
  implement preventDefault.
Signed-off-by: Bi Fuguo <1782765876@qq.com>

* fix some suggestions

* support preventDefault fling
* move `TouchAction` to share touch directory
* check preventDefault everytime when touch
* fix zoom ineffective

Signed-off-by: Bi Fuguo <1782765876@qq.com>

* fix some suggestions

rename on_event_processed to  on_touch_event_processed
clear unused features

Signed-off-by: Bi Fuguo <1782765876@qq.com>

* Optimizes pan performance by continuously sliding without waiting for the eventhandler.

Signed-off-by: kongbai1996 <1782765876@qq.com>

* resolve conflict

Signed-off-by: kongbai1996 <1782765876@qq.com>

---------

Signed-off-by: Bi Fuguo <1782765876@qq.com>
Signed-off-by: kongbai1996 <1782765876@qq.com>
This commit is contained in:
Bi Fuguo 2025-02-17 18:50:04 +08:00 committed by GitHub
parent 6dce329acc
commit 3fe42a0b5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 273 additions and 255 deletions

1
Cargo.lock generated
View file

@ -1853,6 +1853,7 @@ dependencies = [
"base", "base",
"cfg-if", "cfg-if",
"crossbeam-channel", "crossbeam-channel",
"euclid",
"http 1.2.0", "http 1.2.0",
"hyper_serde", "hyper_serde",
"ipc-channel", "ipc-channel",

View file

@ -21,7 +21,7 @@ use compositing_traits::{
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use embedder_traits::{ use embedder_traits::{
Cursor, InputEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, Cursor, InputEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent,
TouchEvent, TouchEventAction, TouchId, TouchAction, TouchEvent, TouchEventType, TouchId,
}; };
use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D}; use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D};
use fnv::{FnvHashMap, FnvHashSet}; use fnv::{FnvHashMap, FnvHashSet};
@ -33,8 +33,8 @@ use pixels::{CorsStatus, Image, PixelFormat};
use profile_traits::time::{self as profile_time, ProfilerCategory}; use profile_traits::time::{self as profile_time, ProfilerCategory};
use profile_traits::time_profile; use profile_traits::time_profile;
use script_traits::{ use script_traits::{
AnimationState, AnimationTickType, ScriptThreadMessage, ScrollState, WindowSizeData, AnimationState, AnimationTickType, EventResult, ScriptThreadMessage, ScrollState,
WindowSizeType, WindowSizeData, WindowSizeType,
}; };
use servo_geometry::DeviceIndependentPixel; use servo_geometry::DeviceIndependentPixel;
use style_traits::{CSSPixel, PinchZoomFactor}; use style_traits::{CSSPixel, PinchZoomFactor};
@ -56,7 +56,7 @@ use webrender_traits::{
CompositorHitTestResult, CrossProcessCompositorMessage, ImageUpdate, UntrustedNodeAddress, CompositorHitTestResult, CrossProcessCompositorMessage, ImageUpdate, UntrustedNodeAddress,
}; };
use crate::touch::{TouchAction, TouchHandler}; use crate::touch::TouchHandler;
use crate::webview::{UnknownWebView, WebView, WebViewAlreadyExists, WebViewManager}; use crate::webview::{UnknownWebView, WebView, WebViewAlreadyExists, WebViewManager};
use crate::windowing::{self, EmbedderCoordinates, WebRenderDebugOption, WindowMethods}; use crate::windowing::{self, EmbedderCoordinates, WebRenderDebugOption, WindowMethods};
use crate::InitialCompositorState; use crate::InitialCompositorState;
@ -501,7 +501,7 @@ impl IOCompositor {
}, },
CompositorMsg::TouchEventProcessed(result) => { CompositorMsg::TouchEventProcessed(result) => {
self.touch_handler.on_event_processed(result); self.on_touch_event_processed(result);
}, },
CompositorMsg::CreatePng(page_rect, reply) => { CompositorMsg::CreatePng(page_rect, reply) => {
@ -1338,13 +1338,28 @@ impl IOCompositor {
InputEvent::MouseButton(event) => { InputEvent::MouseButton(event) => {
match event.action { match event.action {
MouseButtonAction::Click => {}, MouseButtonAction::Click => {},
MouseButtonAction::Down => self.on_touch_down(TouchId(0), event.point), MouseButtonAction::Down => self.on_touch_down(TouchEvent {
MouseButtonAction::Up => self.on_touch_up(TouchId(0), event.point), event_type: TouchEventType::Down,
id: TouchId(0),
point: event.point,
action: TouchAction::NoAction,
}),
MouseButtonAction::Up => self.on_touch_up(TouchEvent {
event_type: TouchEventType::Up,
id: TouchId(0),
point: event.point,
action: TouchAction::NoAction,
}),
} }
return; return;
}, },
InputEvent::MouseMove(event) => { InputEvent::MouseMove(event) => {
self.on_touch_move(TouchId(0), event.point); self.on_touch_move(TouchEvent {
event_type: TouchEventType::Move,
id: TouchId(0),
point: event.point,
action: TouchAction::NoAction,
});
return; return;
}, },
_ => {}, _ => {},
@ -1422,26 +1437,25 @@ impl IOCompositor {
return; return;
} }
match event.action { match event.event_type {
TouchEventAction::Down => self.on_touch_down(event.id, event.point), TouchEventType::Down => self.on_touch_down(event),
TouchEventAction::Move => self.on_touch_move(event.id, event.point), TouchEventType::Move => self.on_touch_move(event),
TouchEventAction::Up => self.on_touch_up(event.id, event.point), TouchEventType::Up => self.on_touch_up(event),
TouchEventAction::Cancel => self.on_touch_cancel(event.id, event.point), TouchEventType::Cancel => self.on_touch_cancel(event),
} }
} }
fn on_touch_down(&mut self, id: TouchId, point: DevicePoint) { fn on_touch_down(&mut self, event: TouchEvent) {
self.touch_handler.on_touch_down(id, point); self.touch_handler.on_touch_down(event.id, event.point);
self.send_touch_event(TouchEvent { self.send_touch_event(event);
action: TouchEventAction::Down,
id,
point,
})
} }
fn on_touch_move(&mut self, id: TouchId, point: DevicePoint) { fn on_touch_move(&mut self, mut event: TouchEvent) {
match self.touch_handler.on_touch_move(id, point) { let action: TouchAction = self.touch_handler.on_touch_move(event.id, event.point);
TouchAction::Scroll(delta) => self.on_scroll_window_event( if TouchAction::NoAction != action {
if !self.touch_handler.prevent_move {
match action {
TouchAction::Scroll(delta, point) => self.on_scroll_window_event(
ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())), ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())),
point.cast(), point.cast(),
), ),
@ -1455,42 +1469,81 @@ impl IOCompositor {
.push(ScrollZoomEvent::PinchZoom(magnification)); .push(ScrollZoomEvent::PinchZoom(magnification));
self.pending_scroll_zoom_events self.pending_scroll_zoom_events
.push(ScrollZoomEvent::Scroll(ScrollEvent { .push(ScrollZoomEvent::Scroll(ScrollEvent {
scroll_location: ScrollLocation::Delta(LayoutVector2D::from_untyped( scroll_location: ScrollLocation::Delta(
scroll_delta.to_untyped(), LayoutVector2D::from_untyped(scroll_delta.to_untyped()),
)), ),
cursor, cursor,
event_count: 1, event_count: 1,
})); }));
}, },
TouchAction::DispatchEvent => self.send_touch_event(TouchEvent {
action: TouchEventAction::Move,
id,
point,
}),
_ => {}, _ => {},
} }
} else {
event.action = action;
}
self.send_touch_event(event);
}
} }
fn on_touch_up(&mut self, id: TouchId, point: DevicePoint) { fn on_touch_up(&mut self, mut event: TouchEvent) {
self.send_touch_event(TouchEvent { let action = self.touch_handler.on_touch_up(event.id, event.point);
action: TouchEventAction::Up, event.action = action;
id, self.send_touch_event(event);
point, }
});
if let TouchAction::Click = self.touch_handler.on_touch_up(id, point) { fn on_touch_cancel(&mut self, event: TouchEvent) {
// Send the event to script.
self.touch_handler.on_touch_cancel(event.id, event.point);
self.send_touch_event(event)
}
fn on_touch_event_processed(&mut self, result: EventResult) {
match result {
EventResult::DefaultPrevented(event_type) => {
match event_type {
TouchEventType::Down | TouchEventType::Move => {
self.touch_handler.prevent_move = true;
},
_ => {},
}
self.touch_handler.prevent_click = true;
},
EventResult::DefaultAllowed(action) => {
self.touch_handler.prevent_move = false;
match action {
TouchAction::Click(point) => {
if !self.touch_handler.prevent_click {
self.simulate_mouse_click(point); self.simulate_mouse_click(point);
} }
} },
TouchAction::Flinging(velocity, point) => {
self.touch_handler.on_fling(velocity, point);
},
TouchAction::Scroll(delta, point) => self.on_scroll_window_event(
ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())),
point.cast(),
),
TouchAction::Zoom(magnification, scroll_delta) => {
let cursor = Point2D::new(-1, -1); // Make sure this hits the base layer.
fn on_touch_cancel(&mut self, id: TouchId, point: DevicePoint) { // The order of these events doesn't matter, because zoom is handled by
// Send the event to script. // a root display list and the scroll event here is handled by the scroll
self.touch_handler.on_touch_cancel(id, point); // applied to the content display list.
self.send_touch_event(TouchEvent { self.pending_scroll_zoom_events
action: TouchEventAction::Cancel, .push(ScrollZoomEvent::PinchZoom(magnification));
id, self.pending_scroll_zoom_events
point, .push(ScrollZoomEvent::Scroll(ScrollEvent {
}) scroll_location: ScrollLocation::Delta(
LayoutVector2D::from_untyped(scroll_delta.to_untyped()),
),
cursor,
event_count: 1,
}));
},
_ => {},
}
},
}
} }
/// <http://w3c.github.io/touch-events/#mouse-events> /// <http://w3c.github.io/touch-events/#mouse-events>
@ -1518,18 +1571,18 @@ impl IOCompositor {
&mut self, &mut self,
scroll_location: ScrollLocation, scroll_location: ScrollLocation,
cursor: DeviceIntPoint, cursor: DeviceIntPoint,
action: TouchEventAction, event_type: TouchEventType,
) { ) {
if self.shutdown_state != ShutdownState::NotShuttingDown { if self.shutdown_state != ShutdownState::NotShuttingDown {
return; return;
} }
match action { match event_type {
TouchEventAction::Move => self.on_scroll_window_event(scroll_location, cursor), TouchEventType::Move => self.on_scroll_window_event(scroll_location, cursor),
TouchEventAction::Up | TouchEventAction::Cancel => { TouchEventType::Up | TouchEventType::Cancel => {
self.on_scroll_window_event(scroll_location, cursor); self.on_scroll_window_event(scroll_location, cursor);
}, },
TouchEventAction::Down => { TouchEventType::Down => {
self.on_scroll_window_event(scroll_location, cursor); self.on_scroll_window_event(scroll_location, cursor);
}, },
} }

View file

@ -2,10 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use embedder_traits::TouchId; use embedder_traits::{TouchAction, TouchId};
use euclid::{Point2D, Scale, Vector2D}; use euclid::{Point2D, Scale, Vector2D};
use log::{debug, warn}; use log::{debug, warn};
use script_traits::EventResult;
use webrender_api::units::{DeviceIntPoint, DevicePixel, LayoutVector2D}; use webrender_api::units::{DeviceIntPoint, DevicePixel, LayoutVector2D};
use self::TouchState::*; use self::TouchState::*;
@ -25,6 +24,8 @@ const FLING_MAX_SCREEN_PX: f32 = 4000.0;
pub struct TouchHandler { pub struct TouchHandler {
pub state: TouchState, pub state: TouchState,
pub active_touch_points: Vec<TouchPoint>, pub active_touch_points: Vec<TouchPoint>,
pub prevent_move: bool,
pub prevent_click: bool,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -44,11 +45,6 @@ impl TouchPoint {
pub enum TouchState { pub enum TouchState {
/// Not tracking any touch point /// Not tracking any touch point
Nothing, Nothing,
/// A touchstart event was dispatched to the page, but the response wasn't received yet.
/// Contains the initial touch point.
WaitingForScript,
/// Script is consuming the current touch sequence; don't perform default actions.
DefaultPrevented,
/// A single touch point is active and may perform click or pan default actions. /// A single touch point is active and may perform click or pan default actions.
/// Contains the initial touch location. /// Contains the initial touch location.
Touching, Touching,
@ -67,21 +63,6 @@ pub enum TouchState {
MultiTouch, MultiTouch,
} }
/// The action to take in response to a touch event
#[derive(Clone, Copy, Debug)]
pub enum TouchAction {
/// Simulate a mouse click.
Click,
/// Scroll by the provided offset.
Scroll(Vector2D<f32, DevicePixel>),
/// Zoom by a magnification factor and scroll by the provided offset.
Zoom(f32, Vector2D<f32, DevicePixel>),
/// Send a JavaScript event to content.
DispatchEvent,
/// Don't do anything.
NoAction,
}
pub(crate) struct FlingAction { pub(crate) struct FlingAction {
pub delta: LayoutVector2D, pub delta: LayoutVector2D,
pub cursor: DeviceIntPoint, pub cursor: DeviceIntPoint,
@ -92,20 +73,21 @@ impl TouchHandler {
TouchHandler { TouchHandler {
state: Nothing, state: Nothing,
active_touch_points: Vec::new(), active_touch_points: Vec::new(),
prevent_move: true,
prevent_click: false,
} }
} }
pub fn on_touch_down(&mut self, id: TouchId, point: Point2D<f32, DevicePixel>) { pub fn on_touch_down(&mut self, id: TouchId, point: Point2D<f32, DevicePixel>) {
let point = TouchPoint::new(id, point); let point = TouchPoint::new(id, point);
self.active_touch_points.push(point); self.active_touch_points.push(point);
self.state = Touching;
self.state = match self.state { self.prevent_click = match self.touch_count() {
Nothing => WaitingForScript, 1 => {
Flinging { .. } => Touching, self.prevent_move = true;
Touching | Panning { .. } => Pinching, false
WaitingForScript => WaitingForScript, },
DefaultPrevented => DefaultPrevented, _ => true,
Pinching | MultiTouch => MultiTouch,
}; };
} }
@ -140,51 +122,51 @@ impl TouchHandler {
}, },
}; };
let old_point = self.active_touch_points[idx].point; let old_point = self.active_touch_points[idx].point;
let action = match self.state {
Touching => {
let delta = point - old_point; let delta = point - old_point;
if delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX || let action = match self.touch_count() {
1 => {
if let Panning { ref mut velocity } = self.state {
// TODO: Probably we should track 1-3 more points and use a smarter algorithm
*velocity += delta;
*velocity /= 2.0;
TouchAction::Scroll(delta, point)
} else if delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
{ {
self.state = Panning { self.state = Panning {
velocity: Vector2D::new(delta.x, delta.y), velocity: Vector2D::new(delta.x, delta.y),
}; };
TouchAction::Scroll(delta) self.prevent_click = true;
TouchAction::Scroll(delta, point)
} else { } else {
TouchAction::NoAction TouchAction::NoAction
} }
}, },
Panning { ref mut velocity } => { 2 => {
let delta = point - old_point; if self.state == Pinching ||
// TODO: Probably we should track 1-3 more points and use a smarter algorithm delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
*velocity += delta; delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
*velocity /= 2.0; {
TouchAction::Scroll(delta) self.state = Pinching;
},
Flinging { .. } => {
unreachable!("Touch Move event received without preceding down.")
},
DefaultPrevented => TouchAction::DispatchEvent,
Pinching => {
let (d0, c0) = self.pinch_distance_and_center(); let (d0, c0) = self.pinch_distance_and_center();
self.active_touch_points[idx].point = point; self.active_touch_points[idx].point = point;
let (d1, c1) = self.pinch_distance_and_center(); let (d1, c1) = self.pinch_distance_and_center();
let magnification = d1 / d0; let magnification = d1 / d0;
let scroll_delta = c1 - c0 * Scale::new(magnification); let scroll_delta = c1 - c0 * Scale::new(magnification);
TouchAction::Zoom(magnification, scroll_delta) TouchAction::Zoom(magnification, scroll_delta)
} else {
TouchAction::NoAction
}
},
_ => {
self.state = MultiTouch;
TouchAction::NoAction
}, },
WaitingForScript => TouchAction::NoAction,
MultiTouch => TouchAction::NoAction,
Nothing => unreachable!(),
}; };
// If we're still waiting to see whether this is a click or pan, remember the original // update the touch point with the latest location.
// location. Otherwise, update the touch point with the latest location. if self.state != Touching {
if self.state != Touching && self.state != WaitingForScript {
self.active_touch_points[idx].point = point; self.active_touch_points[idx].point = point;
} }
action action
@ -198,18 +180,10 @@ impl TouchHandler {
None None
}, },
}; };
match self.state { match self.touch_count() {
Touching => { 0 => {
// FIXME: If the duration exceeds some threshold, send a contextmenu event instead. if let Panning { velocity } = self.state {
// FIXME: Don't send a click if preventDefault is called on the touchend event. if velocity.length().abs() >= FLING_MIN_SCREEN_PX {
self.state = Nothing;
TouchAction::Click
},
Nothing => {
self.state = Nothing;
TouchAction::NoAction
},
Panning { velocity } if velocity.length().abs() >= FLING_MIN_SCREEN_PX => {
// TODO: point != old. Not sure which one is better to take as cursor for flinging. // TODO: point != old. Not sure which one is better to take as cursor for flinging.
debug!( debug!(
"Transitioning to Fling. Cursor is {point:?}. Old cursor was {old:?}. \ "Transitioning to Fling. Cursor is {point:?}. Old cursor was {old:?}. \
@ -221,33 +195,22 @@ impl TouchHandler {
// Multiplying the initial velocity gives the fling a much more snappy feel // Multiplying the initial velocity gives the fling a much more snappy feel
// and serves well as a poor-mans acceleration algorithm. // and serves well as a poor-mans acceleration algorithm.
let velocity = (velocity * 2.0).with_max_length(FLING_MAX_SCREEN_PX); let velocity = (velocity * 2.0).with_max_length(FLING_MAX_SCREEN_PX);
self.state = Flinging { velocity, cursor }; TouchAction::Flinging(velocity, cursor)
TouchAction::NoAction } else {
},
Panning { .. } => {
self.state = Nothing; self.state = Nothing;
TouchAction::NoAction TouchAction::NoAction
},
Pinching => {
self.state = Panning {
velocity: Vector2D::new(0.0, 0.0),
};
TouchAction::NoAction
},
Flinging { .. } => {
unreachable!("On touchup received, but already flinging.")
},
WaitingForScript => {
if self.active_touch_points.is_empty() {
self.state = Nothing;
return TouchAction::Click;
} }
TouchAction::NoAction } else {
},
DefaultPrevented | MultiTouch => {
if self.active_touch_points.is_empty() {
self.state = Nothing; self.state = Nothing;
if !self.prevent_click {
TouchAction::Click(point)
} else {
TouchAction::NoAction
} }
}
},
_ => {
self.state = Touching;
TouchAction::NoAction TouchAction::NoAction
}, },
} }
@ -263,35 +226,11 @@ impl TouchHandler {
return; return;
}, },
} }
match self.state {
Nothing => {},
Touching | Panning { .. } | Flinging { .. } => {
self.state = Nothing; self.state = Nothing;
},
Pinching => {
self.state = Panning {
velocity: Vector2D::new(0.0, 0.0),
};
},
WaitingForScript | DefaultPrevented | MultiTouch => {
if self.active_touch_points.is_empty() {
self.state = Nothing;
}
},
}
} }
pub fn on_event_processed(&mut self, result: EventResult) { pub fn on_fling(&mut self, velocity: Vector2D<f32, DevicePixel>, cursor: DeviceIntPoint) {
if let WaitingForScript = self.state { self.state = Flinging { velocity, cursor };
self.state = match result {
EventResult::DefaultPrevented => DefaultPrevented,
EventResult::DefaultAllowed => match self.touch_count() {
1 => Touching,
2 => Pinching,
_ => MultiTouch,
},
}
}
} }
fn touch_count(&self) -> usize { fn touch_count(&self) -> usize {

View file

@ -26,7 +26,7 @@ use devtools_traits::ScriptToDevtoolsControlMsg;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::{ use embedder_traits::{
AllowOrDeny, ContextMenuResult, EditingActionEvent, EmbedderMsg, ImeEvent, InputEvent, AllowOrDeny, ContextMenuResult, EditingActionEvent, EmbedderMsg, ImeEvent, InputEvent,
LoadStatus, MouseButton, MouseButtonAction, MouseButtonEvent, TouchEvent, TouchEventAction, LoadStatus, MouseButton, MouseButtonAction, MouseButtonEvent, TouchEvent, TouchEventType,
TouchId, WheelEvent, TouchId, WheelEvent,
}; };
use encoding_rs::{Encoding, UTF_8}; use encoding_rs::{Encoding, UTF_8};
@ -2013,11 +2013,11 @@ impl Document {
}; };
let TouchId(identifier) = event.id; let TouchId(identifier) = event.id;
let event_name = match event.action { let event_name = match event.event_type {
TouchEventAction::Down => "touchstart", TouchEventType::Down => "touchstart",
TouchEventAction::Move => "touchmove", TouchEventType::Move => "touchmove",
TouchEventAction::Up => "touchend", TouchEventType::Up => "touchend",
TouchEventAction::Cancel => "touchcancel", TouchEventType::Cancel => "touchcancel",
}; };
let node = unsafe { node::from_untrusted_compositor_node_address(hit_test_result.node) }; let node = unsafe { node::from_untrusted_compositor_node_address(hit_test_result.node) };
@ -2043,14 +2043,14 @@ impl Document {
client_x, client_y, page_x, page_y, client_x, client_y, page_x, page_y,
); );
match event.action { match event.event_type {
TouchEventAction::Down => { TouchEventType::Down => {
// Add a new touch point // Add a new touch point
self.active_touch_points self.active_touch_points
.borrow_mut() .borrow_mut()
.push(Dom::from_ref(&*touch)); .push(Dom::from_ref(&*touch));
}, },
TouchEventAction::Move => { TouchEventType::Move => {
// Replace an existing touch point // Replace an existing touch point
let mut active_touch_points = self.active_touch_points.borrow_mut(); let mut active_touch_points = self.active_touch_points.borrow_mut();
match active_touch_points match active_touch_points
@ -2061,7 +2061,7 @@ impl Document {
None => warn!("Got a touchmove event for a non-active touch point"), None => warn!("Got a touchmove event for a non-active touch point"),
} }
}, },
TouchEventAction::Up | TouchEventAction::Cancel => { TouchEventType::Up | TouchEventType::Cancel => {
// Remove an existing touch point // Remove an existing touch point
let mut active_touch_points = self.active_touch_points.borrow_mut(); let mut active_touch_points = self.active_touch_points.borrow_mut();
match active_touch_points match active_touch_points

View file

@ -45,7 +45,7 @@ use devtools_traits::{
CSSError, DevtoolScriptControlMsg, DevtoolsPageInfo, NavigationState, CSSError, DevtoolScriptControlMsg, DevtoolsPageInfo, NavigationState,
ScriptToDevtoolsControlMsg, WorkerId, ScriptToDevtoolsControlMsg, WorkerId,
}; };
use embedder_traits::{EmbedderMsg, InputEvent, MediaSessionActionType, Theme, TouchEventAction}; use embedder_traits::{EmbedderMsg, InputEvent, MediaSessionActionType, Theme};
use euclid::default::Rect; use euclid::default::Rect;
use fonts::{FontContext, SystemFontServiceProxy}; use fonts::{FontContext, SystemFontServiceProxy};
use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader}; use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader};
@ -1101,13 +1101,12 @@ impl ScriptThread {
InputEvent::Touch(touch_event) => { InputEvent::Touch(touch_event) => {
let touch_result = let touch_result =
document.handle_touch_event(touch_event, event.hit_test_result, can_gc); document.handle_touch_event(touch_event, event.hit_test_result, can_gc);
match (touch_event.action, touch_result) { match touch_result {
(TouchEventAction::Down, TouchEventResult::Processed(handled)) => { TouchEventResult::Processed(handled) => {
let result = if handled { let result = if handled {
// TODO: Wait to see if preventDefault is called on the first touchmove event. EventResult::DefaultAllowed(touch_event.action)
EventResult::DefaultAllowed
} else { } else {
EventResult::DefaultPrevented EventResult::DefaultPrevented(touch_event.event_type)
}; };
let message = ScriptMsg::TouchEventProcessed(result); let message = ScriptMsg::TouchEventProcessed(result);
self.senders self.senders

View file

@ -12,7 +12,7 @@ use compositing::windowing::WebRenderDebugOption;
use compositing::IOCompositor; use compositing::IOCompositor;
use compositing_traits::ConstellationMsg; use compositing_traits::ConstellationMsg;
use embedder_traits::{ use embedder_traits::{
Cursor, InputEvent, LoadStatus, MediaSessionActionType, Theme, TouchEventAction, Cursor, InputEvent, LoadStatus, MediaSessionActionType, Theme, TouchEventType,
TraversalDirection, TraversalDirection,
}; };
use url::Url; use url::Url;
@ -313,7 +313,7 @@ impl WebView {
&self, &self,
location: ScrollLocation, location: ScrollLocation,
point: DeviceIntPoint, point: DeviceIntPoint,
touch_event_action: TouchEventAction, touch_event_action: TouchEventType,
) { ) {
self.inner() self.inner()
.compositor .compositor

View file

@ -18,6 +18,7 @@ webxr = ["dep:webxr-api"]
base = { workspace = true } base = { workspace = true }
cfg-if = { workspace = true } cfg-if = { workspace = true }
crossbeam-channel = { workspace = true } crossbeam-channel = { workspace = true }
euclid = { workspace = true }
http = { workspace = true } http = { workspace = true }
hyper_serde = { workspace = true } hyper_serde = { workspace = true }
ipc-channel = { workspace = true } ipc-channel = { workspace = true }

View file

@ -2,10 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use euclid::Vector2D;
use keyboard_types::{CompositionEvent, KeyboardEvent}; use keyboard_types::{CompositionEvent, KeyboardEvent};
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use webrender_api::units::DevicePoint; use webrender_api::units::{DeviceIntPoint, DevicePixel, DevicePoint};
/// An input event that is sent from the embedder to Servo. /// An input event that is sent from the embedder to Servo.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
@ -104,7 +105,7 @@ pub struct MouseMoveEvent {
/// The type of input represented by a multi-touch event. /// The type of input represented by a multi-touch event.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum TouchEventAction { pub enum TouchEventType {
/// A new touch point came in contact with the screen. /// A new touch point came in contact with the screen.
Down, Down,
/// An existing touch point changed location. /// An existing touch point changed location.
@ -115,6 +116,21 @@ pub enum TouchEventAction {
Cancel, Cancel,
} }
/// The action to take in response to a touch event
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum TouchAction {
/// Simulate a mouse click.
Click(DevicePoint),
/// Fling by the provided offset
Flinging(Vector2D<f32, DevicePixel>, DeviceIntPoint),
/// Scroll by the provided offset.
Scroll(Vector2D<f32, DevicePixel>, DevicePoint),
/// Zoom by a magnification factor and scroll by the provided offset.
Zoom(f32, Vector2D<f32, DevicePixel>),
/// Don't do anything.
NoAction,
}
/// An opaque identifier for a touch point. /// An opaque identifier for a touch point.
/// ///
/// <http://w3c.github.io/touch-events/#widl-Touch-identifier> /// <http://w3c.github.io/touch-events/#widl-Touch-identifier>
@ -123,9 +139,10 @@ pub struct TouchId(pub i32);
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TouchEvent { pub struct TouchEvent {
pub action: TouchEventAction, pub event_type: TouchEventType,
pub id: TouchId, pub id: TouchId,
pub point: DevicePoint, pub point: DevicePoint,
pub action: TouchAction,
} }
/// Mode to measure WheelDelta floats in /// Mode to measure WheelDelta floats in

View file

@ -13,7 +13,9 @@ use base::id::{
use base::Epoch; use base::Epoch;
use canvas_traits::canvas::{CanvasId, CanvasMsg}; use canvas_traits::canvas::{CanvasId, CanvasMsg};
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::{EmbedderMsg, MediaSessionEvent, TraversalDirection}; use embedder_traits::{
EmbedderMsg, MediaSessionEvent, TouchAction, TouchEventType, TraversalDirection,
};
use euclid::default::Size2D as UntypedSize2D; use euclid::default::Size2D as UntypedSize2D;
use euclid::Size2D; use euclid::Size2D;
use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::ipc::{IpcReceiver, IpcSender};
@ -64,9 +66,9 @@ impl fmt::Debug for LayoutMsg {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum EventResult { pub enum EventResult {
/// Allowed by web content /// Allowed by web content
DefaultAllowed, DefaultAllowed(TouchAction),
/// Prevented by web content /// Prevented by web content
DefaultPrevented, DefaultPrevented(TouchEventType),
} }
/// A log entry reported to the constellation /// A log entry reported to the constellation

View file

@ -19,7 +19,7 @@ use servo::webrender_api::ScrollLocation;
use servo::{ use servo::{
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, GamepadHapticEffectType, LoadStatus, AllowOrDenyRequest, AuthenticationRequest, FilterPattern, GamepadHapticEffectType, LoadStatus,
PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo, ServoDelegate, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo, ServoDelegate,
ServoError, TouchEventAction, WebView, WebViewDelegate, ServoError, TouchEventType, WebView, WebViewDelegate,
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use tinyfiledialogs::MessageBoxIcon; use tinyfiledialogs::MessageBoxIcon;
@ -274,36 +274,36 @@ impl RunningAppState {
0.0, 0.0,
-self.inner().window.page_height() + 2.0 * LINE_HEIGHT, -self.inner().window.page_height() + 2.0 * LINE_HEIGHT,
)); ));
webview.notify_scroll_event(scroll_location, origin, TouchEventAction::Move); webview.notify_scroll_event(scroll_location, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::PageUp, || { .shortcut(Modifiers::empty(), Key::PageUp, || {
let scroll_location = ScrollLocation::Delta(Vector2D::new( let scroll_location = ScrollLocation::Delta(Vector2D::new(
0.0, 0.0,
self.inner().window.page_height() - 2.0 * LINE_HEIGHT, self.inner().window.page_height() - 2.0 * LINE_HEIGHT,
)); ));
webview.notify_scroll_event(scroll_location, origin, TouchEventAction::Move); webview.notify_scroll_event(scroll_location, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::Home, || { .shortcut(Modifiers::empty(), Key::Home, || {
webview.notify_scroll_event(ScrollLocation::Start, origin, TouchEventAction::Move); webview.notify_scroll_event(ScrollLocation::Start, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::End, || { .shortcut(Modifiers::empty(), Key::End, || {
webview.notify_scroll_event(ScrollLocation::End, origin, TouchEventAction::Move); webview.notify_scroll_event(ScrollLocation::End, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::ArrowUp, || { .shortcut(Modifiers::empty(), Key::ArrowUp, || {
let location = ScrollLocation::Delta(Vector2D::new(0.0, 3.0 * LINE_HEIGHT)); let location = ScrollLocation::Delta(Vector2D::new(0.0, 3.0 * LINE_HEIGHT));
webview.notify_scroll_event(location, origin, TouchEventAction::Move); webview.notify_scroll_event(location, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::ArrowDown, || { .shortcut(Modifiers::empty(), Key::ArrowDown, || {
let location = ScrollLocation::Delta(Vector2D::new(0.0, -3.0 * LINE_HEIGHT)); let location = ScrollLocation::Delta(Vector2D::new(0.0, -3.0 * LINE_HEIGHT));
webview.notify_scroll_event(location, origin, TouchEventAction::Move); webview.notify_scroll_event(location, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::ArrowLeft, || { .shortcut(Modifiers::empty(), Key::ArrowLeft, || {
let location = ScrollLocation::Delta(Vector2D::new(LINE_HEIGHT, 0.0)); let location = ScrollLocation::Delta(Vector2D::new(LINE_HEIGHT, 0.0));
webview.notify_scroll_event(location, origin, TouchEventAction::Move); webview.notify_scroll_event(location, origin, TouchEventType::Move);
}) })
.shortcut(Modifiers::empty(), Key::ArrowRight, || { .shortcut(Modifiers::empty(), Key::ArrowRight, || {
let location = ScrollLocation::Delta(Vector2D::new(-LINE_HEIGHT, 0.0)); let location = ScrollLocation::Delta(Vector2D::new(-LINE_HEIGHT, 0.0));
webview.notify_scroll_event(location, origin, TouchEventAction::Move); webview.notify_scroll_event(location, origin, TouchEventType::Move);
}); });
} }
} }

View file

@ -26,8 +26,8 @@ use servo::webrender_traits::rendering_context::{OffscreenRenderingContext, Rend
use servo::webrender_traits::SurfmanRenderingContext; use servo::webrender_traits::SurfmanRenderingContext;
use servo::{ use servo::{
Cursor, InputEvent, Key, KeyState, KeyboardEvent, MouseButton as ServoMouseButton, Cursor, InputEvent, Key, KeyState, KeyboardEvent, MouseButton as ServoMouseButton,
MouseButtonAction, MouseButtonEvent, MouseMoveEvent, Theme, TouchEvent, TouchEventAction, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, Theme, TouchAction, TouchEvent,
TouchId, WebView, WheelDelta, WheelEvent, WheelMode, TouchEventType, TouchId, WebView, WheelDelta, WheelEvent, WheelMode,
}; };
use surfman::{Connection, Context, Device, SurfaceType}; use surfman::{Connection, Context, Device, SurfaceType};
use url::Url; use url::Url;
@ -606,7 +606,7 @@ impl WindowPortsMethods for Window {
} }
let scroll_location = ScrollLocation::Delta(Vector2D::new(dx as f32, dy as f32)); let scroll_location = ScrollLocation::Delta(Vector2D::new(dx as f32, dy as f32));
let phase = winit_phase_to_touch_event_action(phase); let phase = winit_phase_to_touch_event_type(phase);
// Send events // Send events
webview.notify_input_event(InputEvent::Wheel(WheelEvent { delta, point })); webview.notify_input_event(InputEvent::Wheel(WheelEvent { delta, point }));
@ -618,9 +618,10 @@ impl WindowPortsMethods for Window {
}, },
WindowEvent::Touch(touch) => { WindowEvent::Touch(touch) => {
webview.notify_input_event(InputEvent::Touch(TouchEvent { webview.notify_input_event(InputEvent::Touch(TouchEvent {
action: winit_phase_to_touch_event_action(touch.phase), event_type: winit_phase_to_touch_event_type(touch.phase),
id: TouchId(touch.id as i32), id: TouchId(touch.id as i32),
point: Point2D::new(touch.location.x as f32, touch.location.y as f32), point: Point2D::new(touch.location.x as f32, touch.location.y as f32),
action: TouchAction::NoAction,
})); }));
}, },
WindowEvent::PinchGesture { delta, .. } => { WindowEvent::PinchGesture { delta, .. } => {
@ -729,12 +730,12 @@ impl WindowMethods for Window {
} }
} }
fn winit_phase_to_touch_event_action(phase: TouchPhase) -> TouchEventAction { fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
match phase { match phase {
TouchPhase::Started => TouchEventAction::Down, TouchPhase::Started => TouchEventType::Down,
TouchPhase::Moved => TouchEventAction::Move, TouchPhase::Moved => TouchEventType::Move,
TouchPhase::Ended => TouchEventAction::Up, TouchPhase::Ended => TouchEventType::Up,
TouchPhase::Cancelled => TouchEventAction::Cancel, TouchPhase::Cancelled => TouchEventType::Cancel,
} }
} }

View file

@ -23,7 +23,8 @@ use servo::{
InputMethodType, Key, KeyState, KeyboardEvent, LoadStatus, MediaSessionActionType, InputMethodType, Key, KeyState, KeyboardEvent, LoadStatus, MediaSessionActionType,
MediaSessionEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, MediaSessionEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent,
NavigationRequest, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo, NavigationRequest, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo,
ServoDelegate, ServoError, TouchEvent, TouchEventAction, TouchId, WebView, WebViewDelegate, ServoDelegate, ServoError, TouchAction, TouchEvent, TouchEventType, TouchId, WebView,
WebViewDelegate,
}; };
use url::Url; use url::Url;
@ -449,7 +450,7 @@ impl RunningAppState {
self.active_webview().notify_scroll_event( self.active_webview().notify_scroll_event(
scroll_location, scroll_location,
Point2D::new(x, y), Point2D::new(x, y),
TouchEventAction::Down, TouchEventType::Down,
); );
self.perform_updates(); self.perform_updates();
} }
@ -463,7 +464,7 @@ impl RunningAppState {
self.active_webview().notify_scroll_event( self.active_webview().notify_scroll_event(
scroll_location, scroll_location,
Point2D::new(x, y), Point2D::new(x, y),
TouchEventAction::Move, TouchEventType::Move,
); );
self.perform_updates(); self.perform_updates();
} }
@ -478,7 +479,7 @@ impl RunningAppState {
self.active_webview().notify_scroll_event( self.active_webview().notify_scroll_event(
scroll_location, scroll_location,
Point2D::new(x, y), Point2D::new(x, y),
TouchEventAction::Up, TouchEventType::Up,
); );
self.perform_updates(); self.perform_updates();
} }
@ -487,9 +488,10 @@ impl RunningAppState {
pub fn touch_down(&self, x: f32, y: f32, pointer_id: i32) { pub fn touch_down(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview() self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent { .notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Down, event_type: TouchEventType::Down,
id: TouchId(pointer_id), id: TouchId(pointer_id),
point: Point2D::new(x, y), point: Point2D::new(x, y),
action: TouchAction::NoAction,
})); }));
self.perform_updates(); self.perform_updates();
} }
@ -498,9 +500,10 @@ impl RunningAppState {
pub fn touch_move(&self, x: f32, y: f32, pointer_id: i32) { pub fn touch_move(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview() self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent { .notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Move, event_type: TouchEventType::Move,
id: TouchId(pointer_id), id: TouchId(pointer_id),
point: Point2D::new(x, y), point: Point2D::new(x, y),
action: TouchAction::NoAction,
})); }));
self.perform_updates(); self.perform_updates();
} }
@ -509,9 +512,10 @@ impl RunningAppState {
pub fn touch_up(&self, x: f32, y: f32, pointer_id: i32) { pub fn touch_up(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview() self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent { .notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Up, event_type: TouchEventType::Up,
id: TouchId(pointer_id), id: TouchId(pointer_id),
point: Point2D::new(x, y), point: Point2D::new(x, y),
action: TouchAction::NoAction,
})); }));
self.perform_updates(); self.perform_updates();
} }
@ -520,9 +524,10 @@ impl RunningAppState {
pub fn touch_cancel(&self, x: f32, y: f32, pointer_id: i32) { pub fn touch_cancel(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview() self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent { .notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Cancel, event_type: TouchEventType::Cancel,
id: TouchId(pointer_id), id: TouchId(pointer_id),
point: Point2D::new(x, y), point: Point2D::new(x, y),
action: TouchAction::NoAction,
})); }));
self.perform_updates(); self.perform_updates();
} }