summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/src/common/JSHandle.ts
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/src/common/JSHandle.ts')
-rw-r--r--remote/test/puppeteer/src/common/JSHandle.ts332
1 files changed, 332 insertions, 0 deletions
diff --git a/remote/test/puppeteer/src/common/JSHandle.ts b/remote/test/puppeteer/src/common/JSHandle.ts
new file mode 100644
index 0000000000..55613eb610
--- /dev/null
+++ b/remote/test/puppeteer/src/common/JSHandle.ts
@@ -0,0 +1,332 @@
+/**
+ * Copyright 2019 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Protocol} from 'devtools-protocol';
+import {assert} from '../util/assert.js';
+import {CDPSession} from './Connection.js';
+import type {ElementHandle} from './ElementHandle.js';
+import {ExecutionContext} from './ExecutionContext.js';
+import {MouseButton} from './Input.js';
+import {EvaluateFunc, HandleFor, HandleOr} from './types.js';
+import {createJSHandle, releaseObject, valueFromRemoteObject} from './util.js';
+
+declare const __JSHandleSymbol: unique symbol;
+
+/**
+ * @public
+ */
+export interface BoxModel {
+ content: Point[];
+ padding: Point[];
+ border: Point[];
+ margin: Point[];
+ width: number;
+ height: number;
+}
+
+/**
+ * @public
+ */
+export interface BoundingBox extends Point {
+ /**
+ * the width of the element in pixels.
+ */
+ width: number;
+ /**
+ * the height of the element in pixels.
+ */
+ height: number;
+}
+
+/**
+ * Represents a reference to a JavaScript object. Instances can be created using
+ * {@link Page.evaluateHandle}.
+ *
+ * Handles prevent the referenced JavaScript object from being garbage-collected
+ * unless the handle is purposely {@link JSHandle.dispose | disposed}. JSHandles
+ * are auto-disposed when their associated frame is navigated away or the parent
+ * context gets destroyed.
+ *
+ * Handles can be used as arguments for any evaluation function such as
+ * {@link Page.$eval}, {@link Page.evaluate}, and {@link Page.evaluateHandle}.
+ * They are resolved to their referenced object.
+ *
+ * @example
+ *
+ * ```ts
+ * const windowHandle = await page.evaluateHandle(() => window);
+ * ```
+ *
+ * @public
+ */
+export class JSHandle<T = unknown> {
+ /**
+ * Used for nominally typing {@link JSHandle}.
+ */
+ [__JSHandleSymbol]?: T;
+
+ #disposed = false;
+ #context: ExecutionContext;
+ #remoteObject: Protocol.Runtime.RemoteObject;
+
+ /**
+ * @internal
+ */
+ get client(): CDPSession {
+ return this.#context._client;
+ }
+
+ /**
+ * @internal
+ */
+ get disposed(): boolean {
+ return this.#disposed;
+ }
+
+ /**
+ * @internal
+ */
+ constructor(
+ context: ExecutionContext,
+ remoteObject: Protocol.Runtime.RemoteObject
+ ) {
+ this.#context = context;
+ this.#remoteObject = remoteObject;
+ }
+
+ /**
+ * @internal
+ */
+ executionContext(): ExecutionContext {
+ return this.#context;
+ }
+
+ /**
+ * Evaluates the given function with the current handle as its first argument.
+ *
+ * @see {@link ExecutionContext.evaluate} for more details.
+ */
+ async evaluate<
+ Params extends unknown[],
+ Func extends EvaluateFunc<[this, ...Params]> = EvaluateFunc<
+ [this, ...Params]
+ >
+ >(
+ pageFunction: Func | string,
+ ...args: Params
+ ): // @ts-expect-error Circularity here is okay because we only need the return
+ // type which doesn't use `this`.
+ Promise<Awaited<ReturnType<Func>>> {
+ return await this.executionContext().evaluate(pageFunction, this, ...args);
+ }
+
+ /**
+ * Evaluates the given function with the current handle as its first argument.
+ *
+ * @see {@link ExecutionContext.evaluateHandle} for more details.
+ */
+ async evaluateHandle<
+ Params extends unknown[],
+ Func extends EvaluateFunc<[this, ...Params]> = EvaluateFunc<
+ [this, ...Params]
+ >
+ >(
+ pageFunction: Func | string,
+ ...args: Params
+ ): // @ts-expect-error Circularity here is okay because we only need the return
+ // type which doesn't use `this`.
+ Promise<HandleFor<Awaited<ReturnType<Func>>>> {
+ return await this.executionContext().evaluateHandle(
+ pageFunction,
+ this,
+ ...args
+ );
+ }
+
+ /**
+ * Fetches a single property from the referenced object.
+ */
+ async getProperty<K extends keyof T>(
+ propertyName: HandleOr<K>
+ ): Promise<HandleFor<T[K]>>;
+ async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
+ async getProperty<K extends keyof T>(
+ propertyName: HandleOr<K>
+ ): Promise<HandleFor<T[K]>> {
+ return this.evaluateHandle((object, propertyName) => {
+ return object[propertyName];
+ }, propertyName);
+ }
+
+ /**
+ * Gets a map of handles representing the properties of the current handle.
+ *
+ * @example
+ *
+ * ```ts
+ * const listHandle = await page.evaluateHandle(() => document.body.children);
+ * const properties = await listHandle.getProperties();
+ * const children = [];
+ * for (const property of properties.values()) {
+ * const element = property.asElement();
+ * if (element) {
+ * children.push(element);
+ * }
+ * }
+ * children; // holds elementHandles to all children of document.body
+ * ```
+ */
+ async getProperties(): Promise<Map<string, JSHandle>> {
+ assert(this.#remoteObject.objectId);
+ // We use Runtime.getProperties rather than iterative building because the
+ // iterative approach might create a distorted snapshot.
+ const response = await this.client.send('Runtime.getProperties', {
+ objectId: this.#remoteObject.objectId,
+ ownProperties: true,
+ });
+ const result = new Map<string, JSHandle>();
+ for (const property of response.result) {
+ if (!property.enumerable || !property.value) {
+ continue;
+ }
+ result.set(property.name, createJSHandle(this.#context, property.value));
+ }
+ return result;
+ }
+
+ /**
+ * @returns A vanilla object representing the serializable portions of the
+ * referenced object.
+ * @throws Throws if the object cannot be serialized due to circularity.
+ *
+ * @remarks
+ * If the object has a `toJSON` function, it **will not** be called.
+ */
+ async jsonValue(): Promise<T> {
+ if (!this.#remoteObject.objectId) {
+ return valueFromRemoteObject(this.#remoteObject);
+ }
+ const value = await this.evaluate(object => {
+ return object;
+ });
+ if (value === undefined) {
+ throw new Error('Could not serialize referenced object');
+ }
+ return value;
+ }
+
+ /**
+ * @returns Either `null` or the handle itself if the handle is an
+ * instance of {@link ElementHandle}.
+ */
+ asElement(): ElementHandle<Node> | null {
+ return null;
+ }
+
+ /**
+ * Releases the object referenced by the handle for garbage collection.
+ */
+ async dispose(): Promise<void> {
+ if (this.#disposed) {
+ return;
+ }
+ this.#disposed = true;
+ await releaseObject(this.client, this.#remoteObject);
+ }
+
+ /**
+ * Returns a string representation of the JSHandle.
+ *
+ * @remarks
+ * Useful during debugging.
+ */
+ toString(): string {
+ if (!this.#remoteObject.objectId) {
+ return 'JSHandle:' + valueFromRemoteObject(this.#remoteObject);
+ }
+ const type = this.#remoteObject.subtype || this.#remoteObject.type;
+ return 'JSHandle@' + type;
+ }
+
+ /**
+ * Provides access to the
+ * [Protocol.Runtime.RemoteObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject)
+ * backing this handle.
+ */
+ remoteObject(): Protocol.Runtime.RemoteObject {
+ return this.#remoteObject;
+ }
+}
+
+/**
+ * @public
+ */
+export interface Offset {
+ /**
+ * x-offset for the clickable point relative to the top-left corner of the border box.
+ */
+ x: number;
+ /**
+ * y-offset for the clickable point relative to the top-left corner of the border box.
+ */
+ y: number;
+}
+
+/**
+ * @public
+ */
+export interface ClickOptions {
+ /**
+ * Time to wait between `mousedown` and `mouseup` in milliseconds.
+ *
+ * @defaultValue 0
+ */
+ delay?: number;
+ /**
+ * @defaultValue 'left'
+ */
+ button?: MouseButton;
+ /**
+ * @defaultValue 1
+ */
+ clickCount?: number;
+ /**
+ * Offset for the clickable point relative to the top-left corner of the border box.
+ */
+ offset?: Offset;
+}
+
+/**
+ * @public
+ */
+export interface PressOptions {
+ /**
+ * Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
+ */
+ delay?: number;
+ /**
+ * If specified, generates an input event with this text.
+ */
+ text?: string;
+}
+
+/**
+ * @public
+ */
+export interface Point {
+ x: number;
+ y: number;
+}