Bug 1830884 - [webdriver-bidi] Update Navigable's seen nodes map for known nodes. r=webdriver-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D177496
This commit is contained in:
@@ -67,6 +67,9 @@ export const OwnershipModel = {
|
||||
*
|
||||
* @property {NodeCache=} nodeCache
|
||||
* The cache containing DOM node references.
|
||||
* @property {Map<BrowsingContext, Array<string>>} seenNodeIds
|
||||
* Map of browsing contexts to their seen node ids during the current
|
||||
* serialization.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -469,28 +472,27 @@ function getHandleForObject(realm, ownershipType, object) {
|
||||
*
|
||||
* @param {Node} node
|
||||
* Node to create the unique reference for.
|
||||
* @param {Realm} realm
|
||||
* The Realm in which the value is serialized.
|
||||
* @param {ExtraSerializationOptions} extraOptions
|
||||
* Extra Remote Value serialization options.
|
||||
*
|
||||
* @returns {string}
|
||||
* Shared unique reference for the Node.
|
||||
*/
|
||||
function getSharedIdForNode(node, realm, extraOptions) {
|
||||
const { nodeCache } = extraOptions;
|
||||
function getSharedIdForNode(node, extraOptions) {
|
||||
const { nodeCache, seenNodeIds } = extraOptions;
|
||||
|
||||
node = Cu.unwaiveXrays(node);
|
||||
|
||||
if (!Node.isInstance(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const browsingContext = realm.browsingContext;
|
||||
const browsingContext = node.ownerGlobal.browsingContext;
|
||||
if (!browsingContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const unwrapped = Cu.unwaiveXrays(node);
|
||||
return nodeCache.getOrCreateNodeReference(unwrapped);
|
||||
return nodeCache.getOrCreateNodeReference(node, seenNodeIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,7 +516,7 @@ function getSharedIdForNode(node, realm, extraOptions) {
|
||||
* Map of internal ids.
|
||||
* @param {Realm} realm
|
||||
* The Realm from which comes the value being serialized.
|
||||
* @param {ExtraSerializationOptions} options
|
||||
* @param {ExtraSerializationOptions} extraOptions
|
||||
* Extra Remote Value serialization options.
|
||||
*
|
||||
* @returns {object} Object for serialized values.
|
||||
@@ -528,7 +530,7 @@ function serializeArrayLike(
|
||||
ownershipType,
|
||||
serializationInternalMap,
|
||||
realm,
|
||||
options
|
||||
extraOptions
|
||||
) {
|
||||
const serialized = buildSerialized(production, handleId);
|
||||
setInternalIdsIfNeeded(serializationInternalMap, serialized, value);
|
||||
@@ -540,7 +542,7 @@ function serializeArrayLike(
|
||||
ownershipType,
|
||||
serializationInternalMap,
|
||||
realm,
|
||||
options
|
||||
extraOptions
|
||||
);
|
||||
}
|
||||
|
||||
@@ -899,7 +901,7 @@ export function serialize(
|
||||
const serialized = buildSerialized("node", handleId);
|
||||
|
||||
// Get or create the shared id for WebDriver classic compat from the node.
|
||||
const sharedId = getSharedIdForNode(value, realm, extraOptions);
|
||||
const sharedId = getSharedIdForNode(value, extraOptions);
|
||||
if (sharedId !== null) {
|
||||
serialized.sharedId = sharedId;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ remote.jar:
|
||||
content/webdriver-bidi/WebDriverBiDiConnection.sys.mjs (WebDriverBiDiConnection.sys.mjs)
|
||||
|
||||
# WebDriver BiDi modules
|
||||
content/webdriver-bidi/modules/Intercept.sys.mjs (modules/Intercept.sys.mjs)
|
||||
content/webdriver-bidi/modules/ModuleRegistry.sys.mjs (modules/ModuleRegistry.sys.mjs)
|
||||
content/webdriver-bidi/modules/WindowGlobalBiDiModule.sys.mjs (modules/WindowGlobalBiDiModule.sys.mjs)
|
||||
|
||||
|
||||
49
remote/webdriver-bidi/modules/Intercept.sys.mjs
Normal file
49
remote/webdriver-bidi/modules/Intercept.sys.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
getSeenNodesForBrowsingContext:
|
||||
"chrome://remote/content/shared/webdriver/Session.sys.mjs",
|
||||
});
|
||||
|
||||
/**
|
||||
* The serialization of JavaScript objects in the content process might produce
|
||||
* extra data that needs to be transfered and then processed by the parent
|
||||
* process. This extra data is part of the payload as returned by commands
|
||||
* and events and can contain the following:
|
||||
*
|
||||
* - {Map<BrowsingContext, Array<string>>} seenNodeIds
|
||||
* DOM nodes that need to be added to the navigable seen nodes map.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* Id of the WebDriver session
|
||||
* @param {object} payload
|
||||
* Payload of the response for the command and event that might contain
|
||||
* a `_extraData` field.
|
||||
*
|
||||
* @returns {object}
|
||||
* The payload with the extra data removed if it was present.
|
||||
*/
|
||||
export function processExtraData(sessionId, payload) {
|
||||
// Process extra data if present and delete it from the payload
|
||||
if ("_extraData" in payload) {
|
||||
const { seenNodeIds } = payload._extraData;
|
||||
|
||||
// Updates the seen nodes for the current session and browsing context.
|
||||
seenNodeIds?.forEach((nodeIds, browsingContext) => {
|
||||
const seenNodes = lazy.getSeenNodesForBrowsingContext(
|
||||
sessionId,
|
||||
browsingContext
|
||||
);
|
||||
|
||||
nodeIds.forEach(nodeId => seenNodes.add(nodeId));
|
||||
});
|
||||
|
||||
delete payload._extraData;
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export class WindowGlobalBiDiModule extends Module {
|
||||
* Extra Remote Value serialization options.
|
||||
*
|
||||
* @returns {object}
|
||||
* Serialized representation of the value.
|
||||
* Promise that resolves to the serialized representation of the value.
|
||||
*/
|
||||
serialize(
|
||||
value,
|
||||
@@ -66,15 +66,18 @@ export class WindowGlobalBiDiModule extends Module {
|
||||
realm,
|
||||
extraOptions = {}
|
||||
) {
|
||||
extraOptions.nodeCache = this.#nodeCache;
|
||||
const { nodeCache = this.#nodeCache, seenNodeIds = new Map() } =
|
||||
extraOptions;
|
||||
|
||||
return lazy.serialize(
|
||||
const serializedValue = lazy.serialize(
|
||||
value,
|
||||
serializationOptions,
|
||||
ownershipType,
|
||||
new Map(),
|
||||
realm,
|
||||
extraOptions
|
||||
{ nodeCache, seenNodeIds }
|
||||
);
|
||||
|
||||
return serializedValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
|
||||
generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
|
||||
OwnershipModel: "chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs",
|
||||
processExtraData:
|
||||
"chrome://remote/content/webdriver-bidi/modules/Intercept.sys.mjs",
|
||||
RealmType: "chrome://remote/content/shared/Realm.sys.mjs",
|
||||
setDefaultAndAssertSerializationOptions:
|
||||
"chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs",
|
||||
@@ -657,6 +659,11 @@ class ScriptModule extends Module {
|
||||
}
|
||||
|
||||
#buildReturnValue(evaluationResult) {
|
||||
evaluationResult = lazy.processExtraData(
|
||||
this.messageHandler.sessionId,
|
||||
evaluationResult
|
||||
);
|
||||
|
||||
const rv = { realm: evaluationResult.realmId };
|
||||
switch (evaluationResult.evaluationStatus) {
|
||||
// TODO: Compare with EvaluationStatus.Normal after Bug 1774444 is fixed.
|
||||
|
||||
@@ -18,7 +18,7 @@ class BrowsingContextModule extends Module {
|
||||
name == "browsingContext.domContentLoaded" ||
|
||||
name == "browsingContext.load"
|
||||
) {
|
||||
// Resolve browsing context to a TabManager id.
|
||||
// Resolve browsing context to a Navigable id.
|
||||
payload.context = lazy.TabManager.getIdForBrowsingContext(
|
||||
payload.context
|
||||
);
|
||||
|
||||
@@ -7,6 +7,8 @@ import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
processExtraData:
|
||||
"chrome://remote/content/webdriver-bidi/modules/Intercept.sys.mjs",
|
||||
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
|
||||
});
|
||||
|
||||
@@ -15,10 +17,12 @@ class LogModule extends Module {
|
||||
|
||||
interceptEvent(name, payload) {
|
||||
if (name == "log.entryAdded") {
|
||||
// Resolve browsing context to a TabManager id.
|
||||
// Resolve browsing context to a Navigable id.
|
||||
payload.source.context = lazy.TabManager.getIdForBrowsingContext(
|
||||
payload.source.context
|
||||
);
|
||||
|
||||
payload = lazy.processExtraData(this.messageHandler.sessionId, payload);
|
||||
}
|
||||
|
||||
return payload;
|
||||
|
||||
@@ -7,6 +7,8 @@ import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
processExtraData:
|
||||
"chrome://remote/content/webdriver-bidi/modules/Intercept.sys.mjs",
|
||||
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
|
||||
});
|
||||
|
||||
@@ -15,10 +17,12 @@ class ScriptModule extends Module {
|
||||
|
||||
interceptEvent(name, payload) {
|
||||
if (name == "script.message") {
|
||||
// Resolve browsing context to a TabManager id.
|
||||
// Resolve browsing context to a Navigable id.
|
||||
payload.source.context = lazy.TabManager.getIdForBrowsingContext(
|
||||
payload.source.context
|
||||
);
|
||||
|
||||
payload = lazy.processExtraData(this.messageHandler.sessionId, payload);
|
||||
}
|
||||
|
||||
return payload;
|
||||
|
||||
@@ -135,9 +135,11 @@ class LogModule extends WindowGlobalBiDiModule {
|
||||
const args = messageArguments || [];
|
||||
text += args.map(String).join(" ");
|
||||
|
||||
// Serialize each arg as remote value.
|
||||
const defaultRealm = this.messageHandler.getRealm();
|
||||
const serializedArgs = [];
|
||||
const seenNodeIds = new Map();
|
||||
|
||||
// Serialize each arg as remote value.
|
||||
for (const arg of args) {
|
||||
// Note that we can pass a default realm for now since realms are only
|
||||
// involved when creating object references, which will not happen with
|
||||
@@ -147,7 +149,8 @@ class LogModule extends WindowGlobalBiDiModule {
|
||||
Cu.waiveXrays(arg),
|
||||
lazy.setDefaultSerializationOptions(),
|
||||
lazy.OwnershipModel.None,
|
||||
defaultRealm
|
||||
defaultRealm,
|
||||
{ seenNodeIds }
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -172,6 +175,7 @@ class LogModule extends WindowGlobalBiDiModule {
|
||||
text,
|
||||
timestamp,
|
||||
stackTrace,
|
||||
_extraData: { seenNodeIds },
|
||||
};
|
||||
|
||||
// TODO: Those steps relate to:
|
||||
|
||||
@@ -62,7 +62,13 @@ class ScriptModule extends WindowGlobalBiDiModule {
|
||||
}
|
||||
}
|
||||
|
||||
#buildExceptionDetails(exception, stack, realm, resultOwnership) {
|
||||
#buildExceptionDetails(
|
||||
exception,
|
||||
stack,
|
||||
realm,
|
||||
resultOwnership,
|
||||
seenNodeIds
|
||||
) {
|
||||
exception = this.#toRawObject(exception);
|
||||
|
||||
// A stacktrace is mandatory to build exception details and a missing stack
|
||||
@@ -96,7 +102,8 @@ class ScriptModule extends WindowGlobalBiDiModule {
|
||||
exception,
|
||||
lazy.setDefaultSerializationOptions(),
|
||||
resultOwnership,
|
||||
realm
|
||||
realm,
|
||||
{ seenNodeIds }
|
||||
),
|
||||
lineNumber: stack.line - 1,
|
||||
stackTrace: { callFrames },
|
||||
@@ -150,28 +157,37 @@ class ScriptModule extends WindowGlobalBiDiModule {
|
||||
stack = rv.stack;
|
||||
}
|
||||
|
||||
const seenNodeIds = new Map();
|
||||
switch (evaluationStatus) {
|
||||
case EvaluationStatus.Normal:
|
||||
const dataSuccess = this.serialize(
|
||||
this.#toRawObject(result),
|
||||
serializationOptions,
|
||||
resultOwnership,
|
||||
realm,
|
||||
{ seenNodeIds }
|
||||
);
|
||||
|
||||
return {
|
||||
evaluationStatus,
|
||||
result: this.serialize(
|
||||
this.#toRawObject(result),
|
||||
serializationOptions,
|
||||
resultOwnership,
|
||||
realm
|
||||
),
|
||||
realmId: realm.id,
|
||||
result: dataSuccess,
|
||||
_extraData: { seenNodeIds },
|
||||
};
|
||||
case EvaluationStatus.Throw:
|
||||
const dataThrow = this.#buildExceptionDetails(
|
||||
exception,
|
||||
stack,
|
||||
realm,
|
||||
resultOwnership,
|
||||
seenNodeIds
|
||||
);
|
||||
|
||||
return {
|
||||
evaluationStatus,
|
||||
exceptionDetails: this.#buildExceptionDetails(
|
||||
exception,
|
||||
stack,
|
||||
realm,
|
||||
resultOwnership
|
||||
),
|
||||
exceptionDetails: dataThrow,
|
||||
realmId: realm.id,
|
||||
_extraData: { seenNodeIds },
|
||||
};
|
||||
default:
|
||||
throw new lazy.error.UnsupportedOperationError(
|
||||
@@ -194,17 +210,20 @@ class ScriptModule extends WindowGlobalBiDiModule {
|
||||
serializationOptions,
|
||||
} = channelProperties;
|
||||
|
||||
const seenNodeIds = new Map();
|
||||
const data = this.serialize(
|
||||
this.#toRawObject(message),
|
||||
lazy.setDefaultSerializationOptions(serializationOptions),
|
||||
ownershipType,
|
||||
realm
|
||||
realm,
|
||||
{ seenNodeIds }
|
||||
);
|
||||
|
||||
this.emitEvent("script.message", {
|
||||
channel,
|
||||
data,
|
||||
source: this.#getSource(realm),
|
||||
_extraData: { seenNodeIds },
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
[DEFAULT]
|
||||
tags = wd
|
||||
subsuite = remote
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[browser_RemoteValue.js]
|
||||
[browser_RemoteValueDOM.js]
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { NodeCache } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/webdriver/NodeCache.sys.mjs"
|
||||
);
|
||||
const { Realm, WindowRealm } = ChromeUtils.importESModule(
|
||||
const { Realm } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/Realm.sys.mjs"
|
||||
);
|
||||
const { deserialize, serialize, setDefaultSerializationOptions, stringify } =
|
||||
@@ -14,13 +11,6 @@ const { deserialize, serialize, setDefaultSerializationOptions, stringify } =
|
||||
"chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs"
|
||||
);
|
||||
|
||||
const browser = Services.appShell.createWindowlessBrowser(false);
|
||||
const bodyEl = browser.document.body;
|
||||
const domEl = browser.document.createElement("div");
|
||||
bodyEl.appendChild(domEl);
|
||||
const iframeEl = browser.document.createElement("iframe");
|
||||
bodyEl.appendChild(iframeEl);
|
||||
|
||||
const PRIMITIVE_TYPES = [
|
||||
{ value: undefined, serialized: { type: "undefined" } },
|
||||
{ value: null, serialized: { type: "null" } },
|
||||
@@ -295,59 +285,6 @@ const REMOTE_COMPLEX_VALUES = [
|
||||
{ value: new Promise(() => true), serialized: { type: "promise" } },
|
||||
{ value: new Int8Array(), serialized: { type: "typedarray" } },
|
||||
{ value: new ArrayBuffer(), serialized: { type: "arraybuffer" } },
|
||||
{
|
||||
value: browser.document.querySelectorAll("div"),
|
||||
serialized: {
|
||||
type: "nodelist",
|
||||
value: [
|
||||
{
|
||||
type: "node",
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
value: browser.document.getElementsByTagName("div"),
|
||||
serialized: {
|
||||
type: "htmlcollection",
|
||||
value: [
|
||||
{
|
||||
type: "node",
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
value: domEl,
|
||||
serialized: {
|
||||
type: "node",
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ value: browser.document.defaultView, serialized: { type: "window" } },
|
||||
{ value: new URL("https://example.com"), serialized: { type: "object" } },
|
||||
{ value: () => true, serialized: { type: "function" } },
|
||||
{ value() {}, serialized: { type: "function" } },
|
||||
@@ -601,106 +538,6 @@ add_task(function test_deserializeHandleInvalidTypes() {
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_deserializeSharedIdInvalidTypes() {
|
||||
const nodeCache = new NodeCache();
|
||||
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
|
||||
for (const invalidType of [false, 42, {}, []]) {
|
||||
info(`Checking type: '${invalidType}'`);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: invalidType,
|
||||
};
|
||||
|
||||
Assert.throws(
|
||||
() => deserialize(realm, serializedValue, { nodeCache }),
|
||||
/InvalidArgumentError:/,
|
||||
`Got expected error for type ${invalidType}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_deserializeSharedIdInvalidValue() {
|
||||
const nodeCache = new NodeCache();
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: "foo",
|
||||
};
|
||||
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
|
||||
Assert.throws(
|
||||
() => deserialize(realm, serializedValue, { nodeCache }),
|
||||
/NoSuchNodeError:/,
|
||||
"Got expected error for unknown 'sharedId'"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function test_deserializeSharedId() {
|
||||
const nodeCache = new NodeCache();
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
|
||||
const node = deserialize(realm, serializedValue, { nodeCache });
|
||||
|
||||
Assert.equal(node, domEl);
|
||||
});
|
||||
|
||||
add_task(function test_deserializeSharedIdPrecedenceOverHandle() {
|
||||
const nodeCache = new NodeCache();
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl);
|
||||
|
||||
const serializedValue = {
|
||||
handle: "foo",
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
|
||||
const node = deserialize(realm, serializedValue, { nodeCache });
|
||||
|
||||
Assert.equal(node, domEl);
|
||||
});
|
||||
|
||||
add_task(function test_deserializeSharedIdNoWindowRealm() {
|
||||
const nodeCache = new NodeCache();
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const realm = new Realm();
|
||||
|
||||
Assert.throws(
|
||||
() => deserialize(realm, serializedValue, { nodeCache }),
|
||||
/NoSuchNodeError/,
|
||||
`Got expected error for a non-window realm`
|
||||
);
|
||||
});
|
||||
|
||||
// Bug 1819902: Instead of a browsing context check compare the origin
|
||||
add_task(function test_deserializeSharedIdOtherBrowsingContext() {
|
||||
const nodeCache = new NodeCache();
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const realm = new WindowRealm(iframeEl.contentWindow);
|
||||
|
||||
const node = deserialize(realm, serializedValue, { nodeCache });
|
||||
|
||||
Assert.equal(node, null);
|
||||
});
|
||||
|
||||
add_task(function test_deserializePrimitiveTypesInvalidValues() {
|
||||
const realm = new Realm();
|
||||
|
||||
@@ -1003,15 +840,15 @@ add_task(function test_serializeRemoteSimpleValues() {
|
||||
});
|
||||
|
||||
add_task(function test_serializeRemoteComplexValues() {
|
||||
const realm = new Realm();
|
||||
|
||||
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,
|
||||
@@ -1049,343 +886,6 @@ add_task(function test_serializeRemoteComplexValues() {
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_serializeNodeChildren() {
|
||||
const nodeCache = new NodeCache();
|
||||
// Add the used elements to the cache so that we know the unique reference.
|
||||
const bodyElRef = nodeCache.getOrCreateNodeReference(bodyEl);
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl);
|
||||
const iframeElRef = nodeCache.getOrCreateNodeReference(iframeEl);
|
||||
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
|
||||
const dataSet = [
|
||||
{
|
||||
node: bodyEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: null,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: bodyElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "body",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 2,
|
||||
children: [
|
||||
{
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
children: [],
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "node",
|
||||
sharedId: iframeElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "iframe",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
children: [],
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: bodyEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 0,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: bodyElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 2,
|
||||
localName: "body",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: bodyEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 1,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: bodyElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 2,
|
||||
children: [
|
||||
{
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "node",
|
||||
sharedId: iframeElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "iframe",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
localName: "body",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: domEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 0,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: domEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 1,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
children: [],
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const { node, serializationOptions, serialized } of dataSet) {
|
||||
const { maxDomDepth } = serializationOptions;
|
||||
info(`Checking '${node.localName}' with maxDomDepth ${maxDomDepth}`);
|
||||
|
||||
const serializationInternalMap = new Map();
|
||||
|
||||
const serializedValue = serialize(
|
||||
node,
|
||||
serializationOptions,
|
||||
"none",
|
||||
serializationInternalMap,
|
||||
realm,
|
||||
{ nodeCache }
|
||||
);
|
||||
|
||||
Assert.deepEqual(serializedValue, serialized, "Got expected structure");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_serializeShadowRoot() {
|
||||
const nodeCache = new NodeCache();
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
|
||||
for (const mode of ["open", "closed"]) {
|
||||
info(`Checking shadow root with mode '${mode}'`);
|
||||
const customElement = browser.document.createElement(
|
||||
`${mode}-custom-element`
|
||||
);
|
||||
const insideShadowRootElement = browser.document.createElement("input");
|
||||
bodyEl.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);
|
||||
const shadowRootRef = nodeCache.getOrCreateNodeReference(shadowRoot);
|
||||
const insideShadowRootElementRef = nodeCache.getOrCreateNodeReference(
|
||||
insideShadowRootElement
|
||||
);
|
||||
|
||||
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(function test_serializeWithSerializationInternalMap() {
|
||||
const dataSet = [
|
||||
{
|
||||
@@ -1505,48 +1005,6 @@ add_task(function test_serializeMultipleValuesWithSerializationInternalMap() {
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function test_serializeNodeSharedId() {
|
||||
const nodeCache = new NodeCache();
|
||||
// Already add the domEl to the cache so that we know the unique reference.
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl);
|
||||
|
||||
const realm = new WindowRealm(browser.document.defaultView);
|
||||
const serializationInternalMap = new Map();
|
||||
|
||||
const serializedValue = serialize(
|
||||
domEl,
|
||||
{ maxDomDepth: 0 },
|
||||
"root",
|
||||
serializationInternalMap,
|
||||
realm,
|
||||
{ nodeCache }
|
||||
);
|
||||
|
||||
Assert.equal(nodeCache.size, 1, "No additional reference added");
|
||||
Assert.equal(serializedValue.sharedId, domElRef);
|
||||
Assert.notEqual(serializedValue.handle, domElRef);
|
||||
});
|
||||
|
||||
add_task(function test_serializeNodeSharedId_noWindowRealm() {
|
||||
const nodeCache = new NodeCache();
|
||||
nodeCache.getOrCreateNodeReference(domEl);
|
||||
|
||||
const realm = new Realm();
|
||||
const serializationInternalMap = new Map();
|
||||
|
||||
const serializedValue = serialize(
|
||||
domEl,
|
||||
{ maxDomDepth: 0 },
|
||||
"none",
|
||||
serializationInternalMap,
|
||||
realm,
|
||||
{ nodeCache }
|
||||
);
|
||||
|
||||
Assert.equal(nodeCache.size, 1, "No additional reference added");
|
||||
Assert.equal(serializedValue.sharedId, undefined);
|
||||
});
|
||||
|
||||
add_task(function test_stringify() {
|
||||
const STRINGIFY_TEST_CASES = [
|
||||
[undefined, "undefined"],
|
||||
|
||||
638
remote/webdriver-bidi/test/browser/browser_RemoteValueDOM.js
Normal file
638
remote/webdriver-bidi/test/browser/browser_RemoteValueDOM.js
Normal file
@@ -0,0 +1,638 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* eslint no-undef: 0 no-unused-vars: 0 */
|
||||
|
||||
add_task(async function test_deserializeSharedIdInvalidTypes() {
|
||||
await runTestInContent(() => {
|
||||
for (const invalidType of [false, 42, {}, []]) {
|
||||
info(`Checking type: '${invalidType}'`);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: invalidType,
|
||||
};
|
||||
|
||||
Assert.throws(
|
||||
() => deserialize(realm, serializedValue, { nodeCache }),
|
||||
/InvalidArgumentError:/,
|
||||
`Got expected error for type ${invalidType}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_deserializeSharedIdInvalidValue() {
|
||||
await runTestInContent(() => {
|
||||
const serializedValue = {
|
||||
sharedId: "foo",
|
||||
};
|
||||
|
||||
Assert.throws(
|
||||
() => deserialize(realm, serializedValue, { nodeCache }),
|
||||
/NoSuchNodeError:/,
|
||||
"Got expected error for unknown 'sharedId'"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_deserializeSharedId() {
|
||||
await loadURL(inline("<div>"));
|
||||
|
||||
await runTestInContent(() => {
|
||||
const domEl = content.document.querySelector("div");
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const node = deserialize(realm, serializedValue, { nodeCache });
|
||||
|
||||
Assert.equal(node, domEl);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_deserializeSharedIdPrecedenceOverHandle() {
|
||||
await loadURL(inline("<div>"));
|
||||
|
||||
await runTestInContent(() => {
|
||||
const domEl = content.document.querySelector("div");
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
|
||||
|
||||
const serializedValue = {
|
||||
handle: "foo",
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const node = deserialize(realm, serializedValue, { nodeCache });
|
||||
|
||||
Assert.equal(node, domEl);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_deserializeSharedIdNoWindowRealm() {
|
||||
await loadURL(inline("<div>"));
|
||||
|
||||
await runTestInContent(() => {
|
||||
const domEl = content.document.querySelector("div");
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
Assert.throws(
|
||||
() => deserialize(new Realm(), serializedValue, { nodeCache }),
|
||||
/NoSuchNodeError/,
|
||||
`Got expected error for a non-window realm`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Bug 1819902: Instead of a browsing context check compare the origin
|
||||
add_task(async function test_deserializeSharedIdOtherBrowsingContext() {
|
||||
await loadURL(inline("<iframe>"));
|
||||
|
||||
await runTestInContent(() => {
|
||||
const iframeEl = content.document.querySelector("iframe");
|
||||
const domEl = iframeEl.contentWindow.document.createElement("div");
|
||||
iframeEl.contentWindow.document.body.appendChild(domEl);
|
||||
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
|
||||
|
||||
const serializedValue = {
|
||||
sharedId: domElRef,
|
||||
};
|
||||
|
||||
const node = deserialize(realm, serializedValue, { nodeCache });
|
||||
|
||||
Assert.equal(node, null);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_serializeRemoteComplexValues() {
|
||||
await loadURL(inline("<div>"));
|
||||
|
||||
await runTestInContent(() => {
|
||||
const domEl = content.document.querySelector("div");
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
|
||||
|
||||
const REMOTE_COMPLEX_VALUES = [
|
||||
{ value: content, serialized: { type: "window" } },
|
||||
{
|
||||
value: content.document.querySelector("div"),
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: content.document.querySelectorAll("div"),
|
||||
serialized: {
|
||||
type: "nodelist",
|
||||
value: [
|
||||
{
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
value: content.document.getElementsByTagName("div"),
|
||||
serialized: {
|
||||
type: "htmlcollection",
|
||||
value: [
|
||||
{
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const type of REMOTE_COMPLEX_VALUES) {
|
||||
const { value, serialized } = type;
|
||||
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"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_serializeNodeChildren() {
|
||||
await loadURL(inline("<div></div><iframe/>"));
|
||||
|
||||
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 iframeEl = bodyEl.querySelector("iframe");
|
||||
|
||||
const bodyElRef = nodeCache.getOrCreateNodeReference(bodyEl, seenNodeIds);
|
||||
const domElRef = nodeCache.getOrCreateNodeReference(domEl, seenNodeIds);
|
||||
const iframeElRef = nodeCache.getOrCreateNodeReference(
|
||||
iframeEl,
|
||||
seenNodeIds
|
||||
);
|
||||
|
||||
const dataSet = [
|
||||
{
|
||||
node: bodyEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: null,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: bodyElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "body",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 2,
|
||||
children: [
|
||||
{
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
children: [],
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "node",
|
||||
sharedId: iframeElRef,
|
||||
value: {
|
||||
nodeType: 1,
|
||||
localName: "iframe",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
childNodeCount: 0,
|
||||
children: [],
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
attributes: {},
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: bodyEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 0,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: bodyElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 2,
|
||||
localName: "body",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: bodyEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 1,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: bodyElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 2,
|
||||
children: [
|
||||
{
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "node",
|
||||
sharedId: iframeElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "iframe",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
localName: "body",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: domEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 0,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: domEl,
|
||||
serializationOptions: {
|
||||
maxDomDepth: 1,
|
||||
},
|
||||
serialized: {
|
||||
type: "node",
|
||||
sharedId: domElRef,
|
||||
value: {
|
||||
attributes: {},
|
||||
childNodeCount: 0,
|
||||
children: [],
|
||||
localName: "div",
|
||||
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||
nodeType: 1,
|
||||
shadowRoot: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const { node, serializationOptions, serialized } of dataSet) {
|
||||
const { maxDomDepth } = serializationOptions;
|
||||
info(`Checking '${node.localName}' with maxDomDepth ${maxDomDepth}`);
|
||||
|
||||
const serializationInternalMap = new Map();
|
||||
|
||||
const serializedValue = serialize(
|
||||
node,
|
||||
serializationOptions,
|
||||
"none",
|
||||
serializationInternalMap,
|
||||
realm,
|
||||
{ nodeCache, seenNodeIds }
|
||||
);
|
||||
|
||||
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("<div>"));
|
||||
|
||||
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();
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(`(${callback})()`);
|
||||
}
|
||||
);
|
||||
}
|
||||
28
remote/webdriver-bidi/test/browser/head.js
Normal file
28
remote/webdriver-bidi/test/browser/head.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Load a given URL in the currently selected tab
|
||||
*/
|
||||
async function loadURL(url, expectedURL = undefined) {
|
||||
expectedURL = expectedURL || url;
|
||||
|
||||
const browser = gBrowser.selectedTab.linkedBrowser;
|
||||
const loaded = BrowserTestUtils.browserLoaded(browser, true, expectedURL);
|
||||
|
||||
BrowserTestUtils.loadURIString(browser, url);
|
||||
await loaded;
|
||||
}
|
||||
|
||||
/** Creates an inline URL for the given source document. */
|
||||
function inline(src, doctype = "html") {
|
||||
let doc;
|
||||
switch (doctype) {
|
||||
case "html":
|
||||
doc = `<!doctype html>\n<meta charset=utf-8>\n${src}`;
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unexpected doctype: " + doctype);
|
||||
}
|
||||
|
||||
return `https://example.com/document-builder.sjs?html=${encodeURIComponent(
|
||||
doc
|
||||
)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user