Bug 803096: add the ability to get an elements location with marionette; r=mdas
authorDavid Burns <dburns@mozilla.com>
Fri, 19 Oct 2012 21:35:30 +0100
changeset 110948 49bf10b166f9acf1722fa3fa73d21c7ae0e77d1f
parent 110947 b47dfc639029e7b6b2aab2b923822641e665d001
child 110949 b2f24b1dc78670c0aa8691c18f885663dbb51425
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersmdas
bugs803096
milestone19.0a1
Bug 803096: add the ability to get an elements location with marionette; r=mdas
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
@@ -74,16 +74,20 @@ class HTMLElement(object):
 
     def is_displayed(self):
         return self.marionette._send_message('isElementDisplayed', '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
@@ -1386,16 +1386,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
    */
@@ -1682,16 +1686,17 @@ MarionetteDriverActor.prototype.requestT
   "clickElement": MarionetteDriverActor.prototype.clickElement,
   "getElementAttribute": MarionetteDriverActor.prototype.getElementAttribute,
   "getElementText": MarionetteDriverActor.prototype.getElementText,
   "getElementTagName": MarionetteDriverActor.prototype.getElementTagName,
   "isElementDisplayed": MarionetteDriverActor.prototype.isElementDisplayed,
   "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
@@ -103,16 +103,17 @@ function startListeners() {
   addMessageListenerId("Marionette:clickElement", clickElement);
   addMessageListenerId("Marionette:getElementAttribute", getElementAttribute);
   addMessageListenerId("Marionette:getElementText", getElementText);
   addMessageListenerId("Marionette:getElementTagName", getElementTagName);
   addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed);
   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);
 }
@@ -165,16 +166,17 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:findElementsContent", findElementsContent);
   removeMessageListenerId("Marionette:clickElement", clickElement);
   removeMessageListenerId("Marionette:getElementAttribute", getElementAttribute);
   removeMessageListenerId("Marionette:getElementTagName", getElementTagName);
   removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed);
   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();
@@ -705,16 +707,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();
   }