first commit

This commit is contained in:
Frank John Begornia
2025-12-23 01:51:15 +08:00
commit c926590e1d
4137 changed files with 613038 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
import { Script } from '../../../protocol/protocol.js';
import { type LoggerFn } from '../../../utils/log.js';
import type { EventManager } from '../session/EventManager.js';
import type { Realm } from './Realm.js';
/**
* Used to send messages from realm to BiDi user.
*/
export declare class ChannelProxy {
#private;
constructor(channel: Script.ChannelProperties, logger?: LoggerFn);
/**
* Creates a channel proxy in the given realm, initialises listener and
* returns a handle to `sendMessage` delegate.
*/
init(realm: Realm, eventManager: EventManager): Promise<Script.Handle>;
/** Gets a ChannelProxy from window and returns its handle. */
startListenerFromWindow(realm: Realm, eventManager: EventManager): Promise<void>;
/**
* String to be evaluated to create a ProxyChannel and put it to window.
* Returns the delegate `sendMessage`. Used to provide an argument for preload
* script. Does the following:
* 1. Creates a ChannelProxy.
* 2. Puts the ChannelProxy to window['${this.#id}'] or resolves the promise
* by calling delegate stored in window['${this.#id}'].
* This is needed because `#getHandleFromWindow` can be called before or
* after this method.
* 3. Returns the delegate `sendMessage` of the created ChannelProxy.
*/
getEvalInWindowStr(): string;
}

View File

@@ -0,0 +1,233 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChannelProxy = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const log_js_1 = require("../../../utils/log.js");
const uuid_js_1 = require("../../../utils/uuid.js");
/**
* Used to send messages from realm to BiDi user.
*/
class ChannelProxy {
#properties;
#id = (0, uuid_js_1.uuidv4)();
#logger;
constructor(channel, logger) {
this.#properties = channel;
this.#logger = logger;
}
/**
* Creates a channel proxy in the given realm, initialises listener and
* returns a handle to `sendMessage` delegate.
*/
async init(realm, eventManager) {
const channelHandle = await ChannelProxy.#createAndGetHandleInRealm(realm);
const sendMessageHandle = await ChannelProxy.#createSendMessageHandle(realm, channelHandle);
void this.#startListener(realm, channelHandle, eventManager);
return sendMessageHandle;
}
/** Gets a ChannelProxy from window and returns its handle. */
async startListenerFromWindow(realm, eventManager) {
try {
const channelHandle = await this.#getHandleFromWindow(realm);
void this.#startListener(realm, channelHandle, eventManager);
}
catch (error) {
this.#logger?.(log_js_1.LogType.debugError, error);
}
}
/**
* Evaluation string which creates a ChannelProxy object on the client side.
*/
static #createChannelProxyEvalStr() {
const functionStr = String(() => {
const queue = [];
let queueNonEmptyResolver = null;
return {
/**
* Gets a promise, which is resolved as soon as a message occurs
* in the queue.
*/
async getMessage() {
const onMessage = queue.length > 0
? Promise.resolve()
: new Promise((resolve) => {
queueNonEmptyResolver = resolve;
});
await onMessage;
return queue.shift();
},
/**
* Adds a message to the queue.
* Resolves the pending promise if needed.
*/
sendMessage(message) {
queue.push(message);
if (queueNonEmptyResolver !== null) {
queueNonEmptyResolver();
queueNonEmptyResolver = null;
}
},
};
});
return `(${functionStr})()`;
}
/** Creates a ChannelProxy in the given realm. */
static async #createAndGetHandleInRealm(realm) {
const createChannelHandleResult = await realm.cdpClient.sendCommand('Runtime.evaluate', {
expression: this.#createChannelProxyEvalStr(),
contextId: realm.executionContextId,
serializationOptions: {
serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
},
});
if (createChannelHandleResult.exceptionDetails ||
createChannelHandleResult.result.objectId === undefined) {
throw new Error(`Cannot create channel`);
}
return createChannelHandleResult.result.objectId;
}
/** Gets a handle to `sendMessage` delegate from the ChannelProxy handle. */
static async #createSendMessageHandle(realm, channelHandle) {
const sendMessageArgResult = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((channelHandle) => {
return channelHandle.sendMessage;
}),
arguments: [{ objectId: channelHandle }],
executionContextId: realm.executionContextId,
serializationOptions: {
serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
},
});
// TODO: check for exceptionDetails.
return sendMessageArgResult.result.objectId;
}
/** Starts listening for the channel events of the provided ChannelProxy. */
async #startListener(realm, channelHandle, eventManager) {
// noinspection InfiniteLoopJS
for (;;) {
try {
const message = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String(async (channelHandle) => await channelHandle.getMessage()),
arguments: [
{
objectId: channelHandle,
},
],
awaitPromise: true,
executionContextId: realm.executionContextId,
serializationOptions: {
serialization: "deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */,
maxDepth: this.#properties.serializationOptions?.maxObjectDepth ??
undefined,
},
});
if (message.exceptionDetails) {
throw message.exceptionDetails;
}
for (const browsingContext of realm.associatedBrowsingContexts) {
eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.Script.EventNames.Message,
params: {
channel: this.#properties.channel,
data: realm.cdpToBidiValue(message, this.#properties.ownership ?? "none" /* Script.ResultOwnership.None */),
source: realm.source,
},
}, browsingContext.id);
}
}
catch (error) {
// If an error is thrown, then the channel is permanently broken, so we
// exit the loop.
this.#logger?.(log_js_1.LogType.debugError, error);
break;
}
}
}
/**
* Returns a handle of ChannelProxy from window's property which was set there
* by `getEvalInWindowStr`. If window property is not set yet, sets a promise
* resolver to the window property, so that `getEvalInWindowStr` can resolve
* the promise later on with the channel.
* This is needed because `getEvalInWindowStr` can be called before or
* after this method.
*/
async #getHandleFromWindow(realm) {
const channelHandleResult = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((id) => {
const w = window;
if (w[id] === undefined) {
// The channelProxy is not created yet. Create a promise, put the
// resolver to window property and return the promise.
// `getEvalInWindowStr` will resolve the promise later.
return new Promise((resolve) => (w[id] = resolve));
}
// The channelProxy is already created by `getEvalInWindowStr` and
// is set into window property. Return it.
const channelProxy = w[id];
delete w[id];
return channelProxy;
}),
arguments: [{ value: this.#id }],
executionContextId: realm.executionContextId,
awaitPromise: true,
serializationOptions: {
serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
},
});
if (channelHandleResult.exceptionDetails !== undefined ||
channelHandleResult.result.objectId === undefined) {
throw new Error(`ChannelHandle not found in window["${this.#id}"]`);
}
return channelHandleResult.result.objectId;
}
/**
* String to be evaluated to create a ProxyChannel and put it to window.
* Returns the delegate `sendMessage`. Used to provide an argument for preload
* script. Does the following:
* 1. Creates a ChannelProxy.
* 2. Puts the ChannelProxy to window['${this.#id}'] or resolves the promise
* by calling delegate stored in window['${this.#id}'].
* This is needed because `#getHandleFromWindow` can be called before or
* after this method.
* 3. Returns the delegate `sendMessage` of the created ChannelProxy.
*/
getEvalInWindowStr() {
const delegate = String((id, channelProxy) => {
const w = window;
if (w[id] === undefined) {
// `#getHandleFromWindow` is not initialized yet, and will get the
// channelProxy later.
w[id] = channelProxy;
}
else {
// `#getHandleFromWindow` is already set a delegate to window property
// and is waiting for it to be called with the channelProxy.
w[id](channelProxy);
delete w[id];
}
return channelProxy.sendMessage;
});
const channelProxyEval = ChannelProxy.#createChannelProxyEvalStr();
return `(${delegate})('${this.#id}',${channelProxyEval})`;
}
}
exports.ChannelProxy = ChannelProxy;
//# sourceMappingURL=ChannelProxy.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Protocol } from 'devtools-protocol';
import type { CdpClient } from '../../../cdp/CdpClient.js';
import type { Script } from '../../../protocol/protocol.js';
import type { LoggerFn } from '../../../utils/log.js';
import type { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import type { EventManager } from '../session/EventManager.js';
import { Realm } from './Realm.js';
import type { RealmStorage } from './RealmStorage.js';
export declare class DedicatedWorkerRealm extends Realm {
#private;
constructor(cdpClient: CdpClient, eventManager: EventManager, executionContextId: Protocol.Runtime.ExecutionContextId, logger: LoggerFn | undefined, origin: string, ownerRealm: Realm, realmId: Script.Realm, realmStorage: RealmStorage);
get associatedBrowsingContexts(): BrowsingContextImpl[];
get realmType(): 'dedicated-worker';
get source(): Script.Source;
get realmInfo(): Script.DedicatedWorkerRealmInfo;
}

View File

@@ -0,0 +1,51 @@
"use strict";
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DedicatedWorkerRealm = void 0;
const Realm_js_1 = require("./Realm.js");
class DedicatedWorkerRealm extends Realm_js_1.Realm {
#ownerRealm;
constructor(cdpClient, eventManager, executionContextId, logger, origin, ownerRealm, realmId, realmStorage) {
super(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage);
this.#ownerRealm = ownerRealm;
this.initialize();
}
get associatedBrowsingContexts() {
return this.#ownerRealm.associatedBrowsingContexts;
}
get realmType() {
return 'dedicated-worker';
}
get source() {
return {
realm: this.realmId,
// This is a hack to make Puppeteer able to track workers.
// TODO: remove after Puppeteer tracks workers by owners and use the base version.
context: this.associatedBrowsingContexts[0]?.id,
};
}
get realmInfo() {
return {
...this.baseInfo,
type: this.realmType,
owners: [this.#ownerRealm.realmId],
};
}
}
exports.DedicatedWorkerRealm = DedicatedWorkerRealm;
//# sourceMappingURL=DedicatedWorkerRealm.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DedicatedWorkerRealm.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/DedicatedWorkerRealm.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAUH,yCAAiC;AAGjC,MAAa,oBAAqB,SAAQ,gBAAK;IACpC,WAAW,CAAQ;IAE5B,YACE,SAAoB,EACpB,YAA0B,EAC1B,kBAAuD,EACvD,MAA4B,EAC5B,MAAc,EACd,UAAiB,EACjB,OAAqB,EACrB,YAA0B;QAE1B,KAAK,CACH,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,MAAM,EACN,MAAM,EACN,OAAO,EACP,YAAY,CACb,CAAC;QAEF,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAE9B,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,IAAa,0BAA0B;QACrC,OAAO,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAAC;IACrD,CAAC;IAED,IAAa,SAAS;QACpB,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,IAAa,MAAM;QACjB,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,OAAO;YACnB,0DAA0D;YAC1D,kFAAkF;YAClF,OAAO,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,EAAE;SAChD,CAAC;IACJ,CAAC;IAED,IAAa,SAAS;QACpB,OAAO;YACL,GAAG,IAAI,CAAC,QAAQ;YAChB,IAAI,EAAE,IAAI,CAAC,SAAS;YACpB,MAAM,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;SACnC,CAAC;IACJ,CAAC;CACF;AApDD,oDAoDC"}

View File

@@ -0,0 +1,41 @@
import type { Protocol } from 'devtools-protocol';
import type { BrowsingContext, Script } from '../../../protocol/protocol.js';
import type { LoggerFn } from '../../../utils/log.js';
import type { CdpTarget } from '../context/CdpTarget.js';
import { ChannelProxy } from './ChannelProxy.js';
/**
* BiDi IDs are generated by the server and are unique within contexts.
*
* CDP preload script IDs are generated by the client and are unique
* within sessions.
*
* The mapping between BiDi and CDP preload script IDs is 1:many.
* BiDi IDs are needed by the mapper to keep track of potential multiple CDP IDs
* in the client.
*/
export declare class PreloadScript {
#private;
get id(): string;
get targetIds(): Set<Protocol.Target.TargetID>;
constructor(params: Script.AddPreloadScriptParameters, logger?: LoggerFn);
/** Channels of the preload script. */
get channels(): ChannelProxy[];
/** Contexts of the preload script, if any */
get contexts(): BrowsingContext.BrowsingContext[] | undefined;
/**
* Adds the script to the given CDP targets by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
initInTargets(cdpTargets: Iterable<CdpTarget>, runImmediately: boolean): Promise<void>;
/**
* Adds the script to the given CDP target by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
initInTarget(cdpTarget: CdpTarget, runImmediately: boolean): Promise<void>;
/**
* Removes this script from all CDP targets.
*/
remove(): Promise<void>;
/** Removes the provided cdp target from the list of cdp preload scripts. */
dispose(cdpTargetId: Protocol.Target.TargetID): void;
}

View File

@@ -0,0 +1,124 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PreloadScript = void 0;
const uuid_js_1 = require("../../../utils/uuid.js");
const ChannelProxy_js_1 = require("./ChannelProxy.js");
/**
* BiDi IDs are generated by the server and are unique within contexts.
*
* CDP preload script IDs are generated by the client and are unique
* within sessions.
*
* The mapping between BiDi and CDP preload script IDs is 1:many.
* BiDi IDs are needed by the mapper to keep track of potential multiple CDP IDs
* in the client.
*/
class PreloadScript {
/** BiDi ID, an automatically generated UUID. */
#id = (0, uuid_js_1.uuidv4)();
/** CDP preload scripts. */
#cdpPreloadScripts = [];
/** The script itself, in a format expected by the spec i.e. a function. */
#functionDeclaration;
/** Targets, in which the preload script is initialized. */
#targetIds = new Set();
/** Channels to be added as arguments to functionDeclaration. */
#channels;
/** The script sandbox / world name. */
#sandbox;
/** The browsing contexts to execute the preload scripts in, if any. */
#contexts;
get id() {
return this.#id;
}
get targetIds() {
return this.#targetIds;
}
constructor(params, logger) {
this.#channels =
params.arguments?.map((a) => new ChannelProxy_js_1.ChannelProxy(a.value, logger)) ?? [];
this.#functionDeclaration = params.functionDeclaration;
this.#sandbox = params.sandbox;
this.#contexts = params.contexts;
}
/** Channels of the preload script. */
get channels() {
return this.#channels;
}
/** Contexts of the preload script, if any */
get contexts() {
return this.#contexts;
}
/**
* String to be evaluated. Wraps user-provided function so that the following
* steps are run:
* 1. Create channels.
* 2. Store the created channels in window.
* 3. Call the user-provided function with channels as arguments.
*/
#getEvaluateString() {
const channelsArgStr = `[${this.channels
.map((c) => c.getEvalInWindowStr())
.join(', ')}]`;
return `(()=>{(${this.#functionDeclaration})(...${channelsArgStr})})()`;
}
/**
* Adds the script to the given CDP targets by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
async initInTargets(cdpTargets, runImmediately) {
await Promise.all(Array.from(cdpTargets).map((cdpTarget) => this.initInTarget(cdpTarget, runImmediately)));
}
/**
* Adds the script to the given CDP target by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
async initInTarget(cdpTarget, runImmediately) {
const addCdpPreloadScriptResult = await cdpTarget.cdpClient.sendCommand('Page.addScriptToEvaluateOnNewDocument', {
source: this.#getEvaluateString(),
worldName: this.#sandbox,
runImmediately,
});
this.#cdpPreloadScripts.push({
target: cdpTarget,
preloadScriptId: addCdpPreloadScriptResult.identifier,
});
this.#targetIds.add(cdpTarget.id);
}
/**
* Removes this script from all CDP targets.
*/
async remove() {
for (const cdpPreloadScript of this.#cdpPreloadScripts) {
const cdpTarget = cdpPreloadScript.target;
const cdpPreloadScriptId = cdpPreloadScript.preloadScriptId;
await cdpTarget.cdpClient.sendCommand('Page.removeScriptToEvaluateOnNewDocument', {
identifier: cdpPreloadScriptId,
});
}
}
/** Removes the provided cdp target from the list of cdp preload scripts. */
dispose(cdpTargetId) {
this.#cdpPreloadScripts = this.#cdpPreloadScripts.filter((cdpPreloadScript) => cdpPreloadScript.target?.id !== cdpTargetId);
this.#targetIds.delete(cdpTargetId);
}
}
exports.PreloadScript = PreloadScript;
//# sourceMappingURL=PreloadScript.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PreloadScript.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/PreloadScript.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,19 @@
import type { CdpTarget } from '../context/CdpTarget.js';
import type { PreloadScript } from './PreloadScript.js';
/** PreloadScripts can be filtered by BiDi ID or target ID. */
export type PreloadScriptFilter = Partial<{
id: PreloadScript['id'];
targetId: CdpTarget['id'];
global: boolean;
}>;
/**
* Container class for preload scripts.
*/
export declare class PreloadScriptStorage {
#private;
/** Finds all entries that match the given filter. */
find(filter?: PreloadScriptFilter): PreloadScript[];
add(preloadScript: PreloadScript): void;
/** Deletes all BiDi preload script entries that match the given filter. */
remove(filter?: PreloadScriptFilter): void;
}

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PreloadScriptStorage = void 0;
/**
* Container class for preload scripts.
*/
class PreloadScriptStorage {
/** Tracks all BiDi preload scripts. */
#scripts = new Set();
/** Finds all entries that match the given filter. */
find(filter) {
if (!filter) {
return [...this.#scripts];
}
return [...this.#scripts].filter((script) => {
if (filter.id !== undefined && filter.id !== script.id) {
return false;
}
if (filter.targetId !== undefined &&
!script.targetIds.has(filter.targetId)) {
return false;
}
if (filter.global !== undefined &&
// Global scripts have no contexts
((filter.global && script.contexts !== undefined) ||
// Non global scripts always have contexts
(!filter.global && script.contexts === undefined))) {
return false;
}
return true;
});
}
add(preloadScript) {
this.#scripts.add(preloadScript);
}
/** Deletes all BiDi preload script entries that match the given filter. */
remove(filter) {
for (const preloadScript of this.find(filter)) {
this.#scripts.delete(preloadScript);
}
}
}
exports.PreloadScriptStorage = PreloadScriptStorage;
//# sourceMappingURL=PreloadScriptStorage.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PreloadScriptStorage.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/PreloadScriptStorage.ts"],"names":[],"mappings":";;;AA2BA;;GAEG;AACH,MAAa,oBAAoB;IAC/B,wCAAwC;IAC/B,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;IAE7C,qDAAqD;IACrD,IAAI,CAAC,MAA4B;QAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;YAC1C,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS,IAAI,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC;gBACvD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IACE,MAAM,CAAC,QAAQ,KAAK,SAAS;gBAC7B,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EACtC,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IACE,MAAM,CAAC,MAAM,KAAK,SAAS;gBAC3B,kCAAkC;gBAClC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC;oBAC/C,0CAA0C;oBAC1C,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,EACpD,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,aAA4B;QAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC;IAED,2EAA2E;IAC3E,MAAM,CAAC,MAA4B;QACjC,KAAK,MAAM,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;CACF;AA3CD,oDA2CC"}

View File

@@ -0,0 +1,65 @@
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Protocol } from 'devtools-protocol';
import type { CdpClient } from '../../../cdp/CdpClient.js';
import { Script } from '../../../protocol/protocol.js';
import { type LoggerFn } from '../../../utils/log.js';
import type { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import type { EventManager } from '../session/EventManager.js';
import type { RealmStorage } from './RealmStorage.js';
export declare abstract class Realm {
#private;
constructor(cdpClient: CdpClient, eventManager: EventManager, executionContextId: Protocol.Runtime.ExecutionContextId, logger: LoggerFn | undefined, origin: string, realmId: Script.Realm, realmStorage: RealmStorage);
cdpToBidiValue(cdpValue: Protocol.Runtime.CallFunctionOnResponse | Protocol.Runtime.EvaluateResponse, resultOwnership: Script.ResultOwnership): Script.RemoteValue;
/**
* Relies on the CDP to implement proper BiDi serialization, except:
* * CDP integer property `backendNodeId` is replaced with `sharedId` of
* `{documentId}_element_{backendNodeId}`;
* * CDP integer property `weakLocalObjectReference` is replaced with UUID `internalId`
* using unique-per serialization `internalIdMap`.
* * CDP type `platformobject` is replaced with `object`.
* @param deepSerializedValue - CDP value to be converted to BiDi.
* @param internalIdMap - Map from CDP integer `weakLocalObjectReference` to BiDi UUID
* `internalId`.
*/
protected serializeForBiDi(deepSerializedValue: Protocol.Runtime.DeepSerializedValue, internalIdMap: Map<number, string>): Script.RemoteValue;
get realmId(): Script.Realm;
get executionContextId(): Protocol.Runtime.ExecutionContextId;
get origin(): string;
get source(): Script.Source;
get cdpClient(): CdpClient;
abstract get associatedBrowsingContexts(): BrowsingContextImpl[];
abstract get realmType(): Script.RealmType;
protected get baseInfo(): Script.BaseRealmInfo;
abstract get realmInfo(): Script.RealmInfo;
evaluate(expression: string, awaitPromise: boolean, resultOwnership: Script.ResultOwnership, serializationOptions: Script.SerializationOptions, userActivation?: boolean): Promise<Script.EvaluateResult>;
protected initialize(): void;
/**
* Serializes a given CDP object into BiDi, keeping references in the
* target's `globalThis`.
*/
serializeCdpObject(cdpRemoteObject: Protocol.Runtime.RemoteObject, resultOwnership: Script.ResultOwnership): Promise<Script.RemoteValue>;
/**
* Gets the string representation of an object. This is equivalent to
* calling `toString()` on the object value.
*/
stringifyObject(cdpRemoteObject: Protocol.Runtime.RemoteObject): Promise<string>;
callFunction(functionDeclaration: string, thisLocalValue: Script.LocalValue, argumentsLocalValues: Script.LocalValue[], awaitPromise: boolean, resultOwnership: Script.ResultOwnership, serializationOptions: Script.SerializationOptions, userActivation?: boolean): Promise<Script.EvaluateResult>;
deserializeForCdp(localValue: Script.LocalValue): Promise<Protocol.Runtime.CallArgument>;
disown(handle: Script.Handle): Promise<void>;
dispose(): void;
}

View File

@@ -0,0 +1,474 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Realm = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const log_js_1 = require("../../../utils/log.js");
const uuid_js_1 = require("../../../utils/uuid.js");
const ChannelProxy_js_1 = require("./ChannelProxy.js");
class Realm {
#cdpClient;
#eventManager;
#executionContextId;
#logger;
#origin;
#realmId;
#realmStorage;
constructor(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage) {
this.#cdpClient = cdpClient;
this.#eventManager = eventManager;
this.#executionContextId = executionContextId;
this.#logger = logger;
this.#origin = origin;
this.#realmId = realmId;
this.#realmStorage = realmStorage;
this.#realmStorage.addRealm(this);
}
cdpToBidiValue(cdpValue, resultOwnership) {
const bidiValue = this.serializeForBiDi(cdpValue.result.deepSerializedValue, new Map());
if (cdpValue.result.objectId) {
const objectId = cdpValue.result.objectId;
if (resultOwnership === "root" /* Script.ResultOwnership.Root */) {
// Extend BiDi value with `handle` based on required `resultOwnership`
// and CDP response but not on the actual BiDi type.
bidiValue.handle = objectId;
// Remember all the handles sent to client.
this.#realmStorage.knownHandlesToRealmMap.set(objectId, this.realmId);
}
else {
// No need to await for the object to be released.
void this.#releaseObject(objectId).catch((error) => this.#logger?.(log_js_1.LogType.debugError, error));
}
}
if (cdpValue.result.type === 'object') {
switch (cdpValue.result.subtype) {
case 'generator':
case 'iterator':
bidiValue.type = cdpValue.result.subtype;
delete bidiValue['value'];
break;
default:
// Intentionally left blank.
}
}
return bidiValue;
}
/**
* Relies on the CDP to implement proper BiDi serialization, except:
* * CDP integer property `backendNodeId` is replaced with `sharedId` of
* `{documentId}_element_{backendNodeId}`;
* * CDP integer property `weakLocalObjectReference` is replaced with UUID `internalId`
* using unique-per serialization `internalIdMap`.
* * CDP type `platformobject` is replaced with `object`.
* @param deepSerializedValue - CDP value to be converted to BiDi.
* @param internalIdMap - Map from CDP integer `weakLocalObjectReference` to BiDi UUID
* `internalId`.
*/
serializeForBiDi(deepSerializedValue, internalIdMap) {
if (Object.hasOwn(deepSerializedValue, 'weakLocalObjectReference')) {
const weakLocalObjectReference = deepSerializedValue.weakLocalObjectReference;
if (!internalIdMap.has(weakLocalObjectReference)) {
internalIdMap.set(weakLocalObjectReference, (0, uuid_js_1.uuidv4)());
}
deepSerializedValue.internalId = internalIdMap.get(weakLocalObjectReference);
delete deepSerializedValue['weakLocalObjectReference'];
}
// Platform object is a special case. It should have only `{type: object}`
// without `value` field.
if (deepSerializedValue.type === 'platformobject') {
return { type: 'object' };
}
const bidiValue = deepSerializedValue.value;
if (bidiValue === undefined) {
return deepSerializedValue;
}
// Recursively update the nested values.
if (['array', 'set', 'htmlcollection', 'nodelist'].includes(deepSerializedValue.type)) {
for (const i in bidiValue) {
bidiValue[i] = this.serializeForBiDi(bidiValue[i], internalIdMap);
}
}
if (['object', 'map'].includes(deepSerializedValue.type)) {
for (const i in bidiValue) {
bidiValue[i] = [
this.serializeForBiDi(bidiValue[i][0], internalIdMap),
this.serializeForBiDi(bidiValue[i][1], internalIdMap),
];
}
}
return deepSerializedValue;
}
get realmId() {
return this.#realmId;
}
get executionContextId() {
return this.#executionContextId;
}
get origin() {
return this.#origin;
}
get source() {
return {
realm: this.realmId,
};
}
get cdpClient() {
return this.#cdpClient;
}
get baseInfo() {
return {
realm: this.realmId,
origin: this.origin,
};
}
async evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation = false) {
const cdpEvaluateResult = await this.cdpClient.sendCommand('Runtime.evaluate', {
contextId: this.executionContextId,
expression,
awaitPromise,
serializationOptions: Realm.#getSerializationOptions("deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */, serializationOptions),
userGesture: userActivation,
});
if (cdpEvaluateResult.exceptionDetails) {
return await this.#getExceptionResult(cdpEvaluateResult.exceptionDetails, 0, resultOwnership);
}
return {
realm: this.realmId,
result: this.cdpToBidiValue(cdpEvaluateResult, resultOwnership),
type: 'success',
};
}
initialize() {
for (const browsingContext of this.associatedBrowsingContexts) {
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.Script.EventNames.RealmCreated,
params: this.realmInfo,
}, browsingContext.id);
}
}
/**
* Serializes a given CDP object into BiDi, keeping references in the
* target's `globalThis`.
*/
async serializeCdpObject(cdpRemoteObject, resultOwnership) {
const argument = Realm.#cdpRemoteObjectToCallArgument(cdpRemoteObject);
const cdpValue = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((remoteObject) => remoteObject),
awaitPromise: false,
arguments: [argument],
serializationOptions: {
serialization: "deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */,
},
executionContextId: this.executionContextId,
});
return this.cdpToBidiValue(cdpValue, resultOwnership);
}
static #cdpRemoteObjectToCallArgument(cdpRemoteObject) {
if (cdpRemoteObject.objectId !== undefined) {
return { objectId: cdpRemoteObject.objectId };
}
if (cdpRemoteObject.unserializableValue !== undefined) {
return { unserializableValue: cdpRemoteObject.unserializableValue };
}
return { value: cdpRemoteObject.value };
}
/**
* Gets the string representation of an object. This is equivalent to
* calling `toString()` on the object value.
*/
async stringifyObject(cdpRemoteObject) {
const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((remoteObject) => String(remoteObject)),
awaitPromise: false,
arguments: [cdpRemoteObject],
returnByValue: true,
executionContextId: this.executionContextId,
});
return result.value;
}
async #flattenKeyValuePairs(mappingLocalValue) {
const keyValueArray = [];
for (const [key, value] of mappingLocalValue) {
let keyArg;
if (typeof key === 'string') {
// Key is a string.
keyArg = { value: key };
}
else {
// Key is a serialized value.
keyArg = await this.deserializeForCdp(key);
}
const valueArg = await this.deserializeForCdp(value);
keyValueArray.push(keyArg);
keyValueArray.push(valueArg);
}
return keyValueArray;
}
async #flattenValueList(listLocalValue) {
return await Promise.all(listLocalValue.map((localValue) => this.deserializeForCdp(localValue)));
}
async #serializeCdpExceptionDetails(cdpExceptionDetails, lineOffset, resultOwnership) {
const callFrames = cdpExceptionDetails.stackTrace?.callFrames.map((frame) => ({
url: frame.url,
functionName: frame.functionName,
lineNumber: frame.lineNumber - lineOffset,
columnNumber: frame.columnNumber,
})) ?? [];
// Exception should always be there.
const exception = cdpExceptionDetails.exception;
return {
exception: await this.serializeCdpObject(exception, resultOwnership),
columnNumber: cdpExceptionDetails.columnNumber,
lineNumber: cdpExceptionDetails.lineNumber - lineOffset,
stackTrace: {
callFrames,
},
text: (await this.stringifyObject(exception)) || cdpExceptionDetails.text,
};
}
async callFunction(functionDeclaration, thisLocalValue, argumentsLocalValues, awaitPromise, resultOwnership, serializationOptions, userActivation = false) {
const callFunctionAndSerializeScript = `(...args) => {
function callFunction(f, args) {
const deserializedThis = args.shift();
const deserializedArgs = args;
return f.apply(deserializedThis, deserializedArgs);
}
return callFunction((
${functionDeclaration}
), args);
}`;
const thisAndArgumentsList = [
await this.deserializeForCdp(thisLocalValue),
...(await Promise.all(argumentsLocalValues.map(async (argumentLocalValue) => await this.deserializeForCdp(argumentLocalValue)))),
];
let cdpCallFunctionResult;
try {
cdpCallFunctionResult = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: callFunctionAndSerializeScript,
awaitPromise,
arguments: thisAndArgumentsList,
serializationOptions: Realm.#getSerializationOptions("deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */, serializationOptions),
executionContextId: this.executionContextId,
userGesture: userActivation,
});
}
catch (error) {
// Heuristic to determine if the problem is in the argument.
// The check can be done on the `deserialization` step, but this approach
// helps to save round-trips.
if (error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
[
'Could not find object with given id',
'Argument should belong to the same JavaScript world as target object',
'Invalid remote object id',
].includes(error.message)) {
throw new protocol_js_1.NoSuchHandleException('Handle was not found.');
}
throw error;
}
if (cdpCallFunctionResult.exceptionDetails) {
return await this.#getExceptionResult(cdpCallFunctionResult.exceptionDetails, 1, resultOwnership);
}
return {
type: 'success',
result: this.cdpToBidiValue(cdpCallFunctionResult, resultOwnership),
realm: this.realmId,
};
}
async deserializeForCdp(localValue) {
if ('handle' in localValue && localValue.handle) {
return { objectId: localValue.handle };
// We tried to find a handle value but failed
// This allows us to have exhaustive switch on `localValue.type`
}
else if ('handle' in localValue || 'sharedId' in localValue) {
throw new protocol_js_1.NoSuchHandleException('Handle was not found.');
}
switch (localValue.type) {
case 'undefined':
return { unserializableValue: 'undefined' };
case 'null':
return { unserializableValue: 'null' };
case 'string':
return { value: localValue.value };
case 'number':
if (localValue.value === 'NaN') {
return { unserializableValue: 'NaN' };
}
else if (localValue.value === '-0') {
return { unserializableValue: '-0' };
}
else if (localValue.value === 'Infinity') {
return { unserializableValue: 'Infinity' };
}
else if (localValue.value === '-Infinity') {
return { unserializableValue: '-Infinity' };
}
return {
value: localValue.value,
};
case 'boolean':
return { value: Boolean(localValue.value) };
case 'bigint':
return {
unserializableValue: `BigInt(${JSON.stringify(localValue.value)})`,
};
case 'date':
return {
unserializableValue: `new Date(Date.parse(${JSON.stringify(localValue.value)}))`,
};
case 'regexp':
return {
unserializableValue: `new RegExp(${JSON.stringify(localValue.value.pattern)}, ${JSON.stringify(localValue.value.flags)})`,
};
case 'map': {
// TODO: If none of the nested keys and values has a remote
// reference, serialize to `unserializableValue` without CDP roundtrip.
const keyValueArray = await this.#flattenKeyValuePairs(localValue.value);
const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((...args) => {
const result = new Map();
for (let i = 0; i < args.length; i += 2) {
result.set(args[i], args[i + 1]);
}
return result;
}),
awaitPromise: false,
arguments: keyValueArray,
returnByValue: false,
executionContextId: this.executionContextId,
});
// TODO(#375): Release `result.objectId` after using.
return { objectId: result.objectId };
}
case 'object': {
// TODO: If none of the nested keys and values has a remote
// reference, serialize to `unserializableValue` without CDP roundtrip.
const keyValueArray = await this.#flattenKeyValuePairs(localValue.value);
const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((...args) => {
const result = {};
for (let i = 0; i < args.length; i += 2) {
// Key should be either `string`, `number`, or `symbol`.
const key = args[i];
result[key] = args[i + 1];
}
return result;
}),
awaitPromise: false,
arguments: keyValueArray,
returnByValue: false,
executionContextId: this.executionContextId,
});
// TODO(#375): Release `result.objectId` after using.
return { objectId: result.objectId };
}
case 'array': {
// TODO: If none of the nested items has a remote reference,
// serialize to `unserializableValue` without CDP roundtrip.
const args = await this.#flattenValueList(localValue.value);
const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((...args) => args),
awaitPromise: false,
arguments: args,
returnByValue: false,
executionContextId: this.executionContextId,
});
// TODO(#375): Release `result.objectId` after using.
return { objectId: result.objectId };
}
case 'set': {
// TODO: if none of the nested items has a remote reference,
// serialize to `unserializableValue` without CDP roundtrip.
const args = await this.#flattenValueList(localValue.value);
const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((...args) => new Set(args)),
awaitPromise: false,
arguments: args,
returnByValue: false,
executionContextId: this.executionContextId,
});
// TODO(#375): Release `result.objectId` after using.
return { objectId: result.objectId };
}
case 'channel': {
const channelProxy = new ChannelProxy_js_1.ChannelProxy(localValue.value, this.#logger);
const channelProxySendMessageHandle = await channelProxy.init(this, this.#eventManager);
return { objectId: channelProxySendMessageHandle };
}
// TODO(#375): Dispose of nested objects.
}
// Intentionally outside to handle unknown types
throw new Error(`Value ${JSON.stringify(localValue)} is not deserializable.`);
}
async #getExceptionResult(exceptionDetails, lineOffset, resultOwnership) {
return {
exceptionDetails: await this.#serializeCdpExceptionDetails(exceptionDetails, lineOffset, resultOwnership),
realm: this.realmId,
type: 'exception',
};
}
static #getSerializationOptions(serialization, serializationOptions) {
return {
serialization,
additionalParameters: Realm.#getAdditionalSerializationParameters(serializationOptions),
...Realm.#getMaxObjectDepth(serializationOptions),
};
}
static #getAdditionalSerializationParameters(serializationOptions) {
const additionalParameters = {};
if (serializationOptions.maxDomDepth !== undefined) {
additionalParameters['maxNodeDepth'] =
serializationOptions.maxDomDepth === null
? 1000
: serializationOptions.maxDomDepth;
}
if (serializationOptions.includeShadowTree !== undefined) {
additionalParameters['includeShadowTree'] =
serializationOptions.includeShadowTree;
}
return additionalParameters;
}
static #getMaxObjectDepth(serializationOptions) {
return serializationOptions.maxObjectDepth === undefined ||
serializationOptions.maxObjectDepth === null
? {}
: { maxDepth: serializationOptions.maxObjectDepth };
}
async #releaseObject(handle) {
try {
await this.cdpClient.sendCommand('Runtime.releaseObject', {
objectId: handle,
});
}
catch (error) {
// Heuristic to determine if the problem is in the unknown handler.
// Ignore the error if so.
if (!(error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
error.message === 'Invalid remote object id')) {
throw error;
}
}
}
async disown(handle) {
// Disowning an object from different realm does nothing.
if (this.#realmStorage.knownHandlesToRealmMap.get(handle) !== this.realmId) {
return;
}
await this.#releaseObject(handle);
this.#realmStorage.knownHandlesToRealmMap.delete(handle);
}
dispose() {
for (const browsingContext of this.associatedBrowsingContexts) {
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.Script.EventNames.RealmDestroyed,
params: {
realm: this.realmId,
},
}, browsingContext.id);
}
}
}
exports.Realm = Realm;
//# sourceMappingURL=Realm.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Protocol } from 'devtools-protocol';
import { type BrowsingContext, type Script } from '../../../protocol/protocol.js';
import type { Realm } from './Realm.js';
type RealmFilter = {
realmId?: Script.Realm;
browsingContextId?: BrowsingContext.BrowsingContext;
executionContextId?: Protocol.Runtime.ExecutionContextId;
origin?: string;
type?: Script.RealmType;
sandbox?: string;
cdpSessionId?: Protocol.Target.SessionID;
};
/** Container class for browsing realms. */
export declare class RealmStorage {
#private;
get knownHandlesToRealmMap(): Map<string, string>;
addRealm(realm: Realm): void;
/** Finds all realms that match the given filter. */
findRealms(filter: RealmFilter): Realm[];
findRealm(filter: RealmFilter): Realm | undefined;
/** Gets the only realm that matches the given filter, if any, otherwise throws. */
getRealm(filter: RealmFilter): Realm;
/** Deletes all realms that match the given filter. */
deleteRealms(filter: RealmFilter): void;
}
export {};

View File

@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RealmStorage = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const WindowRealm_js_1 = require("./WindowRealm.js");
/** Container class for browsing realms. */
class RealmStorage {
/** Tracks handles and their realms sent to the client. */
#knownHandlesToRealmMap = new Map();
/** Map from realm ID to Realm. */
#realmMap = new Map();
get knownHandlesToRealmMap() {
return this.#knownHandlesToRealmMap;
}
addRealm(realm) {
this.#realmMap.set(realm.realmId, realm);
}
/** Finds all realms that match the given filter. */
findRealms(filter) {
return Array.from(this.#realmMap.values()).filter((realm) => {
if (filter.realmId !== undefined && filter.realmId !== realm.realmId) {
return false;
}
if (filter.browsingContextId !== undefined &&
!realm.associatedBrowsingContexts
.map((browsingContext) => browsingContext.id)
.includes(filter.browsingContextId)) {
return false;
}
if (filter.sandbox !== undefined &&
(!(realm instanceof WindowRealm_js_1.WindowRealm) || filter.sandbox !== realm.sandbox)) {
return false;
}
if (filter.executionContextId !== undefined &&
filter.executionContextId !== realm.executionContextId) {
return false;
}
if (filter.origin !== undefined && filter.origin !== realm.origin) {
return false;
}
if (filter.type !== undefined && filter.type !== realm.realmType) {
return false;
}
if (filter.cdpSessionId !== undefined &&
filter.cdpSessionId !== realm.cdpClient.sessionId) {
return false;
}
return true;
});
}
findRealm(filter) {
const maybeRealms = this.findRealms(filter);
if (maybeRealms.length !== 1) {
return undefined;
}
return maybeRealms[0];
}
/** Gets the only realm that matches the given filter, if any, otherwise throws. */
getRealm(filter) {
const maybeRealm = this.findRealm(filter);
if (maybeRealm === undefined) {
throw new protocol_js_1.NoSuchFrameException(`Realm ${JSON.stringify(filter)} not found`);
}
return maybeRealm;
}
/** Deletes all realms that match the given filter. */
deleteRealms(filter) {
this.findRealms(filter).map((realm) => {
realm.dispose();
this.#realmMap.delete(realm.realmId);
Array.from(this.knownHandlesToRealmMap.entries())
.filter(([, r]) => r === realm.realmId)
.map(([handle]) => this.knownHandlesToRealmMap.delete(handle));
});
}
}
exports.RealmStorage = RealmStorage;
//# sourceMappingURL=RealmStorage.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RealmStorage.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/RealmStorage.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type EmptyResult, Script } from '../../../protocol/protocol';
import type { LoggerFn } from '../../../utils/log';
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage';
import type { PreloadScriptStorage } from './PreloadScriptStorage';
import type { RealmStorage } from './RealmStorage';
export declare class ScriptProcessor {
#private;
constructor(browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, preloadScriptStorage: PreloadScriptStorage, logger?: LoggerFn);
addPreloadScript(params: Script.AddPreloadScriptParameters): Promise<Script.AddPreloadScriptResult>;
removePreloadScript(params: Script.RemovePreloadScriptParameters): Promise<EmptyResult>;
callFunction(params: Script.CallFunctionParameters): Promise<Script.EvaluateResult>;
evaluate(params: Script.EvaluateParameters): Promise<Script.EvaluateResult>;
disown(params: Script.DisownParameters): Promise<EmptyResult>;
getRealms(params: Script.GetRealmsParameters): Script.GetRealmsResult;
}

View File

@@ -0,0 +1,117 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScriptProcessor = void 0;
const protocol_1 = require("../../../protocol/protocol");
const PreloadScript_1 = require("./PreloadScript");
class ScriptProcessor {
#browsingContextStorage;
#realmStorage;
#preloadScriptStorage;
#logger;
constructor(browsingContextStorage, realmStorage, preloadScriptStorage, logger) {
this.#browsingContextStorage = browsingContextStorage;
this.#realmStorage = realmStorage;
this.#preloadScriptStorage = preloadScriptStorage;
this.#logger = logger;
}
async addPreloadScript(params) {
const contexts = new Set();
if (params.contexts) {
// XXX: Remove once https://github.com/google/cddlconv/issues/16 is implemented
if (params.contexts.length === 0) {
throw new protocol_1.InvalidArgumentException('Contexts list is empty.');
}
for (const contextId of params.contexts) {
const context = this.#browsingContextStorage.getContext(contextId);
if (context.isTopLevelContext()) {
contexts.add(context);
}
else {
throw new protocol_1.InvalidArgumentException(`Non top-level context '${contextId}' given.`);
}
}
}
const preloadScript = new PreloadScript_1.PreloadScript(params, this.#logger);
this.#preloadScriptStorage.add(preloadScript);
const cdpTargets = contexts.size === 0
? new Set(this.#browsingContextStorage
.getTopLevelContexts()
.map((context) => context.cdpTarget))
: new Set([...contexts.values()].map((context) => context.cdpTarget));
await preloadScript.initInTargets(cdpTargets, false);
return {
script: preloadScript.id,
};
}
async removePreloadScript(params) {
const bidiId = params.script;
const scripts = this.#preloadScriptStorage.find({
id: bidiId,
});
if (scripts.length === 0) {
throw new protocol_1.NoSuchScriptException(`No preload script with BiDi ID '${bidiId}'`);
}
await Promise.all(scripts.map((script) => script.remove()));
this.#preloadScriptStorage.remove({
id: bidiId,
});
return {};
}
async callFunction(params) {
const realm = await this.#getRealm(params.target);
return await realm.callFunction(params.functionDeclaration, params.this ?? {
type: 'undefined',
}, // `this` is `undefined` by default.
params.arguments ?? [], // `arguments` is `[]` by default.
params.awaitPromise, params.resultOwnership ?? "none" /* Script.ResultOwnership.None */, params.serializationOptions ?? {}, params.userActivation ?? false);
}
async evaluate(params) {
const realm = await this.#getRealm(params.target);
return await realm.evaluate(params.expression, params.awaitPromise, params.resultOwnership ?? "none" /* Script.ResultOwnership.None */, params.serializationOptions ?? {}, params.userActivation ?? false);
}
async disown(params) {
const realm = await this.#getRealm(params.target);
await Promise.all(params.handles.map(async (handle) => await realm.disown(handle)));
return {};
}
getRealms(params) {
if (params.context !== undefined) {
// Make sure the context is known.
this.#browsingContextStorage.getContext(params.context);
}
const realms = this.#realmStorage
.findRealms({
browsingContextId: params.context,
type: params.type,
})
.map((realm) => realm.realmInfo);
return { realms };
}
async #getRealm(target) {
if ('realm' in target) {
return this.#realmStorage.getRealm({
realmId: target.realm,
});
}
const context = this.#browsingContextStorage.getContext(target.context);
return await context.getOrCreateSandbox(target.sandbox);
}
}
exports.ScriptProcessor = ScriptProcessor;
//# sourceMappingURL=ScriptProcessor.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ScriptProcessor.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/ScriptProcessor.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,6 @@
export declare function getSharedId(frameId: string, documentId: string, backendNodeId: number, sharedIdWithFrame: boolean): string;
export declare function parseSharedId(sharedId: string): {
frameId: string | undefined;
documentId: string;
backendNodeId: number;
} | null;

View File

@@ -0,0 +1,80 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSharedId = exports.getSharedId = void 0;
const SHARED_ID_DIVIDER = '_element_';
function getSharedId(frameId, documentId, backendNodeId, sharedIdWithFrame) {
if (sharedIdWithFrame) {
return `f.${frameId}.d.${documentId}.e.${backendNodeId}`;
}
// TODO: remove once ChromeDriver accepts sharedId in the new format:
// http://go/chromedriver:weak-map
return `${documentId}${SHARED_ID_DIVIDER}${backendNodeId}`;
}
exports.getSharedId = getSharedId;
function parseLegacySharedId(sharedId) {
const match = sharedId.match(new RegExp(`(.*)${SHARED_ID_DIVIDER}(.*)`));
if (!match) {
// SharedId is incorrectly formatted.
return null;
}
const documentId = match[1];
const elementId = match[2];
if (documentId === undefined || elementId === undefined) {
return null;
}
const backendNodeId = parseInt(elementId ?? '');
if (isNaN(backendNodeId)) {
return null;
}
return {
documentId,
backendNodeId,
};
}
function parseSharedId(sharedId) {
// TODO: remove legacy check once ChromeDriver provides sharedId in the new format.
const legacyFormattedSharedId = parseLegacySharedId(sharedId);
if (legacyFormattedSharedId !== null) {
return { ...legacyFormattedSharedId, frameId: undefined };
}
const match = sharedId.match(/f\.(.*)\.d\.(.*)\.e\.([0-9]*)/);
if (!match) {
// SharedId is incorrectly formatted.
return null;
}
const frameId = match[1];
const documentId = match[2];
const elementId = match[3];
if (frameId === undefined ||
documentId === undefined ||
elementId === undefined) {
return null;
}
const backendNodeId = parseInt(elementId ?? '');
if (isNaN(backendNodeId)) {
return null;
}
return {
frameId,
documentId,
backendNodeId,
};
}
exports.parseSharedId = parseSharedId;
//# sourceMappingURL=SharedId.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SharedId.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/SharedId.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAEtC,SAAgB,WAAW,CACzB,OAAe,EACf,UAAkB,EAClB,aAAqB,EACrB,iBAA0B;IAE1B,IAAI,iBAAiB,EAAE,CAAC;QACtB,OAAO,KAAK,OAAO,MAAM,UAAU,MAAM,aAAa,EAAE,CAAC;IAC3D,CAAC;IACD,qEAAqE;IACrE,mCAAmC;IACnC,OAAO,GAAG,UAAU,GAAG,iBAAiB,GAAG,aAAa,EAAE,CAAC;AAC7D,CAAC;AAZD,kCAYC;AAED,SAAS,mBAAmB,CAAC,QAAgB;IAI3C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,iBAAiB,MAAM,CAAC,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,qCAAqC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE3B,IAAI,UAAU,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,aAAa,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,UAAU;QACV,aAAa;KACd,CAAC;AACJ,CAAC;AAED,SAAgB,aAAa,CAAC,QAAgB;IAQ5C,mFAAmF;IACnF,MAAM,uBAAuB,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC9D,IAAI,uBAAuB,KAAK,IAAI,EAAE,CAAC;QACrC,OAAO,EAAC,GAAG,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAC,CAAC;IAC1D,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,qCAAqC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE3B,IACE,OAAO,KAAK,SAAS;QACrB,UAAU,KAAK,SAAS;QACxB,SAAS,KAAK,SAAS,EACvB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,aAAa,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,OAAO;QACP,UAAU;QACV,aAAa;KACd,CAAC;AACJ,CAAC;AAxCD,sCAwCC"}

View File

@@ -0,0 +1,39 @@
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Protocol } from 'devtools-protocol';
import type { CdpClient } from '../../../cdp/CdpClient.js';
import { type BrowsingContext, type Script } from '../../../protocol/protocol.js';
import type { LoggerFn } from '../../../utils/log.js';
import type { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage.js';
import type { EventManager } from '../session/EventManager.js';
import { Realm } from './Realm.js';
import type { RealmStorage } from './RealmStorage.js';
export declare class WindowRealm extends Realm {
#private;
readonly sandbox: string | undefined;
constructor(browsingContextId: BrowsingContext.BrowsingContext, browsingContextStorage: BrowsingContextStorage, cdpClient: CdpClient, eventManager: EventManager, executionContextId: Protocol.Runtime.ExecutionContextId, logger: LoggerFn | undefined, origin: string, realmId: Script.Realm, realmStorage: RealmStorage, sandbox: string | undefined, sharedIdWithFrame: boolean);
get browsingContext(): BrowsingContextImpl;
get associatedBrowsingContexts(): [BrowsingContextImpl];
get realmType(): 'window';
get realmInfo(): Script.WindowRealmInfo;
get source(): Script.Source;
serializeForBiDi(deepSerializedValue: Protocol.Runtime.DeepSerializedValue, internalIdMap: Map<number, string>): Script.RemoteValue;
deserializeForCdp(localValue: Script.LocalValue): Promise<Protocol.Runtime.CallArgument>;
evaluate(expression: string, awaitPromise: boolean, resultOwnership: Script.ResultOwnership, serializationOptions: Script.SerializationOptions, userActivation?: boolean): Promise<Script.EvaluateResult>;
callFunction(functionDeclaration: string, thisLocalValue: Script.LocalValue, argumentsLocalValues: Script.LocalValue[], awaitPromise: boolean, resultOwnership: Script.ResultOwnership, serializationOptions: Script.SerializationOptions, userActivation?: boolean): Promise<Script.EvaluateResult>;
}

View File

@@ -0,0 +1,142 @@
"use strict";
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.WindowRealm = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const Realm_js_1 = require("./Realm.js");
const SharedId_js_1 = require("./SharedId.js");
class WindowRealm extends Realm_js_1.Realm {
#browsingContextId;
#browsingContextStorage;
#sharedIdWithFrame;
sandbox;
constructor(browsingContextId, browsingContextStorage, cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage, sandbox, sharedIdWithFrame) {
super(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage);
this.#browsingContextId = browsingContextId;
this.#browsingContextStorage = browsingContextStorage;
this.#sharedIdWithFrame = sharedIdWithFrame;
this.sandbox = sandbox;
this.initialize();
}
#getBrowsingContextId(navigableId) {
const maybeBrowsingContext = this.#browsingContextStorage
.getAllContexts()
.find((context) => context.navigableId === navigableId);
return maybeBrowsingContext?.id ?? 'UNKNOWN';
}
get browsingContext() {
return this.#browsingContextStorage.getContext(this.#browsingContextId);
}
get associatedBrowsingContexts() {
return [this.browsingContext];
}
get realmType() {
return 'window';
}
get realmInfo() {
return {
...this.baseInfo,
type: this.realmType,
context: this.#browsingContextId,
sandbox: this.sandbox,
};
}
get source() {
return {
realm: this.realmId,
context: this.browsingContext.id,
};
}
serializeForBiDi(deepSerializedValue, internalIdMap) {
const bidiValue = deepSerializedValue.value;
if (deepSerializedValue.type === 'node') {
if (Object.hasOwn(bidiValue, 'backendNodeId')) {
let navigableId = this.browsingContext.navigableId ?? 'UNKNOWN';
if (Object.hasOwn(bidiValue, 'loaderId')) {
// `loaderId` should be always there after ~2024-03-05, when
// https://crrev.com/c/5116240 reaches stable.
// TODO: remove the check after the date.
navigableId = bidiValue.loaderId;
delete bidiValue['loaderId'];
}
deepSerializedValue.sharedId =
(0, SharedId_js_1.getSharedId)(this.#getBrowsingContextId(navigableId), navigableId, bidiValue.backendNodeId, this.#sharedIdWithFrame);
delete bidiValue['backendNodeId'];
}
if (Object.hasOwn(bidiValue, 'children')) {
for (const i in bidiValue.children) {
bidiValue.children[i] = this.serializeForBiDi(bidiValue.children[i], internalIdMap);
}
}
if (Object.hasOwn(bidiValue, 'shadowRoot') &&
bidiValue.shadowRoot !== null) {
bidiValue.shadowRoot = this.serializeForBiDi(bidiValue.shadowRoot, internalIdMap);
}
// `namespaceURI` can be is either `null` or non-empty string.
if (bidiValue.namespaceURI === '') {
bidiValue.namespaceURI = null;
}
}
return super.serializeForBiDi(deepSerializedValue, internalIdMap);
}
async deserializeForCdp(localValue) {
if ('sharedId' in localValue && localValue.sharedId) {
const parsedSharedId = (0, SharedId_js_1.parseSharedId)(localValue.sharedId);
if (parsedSharedId === null) {
throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`);
}
const { documentId, backendNodeId } = parsedSharedId;
// TODO: add proper validation if the element is accessible from the current realm.
if (this.browsingContext.navigableId !== documentId) {
throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" belongs to different document. Current document is ${this.browsingContext.navigableId}.`);
}
try {
const { object } = await this.cdpClient.sendCommand('DOM.resolveNode', {
backendNodeId,
executionContextId: this.executionContextId,
});
// TODO(#375): Release `obj.object.objectId` after using.
return { objectId: object.objectId };
}
catch (error) {
// Heuristic to detect "no such node" exception. Based on the specific
// CDP implementation.
if (error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
error.message === 'No node with given id found') {
throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`);
}
throw new protocol_js_1.UnknownErrorException(error.message, error.stack);
}
}
return await super.deserializeForCdp(localValue);
}
async evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation) {
await this.#browsingContextStorage
.getContext(this.#browsingContextId)
.targetUnblockedOrThrow();
return await super.evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation);
}
async callFunction(functionDeclaration, thisLocalValue, argumentsLocalValues, awaitPromise, resultOwnership, serializationOptions, userActivation) {
await this.#browsingContextStorage
.getContext(this.#browsingContextId)
.targetUnblockedOrThrow();
return await super.callFunction(functionDeclaration, thisLocalValue, argumentsLocalValues, awaitPromise, resultOwnership, serializationOptions, userActivation);
}
}
exports.WindowRealm = WindowRealm;
//# sourceMappingURL=WindowRealm.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"WindowRealm.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/WindowRealm.ts"],"names":[],"mappings":"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"}