Bug 1623581 - [remote] Add "returnByValue" support to Runtime.evaluate. r=remote-protocol-reviewers,maja_zf

Differential Revision: https://phabricator.services.mozilla.com/D67841

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Henrik Skupin 2020-03-24 20:32:19 +00:00
parent bc1a51934e
commit 2d184d685d
3 changed files with 131 additions and 10 deletions

View File

@ -223,11 +223,14 @@ class Runtime extends ContentProcessDomain {
* The evaluation result, and optionally exception details.
*/
evaluate(options = {}) {
const { expression, contextId } = options;
const { expression, contextId, returnByValue = false } = options;
if (typeof expression != "string") {
throw new Error("expression: string value expected");
}
if (typeof returnByValue != "boolean") {
throw new Error("returnByValue: boolean value expected");
}
let context;
if (typeof contextId != "undefined") {
@ -239,7 +242,7 @@ class Runtime extends ContentProcessDomain {
context = this._getDefaultContextForWindow();
}
return context.evaluate(expression);
return context.evaluate(expression, returnByValue);
}
getProperties({ objectId, ownProperties }) {

View File

@ -76,13 +76,18 @@ class ExecutionContext {
*
* @param {String} expression
* The JS expression to evaluate against the JS context.
* @return {Object} A multi-form object depending if the execution succeed or failed.
* If the expression failed to evaluate, it will return an object with an
* `exceptionDetails` attribute matching the `ExceptionDetails` CDP type.
* Otherwise it will return an object with `result` attribute whose type is
* @param {boolean} returnByValue
* Whether the result is expected to be a JSON object
* that should be sent by value.
*
* @return {Object} A multi-form object depending if the execution
* succeed or failed. If the expression failed to evaluate,
* it will return an object with an `exceptionDetails` attribute
* matching the `ExceptionDetails` CDP type. Otherwise it will
* return an object with `result` attribute whose type is
* `RemoteObject` CDP type.
*/
evaluate(expression) {
evaluate(expression, returnByValue) {
let rv = this._debuggee.executeInGlobal(expression);
if (!rv) {
return {
@ -91,12 +96,19 @@ class ExecutionContext {
},
};
}
if (rv.throw) {
return this._returnError(rv.throw);
}
return {
result: this._toRemoteObject(rv.return),
};
let result;
if (returnByValue) {
result = this._toRemoteObjectByValue(result);
} else {
result = this._toRemoteObject(rv.return);
}
return { result };
}
/**
@ -437,6 +449,7 @@ class ExecutionContext {
*
* @param {Debugger.Object} obj
* The object to convert
*
* @return {Object}
* The converted object
*/

View File

@ -152,6 +152,111 @@ add_task(async function returnAsObjectUndefined({ client }) {
);
});
add_task(async function returnByValueInvalidTypes({ client }) {
const { Runtime } = client;
await enableRuntime(client);
for (const returnByValue of [null, 1, "foo", [], {}]) {
let errorThrown = "";
try {
await Runtime.evaluate({
expression: "",
returnByValue,
});
} catch (e) {
errorThrown = e.message;
}
ok(errorThrown.includes("returnByValue: boolean value expected"));
}
});
add_task(async function returnByValue({ client }) {
const { Runtime } = client;
await enableRuntime(client);
const values = [
null,
42,
42.0,
"42",
true,
false,
{ foo: true },
{ foo: { bar: 42, str: "str", array: [1, 2, 3] } },
[42, "42", true],
[{ foo: true }],
];
for (const value of values) {
const { result } = await Runtime.evaluate({
expression: `(${JSON.stringify(value)})`,
returnByValue: true,
});
Assert.deepEqual(
result,
{
type: typeof value,
value,
description: value != null ? value.toString() : value,
},
`Returned expected value for ${JSON.stringify(value)}`
);
}
});
add_task(async function returnByValueNotSerializable({ client }) {
const { Runtime } = client;
await enableRuntime(client);
const notSerializableNumbers = {
number: ["-0", "NaN", "Infinity", "-Infinity"],
bigint: ["42n"],
};
for (const type in notSerializableNumbers) {
for (const unserializableValue of notSerializableNumbers[type]) {
const { result } = await Runtime.evaluate({
expression: `(${unserializableValue})`,
returnByValue: true,
});
Assert.deepEqual(
result,
{
type,
unserializableValue,
description: unserializableValue,
},
`Returned expected value for ${JSON.stringify(unserializableValue)}`
);
}
}
});
// Test undefined individually as JSON.stringify doesn't return a string
add_task(async function returnByValueUndefined({ client }) {
const { Runtime } = client;
await enableRuntime(client);
const { result } = await Runtime.evaluate({
expression: "undefined",
returnByValue: true,
});
Assert.deepEqual(
result,
{
type: "undefined",
},
"Undefined type is correct"
);
});
add_task(async function exceptionDetailsJavascriptError({ client }) {
const { Runtime } = client;