Bug 803096: add the ability to get an elements location with marionette; r=mdas, DONTBUILD, a=NPOTB
authorDavid Burns <dburns@mozilla.com>
Fri, 19 Oct 2012 21:35:30 +0100
changeset 116614 5da108186d3305a70bb4a478da4ce96cee7541b9
parent 116613 60e63c12ffcaf6f17f657878fe473024d3b9d1d3
child 116615 2ca4c7e6f6ce01f1cff5a267ec96ccd8cb0d2a6e
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmdas, DONTBUILD, NPOTB
bugs803096
milestone18.0a2
Bug 803096: add the ability to get an elements location with marionette; r=mdas, DONTBUILD, a=NPOTB
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_position.py
testing/marionette/client/marionette/www/rectangles.html
testing/marionette/marionette-actors.js
testing/marionette/marionette-listener.js
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -78,16 +78,20 @@ class HTMLElement(object):
     @property
     def size(self):
         return self.marionette._send_message('getElementSize', 'value', element=self.id)
 
     @property
     def tag_name(self):
         return self.marionette._send_message('getElementTagName', 'value', element=self.id)
 
+    @property
+    def location(self):
+        return self.marionette._send_message('getElementPosition', 'value', element=self.id)
+
 
 class Marionette(object):
 
     CONTEXT_CHROME = 'chrome'
     CONTEXT_CONTENT = 'content'
 
     def __init__(self, host='localhost', port=2828, bin=None, profile=None,
                  emulator=None, sdcard=None, emulatorBinary=None,
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_position.py
@@ -0,0 +1,13 @@
+from marionette_test import MarionetteTestCase
+
+
+class TestPosition(MarionetteTestCase):
+
+    def test_should_get_element_position_back(self):
+        test_url = self.marionette.absolute_url('rectangles.html')
+        self.marionette.navigate(test_url)
+
+        r2 = self.marionette.find_element('id', "r2")
+        location = r2.location
+        self.assertEqual(11, location['x'])
+        self.assertEqual(10, location['y'])
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/rectangles.html
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>Rectangles</title>
+    <style type="text/css">
+        div {
+            position: absolute;
+            margin: 0;
+            border: 0;
+            padding: 0;
+        }
+        #r1 {
+            background-color: blue;
+            left: 10px;
+            top: 10px;
+            width: 100px;
+            height: 50px;
+        }
+        #r2 {
+            background-color: red;
+            left: 10.9px;
+            top: 10.1px;
+            width: 48.666666667px;
+            height: 49.333333333px;
+        }
+        #r3 {
+            background-color: yellow;
+            left: 60px;
+            top: 10px;
+            width: 50px;
+            height: 25px;
+        }
+    </style>
+</head>
+    <body>
+        <div id="r1">r1</div>
+        <div id="r2">r2</div>
+        <div id="r3">r3</div>
+    </body>
+</html>
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -1388,16 +1388,20 @@ MarionetteDriverActor.prototype = {
         this.sendError(e.message, e.code, e.stack);
       }
     }
     else {
       this.sendAsync("clearElement", {element:aRequest.element});
     }
   },
 
+  getElementPosition: function MDA_getElementPosition(aRequest) {
+      this.sendAsync("getElementPosition", {element:aRequest.element});
+  },
+
   /**
    * Closes the Browser Window.
    *
    * If it is B2G it returns straight away and does not do anything
    *
    * If is desktop it calculates how many windows are open and if there is only 
    * 1 then it deletes the session otherwise it closes the window
    */
@@ -1688,16 +1692,17 @@ MarionetteDriverActor.prototype.requestT
   "getElementAttribute": MarionetteDriverActor.prototype.getElementAttribute,
   "getElementText": MarionetteDriverActor.prototype.getElementText,
   "getElementTagName": MarionetteDriverActor.prototype.getElementTagName,
   "isElementDisplayed": MarionetteDriverActor.prototype.isElementDisplayed,
   "getElementSize": MarionetteDriverActor.prototype.getElementSize,
   "isElementEnabled": MarionetteDriverActor.prototype.isElementEnabled,
   "isElementSelected": MarionetteDriverActor.prototype.isElementSelected,
   "sendKeysToElement": MarionetteDriverActor.prototype.sendKeysToElement,
+  "getElementPosition": MarionetteDriverActor.prototype.getElementPosition,
   "clearElement": MarionetteDriverActor.prototype.clearElement,
   "getTitle": MarionetteDriverActor.prototype.getTitle,
   "getPageSource": MarionetteDriverActor.prototype.getPageSource,
   "goUrl": MarionetteDriverActor.prototype.goUrl,
   "getUrl": MarionetteDriverActor.prototype.getUrl,
   "goBack": MarionetteDriverActor.prototype.goBack,
   "goForward": MarionetteDriverActor.prototype.goForward,
   "refresh":  MarionetteDriverActor.prototype.refresh,
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -104,16 +104,17 @@ function startListeners() {
   addMessageListenerId("Marionette:getElementAttribute", getElementAttribute);
   addMessageListenerId("Marionette:getElementText", getElementText);
   addMessageListenerId("Marionette:getElementTagName", getElementTagName);
   addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed);
   addMessageListenerId("Marionette:getElementSize", getElementSize);
   addMessageListenerId("Marionette:isElementEnabled", isElementEnabled);
   addMessageListenerId("Marionette:isElementSelected", isElementSelected);
   addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
+  addMessageListenerId("Marionette:getElementPosition", getElementPosition);
   addMessageListenerId("Marionette:clearElement", clearElement);
   addMessageListenerId("Marionette:switchToFrame", switchToFrame);
   addMessageListenerId("Marionette:deleteSession", deleteSession);
   addMessageListenerId("Marionette:sleepSession", sleepSession);
   addMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult);
   addMessageListenerId("Marionette:importScript", importScript);
   addMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
 }
@@ -167,16 +168,17 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:clickElement", clickElement);
   removeMessageListenerId("Marionette:getElementAttribute", getElementAttribute);
   removeMessageListenerId("Marionette:getElementTagName", getElementTagName);
   removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed);
   removeMessageListenerId("Marionette:getElementSize", getElementSize);
   removeMessageListenerId("Marionette:isElementEnabled", isElementEnabled);
   removeMessageListenerId("Marionette:isElementSelected", isElementSelected);
   removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
+  removeMessageListenerId("Marionette:getElementPosition", getElementPosition);
   removeMessageListenerId("Marionette:clearElement", clearElement);
   removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
   removeMessageListenerId("Marionette:deleteSession", deleteSession);
   removeMessageListenerId("Marionette:sleepSession", sleepSession);
   removeMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult);
   removeMessageListenerId("Marionette:importScript", importScript);
   removeMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
   this.elementManager.reset();
@@ -721,16 +723,55 @@ function sendKeysToElement(msg) {
     sendOk();
   }
   catch (e) {
     sendError(e.message, e.code, e.stack);
   }
 }
 
 /**
+ * Get the position of an element
+ */
+function getElementPosition(msg) {
+  try{
+    let el = elementManager.getKnownElement(msg.json.element, curWindow);
+    var x = el.offsetLeft;
+    var y = el.offsetTop;
+    var elementParent = el.offsetParent;
+    while (elementParent != null) {
+      if (elementParent.tagName == "TABLE") {
+        var parentBorder = parseInt(elementParent.border);
+        if (isNaN(parentBorder)) {
+          var parentFrame = elementParent.getAttribute('frame');
+          if (parentFrame != null) {
+            x += 1;
+            y += 1;
+          }
+        } else if (parentBorder > 0) {
+          x += parentBorder;
+          y += parentBorder;
+        }
+      }
+      x += elementParent.offsetLeft;
+      y += elementParent.offsetTop;
+      elementParent = elementParent.offsetParent;
+    }
+
+    let location = {};
+    location.x = x;
+    location.y = y;
+
+    sendResponse({value: location});
+  }
+  catch (e) {
+    sendError(e.message, e.code, e.stack);
+  }
+}
+
+/**
  * Clear the text of an element
  */
 function clearElement(msg) {
   try {
     let el = elementManager.getKnownElement(msg.json.element, curWindow);
     utils.clearElement(el);
     sendOk();
   }