/* 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/. */ /* global Components, XPCOMUtils, Utils, Logger, GestureSettings, GestureTracker */ /* exported PointerRelay, PointerAdapter */ 'use strict'; const Ci = Components.interfaces; const Cu = Components.utils; this.EXPORTED_SYMBOLS = ['PointerRelay', 'PointerAdapter']; // jshint ignore:line Cu.import('resource://gre/modules/XPCOMUtils.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'Utils', // jshint ignore:line 'resource://gre/modules/accessibility/Utils.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'Logger', // jshint ignore:line 'resource://gre/modules/accessibility/Utils.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'GestureSettings', // jshint ignore:line 'resource://gre/modules/accessibility/Gestures.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'GestureTracker', // jshint ignore:line 'resource://gre/modules/accessibility/Gestures.jsm'); // The virtual touch ID generated by a mouse event. const MOUSE_ID = 'mouse'; // Synthesized touch ID. const SYNTH_ID = -1; let PointerRelay = { // jshint ignore:line /** * A mapping of events we should be intercepting. Entries with a value of * |true| are used for compiling high-level gesture events. Entries with a * value of |false| are cancelled and do not propogate to content. */ get _eventsOfInterest() { delete this._eventsOfInterest; switch (Utils.widgetToolkit) { case 'gonk': this._eventsOfInterest = { 'touchstart' : true, 'touchmove' : true, 'touchend' : true, 'mousedown' : false, 'mousemove' : false, 'mouseup': false, 'click': false }; break; case 'android': this._eventsOfInterest = { 'touchstart' : true, 'touchmove' : true, 'touchend' : true, 'mousemove' : true, 'mouseenter' : true, 'mouseleave' : true, 'mousedown' : false, 'mouseup': false, 'click': false }; break; default: // Desktop. this._eventsOfInterest = { 'mousemove' : true, 'mousedown' : true, 'mouseup': true, 'click': false }; if ('ontouchstart' in Utils.win) { for (let eventType of ['touchstart', 'touchmove', 'touchend']) { this._eventsOfInterest[eventType] = true; } } break; } return this._eventsOfInterest; }, _eventMap: { 'touchstart' : 'pointerdown', 'mousedown' : 'pointerdown', 'mouseenter' : 'pointerdown', 'touchmove' : 'pointermove', 'mousemove' : 'pointermove', 'touchend' : 'pointerup', 'mouseup': 'pointerup', 'mouseleave': 'pointerup' }, start: function PointerRelay_start(aOnPointerEvent) { Logger.debug('PointerRelay.start'); this.onPointerEvent = aOnPointerEvent; for (let eventType in this._eventsOfInterest) { Utils.win.addEventListener(eventType, this, true, true); } }, stop: function PointerRelay_stop() { Logger.debug('PointerRelay.stop'); delete this.lastPointerMove; delete this.onPointerEvent; for (let eventType in this._eventsOfInterest) { Utils.win.removeEventListener(eventType, this, true, true); } }, handleEvent: function PointerRelay_handleEvent(aEvent) { // Don't bother with chrome mouse events. if (Utils.MozBuildApp === 'browser' && aEvent.view.top instanceof Ci.nsIDOMChromeWindow) { return; } if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN || aEvent.isSynthesized) { // Ignore events that are scripted or clicks from the a11y API. return; } let changedTouches = aEvent.changedTouches || [{ identifier: MOUSE_ID, screenX: aEvent.screenX, screenY: aEvent.screenY, target: aEvent.target }]; if (changedTouches.length === 1 && changedTouches[0].identifier === SYNTH_ID) { return; } aEvent.preventDefault(); aEvent.stopImmediatePropagation(); let type = aEvent.type; if (!this._eventsOfInterest[type]) { return; } let pointerType = this._eventMap[type]; this.onPointerEvent({ type: pointerType, points: Array.prototype.map.call(changedTouches, function mapTouch(aTouch) { return { identifier: aTouch.identifier, x: aTouch.screenX, y: aTouch.screenY }; } ) }); } }; this.PointerAdapter = { // jshint ignore:line start: function PointerAdapter_start() { Logger.debug('PointerAdapter.start'); GestureTracker.reset(); PointerRelay.start(this.handleEvent); }, stop: function PointerAdapter_stop() { Logger.debug('PointerAdapter.stop'); PointerRelay.stop(); GestureTracker.reset(); }, handleEvent: function PointerAdapter_handleEvent(aDetail) { let timeStamp = Date.now(); GestureTracker.handle(aDetail, timeStamp); } };