Skip to content

Commit

Permalink
Bug 1780752 - [bidi] Handle exception objects which don't support toS…
Browse files Browse the repository at this point in the history
…tring r=webdriver-reviewers,jgraham

Differential Revision: https://phabricator.services.mozilla.com/D152506
  • Loading branch information
juliandescottes committed Jul 22, 2022
1 parent 0b5a7f0 commit 81897c2
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 13 deletions.
28 changes: 26 additions & 2 deletions remote/webdriver-bidi/RemoteValue.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

"use strict";

var EXPORTED_SYMBOLS = ["deserialize", "serialize"];
var EXPORTED_SYMBOLS = ["deserialize", "serialize", "stringify"];

const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
Expand Down Expand Up @@ -418,8 +418,32 @@ function serialize(
}

lazy.logger.warn(
`Unsupported type: ${type} for remote value: ${value.toString()}`
`Unsupported type: ${type} for remote value: ${stringify(value)}`
);

return undefined;
}

/**
* Safely stringify a value.
*
* @param {Object} value
* Value of any type to be stringified.
*
* @returns {String} String representation of the value.
*/
function stringify(obj) {
let text;
try {
text =
obj !== null && typeof obj === "object" ? obj.toString() : String(obj);
} catch (e) {
// The error-case will also be handled in `finally {}`.
} finally {
if (typeof text != "string") {
text = Object.prototype.toString.apply(obj);
}
}

return text;
}
6 changes: 2 additions & 4 deletions remote/webdriver-bidi/modules/windowglobal/script.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
getFramesFromStack: "chrome://remote/content/shared/Stack.jsm",
isChromeFrame: "chrome://remote/content/shared/Stack.jsm",
serialize: "chrome://remote/content/webdriver-bidi/RemoteValue.jsm",
stringify: "chrome://remote/content/webdriver-bidi/RemoteValue.jsm",
});

XPCOMUtils.defineLazyGetter(lazy, "dbg", () => {
Expand Down Expand Up @@ -88,10 +89,7 @@ class ScriptModule extends Module {
exception: lazy.serialize(exception, 1),
lineNumber: stack.line - 1,
stackTrace: { callFrames },
text:
typeof exception === "object"
? exception.toString()
: String(exception),
text: lazy.stringify(exception),
};
}

Expand Down
40 changes: 39 additions & 1 deletion remote/webdriver-bidi/test/xpcshell/test_RemoteValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ const REMOTE_COMPLEX_VALUES = [
},
];

const { deserialize, serialize } = ChromeUtils.import(
const { deserialize, serialize, stringify } = ChromeUtils.import(
"chrome://remote/content/webdriver-bidi/RemoteValue.jsm"
);

Expand Down Expand Up @@ -557,3 +557,41 @@ add_test(function test_serializeRemoteComplexValues() {

run_next_test();
});

add_test(function test_stringify() {
const STRINGIFY_TEST_CASES = [
[undefined, "undefined"],
[null, "null"],
["foobar", "foobar"],
["2", "2"],
[-0, "0"],
[Infinity, "Infinity"],
[-Infinity, "-Infinity"],
[3, "3"],
[1.4, "1.4"],
[true, "true"],
[42n, "42"],
[{ toString: () => "bar" }, "bar", "toString: () => 'bar'"],
[{ toString: () => 4 }, "[object Object]", "toString: () => 4"],
[{ toString: undefined }, "[object Object]", "toString: undefined"],
[{ toString: null }, "[object Object]", "toString: null"],
[
{
toString: () => {
throw new Error("toString error");
},
},
"[object Object]",
"toString: () => { throw new Error('toString error'); }",
],
];

for (const [value, expectedString, description] of STRINGIFY_TEST_CASES) {
info(`Checking '${description || value}'`);
const stringifiedValue = stringify(value);

Assert.strictEqual(expectedString, stringifiedValue, "Got expected string");
}

run_next_test();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import pytest

from webdriver.bidi.modules.script import ContextTarget, ScriptEvaluateResultException


@pytest.mark.asyncio
@pytest.mark.parametrize("await_promise", [True, False])
@pytest.mark.parametrize(
"expression",
[
"null",
"{ toString: 'not a function' }",
"{ toString: () => {{ throw 'toString not allowed'; }} }",
"{ toString: () => true }",
],
)
@pytest.mark.asyncio
async def test_call_function_without_to_string_interface(
bidi_session, top_context, await_promise, expression
):
function_declaration = "()=>{throw { toString: 'not a function' } }"
if await_promise:
function_declaration = "async" + function_declaration

with pytest.raises(ScriptEvaluateResultException) as exception:
await bidi_session.script.call_function(
function_declaration=function_declaration,
await_promise=await_promise,
target=ContextTarget(top_context["context"]),
)

assert "exceptionDetails" in exception.value.result
exceptionDetails = exception.value.result["exceptionDetails"]

assert "text" in exceptionDetails
assert isinstance(exceptionDetails["text"], str)


@pytest.mark.asyncio
@pytest.mark.parametrize("await_promise", [True, False])
@pytest.mark.parametrize(
"expression",
[
"null",
"{ toString: 'not a function' }",
"{ toString: () => {{ throw 'toString not allowed'; }} }",
"{ toString: () => true }",
],
)
@pytest.mark.asyncio
async def test_evaluate_without_to_string_interface(
bidi_session, top_context, await_promise, expression
):
if await_promise:
expression = f"Promise.reject({expression})"
else:
expression = f"throw {expression}"

with pytest.raises(ScriptEvaluateResultException) as exception:
await bidi_session.script.evaluate(
expression=expression,
await_promise=await_promise,
target=ContextTarget(top_context["context"]),
)

assert "exceptionDetails" in exception.value.result
exceptionDetails = exception.value.result["exceptionDetails"]

assert "text" in exceptionDetails
assert isinstance(exceptionDetails["text"], str)
6 changes: 0 additions & 6 deletions testing/web-platform/tests/webdriver/tests/bidi/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,3 @@ def test_page_cross_origin_frame(inline, test_page_cross_origin):
@pytest.fixture
def test_page_same_origin_frame(inline, test_page):
return inline(f"<iframe src='{test_page}'></iframe>")


@pytest.fixture
async def top_context(bidi_session):
contexts = await bidi_session.browsing_context.get_tree()
return contexts[0]
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,9 @@ def iframe(src, **kwargs):
return "<iframe src='{}'></iframe>".format(inline(src, **kwargs))

return iframe


@pytest.fixture
async def top_context(bidi_session):
contexts = await bidi_session.browsing_context.get_tree()
return contexts[0]

0 comments on commit 81897c2

Please sign in to comment.