| /** |
| * @license |
| * Copyright 2024 Google Inc. |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| import {JSHandle} from '../api/JSHandle.js'; |
| import {debugError} from '../common/util.js'; |
| import {DisposableStack} from '../util/disposable.js'; |
| import {isErrorLike} from '../util/ErrorLike.js'; |
| |
| import type {ExecutionContext} from './ExecutionContext.js'; |
| |
| /** |
| * @internal |
| */ |
| export class Binding { |
| #name: string; |
| #fn: (...args: unknown[]) => unknown; |
| #initSource: string; |
| constructor( |
| name: string, |
| fn: (...args: unknown[]) => unknown, |
| initSource: string, |
| ) { |
| this.#name = name; |
| this.#fn = fn; |
| this.#initSource = initSource; |
| } |
| |
| get name(): string { |
| return this.#name; |
| } |
| |
| get initSource(): string { |
| return this.#initSource; |
| } |
| |
| /** |
| * @param context - Context to run the binding in; the context should have |
| * the binding added to it beforehand. |
| * @param id - ID of the call. This should come from the CDP |
| * `onBindingCalled` response. |
| * @param args - Plain arguments from CDP. |
| */ |
| async run( |
| context: ExecutionContext, |
| id: number, |
| args: unknown[], |
| isTrivial: boolean, |
| ): Promise<void> { |
| const stack = new DisposableStack(); |
| try { |
| if (!isTrivial) { |
| // Getting non-trivial arguments. |
| using handles = await context.evaluateHandle( |
| (name, seq) => { |
| // @ts-expect-error Code is evaluated in a different context. |
| return globalThis[name].args.get(seq); |
| }, |
| this.#name, |
| id, |
| ); |
| const properties = await handles.getProperties(); |
| for (const [index, handle] of properties) { |
| // This is not straight-forward since some arguments can stringify, but |
| // aren't plain objects so add subtypes when the use-case arises. |
| if (index in args) { |
| switch (handle.remoteObject().subtype) { |
| case 'node': |
| args[+index] = handle; |
| break; |
| default: |
| stack.use(handle); |
| } |
| } else { |
| stack.use(handle); |
| } |
| } |
| } |
| |
| await context.evaluate( |
| (name, seq, result) => { |
| // @ts-expect-error Code is evaluated in a different context. |
| const callbacks = globalThis[name].callbacks; |
| callbacks.get(seq).resolve(result); |
| callbacks.delete(seq); |
| }, |
| this.#name, |
| id, |
| await this.#fn(...args), |
| ); |
| |
| for (const arg of args) { |
| if (arg instanceof JSHandle) { |
| stack.use(arg); |
| } |
| } |
| } catch (error) { |
| if (isErrorLike(error)) { |
| await context |
| .evaluate( |
| (name, seq, message, stack) => { |
| const error = new Error(message); |
| error.stack = stack; |
| // @ts-expect-error Code is evaluated in a different context. |
| const callbacks = globalThis[name].callbacks; |
| callbacks.get(seq).reject(error); |
| callbacks.delete(seq); |
| }, |
| this.#name, |
| id, |
| error.message, |
| error.stack, |
| ) |
| .catch(debugError); |
| } else { |
| await context |
| .evaluate( |
| (name, seq, error) => { |
| // @ts-expect-error Code is evaluated in a different context. |
| const callbacks = globalThis[name].callbacks; |
| callbacks.get(seq).reject(error); |
| callbacks.delete(seq); |
| }, |
| this.#name, |
| id, |
| error, |
| ) |
| .catch(debugError); |
| } |
| } |
| } |
| } |