diff options
-rw-r--r-- | components/compositing/compositor.rs | 18 | ||||
-rw-r--r-- | components/compositing/compositor_layer.rs | 22 | ||||
-rw-r--r-- | components/compositing/windowing.rs | 5 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 3 | ||||
-rw-r--r-- | components/script/dom/document.rs | 69 | ||||
-rw-r--r-- | components/script/dom/forcetouchevent.rs | 59 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/webidls/ForceTouchEvent.webidl | 35 | ||||
-rw-r--r-- | components/script/script_thread.rs | 8 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 13 | ||||
-rw-r--r-- | ports/glutin/window.rs | 18 | ||||
-rw-r--r-- | python/tidy.py | 1 | ||||
-rw-r--r-- | resources/prefs.json | 1 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/interfaces.html | 1 |
14 files changed, 246 insertions, 8 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 9c35ed60463..ad5fc71b765 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -37,7 +37,7 @@ use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest}; use profile_traits::time::{self, ProfilerCategory, profile}; use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent}; use script_traits::{AnimationState, ConstellationControlMsg, LayoutControlMsg}; -use script_traits::{MouseButton, MouseEventType, TouchEventType, TouchId}; +use script_traits::{MouseButton, MouseEventType, TouchpadPressurePhase, TouchEventType, TouchId}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{HashMap, HashSet}; use std::fs::File; @@ -50,8 +50,8 @@ use time::{precise_time_ns, precise_time_s}; use touch::{TouchHandler, TouchAction}; use url::Url; use util::geometry::{PagePx, ScreenPx, ViewportPx}; -use util::opts; use util::print_tree::PrintTree; +use util::{opts, prefs}; use webrender; use webrender_traits::{self, ScrollEventPhase}; use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}; @@ -1225,6 +1225,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.on_navigation_window_event(direction); } + WindowEvent::TouchpadPressure(cursor, pressure, stage) => { + self.on_touchpad_pressure_event(cursor, pressure, stage); + } + WindowEvent::KeyEvent(key, state, modifiers) => { self.on_key_event(key, state, modifiers); } @@ -1701,6 +1705,16 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.constellation_chan.send(ConstellationMsg::Navigate(None, direction)).unwrap() } + fn on_touchpad_pressure_event(&self, cursor: TypedPoint2D<DevicePixel, f32>, pressure: f32, + phase: TouchpadPressurePhase) { + if prefs::get_pref("dom.forcetouch.enabled").as_boolean().unwrap() { + match self.find_topmost_layer_at_point(cursor / self.scene.scale) { + Some(result) => result.layer.send_touchpad_pressure_event(self, result.point, pressure, phase), + None => {}, + } + } + } + fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) { self.constellation_chan.send(ConstellationMsg::KeyEvent(key, state, modifiers)).unwrap() } diff --git a/components/compositing/compositor_layer.rs b/components/compositing/compositor_layer.rs index 8246f973b66..9f5e0480e1b 100644 --- a/components/compositing/compositor_layer.rs +++ b/components/compositing/compositor_layer.rs @@ -14,9 +14,10 @@ use layers::geometry::LayerPixel; use layers::layers::{Layer, LayerBufferSet}; use msg::constellation_msg::PipelineId; use script_traits::CompositorEvent; -use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent}; +use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchpadPressureEvent}; use script_traits::ConstellationControlMsg; use script_traits::MouseEventType; +use script_traits::TouchpadPressurePhase; use std::rc::Rc; use windowing::{MouseWindowEvent, WindowMethods}; @@ -139,6 +140,13 @@ pub trait CompositorLayer { event: CompositorEvent) where Window: WindowMethods; + fn send_touchpad_pressure_event<Window>(&self, + compositor: &IOCompositor<Window>, + cursor: TypedPoint2D<LayerPixel, f32>, + pressure: f32, + phase: TouchpadPressurePhase) + where Window: WindowMethods; + fn clamp_scroll_offset_and_scroll_layer(&self, new_offset: TypedPoint2D<LayerPixel, f32>) -> ScrollEventResult; @@ -399,6 +407,18 @@ impl CompositorLayer for Layer<CompositorData> { } } + fn send_touchpad_pressure_event<Window>(&self, + compositor: &IOCompositor<Window>, + cursor: TypedPoint2D<LayerPixel, f32>, + pressure: f32, + phase: TouchpadPressurePhase) + where Window: WindowMethods { + if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) { + let message = TouchpadPressureEvent(cursor.to_untyped(), pressure, phase); + let _ = pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)); + } + } + fn scroll_layer_and_all_child_layers(&self, new_offset: TypedPoint2D<LayerPixel, f32>) -> bool { let mut result = false; diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 8719db56a03..b07298d3fc3 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -13,7 +13,7 @@ use layers::geometry::DevicePixel; use layers::platform::surface::NativeDisplay; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use net_traits::net_error_list::NetError; -use script_traits::{MouseButton, TouchEventType, TouchId}; +use script_traits::{MouseButton, TouchpadPressurePhase, TouchEventType, TouchId}; use std::fmt::{Debug, Error, Formatter}; use style_traits::cursor::Cursor; use url::Url; @@ -50,6 +50,8 @@ pub enum WindowEvent { InitializeCompositing, /// Sent when the window is resized. Resize(TypedSize2D<DevicePixel, u32>), + /// Touchpad Pressure + TouchpadPressure(TypedPoint2D<DevicePixel, f32>, f32, TouchpadPressurePhase), /// Sent when you want to override the viewport. Viewport(TypedPoint2D<DevicePixel, u32>, TypedSize2D<DevicePixel, u32>), /// Sent when a new URL is to be loaded. @@ -84,6 +86,7 @@ impl Debug for WindowEvent { WindowEvent::Refresh => write!(f, "Refresh"), WindowEvent::InitializeCompositing => write!(f, "InitializeCompositing"), WindowEvent::Resize(..) => write!(f, "Resize"), + WindowEvent::TouchpadPressure(..) => write!(f, "TouchpadPressure"), WindowEvent::Viewport(..) => write!(f, "Viewport"), WindowEvent::KeyEvent(..) => write!(f, "Key"), WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"), diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 2491058750c..a23a5452f1c 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -64,7 +64,7 @@ use net_traits::storage_thread::StorageType; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_thread::ScriptChan; -use script_traits::{LayoutMsg, ScriptMsg, TimerEventId, TimerSource, UntrustedNodeAddress}; +use script_traits::{LayoutMsg, ScriptMsg, TimerEventId, TimerSource, TouchpadPressurePhase, UntrustedNodeAddress}; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::boxed::FnBox; @@ -319,6 +319,7 @@ no_jsmanaged_fields!(AttrIdentifier); no_jsmanaged_fields!(AttrValue); no_jsmanaged_fields!(ElementSnapshot); no_jsmanaged_fields!(HttpsState); +no_jsmanaged_fields!(TouchpadPressurePhase); impl JSTraceable for ConstellationChan<ScriptMsg> { #[inline] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 703a7c90572..c99099e04f9 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -41,6 +41,7 @@ use dom::element::{Element, ElementCreator}; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; use dom::focusevent::FocusEvent; +use dom::forcetouchevent::ForceTouchEvent; use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlappletelement::HTMLAppletElement; use dom::htmlareaelement::HTMLAreaElement; @@ -97,7 +98,7 @@ use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, ScriptC use script_traits::UntrustedNodeAddress; use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{ScriptMsg as ConstellationMsg, ScriptToCompositorMsg}; -use script_traits::{TouchEventType, TouchId}; +use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId}; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::boxed::FnBox; @@ -218,6 +219,7 @@ pub struct Document { css_errors_store: DOMRefCell<Vec<CSSError>>, /// https://html.spec.whatwg.org/multipage/#concept-document-https-state https_state: Cell<HttpsState>, + touchpad_pressure_phase: Cell<TouchpadPressurePhase>, } #[derive(JSTraceable, HeapSizeOf)] @@ -732,6 +734,70 @@ impl Document { ReflowReason::MouseEvent); } + pub fn handle_touchpad_pressure_event(&self, + js_runtime: *mut JSRuntime, + client_point: Point2D<f32>, + pressure: f32, + phase_now: TouchpadPressurePhase) { + + let phase_before = self.touchpad_pressure_phase.get(); + self.touchpad_pressure_phase.set(phase_now); + + if phase_before == TouchpadPressurePhase::BeforeClick && + phase_now == TouchpadPressurePhase::BeforeClick { + return; + } + + let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32, + client_point.y + self.window.PageYOffset() as f32); + let node = match self.window.hit_test_query(page_point, false) { + Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address), + None => return + }; + + let el = match node.downcast::<Element>() { + Some(el) => Root::from_ref(el), + None => { + let parent = node.GetParentNode(); + match parent.and_then(Root::downcast::<Element>) { + Some(parent) => parent, + None => return + } + }, + }; + + let node = el.upcast::<Node>(); + let target = node.upcast(); + + let force = match phase_now { + TouchpadPressurePhase::BeforeClick => pressure, + TouchpadPressurePhase::AfterFirstClick => 1. + pressure, + TouchpadPressurePhase::AfterSecondClick => 2. + pressure, + }; + + if phase_now != TouchpadPressurePhase::BeforeClick { + self.fire_forcetouch_event("servomouseforcechanged".to_owned(), target, force); + } + + if phase_before != TouchpadPressurePhase::AfterSecondClick && + phase_now == TouchpadPressurePhase::AfterSecondClick { + self.fire_forcetouch_event("servomouseforcedown".to_owned(), target, force); + } + + if phase_before == TouchpadPressurePhase::AfterSecondClick && + phase_now != TouchpadPressurePhase::AfterSecondClick { + self.fire_forcetouch_event("servomouseforceup".to_owned(), target, force); + } + } + + fn fire_forcetouch_event(&self, event_name: String, target: &EventTarget, force: f32) { + let force_event = ForceTouchEvent::new(&self.window, + DOMString::from(event_name), + force); + let event = force_event.upcast::<Event>(); + event.fire(target); + } + pub fn fire_mouse_event(&self, client_point: Point2D<f32>, target: &EventTarget, event_name: String) { let client_x = client_point.x.to_i32().unwrap_or(0); let client_y = client_point.y.to_i32().unwrap_or(0); @@ -1593,6 +1659,7 @@ impl Document { dom_complete: Cell::new(Default::default()), css_errors_store: DOMRefCell::new(vec![]), https_state: Cell::new(HttpsState::None), + touchpad_pressure_phase: Cell::new(TouchpadPressurePhase::BeforeClick), } } diff --git a/components/script/dom/forcetouchevent.rs b/components/script/dom/forcetouchevent.rs new file mode 100644 index 00000000000..b0fd6376760 --- /dev/null +++ b/components/script/dom/forcetouchevent.rs @@ -0,0 +1,59 @@ +/* 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::ForceTouchEventBinding; +use dom::bindings::codegen::Bindings::ForceTouchEventBinding::ForceTouchEventMethods; +use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; +use dom::bindings::global::GlobalRef; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{Root}; +use dom::bindings::num::Finite; +use dom::bindings::reflector::reflect_dom_object; +use dom::uievent::UIEvent; +use dom::window::Window; +use util::str::DOMString; + +#[dom_struct] +pub struct ForceTouchEvent { + uievent: UIEvent, + force: f32, +} + +impl ForceTouchEvent { + fn new_inherited(force: f32) -> ForceTouchEvent { + ForceTouchEvent { + uievent: UIEvent::new_inherited(), + force: force, + } + } + + pub fn new(window: &Window, + type_: DOMString, + force: f32) -> Root<ForceTouchEvent> { + let event = box ForceTouchEvent::new_inherited(force); + let ev = reflect_dom_object(event, GlobalRef::Window(window), ForceTouchEventBinding::Wrap); + ev.upcast::<UIEvent>().InitUIEvent(type_, true, true, Some(window), 0); + ev + } +} + +impl<'a> ForceTouchEventMethods for &'a ForceTouchEvent { + + fn ServoForce(&self) -> Finite<f32> { + Finite::wrap(self.force) + } + + fn SERVO_FORCE_AT_MOUSE_DOWN(&self) -> Finite<f32> { + Finite::wrap(1.0) + } + + fn SERVO_FORCE_AT_FORCE_MOUSE_DOWN(&self) -> Finite<f32> { + Finite::wrap(2.0) + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.uievent.IsTrusted() + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 662f41dc7a9..36921d8be89 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -264,6 +264,7 @@ pub mod file; pub mod filelist; pub mod filereader; pub mod focusevent; +pub mod forcetouchevent; pub mod formdata; pub mod htmlanchorelement; pub mod htmlappletelement; diff --git a/components/script/dom/webidls/ForceTouchEvent.webidl b/components/script/dom/webidls/ForceTouchEvent.webidl new file mode 100644 index 00000000000..a5f8ceef9a4 --- /dev/null +++ b/components/script/dom/webidls/ForceTouchEvent.webidl @@ -0,0 +1,35 @@ +/* -*- 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/. */ + +// https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/SafariJSProgTopics/RespondingtoForceTouchEventsfromJavaScript.html + +/** + * Events: (copy/paste from apple.com) + * + * webkitmouseforcewillbegin: This event occurs immediately before the mousedown event. It allows you to + * prevent the default system behavior, such as displaying a dictionary window when force clicking on a + * word, in order to perform a custom action instead. To prevent the default system behavior, call the + * preventDefault() method on the event. + * webkitmouseforcedown: This event occurs after the mousedown event, once enough force has been applied + * to register as a force click. The user receives haptic feedback representing the force click when this + * event occurs. + * webkitmouseforceup: This event occurs after a webkitmouseforcedown event, once enough force has been + * released to exit the force click operation. The user receives haptic feedback representing the exit + * from force click when this event occurs. + * webkitmouseforcechanged: This event occurs whenever a change in trackpad force is detected between the + * mousedown and mouseup events. + * + */ + + +[Pref="dom.forcetouch.enabled"] +interface ForceTouchEvent : UIEvent { + // Represents the amount of force required to perform a regular click. + readonly attribute float SERVO_FORCE_AT_MOUSE_DOWN; + // Represents the force required to perform a force click. + readonly attribute float SERVO_FORCE_AT_FORCE_MOUSE_DOWN; + // force level + readonly attribute float servoForce; +}; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 5cd38e80532..6726a6ca4e2 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -79,7 +79,7 @@ use parse::xml::{self, parse_xml}; use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, ProfilerCategory, profile}; use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; -use script_traits::CompositorEvent::{TouchEvent}; +use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent}; use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult}; use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent, NewLayoutInfo}; use script_traits::{LayoutMsg, OpaqueScriptLayoutChannel, ScriptMsg as ConstellationMsg}; @@ -1969,6 +1969,12 @@ impl ScriptThread { } } + TouchpadPressureEvent(point, pressure, phase) => { + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document(); + document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase); + } + KeyEvent(key, state, modifiers) => { let page = get_page(&self.root_page(), pipeline_id); let document = page.document(); diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index ac3632c6a08..7175536f280 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -227,10 +227,23 @@ pub enum CompositorEvent { MouseMoveEvent(Option<Point2D<f32>>), /// A touch event was generated with a touch ID and location. TouchEvent(TouchEventType, TouchId, Point2D<f32>), + /// Touchpad pressure event + TouchpadPressureEvent(Point2D<f32>, f32, TouchpadPressurePhase), /// A key was pressed. KeyEvent(Key, KeyState, KeyModifiers), } +/// Touchpad pressure phase for TouchpadPressureEvent. +#[derive(Copy, Clone, HeapSizeOf, PartialEq, Deserialize, Serialize)] +pub enum TouchpadPressurePhase { + /// Pressure before a regular click. + BeforeClick, + /// Pressure after a regular click. + AfterFirstClick, + /// Pressure after a "forceTouch" click + AfterSecondClick, +} + /// An opaque wrapper around script<->layout channels to avoid leaking message types into /// crates that don't need to know about them. pub struct OpaqueScriptLayoutChannel(pub (Box<Any + Send>, Box<Any + Send>)); diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index c8534710797..ae7602daafc 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -20,7 +20,7 @@ use layers::platform::surface::NativeDisplay; use msg::constellation_msg::{KeyState, NONE, CONTROL, SHIFT, ALT, SUPER}; use msg::constellation_msg::{self, Key}; use net_traits::net_error_list::NetError; -use script_traits::TouchEventType; +use script_traits::{TouchEventType, TouchpadPressurePhase}; use std::cell::{Cell, RefCell}; use std::os::raw::c_void; use std::rc::Rc; @@ -241,6 +241,12 @@ impl Window { let point = Point2D::typed(touch.location.0 as f32, touch.location.1 as f32); self.event_queue.borrow_mut().push(WindowEvent::Touch(phase, id, point)); } + Event::TouchpadPressure(pressure, stage) => { + let m = self.mouse_pos.get(); + let point = Point2D::typed(m.x as f32, m.y as f32); + let phase = glutin_pressure_stage_to_touchpad_pressure_phase(stage); + self.event_queue.borrow_mut().push(WindowEvent::TouchpadPressure(point, pressure, phase)); + } Event::Refresh => { self.event_queue.borrow_mut().push(WindowEvent::Refresh); } @@ -812,6 +818,16 @@ fn glutin_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType { } } +fn glutin_pressure_stage_to_touchpad_pressure_phase(stage: i64) -> TouchpadPressurePhase { + if stage < 1 { + TouchpadPressurePhase::BeforeClick + } else if stage < 2 { + TouchpadPressurePhase::AfterFirstClick + } else { + TouchpadPressurePhase::AfterSecondClick + } +} + // These functions aren't actually called. They are here as a link // hack because Skia references them. diff --git a/python/tidy.py b/python/tidy.py index febd9cd6a95..7e160ba6391 100644 --- a/python/tidy.py +++ b/python/tidy.py @@ -39,6 +39,7 @@ ignored_files = [ os.path.join(".", "resources", "hsts_preload.json"), os.path.join(".", "tests", "wpt", "metadata", "MANIFEST.json"), os.path.join(".", "tests", "wpt", "metadata-css", "MANIFEST.json"), + os.path.join(".", "components", "script", "dom", "webidls", "ForceTouchEvent.webidl"), # Hidden files os.path.join(".", "."), ] diff --git a/resources/prefs.json b/resources/prefs.json index 30ec97d3a59..5a9b084a675 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -1,4 +1,5 @@ { + "dom.forcetouch.enabled": false, "dom.mouseevent.which.enabled": false, "dom.mozbrowser.enabled": false, "gfx.webrender.enabled": false, diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index ececea5e35e..7ff68db1034 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -23,6 +23,7 @@ var ecmaGlobals = [ "Float32Array", "Float64Array", "FocusEvent", + "ForceTouchEvent", "Function", "Infinity", "Int16Array", |