Bug 1363053 - Move element.fromJson/toJson to evaluate module draft
authorAndreas Tolfsen <ato@mozilla.com>
Mon, 08 May 2017 17:05:20 +0100
changeset 576898 5368ca98a914378c2d75b9acd7f432fc5446f925
parent 576897 1178b701781de2b1a5afb7b7d6b4954a3a7a51ba
child 576899 1feaa47013467c7d1ea6ce3e217d0553d77e1c0b
child 576902 bd4b73344eea7393d0018397ecce04da4dbbd6e6
push id58514
push userbmo:ato@mozilla.com
push dateFri, 12 May 2017 13:15:37 +0000
bugs1363053
milestone55.0a1
Bug 1363053 - Move element.fromJson/toJson to evaluate module This patch renames element.fromJson and element.toJson to evaluate.fromJSON and evaluate.toJSON, respectively. The JSON (de)serialisation code belongs more naturally in the evaluate module, which did not exist when these functions were written. MozReview-Commit-ID: FJGbjGD1kZ6
testing/marionette/driver.js
testing/marionette/element.js
testing/marionette/evaluate.js
testing/marionette/legacyaction.js
testing/marionette/listener.js
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -885,19 +885,19 @@ GeckoDriver.prototype.execute_ = functio
       let sb = this.sandboxes.get(opts.sandboxName, opts.newSandbox);
       if (opts.sandboxName) {
         sb = sandbox.augment(sb, new logging.Adapter(this.marionetteLog));
         sb = sandbox.augment(sb, {global: sb});
       }
 
       opts.timeout = timeout;
       script = this.importedScripts.for(Context.CHROME).concat(script);
-      let wargs = element.fromJson(args, this.curBrowser.seenEls, sb.window);
+      let wargs = evaluate.fromJSON(args, this.curBrowser.seenEls, sb.window);
       let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
-      return evaluatePromise.then(res => element.toJson(res, this.curBrowser.seenEls));
+      return evaluatePromise.then(res => evaluate.toJSON(res, this.curBrowser.seenEls));
   }
 };
 
 /**
  * Execute pure JavaScript.  Used to execute simpletest harness tests,
  * which are like mochitests only injected using Marionette.
  *
  * Scripts are expected to call the {@code finish} global when done.
@@ -911,31 +911,31 @@ GeckoDriver.prototype.executeJSScript = 
   let opts = {
     filename: cmd.parameters.filename,
     line: cmd.parameters.line,
     async: cmd.parameters.async,
   };
 
   switch (this.context) {
     case Context.CHROME:
-      let wargs = element.fromJson(args, this.curBrowser.seenEls, win);
+      let wargs = evaluate.fromJSON(args, this.curBrowser.seenEls, win);
       let harness = new simpletest.Harness(
           win,
           Context.CHROME,
           this.marionetteLog,
           scriptTimeout,
           function() {},
           this.testName);
 
       let sb = sandbox.createSimpleTest(win, harness);
       // TODO(ato): Not sure this is needed:
       sb = sandbox.augment(sb, new logging.Adapter(this.marionetteLog));
 
       let res = yield evaluate.sandbox(sb, script, wargs, opts);
-      resp.body.value = element.toJson(res, this.curBrowser.seenEls);
+      resp.body.value = evaluate.toJSON(res, this.curBrowser.seenEls);
       break;
 
     case Context.CONTENT:
       resp.body.value = yield this.listener.executeSimpleTest(script, args, scriptTimeout, opts);
       break;
   }
 };
 
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 
 Cu.import("chrome://marionette/content/assert.js");
 Cu.import("chrome://marionette/content/atom.js");
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/wait.js");
 
@@ -607,127 +607,16 @@ element.isWebElementReference = function
 };
 
 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;
-      }
-  }
-};
-
-/**
- * Convert arbitrary objects to JSON-safe primitives that can be
- * transported over the Marionette protocol.
- *
- * Any DOM elements are converted to web elements by looking them up
- * and/or adding them to the element store provided.
- *
- * @param {?} obj
- *     Object to be marshaled.
- * @param {element.Store} seenEls
- *     Element store to use for lookup of web element references.
- *
- * @return {?}
- *     Same object as provided by |obj| with the elements replaced by
- *     web elements.
- */
-element.toJson = function (obj, seenEls) {
-  let t = Object.prototype.toString.call(obj);
-
-  // null
-  if (t == "[object Undefined]" || t == "[object Null]") {
-    return null;
-  }
-
-  // literals
-  else if (t == "[object Boolean]" || t == "[object Number]" || t == "[object String]") {
-    return obj;
-  }
-
-  // Array, NodeList, HTMLCollection, et al.
-  else if (element.isCollection(obj)) {
-    return [...obj].map(el => element.toJson(el, seenEls));
-  }
-
-  // HTMLElement
-  else if ("nodeType" in obj && obj.nodeType == 1) {
-    let uuid = seenEls.add(obj);
-    return element.makeWebElement(uuid);
-  }
-
-  // arbitrary objects + files
-  else {
-    let rv = {};
-    for (let prop in obj) {
-      try {
-        rv[prop] = element.toJson(obj[prop], seenEls);
-      } catch (e if (e.result == Cr.NS_ERROR_NOT_IMPLEMENTED)) {
-        logger.debug(`Skipping ${prop}: ${e.message}`);
-      }
-    }
-    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 {Container} container
  *     Container with |frame|, which is the window object that contains
  *     the element, and an optional |shadowRoot|.
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -1,21 +1,22 @@
 /* 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/. */
 
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/error.js");
 
 const logger = Log.repository.getLogger("Marionette");
 
 this.EXPORTED_SYMBOLS = ["evaluate", "sandbox", "Sandboxes"];
 
 const ARGUMENTS = "__webDriverArguments";
 const CALLBACK = "__webDriverCallback";
@@ -168,16 +169,126 @@ evaluate.sandbox = function (sb, script,
 
   return promise.then(res => {
     clearTimeout(scriptTimeoutID);
     sb.window.removeEventListener("unload", unloadHandler);
     return res;
   });
 };
 
+/**
+ * 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.
+ */
+evaluate.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 => evaluate.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] = evaluate.fromJSON(obj[prop], seenEls, win, shadowRoot);
+        }
+        return rv;
+      }
+  }
+};
+
+/**
+ * Convert arbitrary objects to JSON-safe primitives that can be
+ * transported over the Marionette protocol.
+ *
+ * Any DOM elements are converted to web elements by looking them up
+ * and/or adding them to the element store provided.
+ *
+ * @param {?} obj
+ *     Object to be marshaled.
+ * @param {element.Store} seenEls
+ *     Element store to use for lookup of web element references.
+ *
+ * @return {?}
+ *     Same object as provided by |obj| with the elements replaced by
+ *     web elements.
+ */
+evaluate.toJSON = function (obj, seenEls) {
+  let t = Object.prototype.toString.call(obj);
+
+  // null
+  if (t == "[object Undefined]" || t == "[object Null]") {
+    return null;
+  }
+
+  // literals
+  else if (t == "[object Boolean]" || t == "[object Number]" || t == "[object String]") {
+    return obj;
+  }
+
+  // Array, NodeList, HTMLCollection, et al.
+  else if (element.isCollection(obj)) {
+    return [...obj].map(el => evaluate.toJSON(el, seenEls));
+  }
+
+  // HTMLElement
+  else if ("nodeType" in obj && obj.nodeType == 1) {
+    let uuid = seenEls.add(obj);
+    return element.makeWebElement(uuid);
+  }
+
+  // arbitrary objects + files
+  else {
+    let rv = {};
+    for (let prop in obj) {
+      try {
+        rv[prop] = evaluate.toJSON(obj[prop], seenEls);
+      } catch (e if (e.result == Cr.NS_ERROR_NOT_IMPLEMENTED)) {
+        logger.debug(`Skipping ${prop}: ${e.message}`);
+      }
+    }
+    return rv;
+  }
+};
+
 this.sandbox = {};
 
 /**
  * Provides a safe way to take an object defined in a privileged scope and
  * create a structured clone of it in a less-privileged scope.  It returns
  * a reference to the clone.
  *
  * Unlike for |Components.utils.cloneInto|, |obj| may contain functions
--- a/testing/marionette/legacyaction.js
+++ b/testing/marionette/legacyaction.js
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 
 Cu.import("chrome://marionette/content/element.js");
+Cu.import("chrome://marionette/content/evaluate.js");
 Cu.import("chrome://marionette/content/event.js");
 
 const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
 const DEFAULT_CONTEXT_MENU_DELAY = 750;  // ms
 
 this.EXPORTED_SYMBOLS = ["legacyaction"];
 
 const logger = Log.repository.getLogger("Marionette");
@@ -54,17 +55,17 @@ action.Chain.prototype.dispatchActions =
   // 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.seenEls = seenEls;
   this.container = container;
-  let commandArray = element.fromJson(
+  let commandArray = evaluate.fromJSON(
       args, seenEls, container.frame, container.shadowRoot);
 
   if (touchId == null) {
     touchId = this.nextTouchId++;
   }
 
   if (!container.frame.document.createTouch) {
     this.mouseEventsOnly = true;
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -733,42 +733,42 @@ 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 = element.fromJson(
+  let wargs = evaluate.fromJSON(
       args, seenEls, curContainer.frame, curContainer.shadowRoot);
   let res = yield evaluate.sandbox(sb, script, wargs, opts);
 
-  return element.toJson(res, seenEls);
+  return evaluate.toJSON(res, seenEls);
 }
 
 function* executeInSandbox(script, args, timeout, opts) {
   opts.timeout = timeout;
   script = importedScripts.for("content").concat(script);
 
   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 wargs = element.fromJson(
+  let wargs = evaluate.fromJSON(
       args, seenEls, curContainer.frame, curContainer.shadowRoot);
   let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
 
   let res = yield evaluatePromise;
   sendSyncMessage(
       "Marionette:shareData",
-      {log: element.toJson(contentLog.get(), seenEls)});
-  return element.toJson(res, seenEls);
+      {log: evaluate.toJSON(contentLog.get(), seenEls)});
+  return evaluate.toJSON(res, seenEls);
 }
 
 function* executeSimpleTest(script, args, timeout, opts) {
   opts.timeout = timeout;
   let win = curContainer.frame;
   script = importedScripts.for("content").concat(script);
 
   let harness = new simpletest.Harness(
@@ -776,25 +776,25 @@ 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 = element.fromJson(
+  let wargs = evaluate.fromJSON(
       args, seenEls, curContainer.frame, curContainer.shadowRoot);
   let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
 
   let res = yield evaluatePromise;
   sendSyncMessage(
       "Marionette:shareData",
-      {log: element.toJson(contentLog.get(), seenEls)});
-  return element.toJson(res, seenEls);
+      {log: evaluate.toJSON(contentLog.get(), seenEls)});
+  return evaluate.toJSON(res, seenEls);
 }
 
 /**
  * Sets the test name, used in logging messages.
  */
 function setTestName(msg) {
   marionetteTestName = msg.json.value;
   sendOk(msg.json.command_id);
@@ -828,17 +828,17 @@ function emitTouchEvent(type, touch) {
       }
     }
     // we get here if we're not in asyncPacZoomEnabled land, or if we're the main process
     /*
     Disabled per bug 888303
     contentLog.log(loggingInfo, "TRACE");
     sendSyncMessage(
         "Marionette:shareData",
-        {log: element.toJson(contentLog.get(), seenEls)});
+        {log: evaluate.toJSON(contentLog.get(), seenEls)});
     contentLog.clear();
     */
     let domWindowUtils = curContainer.frame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
     domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.clientX], [touch.clientY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
   }
 }
 
 /**
@@ -1063,17 +1063,17 @@ 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 = element.fromJson(
+  let commandArray = evaluate.fromJSON(
       args, seenEls, 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]
@@ -1281,17 +1281,17 @@ function* findElementsContent(strategy, 
   let elRefs = seenEls.addAll(els);
   let webEls = elRefs.map(element.makeWebElement);
   return webEls;
 }
 
 /** Find and return the active element on the page. */
 function getActiveElement() {
   let el = curContainer.frame.document.activeElement;
-  return element.toJson(el, seenEls);
+  return evaluate.toJSON(el, seenEls);
 }
 
 /**
  * Send click event to element.
  *
  * @param {number} command_id
  *     ID of the currently handled message between the driver and listener.
  * @param {WebElement} id
@@ -1637,17 +1637,17 @@ function switchToFrame(msg) {
 
   if (foundFrame === null) {
     sendError(new NoSuchFrameError("Unable to locate frame: " + (msg.json.id || msg.json.element)), command_id);
     return true;
   }
 
   // send a synchronous message to let the server update the currently active
   // frame element (for getActiveFrame)
-  let frameValue = element.toJson(
+  let frameValue = evaluate.toJSON(
       curContainer.frame.wrappedJSObject, seenEls)[element.Key];
   sendSyncMessage("Marionette:switchedToFrame", {frameValue: frameValue});
 
   if (curContainer.frame.contentWindow === null) {
     // The frame we want to switch to is a remote/OOP frame;
     // notify our parent to handle the switch
     curContainer.frame = content;
     let rv = {win: parWindow, frame: foundFrame};