tor-browser

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

ElementHandle.ts (4163B)


      1 /**
      2 * @license
      3 * Copyright 2023 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 
      7 import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
      8 
      9 import {
     10  bindIsolatedHandle,
     11  ElementHandle,
     12  type AutofillData,
     13 } from '../api/ElementHandle.js';
     14 import {UnsupportedOperation} from '../common/Errors.js';
     15 import type {AwaitableIterable} from '../common/types.js';
     16 import {environment} from '../environment.js';
     17 import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
     18 import {throwIfDisposed} from '../util/decorators.js';
     19 
     20 import type {BidiFrame} from './Frame.js';
     21 import {BidiJSHandle} from './JSHandle.js';
     22 import type {BidiFrameRealm} from './Realm.js';
     23 
     24 /**
     25 * @internal
     26 */
     27 export class BidiElementHandle<
     28  ElementType extends Node = Element,
     29 > extends ElementHandle<ElementType> {
     30  #backendNodeId?: number;
     31 
     32  static from<ElementType extends Node = Element>(
     33    value: Bidi.Script.RemoteValue,
     34    realm: BidiFrameRealm,
     35  ): BidiElementHandle<ElementType> {
     36    return new BidiElementHandle(value, realm);
     37  }
     38 
     39  declare handle: BidiJSHandle<ElementType>;
     40 
     41  constructor(value: Bidi.Script.RemoteValue, realm: BidiFrameRealm) {
     42    super(BidiJSHandle.from(value, realm));
     43  }
     44 
     45  override get realm(): BidiFrameRealm {
     46    // SAFETY: See the super call in the constructor.
     47    return this.handle.realm as BidiFrameRealm;
     48  }
     49 
     50  override get frame(): BidiFrame {
     51    return this.realm.environment;
     52  }
     53 
     54  remoteValue(): Bidi.Script.RemoteValue {
     55    return this.handle.remoteValue();
     56  }
     57 
     58  @throwIfDisposed()
     59  override async autofill(data: AutofillData): Promise<void> {
     60    const client = this.frame.client;
     61    const nodeInfo = await client.send('DOM.describeNode', {
     62      objectId: this.handle.id,
     63    });
     64    const fieldId = nodeInfo.node.backendNodeId;
     65    const frameId = this.frame._id;
     66    await client.send('Autofill.trigger', {
     67      fieldId,
     68      frameId,
     69      card: data.creditCard,
     70    });
     71  }
     72 
     73  override async contentFrame(
     74    this: BidiElementHandle<HTMLIFrameElement>,
     75  ): Promise<BidiFrame>;
     76  @throwIfDisposed()
     77  @bindIsolatedHandle
     78  override async contentFrame(): Promise<BidiFrame | null> {
     79    using handle = (await this.evaluateHandle(element => {
     80      if (
     81        element instanceof HTMLIFrameElement ||
     82        element instanceof HTMLFrameElement
     83      ) {
     84        return element.contentWindow;
     85      }
     86      return;
     87    })) as BidiJSHandle;
     88    const value = handle.remoteValue();
     89    if (value.type === 'window') {
     90      return (
     91        this.frame
     92          .page()
     93          .frames()
     94          .find(frame => {
     95            return frame._id === value.value.context;
     96          }) ?? null
     97      );
     98    }
     99    return null;
    100  }
    101 
    102  override async uploadFile(
    103    this: BidiElementHandle<HTMLInputElement>,
    104    ...files: string[]
    105  ): Promise<void> {
    106    // Locate all files and confirm that they exist.
    107    const path = environment.value.path;
    108    if (path) {
    109      files = files.map(file => {
    110        if (path.win32.isAbsolute(file) || path.posix.isAbsolute(file)) {
    111          return file;
    112        } else {
    113          return path.resolve(file);
    114        }
    115      });
    116    }
    117    await this.frame.setFiles(this, files);
    118  }
    119 
    120  override async *queryAXTree(
    121    this: BidiElementHandle<HTMLElement>,
    122    name?: string | undefined,
    123    role?: string | undefined,
    124  ): AwaitableIterable<ElementHandle<Node>> {
    125    const results = await this.frame.locateNodes(this, {
    126      type: 'accessibility',
    127      value: {
    128        role,
    129        name,
    130      },
    131    });
    132 
    133    return yield* AsyncIterableUtil.map(results, node => {
    134      // TODO: maybe change ownership since the default ownership is probably none.
    135      return Promise.resolve(BidiElementHandle.from(node, this.realm));
    136    });
    137  }
    138 
    139  override async backendNodeId(): Promise<number> {
    140    if (!this.frame.page().browser().cdpSupported) {
    141      throw new UnsupportedOperation();
    142    }
    143    if (this.#backendNodeId) {
    144      return this.#backendNodeId;
    145    }
    146    const {node} = await this.frame.client.send('DOM.describeNode', {
    147      objectId: this.handle.id,
    148    });
    149    this.#backendNodeId = node.backendNodeId;
    150    return this.#backendNodeId;
    151  }
    152 }