mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #7204 - mbrubeck:touchevent, r=jdm
Very basic touch events and touch scrolling This implements just enough of [Touch Events](http://w3c.github.io/touch-events/) to enable scrolling on Android without regressing basic single-touch interaction like clicking on links. Dragging a page will scroll it, unless the page calls `preventDefault` on the "touchstart" event. Does **not** yet support pinch zooming or other multi-touch gestures or events. Includes a `-Z convert-mouse-to-touch` command line flag for testing on non-touch platforms. This is also enabled by default on Android because Glutin currently translates touch input to mouse events on Android. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/7204) <!-- Reviewable:end -->
This commit is contained in:
commit
dcd207f9bf
21 changed files with 672 additions and 18 deletions
|
@ -26,7 +26,7 @@ use layers::rendergl;
|
||||||
use layers::rendergl::RenderContext;
|
use layers::rendergl::RenderContext;
|
||||||
use layers::scene::Scene;
|
use layers::scene::Scene;
|
||||||
use layout_traits::LayoutControlChan;
|
use layout_traits::LayoutControlChan;
|
||||||
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerKind};
|
use msg::compositor_msg::{Epoch, EventResult, FrameTreeId, LayerId, LayerKind};
|
||||||
use msg::compositor_msg::{LayerProperties, ScrollPolicy};
|
use msg::compositor_msg::{LayerProperties, ScrollPolicy};
|
||||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||||
use msg::constellation_msg::{AnimationState, Image, PixelFormat};
|
use msg::constellation_msg::{AnimationState, Image, PixelFormat};
|
||||||
|
@ -35,7 +35,8 @@ use msg::constellation_msg::{NavigationDirection, PipelineId, WindowSizeData};
|
||||||
use pipeline::CompositionPipeline;
|
use pipeline::CompositionPipeline;
|
||||||
use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
|
use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
|
||||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
|
use script_traits::CompositorEvent::{TouchDownEvent, TouchMoveEvent, TouchUpEvent};
|
||||||
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg, MouseButton};
|
||||||
use scrolling::ScrollingTimerProxy;
|
use scrolling::ScrollingTimerProxy;
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -59,6 +60,9 @@ const BUFFER_MAP_SIZE: usize = 10000000;
|
||||||
const MAX_ZOOM: f32 = 8.0;
|
const MAX_ZOOM: f32 = 8.0;
|
||||||
const MIN_ZOOM: f32 = 0.1;
|
const MIN_ZOOM: f32 = 0.1;
|
||||||
|
|
||||||
|
/// Minimum number of ScreenPx to begin touch scrolling.
|
||||||
|
const TOUCH_PAN_MIN_SCREEN_PX: f32 = 20.0;
|
||||||
|
|
||||||
/// Holds the state when running reftests that determines when it is
|
/// Holds the state when running reftests that determines when it is
|
||||||
/// safe to save the output image.
|
/// safe to save the output image.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
@ -68,6 +72,23 @@ enum ReadyState {
|
||||||
ReadyToSaveImage,
|
ReadyToSaveImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The states of the touch input state machine.
|
||||||
|
///
|
||||||
|
/// TODO: Currently Add support for "flinging" (scrolling inertia), pinch zooming, better
|
||||||
|
/// support for multiple touch points.
|
||||||
|
enum TouchState {
|
||||||
|
/// Not tracking any touch point
|
||||||
|
Nothing,
|
||||||
|
/// A touchstart event was dispatched to the page, but the response wasn't received yet.
|
||||||
|
WaitingForScript,
|
||||||
|
/// Script is consuming the current touch point; don't perform default actions.
|
||||||
|
DefaultPrevented,
|
||||||
|
/// A single touch point is active and may perform click or pan default actions.
|
||||||
|
Touching,
|
||||||
|
/// A single touch point is active and has started panning.
|
||||||
|
Panning,
|
||||||
|
}
|
||||||
|
|
||||||
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
|
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
|
||||||
pub struct IOCompositor<Window: WindowMethods> {
|
pub struct IOCompositor<Window: WindowMethods> {
|
||||||
/// The application window.
|
/// The application window.
|
||||||
|
@ -156,6 +177,15 @@ pub struct IOCompositor<Window: WindowMethods> {
|
||||||
/// Pending scroll to fragment event, if any
|
/// Pending scroll to fragment event, if any
|
||||||
fragment_point: Option<Point2D<f32>>,
|
fragment_point: Option<Point2D<f32>>,
|
||||||
|
|
||||||
|
/// Touch input state machine
|
||||||
|
touch_gesture_state: TouchState,
|
||||||
|
|
||||||
|
/// When tracking a touch for gesture detection, the point where it started
|
||||||
|
first_touch_point: Option<TypedPoint2D<DevicePixel, f32>>,
|
||||||
|
|
||||||
|
/// When tracking a touch for gesture detection, its most recent point
|
||||||
|
last_touch_point: Option<TypedPoint2D<DevicePixel, f32>>,
|
||||||
|
|
||||||
/// Pending scroll events.
|
/// Pending scroll events.
|
||||||
pending_scroll_events: Vec<ScrollEvent>,
|
pending_scroll_events: Vec<ScrollEvent>,
|
||||||
|
|
||||||
|
@ -295,6 +325,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
channel_to_self: state.sender.clone_compositor_proxy(),
|
channel_to_self: state.sender.clone_compositor_proxy(),
|
||||||
scrolling_timer: ScrollingTimerProxy::new(state.sender),
|
scrolling_timer: ScrollingTimerProxy::new(state.sender),
|
||||||
composition_request: CompositionRequest::NoCompositingNecessary,
|
composition_request: CompositionRequest::NoCompositingNecessary,
|
||||||
|
touch_gesture_state: TouchState::Nothing,
|
||||||
|
first_touch_point: None,
|
||||||
|
last_touch_point: None,
|
||||||
pending_scroll_events: Vec::new(),
|
pending_scroll_events: Vec::new(),
|
||||||
composite_target: composite_target,
|
composite_target: composite_target,
|
||||||
shutdown_state: ShutdownState::NotShuttingDown,
|
shutdown_state: ShutdownState::NotShuttingDown,
|
||||||
|
@ -502,6 +535,18 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
|
||||||
|
match self.touch_gesture_state {
|
||||||
|
TouchState::WaitingForScript => {
|
||||||
|
self.touch_gesture_state = match result {
|
||||||
|
EventResult::DefaultAllowed => TouchState::Touching,
|
||||||
|
EventResult::DefaultPrevented => TouchState::DefaultPrevented,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(Msg::SetCursor(cursor), ShutdownState::NotShuttingDown) => {
|
(Msg::SetCursor(cursor), ShutdownState::NotShuttingDown) => {
|
||||||
self.window.set_cursor(cursor)
|
self.window.set_cursor(cursor)
|
||||||
}
|
}
|
||||||
|
@ -1078,7 +1123,16 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
chan.send(msg).unwrap()
|
chan.send(msg).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse_window_event_class(&self, mouse_window_event: MouseWindowEvent) {
|
fn on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
|
||||||
|
if opts::get().convert_mouse_to_touch {
|
||||||
|
match mouse_window_event {
|
||||||
|
MouseWindowEvent::Click(_, _) => {}
|
||||||
|
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(0, p),
|
||||||
|
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(0, p),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let point = match mouse_window_event {
|
let point = match mouse_window_event {
|
||||||
MouseWindowEvent::Click(_, p) => p,
|
MouseWindowEvent::Click(_, p) => p,
|
||||||
MouseWindowEvent::MouseDown(_, p) => p,
|
MouseWindowEvent::MouseDown(_, p) => p,
|
||||||
|
@ -1090,13 +1144,121 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D<DevicePixel, f32>) {
|
fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<DevicePixel, f32>) {
|
||||||
|
if opts::get().convert_mouse_to_touch {
|
||||||
|
self.on_touch_move(0, cursor);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
match self.find_topmost_layer_at_point(cursor / self.scene.scale) {
|
match self.find_topmost_layer_at_point(cursor / self.scene.scale) {
|
||||||
Some(result) => result.layer.send_mouse_move_event(self, result.point),
|
Some(result) => result.layer.send_mouse_move_event(self, result.point),
|
||||||
None => {},
|
None => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_touch_down(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
|
match self.touch_gesture_state {
|
||||||
|
TouchState::Nothing => {
|
||||||
|
// TODO: Don't wait for script if we know the page has no touch event listeners.
|
||||||
|
self.first_touch_point = Some(point);
|
||||||
|
self.last_touch_point = Some(point);
|
||||||
|
self.touch_gesture_state = TouchState::WaitingForScript;
|
||||||
|
}
|
||||||
|
TouchState::WaitingForScript => {
|
||||||
|
// TODO: Queue events while waiting for script?
|
||||||
|
}
|
||||||
|
TouchState::DefaultPrevented => {}
|
||||||
|
TouchState::Touching => {}
|
||||||
|
TouchState::Panning => {}
|
||||||
|
}
|
||||||
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
|
result.layer.send_event(self, TouchDownEvent(identifier, result.point.to_untyped()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_touch_move(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
|
match self.touch_gesture_state {
|
||||||
|
TouchState::Nothing => warn!("Got unexpected touch move event"),
|
||||||
|
|
||||||
|
TouchState::WaitingForScript => {
|
||||||
|
// TODO: Queue events while waiting for script?
|
||||||
|
}
|
||||||
|
TouchState::Touching => {
|
||||||
|
match self.first_touch_point {
|
||||||
|
Some(p0) => {
|
||||||
|
let delta = point - p0;
|
||||||
|
let px: TypedPoint2D<ScreenPx, _> = delta / self.device_pixels_per_screen_px();
|
||||||
|
let px = px.to_untyped();
|
||||||
|
|
||||||
|
if px.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
|
||||||
|
px.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
|
||||||
|
{
|
||||||
|
self.touch_gesture_state = TouchState::Panning;
|
||||||
|
self.on_scroll_window_event(delta, point.cast().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => warn!("first_touch_point not set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TouchState::Panning => {
|
||||||
|
match self.last_touch_point {
|
||||||
|
Some(p0) => {
|
||||||
|
let delta = point - p0;
|
||||||
|
self.on_scroll_window_event(delta, point.cast().unwrap());
|
||||||
|
}
|
||||||
|
None => warn!("last_touch_point not set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TouchState::DefaultPrevented => {
|
||||||
|
// Send the event to script.
|
||||||
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
|
result.layer.send_event(self,
|
||||||
|
TouchMoveEvent(identifier, result.point.to_untyped()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.last_touch_point = Some(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_touch_up(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
|
||||||
|
// TODO: Track the number of active touch points, and don't reset stuff until it is zero.
|
||||||
|
self.first_touch_point = None;
|
||||||
|
self.last_touch_point = None;
|
||||||
|
|
||||||
|
// Send the event to script.
|
||||||
|
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
|
||||||
|
result.layer.send_event(self, TouchUpEvent(identifier, result.point.to_untyped()));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.touch_gesture_state {
|
||||||
|
TouchState::Nothing => warn!("Got unexpected touch up event"),
|
||||||
|
|
||||||
|
TouchState::WaitingForScript => {}
|
||||||
|
TouchState::Touching => {
|
||||||
|
// TODO: If the duration exceeds some threshold, send a contextmenu event instead.
|
||||||
|
// TODO: Don't send a click if preventDefault is called on the touchend event.
|
||||||
|
self.simulate_mouse_click(point);
|
||||||
|
}
|
||||||
|
TouchState::Panning => {}
|
||||||
|
TouchState::DefaultPrevented => {}
|
||||||
|
}
|
||||||
|
self.touch_gesture_state = TouchState::Nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// http://w3c.github.io/touch-events/#mouse-events
|
||||||
|
fn simulate_mouse_click(&self, p: TypedPoint2D<DevicePixel, f32>) {
|
||||||
|
match self.find_topmost_layer_at_point(p / self.scene.scale) {
|
||||||
|
Some(HitTestResult { layer, point }) => {
|
||||||
|
let button = MouseButton::Left;
|
||||||
|
layer.send_mouse_move_event(self, point);
|
||||||
|
layer.send_mouse_event(self, MouseWindowEvent::MouseDown(button, p), point);
|
||||||
|
layer.send_mouse_event(self, MouseWindowEvent::MouseUp(button, p), point);
|
||||||
|
layer.send_mouse_event(self, MouseWindowEvent::Click(button, p), point);
|
||||||
|
}
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn on_scroll_window_event(&mut self,
|
fn on_scroll_window_event(&mut self,
|
||||||
delta: TypedPoint2D<DevicePixel, f32>,
|
delta: TypedPoint2D<DevicePixel, f32>,
|
||||||
cursor: TypedPoint2D<DevicePixel, i32>) {
|
cursor: TypedPoint2D<DevicePixel, i32>) {
|
||||||
|
|
|
@ -12,7 +12,8 @@ use layers::color::Color;
|
||||||
use layers::geometry::LayerPixel;
|
use layers::geometry::LayerPixel;
|
||||||
use layers::layers::{Layer, LayerBufferSet};
|
use layers::layers::{Layer, LayerBufferSet};
|
||||||
use msg::compositor_msg::{Epoch, LayerId, LayerProperties, ScrollPolicy};
|
use msg::compositor_msg::{Epoch, LayerId, LayerProperties, ScrollPolicy};
|
||||||
use msg::constellation_msg::{PipelineId};
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use script_traits::CompositorEvent;
|
||||||
use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
||||||
use script_traits::ConstellationControlMsg;
|
use script_traits::ConstellationControlMsg;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -132,6 +133,11 @@ pub trait CompositorLayer {
|
||||||
cursor: TypedPoint2D<LayerPixel, f32>)
|
cursor: TypedPoint2D<LayerPixel, f32>)
|
||||||
where Window: WindowMethods;
|
where Window: WindowMethods;
|
||||||
|
|
||||||
|
fn send_event<Window>(&self,
|
||||||
|
compositor: &IOCompositor<Window>,
|
||||||
|
event: CompositorEvent)
|
||||||
|
where Window: WindowMethods;
|
||||||
|
|
||||||
fn clamp_scroll_offset_and_scroll_layer(&self,
|
fn clamp_scroll_offset_and_scroll_layer(&self,
|
||||||
new_offset: TypedPoint2D<LayerPixel, f32>)
|
new_offset: TypedPoint2D<LayerPixel, f32>)
|
||||||
-> ScrollEventResult;
|
-> ScrollEventResult;
|
||||||
|
@ -372,22 +378,22 @@ impl CompositorLayer for Layer<CompositorData> {
|
||||||
MouseWindowEvent::MouseUp(button, _) =>
|
MouseWindowEvent::MouseUp(button, _) =>
|
||||||
MouseUpEvent(button, event_point),
|
MouseUpEvent(button, event_point),
|
||||||
};
|
};
|
||||||
|
self.send_event(compositor, message);
|
||||||
if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) {
|
|
||||||
pipeline.script_chan
|
|
||||||
.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_mouse_move_event<Window>(&self,
|
fn send_mouse_move_event<Window>(&self,
|
||||||
compositor: &IOCompositor<Window>,
|
compositor: &IOCompositor<Window>,
|
||||||
cursor: TypedPoint2D<LayerPixel, f32>)
|
cursor: TypedPoint2D<LayerPixel, f32>)
|
||||||
where Window: WindowMethods {
|
where Window: WindowMethods {
|
||||||
let message = MouseMoveEvent(cursor.to_untyped());
|
self.send_event(compositor, MouseMoveEvent(cursor.to_untyped()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_event<Window>(&self,
|
||||||
|
compositor: &IOCompositor<Window>,
|
||||||
|
event: CompositorEvent) where Window: WindowMethods {
|
||||||
if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) {
|
if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) {
|
||||||
let _ = pipeline.script_chan
|
let _ = pipeline.script_chan
|
||||||
.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message));
|
.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use headless;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use layers::layers::{BufferRequest, LayerBufferSet};
|
use layers::layers::{BufferRequest, LayerBufferSet};
|
||||||
use layers::platform::surface::{NativeDisplay, NativeSurface};
|
use layers::platform::surface::{NativeDisplay, NativeSurface};
|
||||||
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerProperties};
|
use msg::compositor_msg::{Epoch, EventResult, FrameTreeId, LayerId, LayerProperties};
|
||||||
use msg::compositor_msg::{PaintListener, ScriptToCompositorMsg};
|
use msg::compositor_msg::{PaintListener, ScriptToCompositorMsg};
|
||||||
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
|
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
|
||||||
use msg::constellation_msg::{Image, Key, KeyModifiers, KeyState};
|
use msg::constellation_msg::{Image, Key, KeyModifiers, KeyState};
|
||||||
|
@ -94,6 +94,10 @@ pub fn run_script_listener_thread(compositor_proxy: Box<CompositorProxy + 'stati
|
||||||
ScriptToCompositorMsg::SendKeyEvent(key, key_state, key_modifiers) => {
|
ScriptToCompositorMsg::SendKeyEvent(key, key_state, key_modifiers) => {
|
||||||
compositor_proxy.send(Msg::KeyEvent(key, key_state, key_modifiers))
|
compositor_proxy.send(Msg::KeyEvent(key, key_state, key_modifiers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptToCompositorMsg::TouchEventProcessed(result) => {
|
||||||
|
compositor_proxy.send(Msg::TouchEventProcessed(result))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,6 +193,8 @@ pub enum Msg {
|
||||||
RecompositeAfterScroll,
|
RecompositeAfterScroll,
|
||||||
/// Sends an unconsumed key event back to the compositor.
|
/// Sends an unconsumed key event back to the compositor.
|
||||||
KeyEvent(Key, KeyState, KeyModifiers),
|
KeyEvent(Key, KeyState, KeyModifiers),
|
||||||
|
/// Script has handled a touch event, and either prevented or allowed default actions.
|
||||||
|
TouchEventProcessed(EventResult),
|
||||||
/// Changes the cursor.
|
/// Changes the cursor.
|
||||||
SetCursor(Cursor),
|
SetCursor(Cursor),
|
||||||
/// Composite to a PNG file and return the Image over a passed channel.
|
/// Composite to a PNG file and return the Image over a passed channel.
|
||||||
|
@ -238,6 +244,7 @@ impl Debug for Msg {
|
||||||
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
|
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
|
||||||
Msg::RecompositeAfterScroll => write!(f, "RecompositeAfterScroll"),
|
Msg::RecompositeAfterScroll => write!(f, "RecompositeAfterScroll"),
|
||||||
Msg::KeyEvent(..) => write!(f, "KeyEvent"),
|
Msg::KeyEvent(..) => write!(f, "KeyEvent"),
|
||||||
|
Msg::TouchEventProcessed(..) => write!(f, "TouchEventProcessed"),
|
||||||
Msg::SetCursor(..) => write!(f, "SetCursor"),
|
Msg::SetCursor(..) => write!(f, "SetCursor"),
|
||||||
Msg::CreatePng(..) => write!(f, "CreatePng"),
|
Msg::CreatePng(..) => write!(f, "CreatePng"),
|
||||||
Msg::PaintTaskExited(..) => write!(f, "PaintTaskExited"),
|
Msg::PaintTaskExited(..) => write!(f, "PaintTaskExited"),
|
||||||
|
|
|
@ -120,6 +120,7 @@ impl CompositorEventListener for NullCompositor {
|
||||||
Msg::ChangePageTitle(..) |
|
Msg::ChangePageTitle(..) |
|
||||||
Msg::ChangePageUrl(..) |
|
Msg::ChangePageUrl(..) |
|
||||||
Msg::KeyEvent(..) |
|
Msg::KeyEvent(..) |
|
||||||
|
Msg::TouchEventProcessed(..) |
|
||||||
Msg::SetCursor(..) |
|
Msg::SetCursor(..) |
|
||||||
Msg::ViewportConstrained(..) => {}
|
Msg::ViewportConstrained(..) => {}
|
||||||
Msg::CreatePng(..) |
|
Msg::CreatePng(..) |
|
||||||
|
|
|
@ -160,5 +160,12 @@ pub enum ScriptToCompositorMsg {
|
||||||
GetClientWindow(IpcSender<(Size2D<u32>, Point2D<i32>)>),
|
GetClientWindow(IpcSender<(Size2D<u32>, Point2D<i32>)>),
|
||||||
MoveTo(Point2D<i32>),
|
MoveTo(Point2D<i32>),
|
||||||
ResizeTo(Size2D<u32>),
|
ResizeTo(Size2D<u32>),
|
||||||
|
TouchEventProcessed(EventResult),
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub enum EventResult {
|
||||||
|
DefaultAllowed,
|
||||||
|
DefaultPrevented,
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use num::Float;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Encapsulates the IDL restricted float type.
|
/// Encapsulates the IDL restricted float type.
|
||||||
#[derive(JSTraceable, Clone, Eq, PartialEq)]
|
#[derive(JSTraceable, Clone, Copy, Eq, PartialEq)]
|
||||||
pub struct Finite<T: Float>(T);
|
pub struct Finite<T: Float>(T);
|
||||||
|
|
||||||
unsafe impl<T: Float> Zeroable for Finite<T> {}
|
unsafe impl<T: Float> Zeroable for Finite<T> {}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||||
use dom::bindings::global::GlobalRef;
|
use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::RootedReference;
|
use dom::bindings::js::RootedReference;
|
||||||
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
|
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
|
||||||
|
use dom::bindings::num::Finite;
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::bindings::trace::RootedVec;
|
use dom::bindings::trace::RootedVec;
|
||||||
use dom::bindings::utils::XMLName::InvalidXMLName;
|
use dom::bindings::utils::XMLName::InvalidXMLName;
|
||||||
|
@ -60,6 +61,9 @@ use dom::processinginstruction::ProcessingInstruction;
|
||||||
use dom::range::Range;
|
use dom::range::Range;
|
||||||
use dom::servohtmlparser::ServoHTMLParser;
|
use dom::servohtmlparser::ServoHTMLParser;
|
||||||
use dom::text::Text;
|
use dom::text::Text;
|
||||||
|
use dom::touch::Touch;
|
||||||
|
use dom::touchevent::TouchEvent;
|
||||||
|
use dom::touchlist::TouchList;
|
||||||
use dom::treewalker::TreeWalker;
|
use dom::treewalker::TreeWalker;
|
||||||
use dom::uievent::UIEvent;
|
use dom::uievent::UIEvent;
|
||||||
use dom::window::{ReflowReason, Window};
|
use dom::window::{ReflowReason, Window};
|
||||||
|
@ -590,7 +594,7 @@ impl Document {
|
||||||
EventCancelable::Cancelable,
|
EventCancelable::Cancelable,
|
||||||
Some(&self.window),
|
Some(&self.window),
|
||||||
clickCount,
|
clickCount,
|
||||||
x, y, x, y,
|
x, y, x, y, // TODO: Get real screen coordinates?
|
||||||
false, false, false, false,
|
false, false, false, false,
|
||||||
0i16,
|
0i16,
|
||||||
None);
|
None);
|
||||||
|
@ -699,6 +703,60 @@ impl Document {
|
||||||
ReflowReason::MouseEvent);
|
ReflowReason::MouseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_touch_event(&self,
|
||||||
|
js_runtime: *mut JSRuntime,
|
||||||
|
identifier: i32,
|
||||||
|
point: Point2D<f32>,
|
||||||
|
event_name: String) -> bool {
|
||||||
|
let node = match self.hit_test(&point) {
|
||||||
|
Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address),
|
||||||
|
None => return false
|
||||||
|
};
|
||||||
|
let el = match node.downcast::<Element>() {
|
||||||
|
Some(el) => Root::from_ref(el),
|
||||||
|
None => {
|
||||||
|
let parent = node.r().GetParentNode();
|
||||||
|
match parent.and_then(Root::downcast::<Element>) {
|
||||||
|
Some(parent) => parent,
|
||||||
|
None => return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let target = el.upcast::<EventTarget>();
|
||||||
|
let window = self.window.root();
|
||||||
|
|
||||||
|
let client_x = Finite::wrap(point.x as f64);
|
||||||
|
let client_y = Finite::wrap(point.y as f64);
|
||||||
|
let page_x = Finite::wrap(point.x as f64 + window.PageXOffset() as f64);
|
||||||
|
let page_y = Finite::wrap(point.y as f64 + window.PageYOffset() as f64);
|
||||||
|
|
||||||
|
let touch = Touch::new(window.r(), identifier, target,
|
||||||
|
client_x, client_y, // TODO: Get real screen coordinates?
|
||||||
|
client_x, client_y,
|
||||||
|
page_x, page_y);
|
||||||
|
|
||||||
|
let mut touches = RootedVec::new();
|
||||||
|
touches.push(JS::from_rooted(&touch));
|
||||||
|
let touches = TouchList::new(window.r(), touches.r());
|
||||||
|
|
||||||
|
let event = TouchEvent::new(window.r(),
|
||||||
|
event_name,
|
||||||
|
EventBubbles::Bubbles,
|
||||||
|
EventCancelable::Cancelable,
|
||||||
|
Some(window.r()),
|
||||||
|
0i32,
|
||||||
|
&touches, &touches, &touches,
|
||||||
|
// FIXME: modifier keys
|
||||||
|
false, false, false, false);
|
||||||
|
let event = event.upcast::<Event>();
|
||||||
|
let result = event.fire(target);
|
||||||
|
|
||||||
|
window.r().reflow(ReflowGoal::ForDisplay,
|
||||||
|
ReflowQueryType::NoQuery,
|
||||||
|
ReflowReason::MouseEvent);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
/// The entry point for all key processing for web content
|
/// The entry point for all key processing for web content
|
||||||
pub fn dispatch_key_event(&self,
|
pub fn dispatch_key_event(&self,
|
||||||
key: Key,
|
key: Key,
|
||||||
|
@ -1361,6 +1419,21 @@ impl DocumentMethods for Document {
|
||||||
NodeIterator::new(self, root, whatToShow, filter)
|
NodeIterator::new(self, root, whatToShow, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/touch-events/#idl-def-Document
|
||||||
|
fn CreateTouch(&self,
|
||||||
|
window: &Window,
|
||||||
|
target: &EventTarget,
|
||||||
|
identifier: i32,
|
||||||
|
pageX: Finite<f64>,
|
||||||
|
pageY: Finite<f64>,
|
||||||
|
screenX: Finite<f64>,
|
||||||
|
screenY: Finite<f64>)
|
||||||
|
-> Root<Touch> {
|
||||||
|
let clientX = Finite::wrap(*pageX - window.PageXOffset() as f64);
|
||||||
|
let clientY = Finite::wrap(*pageY - window.PageYOffset() as f64);
|
||||||
|
Touch::new(window, identifier, target, screenX, screenY, clientX, clientY, pageX, pageY)
|
||||||
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-document-createtreewalker
|
// https://dom.spec.whatwg.org/#dom-document-createtreewalker
|
||||||
fn CreateTreeWalker(&self, root: &Node, whatToShow: u32, filter: Option<Rc<NodeFilter>>)
|
fn CreateTreeWalker(&self, root: &Node, whatToShow: u32, filter: Option<Rc<NodeFilter>>)
|
||||||
-> Root<TreeWalker> {
|
-> Root<TreeWalker> {
|
||||||
|
|
|
@ -345,6 +345,9 @@ pub mod testbindingproxy;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod textdecoder;
|
pub mod textdecoder;
|
||||||
pub mod textencoder;
|
pub mod textencoder;
|
||||||
|
pub mod touch;
|
||||||
|
pub mod touchevent;
|
||||||
|
pub mod touchlist;
|
||||||
pub mod treewalker;
|
pub mod treewalker;
|
||||||
pub mod uievent;
|
pub mod uievent;
|
||||||
pub mod url;
|
pub mod url;
|
||||||
|
|
97
components/script/dom/touch.rs
Normal file
97
components/script/dom/touch.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::TouchBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::TouchBinding::TouchMethods;
|
||||||
|
use dom::bindings::global::GlobalRef;
|
||||||
|
use dom::bindings::js::{JS, MutHeap, Root};
|
||||||
|
use dom::bindings::num::Finite;
|
||||||
|
use dom::bindings::utils::{Reflector, reflect_dom_object};
|
||||||
|
use dom::eventtarget::EventTarget;
|
||||||
|
use dom::window::Window;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct Touch {
|
||||||
|
reflector_: Reflector,
|
||||||
|
identifier: i32,
|
||||||
|
target: MutHeap<JS<EventTarget>>,
|
||||||
|
screen_x: f64,
|
||||||
|
screen_y: f64,
|
||||||
|
client_x: f64,
|
||||||
|
client_y: f64,
|
||||||
|
page_x: f64,
|
||||||
|
page_y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Touch {
|
||||||
|
fn new_inherited(identifier: i32, target: &EventTarget,
|
||||||
|
screen_x: Finite<f64>, screen_y: Finite<f64>,
|
||||||
|
client_x: Finite<f64>, client_y: Finite<f64>,
|
||||||
|
page_x: Finite<f64>, page_y: Finite<f64>) -> Touch {
|
||||||
|
Touch {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
identifier: identifier,
|
||||||
|
target: MutHeap::new(target),
|
||||||
|
screen_x: *screen_x,
|
||||||
|
screen_y: *screen_y,
|
||||||
|
client_x: *client_x,
|
||||||
|
client_y: *client_y,
|
||||||
|
page_x: *page_x,
|
||||||
|
page_y: *page_y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(window: &Window, identifier: i32, target: &EventTarget,
|
||||||
|
screen_x: Finite<f64>, screen_y: Finite<f64>,
|
||||||
|
client_x: Finite<f64>, client_y: Finite<f64>,
|
||||||
|
page_x: Finite<f64>, page_y: Finite<f64>) -> Root<Touch> {
|
||||||
|
reflect_dom_object(box Touch::new_inherited(identifier, target,
|
||||||
|
screen_x, screen_y,
|
||||||
|
client_x, client_y,
|
||||||
|
page_x, page_y),
|
||||||
|
GlobalRef::Window(window), TouchBinding::Wrap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchMethods for Touch {
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-identifier
|
||||||
|
fn Identifier(&self) -> i32 {
|
||||||
|
self.identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-target
|
||||||
|
fn Target(&self) -> Root<EventTarget> {
|
||||||
|
self.target.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-screenX
|
||||||
|
fn ScreenX(&self) -> Finite<f64> {
|
||||||
|
Finite::wrap(self.screen_x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-screenY
|
||||||
|
fn ScreenY(&self) -> Finite<f64> {
|
||||||
|
Finite::wrap(self.screen_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-clientX
|
||||||
|
fn ClientX(&self) -> Finite<f64> {
|
||||||
|
Finite::wrap(self.client_x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-clientY
|
||||||
|
fn ClientY(&self) -> Finite<f64> {
|
||||||
|
Finite::wrap(self.client_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-clientX
|
||||||
|
fn PageX(&self) -> Finite<f64> {
|
||||||
|
Finite::wrap(self.page_x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-Touch-clientY
|
||||||
|
fn PageY(&self) -> Finite<f64> {
|
||||||
|
Finite::wrap(self.page_y)
|
||||||
|
}
|
||||||
|
}
|
117
components/script/dom/touchevent.rs
Normal file
117
components/script/dom/touchevent.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::TouchEventBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::TouchEventBinding::TouchEventMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
|
||||||
|
use dom::bindings::conversions::Castable;
|
||||||
|
use dom::bindings::global::GlobalRef;
|
||||||
|
use dom::bindings::js::{JS, MutHeap, Root};
|
||||||
|
use dom::bindings::utils::reflect_dom_object;
|
||||||
|
use dom::event::{EventBubbles, EventCancelable};
|
||||||
|
use dom::touchlist::TouchList;
|
||||||
|
use dom::uievent::UIEvent;
|
||||||
|
use dom::window::Window;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use util::str::DOMString;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct TouchEvent {
|
||||||
|
uievent: UIEvent,
|
||||||
|
touches: MutHeap<JS<TouchList>>,
|
||||||
|
target_touches: MutHeap<JS<TouchList>>,
|
||||||
|
changed_touches: MutHeap<JS<TouchList>>,
|
||||||
|
alt_key: Cell<bool>,
|
||||||
|
meta_key: Cell<bool>,
|
||||||
|
ctrl_key: Cell<bool>,
|
||||||
|
shift_key: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchEvent {
|
||||||
|
fn new_inherited(touches: &TouchList,
|
||||||
|
changed_touches: &TouchList,
|
||||||
|
target_touches: &TouchList) -> TouchEvent {
|
||||||
|
TouchEvent {
|
||||||
|
uievent: UIEvent::new_inherited(),
|
||||||
|
touches: MutHeap::new(touches),
|
||||||
|
target_touches: MutHeap::new(target_touches),
|
||||||
|
changed_touches: MutHeap::new(changed_touches),
|
||||||
|
ctrl_key: Cell::new(false),
|
||||||
|
shift_key: Cell::new(false),
|
||||||
|
alt_key: Cell::new(false),
|
||||||
|
meta_key: Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_uninitialized(window: &Window,
|
||||||
|
touches: &TouchList,
|
||||||
|
changed_touches: &TouchList,
|
||||||
|
target_touches: &TouchList) -> Root<TouchEvent> {
|
||||||
|
reflect_dom_object(box TouchEvent::new_inherited(touches, changed_touches, target_touches),
|
||||||
|
GlobalRef::Window(window),
|
||||||
|
TouchEventBinding::Wrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(window: &Window,
|
||||||
|
type_: DOMString,
|
||||||
|
canBubble: EventBubbles,
|
||||||
|
cancelable: EventCancelable,
|
||||||
|
view: Option<&Window>,
|
||||||
|
detail: i32,
|
||||||
|
touches: &TouchList,
|
||||||
|
changed_touches: &TouchList,
|
||||||
|
target_touches: &TouchList,
|
||||||
|
ctrlKey: bool,
|
||||||
|
altKey: bool,
|
||||||
|
shiftKey: bool,
|
||||||
|
metaKey: bool) -> Root<TouchEvent> {
|
||||||
|
let ev = TouchEvent::new_uninitialized(window, touches, changed_touches, target_touches);
|
||||||
|
ev.upcast::<UIEvent>().InitUIEvent(type_,
|
||||||
|
canBubble == EventBubbles::Bubbles,
|
||||||
|
cancelable == EventCancelable::Cancelable,
|
||||||
|
view, detail);
|
||||||
|
ev.ctrl_key.set(ctrlKey);
|
||||||
|
ev.alt_key.set(altKey);
|
||||||
|
ev.shift_key.set(shiftKey);
|
||||||
|
ev.meta_key.set(metaKey);
|
||||||
|
ev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TouchEventMethods for &'a TouchEvent {
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEvent-ctrlKey
|
||||||
|
fn CtrlKey(&self) -> bool {
|
||||||
|
self.ctrl_key.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEvent-shiftKey
|
||||||
|
fn ShiftKey(&self) -> bool {
|
||||||
|
self.shift_key.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEvent-altKey
|
||||||
|
fn AltKey(&self) -> bool {
|
||||||
|
self.alt_key.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEvent-metaKey
|
||||||
|
fn MetaKey(&self) -> bool {
|
||||||
|
self.meta_key.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEventInit-touches
|
||||||
|
fn Touches(&self) -> Root<TouchList> {
|
||||||
|
self.touches.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEvent-targetTouches
|
||||||
|
fn TargetTouches(&self) -> Root<TouchList> {
|
||||||
|
self.target_touches.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchEvent-changedTouches
|
||||||
|
fn ChangedTouches(&self) -> Root<TouchList> {
|
||||||
|
self.changed_touches.get()
|
||||||
|
}
|
||||||
|
}
|
50
components/script/dom/touchlist.rs
Normal file
50
components/script/dom/touchlist.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::TouchListBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::TouchListBinding::TouchListMethods;
|
||||||
|
use dom::bindings::global::GlobalRef;
|
||||||
|
use dom::bindings::js::{JS, Root};
|
||||||
|
use dom::bindings::utils::{Reflector, reflect_dom_object};
|
||||||
|
use dom::touch::Touch;
|
||||||
|
use dom::window::Window;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct TouchList {
|
||||||
|
reflector_: Reflector,
|
||||||
|
touches: Vec<JS<Touch>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchList {
|
||||||
|
fn new_inherited(touches: &[&Touch]) -> TouchList {
|
||||||
|
TouchList {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
touches: touches.iter().map(|touch| JS::from_ref(*touch)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(window: &Window, touches: &[&Touch]) -> Root<TouchList> {
|
||||||
|
reflect_dom_object(box TouchList::new_inherited(touches),
|
||||||
|
GlobalRef::Window(window), TouchListBinding::Wrap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchListMethods for TouchList {
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchList-length
|
||||||
|
fn Length(&self) -> u32 {
|
||||||
|
self.touches.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchList-item-getter-Touch-unsigned-long-index
|
||||||
|
fn Item(&self, index: u32) -> Option<Root<Touch>> {
|
||||||
|
self.touches.get(index as usize).map(JS::root)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/touch-events/#widl-TouchList-item-getter-Touch-unsigned-long-index
|
||||||
|
fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Touch>> {
|
||||||
|
let item = self.Item(index);
|
||||||
|
*found = item.is_some();
|
||||||
|
item
|
||||||
|
}
|
||||||
|
}
|
|
@ -159,3 +159,16 @@ partial interface Document {
|
||||||
// Tracking issue for document.all: https://github.com/servo/servo/issues/7396
|
// Tracking issue for document.all: https://github.com/servo/servo/issues/7396
|
||||||
// readonly attribute HTMLAllCollection all;
|
// readonly attribute HTMLAllCollection all;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// http://w3c.github.io/touch-events/#idl-def-Document
|
||||||
|
partial interface Document {
|
||||||
|
Touch createTouch(Window/*Proxy*/ view,
|
||||||
|
EventTarget target,
|
||||||
|
long identifier,
|
||||||
|
double pageX,
|
||||||
|
double pageY,
|
||||||
|
double screenX,
|
||||||
|
double screenY);
|
||||||
|
// FIXME (#8159):
|
||||||
|
// TouchList createTouchList(Touch... touches);
|
||||||
|
};
|
||||||
|
|
21
components/script/dom/webidls/Touch.webidl
Normal file
21
components/script/dom/webidls/Touch.webidl
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// http://w3c.github.io/touch-events/#idl-def-Touch
|
||||||
|
|
||||||
|
interface Touch {
|
||||||
|
readonly attribute long identifier;
|
||||||
|
readonly attribute EventTarget target;
|
||||||
|
readonly attribute double screenX;
|
||||||
|
readonly attribute double screenY;
|
||||||
|
readonly attribute double clientX;
|
||||||
|
readonly attribute double clientY;
|
||||||
|
readonly attribute double pageX;
|
||||||
|
readonly attribute double pageY;
|
||||||
|
// readonly attribute float radiusX;
|
||||||
|
// readonly attribute float radiusY;
|
||||||
|
// readonly attribute float rotationAngle;
|
||||||
|
// readonly attribute float force;
|
||||||
|
};
|
16
components/script/dom/webidls/TouchEvent.webidl
Normal file
16
components/script/dom/webidls/TouchEvent.webidl
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// http://w3c.github.io/touch-events/#idl-def-TouchEvent
|
||||||
|
|
||||||
|
interface TouchEvent : UIEvent {
|
||||||
|
readonly attribute TouchList touches;
|
||||||
|
readonly attribute TouchList targetTouches;
|
||||||
|
readonly attribute TouchList changedTouches;
|
||||||
|
readonly attribute boolean altKey;
|
||||||
|
readonly attribute boolean metaKey;
|
||||||
|
readonly attribute boolean ctrlKey;
|
||||||
|
readonly attribute boolean shiftKey;
|
||||||
|
};
|
11
components/script/dom/webidls/TouchList.webidl
Normal file
11
components/script/dom/webidls/TouchList.webidl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// http://w3c.github.io/touch-events/#idl-def-TouchList
|
||||||
|
|
||||||
|
interface TouchList {
|
||||||
|
readonly attribute unsigned long length;
|
||||||
|
getter Touch? item (unsigned long index);
|
||||||
|
};
|
|
@ -60,7 +60,7 @@ use layout_interface::{ReflowQueryType};
|
||||||
use layout_interface::{self, LayoutChan, NewLayoutTaskInfo, ReflowGoal, ScriptLayoutChan};
|
use layout_interface::{self, LayoutChan, NewLayoutTaskInfo, ReflowGoal, ScriptLayoutChan};
|
||||||
use libc;
|
use libc;
|
||||||
use mem::heap_size_of_self_and_children;
|
use mem::heap_size_of_self_and_children;
|
||||||
use msg::compositor_msg::{LayerId, ScriptToCompositorMsg};
|
use msg::compositor_msg::{EventResult, LayerId, ScriptToCompositorMsg};
|
||||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||||
use msg::constellation_msg::{ConstellationChan, FocusType, LoadData};
|
use msg::constellation_msg::{ConstellationChan, FocusType, LoadData};
|
||||||
use msg::constellation_msg::{MozBrowserEvent, PipelineExitType, PipelineId};
|
use msg::constellation_msg::{MozBrowserEvent, PipelineExitType, PipelineId};
|
||||||
|
@ -79,6 +79,7 @@ use profile_traits::time::{self, ProfilerCategory, profile};
|
||||||
use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
|
use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
|
||||||
use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
|
use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
|
||||||
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
|
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
|
||||||
|
use script_traits::CompositorEvent::{TouchDownEvent, TouchMoveEvent, TouchUpEvent};
|
||||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||||
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
|
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
|
||||||
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
|
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
|
||||||
|
@ -1784,6 +1785,27 @@ impl ScriptTask {
|
||||||
std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets);
|
std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TouchDownEvent(identifier, point) => {
|
||||||
|
let default_action_allowed =
|
||||||
|
self.handle_touch_event(pipeline_id, identifier, point, "touchstart");
|
||||||
|
if default_action_allowed {
|
||||||
|
// TODO: Wait to see if preventDefault is called on the first touchmove event.
|
||||||
|
self.compositor.borrow_mut().send(ScriptToCompositorMsg::TouchEventProcessed(
|
||||||
|
EventResult::DefaultAllowed)).unwrap();
|
||||||
|
} else {
|
||||||
|
self.compositor.borrow_mut().send(ScriptToCompositorMsg::TouchEventProcessed(
|
||||||
|
EventResult::DefaultPrevented)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TouchMoveEvent(identifier, point) => {
|
||||||
|
self.handle_touch_event(pipeline_id, identifier, point, "touchmove");
|
||||||
|
}
|
||||||
|
|
||||||
|
TouchUpEvent(identifier, point) => {
|
||||||
|
self.handle_touch_event(pipeline_id, identifier, point, "touchend");
|
||||||
|
}
|
||||||
|
|
||||||
KeyEvent(key, state, modifiers) => {
|
KeyEvent(key, state, modifiers) => {
|
||||||
let page = get_page(&self.root_page(), pipeline_id);
|
let page = get_page(&self.root_page(), pipeline_id);
|
||||||
let document = page.document();
|
let document = page.document();
|
||||||
|
@ -1803,6 +1825,17 @@ impl ScriptTask {
|
||||||
document.r().handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type);
|
document.r().handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_touch_event(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
identifier: i32,
|
||||||
|
point: Point2D<f32>,
|
||||||
|
event_name: &str) -> bool {
|
||||||
|
let page = get_page(&self.root_page(), pipeline_id);
|
||||||
|
let document = page.document();
|
||||||
|
document.r().handle_touch_event(self.js_runtime.rt(), identifier, point,
|
||||||
|
event_name.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
|
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
|
||||||
/// The entry point for content to notify that a new load has been requested
|
/// The entry point for content to notify that a new load has been requested
|
||||||
/// for the given pipeline (specifically the "navigate" algorithm).
|
/// for the given pipeline (specifically the "navigate" algorithm).
|
||||||
|
|
|
@ -150,7 +150,7 @@ pub enum ConstellationControlMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The mouse button involved in the event.
|
/// The mouse button involved in the event.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum MouseButton {
|
pub enum MouseButton {
|
||||||
/// The left mouse button.
|
/// The left mouse button.
|
||||||
Left,
|
Left,
|
||||||
|
@ -172,6 +172,12 @@ pub enum CompositorEvent {
|
||||||
MouseUpEvent(MouseButton, Point2D<f32>),
|
MouseUpEvent(MouseButton, Point2D<f32>),
|
||||||
/// The mouse was moved over a point.
|
/// The mouse was moved over a point.
|
||||||
MouseMoveEvent(Point2D<f32>),
|
MouseMoveEvent(Point2D<f32>),
|
||||||
|
/// A touch began at a point.
|
||||||
|
TouchDownEvent(i32, Point2D<f32>),
|
||||||
|
/// A touch was moved over a point.
|
||||||
|
TouchMoveEvent(i32, Point2D<f32>),
|
||||||
|
/// A touch ended at a point.
|
||||||
|
TouchUpEvent(i32, Point2D<f32>),
|
||||||
/// A key was pressed.
|
/// A key was pressed.
|
||||||
KeyEvent(Key, KeyState, KeyModifiers),
|
KeyEvent(Key, KeyState, KeyModifiers),
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,9 @@ pub struct Opts {
|
||||||
/// Whether to run absolute position calculation and display list construction in parallel.
|
/// Whether to run absolute position calculation and display list construction in parallel.
|
||||||
pub parallel_display_list_building: bool,
|
pub parallel_display_list_building: bool,
|
||||||
|
|
||||||
|
/// Translate mouse input into touch events.
|
||||||
|
pub convert_mouse_to_touch: bool,
|
||||||
|
|
||||||
/// True to exit after the page load (`-x`).
|
/// True to exit after the page load (`-x`).
|
||||||
pub exit_after_load: bool,
|
pub exit_after_load: bool,
|
||||||
|
|
||||||
|
@ -247,6 +250,9 @@ pub struct DebugOptions {
|
||||||
/// Build display lists in parallel.
|
/// Build display lists in parallel.
|
||||||
pub parallel_display_list_building: bool,
|
pub parallel_display_list_building: bool,
|
||||||
|
|
||||||
|
/// Translate mouse input into touch events.
|
||||||
|
pub convert_mouse_to_touch: bool,
|
||||||
|
|
||||||
/// Replace unpaires surrogates in DOM strings with U+FFFD.
|
/// Replace unpaires surrogates in DOM strings with U+FFFD.
|
||||||
/// See https://github.com/servo/servo/issues/6564
|
/// See https://github.com/servo/servo/issues/6564
|
||||||
pub replace_surrogates: bool,
|
pub replace_surrogates: bool,
|
||||||
|
@ -260,6 +266,12 @@ impl DebugOptions {
|
||||||
pub fn new(debug_string: &str) -> Result<DebugOptions, &str> {
|
pub fn new(debug_string: &str) -> Result<DebugOptions, &str> {
|
||||||
let mut debug_options = DebugOptions::default();
|
let mut debug_options = DebugOptions::default();
|
||||||
|
|
||||||
|
// FIXME: Glutin currently converts touch input to mouse events on Android.
|
||||||
|
// Convert it back to touch events.
|
||||||
|
if cfg!(target_os = "android") {
|
||||||
|
debug_options.convert_mouse_to_touch = true;
|
||||||
|
}
|
||||||
|
|
||||||
for option in debug_string.split(',') {
|
for option in debug_string.split(',') {
|
||||||
match option {
|
match option {
|
||||||
"help" => debug_options.help = true,
|
"help" => debug_options.help = true,
|
||||||
|
@ -283,6 +295,7 @@ impl DebugOptions {
|
||||||
"validate-display-list-geometry" => debug_options.validate_display_list_geometry = true,
|
"validate-display-list-geometry" => debug_options.validate_display_list_geometry = true,
|
||||||
"disable-share-style-cache" => debug_options.disable_share_style_cache = true,
|
"disable-share-style-cache" => debug_options.disable_share_style_cache = true,
|
||||||
"parallel-display-list-building" => debug_options.parallel_display_list_building = true,
|
"parallel-display-list-building" => debug_options.parallel_display_list_building = true,
|
||||||
|
"convert-mouse-to-touch" => debug_options.convert_mouse_to_touch = true,
|
||||||
"replace-surrogates" => debug_options.replace_surrogates = true,
|
"replace-surrogates" => debug_options.replace_surrogates = true,
|
||||||
"gc-profile" => debug_options.gc_profile = true,
|
"gc-profile" => debug_options.gc_profile = true,
|
||||||
"" => {},
|
"" => {},
|
||||||
|
@ -326,6 +339,7 @@ pub fn print_debug_usage(app: &str) -> ! {
|
||||||
print_option("replace-surrogates", "Replace unpaires surrogates in DOM strings with U+FFFD. \
|
print_option("replace-surrogates", "Replace unpaires surrogates in DOM strings with U+FFFD. \
|
||||||
See https://github.com/servo/servo/issues/6564");
|
See https://github.com/servo/servo/issues/6564");
|
||||||
print_option("gc-profile", "Log GC passes and their durations.");
|
print_option("gc-profile", "Log GC passes and their durations.");
|
||||||
|
print_option("convert-mouse-to-touch", "Send touch events instead of mouse events");
|
||||||
|
|
||||||
println!("");
|
println!("");
|
||||||
|
|
||||||
|
@ -422,6 +436,7 @@ pub fn default_opts() -> Opts {
|
||||||
sniff_mime_types: false,
|
sniff_mime_types: false,
|
||||||
disable_share_style_cache: false,
|
disable_share_style_cache: false,
|
||||||
parallel_display_list_building: false,
|
parallel_display_list_building: false,
|
||||||
|
convert_mouse_to_touch: false,
|
||||||
exit_after_load: false,
|
exit_after_load: false,
|
||||||
no_native_titlebar: false,
|
no_native_titlebar: false,
|
||||||
}
|
}
|
||||||
|
@ -628,6 +643,7 @@ pub fn from_cmdline_args(args: &[String]) {
|
||||||
sniff_mime_types: opt_match.opt_present("sniff-mime-types"),
|
sniff_mime_types: opt_match.opt_present("sniff-mime-types"),
|
||||||
disable_share_style_cache: debug_options.disable_share_style_cache,
|
disable_share_style_cache: debug_options.disable_share_style_cache,
|
||||||
parallel_display_list_building: debug_options.parallel_display_list_building,
|
parallel_display_list_building: debug_options.parallel_display_list_building,
|
||||||
|
convert_mouse_to_touch: debug_options.convert_mouse_to_touch,
|
||||||
exit_after_load: opt_match.opt_present("x"),
|
exit_after_load: opt_match.opt_present("x"),
|
||||||
no_native_titlebar: opt_match.opt_present("b"),
|
no_native_titlebar: opt_match.opt_present("b"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,8 @@ skip: true
|
||||||
skip: false
|
skip: false
|
||||||
[url]
|
[url]
|
||||||
skip: false
|
skip: false
|
||||||
|
[touch-events]
|
||||||
|
skip: false
|
||||||
[workers]
|
[workers]
|
||||||
skip: false
|
skip: false
|
||||||
[XMLHttpRequest]
|
[XMLHttpRequest]
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
[create-touch-touchlist.html]
|
||||||
|
type: testharness
|
||||||
|
[document.createTouchList exists and correctly creates a TouchList from zero Touch objects]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[document.createTouchList exists and correctly creates a TouchList from a single Touch]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[document.createTouchList exists and correctly creates a TouchList from two Touch objects]
|
||||||
|
expected: FAIL
|
|
@ -198,6 +198,9 @@ var interfaceNamesInGlobalScope = [
|
||||||
"Text",
|
"Text",
|
||||||
"TextDecoder",
|
"TextDecoder",
|
||||||
"TextEncoder",
|
"TextEncoder",
|
||||||
|
"Touch",
|
||||||
|
"TouchEvent",
|
||||||
|
"TouchList",
|
||||||
"TreeWalker",
|
"TreeWalker",
|
||||||
"UIEvent",
|
"UIEvent",
|
||||||
"URL",
|
"URL",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue