accessible/src/jsat/PointerAdapter.jsm

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:21ad9505d4a8
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 /* global Components, XPCOMUtils, Utils, Logger, GestureSettings,
6 GestureTracker */
7 /* exported PointerRelay, PointerAdapter */
8
9 'use strict';
10
11 const Ci = Components.interfaces;
12 const Cu = Components.utils;
13
14 this.EXPORTED_SYMBOLS = ['PointerRelay', 'PointerAdapter']; // jshint ignore:line
15
16 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
17
18 XPCOMUtils.defineLazyModuleGetter(this, 'Utils', // jshint ignore:line
19 'resource://gre/modules/accessibility/Utils.jsm');
20 XPCOMUtils.defineLazyModuleGetter(this, 'Logger', // jshint ignore:line
21 'resource://gre/modules/accessibility/Utils.jsm');
22 XPCOMUtils.defineLazyModuleGetter(this, 'GestureSettings', // jshint ignore:line
23 'resource://gre/modules/accessibility/Gestures.jsm');
24 XPCOMUtils.defineLazyModuleGetter(this, 'GestureTracker', // jshint ignore:line
25 'resource://gre/modules/accessibility/Gestures.jsm');
26
27 // The virtual touch ID generated by a mouse event.
28 const MOUSE_ID = 'mouse';
29 // Synthesized touch ID.
30 const SYNTH_ID = -1;
31
32 let PointerRelay = { // jshint ignore:line
33 /**
34 * A mapping of events we should be intercepting. Entries with a value of
35 * |true| are used for compiling high-level gesture events. Entries with a
36 * value of |false| are cancelled and do not propogate to content.
37 */
38 get _eventsOfInterest() {
39 delete this._eventsOfInterest;
40
41 switch (Utils.widgetToolkit) {
42 case 'gonk':
43 this._eventsOfInterest = {
44 'touchstart' : true,
45 'touchmove' : true,
46 'touchend' : true,
47 'mousedown' : false,
48 'mousemove' : false,
49 'mouseup': false,
50 'click': false };
51 break;
52
53 case 'android':
54 this._eventsOfInterest = {
55 'touchstart' : true,
56 'touchmove' : true,
57 'touchend' : true,
58 'mousemove' : true,
59 'mouseenter' : true,
60 'mouseleave' : true,
61 'mousedown' : false,
62 'mouseup': false,
63 'click': false };
64 break;
65
66 default:
67 // Desktop.
68 this._eventsOfInterest = {
69 'mousemove' : true,
70 'mousedown' : true,
71 'mouseup': true,
72 'click': false
73 };
74 if ('ontouchstart' in Utils.win) {
75 for (let eventType of ['touchstart', 'touchmove', 'touchend']) {
76 this._eventsOfInterest[eventType] = true;
77 }
78 }
79 break;
80 }
81
82 return this._eventsOfInterest;
83 },
84
85 _eventMap: {
86 'touchstart' : 'pointerdown',
87 'mousedown' : 'pointerdown',
88 'mouseenter' : 'pointerdown',
89 'touchmove' : 'pointermove',
90 'mousemove' : 'pointermove',
91 'touchend' : 'pointerup',
92 'mouseup': 'pointerup',
93 'mouseleave': 'pointerup'
94 },
95
96 start: function PointerRelay_start(aOnPointerEvent) {
97 Logger.debug('PointerRelay.start');
98 this.onPointerEvent = aOnPointerEvent;
99 for (let eventType in this._eventsOfInterest) {
100 Utils.win.addEventListener(eventType, this, true, true);
101 }
102 },
103
104 stop: function PointerRelay_stop() {
105 Logger.debug('PointerRelay.stop');
106 delete this.lastPointerMove;
107 delete this.onPointerEvent;
108 for (let eventType in this._eventsOfInterest) {
109 Utils.win.removeEventListener(eventType, this, true, true);
110 }
111 },
112
113 _suppressPointerMove: function PointerRelay__suppressPointerMove(aChangedTouches) {
114 if (!this.lastPointerMove) {
115 return false;
116 }
117 for (let i = 0; i < aChangedTouches.length; ++i) {
118 let touch = aChangedTouches[i];
119 let lastTouch;
120 try {
121 lastTouch = this.lastPointerMove.identifiedTouch ?
122 this.lastPointerMove.identifiedTouch(touch.identifier) :
123 this.lastPointerMove[i];
124 } catch (x) {
125 // Sometimes touch object can't be accessed after page navigation.
126 }
127 if (!lastTouch || lastTouch.target !== touch.target ||
128 Math.hypot(touch.screenX - lastTouch.screenX, touch.screenY -
129 lastTouch.screenY) / Utils.dpi >= GestureSettings.travelThreshold) {
130 return false;
131 }
132 }
133 return true;
134 },
135
136 handleEvent: function PointerRelay_handleEvent(aEvent) {
137 // Don't bother with chrome mouse events.
138 if (Utils.MozBuildApp === 'browser' &&
139 aEvent.view.top instanceof Ci.nsIDOMChromeWindow) {
140 return;
141 }
142 if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN) {
143 // Ignore events that are scripted or clicks from the a11y API.
144 return;
145 }
146
147 let changedTouches = aEvent.changedTouches || [{
148 identifier: MOUSE_ID,
149 screenX: aEvent.screenX,
150 screenY: aEvent.screenY,
151 target: aEvent.target
152 }];
153
154 if (changedTouches.length === 1 &&
155 changedTouches[0].identifier === SYNTH_ID) {
156 return;
157 }
158
159 aEvent.preventDefault();
160 aEvent.stopImmediatePropagation();
161
162 let type = aEvent.type;
163 if (!this._eventsOfInterest[type]) {
164 return;
165 }
166 let pointerType = this._eventMap[type];
167 if (pointerType === 'pointermove') {
168 if (this._suppressPointerMove(changedTouches)) {
169 // Do not fire pointermove more than every POINTERMOVE_THROTTLE.
170 return;
171 }
172 this.lastPointerMove = changedTouches;
173 }
174 this.onPointerEvent({
175 type: pointerType,
176 points: Array.prototype.map.call(changedTouches,
177 function mapTouch(aTouch) {
178 return {
179 identifier: aTouch.identifier,
180 x: aTouch.screenX,
181 y: aTouch.screenY
182 };
183 }
184 )
185 });
186 }
187 };
188
189 this.PointerAdapter = { // jshint ignore:line
190 start: function PointerAdapter_start() {
191 Logger.debug('PointerAdapter.start');
192 GestureTracker.reset();
193 PointerRelay.start(this.handleEvent);
194 },
195
196 stop: function PointerAdapter_stop() {
197 Logger.debug('PointerAdapter.stop');
198 PointerRelay.stop();
199 GestureTracker.reset();
200 },
201
202 handleEvent: function PointerAdapter_handleEvent(aDetail) {
203 let timeStamp = Date.now();
204 GestureTracker.handle(aDetail, timeStamp);
205 }
206 };

mercurial