Bug 1408509 - Add web frame, web window, and chrome element support to Marionette client. r?whimboo draft
authorAndreas Tolfsen <ato@sny.no>
Tue, 19 Jun 2018 19:54:10 +0100
changeset 810826 73b4fe068be41301f0d98b5a3be6ee3a6ae47c12
parent 810825 673335c7b62c4163d894d5a39fc8b7aa0195b280
child 810827 7d370b365d635321cbb61f13085f034671fa4118
push id114124
push userbmo:ato@sny.no
push dateTue, 26 Jun 2018 15:47:34 +0000
reviewerswhimboo
bugs1408509
milestone63.0a1
Bug 1408509 - Add web frame, web window, and chrome element support to Marionette client. r?whimboo MozReview-Commit-ID: 6HFzfYckrB5
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -19,23 +19,29 @@ from six import reraise
 
 from . import errors
 from . import transport
 from .decorators import do_process_check
 from .geckoinstance import GeckoInstance
 from .keys import Keys
 from .timeout import Timeouts
 
-WEBELEMENT_KEY = "ELEMENT"
-W3C_WEBELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
+CHROME_ELEMENT_KEY = "chromeelement-9fc5-4b51-a3c8-01716eedeb04"
+FRAME_KEY = "frame-075b-4da1-b6ba-e579c2d3230a"
+LEGACY_ELEMENT_KEY = "ELEMENT"
+WEB_ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
+WINDOW_KEY = "window-fcc6-11e5-b4f8-330a88ab9d7f"
 
 
 class HTMLElement(object):
     """Represents a DOM Element."""
 
+    identifiers = (CHROME_ELEMENT_KEY, FRAME_KEY, WINDOW_KEY,
+                   LEGACY_ELEMENT_KEY, WEB_ELEMENT_KEY)
+
     def __init__(self, marionette, id):
         self.marionette = marionette
         assert(id is not None)
         self.id = id
 
     def __str__(self):
         return self.id
 
@@ -171,16 +177,31 @@ class HTMLElement(object):
         """Gets the value of the specified CSS property name.
 
         :param property_name: Property name to get the value of.
         """
         body = {"id": self.id, "propertyName": property_name}
         return self.marionette._send_message("WebDriver:GetElementCSSValue",
                                              body, key="value")
 
+    @classmethod
+    def _from_json(cls, json, marionette):
+        if isinstance(json, dict):
+            if WEB_ELEMENT_KEY in json:
+                return cls(marionette, json[WEB_ELEMENT_KEY])
+            elif LEGACY_ELEMENT_KEY in json:
+                return cls(marionette, json[LEGACY_ELEMENT_KEY])
+            elif CHROME_ELEMENT_KEY in json:
+                return cls(marionette, json[CHROME_ELEMENT_KEY])
+            elif FRAME_KEY in json:
+                return cls(marionette, json[FRAME_KEY])
+            elif WINDOW_KEY in json:
+                return cls(marionette, json[WINDOW_KEY])
+        raise ValueError("Unrecognised web element")
+
 
 class MouseButton(object):
     """Enum-like class for mouse button constants."""
     LEFT = 0
     MIDDLE = 1
     RIGHT = 2
 
 
@@ -740,22 +761,18 @@ class Marionette(object):
             self._handle_error(err)
 
         if key is not None:
             return self._unwrap_response(res.get(key))
         else:
             return self._unwrap_response(res)
 
     def _unwrap_response(self, value):
-        if isinstance(value, dict) and (WEBELEMENT_KEY in value or
-                                        W3C_WEBELEMENT_KEY in value):
-            if value.get(WEBELEMENT_KEY):
-                return HTMLElement(self, value.get(WEBELEMENT_KEY))
-            else:
-                return HTMLElement(self, value.get(W3C_WEBELEMENT_KEY))
+        if isinstance(value, dict) and any(k in value.keys() for k in HTMLElement.identifiers):
+            return HTMLElement._from_json(value, self)
         elif isinstance(value, list):
             return list(self._unwrap_response(item) for item in value)
         else:
             return value
 
     def _handle_error(self, obj):
         error = obj["error"]
         message = obj["message"]
@@ -1588,42 +1605,39 @@ class Marionette(object):
             wrapped = []
             for arg in args:
                 wrapped.append(self._to_json(arg))
         elif isinstance(args, dict):
             wrapped = {}
             for arg in args:
                 wrapped[arg] = self._to_json(args[arg])
         elif type(args) == HTMLElement:
-            wrapped = {W3C_WEBELEMENT_KEY: args.id,
-                       WEBELEMENT_KEY: args.id}
+            wrapped = {WEB_ELEMENT_KEY: args.id,
+                       LEGACY_ELEMENT_KEY: args.id}
         elif (isinstance(args, bool) or isinstance(args, basestring) or
               isinstance(args, int) or isinstance(args, float) or args is None):
             wrapped = args
         return wrapped
 
     def _from_json(self, value):
         if isinstance(value, list):
             unwrapped = []
             for item in value:
                 unwrapped.append(self._from_json(item))
+            return unwrapped
         elif isinstance(value, dict):
             unwrapped = {}
             for key in value:
-                if key == W3C_WEBELEMENT_KEY:
-                    unwrapped = HTMLElement(self, value[key])
-                    break
-                elif key == WEBELEMENT_KEY:
-                    unwrapped = HTMLElement(self, value[key])
-                    break
+                if key in HTMLElement.identifiers:
+                    return HTMLElement._from_json(value[key], self)
                 else:
                     unwrapped[key] = self._from_json(value[key])
+            return unwrapped
         else:
-            unwrapped = value
-        return unwrapped
+            return value
 
     def execute_script(self, script, script_args=(), new_sandbox=True,
                        sandbox="default", script_timeout=None):
         """Executes a synchronous JavaScript script, and returns the
         result (or None if the script does return a value).
 
         The script is executed in the context set by the most recent
         :func:`set_context` call, or to the CONTEXT_CONTENT context if
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
@@ -3,17 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 
 import urllib
 
 from marionette_driver import By, errors, Wait
 from marionette_driver.keys import Keys
-from marionette_driver.marionette import W3C_WEBELEMENT_KEY
+from marionette_driver.marionette import WEB_ELEMENT_KEY
 
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
     return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
 
 
@@ -35,19 +35,17 @@ class Actions(object):
             "type": "pointer"
         }]}
 
         return self.marionette._send_message("performActions", params=params)
 
     def move(self, element, x=0, y=0, duration=250):
         self.action_chain.append({
             "duration": duration,
-            "origin": {
-                W3C_WEBELEMENT_KEY: element.id
-            },
+            "origin": {WEB_ELEMENT_KEY: element.id},
             "type": "pointerMove",
             "x": x,
             "y": y,
         })
 
         return self
 
     def click(self):