Bug 865449 - Make AccessFu more desktop-browser friendly. r=yzen
authorEitan Isaacson <eitan@monotonous.org>
Thu, 25 Apr 2013 12:39:16 -0700
changeset 129953 866cf655cfd1efa43ac754d28da956294df09234
parent 129907 7f68735fc8dabaddf9653ae498203f6f432d07ae
child 129954 38cac63b52b382b318500dab23c437388b3fcc33
push id1552
push userttaubert@mozilla.com
push dateSat, 27 Apr 2013 15:33:29 +0000
treeherderfx-team@40dafc376794 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs865449
milestone23.0a1
Bug 865449 - Make AccessFu more desktop-browser friendly. r=yzen - Use Speech presenter on everything but mobile/android - Filter out accessibility events that are chrome related. - Don't capture key presses on desktop - Rework TouchAdapter to take mouse events on desktop. Generating touch events does not work on desktop since that interface is preffed out by default. We lost the ability to do multiple finger gestures with modifier keys, but that was not really used anyway.
accessible/src/jsat/AccessFu.jsm
accessible/src/jsat/EventManager.jsm
accessible/src/jsat/Presentation.jsm
accessible/src/jsat/TouchAdapter.jsm
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -470,22 +470,28 @@ var Output = {
       scale(vp.zoom, vp.zoom).expandToIntegers();
   }
 };
 
 var Input = {
   editState: {},
 
   start: function start() {
-    Utils.win.document.addEventListener('keypress', this, true);
+    // XXX: This is too disruptive on desktop for now.
+    // Might need to add special modifiers.
+    if (Utils.MozBuildApp != 'browser') {
+      Utils.win.document.addEventListener('keypress', this, true);
+    }
     Utils.win.addEventListener('mozAccessFuGesture', this, true);
   },
 
   stop: function stop() {
-    Utils.win.document.removeEventListener('keypress', this, true);
+    if (Utils.MozBuildApp != 'browser') {
+      Utils.win.document.removeEventListener('keypress', this, true);
+    }
     Utils.win.removeEventListener('mozAccessFuGesture', this, true);
   },
 
   handleEvent: function Input_handleEvent(aEvent) {
     try {
       switch (aEvent.type) {
       case 'keypress':
         this._handleKeypress(aEvent);
--- a/accessible/src/jsat/EventManager.jsm
+++ b/accessible/src/jsat/EventManager.jsm
@@ -100,16 +100,23 @@ this.EventManager = {
     }
   },
 
   handleAccEvent: function handleAccEvent(aEvent) {
     if (Logger.logLevel >= Logger.DEBUG)
       Logger.debug('A11yEvent', Logger.eventToString(aEvent),
                    Logger.accessibleToString(aEvent.accessible));
 
+    // Don't bother with non-content events in firefox.
+    if (Utils.MozBuildApp == 'browser' &&
+        aEvent.eventType != Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED &&
+        aEvent.accessibleDocument != Utils.CurrentContentDoc) {
+      return;
+    }
+
     switch (aEvent.eventType) {
       case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
       {
         let pivot = aEvent.accessible.
           QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
         let position = pivot.position;
         if (position.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME)
           break;
--- a/accessible/src/jsat/Presentation.jsm
+++ b/accessible/src/jsat/Presentation.jsm
@@ -356,21 +356,21 @@ HapticPresenter.prototype = {
   }
 };
 
 this.Presentation = {
   get presenters() {
     delete this.presenters;
     this.presenters = [new VisualPresenter()];
 
-    if (Utils.MozBuildApp == 'b2g') {
+    if (Utils.MozBuildApp == 'mobile/android') {
+      this.presenters.push(new AndroidPresenter());
+    } else {
       this.presenters.push(new SpeechPresenter());
       this.presenters.push(new HapticPresenter());
-    } else if (Utils.MozBuildApp == 'mobile/android') {
-      this.presenters.push(new AndroidPresenter());
     }
 
     return this.presenters;
   },
 
   pivotChanged: function Presentation_pivotChanged(aPosition,
                                                    aOldPosition,
                                                    aReason) {
--- a/accessible/src/jsat/TouchAdapter.jsm
+++ b/accessible/src/jsat/TouchAdapter.jsm
@@ -34,18 +34,18 @@ this.TouchAdapter = {
   DWELL_THRESHOLD: 500,
 
   // delay before distinct dwell events
   DWELL_REPEAT_DELAY: 300,
 
   // maximum distance the mouse could move during a tap in inches
   TAP_MAX_RADIUS: 0.2,
 
-  // The virtual touch ID generated by an Android hover event.
-  HOVER_ID: 'hover',
+  // The virtual touch ID generated by a mouse event.
+  MOUSE_ID: 'mouse',
 
   start: function TouchAdapter_start() {
     Logger.info('TouchAdapter.start');
 
     this._touchPoints = {};
     this._dwellTimeout = 0;
     this._prevGestures = {};
     this._lastExploreTime = 0;
@@ -57,109 +57,121 @@ this.TouchAdapter = {
     if (Utils.MozBuildApp == 'b2g') {
       this.glass = Utils.win.document.
         createElementNS('http://www.w3.org/1999/xhtml', 'div');
       this.glass.id = 'accessfu-glass';
       Utils.win.document.documentElement.appendChild(this.glass);
       target = this.glass;
     }
 
-    target.addEventListener('mousemove', this, true, true);
-    target.addEventListener('mouseenter', this, true, true);
-    target.addEventListener('mouseleave', this, true, true);
+    for each (let eventType in this.eventsOfInterest) {
+      target.addEventListener(eventType, this, true, true);
+    }
 
-    target.addEventListener('touchend', this, true, true);
-    target.addEventListener('touchmove', this, true, true);
-    target.addEventListener('touchstart', this, true, true);
-
-    if (Utils.OS != 'Android')
-      Mouse2Touch.start();
   },
 
   stop: function TouchAdapter_stop() {
     Logger.info('TouchAdapter.stop');
 
     let target = Utils.win;
 
     if (Utils.MozBuildApp == 'b2g') {
       target = this.glass;
       this.glass.parentNode.removeChild(this.glass);
     }
 
-    target.removeEventListener('mousemove', this, true, true);
-    target.removeEventListener('mouseenter', this, true, true);
-    target.removeEventListener('mouseleave', this, true, true);
+    for each (let eventType in this.eventsOfInterest) {
+      target.removeEventListener(eventType, this, true, true);
+    }
+  },
+
+  get eventsOfInterest() {
+    delete this.eventsOfInterest;
 
-    target.removeEventListener('touchend', this, true, true);
-    target.removeEventListener('touchmove', this, true, true);
-    target.removeEventListener('touchstart', this, true, true);
+    if ('ontouchstart' in Utils.win) {
+      this.eventsOfInterest = ['touchstart', 'touchmove', 'touchend'];
+      if (Utils.MozBuildApp == 'mobile/android') {
+        this.eventsOfInterest.push.apply(
+          this.eventsOfInterest, ['mouseenter', 'mousemove', 'mouseleave']);
+      }
+    } else {
+      this.eventsOfInterest = ['mousedown', 'mousemove', 'mouseup'];
+    }
 
-    if (Utils.OS != 'Android')
-      Mouse2Touch.stop();
+    return this.eventsOfInterest;
   },
 
   handleEvent: function TouchAdapter_handleEvent(aEvent) {
+    // Don't bother with chrome mouse events.
+    if (Utils.MozBuildApp == 'browser' &&
+        aEvent.view.top instanceof Ci.nsIDOMChromeWindow) {
+      return;
+    }
+
     if (this._delayedEvent) {
       Utils.win.clearTimeout(this._delayedEvent);
       delete this._delayedEvent;
     }
 
     let changedTouches = aEvent.changedTouches || [aEvent];
 
     // XXX: Until bug 77992 is resolved, on desktop we get microseconds
     // instead of milliseconds.
     let timeStamp = (Utils.OS == 'Android') ? aEvent.timeStamp : Date.now();
     switch (aEvent.type) {
+      case 'mousedown':
       case 'mouseenter':
       case 'touchstart':
         for (var i = 0; i < changedTouches.length; i++) {
           let touch = changedTouches[i];
           let touchPoint = new TouchPoint(touch, timeStamp, this._dpi);
           let identifier = (touch.identifier == undefined) ?
-            this.HOVER_ID : touch.identifier;
+            this.MOUSE_ID : touch.identifier;
           this._touchPoints[identifier] = touchPoint;
           this._lastExploreTime = timeStamp + this.SWIPE_MAX_DURATION;
         }
         this._dwellTimeout = Utils.win.setTimeout(
           (function () {
              this.compileAndEmit(timeStamp + this.DWELL_THRESHOLD);
            }).bind(this), this.DWELL_THRESHOLD);
         break;
       case 'mousemove':
       case 'touchmove':
         for (var i = 0; i < changedTouches.length; i++) {
           let touch = changedTouches[i];
           let identifier = (touch.identifier == undefined) ?
-            this.HOVER_ID : touch.identifier;
+            this.MOUSE_ID : touch.identifier;
           let touchPoint = this._touchPoints[identifier];
           if (touchPoint)
             touchPoint.update(touch, timeStamp);
         }
         if (timeStamp - this._lastExploreTime >= EXPLORE_THROTTLE) {
           this.compileAndEmit(timeStamp);
           this._lastExploreTime = timeStamp;
         }
         break;
+      case 'mouseup':
       case 'mouseleave':
       case 'touchend':
         for (var i = 0; i < changedTouches.length; i++) {
           let touch = changedTouches[i];
           let identifier = (touch.identifier == undefined) ?
-            this.HOVER_ID : touch.identifier;
+            this.MOUSE_ID : touch.identifier;
           let touchPoint = this._touchPoints[identifier];
           if (touchPoint) {
             touchPoint.update(touch, timeStamp);
             touchPoint.finish();
           }
         }
         this.compileAndEmit(timeStamp);
         break;
     }
 
     aEvent.preventDefault();
+    aEvent.stopImmediatePropagation();
   },
 
   cleanupTouches: function cleanupTouches() {
     for (var identifier in this._touchPoints) {
       if (!this._touchPoints[identifier].done)
         continue;
 
       delete this._touchPoints[identifier];
@@ -234,23 +246,23 @@ this.TouchAdapter = {
   emitGesture: function TouchAdapter_emitGesture(aDetails) {
     let emitDelay = 0;
 
     // Unmutate gestures we are getting from Android when EBT is enabled.
     // Two finger gestures are translated to one. Double taps are translated
     // to single taps.
     if (Utils.MozBuildApp == 'mobile/android' &&
         Utils.AndroidSdkVersion >= 14 &&
-        aDetails.touches[0] != this.HOVER_ID) {
+        aDetails.touches[0] != this.MOUSE_ID) {
       if (aDetails.touches.length == 1) {
         if (aDetails.type == 'tap') {
           emitDelay = 50;
           aDetails.type = 'doubletap';
         } else {
-          aDetails.touches.push(this.HOVER_ID);
+          aDetails.touches.push(this.MOUSE_ID);
         }
       }
     }
 
     let emit = function emit() {
       let evt = Utils.win.document.createEvent('CustomEvent');
       evt.initCustomEvent('mozAccessFuGesture', true, true, aDetails);
       Utils.win.dispatchEvent(evt);
@@ -365,71 +377,8 @@ TouchPoint.prototype = {
 
     return null;
   },
 
   get directDistanceTraveled() {
     return this.getDistanceToCoord(this.startX, this.startY);
   }
 };
-
-var Mouse2Touch = {
-  _MouseToTouchMap: {
-    mousedown: 'touchstart',
-    mouseup: 'touchend',
-    mousemove: 'touchmove'
-  },
-
-  start: function Mouse2Touch_start() {
-    Utils.win.addEventListener('mousedown', this, true, true);
-    Utils.win.addEventListener('mouseup', this, true, true);
-    Utils.win.addEventListener('mousemove', this, true, true);
-  },
-
-  stop: function Mouse2Touch_stop() {
-    Utils.win.removeEventListener('mousedown', this, true, true);
-    Utils.win.removeEventListener('mouseup', this, true, true);
-    Utils.win.removeEventListener('mousemove', this, true, true);
-  },
-
-  handleEvent: function Mouse2Touch_handleEvent(aEvent) {
-    if (aEvent.buttons == 0)
-      return;
-
-    let name = this._MouseToTouchMap[aEvent.type];
-    let evt = Utils.win.document.createEvent("touchevent");
-    let points = [Utils.win.document.createTouch(
-                    Utils.win, aEvent.target, 0,
-                    aEvent.pageX, aEvent.pageY, aEvent.screenX, aEvent.screenY,
-                    aEvent.clientX, aEvent.clientY, 1, 1, 0, 0)];
-
-    // Simulate another touch point at a 5px offset when ctrl is pressed.
-    if (aEvent.ctrlKey)
-      points.push(Utils.win.document.createTouch(
-                    Utils.win, aEvent.target, 1,
-                    aEvent.pageX + 5, aEvent.pageY + 5,
-                    aEvent.screenX + 5, aEvent.screenY + 5,
-                    aEvent.clientX + 5, aEvent.clientY + 5,
-                    1, 1, 0, 0));
-
-    // Simulate another touch point at a -5px offset when alt is pressed.
-    if (aEvent.altKey)
-      points.push(Utils.win.document.createTouch(
-                    Utils.win, aEvent.target, 2,
-                    aEvent.pageX - 5, aEvent.pageY - 5,
-                    aEvent.screenX - 5, aEvent.screenY - 5,
-                    aEvent.clientX - 5, aEvent.clientY - 5,
-                    1, 1, 0, 0));
-
-    let touches = Utils.win.document.createTouchList(points);
-    if (name == "touchend") {
-      let empty = Utils.win.document.createTouchList();
-      evt.initTouchEvent(name, true, true, Utils.win, 0,
-                         false, false, false, false, empty, empty, touches);
-    } else {
-      evt.initTouchEvent(name, true, true, Utils.win, 0,
-                         false, false, false, false, touches, touches, touches);
-    }
-    aEvent.target.dispatchEvent(evt);
-    aEvent.preventDefault();
-    aEvent.stopImmediatePropagation();
-  }
-};