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
--- 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};