Bug 1050383 - Send edge gesture events on two finger edge swipes. r=yzen
authorEitan Isaacson <eitan@monotonous.org>
Mon, 11 Aug 2014 14:27:15 -0400
changeset 220590 fb5248ee307b9196801b82663902c195bb063b79
parent 220589 74aae2dec3c9f2a50a70fa101f2d5986d7c6048f
child 220591 abe747ee0aca6ae8fff459e28abb951084738ccf
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1050383
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1050383 - Send edge gesture events on two finger edge swipes. r=yzen
accessible/jsat/AccessFu.jsm
accessible/jsat/Gestures.jsm
accessible/jsat/Utils.jsm
accessible/tests/mochitest/jsat/dom_helper.js
accessible/tests/mochitest/jsat/gestures.json
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -523,33 +523,17 @@ var Output = {
   stop: function stop() {
     if (this.highlightBox) {
       Utils.win.document.documentElement.removeChild(this.highlightBox.get());
       delete this.highlightBox;
     }
   },
 
   B2G: function B2G(aDetails) {
-    let details = {
-      type: 'accessfu-output',
-      details: JSON.stringify(aDetails)
-    };
-    let window = Utils.win;
-    if (window.shell) {
-      // On B2G device.
-      window.shell.sendChromeEvent(details);
-    } else {
-      // Dispatch custom event to have support for desktop and screen reader
-      // emulator add-on.
-      window.dispatchEvent(new window.CustomEvent(details.type, {
-        bubbles: true,
-        cancelable: true,
-        detail: details
-      }));
-    }
+    Utils.dispatchChromeEvent('accessfu-output', aDetails);
   },
 
   Visual: function Visual(aDetail, aBrowser) {
     switch (aDetail.eventType) {
       case 'viewport-change':
       case 'vc-change':
       {
         let highlightBox = null;
@@ -706,25 +690,42 @@ var Input = {
       case 'swipedown1':
         this.contextAction('forward');
         break;
       case 'exploreend1':
       case 'dwellend1':
         this.activateCurrent(null, true);
         break;
       case 'swiperight2':
+        if (aGesture.edge) {
+          Utils.dispatchChromeEvent('accessibility-control',
+            'edge-swipe-right');
+          break;
+        }
         this.sendScrollMessage(-1, true);
         break;
       case 'swipedown2':
+        if (aGesture.edge) {
+          Utils.dispatchChromeEvent('accessibility-control', 'edge-swipe-down');
+          break;
+        }
         this.sendScrollMessage(-1);
         break;
       case 'swipeleft2':
+        if (aGesture.edge) {
+          Utils.dispatchChromeEvent('accessibility-control', 'edge-swipe-left');
+          break;
+        }
         this.sendScrollMessage(1, true);
         break;
       case 'swipeup2':
+        if (aGesture.edge) {
+          Utils.dispatchChromeEvent('accessibility-control', 'edge-swipe-up');
+          break;
+        }
         this.sendScrollMessage(1);
         break;
       case 'explore2':
         Utils.CurrentBrowser.contentWindow.scrollBy(
           -aGesture.deltaX, -aGesture.deltaY);
         break;
       case 'swiperight3':
         this.moveCursor('moveNext', this.quickNavMode.current, 'gesture');
--- a/accessible/jsat/Gestures.jsm
+++ b/accessible/jsat/Gestures.jsm
@@ -73,16 +73,18 @@ const DIRECTNESS_COEFF = 1.44;
 // An android flag.
 const IS_ANDROID = Utils.MozBuildApp === 'mobile/android' &&
   Utils.AndroidSdkVersion >= 14;
 // A single pointer down/up sequence periodically precedes the tripple swipe
 // gesture on Android. This delay acounts for that.
 const ANDROID_TRIPLE_SWIPE_DELAY = 50;
 // The virtual touch ID generated by a mouse event.
 const MOUSE_ID = 'mouse';
+// Amount in inches from the edges of the screen for it to be an edge swipe
+const EDGE = 0.1;
 
 /**
  * A point object containing distance travelled data.
  * @param {Object} aPoint A point object that looks like: {
  *   x: x coordinate in pixels,
  *   y: y coordinate in pixels
  * }
  */
@@ -936,17 +938,34 @@ Swipe.prototype.test = function Swipe_te
  * @return {Object} A mozAccessFuGesture detail object.
  */
 Swipe.prototype.compile = function Swipe_compile() {
   let type = this.type;
   let detail = compileDetail(type, this.points,
     {x1: 'startX', y1: 'startY', x2: 'x', y2: 'y'});
   let deltaX = detail.deltaX;
   let deltaY = detail.deltaY;
+  let edge = EDGE * Utils.dpi;
   if (Math.abs(deltaX) > Math.abs(deltaY)) {
     // Horizontal swipe.
-    detail.type = type + (deltaX > 0 ? 'right' : 'left');
+    let startPoints = [touch.x1 for (touch of detail.touches)];
+    if (deltaX > 0) {
+      detail.type = type + 'right';
+      detail.edge = Math.min.apply(null, startPoints) <= edge;
+    } else {
+      detail.type = type + 'left';
+      detail.edge =
+        Utils.win.screen.width - Math.max.apply(null, startPoints) <= edge;
+    }
   } else {
-    // Vertival swipe.
-    detail.type = type + (deltaY > 0 ? 'down' : 'up');
+    // Vertical swipe.
+    let startPoints = [touch.y1 for (touch of detail.touches)];
+    if (deltaY > 0) {
+      detail.type = type + 'down';
+      detail.edge = Math.min.apply(null, startPoints) <= edge;
+    } else {
+      detail.type = type + 'up';
+      detail.edge =
+        Utils.win.screen.height - Math.max.apply(null, startPoints) <= edge;
+    }
   }
   return detail;
 };
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -417,16 +417,38 @@ this.Utils = { // jshint ignore:line
                                                     aExcludeOrdered) {
     let parent = aStaticText.parent;
     if (aExcludeOrdered && parent.parent.DOMNode.nodeName === 'OL') {
       return false;
     }
 
     return parent.role === Roles.LISTITEM && parent.childCount > 1 &&
       aStaticText.indexInParent === 0;
+  },
+
+  dispatchChromeEvent: function dispatchChromeEvent(aType, aDetails) {
+    let details = {
+      type: aType,
+      details: JSON.stringify(
+        typeof aDetails === 'string' ? { eventType : aDetails } : aDetails)
+    };
+    let window = this.win;
+    if (window.shell) {
+      // On B2G device.
+      window.shell.sendChromeEvent(details);
+    } else {
+      // Dispatch custom event to have support for desktop and screen reader
+      // emulator add-on.
+      window.dispatchEvent(new window.CustomEvent(aType, {
+        bubbles: true,
+        cancelable: true,
+        detail: details
+      }));
+    }
+
   }
 };
 
 /**
  * State object used internally to process accessible's states.
  * @param {Number} aBase     Base state.
  * @param {Number} aExtended Extended state.
  */
--- a/accessible/tests/mochitest/jsat/dom_helper.js
+++ b/accessible/tests/mochitest/jsat/dom_helper.js
@@ -105,23 +105,23 @@ var originalSwipeMaxDuration = GestureSe
 
 /**
  * Attach a listener for the mozAccessFuGesture event that tests its
  * type.
  * @param  {Array} aExpectedGestures A stack of expected event types.
  * Note: the listener is removed once the stack reaches 0.
  */
 function testMozAccessFuGesture(aExpectedGestures) {
-  var types = typeof aExpectedGestures === "string" ?
-    [aExpectedGestures] : aExpectedGestures;
+  var types = aExpectedGestures;
   function handleGesture(aEvent) {
-    if (aEvent.detail.type !== types[0]) {
+    if (aEvent.detail.type !== types[0].type) {
       // The is not the event of interest.
       return;
     }
+    is(!!aEvent.detail.edge, !!types[0].edge);
     ok(true, 'Received correct mozAccessFuGesture: ' + types.shift() + '.');
     if (types.length === 0) {
       win.removeEventListener('mozAccessFuGesture', handleGesture);
       if (AccessFuTest.sequenceCleanup) {
         AccessFuTest.sequenceCleanup();
       }
       AccessFuTest.nextTest();
     }
--- a/accessible/tests/mochitest/jsat/gestures.json
+++ b/accessible/tests/mochitest/jsat/gestures.json
@@ -1,235 +1,239 @@
 [
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": "tap"
+    "expectedGestures": [{ "type": "tap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]}
     ],
-    "expectedGestures": "tap"
+    "expectedGestures": [{ "type": "tap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["dwell", "dwellend"]
+    "expectedGestures": [{ "type": "dwell" }, { "type": "dwellend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
       {"type": "pointerup",
         "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 0.97, "y": 1.01, "identifier": 1}]},
       {"type": "pointerup",
         "points": [{"x": 0.97, "y": 1.01, "identifier": 1}]}
     ],
-    "expectedGestures": ["tap", "doubletap"]
+    "expectedGestures": [{ "type": "tap" }, { "type": "doubletap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["tap", "doubletap", "tripletap"]
+    "expectedGestures": [{ "type": "tap" }, { "type": "doubletap" }, { "type": "tripletap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["tap", "doubletap", "doubletaphold",
-      "doubletapholdend"]
+    "expectedGestures": [{ "type": "tap" }, { "type": "doubletap" },
+      { "type": "doubletaphold" }, { "type": "doubletapholdend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["tap", "taphold", "tapholdend"]
+    "expectedGestures": [{ "type": "tap" }, { "type": "taphold" },
+      { "type": "tapholdend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": "swiperight"
+    "expectedGestures": [{ "type": "swiperight" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1.15, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1.3, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.3, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": "swiperight"
+    "expectedGestures": [{ "type": "swiperight" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": "swipeleft"
+    "expectedGestures": [{ "type": "swipeleft" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1.5, "identifier": 1}]}
     ],
-    "expectedGestures": "swipedown"
+    "expectedGestures": [{ "type": "swipedown" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": "swipeup"
+    "expectedGestures": [{ "type": "swipeup" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]}
     ],
-    "expectedGestures": "swiperight"
+    "expectedGestures": [{ "type": "swiperight" }]
   },
   {
     "events": [
       {"type": "pointerdown",
         "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 0.95, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 0.95, "identifier": 1}]}
     ],
-    "expectedGestures": "swipeleft"
+    "expectedGestures": [{ "type": "swipeleft" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 0.9, "y": 1.5, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 0.9, "y": 1.5, "identifier": 1}]}
     ],
-    "expectedGestures": "swipedown"
+    "expectedGestures": [{ "type": "swipedown" }]
   },
   {
     "events": [
       {"type": "pointerdown",
         "points": [{"x": 1.1, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": "swipeup"
+    "expectedGestures": [{ "type": "swipeup" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
         {"x": 1, "y": 1.5, "identifier": 2}]},
       {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
         {"x": 1.5, "y": 1.5, "identifier": 2}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
         {"x": 1.5, "y": 1.5, "identifier": 2}]}
     ],
-    "expectedGestures": "swiperight"
+    "expectedGestures": [{ "type": "swiperight" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
         {"x": 1, "y": 1.5, "identifier": 2}]},
       {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
         {"x": 1.5, "y": 1.5, "identifier": 2}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1.5, "identifier": 2}]}
     ],
-    "expectedGestures": "swiperight"
+    "expectedGestures": [{ "type": "swiperight" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
         {"x": 1, "y": 1.5, "identifier": 2},
         {"x": 1, "y": 2, "identifier": 3}]},
       {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
         {"x": 1.5, "y": 1.5, "identifier": 2},
         {"x": 1.5, "y": 2, "identifier": 3}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
         {"x": 1.5, "y": 1.5, "identifier": 2},
         {"x": 1.5, "y": 2, "identifier": 3}]}
     ],
-    "expectedGestures": "swiperight"
+    "expectedGestures": [{ "type": "swiperight" }]
   },
   {
     "events": [
       {"type": "pointerdown",
         "points": [{"x": 1.6, "y": 1.5, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["dwell", "explore", "exploreend"]
+    "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+      { "type": "exploreend" }]
   },
   {
     "events": [
       {"type": "pointerdown",
         "points": [{"x": 1.6, "y": 1.5, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 2, "y": 2, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 2, "y": 2, "identifier": 1}]}
     ],
-    "expectedGestures": ["dwell", "explore", "explore", "exploreend"]
+    "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+      { "type": "explore" }, { "type": "exploreend" }]
   },
   {
     "events": [
       {"type": "pointerdown",
         "points": [{"x": 1.6, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeSwipeMaxDuration": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["explore", "exploreend"]
+    "expectedGestures": [{ "type": "explore" }, { "type": "exploreend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeSwipeMaxDuration": true},
       {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": ["explore", "explore", "exploreend"]
+    "expectedGestures": [{ "type": "explore" }, { "type": "explore" },
+      { "type": "exploreend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointermove",
         "points": [{"x": 1.5, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove",
@@ -239,11 +243,23 @@
       {"type": "pointermove",
         "points": [{"x": 1.65, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 1.7, "y": 1.5, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 1.75, "y": 1.5, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.75, "y": 1.5, "identifier": 1}]}
     ],
-    "expectedGestures": ["dwell", "explore", "explore", "exploreend"]
+    "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+      { "type": "explore" }, { "type": "exploreend" }]
+  },
+  {
+    "events": [
+      {"type": "pointerdown", "points": [{"x": 0.075, "y": 1, "identifier": 1},
+        {"x": 1, "y": 1.5, "identifier": 2}]},
+      {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+        {"x": 1.5, "y": 1.5, "identifier": 2}]},
+      {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+        {"x": 1.5, "y": 1.5, "identifier": 2}]}
+    ],
+    "expectedGestures": [{ "type": "swiperight", "edge": true }]
   }
 ]