accessible/src/jsat/PointerAdapter.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/accessible/src/jsat/PointerAdapter.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,206 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/* global Components, XPCOMUtils, Utils, Logger, GestureSettings,
     1.9 +   GestureTracker */
    1.10 +/* exported PointerRelay, PointerAdapter */
    1.11 +
    1.12 +'use strict';
    1.13 +
    1.14 +const Ci = Components.interfaces;
    1.15 +const Cu = Components.utils;
    1.16 +
    1.17 +this.EXPORTED_SYMBOLS = ['PointerRelay', 'PointerAdapter']; // jshint ignore:line
    1.18 +
    1.19 +Cu.import('resource://gre/modules/XPCOMUtils.jsm');
    1.20 +
    1.21 +XPCOMUtils.defineLazyModuleGetter(this, 'Utils', // jshint ignore:line
    1.22 +  'resource://gre/modules/accessibility/Utils.jsm');
    1.23 +XPCOMUtils.defineLazyModuleGetter(this, 'Logger', // jshint ignore:line
    1.24 +  'resource://gre/modules/accessibility/Utils.jsm');
    1.25 +XPCOMUtils.defineLazyModuleGetter(this, 'GestureSettings', // jshint ignore:line
    1.26 +  'resource://gre/modules/accessibility/Gestures.jsm');
    1.27 +XPCOMUtils.defineLazyModuleGetter(this, 'GestureTracker', // jshint ignore:line
    1.28 +  'resource://gre/modules/accessibility/Gestures.jsm');
    1.29 +
    1.30 +// The virtual touch ID generated by a mouse event.
    1.31 +const MOUSE_ID = 'mouse';
    1.32 +// Synthesized touch ID.
    1.33 +const SYNTH_ID = -1;
    1.34 +
    1.35 +let PointerRelay = { // jshint ignore:line
    1.36 +  /**
    1.37 +   * A mapping of events we should be intercepting. Entries with a value of
    1.38 +   * |true| are used for compiling high-level gesture events. Entries with a
    1.39 +   * value of |false| are cancelled and do not propogate to content.
    1.40 +   */
    1.41 +  get _eventsOfInterest() {
    1.42 +    delete this._eventsOfInterest;
    1.43 +
    1.44 +    switch (Utils.widgetToolkit) {
    1.45 +      case 'gonk':
    1.46 +        this._eventsOfInterest = {
    1.47 +          'touchstart' : true,
    1.48 +          'touchmove' : true,
    1.49 +          'touchend' : true,
    1.50 +          'mousedown' : false,
    1.51 +          'mousemove' : false,
    1.52 +          'mouseup': false,
    1.53 +          'click': false };
    1.54 +        break;
    1.55 +
    1.56 +      case 'android':
    1.57 +        this._eventsOfInterest = {
    1.58 +          'touchstart' : true,
    1.59 +          'touchmove' : true,
    1.60 +          'touchend' : true,
    1.61 +          'mousemove' : true,
    1.62 +          'mouseenter' : true,
    1.63 +          'mouseleave' : true,
    1.64 +          'mousedown' : false,
    1.65 +          'mouseup': false,
    1.66 +          'click': false };
    1.67 +        break;
    1.68 +
    1.69 +      default:
    1.70 +        // Desktop.
    1.71 +        this._eventsOfInterest = {
    1.72 +          'mousemove' : true,
    1.73 +          'mousedown' : true,
    1.74 +          'mouseup': true,
    1.75 +          'click': false
    1.76 +        };
    1.77 +        if ('ontouchstart' in Utils.win) {
    1.78 +          for (let eventType of ['touchstart', 'touchmove', 'touchend']) {
    1.79 +            this._eventsOfInterest[eventType] = true;
    1.80 +          }
    1.81 +        }
    1.82 +        break;
    1.83 +    }
    1.84 +
    1.85 +    return this._eventsOfInterest;
    1.86 +  },
    1.87 +
    1.88 +  _eventMap: {
    1.89 +    'touchstart' : 'pointerdown',
    1.90 +    'mousedown' : 'pointerdown',
    1.91 +    'mouseenter' : 'pointerdown',
    1.92 +    'touchmove' : 'pointermove',
    1.93 +    'mousemove' : 'pointermove',
    1.94 +    'touchend' : 'pointerup',
    1.95 +    'mouseup': 'pointerup',
    1.96 +    'mouseleave': 'pointerup'
    1.97 +  },
    1.98 +
    1.99 +  start: function PointerRelay_start(aOnPointerEvent) {
   1.100 +    Logger.debug('PointerRelay.start');
   1.101 +    this.onPointerEvent = aOnPointerEvent;
   1.102 +    for (let eventType in this._eventsOfInterest) {
   1.103 +      Utils.win.addEventListener(eventType, this, true, true);
   1.104 +    }
   1.105 +  },
   1.106 +
   1.107 +  stop: function PointerRelay_stop() {
   1.108 +    Logger.debug('PointerRelay.stop');
   1.109 +    delete this.lastPointerMove;
   1.110 +    delete this.onPointerEvent;
   1.111 +    for (let eventType in this._eventsOfInterest) {
   1.112 +      Utils.win.removeEventListener(eventType, this, true, true);
   1.113 +    }
   1.114 +  },
   1.115 +
   1.116 +  _suppressPointerMove: function PointerRelay__suppressPointerMove(aChangedTouches) {
   1.117 +    if (!this.lastPointerMove) {
   1.118 +      return false;
   1.119 +    }
   1.120 +    for (let i = 0; i < aChangedTouches.length; ++i) {
   1.121 +      let touch = aChangedTouches[i];
   1.122 +      let lastTouch;
   1.123 +      try {
   1.124 +        lastTouch = this.lastPointerMove.identifiedTouch ?
   1.125 +          this.lastPointerMove.identifiedTouch(touch.identifier) :
   1.126 +          this.lastPointerMove[i];
   1.127 +      } catch (x) {
   1.128 +        // Sometimes touch object can't be accessed after page navigation.
   1.129 +      }
   1.130 +      if (!lastTouch || lastTouch.target !== touch.target ||
   1.131 +        Math.hypot(touch.screenX - lastTouch.screenX, touch.screenY -
   1.132 +          lastTouch.screenY) / Utils.dpi >= GestureSettings.travelThreshold) {
   1.133 +        return false;
   1.134 +      }
   1.135 +    }
   1.136 +    return true;
   1.137 +  },
   1.138 +
   1.139 +  handleEvent: function PointerRelay_handleEvent(aEvent) {
   1.140 +    // Don't bother with chrome mouse events.
   1.141 +    if (Utils.MozBuildApp === 'browser' &&
   1.142 +      aEvent.view.top instanceof Ci.nsIDOMChromeWindow) {
   1.143 +      return;
   1.144 +    }
   1.145 +    if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN) {
   1.146 +      // Ignore events that are scripted or clicks from the a11y API.
   1.147 +      return;
   1.148 +    }
   1.149 +
   1.150 +    let changedTouches = aEvent.changedTouches || [{
   1.151 +      identifier: MOUSE_ID,
   1.152 +      screenX: aEvent.screenX,
   1.153 +      screenY: aEvent.screenY,
   1.154 +      target: aEvent.target
   1.155 +    }];
   1.156 +
   1.157 +    if (changedTouches.length === 1 &&
   1.158 +        changedTouches[0].identifier === SYNTH_ID) {
   1.159 +      return;
   1.160 +    }
   1.161 +
   1.162 +    aEvent.preventDefault();
   1.163 +    aEvent.stopImmediatePropagation();
   1.164 +
   1.165 +    let type = aEvent.type;
   1.166 +    if (!this._eventsOfInterest[type]) {
   1.167 +      return;
   1.168 +    }
   1.169 +    let pointerType = this._eventMap[type];
   1.170 +    if (pointerType === 'pointermove') {
   1.171 +      if (this._suppressPointerMove(changedTouches)) {
   1.172 +        // Do not fire pointermove more than every POINTERMOVE_THROTTLE.
   1.173 +        return;
   1.174 +      }
   1.175 +      this.lastPointerMove = changedTouches;
   1.176 +    }
   1.177 +    this.onPointerEvent({
   1.178 +      type: pointerType,
   1.179 +      points: Array.prototype.map.call(changedTouches,
   1.180 +        function mapTouch(aTouch) {
   1.181 +          return {
   1.182 +            identifier: aTouch.identifier,
   1.183 +            x: aTouch.screenX,
   1.184 +            y: aTouch.screenY
   1.185 +          };
   1.186 +        }
   1.187 +      )
   1.188 +    });
   1.189 +  }
   1.190 +};
   1.191 +
   1.192 +this.PointerAdapter = { // jshint ignore:line
   1.193 +  start: function PointerAdapter_start() {
   1.194 +    Logger.debug('PointerAdapter.start');
   1.195 +    GestureTracker.reset();
   1.196 +    PointerRelay.start(this.handleEvent);
   1.197 +  },
   1.198 +
   1.199 +  stop: function PointerAdapter_stop() {
   1.200 +    Logger.debug('PointerAdapter.stop');
   1.201 +    PointerRelay.stop();
   1.202 +    GestureTracker.reset();
   1.203 +  },
   1.204 +
   1.205 +  handleEvent: function PointerAdapter_handleEvent(aDetail) {
   1.206 +    let timeStamp = Date.now();
   1.207 +    GestureTracker.handle(aDetail, timeStamp);
   1.208 +  }
   1.209 +};

mercurial