зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1106913 - Detect cyclic objects when marshaling objects. r=whimboo
Marionette does currently not test for cyclic object references as it marshals return values for transport across the wire. Example of cyclic object: let obj = {}; obj.cyclic = obj; Passing this through evalaute.toJSON currently causes an infinite recursion due to obj being referenced inside itself. We can use JSON.stringify to test if obj contains such cyclic values. It is assumed that the input to assert.acyclic is already JSON safe, so it can be parsed by JSON.stringify, because of the previous checks it has made. MozReview-Commit-ID: 4CnY2dcW5IF --HG-- extra : rebase_source : e1a5fb595ad487fa47566bad5c2129a79c1d7b34
This commit is contained in:
Родитель
17e02f9dbb
Коммит
7fe6af9075
|
@ -11,6 +11,7 @@ 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/assert.js");
|
||||
const {
|
||||
element,
|
||||
WebElement,
|
||||
|
@ -243,11 +244,37 @@ evaluate.fromJSON = function(obj, seenEls = undefined, window = undefined) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Convert arbitrary objects to JSON-safe primitives that can be
|
||||
* Marshal 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.
|
||||
* The marshaling rules are as follows:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>
|
||||
* Primitives are returned as is.
|
||||
*
|
||||
* <li>
|
||||
* Collections, such as <code>Array</code>, <code>NodeList</code>,
|
||||
* <code>HTMLCollection</code> et al. are expanded to arrays and
|
||||
* then recursed.
|
||||
*
|
||||
* <li>
|
||||
* Elements that are not known web elements are added to the
|
||||
* <var>seenEls</var> element store. Once known, the elements'
|
||||
* associated web element representation is returned.
|
||||
*
|
||||
* <li>
|
||||
* Objects with custom JSON representations, i.e. if they have a
|
||||
* callable <code>toJSON</code> function, are returned verbatim.
|
||||
* This means their internal integrity <em>are not</em> checked.
|
||||
* Be careful.
|
||||
*
|
||||
* <li>
|
||||
* Other arbitrary objects are first tested for cyclic references
|
||||
* and then recursed into.
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* @param {Object} obj
|
||||
* Object to be marshaled.
|
||||
|
@ -257,6 +284,9 @@ evaluate.fromJSON = function(obj, seenEls = undefined, window = undefined) {
|
|||
* @return {Object}
|
||||
* Same object as provided by <var>obj</var> with the elements
|
||||
* replaced by web elements.
|
||||
*
|
||||
* @throws {JavaScriptError}
|
||||
* If an object contains cyclic references.
|
||||
*/
|
||||
evaluate.toJSON = function(obj, seenEls) {
|
||||
const t = Object.prototype.toString.call(obj);
|
||||
|
@ -273,13 +303,14 @@ evaluate.toJSON = function(obj, seenEls) {
|
|||
|
||||
// Array, NodeList, HTMLCollection, et al.
|
||||
} else if (element.isCollection(obj)) {
|
||||
assert.acyclic(obj);
|
||||
return [...obj].map(el => evaluate.toJSON(el, seenEls));
|
||||
|
||||
// WebElement
|
||||
} else if (WebElement.isReference(obj)) {
|
||||
return obj;
|
||||
|
||||
// Element (HTMLElement, SVGElement, XULElement, &c.)
|
||||
// Element (HTMLElement, SVGElement, XULElement, et al.)
|
||||
} else if (element.isElement(obj)) {
|
||||
let webEl = seenEls.add(obj);
|
||||
return webEl.toJSON();
|
||||
|
@ -293,6 +324,8 @@ evaluate.toJSON = function(obj, seenEls) {
|
|||
// arbitrary objects + files
|
||||
let rv = {};
|
||||
for (let prop in obj) {
|
||||
assert.acyclic(obj[prop]);
|
||||
|
||||
try {
|
||||
rv[prop] = evaluate.toJSON(obj[prop], seenEls);
|
||||
} catch (e) {
|
||||
|
|
|
@ -373774,6 +373774,12 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"webdriver/tests/execute_script/cyclic.py": [
|
||||
[
|
||||
"/webdriver/tests/execute_script/cyclic.py",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"webdriver/tests/execute_script/user_prompts.py": [
|
||||
[
|
||||
"/webdriver/tests/execute_script/user_prompts.py",
|
||||
|
@ -575624,7 +575630,7 @@
|
|||
"support"
|
||||
],
|
||||
"wasm/wasm_local_iframe_test.html": [
|
||||
"dd715a4da792b9d8d634536d938b278230c66df5",
|
||||
"e7b86a731b7cf7c122a1e37118cebce7342291fc",
|
||||
"testharness"
|
||||
],
|
||||
"wasm/wasm_serialization_tests.html": [
|
||||
|
@ -576432,7 +576438,7 @@
|
|||
"wdspec"
|
||||
],
|
||||
"webdriver/tests/actions/modifier_click.py": [
|
||||
"be31579cae0cb3dd26a913ce0d966be72fd79495",
|
||||
"a41f28b359c950af698be51ef35e4d78dca53e2c",
|
||||
"wdspec"
|
||||
],
|
||||
"webdriver/tests/actions/mouse.py": [
|
||||
|
@ -576547,6 +576553,10 @@
|
|||
"e31edd4537f9b7479a348465154381f5b18f938c",
|
||||
"wdspec"
|
||||
],
|
||||
"webdriver/tests/execute_script/cyclic.py": [
|
||||
"cbebfbd2413ea0b10f547ab66fcc7159898e684a",
|
||||
"wdspec"
|
||||
],
|
||||
"webdriver/tests/execute_script/user_prompts.py": [
|
||||
"901487f8270dcce693867ca090393e093d26f22b",
|
||||
"wdspec"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[json_serialize_windowproxy.py]
|
||||
expected: TIMEOUT
|
||||
|
||||
[json_serialize_windowproxy.py::test_initial_window]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
[user_prompts.py]
|
||||
expected: ERROR
|
||||
|
||||
[user_prompts.py::test_handle_prompt_accept]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
from tests.support.asserts import assert_error
|
||||
|
||||
|
||||
def execute_script(session, script, args=None):
|
||||
if args is None:
|
||||
args = []
|
||||
body = {"script": script, "args": args}
|
||||
return session.transport.send(
|
||||
"POST",
|
||||
"/session/{session_id}/execute/sync".format(
|
||||
session_id=session.session_id),
|
||||
body)
|
||||
|
||||
|
||||
def test_array(session):
|
||||
response = execute_script(session, """
|
||||
let arr = [];
|
||||
arr.push(arr);
|
||||
return arr;
|
||||
""")
|
||||
assert_error(response, "javascript error")
|
||||
|
||||
|
||||
def test_object(session):
|
||||
response = execute_script(session, """
|
||||
let obj = {};
|
||||
obj.reference = obj;
|
||||
return obj;
|
||||
""")
|
||||
assert_error(response, "javascript error")
|
||||
|
||||
|
||||
def test_array_in_object(session):
|
||||
response = execute_script(session, """
|
||||
let arr = [];
|
||||
arr.push(arr);
|
||||
return {arr};
|
||||
""")
|
||||
assert_error(response, "javascript error")
|
||||
|
||||
|
||||
def test_object_in_array(session):
|
||||
response = execute_script(session, """
|
||||
let obj = {};
|
||||
obj.reference = obj;
|
||||
return [obj];
|
||||
""")
|
||||
assert_error(response, "javascript error")
|
Загрузка…
Ссылка в новой задаче