tor-browser

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

JSHandle.ts (5729B)


      1 /**
      2 * @license
      3 * Copyright 2023 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 
      7 import type Protocol from 'devtools-protocol';
      8 
      9 import type {EvaluateFuncWith, HandleFor, HandleOr} from '../common/types.js';
     10 import {debugError, withSourcePuppeteerURLIfNone} from '../common/util.js';
     11 import {moveable, throwIfDisposed} from '../util/decorators.js';
     12 import {disposeSymbol, asyncDisposeSymbol} from '../util/disposable.js';
     13 
     14 import type {ElementHandle} from './ElementHandle.js';
     15 import type {Realm} from './Realm.js';
     16 
     17 /**
     18 * Represents a reference to a JavaScript object. Instances can be created using
     19 * {@link Page.evaluateHandle}.
     20 *
     21 * Handles prevent the referenced JavaScript object from being garbage-collected
     22 * unless the handle is purposely {@link JSHandle.dispose | disposed}. JSHandles
     23 * are auto-disposed when their associated frame is navigated away or the parent
     24 * context gets destroyed.
     25 *
     26 * Handles can be used as arguments for any evaluation function such as
     27 * {@link Page.$eval}, {@link Page.evaluate}, and {@link Page.evaluateHandle}.
     28 * They are resolved to their referenced object.
     29 *
     30 * @example
     31 *
     32 * ```ts
     33 * const windowHandle = await page.evaluateHandle(() => window);
     34 * ```
     35 *
     36 * @public
     37 */
     38 @moveable
     39 export abstract class JSHandle<T = unknown> {
     40  declare move: () => this;
     41 
     42  /**
     43   * Used for nominally typing {@link JSHandle}.
     44   */
     45  declare _?: T;
     46 
     47  /**
     48   * @internal
     49   */
     50  constructor() {}
     51 
     52  /**
     53   * @internal
     54   */
     55  abstract get realm(): Realm;
     56 
     57  /**
     58   * @internal
     59   */
     60  abstract get disposed(): boolean;
     61 
     62  /**
     63   * Evaluates the given function with the current handle as its first argument.
     64   */
     65  async evaluate<
     66    Params extends unknown[],
     67    Func extends EvaluateFuncWith<T, Params> = EvaluateFuncWith<T, Params>,
     68  >(
     69    pageFunction: Func | string,
     70    ...args: Params
     71  ): Promise<Awaited<ReturnType<Func>>> {
     72    pageFunction = withSourcePuppeteerURLIfNone(
     73      this.evaluate.name,
     74      pageFunction,
     75    );
     76    return await this.realm.evaluate(pageFunction, this, ...args);
     77  }
     78 
     79  /**
     80   * Evaluates the given function with the current handle as its first argument.
     81   *
     82   */
     83  async evaluateHandle<
     84    Params extends unknown[],
     85    Func extends EvaluateFuncWith<T, Params> = EvaluateFuncWith<T, Params>,
     86  >(
     87    pageFunction: Func | string,
     88    ...args: Params
     89  ): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
     90    pageFunction = withSourcePuppeteerURLIfNone(
     91      this.evaluateHandle.name,
     92      pageFunction,
     93    );
     94    return await this.realm.evaluateHandle(pageFunction, this, ...args);
     95  }
     96 
     97  /**
     98   * Fetches a single property from the referenced object.
     99   */
    100  getProperty<K extends keyof T>(
    101    propertyName: HandleOr<K>,
    102  ): Promise<HandleFor<T[K]>>;
    103  getProperty(propertyName: string): Promise<JSHandle<unknown>>;
    104 
    105  /**
    106   * @internal
    107   */
    108  @throwIfDisposed()
    109  async getProperty<K extends keyof T>(
    110    propertyName: HandleOr<K>,
    111  ): Promise<HandleFor<T[K]>> {
    112    return await this.evaluateHandle((object, propertyName) => {
    113      return object[propertyName as K];
    114    }, propertyName);
    115  }
    116 
    117  /**
    118   * Gets a map of handles representing the properties of the current handle.
    119   *
    120   * @example
    121   *
    122   * ```ts
    123   * const listHandle = await page.evaluateHandle(() => document.body.children);
    124   * const properties = await listHandle.getProperties();
    125   * const children = [];
    126   * for (const property of properties.values()) {
    127   *   const element = property.asElement();
    128   *   if (element) {
    129   *     children.push(element);
    130   *   }
    131   * }
    132   * children; // holds elementHandles to all children of document.body
    133   * ```
    134   */
    135  @throwIfDisposed()
    136  async getProperties(): Promise<Map<string, JSHandle>> {
    137    const propertyNames = await this.evaluate(object => {
    138      const enumerableProperties = [];
    139      const descriptors = Object.getOwnPropertyDescriptors(object);
    140      for (const propertyName in descriptors) {
    141        if (descriptors[propertyName]?.enumerable) {
    142          enumerableProperties.push(propertyName);
    143        }
    144      }
    145      return enumerableProperties;
    146    });
    147    const map = new Map<string, JSHandle>();
    148    const results = await Promise.all(
    149      propertyNames.map(key => {
    150        return this.getProperty(key);
    151      }),
    152    );
    153    for (const [key, value] of Object.entries(propertyNames)) {
    154      using handle = results[key as any];
    155      if (handle) {
    156        map.set(value, handle.move());
    157      }
    158    }
    159    return map;
    160  }
    161 
    162  /**
    163   * A vanilla object representing the serializable portions of the
    164   * referenced object.
    165   * @throws Throws if the object cannot be serialized due to circularity.
    166   *
    167   * @remarks
    168   * If the object has a `toJSON` function, it **will not** be called.
    169   */
    170  abstract jsonValue(): Promise<T>;
    171 
    172  /**
    173   * Either `null` or the handle itself if the handle is an
    174   * instance of {@link ElementHandle}.
    175   */
    176  abstract asElement(): ElementHandle<Node> | null;
    177 
    178  /**
    179   * Releases the object referenced by the handle for garbage collection.
    180   */
    181  abstract dispose(): Promise<void>;
    182 
    183  /**
    184   * Returns a string representation of the JSHandle.
    185   *
    186   * @remarks
    187   * Useful during debugging.
    188   */
    189  abstract toString(): string;
    190 
    191  /**
    192   * @internal
    193   */
    194  abstract get id(): string | undefined;
    195 
    196  /**
    197   * Provides access to the
    198   * {@link https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject | Protocol.Runtime.RemoteObject}
    199   * backing this handle.
    200   */
    201  abstract remoteObject(): Protocol.Runtime.RemoteObject;
    202 
    203  /** @internal */
    204  [disposeSymbol](): void {
    205    return void this.dispose().catch(debugError);
    206  }
    207 
    208  /** @internal */
    209  [asyncDisposeSymbol](): Promise<void> {
    210    return this.dispose();
    211  }
    212 }