1117 lines
26 KiB
JavaScript
1117 lines
26 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const { Realm } = ChromeUtils.importESModule(
|
|
"chrome://remote/content/shared/Realm.sys.mjs"
|
|
);
|
|
const { deserialize, serialize, setDefaultSerializationOptions, stringify } =
|
|
ChromeUtils.importESModule(
|
|
"chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs"
|
|
);
|
|
|
|
const PRIMITIVE_TYPES = [
|
|
{ value: undefined, serialized: { type: "undefined" } },
|
|
{ value: null, serialized: { type: "null" } },
|
|
{ value: "foo", serialized: { type: "string", value: "foo" } },
|
|
{ value: Number.NaN, serialized: { type: "number", value: "NaN" } },
|
|
{ value: -0, serialized: { type: "number", value: "-0" } },
|
|
{
|
|
value: Number.POSITIVE_INFINITY,
|
|
serialized: { type: "number", value: "Infinity" },
|
|
},
|
|
{
|
|
value: Number.NEGATIVE_INFINITY,
|
|
serialized: { type: "number", value: "-Infinity" },
|
|
},
|
|
{ value: 42, serialized: { type: "number", value: 42 } },
|
|
{ value: false, serialized: { type: "boolean", value: false } },
|
|
{ value: 42n, serialized: { type: "bigint", value: "42" } },
|
|
];
|
|
|
|
const REMOTE_SIMPLE_VALUES = [
|
|
{
|
|
value: new RegExp(/foo/),
|
|
serialized: {
|
|
type: "regexp",
|
|
value: {
|
|
pattern: "foo",
|
|
flags: "",
|
|
},
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new RegExp(/foo/g),
|
|
serialized: {
|
|
type: "regexp",
|
|
value: {
|
|
pattern: "foo",
|
|
flags: "g",
|
|
},
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Date(1654004849000),
|
|
serialized: {
|
|
type: "date",
|
|
value: "2022-05-31T13:47:29.000Z",
|
|
},
|
|
deserializable: true,
|
|
},
|
|
];
|
|
|
|
const REMOTE_COMPLEX_VALUES = [
|
|
{ value: Symbol("foo"), serialized: { type: "symbol" } },
|
|
{
|
|
value: [1],
|
|
serialized: {
|
|
type: "array",
|
|
value: [{ type: "number", value: 1 }],
|
|
},
|
|
},
|
|
{
|
|
value: [1],
|
|
serializationOptions: {
|
|
maxObjectDepth: 0,
|
|
},
|
|
serialized: {
|
|
type: "array",
|
|
},
|
|
},
|
|
{
|
|
value: [1, "2", true, new RegExp(/foo/g)],
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "array",
|
|
value: [
|
|
{ type: "number", value: 1 },
|
|
{ type: "string", value: "2" },
|
|
{ type: "boolean", value: true },
|
|
{
|
|
type: "regexp",
|
|
value: {
|
|
pattern: "foo",
|
|
flags: "g",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: [1, [3, "4"]],
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "array",
|
|
value: [{ type: "number", value: 1 }, { type: "array" }],
|
|
},
|
|
},
|
|
{
|
|
value: [1, [3, "4"]],
|
|
serializationOptions: {
|
|
maxObjectDepth: 2,
|
|
},
|
|
serialized: {
|
|
type: "array",
|
|
value: [
|
|
{ type: "number", value: 1 },
|
|
{
|
|
type: "array",
|
|
value: [
|
|
{ type: "number", value: 3 },
|
|
{ type: "string", value: "4" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Map(),
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "map",
|
|
value: [],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Map([]),
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "map",
|
|
value: [],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Map([
|
|
[1, 2],
|
|
["2", "3"],
|
|
[true, false],
|
|
]),
|
|
serialized: {
|
|
type: "map",
|
|
value: [
|
|
[
|
|
{ type: "number", value: 1 },
|
|
{ type: "number", value: 2 },
|
|
],
|
|
["2", { type: "string", value: "3" }],
|
|
[
|
|
{ type: "boolean", value: true },
|
|
{ type: "boolean", value: false },
|
|
],
|
|
],
|
|
},
|
|
},
|
|
{
|
|
value: new Map([
|
|
[1, 2],
|
|
["2", "3"],
|
|
[true, false],
|
|
]),
|
|
serializationOptions: {
|
|
maxObjectDepth: 0,
|
|
},
|
|
serialized: {
|
|
type: "map",
|
|
},
|
|
},
|
|
{
|
|
value: new Map([
|
|
[1, 2],
|
|
["2", "3"],
|
|
[true, false],
|
|
]),
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "map",
|
|
value: [
|
|
[
|
|
{ type: "number", value: 1 },
|
|
{ type: "number", value: 2 },
|
|
],
|
|
["2", { type: "string", value: "3" }],
|
|
[
|
|
{ type: "boolean", value: true },
|
|
{ type: "boolean", value: false },
|
|
],
|
|
],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Set(),
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "set",
|
|
value: [],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Set([]),
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "set",
|
|
value: [],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: new Set([1, "2", true]),
|
|
serialized: {
|
|
type: "set",
|
|
value: [
|
|
{ type: "number", value: 1 },
|
|
{ type: "string", value: "2" },
|
|
{ type: "boolean", value: true },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
value: new Set([1, "2", true]),
|
|
serializationOptions: {
|
|
maxObjectDepth: 0,
|
|
},
|
|
serialized: {
|
|
type: "set",
|
|
},
|
|
},
|
|
{
|
|
value: new Set([1, "2", true]),
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "set",
|
|
value: [
|
|
{ type: "number", value: 1 },
|
|
{ type: "string", value: "2" },
|
|
{ type: "boolean", value: true },
|
|
],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{ value: new WeakMap([[{}, 1]]), serialized: { type: "weakmap" } },
|
|
{ value: new WeakSet([{}]), serialized: { type: "weakset" } },
|
|
{
|
|
value: (function* () {
|
|
yield "a";
|
|
})(),
|
|
serialized: { type: "generator" },
|
|
},
|
|
{
|
|
value: (async function* () {
|
|
yield await Promise.resolve(1);
|
|
})(),
|
|
serialized: { type: "generator" },
|
|
},
|
|
{ value: new Error("error message"), serialized: { type: "error" } },
|
|
{
|
|
value: new SyntaxError("syntax error message"),
|
|
serialized: { type: "error" },
|
|
},
|
|
{
|
|
value: new TypeError("type error message"),
|
|
serialized: { type: "error" },
|
|
},
|
|
{ value: new Proxy({}, {}), serialized: { type: "proxy" } },
|
|
{ value: new Promise(() => true), serialized: { type: "promise" } },
|
|
{ value: new Int8Array(), serialized: { type: "typedarray" } },
|
|
{ value: new ArrayBuffer(), serialized: { type: "arraybuffer" } },
|
|
{ value: new URL("https://example.com"), serialized: { type: "object" } },
|
|
{ value: () => true, serialized: { type: "function" } },
|
|
{ value() {}, serialized: { type: "function" } },
|
|
{
|
|
value: {},
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "object",
|
|
value: [],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: {
|
|
1: 1,
|
|
2: "2",
|
|
foo: true,
|
|
},
|
|
serialized: {
|
|
type: "object",
|
|
value: [
|
|
["1", { type: "number", value: 1 }],
|
|
["2", { type: "string", value: "2" }],
|
|
["foo", { type: "boolean", value: true }],
|
|
],
|
|
},
|
|
},
|
|
{
|
|
value: {
|
|
1: 1,
|
|
2: "2",
|
|
foo: true,
|
|
},
|
|
serializationOptions: {
|
|
maxObjectDepth: 0,
|
|
},
|
|
serialized: {
|
|
type: "object",
|
|
},
|
|
},
|
|
{
|
|
value: {
|
|
1: 1,
|
|
2: "2",
|
|
foo: true,
|
|
},
|
|
serializationOptions: {
|
|
maxObjectDepth: 1,
|
|
},
|
|
serialized: {
|
|
type: "object",
|
|
value: [
|
|
["1", { type: "number", value: 1 }],
|
|
["2", { type: "string", value: "2" }],
|
|
["foo", { type: "boolean", value: true }],
|
|
],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
{
|
|
value: {
|
|
1: 1,
|
|
2: "2",
|
|
3: {
|
|
bar: "foo",
|
|
},
|
|
foo: true,
|
|
},
|
|
serializationOptions: {
|
|
maxObjectDepth: 2,
|
|
},
|
|
serialized: {
|
|
type: "object",
|
|
value: [
|
|
["1", { type: "number", value: 1 }],
|
|
["2", { type: "string", value: "2" }],
|
|
[
|
|
"3",
|
|
{
|
|
type: "object",
|
|
value: [["bar", { type: "string", value: "foo" }]],
|
|
},
|
|
],
|
|
["foo", { type: "boolean", value: true }],
|
|
],
|
|
},
|
|
deserializable: true,
|
|
},
|
|
];
|
|
|
|
add_task(function test_deserializePrimitiveTypes() {
|
|
const realm = new Realm();
|
|
|
|
for (const type of PRIMITIVE_TYPES) {
|
|
const { value: expectedValue, serialized } = type;
|
|
|
|
info(`Checking '${serialized.type}'`);
|
|
const value = deserialize(serialized, realm, {});
|
|
|
|
if (serialized.value == "NaN") {
|
|
ok(Number.isNaN(value), `Got expected value for ${serialized}`);
|
|
} else {
|
|
Assert.strictEqual(
|
|
value,
|
|
expectedValue,
|
|
`Got expected value for ${serialized}`
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializeDateLocalValue() {
|
|
const realm = new Realm();
|
|
|
|
const validaDateStrings = [
|
|
"2009",
|
|
"2009-05",
|
|
"2009-05-19",
|
|
"2022-02-29",
|
|
"2009T15:00",
|
|
"2009-05T15:00",
|
|
"2022-06-31T15:00",
|
|
"2009-05-19T15:00",
|
|
"2009-05-19T15:00:15",
|
|
"2009-05-19T15:00-00:00",
|
|
"2009-05-19T15:00:15.452",
|
|
"2009-05-19T15:00:15.452Z",
|
|
"2009-05-19T15:00:15.452+02:00",
|
|
"2009-05-19T15:00:15.452-02:00",
|
|
"-271821-04-20T00:00:00Z",
|
|
"+000000-01-01T00:00:00Z",
|
|
];
|
|
for (const dateString of validaDateStrings) {
|
|
info(`Checking '${dateString}'`);
|
|
const value = deserialize({ type: "date", value: dateString }, realm, {});
|
|
|
|
Assert.equal(
|
|
value.getTime(),
|
|
new Date(dateString).getTime(),
|
|
`Got expected value for ${dateString}`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializeLocalValues() {
|
|
const realm = new Realm();
|
|
|
|
for (const type of REMOTE_SIMPLE_VALUES.concat(REMOTE_COMPLEX_VALUES)) {
|
|
const { value: expectedValue, serialized, deserializable } = type;
|
|
|
|
// Skip non deserializable cases
|
|
if (!deserializable) {
|
|
continue;
|
|
}
|
|
|
|
info(`Checking '${serialized.type}'`);
|
|
const value = deserialize(serialized, realm, {});
|
|
assertLocalValue(serialized.type, value, expectedValue);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_deserializeLocalValuesInWindowRealm() {
|
|
for (const type of REMOTE_SIMPLE_VALUES.concat(REMOTE_COMPLEX_VALUES)) {
|
|
const { value: expectedValue, serialized, deserializable } = type;
|
|
|
|
// Skip non deserializable cases
|
|
if (!deserializable) {
|
|
continue;
|
|
}
|
|
|
|
const value = await deserializeInWindowRealm(serialized);
|
|
assertLocalValue(serialized.type, value, expectedValue);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_deserializeChannel() {
|
|
const realm = new Realm();
|
|
const channel = {
|
|
type: "channel",
|
|
value: { channel: "channel_name" },
|
|
};
|
|
const deserializationOptions = {
|
|
emitScriptMessage: (realm, channelProperties, message) => message,
|
|
};
|
|
|
|
info(`Checking 'channel'`);
|
|
const value = deserialize(channel, realm, deserializationOptions, {});
|
|
Assert.equal(
|
|
Object.prototype.toString.call(value),
|
|
"[object Function]",
|
|
"Got expected type Function"
|
|
);
|
|
Assert.equal(value("foo"), "foo", "Got expected result");
|
|
});
|
|
|
|
add_task(function test_deserializeLocalValuesByHandle() {
|
|
// Create two realms, realm1 will be used to serialize values, while realm2
|
|
// will be used as a reference empty realm without any object reference.
|
|
const realm1 = new Realm();
|
|
const realm2 = new Realm();
|
|
|
|
for (const type of REMOTE_SIMPLE_VALUES.concat(REMOTE_COMPLEX_VALUES)) {
|
|
const { value: expectedValue, serialized } = type;
|
|
|
|
// No need to skip non-deserializable cases here.
|
|
|
|
info(`Checking '${serialized.type}'`);
|
|
// Serialize the value once to get a handle.
|
|
const serializedValue = serialize(
|
|
expectedValue,
|
|
{ maxObjectDepth: 0 },
|
|
"root",
|
|
new Map(),
|
|
realm1,
|
|
{}
|
|
);
|
|
|
|
// Create a remote reference containing only the handle.
|
|
// `deserialize` should not need any other property.
|
|
const remoteReference = { handle: serializedValue.handle };
|
|
|
|
// Check that the remote reference can be deserialized in realm1.
|
|
const value = deserialize(remoteReference, realm1, {});
|
|
assertLocalValue(serialized.type, value, expectedValue);
|
|
|
|
Assert.throws(
|
|
() => deserialize(remoteReference, realm2, {}),
|
|
/NoSuchHandleError:/,
|
|
`Got expected error when using the wrong realm for deserialize`
|
|
);
|
|
|
|
realm1.removeObjectHandle(serializedValue.handle);
|
|
Assert.throws(
|
|
() => deserialize(remoteReference, realm1, {}),
|
|
/NoSuchHandleError:/,
|
|
`Got expected error when after deleting the object handle`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializeHandleInvalidTypes() {
|
|
const realm = new Realm();
|
|
|
|
for (const invalidType of [false, 42, {}, []]) {
|
|
info(`Checking type: '${invalidType}'`);
|
|
|
|
Assert.throws(
|
|
() => deserialize({ type: "object", handle: invalidType }, realm, {}),
|
|
/InvalidArgumentError:/,
|
|
`Got expected error for type ${invalidType}`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializePrimitiveTypesInvalidValues() {
|
|
const realm = new Realm();
|
|
|
|
const invalidValues = [
|
|
{ type: "bigint", values: [undefined, null, false, "foo", [], {}] },
|
|
{ type: "boolean", values: [undefined, null, 42, "foo", [], {}] },
|
|
{
|
|
type: "number",
|
|
values: [undefined, null, false, "43", [], {}],
|
|
},
|
|
{ type: "string", values: [undefined, null, false, 42, [], {}] },
|
|
];
|
|
|
|
for (const invalidValue of invalidValues) {
|
|
const { type, values } = invalidValue;
|
|
|
|
for (const value of values) {
|
|
info(`Checking '${type}' with value ${value}`);
|
|
|
|
Assert.throws(
|
|
() => deserialize({ type, value }, realm, {}),
|
|
/InvalidArgument/,
|
|
`Got expected error for type ${type} and value ${value}`
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializeDateLocalValueInvalidValues() {
|
|
const realm = new Realm();
|
|
|
|
const invalidaDateStrings = [
|
|
"10",
|
|
"20009",
|
|
"+20009",
|
|
"2009-",
|
|
"2009-0",
|
|
"2009-15",
|
|
"2009-02-1",
|
|
"2009-02-50",
|
|
"15:00",
|
|
"T15:00",
|
|
"9-05-19T15:00",
|
|
"2009-5-19T15:00",
|
|
"2009-05-1T15:00",
|
|
"2009-02-10T15",
|
|
"2009-05-19T15:",
|
|
"2009-05-19T1:00",
|
|
"2009-05-19T10:1",
|
|
"2009-05-19T60:00",
|
|
"2009-05-19T15:70",
|
|
"2009-05-19T15:00.25",
|
|
"2009-05-19+10:00",
|
|
"2009-05-19Z",
|
|
"2009-05-19 15:00",
|
|
"2009-05-19t15:00Z",
|
|
"2009-05-19T15:00z",
|
|
"2009-05-19T15:00+01",
|
|
"2009-05-19T10:10+1:00",
|
|
"2009-05-19T10:10+01:1",
|
|
"2009-05-19T15:00+75:00",
|
|
"2009-05-19T15:00+02:80",
|
|
"02009-05-19T15:00",
|
|
];
|
|
for (const dateString of invalidaDateStrings) {
|
|
info(`Checking '${dateString}'`);
|
|
|
|
Assert.throws(
|
|
() => deserialize({ type: "date", value: dateString }, realm, {}),
|
|
/InvalidArgumentError:/,
|
|
`Got expected error for date string: ${dateString}`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializeLocalValuesInvalidType() {
|
|
const realm = new Realm();
|
|
|
|
const invalidTypes = [undefined, null, false, 42, {}];
|
|
|
|
for (const invalidType of invalidTypes) {
|
|
info(`Checking type: '${invalidType}'`);
|
|
|
|
Assert.throws(
|
|
() => deserialize({ type: invalidType }, realm, {}),
|
|
/InvalidArgumentError:/,
|
|
`Got expected error for type ${invalidType}`
|
|
);
|
|
|
|
Assert.throws(
|
|
() =>
|
|
deserialize(
|
|
{
|
|
type: "array",
|
|
value: [{ type: invalidType }],
|
|
},
|
|
realm,
|
|
{}
|
|
),
|
|
/InvalidArgumentError:/,
|
|
`Got expected error for nested type ${invalidType}`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_deserializeLocalValuesInvalidValues() {
|
|
const realm = new Realm();
|
|
|
|
const invalidValues = [
|
|
{ type: "array", values: [undefined, null, false, 42, "foo", {}] },
|
|
{
|
|
type: "regexp",
|
|
values: [
|
|
undefined,
|
|
null,
|
|
false,
|
|
"foo",
|
|
42,
|
|
[],
|
|
{},
|
|
{ pattern: null },
|
|
{ pattern: 1 },
|
|
{ pattern: true },
|
|
{ pattern: "foo", flags: null },
|
|
{ pattern: "foo", flags: 1 },
|
|
{ pattern: "foo", flags: false },
|
|
{ pattern: "foo", flags: "foo" },
|
|
],
|
|
},
|
|
{
|
|
type: "date",
|
|
values: [
|
|
undefined,
|
|
null,
|
|
false,
|
|
"foo",
|
|
"05 October 2011 14:48 UTC",
|
|
"Tue Jun 14 2022 10:46:50 GMT+0200!",
|
|
42,
|
|
[],
|
|
{},
|
|
],
|
|
},
|
|
{
|
|
type: "map",
|
|
values: [
|
|
undefined,
|
|
null,
|
|
false,
|
|
"foo",
|
|
42,
|
|
["1"],
|
|
[[]],
|
|
[["1"]],
|
|
[{ 1: "2" }],
|
|
{},
|
|
],
|
|
},
|
|
{
|
|
type: "set",
|
|
values: [undefined, null, false, "foo", 42, {}],
|
|
},
|
|
{
|
|
type: "object",
|
|
values: [
|
|
undefined,
|
|
null,
|
|
false,
|
|
"foo",
|
|
42,
|
|
{},
|
|
["1"],
|
|
[[]],
|
|
[["1"]],
|
|
[{ 1: "2" }],
|
|
[
|
|
[
|
|
{ type: "number", value: "1" },
|
|
{ type: "number", value: "2" },
|
|
],
|
|
],
|
|
[
|
|
[
|
|
{ type: "object", value: [] },
|
|
{ type: "number", value: "1" },
|
|
],
|
|
],
|
|
[
|
|
[
|
|
{
|
|
type: "regexp",
|
|
value: {
|
|
pattern: "foo",
|
|
},
|
|
},
|
|
{ type: "number", value: "1" },
|
|
],
|
|
],
|
|
],
|
|
},
|
|
];
|
|
|
|
for (const invalidValue of invalidValues) {
|
|
const { type, values } = invalidValue;
|
|
|
|
for (const value of values) {
|
|
info(`Checking '${type}' with value ${value}`);
|
|
|
|
Assert.throws(
|
|
() => deserialize({ type, value }, realm, {}),
|
|
/InvalidArgumentError:/,
|
|
`Got expected error for type ${type} and value ${value}`
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
add_task(function test_serializePrimitiveTypes() {
|
|
const realm = new Realm();
|
|
|
|
for (const type of PRIMITIVE_TYPES) {
|
|
const { value, serialized } = type;
|
|
const defaultSerializationOptions = setDefaultSerializationOptions();
|
|
|
|
const serializationInternalMap = new Map();
|
|
const serializedValue = serialize(
|
|
value,
|
|
defaultSerializationOptions,
|
|
"none",
|
|
serializationInternalMap,
|
|
realm,
|
|
{}
|
|
);
|
|
assertInternalIds(serializationInternalMap, 0);
|
|
Assert.deepEqual(serialized, serializedValue, "Got expected structure");
|
|
|
|
// For primitive values, the serialization with ownershipType=root should
|
|
// be exactly identical to the one with ownershipType=none.
|
|
const serializationInternalMapWithRoot = new Map();
|
|
const serializedWithRoot = serialize(
|
|
value,
|
|
defaultSerializationOptions,
|
|
"root",
|
|
serializationInternalMapWithRoot,
|
|
realm,
|
|
{}
|
|
);
|
|
assertInternalIds(serializationInternalMapWithRoot, 0);
|
|
Assert.deepEqual(serialized, serializedWithRoot, "Got expected structure");
|
|
}
|
|
});
|
|
|
|
add_task(function test_serializeRemoteSimpleValues() {
|
|
const realm = new Realm();
|
|
|
|
for (const type of REMOTE_SIMPLE_VALUES) {
|
|
const { value, serialized } = type;
|
|
const defaultSerializationOptions = setDefaultSerializationOptions();
|
|
|
|
info(`Checking '${serialized.type}' with none ownershipType`);
|
|
const serializationInternalMapWithNone = new Map();
|
|
const serializedValue = serialize(
|
|
value,
|
|
defaultSerializationOptions,
|
|
"none",
|
|
serializationInternalMapWithNone,
|
|
realm,
|
|
{}
|
|
);
|
|
|
|
assertInternalIds(serializationInternalMapWithNone, 0);
|
|
Assert.deepEqual(serialized, serializedValue, "Got expected structure");
|
|
|
|
info(`Checking '${serialized.type}' with root ownershipType`);
|
|
const serializationInternalMapWithRoot = new Map();
|
|
const serializedWithRoot = serialize(
|
|
value,
|
|
defaultSerializationOptions,
|
|
"root",
|
|
serializationInternalMapWithRoot,
|
|
realm,
|
|
{}
|
|
);
|
|
|
|
assertInternalIds(serializationInternalMapWithRoot, 0);
|
|
Assert.equal(
|
|
typeof serializedWithRoot.handle,
|
|
"string",
|
|
"Got a handle property"
|
|
);
|
|
Assert.deepEqual(
|
|
Object.assign({}, serialized, { handle: serializedWithRoot.handle }),
|
|
serializedWithRoot,
|
|
"Got expected structure, plus a generated handle id"
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_serializeRemoteComplexValues() {
|
|
for (const type of REMOTE_COMPLEX_VALUES) {
|
|
const { value, serialized, serializationOptions } = type;
|
|
const serializationOptionsWithDefaults =
|
|
setDefaultSerializationOptions(serializationOptions);
|
|
|
|
info(`Checking '${serialized.type}' with none ownershipType`);
|
|
const realm = new Realm();
|
|
const serializationInternalMapWithNone = new Map();
|
|
|
|
const serializedValue = serialize(
|
|
value,
|
|
serializationOptionsWithDefaults,
|
|
"none",
|
|
serializationInternalMapWithNone,
|
|
realm,
|
|
{}
|
|
);
|
|
|
|
assertInternalIds(serializationInternalMapWithNone, 0);
|
|
Assert.deepEqual(serialized, serializedValue, "Got expected structure");
|
|
|
|
info(`Checking '${serialized.type}' with root ownershipType`);
|
|
const serializationInternalMapWithRoot = new Map();
|
|
const serializedWithRoot = serialize(
|
|
value,
|
|
serializationOptionsWithDefaults,
|
|
"root",
|
|
serializationInternalMapWithRoot,
|
|
realm,
|
|
{}
|
|
);
|
|
|
|
assertInternalIds(serializationInternalMapWithRoot, 0);
|
|
Assert.equal(
|
|
typeof serializedWithRoot.handle,
|
|
"string",
|
|
"Got a handle property"
|
|
);
|
|
Assert.deepEqual(
|
|
Object.assign({}, serialized, { handle: serializedWithRoot.handle }),
|
|
serializedWithRoot,
|
|
"Got expected structure, plus a generated handle id"
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(function test_serializeWithSerializationInternalMap() {
|
|
const dataSet = [
|
|
{
|
|
data: [1],
|
|
serializedData: [{ type: "number", value: 1 }],
|
|
type: "array",
|
|
},
|
|
{
|
|
data: new Map([[true, false]]),
|
|
serializedData: [
|
|
[
|
|
{ type: "boolean", value: true },
|
|
{ type: "boolean", value: false },
|
|
],
|
|
],
|
|
type: "map",
|
|
},
|
|
{
|
|
data: new Set(["foo"]),
|
|
serializedData: [{ type: "string", value: "foo" }],
|
|
type: "set",
|
|
},
|
|
{
|
|
data: { foo: "bar" },
|
|
serializedData: [["foo", { type: "string", value: "bar" }]],
|
|
type: "object",
|
|
},
|
|
];
|
|
const realm = new Realm();
|
|
|
|
for (const { type, data, serializedData } of dataSet) {
|
|
info(`Checking '${type}' with serializationInternalMap`);
|
|
|
|
const serializationInternalMap = new Map();
|
|
const value = [
|
|
data,
|
|
data,
|
|
[data],
|
|
new Set([data]),
|
|
new Map([["bar", data]]),
|
|
{ bar: data },
|
|
];
|
|
|
|
const serializedValue = serialize(
|
|
value,
|
|
{ maxObjectDepth: 2 },
|
|
"none",
|
|
serializationInternalMap,
|
|
realm,
|
|
{}
|
|
);
|
|
|
|
assertInternalIds(serializationInternalMap, 1);
|
|
|
|
const internalId = serializationInternalMap.get(data).internalId;
|
|
|
|
const serialized = {
|
|
type: "array",
|
|
value: [
|
|
{
|
|
type,
|
|
value: serializedData,
|
|
internalId,
|
|
},
|
|
{
|
|
type,
|
|
internalId,
|
|
},
|
|
{
|
|
type: "array",
|
|
value: [{ type, internalId }],
|
|
},
|
|
{
|
|
type: "set",
|
|
value: [{ type, internalId }],
|
|
},
|
|
{
|
|
type: "map",
|
|
value: [["bar", { type, internalId }]],
|
|
},
|
|
{
|
|
type: "object",
|
|
value: [["bar", { type, internalId }]],
|
|
},
|
|
],
|
|
};
|
|
|
|
Assert.deepEqual(serialized, serializedValue, "Got expected structure");
|
|
}
|
|
});
|
|
|
|
add_task(function test_serializeMultipleValuesWithSerializationInternalMap() {
|
|
const realm = new Realm();
|
|
const serializationInternalMap = new Map();
|
|
const obj1 = { foo: "bar" };
|
|
const obj2 = [1, 2];
|
|
const value = [obj1, obj2, obj1, obj2];
|
|
|
|
serialize(
|
|
value,
|
|
{ maxObjectDepth: 2 },
|
|
"none",
|
|
serializationInternalMap,
|
|
realm,
|
|
{}
|
|
);
|
|
|
|
assertInternalIds(serializationInternalMap, 2);
|
|
|
|
const internalId1 = serializationInternalMap.get(obj1).internalId;
|
|
const internalId2 = serializationInternalMap.get(obj2).internalId;
|
|
|
|
Assert.notEqual(
|
|
internalId1,
|
|
internalId2,
|
|
"Internal ids for different object are also different"
|
|
);
|
|
});
|
|
|
|
add_task(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");
|
|
}
|
|
});
|
|
|
|
function assertLocalValue(type, value, expectedValue) {
|
|
let formattedValue = value;
|
|
let formattedExpectedValue = expectedValue;
|
|
|
|
// Format certain types for easier assertion
|
|
if (type == "map") {
|
|
Assert.equal(
|
|
Object.prototype.toString.call(expectedValue),
|
|
"[object Map]",
|
|
"Got expected type Map"
|
|
);
|
|
|
|
formattedValue = Array.from(value.values());
|
|
formattedExpectedValue = Array.from(expectedValue.values());
|
|
} else if (type == "set") {
|
|
Assert.equal(
|
|
Object.prototype.toString.call(expectedValue),
|
|
"[object Set]",
|
|
"Got expected type Set"
|
|
);
|
|
|
|
formattedValue = Array.from(value);
|
|
formattedExpectedValue = Array.from(expectedValue);
|
|
}
|
|
|
|
Assert.deepEqual(
|
|
formattedValue,
|
|
formattedExpectedValue,
|
|
"Got expected structure"
|
|
);
|
|
}
|
|
|
|
function assertInternalIds(serializationInternalMap, amount) {
|
|
const remoteValuesWithInternalIds = Array.from(
|
|
serializationInternalMap.values()
|
|
).filter(remoteValue => !!remoteValue.internalId);
|
|
|
|
Assert.equal(
|
|
remoteValuesWithInternalIds.length,
|
|
amount,
|
|
"Got expected amount of internalIds in serializationInternalMap"
|
|
);
|
|
}
|
|
|
|
function deserializeInWindowRealm(serialized) {
|
|
return SpecialPowers.spawn(
|
|
gBrowser.selectedBrowser,
|
|
[serialized],
|
|
async _serialized => {
|
|
const { WindowRealm } = ChromeUtils.importESModule(
|
|
"chrome://remote/content/shared/Realm.sys.mjs"
|
|
);
|
|
const { deserialize } = ChromeUtils.importESModule(
|
|
"chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs"
|
|
);
|
|
const realm = new WindowRealm(content);
|
|
info(`Checking '${_serialized.type}'`);
|
|
return deserialize(_serialized, realm, {});
|
|
}
|
|
);
|
|
}
|