Bug 836375 - Add press() and release() tap gestures on elements, r=mdas
authorYiming Yang <yiyang@mozilla.com>
Wed, 06 Feb 2013 11:58:14 -0800
changeset 130930 b3e63d9ea7acd9c52f92d6a4674ad959edf4c0e9
parent 130929 6abdc780ba2b0170fa870a9d7e31cb6ecd810c0a
child 130931 d9708e34085ae4c6b04b61c439b6fe1a50e6cfa2
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmdas
bugs836375
milestone21.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 836375 - Add press() and release() tap gestures on elements, r=mdas
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_press_release.py
testing/marionette/client/marionette/tests/unit/unit-tests.ini
testing/marionette/marionette-actors.js
testing/marionette/marionette-listener.js
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -51,16 +51,22 @@ class HTMLElement(object):
         return self.marionette._send_message('clickElement', 'ok', element=self.id)
 
     def single_tap(self, x=None, y=None):
         return self.marionette._send_message('singleTap', 'ok', element=self.id, x=x, y=y)
 
     def double_tap(self, x=None, y=None):
         return self.marionette._send_message('doubleTap', 'ok', element=self.id, x=x, y=y)
 
+    def press(self, x=None, y=None):
+        return self.marionette._send_message('press', 'value', element=self.id, x=x, y=y)
+
+    def release(self, touch_id, x=None, y=None):
+        return self.marionette._send_message('release', 'ok', element=self.id, touchId=touch_id, x=x, y=y)
+
     @property
     def text(self):
         return self.marionette._send_message('getElementText', 'value', element=self.id)
 
     def send_keys(self, *string):
         typing = []
         for val in string:
             if isinstance(val, Keys):
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_press_release.py
@@ -0,0 +1,30 @@
+# 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/.
+
+import os
+import time
+from marionette_test import MarionetteTestCase
+
+class testPressRelease(MarionetteTestCase):
+    def test_coordinates(self):
+        testTouch = self.marionette.absolute_url("testTouch.html")
+        self.marionette.navigate(testTouch)
+        button = self.marionette.find_element("id", "mozLink")
+        new_id = button.press(0, 300)
+        button.release(new_id, 0, 300)
+        time.sleep(10)
+        self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
+        new_id2 = button.press(0, 0)
+        button.release(new_id2, 0, 0)
+        time.sleep(10)
+        self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
+
+    def test_no_coordinates(self):
+      testTouch = self.marionette.absolute_url("testTouch.html")
+      self.marionette.navigate(testTouch)
+      ele = self.marionette.find_element("id", "scroll")
+      scroll_id = ele.press()
+      ele.release(scroll_id)
+      time.sleep(10)
+      self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('scroll').innerHTML;"))
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -43,16 +43,20 @@ b2g = false
 
 [test_timeouts.py]
 b2g = false
 
 [test_touch.py]
 b2g = true
 browser = false
 
+[test_press_release.py]
+b2g = true
+browser = false
+
 [test_simpletest_pass.js]
 [test_simpletest_sanity.py]
 [test_simpletest_chrome.js]
 [test_simpletest_timeout.js]
 [test_specialpowers.py]
 [test_switch_frame.py]
 b2g = false
 
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -1223,17 +1223,17 @@ MarionetteDriverActor.prototype = {
       }
     }
     else {
       this.sendAsync("setSearchTimeout", {value: aRequest.value,
                                           command_id: this.command_id});
     }
   },
 
-/**
+  /**
    * Set timeout for page loading, searching and scripts
    *
    * @param object aRequest
    *        'type' hold the type of timeout
    *        'ms' holds the timeout in milliseconds
    */
   timeouts: function MDA_timeouts(aRequest){
     /*setTimeout*/
@@ -1299,16 +1299,52 @@ MarionetteDriverActor.prototype = {
       this.sendAsync("doubleTap", {value: serId,
                                    corx: x,
                                    cory: y,
                                    command_id: this.command_id});
     }
   },
 
   /**
+   * Start touch
+   *
+   * @param object aRequest
+   *        'element' represents the ID of the element to touch
+   */
+  press: function MDA_press(aRequest) {
+    this.command_id = this.getCommandId();
+    let element = aRequest.element;
+    let x = aRequest.x;
+    let y = aRequest.y;
+    this.sendAsync("press", {value: element,
+                             corx: x,
+                             cory: y,
+                             command_id: this.command_id});
+  },
+
+  /**
+   * End touch
+   *
+   * @param object aRequest
+   *        'element' represents the ID of the element to end the touch
+   */
+  release: function MDA_release(aRequest) {
+    this.command_id = this.getCommandId();
+    let element = aRequest.element;
+    let touchId = aRequest.touchId;
+    let x = aRequest.x;
+    let y = aRequest.y;
+    this.sendAsync("release", {value: element,
+                               touchId: touchId,
+                               corx: x,
+                               cory: y,
+                               command_id: this.command_id});
+  },
+
+  /**
    * Find an element using the indicated search strategy.
    *
    * @param object aRequest
    *        'using' member indicates which search method to use
    *        'value' member is the value the client is looking for
    */
   findElement: function MDA_findElement(aRequest) {
     let command_id = this.command_id = this.getCommandId();
@@ -2050,16 +2086,18 @@ MarionetteDriverActor.prototype.requestT
   "addPerfData": MarionetteDriverActor.prototype.addPerfData,
   "getPerfData": MarionetteDriverActor.prototype.getPerfData,
   "setContext": MarionetteDriverActor.prototype.setContext,
   "executeScript": MarionetteDriverActor.prototype.execute,
   "setScriptTimeout": MarionetteDriverActor.prototype.setScriptTimeout,
   "timeouts": MarionetteDriverActor.prototype.timeouts,
   "singleTap": MarionetteDriverActor.prototype.singleTap,
   "doubleTap": MarionetteDriverActor.prototype.doubleTap,
+  "press": MarionetteDriverActor.prototype.press,
+  "release": MarionetteDriverActor.prototype.release,
   "executeAsyncScript": MarionetteDriverActor.prototype.executeWithCallback,
   "executeJSScript": MarionetteDriverActor.prototype.executeJSScript,
   "setSearchTimeout": MarionetteDriverActor.prototype.setSearchTimeout,
   "findElement": MarionetteDriverActor.prototype.findElement,
   "findElements": MarionetteDriverActor.prototype.findElements,
   "clickElement": MarionetteDriverActor.prototype.clickElement,
   "getElementAttribute": MarionetteDriverActor.prototype.getElementAttribute,
   "getElementText": MarionetteDriverActor.prototype.getElementText,
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -55,17 +55,17 @@ let originalOnError;
 //timer for doc changes
 let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 // Send move events about this often
 let EVENT_INTERVAL = 30; // milliseconds
 // The current array of all pending touches
 let touches = [];
 // For assigning unique ids to all touches
 let nextTouchId = 1000;
-
+let touchIds = [];
 /**
  * Called when listener is first started up. 
  * The listener sends its unique window ID and its current URI to the actor.
  * If the actor returns an ID, we start the listeners. Otherwise, nothing happens.
  */
 function registerSelf() {
   let msg = {value: winUtil.outerWindowID, href: content.location.href};
   let register = sendSyncMessage("Marionette:register", msg);
@@ -96,16 +96,18 @@ function removeMessageListenerId(message
  */
 function startListeners() {
   addMessageListenerId("Marionette:newSession", newSession);
   addMessageListenerId("Marionette:executeScript", executeScript);
   addMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
   addMessageListenerId("Marionette:executeJSScript", executeJSScript);
   addMessageListenerId("Marionette:singleTap", singleTap);
   addMessageListenerId("Marionette:doubleTap", doubleTap);
+  addMessageListenerId("Marionette:press", press);
+  addMessageListenerId("Marionette:release", release);
   addMessageListenerId("Marionette:setSearchTimeout", setSearchTimeout);
   addMessageListenerId("Marionette:goUrl", goUrl);
   addMessageListenerId("Marionette:getUrl", getUrl);
   addMessageListenerId("Marionette:getTitle", getTitle);
   addMessageListenerId("Marionette:getPageSource", getPageSource);
   addMessageListenerId("Marionette:goBack", goBack);
   addMessageListenerId("Marionette:goForward", goForward);
   addMessageListenerId("Marionette:refresh", refresh);
@@ -185,16 +187,18 @@ function restart(msg) {
  */
 function deleteSession(msg) {
   removeMessageListenerId("Marionette:newSession", newSession);
   removeMessageListenerId("Marionette:executeScript", executeScript);
   removeMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
   removeMessageListenerId("Marionette:executeJSScript", executeJSScript);
   removeMessageListenerId("Marionette:singleTap", singleTap);
   removeMessageListenerId("Marionette:doubleTap", doubleTap);
+  removeMessageListenerId("Marionette:press", press);
+  removeMessageListenerId("Marionette:release", release);
   removeMessageListenerId("Marionette:setSearchTimeout", setSearchTimeout);
   removeMessageListenerId("Marionette:goUrl", goUrl);
   removeMessageListenerId("Marionette:getTitle", getTitle);
   removeMessageListenerId("Marionette:getPageSource", getPageSource);
   removeMessageListenerId("Marionette:getUrl", getUrl);
   removeMessageListenerId("Marionette:goBack", goBack);
   removeMessageListenerId("Marionette:goForward", goForward);
   removeMessageListenerId("Marionette:refresh", refresh);
@@ -223,16 +227,18 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:addCookie", addCookie);
   removeMessageListenerId("Marionette:getAllCookies", getAllCookies);
   removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookies);
   removeMessageListenerId("Marionette:deleteCookie", deleteCookie);
   this.elementManager.reset();
   // reset frame to the top-most frame
   curWindow = content;
   curWindow.focus();
+  touches = [];
+  touchIds = [];
 }
 
 /*
  * Helper methods 
  */
 
 /**
  * Generic method to send a message to the server
@@ -797,16 +803,96 @@ function doubleTap(msg) {
     sendOk(msg.json.command_id);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, msg.json.command_id);
   }
 }
 
 /**
+ * Function to create a touch based on the element
+ */
+function createATouch(el, corx, cory, id) {
+  var doc = el.ownerDocument;
+  var win = doc.defaultView;
+  if (corx == null) {
+    corx = '50%';
+  }
+  if (cory == null){
+    cory = '50%';
+  }
+  var c = coordinates(el, corx, cory);
+  var clientX = Math.round(c.x0),
+      clientY = Math.round(c.y0);
+  var pageX = clientX + win.pageXOffset,
+      pageY = clientY + win.pageYOffset;
+  var screenX = clientX + win.mozInnerScreenX,
+      screenY = clientY + win.mozInnerScreenY;
+  var atouch = doc.createTouch(win, el, id, pageX, pageY, screenX, screenY, clientX, clientY);
+  return atouch;
+}
+
+/**
+ * Function to start a touch event
+ */
+function press(msg) {
+  let command_id = msg.json.command_id;
+  let el;
+  try {
+    el = elementManager.getKnownElement(msg.json.value, curWindow);
+    let corx = msg.json.corx;
+    let cory = msg.json.cory;
+    if (!checkVisible(el, command_id)) {
+      sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
+      return;
+    }
+    var touchId = nextTouchId++;
+    var touch = createATouch(el, corx, cory, touchId);
+    emitTouchEvent('touchstart', touch);
+    touchIds.push(touchId);
+    sendResponse({value: touch.identifier}, command_id);
+  }
+  catch (e) {
+    sendError(e.message, e.code, e.stack, msg.json.command_id);
+  }
+}
+
+/**
+ * Function to end a touch event
+ */
+function release(msg) {
+  let command_id = msg.json.command_id;
+  let el;
+  try {
+    let id = msg.json.touchId;
+    let currentIndex = touchIds.indexOf(id);
+    if (currentIndex != -1) {
+      el = elementManager.getKnownElement(msg.json.value, curWindow);
+      let corx = msg.json.corx;
+      let cory = msg.json.cory;
+      if (!checkVisible(el, command_id)) {
+        sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
+        return;
+      }
+      var touch = createATouch(el, corx, cory, id);
+      emitTouchEvent('touchend', touch);
+      touchIds.splice(currentIndex, 1);
+      sendOk(msg.json.command_id);
+    }
+    else {
+      sendError("Element has not be pressed: InvalidElementCoordinates", 29, null, command_id);
+      return;
+    }
+  }
+  catch (e) {
+    sendError(e.message, e.code, e.stack, msg.json.command_id);
+  }
+}
+
+/**
  * Function to set the timeout period for element searching 
  */
 function setSearchTimeout(msg) {
   try {
     elementManager.setSearchTimeout(msg.json.value);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, msg.json.command_id);