mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
refactor: move binding called handling (#12410)
This commit is contained in:
parent
d0cd710e49
commit
83a97164ba
@ -67,13 +67,15 @@ export class ExecutionContext
|
|||||||
/** Emitted when this execution context is disposed. */
|
/** Emitted when this execution context is disposed. */
|
||||||
disposed: undefined;
|
disposed: undefined;
|
||||||
consoleapicalled: Protocol.Runtime.ConsoleAPICalledEvent;
|
consoleapicalled: Protocol.Runtime.ConsoleAPICalledEvent;
|
||||||
|
/** Emitted when a binding that is not installed by the ExecutionContext is called. */
|
||||||
|
bindingcalled: Protocol.Runtime.BindingCalledEvent;
|
||||||
}>
|
}>
|
||||||
implements Disposable
|
implements Disposable
|
||||||
{
|
{
|
||||||
_client: CDPSession;
|
#client: CDPSession;
|
||||||
_world: IsolatedWorld;
|
#world: IsolatedWorld;
|
||||||
_contextId: number;
|
#id: number;
|
||||||
_contextName?: string;
|
#name?: string;
|
||||||
|
|
||||||
readonly #disposables = new DisposableStack();
|
readonly #disposables = new DisposableStack();
|
||||||
|
|
||||||
@ -83,16 +85,16 @@ export class ExecutionContext
|
|||||||
world: IsolatedWorld
|
world: IsolatedWorld
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._client = client;
|
this.#client = client;
|
||||||
this._world = world;
|
this.#world = world;
|
||||||
this._contextId = contextPayload.id;
|
this.#id = contextPayload.id;
|
||||||
if (contextPayload.name) {
|
if (contextPayload.name) {
|
||||||
this._contextName = contextPayload.name;
|
this.#name = contextPayload.name;
|
||||||
}
|
}
|
||||||
const clientEmitter = this.#disposables.use(new EventEmitter(this._client));
|
const clientEmitter = this.#disposables.use(new EventEmitter(this.#client));
|
||||||
clientEmitter.on('Runtime.bindingCalled', this.#onBindingCalled.bind(this));
|
clientEmitter.on('Runtime.bindingCalled', this.#onBindingCalled.bind(this));
|
||||||
clientEmitter.on('Runtime.executionContextDestroyed', async event => {
|
clientEmitter.on('Runtime.executionContextDestroyed', async event => {
|
||||||
if (event.executionContextId === this._contextId) {
|
if (event.executionContextId === this.#id) {
|
||||||
this[disposeSymbol]();
|
this[disposeSymbol]();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -118,16 +120,16 @@ export class ExecutionContext
|
|||||||
|
|
||||||
using _ = await this.#mutex.acquire();
|
using _ = await this.#mutex.acquire();
|
||||||
try {
|
try {
|
||||||
await this._client.send(
|
await this.#client.send(
|
||||||
'Runtime.addBinding',
|
'Runtime.addBinding',
|
||||||
this._contextName
|
this.#name
|
||||||
? {
|
? {
|
||||||
name: binding.name,
|
name: binding.name,
|
||||||
executionContextName: this._contextName,
|
executionContextName: this.#name,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
name: binding.name,
|
name: binding.name,
|
||||||
executionContextId: this._contextId,
|
executionContextId: this.#id,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -166,14 +168,16 @@ export class ExecutionContext
|
|||||||
}
|
}
|
||||||
const {type, name, seq, args, isTrivial} = payload;
|
const {type, name, seq, args, isTrivial} = payload;
|
||||||
if (type !== 'internal') {
|
if (type !== 'internal') {
|
||||||
|
this.emit('bindingcalled', event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.#bindings.has(name)) {
|
if (!this.#bindings.has(name)) {
|
||||||
|
this.emit('bindingcalled', event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (event.executionContextId !== this._contextId) {
|
if (event.executionContextId !== this.#id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,8 +188,12 @@ export class ExecutionContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get id(): number {
|
||||||
|
return this.#id;
|
||||||
|
}
|
||||||
|
|
||||||
#onConsoleAPI(event: Protocol.Runtime.ConsoleAPICalledEvent): void {
|
#onConsoleAPI(event: Protocol.Runtime.ConsoleAPICalledEvent): void {
|
||||||
if (event.executionContextId !== this._contextId) {
|
if (event.executionContextId !== this.#id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit('consoleapicalled', event);
|
this.emit('consoleapicalled', event);
|
||||||
@ -365,13 +373,13 @@ export class ExecutionContext
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isString(pageFunction)) {
|
if (isString(pageFunction)) {
|
||||||
const contextId = this._contextId;
|
const contextId = this.#id;
|
||||||
const expression = pageFunction;
|
const expression = pageFunction;
|
||||||
const expressionWithSourceUrl = SOURCE_URL_REGEX.test(expression)
|
const expressionWithSourceUrl = SOURCE_URL_REGEX.test(expression)
|
||||||
? expression
|
? expression
|
||||||
: `${expression}\n${sourceUrlComment}\n`;
|
: `${expression}\n${sourceUrlComment}\n`;
|
||||||
|
|
||||||
const {exceptionDetails, result: remoteObject} = await this._client
|
const {exceptionDetails, result: remoteObject} = await this.#client
|
||||||
.send('Runtime.evaluate', {
|
.send('Runtime.evaluate', {
|
||||||
expression: expressionWithSourceUrl,
|
expression: expressionWithSourceUrl,
|
||||||
contextId,
|
contextId,
|
||||||
@ -387,7 +395,7 @@ export class ExecutionContext
|
|||||||
|
|
||||||
return returnByValue
|
return returnByValue
|
||||||
? valueFromRemoteObject(remoteObject)
|
? valueFromRemoteObject(remoteObject)
|
||||||
: this._world.createCdpHandle(remoteObject);
|
: this.#world.createCdpHandle(remoteObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
const functionDeclaration = stringifyFunction(pageFunction);
|
const functionDeclaration = stringifyFunction(pageFunction);
|
||||||
@ -398,9 +406,9 @@ export class ExecutionContext
|
|||||||
: `${functionDeclaration}\n${sourceUrlComment}\n`;
|
: `${functionDeclaration}\n${sourceUrlComment}\n`;
|
||||||
let callFunctionOnPromise;
|
let callFunctionOnPromise;
|
||||||
try {
|
try {
|
||||||
callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', {
|
callFunctionOnPromise = this.#client.send('Runtime.callFunctionOn', {
|
||||||
functionDeclaration: functionDeclarationWithSourceUrl,
|
functionDeclaration: functionDeclarationWithSourceUrl,
|
||||||
executionContextId: this._contextId,
|
executionContextId: this.#id,
|
||||||
arguments: args.length
|
arguments: args.length
|
||||||
? await Promise.all(args.map(convertArgument.bind(this)))
|
? await Promise.all(args.map(convertArgument.bind(this)))
|
||||||
: [],
|
: [],
|
||||||
@ -424,7 +432,7 @@ export class ExecutionContext
|
|||||||
}
|
}
|
||||||
return returnByValue
|
return returnByValue
|
||||||
? valueFromRemoteObject(remoteObject)
|
? valueFromRemoteObject(remoteObject)
|
||||||
: this._world.createCdpHandle(remoteObject);
|
: this.#world.createCdpHandle(remoteObject);
|
||||||
|
|
||||||
async function convertArgument(
|
async function convertArgument(
|
||||||
this: ExecutionContext,
|
this: ExecutionContext,
|
||||||
@ -454,7 +462,7 @@ export class ExecutionContext
|
|||||||
? arg
|
? arg
|
||||||
: null;
|
: null;
|
||||||
if (objectHandle) {
|
if (objectHandle) {
|
||||||
if (objectHandle.realm !== this._world) {
|
if (objectHandle.realm !== this.#world) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'JSHandles can be evaluated only in the context they were created!'
|
'JSHandles can be evaluated only in the context they were created!'
|
||||||
);
|
);
|
||||||
|
@ -80,6 +80,10 @@ export class CdpFrame extends Frame {
|
|||||||
'consoleapicalled',
|
'consoleapicalled',
|
||||||
this.#onMainWorldConsoleApiCalled.bind(this)
|
this.#onMainWorldConsoleApiCalled.bind(this)
|
||||||
);
|
);
|
||||||
|
this.worlds[MAIN_WORLD].emitter.on(
|
||||||
|
'bindingcalled',
|
||||||
|
this.#onMainWorldBindingCalled.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#onMainWorldConsoleApiCalled(
|
#onMainWorldConsoleApiCalled(
|
||||||
@ -91,6 +95,13 @@ export class CdpFrame extends Frame {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#onMainWorldBindingCalled(event: Protocol.Runtime.BindingCalledEvent) {
|
||||||
|
this._frameManager.emit(FrameManagerEvent.BindingCalled, [
|
||||||
|
this.worlds[MAIN_WORLD],
|
||||||
|
event,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used internally in DevTools.
|
* This is used internally in DevTools.
|
||||||
*
|
*
|
||||||
|
@ -41,7 +41,6 @@ export class FrameManager extends EventEmitter<FrameManagerEvents> {
|
|||||||
#page: CdpPage;
|
#page: CdpPage;
|
||||||
#networkManager: NetworkManager;
|
#networkManager: NetworkManager;
|
||||||
#timeoutSettings: TimeoutSettings;
|
#timeoutSettings: TimeoutSettings;
|
||||||
#contextIdToContext = new Map<string, ExecutionContext>();
|
|
||||||
#isolatedWorlds = new Set<string>();
|
#isolatedWorlds = new Set<string>();
|
||||||
#client: CDPSession;
|
#client: CDPSession;
|
||||||
|
|
||||||
@ -223,22 +222,6 @@ export class FrameManager extends EventEmitter<FrameManagerEvents> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executionContextById(
|
|
||||||
contextId: number,
|
|
||||||
session: CDPSession = this.#client
|
|
||||||
): ExecutionContext {
|
|
||||||
const context = this.getExecutionContextById(contextId, session);
|
|
||||||
assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
getExecutionContextById(
|
|
||||||
contextId: number,
|
|
||||||
session: CDPSession = this.#client
|
|
||||||
): ExecutionContext | undefined {
|
|
||||||
return this.#contextIdToContext.get(`${session.id()}:${contextId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
page(): CdpPage {
|
page(): CdpPage {
|
||||||
return this.#page;
|
return this.#page;
|
||||||
}
|
}
|
||||||
@ -491,16 +474,6 @@ export class FrameManager extends EventEmitter<FrameManagerEvents> {
|
|||||||
world
|
world
|
||||||
);
|
);
|
||||||
world.setContext(context);
|
world.setContext(context);
|
||||||
const key = `${session.id()}:${contextPayload.id}`;
|
|
||||||
this.#contextIdToContext.set(key, context);
|
|
||||||
context.once('disposed', () => {
|
|
||||||
const key = `${session.id()}:${contextPayload.id}`;
|
|
||||||
const context = this.#contextIdToContext.get(key);
|
|
||||||
if (!context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.#contextIdToContext.delete(key);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#removeFramesRecursively(frame: CdpFrame): void {
|
#removeFramesRecursively(frame: CdpFrame): void {
|
||||||
|
@ -28,6 +28,7 @@ export namespace FrameManagerEvent {
|
|||||||
'FrameManager.FrameNavigatedWithinDocument'
|
'FrameManager.FrameNavigatedWithinDocument'
|
||||||
);
|
);
|
||||||
export const ConsoleApiCalled = Symbol('FrameManager.ConsoleApiCalled');
|
export const ConsoleApiCalled = Symbol('FrameManager.ConsoleApiCalled');
|
||||||
|
export const BindingCalled = Symbol('FrameManager.BindingCalled');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,4 +46,8 @@ export interface FrameManagerEvents extends Record<EventType, unknown> {
|
|||||||
IsolatedWorld,
|
IsolatedWorld,
|
||||||
Protocol.Runtime.ConsoleAPICalledEvent,
|
Protocol.Runtime.ConsoleAPICalledEvent,
|
||||||
];
|
];
|
||||||
|
[FrameManagerEvent.BindingCalled]: [
|
||||||
|
IsolatedWorld,
|
||||||
|
Protocol.Runtime.BindingCalledEvent,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,8 @@ type IsolatedWorldEmitter = EventEmitter<{
|
|||||||
disposed: undefined;
|
disposed: undefined;
|
||||||
// Emitted when a new console message is logged.
|
// Emitted when a new console message is logged.
|
||||||
consoleapicalled: Protocol.Runtime.ConsoleAPICalledEvent;
|
consoleapicalled: Protocol.Runtime.ConsoleAPICalledEvent;
|
||||||
|
/** Emitted when a binding that is not installed by the ExecutionContext is called. */
|
||||||
|
bindingcalled: Protocol.Runtime.BindingCalledEvent;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,6 +91,7 @@ export class IsolatedWorld extends Realm {
|
|||||||
this.#context?.[disposeSymbol]();
|
this.#context?.[disposeSymbol]();
|
||||||
context.once('disposed', this.#onContextDisposed.bind(this));
|
context.once('disposed', this.#onContextDisposed.bind(this));
|
||||||
context.on('consoleapicalled', this.#onContextConsoleApiCalled.bind(this));
|
context.on('consoleapicalled', this.#onContextConsoleApiCalled.bind(this));
|
||||||
|
context.on('bindingcalled', this.#onContextBindingCalled.bind(this));
|
||||||
this.#context = context;
|
this.#context = context;
|
||||||
this.#emitter.emit('context', context);
|
this.#emitter.emit('context', context);
|
||||||
void this.taskManager.rerunAll();
|
void this.taskManager.rerunAll();
|
||||||
@ -107,10 +110,18 @@ export class IsolatedWorld extends Realm {
|
|||||||
this.#emitter.emit('consoleapicalled', event);
|
this.#emitter.emit('consoleapicalled', event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#onContextBindingCalled(event: Protocol.Runtime.BindingCalledEvent): void {
|
||||||
|
this.#emitter.emit('bindingcalled', event);
|
||||||
|
}
|
||||||
|
|
||||||
hasContext(): boolean {
|
hasContext(): boolean {
|
||||||
return !!this.#context;
|
return !!this.#context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get context(): ExecutionContext | undefined {
|
||||||
|
return this.#context;
|
||||||
|
}
|
||||||
|
|
||||||
#executionContext(): ExecutionContext | undefined {
|
#executionContext(): ExecutionContext | undefined {
|
||||||
if (this.disposed) {
|
if (this.disposed) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -193,7 +204,7 @@ export class IsolatedWorld extends Realm {
|
|||||||
}
|
}
|
||||||
const {object} = await this.client.send('DOM.resolveNode', {
|
const {object} = await this.client.send('DOM.resolveNode', {
|
||||||
backendNodeId: backendNodeId,
|
backendNodeId: backendNodeId,
|
||||||
executionContextId: context._contextId,
|
executionContextId: context.id,
|
||||||
});
|
});
|
||||||
return this.createCdpHandle(object) as JSHandle<Node>;
|
return this.createCdpHandle(object) as JSHandle<Node>;
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,6 @@ export class CdpPage extends Page {
|
|||||||
return this.emit(PageEvent.Load, undefined);
|
return this.emit(PageEvent.Load, undefined);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
['Runtime.bindingCalled', this.#onBindingCalled.bind(this)],
|
|
||||||
['Page.javascriptDialogOpening', this.#onDialog.bind(this)],
|
['Page.javascriptDialogOpening', this.#onDialog.bind(this)],
|
||||||
['Runtime.exceptionThrown', this.#handleException.bind(this)],
|
['Runtime.exceptionThrown', this.#handleException.bind(this)],
|
||||||
['Inspector.targetCrashed', this.#onTargetCrashed.bind(this)],
|
['Inspector.targetCrashed', this.#onTargetCrashed.bind(this)],
|
||||||
@ -259,6 +258,16 @@ export class CdpPage extends Page {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.#frameManager.on(
|
||||||
|
FrameManagerEvent.BindingCalled,
|
||||||
|
([world, event]: [
|
||||||
|
IsolatedWorld,
|
||||||
|
Protocol.Runtime.BindingCalledEvent,
|
||||||
|
]) => {
|
||||||
|
void this.#onBindingCalled(world, event);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
for (const [eventName, handler] of this.#networkManagerHandlers) {
|
for (const [eventName, handler] of this.#networkManagerHandlers) {
|
||||||
// TODO: Remove any.
|
// TODO: Remove any.
|
||||||
this.#frameManager.networkManager.on(eventName, handler as any);
|
this.#frameManager.networkManager.on(eventName, handler as any);
|
||||||
@ -803,6 +812,7 @@ export class CdpPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #onBindingCalled(
|
async #onBindingCalled(
|
||||||
|
world: IsolatedWorld,
|
||||||
event: Protocol.Runtime.BindingCalledEvent
|
event: Protocol.Runtime.BindingCalledEvent
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let payload: BindingPayload;
|
let payload: BindingPayload;
|
||||||
@ -818,10 +828,7 @@ export class CdpPage extends Page {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = this.#frameManager.executionContextById(
|
const context = world.context;
|
||||||
event.executionContextId,
|
|
||||||
this.#primaryTargetClient
|
|
||||||
);
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user