tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Binding.ts (3663B)


      1 /**
      2 * @license
      3 * Copyright 2024 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 import {JSHandle} from '../api/JSHandle.js';
      7 import {debugError} from '../common/util.js';
      8 import {DisposableStack} from '../util/disposable.js';
      9 import {isErrorLike} from '../util/ErrorLike.js';
     10 
     11 import type {ExecutionContext} from './ExecutionContext.js';
     12 
     13 /**
     14 * @internal
     15 */
     16 export class Binding {
     17  #name: string;
     18  #fn: (...args: unknown[]) => unknown;
     19  #initSource: string;
     20  constructor(
     21    name: string,
     22    fn: (...args: unknown[]) => unknown,
     23    initSource: string,
     24  ) {
     25    this.#name = name;
     26    this.#fn = fn;
     27    this.#initSource = initSource;
     28  }
     29 
     30  get name(): string {
     31    return this.#name;
     32  }
     33 
     34  get initSource(): string {
     35    return this.#initSource;
     36  }
     37 
     38  /**
     39   * @param context - Context to run the binding in; the context should have
     40   * the binding added to it beforehand.
     41   * @param id - ID of the call. This should come from the CDP
     42   * `onBindingCalled` response.
     43   * @param args - Plain arguments from CDP.
     44   */
     45  async run(
     46    context: ExecutionContext,
     47    id: number,
     48    args: unknown[],
     49    isTrivial: boolean,
     50  ): Promise<void> {
     51    const stack = new DisposableStack();
     52    try {
     53      if (!isTrivial) {
     54        // Getting non-trivial arguments.
     55        using handles = await context.evaluateHandle(
     56          (name, seq) => {
     57            // @ts-expect-error Code is evaluated in a different context.
     58            return globalThis[name].args.get(seq);
     59          },
     60          this.#name,
     61          id,
     62        );
     63        const properties = await handles.getProperties();
     64        for (const [index, handle] of properties) {
     65          // This is not straight-forward since some arguments can stringify, but
     66          // aren't plain objects so add subtypes when the use-case arises.
     67          if (index in args) {
     68            switch (handle.remoteObject().subtype) {
     69              case 'node':
     70                args[+index] = handle;
     71                break;
     72              default:
     73                stack.use(handle);
     74            }
     75          } else {
     76            stack.use(handle);
     77          }
     78        }
     79      }
     80 
     81      await context.evaluate(
     82        (name, seq, result) => {
     83          // @ts-expect-error Code is evaluated in a different context.
     84          const callbacks = globalThis[name].callbacks;
     85          callbacks.get(seq).resolve(result);
     86          callbacks.delete(seq);
     87        },
     88        this.#name,
     89        id,
     90        await this.#fn(...args),
     91      );
     92 
     93      for (const arg of args) {
     94        if (arg instanceof JSHandle) {
     95          stack.use(arg);
     96        }
     97      }
     98    } catch (error) {
     99      if (isErrorLike(error)) {
    100        await context
    101          .evaluate(
    102            (name, seq, message, stack) => {
    103              const error = new Error(message);
    104              error.stack = stack;
    105              // @ts-expect-error Code is evaluated in a different context.
    106              const callbacks = globalThis[name].callbacks;
    107              callbacks.get(seq).reject(error);
    108              callbacks.delete(seq);
    109            },
    110            this.#name,
    111            id,
    112            error.message,
    113            error.stack,
    114          )
    115          .catch(debugError);
    116      } else {
    117        await context
    118          .evaluate(
    119            (name, seq, error) => {
    120              // @ts-expect-error Code is evaluated in a different context.
    121              const callbacks = globalThis[name].callbacks;
    122              callbacks.get(seq).reject(error);
    123              callbacks.delete(seq);
    124            },
    125            this.#name,
    126            id,
    127            error,
    128          )
    129          .catch(debugError);
    130      }
    131    }
    132  }
    133 }