Bug 1274274 - Disassociate convertWrappedArguments from element store; r=automatedtester a=test-only
authorAndreas Tolfsen <ato@mozilla.com>
Fri, 20 May 2016 16:16:56 +0100
changeset 333259 46a6bf484749d70ae0d075cc6719763acefaa0fc
parent 333258 2cf01cb36c322b3df61a77eaee5f4ef0f27b9c48
child 333260 9ae0add975e2f299bdfcbd9e57c10340aedf2124
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester, test-only
bugs1274274
milestone48.0a2
Bug 1274274 - Disassociate convertWrappedArguments from element store; r=automatedtester a=test-only Moved ElementManager#convertWrappedArguments to the testing/marionette/element.js module scope and renamed it to fromJson. MozReview-Commit-ID: JMTZcG0JSUE
testing/marionette/action.js
testing/marionette/driver.js
testing/marionette/element.js
testing/marionette/listener.js
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -53,18 +53,19 @@ action.Chain.prototype.dispatchActions =
     touchProvider) {
   // Some touch events code in the listener needs to do ipc, so we can't
   // share this code across chrome/content.
   if (touchProvider) {
     this.touchProvider = touchProvider;
   }
 
   this.elementManager = elementManager;
-  let commandArray = elementManager.convertWrappedArguments(args, container);
   this.container = container;
+  let commandArray = element.fromJson(
+      args, elementManager, container.frame, container.shadowRoot);
 
   if (touchId == null) {
     touchId = this.nextTouchId++;
   }
 
   if (!container.frame.document.createTouch) {
     this.mouseEventsOnly = true;
   }
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -882,17 +882,17 @@ GeckoDriver.prototype.execute_ = functio
       if (opts.sandboxName) {
         sb = sandbox.augment(sb, new logging.Adapter(this.marionetteLog));
         sb = sandbox.augment(sb, {global: sb});
         sb = sandbox.augment(sb, new emulator.Adapter(this.emulator));
       }
 
       opts.timeout = timeout;
       script = this.importedScripts.for(Context.CHROME).concat(script);
-      let wargs = this.curBrowser.elementManager.convertWrappedArguments(args, {frame: sb.window});
+      let wargs = element.fromJson(args, this.curBrowser.elementManager, sb.window);
       let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
       return evaluatePromise.then(res => this.curBrowser.elementManager.wrapValue(res));
   }
 };
 
 /**
  * Execute pure JavaScript.  Used to execute simpletest harness tests,
  * which are like mochitests only injected using Marionette.
@@ -907,17 +907,17 @@ GeckoDriver.prototype.executeJSScript = 
     filename: cmd.parameters.filename,
     line: cmd.parameters.line,
     async: cmd.parameters.async,
   };
 
   switch (this.context) {
     case Context.CHROME:
       let win = this.getCurrentWindow();
-      let wargs = this.curBrowser.elementManager.convertWrappedArguments(args, {frame: win});
+      let wargs = element.fromJson(args, this.curBrowser.elementManager, win);
       let harness = new simpletest.Harness(
           win,
           Context.CHROME,
           this.marionetteLog,
           scriptTimeout,
           function() {},
           this.testName);
 
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -245,66 +245,16 @@ element.Store = class {
             }
           }
         }
         break;
     }
 
     return result;
   }
-
-  /**
-   * Convert any ELEMENT references in 'args' to the actual elements
-   *
-   * @param object args
-   *        Arguments passed in by client
-   * @param nsIDOMWindow, ShadowRoot container
-   *        The window and an optional shadow root that contains the element
-   *
-   * @returns object
-   *        Returns the objects passed in by the client, with the
-   *        reference IDs replaced by the actual elements.
-   */
-  convertWrappedArguments(args, container) {
-    let converted;
-    switch (typeof(args)) {
-      case 'number':
-      case 'string':
-      case 'boolean':
-        converted = args;
-        break;
-      case 'object':
-        if (args == null) {
-          converted = null;
-        }
-        else if (Object.prototype.toString.call(args) == '[object Array]') {
-          converted = [];
-          for (let i in args) {
-            converted.push(this.convertWrappedArguments(args[i], container));
-          }
-        }
-        else if (((typeof(args[element.LegacyKey]) === 'string') && args.hasOwnProperty(element.LegacyKey)) ||
-                 ((typeof(args[element.Key]) === 'string') &&
-                     args.hasOwnProperty(element.Key))) {
-          let elementUniqueIdentifier = args[element.Key] ? args[element.Key] : args[element.LegacyKey];
-          converted = this.get(elementUniqueIdentifier, container);
-          if (converted == null) {
-            throw new WebDriverError(`Unknown element: ${elementUniqueIdentifier}`);
-          }
-        }
-        else {
-          converted = {};
-          for (let prop in args) {
-            converted[prop] = this.convertWrappedArguments(args[prop], container);
-          }
-        }
-        break;
-    }
-    return converted;
-  }
 };
 
 /**
  * Find a single element or a collection of elements starting at the
  * document root or a given node.
  *
  * If |timeout| is above 0, an implicit search technique is used.
  * This will wait for the duration of |timeout| for the element
@@ -701,16 +651,73 @@ element.makeWebElement = function(uuid) 
 };
 
 element.generateUUID = function() {
   let uuid = uuidGen.generateUUID().toString();
   return uuid.substring(1, uuid.length - 1);
 };
 
 /**
+ * Convert any web elements in arbitrary objects to DOM elements by
+ * looking them up in the seen element store.
+ *
+ * @param {?} obj
+ *     Arbitrary object containing web elements.
+ * @param {element.Store} seenEls
+ *     Element store to use for lookup of web element references.
+ * @param {Window} win
+ *     Window.
+ * @param {ShadowRoot} shadowRoot
+ *     Shadow root.
+ *
+ * @return {?}
+ *     Same object as provided by |obj| with the web elements replaced
+ *     by DOM elements.
+ */
+element.fromJson = function(
+    obj, seenEls, win, shadowRoot = undefined) {
+  switch (typeof(obj)) {
+    case "boolean":
+    case "number":
+    case "string":
+      return obj;
+
+    case "object":
+      if (obj === null) {
+        return obj;
+      }
+
+      // arrays
+      else if (Array.isArray(obj)) {
+        return obj.map(e => element.fromJson(e, seenEls, win, shadowRoot));
+      }
+
+      // web elements
+      else if (Object.keys(obj).includes(element.Key) ||
+          Object.keys(obj).includes(element.LegacyKey)) {
+        let uuid = obj[element.Key] || obj[element.LegacyKey];
+        let el = seenEls.get(uuid, {frame: win, shadowRoot: shadowRoot});
+        if (!el) {
+          throw new WebDriverError(`Unknown element: ${uuid}`);
+        }
+        return el;
+      }
+
+      // arbitrary objects
+      else {
+        let rv = {};
+        for (let prop in obj) {
+          rv[prop] = element.fromJson(obj[prop], seenEls, win, shadowRoot);
+        }
+        return rv;
+      }
+  }
+};
+
+/**
  * Check if the element is detached from the current frame as well as
  * the optional shadow root (when inside a Shadow DOM context).
  *
  * @param {nsIDOMElement} el
  *     Element to be checked.
  * @param nsIDOMWindow frame
  *     Window object that contains the element or the current host
  *     of the shadow root.
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -521,17 +521,18 @@ function checkForInterrupted() {
     }
 }
 
 function* execute(script, args, timeout, opts) {
   opts.timeout = timeout;
   script = importedScripts.for("content").concat(script);
 
   let sb = sandbox.createMutable(curContainer.frame);
-  let wargs = elementManager.convertWrappedArguments(args, curContainer);
+  let wargs = element.fromJson(
+      args, elementManager, curContainer.frame, curContainer.shadowRoot);
   let res = yield evaluate.sandbox(sb, script, wargs, opts);
 
   return elementManager.wrapValue(res);
 }
 
 function* executeInSandbox(script, args, timeout, opts) {
   opts.timeout = timeout;
   script = importedScripts.for("content").concat(script);
@@ -539,17 +540,18 @@ function* executeInSandbox(script, args,
   let sb = sandboxes.get(opts.sandboxName, opts.newSandbox);
   if (opts.sandboxName) {
     sb = sandbox.augment(sb, {global: sb});
     sb = sandbox.augment(sb, new logging.Adapter(contentLog));
     let emulatorClient = new emulator.EmulatorServiceClient(asyncChrome);
     sb = sandbox.augment(sb, new emulator.Adapter(emulatorClient));
   }
 
-  let wargs = elementManager.convertWrappedArguments(args, curContainer);
+  let wargs = element.fromJson(
+      args, elementManager, curContainer.frame, curContainer.shadowRoot);
   let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
 
   let res = yield evaluatePromise;
   sendSyncMessage("Marionette:shareData", {log: elementManager.wrapValue(contentLog.get())});
   return elementManager.wrapValue(res);
 }
 
 function* executeSimpleTest(script, args, timeout, opts) {
@@ -562,17 +564,18 @@ function* executeSimpleTest(script, args
       "content",
       contentLog,
       timeout,
       marionetteTestName);
   let sb = sandbox.createSimpleTest(curContainer.frame, harness);
   // TODO(ato): Not sure this is needed:
   sb = sandbox.augment(sb, new logging.Adapter(contentLog));
 
-  let wargs = elementManager.convertWrappedArguments(args, curContainer);
+  let wargs = element.fromJson(
+      args, elementManager, curContainer.frame, curContainer.shadowRoot);
   let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
 
   let res = yield evaluatePromise;
   sendSyncMessage("Marionette:shareData", {log: elementManager.wrapValue(contentLog.get())});
   return elementManager.wrapValue(res);
 }
 
 /**
@@ -840,17 +843,18 @@ function setDispatch(batches, touches, b
 /**
  * Start multi-action.
  *
  * @param {Number} maxLen
  *     Longest action chain for one finger.
  */
 function multiAction(args, maxLen) {
   // unwrap the original nested array
-  let commandArray = elementManager.convertWrappedArguments(args, curContainer);
+  let commandArray = element.fromJson(
+      args, elementManager, curContainer.frame, curContainer.shadowRoot);
   let concurrentEvent = [];
   let temp;
   for (let i = 0; i < maxLen; i++) {
     let row = [];
     for (let j = 0; j < commandArray.length; j++) {
       if (typeof commandArray[j][i] != "undefined") {
         // add finger id to the front of each action, i.e. [finger_id, action, element]
         temp = commandArray[j][i];