"));
await runTestInContent(() => {
// Add the used elements to the cache so that we know the unique reference.
const bodyEl = content.document.body;
const domEl = bodyEl.querySelector("div");
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
const dataSet = [
{
embedder: "array",
wrapper: node => [node],
serialized: {
type: "array",
value: [
{
type: "node",
sharedId: domElRef,
value: {
attributes: {},
childNodeCount: 0,
localName: "div",
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: null,
},
},
],
},
},
{
embedder: "map",
wrapper: node => {
const map = new Map();
map.set(node, "elem");
return map;
},
serialized: {
type: "map",
value: [
[
{
type: "node",
sharedId: domElRef,
value: {
attributes: {},
childNodeCount: 0,
localName: "div",
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: null,
},
},
{
type: "string",
value: "elem",
},
],
],
},
},
{
embedder: "map",
wrapper: node => {
const map = new Map();
map.set("elem", node);
return map;
},
serialized: {
type: "map",
value: [
[
"elem",
{
type: "node",
sharedId: domElRef,
value: {
attributes: {},
childNodeCount: 0,
localName: "div",
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: null,
},
},
],
],
},
},
{
embedder: "object",
wrapper: node => ({ elem: node }),
serialized: {
type: "object",
value: [
[
"elem",
{
type: "node",
sharedId: domElRef,
value: {
attributes: {},
childNodeCount: 0,
localName: "div",
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: null,
},
},
],
],
},
},
{
embedder: "set",
wrapper: node => {
const set = new Set();
set.add(node);
return set;
},
serialized: {
type: "set",
value: [
{
type: "node",
sharedId: domElRef,
value: {
attributes: {},
childNodeCount: 0,
localName: "div",
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: null,
},
},
],
},
},
];
for (const { embedder, wrapper, serialized } of dataSet) {
info(`Checking embedding node within ${embedder}`);
const serializationInternalMap = new Map();
const serializedValue = serialize(
wrapper(domEl),
{ maxDomDepth: 0 },
"none",
serializationInternalMap,
realm,
{ nodeCache }
);
Assert.deepEqual(serializedValue, serialized, "Got expected structure");
}
});
});
add_task(async function test_serializeShadowRoot() {
await runTestInContent(() => {
for (const mode of ["open", "closed"]) {
info(`Checking shadow root with mode '${mode}'`);
const customElement = content.document.createElement(
`${mode}-custom-element`
);
const insideShadowRootElement = content.document.createElement("input");
content.document.body.appendChild(customElement);
const shadowRoot = customElement.attachShadow({ mode });
shadowRoot.appendChild(insideShadowRootElement);
// Add the used elements to the cache so that we know the unique reference.
const customElementRef = nodeCache.getOrCreateNodeReference(
customElement,
seenNodeIds
);
const shadowRootRef = nodeCache.getOrCreateNodeReference(
shadowRoot,
seenNodeIds
);
const insideShadowRootElementRef = nodeCache.getOrCreateNodeReference(
insideShadowRootElement,
seenNodeIds
);
const dataSet = [
{
node: customElement,
serializationOptions: {
maxDomDepth: 1,
},
serialized: {
type: "node",
sharedId: customElementRef,
value: {
attributes: {},
childNodeCount: 0,
children: [],
localName: `${mode}-custom-element`,
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: {
sharedId: shadowRootRef,
type: "node",
value: {
childNodeCount: 1,
mode,
nodeType: 11,
},
},
},
},
},
{
node: customElement,
serializationOptions: {
includeShadowTree: "open",
maxDomDepth: 1,
},
serialized: {
type: "node",
sharedId: customElementRef,
value: {
attributes: {},
childNodeCount: 0,
children: [],
localName: `${mode}-custom-element`,
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: {
sharedId: shadowRootRef,
type: "node",
value: {
childNodeCount: 1,
mode,
nodeType: 11,
...(mode === "open"
? {
children: [
{
type: "node",
sharedId: insideShadowRootElementRef,
value: {
nodeType: 1,
localName: "input",
namespaceURI: "http://www.w3.org/1999/xhtml",
childNodeCount: 0,
attributes: {},
shadowRoot: null,
},
},
],
}
: {}),
},
},
},
},
},
{
node: customElement,
serializationOptions: {
includeShadowTree: "all",
maxDomDepth: 1,
},
serialized: {
type: "node",
sharedId: customElementRef,
value: {
attributes: {},
childNodeCount: 0,
children: [],
localName: `${mode}-custom-element`,
namespaceURI: "http://www.w3.org/1999/xhtml",
nodeType: 1,
shadowRoot: {
sharedId: shadowRootRef,
type: "node",
value: {
childNodeCount: 1,
mode,
nodeType: 11,
children: [
{
type: "node",
sharedId: insideShadowRootElementRef,
value: {
nodeType: 1,
localName: "input",
namespaceURI: "http://www.w3.org/1999/xhtml",
childNodeCount: 0,
attributes: {},
shadowRoot: null,
},
},
],
},
},
},
},
},
];
for (const { node, serializationOptions, serialized } of dataSet) {
const { maxDomDepth, includeShadowTree } = serializationOptions;
info(
`Checking shadow root with maxDomDepth ${maxDomDepth} and includeShadowTree ${includeShadowTree}`
);
const serializationInternalMap = new Map();
const serializedValue = serialize(
node,
serializationOptions,
"none",
serializationInternalMap,
realm,
{ nodeCache }
);
Assert.deepEqual(serializedValue, serialized, "Got expected structure");
}
}
});
});
add_task(async function test_serializeNodeSharedId() {
await loadURL(inline("
"));
await runTestInContent(() => {
const domEl = content.document.querySelector("div");
// Already add the domEl to the cache so that we know the unique reference.
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
const serializedValue = serialize(
domEl,
{ maxDomDepth: 0 },
"root",
serializationInternalMap,
realm,
{ nodeCache, seenNodeIds }
);
Assert.equal(nodeCache.size, 1, "No additional reference added");
Assert.equal(serializedValue.sharedId, domElRef);
Assert.notEqual(serializedValue.handle, domElRef);
});
});
function runTestInContent(callback) {
return SpecialPowers.spawn(
gBrowser.selectedBrowser,
[callback.toString()],
async callback => {
const { NodeCache } = ChromeUtils.importESModule(
"chrome://remote/content/shared/webdriver/NodeCache.sys.mjs"
);
const { Realm, WindowRealm } = ChromeUtils.importESModule(
"chrome://remote/content/shared/Realm.sys.mjs"
);
const { deserialize, serialize, setDefaultSerializationOptions } =
ChromeUtils.importESModule(
"chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs"
);
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"
);
}
const nodeCache = new NodeCache();
const seenNodeIds = new Map();
const realm = new WindowRealm(content);
const serializationInternalMap = new Map();
function serializeAndAssertRemoteValue(remoteValue) {
const { value, serialized } = remoteValue;
const serializationOptionsWithDefaults =
setDefaultSerializationOptions();
const serializationInternalMapWithNone = new Map();
info(`Checking '${serialized.type}' with none ownershipType`);
const serializedValue = serialize(
value,
serializationOptionsWithDefaults,
"none",
serializationInternalMapWithNone,
realm,
{ nodeCache, seenNodeIds }
);
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,
{ nodeCache, seenNodeIds }
);
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"
);
}
// eslint-disable-next-line no-eval
eval(`(${callback})()`);
}
);
}