tor-browser

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

Frame.ts (36896B)


      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 {ClickOptions, ElementHandle} from '../api/ElementHandle.js';
     10 import type {HTTPResponse} from '../api/HTTPResponse.js';
     11 import type {
     12  Page,
     13  QueryOptions,
     14  WaitForSelectorOptions,
     15  WaitTimeoutOptions,
     16 } from '../api/Page.js';
     17 import type {Accessibility} from '../cdp/Accessibility.js';
     18 import type {DeviceRequestPrompt} from '../cdp/DeviceRequestPrompt.js';
     19 import type {PuppeteerLifeCycleEvent} from '../cdp/LifecycleWatcher.js';
     20 import {EventEmitter, type EventType} from '../common/EventEmitter.js';
     21 import {getQueryHandlerAndSelector} from '../common/GetQueryHandler.js';
     22 import {transposeIterableHandle} from '../common/HandleIterator.js';
     23 import type {
     24  Awaitable,
     25  EvaluateFunc,
     26  EvaluateFuncWith,
     27  HandleFor,
     28  NodeFor,
     29 } from '../common/types.js';
     30 import {withSourcePuppeteerURLIfNone} from '../common/util.js';
     31 import {environment} from '../environment.js';
     32 import {assert} from '../util/assert.js';
     33 import {throwIfDisposed} from '../util/decorators.js';
     34 
     35 import type {CDPSession} from './CDPSession.js';
     36 import type {KeyboardTypeOptions} from './Input.js';
     37 import {
     38  FunctionLocator,
     39  NodeLocator,
     40  type Locator,
     41 } from './locators/locators.js';
     42 import type {Realm} from './Realm.js';
     43 
     44 /**
     45 * @public
     46 */
     47 export interface WaitForOptions {
     48  /**
     49   * Maximum wait time in milliseconds. Pass 0 to disable the timeout.
     50   *
     51   * The default value can be changed by using the
     52   * {@link Page.setDefaultTimeout} or {@link Page.setDefaultNavigationTimeout}
     53   * methods.
     54   *
     55   * @defaultValue `30000`
     56   */
     57  timeout?: number;
     58  /**
     59   * When to consider waiting succeeds. Given an array of event strings, waiting
     60   * is considered to be successful after all events have been fired.
     61   *
     62   * @defaultValue `'load'`
     63   */
     64  waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
     65  /**
     66   * @internal
     67   */
     68  ignoreSameDocumentNavigation?: boolean;
     69  /**
     70   * A signal object that allows you to cancel the call.
     71   */
     72  signal?: AbortSignal;
     73 }
     74 
     75 /**
     76 * @public
     77 */
     78 export interface GoToOptions extends WaitForOptions {
     79  /**
     80   * If provided, it will take preference over the referer header value set by
     81   * {@link Page.setExtraHTTPHeaders | page.setExtraHTTPHeaders()}.
     82   */
     83  referer?: string;
     84  /**
     85   * If provided, it will take preference over the referer-policy header value
     86   * set by {@link Page.setExtraHTTPHeaders | page.setExtraHTTPHeaders()}.
     87   */
     88  referrerPolicy?: string;
     89 }
     90 
     91 /**
     92 * @public
     93 */
     94 export interface FrameWaitForFunctionOptions {
     95  /**
     96   * An interval at which the `pageFunction` is executed, defaults to `raf`. If
     97   * `polling` is a number, then it is treated as an interval in milliseconds at
     98   * which the function would be executed. If `polling` is a string, then it can
     99   * be one of the following values:
    100   *
    101   * - `raf` - to constantly execute `pageFunction` in `requestAnimationFrame`
    102   *   callback. This is the tightest polling mode which is suitable to observe
    103   *   styling changes.
    104   *
    105   * - `mutation` - to execute `pageFunction` on every DOM mutation.
    106   */
    107  polling?: 'raf' | 'mutation' | number;
    108  /**
    109   * Maximum time to wait in milliseconds. Defaults to `30000` (30 seconds).
    110   * Pass `0` to disable the timeout. Puppeteer's default timeout can be changed
    111   * using {@link Page.setDefaultTimeout}.
    112   */
    113  timeout?: number;
    114  /**
    115   * A signal object that allows you to cancel a waitForFunction call.
    116   */
    117  signal?: AbortSignal;
    118 }
    119 
    120 /**
    121 * @public
    122 */
    123 export interface FrameAddScriptTagOptions {
    124  /**
    125   * URL of the script to be added.
    126   */
    127  url?: string;
    128  /**
    129   * Path to a JavaScript file to be injected into the frame.
    130   *
    131   * @remarks
    132   * If `path` is a relative path, it is resolved relative to the current
    133   * working directory (`process.cwd()` in Node.js).
    134   */
    135  path?: string;
    136  /**
    137   * JavaScript to be injected into the frame.
    138   */
    139  content?: string;
    140  /**
    141   * Sets the `type` of the script. Use `module` in order to load an ES2015 module.
    142   */
    143  type?: string;
    144  /**
    145   * Sets the `id` of the script.
    146   */
    147  id?: string;
    148 }
    149 
    150 /**
    151 * @public
    152 */
    153 export interface FrameAddStyleTagOptions {
    154  /**
    155   * the URL of the CSS file to be added.
    156   */
    157  url?: string;
    158  /**
    159   * The path to a CSS file to be injected into the frame.
    160   * @remarks
    161   * If `path` is a relative path, it is resolved relative to the current
    162   * working directory (`process.cwd()` in Node.js).
    163   */
    164  path?: string;
    165  /**
    166   * Raw CSS content to be injected into the frame.
    167   */
    168  content?: string;
    169 }
    170 
    171 /**
    172 * @public
    173 */
    174 export interface FrameEvents extends Record<EventType, unknown> {
    175  /** @internal */
    176  [FrameEvent.FrameNavigated]: Protocol.Page.NavigationType;
    177  /** @internal */
    178  [FrameEvent.FrameSwapped]: undefined;
    179  /** @internal */
    180  [FrameEvent.LifecycleEvent]: undefined;
    181  /** @internal */
    182  [FrameEvent.FrameNavigatedWithinDocument]: undefined;
    183  /** @internal */
    184  [FrameEvent.FrameDetached]: Frame;
    185  /** @internal */
    186  [FrameEvent.FrameSwappedByActivation]: undefined;
    187 }
    188 
    189 /**
    190 * We use symbols to prevent external parties listening to these events.
    191 * They are internal to Puppeteer.
    192 *
    193 * @internal
    194 */
    195 // eslint-disable-next-line @typescript-eslint/no-namespace
    196 export namespace FrameEvent {
    197  export const FrameNavigated = Symbol('Frame.FrameNavigated');
    198  export const FrameSwapped = Symbol('Frame.FrameSwapped');
    199  export const LifecycleEvent = Symbol('Frame.LifecycleEvent');
    200  export const FrameNavigatedWithinDocument = Symbol(
    201    'Frame.FrameNavigatedWithinDocument',
    202  );
    203  export const FrameDetached = Symbol('Frame.FrameDetached');
    204  export const FrameSwappedByActivation = Symbol(
    205    'Frame.FrameSwappedByActivation',
    206  );
    207 }
    208 
    209 /**
    210 * @internal
    211 */
    212 export const throwIfDetached = throwIfDisposed<Frame>(frame => {
    213  return `Attempted to use detached Frame '${frame._id}'.`;
    214 });
    215 
    216 /**
    217 * Represents a DOM frame.
    218 *
    219 * To understand frames, you can think of frames as `<iframe>` elements. Just
    220 * like iframes, frames can be nested, and when JavaScript is executed in a
    221 * frame, the JavaScript does not affect frames inside the ambient frame the
    222 * JavaScript executes in.
    223 *
    224 * @example
    225 * At any point in time, {@link Page | pages} expose their current frame
    226 * tree via the {@link Page.mainFrame} and {@link Frame.childFrames} methods.
    227 *
    228 * @example
    229 * An example of dumping frame tree:
    230 *
    231 * ```ts
    232 * import puppeteer from 'puppeteer';
    233 *
    234 * (async () => {
    235 *   const browser = await puppeteer.launch();
    236 *   const page = await browser.newPage();
    237 *   await page.goto('https://www.google.com/chrome/browser/canary.html');
    238 *   dumpFrameTree(page.mainFrame(), '');
    239 *   await browser.close();
    240 *
    241 *   function dumpFrameTree(frame, indent) {
    242 *     console.log(indent + frame.url());
    243 *     for (const child of frame.childFrames()) {
    244 *       dumpFrameTree(child, indent + '  ');
    245 *     }
    246 *   }
    247 * })();
    248 * ```
    249 *
    250 * @example
    251 * An example of getting text from an iframe element:
    252 *
    253 * ```ts
    254 * const frames = page.frames();
    255 * let frame = null;
    256 * for (const currentFrame of frames) {
    257 *   const frameElement = await currentFrame.frameElement();
    258 *   const name = await frameElement.evaluate(el => el.getAttribute('name'));
    259 *   if (name === 'myframe') {
    260 *     frame = currentFrame;
    261 *     break;
    262 *   }
    263 * }
    264 * if (frame) {
    265 *   const text = await frame.$eval(
    266 *     '.selector',
    267 *     element => element.textContent,
    268 *   );
    269 *   console.log(text);
    270 * } else {
    271 *   console.error('Frame with name "myframe" not found.');
    272 * }
    273 * ```
    274 *
    275 * @remarks
    276 * Frame lifecycles are controlled by three events that are all dispatched on
    277 * the parent {@link Frame.page | page}:
    278 *
    279 * - {@link PageEvent.FrameAttached}
    280 * - {@link PageEvent.FrameNavigated}
    281 * - {@link PageEvent.FrameDetached}
    282 *
    283 * @public
    284 */
    285 export abstract class Frame extends EventEmitter<FrameEvents> {
    286  /**
    287   * @internal
    288   */
    289  _id!: string;
    290  /**
    291   * @internal
    292   */
    293  _parentId?: string;
    294 
    295  /**
    296   * @internal
    297   */
    298  _name?: string;
    299 
    300  /**
    301   * @internal
    302   */
    303  _hasStartedLoading = false;
    304 
    305  /**
    306   * @internal
    307   */
    308  constructor() {
    309    super();
    310  }
    311 
    312  /**
    313   * The page associated with the frame.
    314   */
    315  abstract page(): Page;
    316 
    317  /**
    318   * Navigates the frame or page to the given `url`.
    319   *
    320   * @remarks
    321   * Navigation to `about:blank` or navigation to the same URL with a different
    322   * hash will succeed and return `null`.
    323   *
    324   * :::warning
    325   *
    326   * Headless shell mode doesn't support navigation to a PDF document. See the
    327   * {@link https://crbug.com/761295 | upstream issue}.
    328   *
    329   * :::
    330   *
    331   * In headless shell, this method will not throw an error when any valid HTTP
    332   * status code is returned by the remote server, including 404 "Not Found" and
    333   * 500 "Internal Server Error". The status code for such responses can be
    334   * retrieved by calling {@link HTTPResponse.status}.
    335   *
    336   * @param url - URL to navigate the frame to. The URL should include scheme,
    337   * e.g. `https://`
    338   * @param options - Options to configure waiting behavior.
    339   * @returns A promise which resolves to the main resource response. In case of
    340   * multiple redirects, the navigation will resolve with the response of the
    341   * last redirect.
    342   * @throws If:
    343   *
    344   * - there's an SSL error (e.g. in case of self-signed certificates).
    345   *
    346   * - target URL is invalid.
    347   *
    348   * - the timeout is exceeded during navigation.
    349   *
    350   * - the remote server does not respond or is unreachable.
    351   *
    352   * - the main resource failed to load.
    353   */
    354  abstract goto(
    355    url: string,
    356    options?: GoToOptions,
    357  ): Promise<HTTPResponse | null>;
    358 
    359  /**
    360   * Waits for the frame to navigate. It is useful for when you run code which
    361   * will indirectly cause the frame to navigate.
    362   *
    363   * Usage of the
    364   * {@link https://developer.mozilla.org/en-US/docs/Web/API/History_API | History API}
    365   * to change the URL is considered a navigation.
    366   *
    367   * @example
    368   *
    369   * ```ts
    370   * const [response] = await Promise.all([
    371   *   // The navigation promise resolves after navigation has finished
    372   *   frame.waitForNavigation(),
    373   *   // Clicking the link will indirectly cause a navigation
    374   *   frame.click('a.my-link'),
    375   * ]);
    376   * ```
    377   *
    378   * @param options - Options to configure waiting behavior.
    379   * @returns A promise which resolves to the main resource response.
    380   */
    381  abstract waitForNavigation(
    382    options?: WaitForOptions,
    383  ): Promise<HTTPResponse | null>;
    384 
    385  /**
    386   * @internal
    387   */
    388  abstract get client(): CDPSession;
    389 
    390  /**
    391   * @internal
    392   */
    393  abstract get accessibility(): Accessibility;
    394 
    395  /**
    396   * @internal
    397   */
    398  abstract mainRealm(): Realm;
    399 
    400  /**
    401   * @internal
    402   */
    403  abstract isolatedRealm(): Realm;
    404 
    405  #_document: Promise<ElementHandle<Document>> | undefined;
    406 
    407  /**
    408   * @internal
    409   */
    410  #document(): Promise<ElementHandle<Document>> {
    411    if (!this.#_document) {
    412      this.#_document = this.mainRealm().evaluateHandle(() => {
    413        return document;
    414      });
    415    }
    416    return this.#_document;
    417  }
    418 
    419  /**
    420   * Used to clear the document handle that has been destroyed.
    421   *
    422   * @internal
    423   */
    424  clearDocumentHandle(): void {
    425    this.#_document = undefined;
    426  }
    427 
    428  /**
    429   * @returns The frame element associated with this frame (if any).
    430   */
    431  @throwIfDetached
    432  async frameElement(): Promise<HandleFor<HTMLIFrameElement> | null> {
    433    const parentFrame = this.parentFrame();
    434    if (!parentFrame) {
    435      return null;
    436    }
    437    using list = await parentFrame.isolatedRealm().evaluateHandle(() => {
    438      return document.querySelectorAll('iframe,frame');
    439    });
    440    for await (using iframe of transposeIterableHandle(list)) {
    441      const frame = await iframe.contentFrame();
    442      if (frame?._id === this._id) {
    443        return (await parentFrame
    444          .mainRealm()
    445          .adoptHandle(iframe)) as HandleFor<HTMLIFrameElement>;
    446      }
    447    }
    448    return null;
    449  }
    450 
    451  /**
    452   * Behaves identically to {@link Page.evaluateHandle} except it's run within
    453   * the context of this frame.
    454   *
    455   * See {@link Page.evaluateHandle} for details.
    456   */
    457  @throwIfDetached
    458  async evaluateHandle<
    459    Params extends unknown[],
    460    Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
    461  >(
    462    pageFunction: Func | string,
    463    ...args: Params
    464  ): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
    465    pageFunction = withSourcePuppeteerURLIfNone(
    466      this.evaluateHandle.name,
    467      pageFunction,
    468    );
    469    return await this.mainRealm().evaluateHandle(pageFunction, ...args);
    470  }
    471 
    472  /**
    473   * Behaves identically to {@link Page.evaluate} except it's run within
    474   * the context of this frame.
    475   *
    476   * See {@link Page.evaluate} for details.
    477   */
    478  @throwIfDetached
    479  async evaluate<
    480    Params extends unknown[],
    481    Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
    482  >(
    483    pageFunction: Func | string,
    484    ...args: Params
    485  ): Promise<Awaited<ReturnType<Func>>> {
    486    pageFunction = withSourcePuppeteerURLIfNone(
    487      this.evaluate.name,
    488      pageFunction,
    489    );
    490    return await this.mainRealm().evaluate(pageFunction, ...args);
    491  }
    492 
    493  /**
    494   * Creates a locator for the provided selector. See {@link Locator} for
    495   * details and supported actions.
    496   *
    497   * @param selector -
    498   * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
    499   * to query the page for.
    500   * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
    501   * can be passed as-is and a
    502   * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
    503   * allows querying by
    504   * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
    505   * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
    506   * and
    507   * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
    508   * and
    509   * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
    510   * Alternatively, you can specify the selector type using a
    511   * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
    512   */
    513  locator<Selector extends string>(
    514    selector: Selector,
    515  ): Locator<NodeFor<Selector>>;
    516 
    517  /**
    518   * Creates a locator for the provided function. See {@link Locator} for
    519   * details and supported actions.
    520   */
    521  locator<Ret>(func: () => Awaitable<Ret>): Locator<Ret>;
    522 
    523  /**
    524   * @internal
    525   */
    526  @throwIfDetached
    527  locator<Selector extends string, Ret>(
    528    selectorOrFunc: Selector | (() => Awaitable<Ret>),
    529  ): Locator<NodeFor<Selector>> | Locator<Ret> {
    530    if (typeof selectorOrFunc === 'string') {
    531      return NodeLocator.create(this, selectorOrFunc);
    532    } else {
    533      return FunctionLocator.create(this, selectorOrFunc);
    534    }
    535  }
    536  /**
    537   * Queries the frame for an element matching the given selector.
    538   *
    539   * @param selector -
    540   * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
    541   * to query the page for.
    542   * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
    543   * can be passed as-is and a
    544   * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
    545   * allows querying by
    546   * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
    547   * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
    548   * and
    549   * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
    550   * and
    551   * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
    552   * Alternatively, you can specify the selector type using a
    553   * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
    554   *
    555   * @returns A {@link ElementHandle | element handle} to the first element
    556   * matching the given selector. Otherwise, `null`.
    557   */
    558  @throwIfDetached
    559  async $<Selector extends string>(
    560    selector: Selector,
    561  ): Promise<ElementHandle<NodeFor<Selector>> | null> {
    562    // eslint-disable-next-line rulesdir/use-using -- This is cached.
    563    const document = await this.#document();
    564    return await document.$(selector);
    565  }
    566 
    567  /**
    568   * Queries the frame for all elements matching the given selector.
    569   *
    570   * @param selector -
    571   * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
    572   * to query the page for.
    573   * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
    574   * can be passed as-is and a
    575   * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
    576   * allows querying by
    577   * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
    578   * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
    579   * and
    580   * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
    581   * and
    582   * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
    583   * Alternatively, you can specify the selector type using a
    584   * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
    585   *
    586   * @returns An array of {@link ElementHandle | element handles} that point to
    587   * elements matching the given selector.
    588   */
    589  @throwIfDetached
    590  async $$<Selector extends string>(
    591    selector: Selector,
    592    options?: QueryOptions,
    593  ): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
    594    // eslint-disable-next-line rulesdir/use-using -- This is cached.
    595    const document = await this.#document();
    596    return await document.$$(selector, options);
    597  }
    598 
    599  /**
    600   * Runs the given function on the first element matching the given selector in
    601   * the frame.
    602   *
    603   * If the given function returns a promise, then this method will wait till
    604   * the promise resolves.
    605   *
    606   * @example
    607   *
    608   * ```ts
    609   * const searchValue = await frame.$eval('#search', el => el.value);
    610   * ```
    611   *
    612   * @param selector -
    613   * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
    614   * to query the page for.
    615   * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
    616   * can be passed as-is and a
    617   * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
    618   * allows querying by
    619   * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
    620   * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
    621   * and
    622   * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
    623   * and
    624   * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
    625   * Alternatively, you can specify the selector type using a
    626   * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
    627   * @param pageFunction - The function to be evaluated in the frame's context.
    628   * The first element matching the selector will be passed to the function as
    629   * its first argument.
    630   * @param args - Additional arguments to pass to `pageFunction`.
    631   * @returns A promise to the result of the function.
    632   */
    633  @throwIfDetached
    634  async $eval<
    635    Selector extends string,
    636    Params extends unknown[],
    637    Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
    638      NodeFor<Selector>,
    639      Params
    640    >,
    641  >(
    642    selector: Selector,
    643    pageFunction: string | Func,
    644    ...args: Params
    645  ): Promise<Awaited<ReturnType<Func>>> {
    646    pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
    647    // eslint-disable-next-line rulesdir/use-using -- This is cached.
    648    const document = await this.#document();
    649    return await document.$eval(selector, pageFunction, ...args);
    650  }
    651 
    652  /**
    653   * Runs the given function on an array of elements matching the given selector
    654   * in the frame.
    655   *
    656   * If the given function returns a promise, then this method will wait till
    657   * the promise resolves.
    658   *
    659   * @example
    660   *
    661   * ```ts
    662   * const divsCounts = await frame.$$eval('div', divs => divs.length);
    663   * ```
    664   *
    665   * @param selector -
    666   * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
    667   * to query the page for.
    668   * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
    669   * can be passed as-is and a
    670   * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
    671   * allows querying by
    672   * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
    673   * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
    674   * and
    675   * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
    676   * and
    677   * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
    678   * Alternatively, you can specify the selector type using a
    679   * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
    680   * @param pageFunction - The function to be evaluated in the frame's context.
    681   * An array of elements matching the given selector will be passed to the
    682   * function as its first argument.
    683   * @param args - Additional arguments to pass to `pageFunction`.
    684   * @returns A promise to the result of the function.
    685   */
    686  @throwIfDetached
    687  async $$eval<
    688    Selector extends string,
    689    Params extends unknown[],
    690    Func extends EvaluateFuncWith<
    691      Array<NodeFor<Selector>>,
    692      Params
    693    > = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
    694  >(
    695    selector: Selector,
    696    pageFunction: string | Func,
    697    ...args: Params
    698  ): Promise<Awaited<ReturnType<Func>>> {
    699    pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
    700    // eslint-disable-next-line rulesdir/use-using -- This is cached.
    701    const document = await this.#document();
    702    return await document.$$eval(selector, pageFunction, ...args);
    703  }
    704 
    705  /**
    706   * Waits for an element matching the given selector to appear in the frame.
    707   *
    708   * This method works across navigations.
    709   *
    710   * @example
    711   *
    712   * ```ts
    713   * import puppeteer from 'puppeteer';
    714   *
    715   * (async () => {
    716   *   const browser = await puppeteer.launch();
    717   *   const page = await browser.newPage();
    718   *   let currentURL;
    719   *   page
    720   *     .mainFrame()
    721   *     .waitForSelector('img')
    722   *     .then(() => console.log('First URL with image: ' + currentURL));
    723   *
    724   *   for (currentURL of [
    725   *     'https://example.com',
    726   *     'https://google.com',
    727   *     'https://bbc.com',
    728   *   ]) {
    729   *     await page.goto(currentURL);
    730   *   }
    731   *   await browser.close();
    732   * })();
    733   * ```
    734   *
    735   * @param selector - The selector to query and wait for.
    736   * @param options - Options for customizing waiting behavior.
    737   * @returns An element matching the given selector.
    738   * @throws Throws if an element matching the given selector doesn't appear.
    739   */
    740  @throwIfDetached
    741  async waitForSelector<Selector extends string>(
    742    selector: Selector,
    743    options: WaitForSelectorOptions = {},
    744  ): Promise<ElementHandle<NodeFor<Selector>> | null> {
    745    const {updatedSelector, QueryHandler, polling} =
    746      getQueryHandlerAndSelector(selector);
    747    return (await QueryHandler.waitFor(this, updatedSelector, {
    748      polling,
    749      ...options,
    750    })) as ElementHandle<NodeFor<Selector>> | null;
    751  }
    752 
    753  /**
    754   * @example
    755   * The `waitForFunction` can be used to observe viewport size change:
    756   *
    757   * ```ts
    758   * import puppeteer from 'puppeteer';
    759   *
    760   * (async () => {
    761   * .  const browser = await puppeteer.launch();
    762   * .  const page = await browser.newPage();
    763   * .  const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
    764   * .  page.setViewport({width: 50, height: 50});
    765   * .  await watchDog;
    766   * .  await browser.close();
    767   * })();
    768   * ```
    769   *
    770   * To pass arguments from Node.js to the predicate of `page.waitForFunction` function:
    771   *
    772   * ```ts
    773   * const selector = '.foo';
    774   * await frame.waitForFunction(
    775   *   selector => !!document.querySelector(selector),
    776   *   {}, // empty options object
    777   *   selector,
    778   * );
    779   * ```
    780   *
    781   * @param pageFunction - the function to evaluate in the frame context.
    782   * @param options - options to configure the polling method and timeout.
    783   * @param args - arguments to pass to the `pageFunction`.
    784   * @returns the promise which resolve when the `pageFunction` returns a truthy value.
    785   */
    786  @throwIfDetached
    787  async waitForFunction<
    788    Params extends unknown[],
    789    Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
    790  >(
    791    pageFunction: Func | string,
    792    options: FrameWaitForFunctionOptions = {},
    793    ...args: Params
    794  ): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
    795    return await (this.mainRealm().waitForFunction(
    796      pageFunction,
    797      options,
    798      ...args,
    799    ) as Promise<HandleFor<Awaited<ReturnType<Func>>>>);
    800  }
    801  /**
    802   * The full HTML contents of the frame, including the DOCTYPE.
    803   */
    804  @throwIfDetached
    805  async content(): Promise<string> {
    806    return await this.evaluate(() => {
    807      let content = '';
    808      for (const node of document.childNodes) {
    809        switch (node) {
    810          case document.documentElement:
    811            content += document.documentElement.outerHTML;
    812            break;
    813          default:
    814            content += new XMLSerializer().serializeToString(node);
    815            break;
    816        }
    817      }
    818 
    819      return content;
    820    });
    821  }
    822 
    823  /**
    824   * Set the content of the frame.
    825   *
    826   * @param html - HTML markup to assign to the page.
    827   * @param options - Options to configure how long before timing out and at
    828   * what point to consider the content setting successful.
    829   */
    830  abstract setContent(html: string, options?: WaitForOptions): Promise<void>;
    831 
    832  /**
    833   * @internal
    834   */
    835  async setFrameContent(content: string): Promise<void> {
    836    return await this.evaluate(html => {
    837      document.open();
    838      document.write(html);
    839      document.close();
    840    }, content);
    841  }
    842 
    843  /**
    844   * The frame's `name` attribute as specified in the tag.
    845   *
    846   * @remarks
    847   * If the name is empty, it returns the `id` attribute instead.
    848   *
    849   * @remarks
    850   * This value is calculated once when the frame is created, and will not
    851   * update if the attribute is changed later.
    852   *
    853   * @deprecated Use
    854   *
    855   * ```ts
    856   * const element = await frame.frameElement();
    857   * const nameOrId = await element.evaluate(frame => frame.name ?? frame.id);
    858   * ```
    859   */
    860  name(): string {
    861    return this._name || '';
    862  }
    863 
    864  /**
    865   * The frame's URL.
    866   */
    867  abstract url(): string;
    868 
    869  /**
    870   * The parent frame, if any. Detached and main frames return `null`.
    871   */
    872  abstract parentFrame(): Frame | null;
    873 
    874  /**
    875   * An array of child frames.
    876   */
    877  abstract childFrames(): Frame[];
    878 
    879  /**
    880   * @returns `true` if the frame has detached. `false` otherwise.
    881   */
    882  abstract get detached(): boolean;
    883 
    884  /**
    885   * Is`true` if the frame has been detached. Otherwise, `false`.
    886   *
    887   * @deprecated Use the `detached` getter.
    888   */
    889  isDetached(): boolean {
    890    return this.detached;
    891  }
    892 
    893  /**
    894   * @internal
    895   */
    896  get disposed(): boolean {
    897    return this.detached;
    898  }
    899 
    900  /**
    901   * Adds a `<script>` tag into the page with the desired url or content.
    902   *
    903   * @param options - Options for the script.
    904   * @returns An {@link ElementHandle | element handle} to the injected
    905   * `<script>` element.
    906   */
    907  @throwIfDetached
    908  async addScriptTag(
    909    options: FrameAddScriptTagOptions,
    910  ): Promise<ElementHandle<HTMLScriptElement>> {
    911    let {content = '', type} = options;
    912    const {path} = options;
    913    if (+!!options.url + +!!path + +!!content !== 1) {
    914      throw new Error(
    915        'Exactly one of `url`, `path`, or `content` must be specified.',
    916      );
    917    }
    918 
    919    if (path) {
    920      content = await environment.value.fs.promises.readFile(path, 'utf8');
    921      content += `//# sourceURL=${path.replace(/\n/g, '')}`;
    922    }
    923 
    924    type = type ?? 'text/javascript';
    925 
    926    return await this.mainRealm().transferHandle(
    927      await this.isolatedRealm().evaluateHandle(
    928        async ({url, id, type, content}) => {
    929          return await new Promise<HTMLScriptElement>((resolve, reject) => {
    930            const script = document.createElement('script');
    931            script.type = type;
    932            script.text = content;
    933            script.addEventListener(
    934              'error',
    935              event => {
    936                reject(new Error(event.message ?? 'Could not load script'));
    937              },
    938              {once: true},
    939            );
    940            if (id) {
    941              script.id = id;
    942            }
    943            if (url) {
    944              script.src = url;
    945              script.addEventListener(
    946                'load',
    947                () => {
    948                  resolve(script);
    949                },
    950                {once: true},
    951              );
    952              document.head.appendChild(script);
    953            } else {
    954              document.head.appendChild(script);
    955              resolve(script);
    956            }
    957          });
    958        },
    959        {...options, type, content},
    960      ),
    961    );
    962  }
    963 
    964  /**
    965   * Adds a `HTMLStyleElement` into the frame with the desired URL
    966   *
    967   * @returns An {@link ElementHandle | element handle} to the loaded `<style>`
    968   * element.
    969   */
    970  async addStyleTag(
    971    options: Omit<FrameAddStyleTagOptions, 'url'>,
    972  ): Promise<ElementHandle<HTMLStyleElement>>;
    973 
    974  /**
    975   * Adds a `HTMLLinkElement` into the frame with the desired URL
    976   *
    977   * @returns An {@link ElementHandle | element handle} to the loaded `<link>`
    978   * element.
    979   */
    980  async addStyleTag(
    981    options: FrameAddStyleTagOptions,
    982  ): Promise<ElementHandle<HTMLLinkElement>>;
    983 
    984  /**
    985   * @internal
    986   */
    987  @throwIfDetached
    988  async addStyleTag(
    989    options: FrameAddStyleTagOptions,
    990  ): Promise<ElementHandle<HTMLStyleElement | HTMLLinkElement>> {
    991    let {content = ''} = options;
    992    const {path} = options;
    993    if (+!!options.url + +!!path + +!!content !== 1) {
    994      throw new Error(
    995        'Exactly one of `url`, `path`, or `content` must be specified.',
    996      );
    997    }
    998 
    999    if (path) {
   1000      content = await environment.value.fs.promises.readFile(path, 'utf8');
   1001      content += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
   1002      options.content = content;
   1003    }
   1004 
   1005    return await this.mainRealm().transferHandle(
   1006      await this.isolatedRealm().evaluateHandle(async ({url, content}) => {
   1007        return await new Promise<HTMLStyleElement | HTMLLinkElement>(
   1008          (resolve, reject) => {
   1009            let element: HTMLStyleElement | HTMLLinkElement;
   1010            if (!url) {
   1011              element = document.createElement('style');
   1012              element.appendChild(document.createTextNode(content!));
   1013            } else {
   1014              const link = document.createElement('link');
   1015              link.rel = 'stylesheet';
   1016              link.href = url;
   1017              element = link;
   1018            }
   1019            element.addEventListener(
   1020              'load',
   1021              () => {
   1022                resolve(element);
   1023              },
   1024              {once: true},
   1025            );
   1026            element.addEventListener(
   1027              'error',
   1028              event => {
   1029                reject(
   1030                  new Error(
   1031                    (event as ErrorEvent).message ?? 'Could not load style',
   1032                  ),
   1033                );
   1034              },
   1035              {once: true},
   1036            );
   1037            document.head.appendChild(element);
   1038            return element;
   1039          },
   1040        );
   1041      }, options),
   1042    );
   1043  }
   1044 
   1045  /**
   1046   * Clicks the first element found that matches `selector`.
   1047   *
   1048   * @remarks
   1049   * If `click()` triggers a navigation event and there's a separate
   1050   * `page.waitForNavigation()` promise to be resolved, you may end up with a
   1051   * race condition that yields unexpected results. The correct pattern for
   1052   * click and wait for navigation is the following:
   1053   *
   1054   * ```ts
   1055   * const [response] = await Promise.all([
   1056   *   page.waitForNavigation(waitOptions),
   1057   *   frame.click(selector, clickOptions),
   1058   * ]);
   1059   * ```
   1060   *
   1061   * @param selector - The selector to query for.
   1062   */
   1063  @throwIfDetached
   1064  async click(
   1065    selector: string,
   1066    options: Readonly<ClickOptions> = {},
   1067  ): Promise<void> {
   1068    using handle = await this.$(selector);
   1069    assert(handle, `No element found for selector: ${selector}`);
   1070    await handle.click(options);
   1071    await handle.dispose();
   1072  }
   1073 
   1074  /**
   1075   * Focuses the first element that matches the `selector`.
   1076   *
   1077   * @param selector - The selector to query for.
   1078   * @throws Throws if there's no element matching `selector`.
   1079   */
   1080  @throwIfDetached
   1081  async focus(selector: string): Promise<void> {
   1082    using handle = await this.$(selector);
   1083    assert(handle, `No element found for selector: ${selector}`);
   1084    await handle.focus();
   1085  }
   1086 
   1087  /**
   1088   * Hovers the pointer over the center of the first element that matches the
   1089   * `selector`.
   1090   *
   1091   * @param selector - The selector to query for.
   1092   * @throws Throws if there's no element matching `selector`.
   1093   */
   1094  @throwIfDetached
   1095  async hover(selector: string): Promise<void> {
   1096    using handle = await this.$(selector);
   1097    assert(handle, `No element found for selector: ${selector}`);
   1098    await handle.hover();
   1099  }
   1100 
   1101  /**
   1102   * Selects a set of value on the first `<select>` element that matches the
   1103   * `selector`.
   1104   *
   1105   * @example
   1106   *
   1107   * ```ts
   1108   * frame.select('select#colors', 'blue'); // single selection
   1109   * frame.select('select#colors', 'red', 'green', 'blue'); // multiple selections
   1110   * ```
   1111   *
   1112   * @param selector - The selector to query for.
   1113   * @param values - The array of values to select. If the `<select>` has the
   1114   * `multiple` attribute, all values are considered, otherwise only the first
   1115   * one is taken into account.
   1116   * @returns the list of values that were successfully selected.
   1117   * @throws Throws if there's no `<select>` matching `selector`.
   1118   */
   1119  @throwIfDetached
   1120  async select(selector: string, ...values: string[]): Promise<string[]> {
   1121    using handle = await this.$(selector);
   1122    assert(handle, `No element found for selector: ${selector}`);
   1123    return await handle.select(...values);
   1124  }
   1125 
   1126  /**
   1127   * Taps the first element that matches the `selector`.
   1128   *
   1129   * @param selector - The selector to query for.
   1130   * @throws Throws if there's no element matching `selector`.
   1131   */
   1132  @throwIfDetached
   1133  async tap(selector: string): Promise<void> {
   1134    using handle = await this.$(selector);
   1135    assert(handle, `No element found for selector: ${selector}`);
   1136    await handle.tap();
   1137  }
   1138 
   1139  /**
   1140   * Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character
   1141   * in the text.
   1142   *
   1143   * @remarks
   1144   * To press a special key, like `Control` or `ArrowDown`, use
   1145   * {@link Keyboard.press}.
   1146   *
   1147   * @example
   1148   *
   1149   * ```ts
   1150   * await frame.type('#mytextarea', 'Hello'); // Types instantly
   1151   * await frame.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
   1152   * ```
   1153   *
   1154   * @param selector - the selector for the element to type into. If there are
   1155   * multiple the first will be used.
   1156   * @param text - text to type into the element
   1157   * @param options - takes one option, `delay`, which sets the time to wait
   1158   * between key presses in milliseconds. Defaults to `0`.
   1159   */
   1160  @throwIfDetached
   1161  async type(
   1162    selector: string,
   1163    text: string,
   1164    options?: Readonly<KeyboardTypeOptions>,
   1165  ): Promise<void> {
   1166    using handle = await this.$(selector);
   1167    assert(handle, `No element found for selector: ${selector}`);
   1168    await handle.type(text, options);
   1169  }
   1170 
   1171  /**
   1172   * The frame's title.
   1173   */
   1174  @throwIfDetached
   1175  async title(): Promise<string> {
   1176    return await this.isolatedRealm().evaluate(() => {
   1177      return document.title;
   1178    });
   1179  }
   1180 
   1181  /**
   1182   * This method is typically coupled with an action that triggers a device
   1183   * request from an api such as WebBluetooth.
   1184   *
   1185   * :::caution
   1186   *
   1187   * This must be called before the device request is made. It will not return a
   1188   * currently active device prompt.
   1189   *
   1190   * :::
   1191   *
   1192   * @example
   1193   *
   1194   * ```ts
   1195   * const [devicePrompt] = Promise.all([
   1196   *   frame.waitForDevicePrompt(),
   1197   *   frame.click('#connect-bluetooth'),
   1198   * ]);
   1199   * await devicePrompt.select(
   1200   *   await devicePrompt.waitForDevice(({name}) => name.includes('My Device')),
   1201   * );
   1202   * ```
   1203   *
   1204   * @internal
   1205   */
   1206  abstract waitForDevicePrompt(
   1207    options?: WaitTimeoutOptions,
   1208  ): Promise<DeviceRequestPrompt>;
   1209 }