chore: refactor Frame (#10808)

This commit is contained in:
jrandolf 2023-08-30 13:09:27 +02:00 committed by GitHub
parent 3ffafc67d5
commit 4d3cb6ccf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 78 additions and 284 deletions

View File

@ -21,7 +21,7 @@ class Frame {
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
>(
selector: Selector,
pageFunction: Func | string,
pageFunction: string | Func,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
}
@ -32,7 +32,7 @@ class Frame {
| Parameter | Type | Description |
| ------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| selector | Selector | The selector to query for. |
| pageFunction | Func \| string | The function to be evaluated in the frame's context. An array of elements matching the given selector will be passed to the function as its first argument. |
| pageFunction | string \| Func | The function to be evaluated in the frame's context. An array of elements matching the given selector will be passed to the function as its first argument. |
| args | Params | Additional arguments to pass to <code>pageFunction</code>. |
**Returns:**

View File

@ -21,7 +21,7 @@ class Frame {
>,
>(
selector: Selector,
pageFunction: Func | string,
pageFunction: string | Func,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
}
@ -32,7 +32,7 @@ class Frame {
| Parameter | Type | Description |
| ------------ | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| selector | Selector | The selector to query for. |
| pageFunction | Func \| string | The function to be evaluated in the frame's context. The first element matching the selector will be passed to the function as its first argument. |
| pageFunction | string \| Func | The function to be evaluated in the frame's context. The first element matching the selector will be passed to the function as its first argument. |
| args | Params | Additional arguments to pass to <code>pageFunction</code>. |
**Returns:**

View File

@ -10,7 +10,7 @@ An array of child frames.
```typescript
class Frame {
childFrames(): Frame[];
abstract childFrames(): Frame[];
}
```

View File

@ -10,7 +10,7 @@ Navigates a frame to the given url.
```typescript
class Frame {
goto(
abstract goto(
url: string,
options?: {
referer?: string;

View File

@ -10,7 +10,7 @@ Is`true` if the frame has been detached. Otherwise, `false`.
```typescript
class Frame {
isDetached(): boolean;
abstract isDetached(): boolean;
}
```

View File

@ -11,7 +11,7 @@ To understand frames, you can think of frames as `<iframe>` elements. Just like
#### Signature:
```typescript
export declare class Frame extends EventEmitter
export declare abstract class Frame extends EventEmitter
```
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)

View File

@ -10,7 +10,7 @@ The page associated with the frame.
```typescript
class Frame {
page(): Page;
abstract page(): Page;
}
```

View File

@ -10,7 +10,7 @@ The parent frame, if any. Detached and main frames return `null`.
```typescript
class Frame {
parentFrame(): Frame | null;
abstract parentFrame(): Frame | null;
}
```

View File

@ -10,7 +10,7 @@ Set the content of the frame.
```typescript
class Frame {
setContent(
abstract setContent(
html: string,
options?: {
timeout?: number;

View File

@ -10,7 +10,7 @@ The frame's URL.
```typescript
class Frame {
url(): string;
abstract url(): string;
}
```

View File

@ -12,7 +12,7 @@ Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/Hist
```typescript
class Frame {
waitForNavigation(options?: {
abstract waitForNavigation(options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<HTTPResponse | null>;

View File

@ -36,7 +36,10 @@ import {
HandleFor,
NodeFor,
} from '../common/types.js';
import {importFSPromises} from '../common/util.js';
import {
importFSPromises,
withSourcePuppeteerURLIfNone,
} from '../common/util.js';
import {KeyboardTypeOptions} from './Input.js';
import {FunctionLocator, Locator, NodeLocator} from './locators/locators.js';
@ -175,7 +178,7 @@ export interface FrameAddStyleTagOptions {
*
* @public
*/
export class Frame extends EventEmitter {
export abstract class Frame extends EventEmitter {
/**
* @internal
*/
@ -210,9 +213,7 @@ export class Frame extends EventEmitter {
/**
* The page associated with the frame.
*/
page(): Page {
throw new Error('Not implemented');
}
abstract page(): Page;
/**
* Is `true` if the frame is an out-of-process (OOP) frame. Otherwise,
@ -259,7 +260,7 @@ export class Frame extends EventEmitter {
* Server Error". The status code for such responses can be retrieved by
* calling {@link HTTPResponse.status}.
*/
async goto(
abstract goto(
url: string,
options?: {
referer?: string;
@ -268,9 +269,6 @@ export class Frame extends EventEmitter {
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}
): Promise<HTTPResponse | null>;
async goto(): Promise<HTTPResponse | null> {
throw new Error('Not implemented');
}
/**
* Waits for the frame to navigate. It is useful for when you run code which
@ -295,20 +293,15 @@ export class Frame extends EventEmitter {
* finished.
* @returns a promise that resolves when the frame navigates to a new URL.
*/
async waitForNavigation(options?: {
abstract waitForNavigation(options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<HTTPResponse | null>;
async waitForNavigation(): Promise<HTTPResponse | null> {
throw new Error('Not implemented');
}
/**
* @internal
*/
_client(): CDPSession {
throw new Error('Not implemented');
}
abstract _client(): CDPSession;
/**
* @internal
@ -320,16 +313,12 @@ export class Frame extends EventEmitter {
/**
* @internal
*/
mainRealm(): Realm {
throw new Error('Not implemented');
}
abstract mainRealm(): Realm;
/**
* @internal
*/
isolatedRealm(): Realm {
throw new Error('Not implemented');
}
abstract isolatedRealm(): Realm;
/**
* @internal
@ -363,12 +352,12 @@ export class Frame extends EventEmitter {
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
>(): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
throw new Error('Not implemented');
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
pageFunction = withSourcePuppeteerURLIfNone(
this.evaluateHandle.name,
pageFunction
);
return this.mainRealm().evaluateHandle(pageFunction, ...args);
}
/**
@ -383,12 +372,12 @@ export class Frame extends EventEmitter {
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
>(): Promise<Awaited<ReturnType<Func>>> {
throw new Error('Not implemented');
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(
this.evaluate.name,
pageFunction
);
return this.mainRealm().evaluate(pageFunction, ...args);
}
/**
@ -430,11 +419,8 @@ export class Frame extends EventEmitter {
*/
async $<Selector extends string>(
selector: Selector
): Promise<ElementHandle<NodeFor<Selector>> | null>;
async $<Selector extends string>(): Promise<ElementHandle<
NodeFor<Selector>
> | null> {
throw new Error('Not implemented');
): Promise<ElementHandle<NodeFor<Selector>> | null> {
return this.mainRealm().$(selector);
}
/**
@ -446,11 +432,8 @@ export class Frame extends EventEmitter {
*/
async $$<Selector extends string>(
selector: Selector
): Promise<Array<ElementHandle<NodeFor<Selector>>>>;
async $$<Selector extends string>(): Promise<
Array<ElementHandle<NodeFor<Selector>>>
> {
throw new Error('Not implemented');
): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
return this.mainRealm().$$(selector);
}
/**
@ -473,7 +456,7 @@ export class Frame extends EventEmitter {
* @param args - Additional arguments to pass to `pageFunction`.
* @returns A promise to the result of the function.
*/
async $eval<
$eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
@ -482,18 +465,11 @@ export class Frame extends EventEmitter {
>,
>(
selector: Selector,
pageFunction: Func | string,
pageFunction: string | Func,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
async $eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
NodeFor<Selector>,
Params
>,
>(): Promise<Awaited<ReturnType<Func>>> {
throw new Error('Not implemented');
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
return this.mainRealm().$eval(selector, pageFunction, ...args);
}
/**
@ -516,7 +492,7 @@ export class Frame extends EventEmitter {
* @param args - Additional arguments to pass to `pageFunction`.
* @returns A promise to the result of the function.
*/
async $$eval<
$$eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<
@ -525,18 +501,11 @@ export class Frame extends EventEmitter {
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
>(
selector: Selector,
pageFunction: Func | string,
pageFunction: string | Func,
...args: Params
): Promise<Awaited<ReturnType<Func>>>;
async $$eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<
Array<NodeFor<Selector>>,
Params
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
>(): Promise<Awaited<ReturnType<Func>>> {
throw new Error('Not implemented');
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
return this.mainRealm().$$eval(selector, pageFunction, ...args);
}
/**
@ -549,9 +518,8 @@ export class Frame extends EventEmitter {
* automatically.
* @param expression - the XPath expression to evaluate.
*/
async $x(expression: string): Promise<Array<ElementHandle<Node>>>;
async $x(): Promise<Array<ElementHandle<Node>>> {
throw new Error('Not implemented');
$x(expression: string): Promise<Array<ElementHandle<Node>>> {
return this.mainRealm().$x(expression);
}
/**
@ -684,8 +652,8 @@ export class Frame extends EventEmitter {
/**
* The full HTML contents of the frame, including the DOCTYPE.
*/
async content(): Promise<string> {
throw new Error('Not implemented');
content(): Promise<string> {
return this.isolatedRealm().content();
}
/**
@ -695,16 +663,13 @@ export class Frame extends EventEmitter {
* @param options - Options to configure how long before timing out and at
* what point to consider the content setting successful.
*/
async setContent(
abstract setContent(
html: string,
options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}
): Promise<void>;
async setContent(): Promise<void> {
throw new Error('Not implemented');
}
/**
* The frame's `name` attribute as specified in the tag.
@ -723,30 +688,22 @@ export class Frame extends EventEmitter {
/**
* The frame's URL.
*/
url(): string {
throw new Error('Not implemented');
}
abstract url(): string;
/**
* The parent frame, if any. Detached and main frames return `null`.
*/
parentFrame(): Frame | null {
throw new Error('Not implemented');
}
abstract parentFrame(): Frame | null;
/**
* An array of child frames.
*/
childFrames(): Frame[] {
throw new Error('Not implemented');
}
abstract childFrames(): Frame[];
/**
* Is`true` if the frame has been detached. Otherwise, `false`.
*/
isDetached(): boolean {
throw new Error('Not implemented');
}
abstract isDetached(): boolean;
/**
* Adds a `<script>` tag into the page with the desired url or content.
@ -1026,8 +983,8 @@ export class Frame extends EventEmitter {
/**
* The frame's title.
*/
async title(): Promise<string> {
throw new Error('Not implemented');
title(): Promise<string> {
return this.isolatedRealm().title();
}
/**

View File

@ -130,6 +130,12 @@ export abstract class Realm implements Disposable {
return await this.evaluate(getPageContent);
}
async title(): Promise<string> {
return this.evaluate(() => {
return document.title;
});
}
waitForFunction<
Params extends unknown[],
Func extends EvaluateFunc<InnerLazyParams<Params>> = EvaluateFunc<

View File

@ -34,8 +34,7 @@ import {FrameManager} from './FrameManager.js';
import {IsolatedWorld} from './IsolatedWorld.js';
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from './types.js';
import {withSourcePuppeteerURLIfNone} from './util.js';
import {NodeFor} from './types.js';
/**
* We use symbols to prevent external parties listening to these events.
@ -255,34 +254,6 @@ export class Frame extends BaseFrame {
return this.worlds[PUPPETEER_WORLD];
}
override async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
pageFunction = withSourcePuppeteerURLIfNone(
this.evaluateHandle.name,
pageFunction
);
return this.mainRealm().evaluateHandle(pageFunction, ...args);
}
override async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(
this.evaluate.name,
pageFunction
);
return this.mainRealm().evaluate(pageFunction, ...args);
}
override async $<Selector extends string>(
selector: Selector
): Promise<ElementHandle<NodeFor<Selector>> | null> {
@ -295,46 +266,6 @@ export class Frame extends BaseFrame {
return this.mainRealm().$$(selector);
}
override async $eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
NodeFor<Selector>,
Params
>,
>(
selector: Selector,
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
return this.mainRealm().$eval(selector, pageFunction, ...args);
}
override async $$eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<
Array<NodeFor<Selector>>,
Params
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
>(
selector: Selector,
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
return this.mainRealm().$$eval(selector, pageFunction, ...args);
}
override async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
return this.mainRealm().$x(expression);
}
override async content(): Promise<string> {
return this.isolatedRealm().content();
}
override async setContent(
html: string,
options: {
@ -345,10 +276,6 @@ export class Frame extends BaseFrame {
return this.isolatedRealm().setContent(html, options);
}
override name(): string {
return this._name || '';
}
override url(): string {
return this.#url;
}
@ -365,23 +292,19 @@ export class Frame extends BaseFrame {
return this.#detached;
}
override async title(): Promise<string> {
return this.isolatedRealm().title();
}
_deviceRequestPromptManager(): DeviceRequestPromptManager {
#deviceRequestPromptManager(): DeviceRequestPromptManager {
if (this.isOOPFrame()) {
return this._frameManager._deviceRequestPromptManager(this.#client);
}
const parentFrame = this.parentFrame();
assert(parentFrame !== null);
return parentFrame._deviceRequestPromptManager();
return parentFrame.#deviceRequestPromptManager();
}
override waitForDevicePrompt(
options: WaitTimeoutOptions = {}
): Promise<DeviceRequestPrompt> {
return this._deviceRequestPromptManager().waitForDevicePrompt(options);
return this.#deviceRequestPromptManager().waitForDevicePrompt(options);
}
_navigated(framePayload: Protocol.Page.Frame): void {
@ -410,7 +333,7 @@ export class Frame extends BaseFrame {
this._hasStartedLoading = true;
}
_detach(): void {
[Symbol.dispose](): void {
this.#detached = true;
this.worlds[MAIN_WORLD][Symbol.dispose]();
this.worlds[PUPPETEER_WORLD][Symbol.dispose]();

View File

@ -551,7 +551,7 @@ export class FrameManager extends EventEmitter {
for (const child of frame.childFrames()) {
this.#removeFramesRecursively(child);
}
frame._detach();
frame[Symbol.dispose]();
this._frameTree.removeFrame(frame);
this.emit(FrameManagerEmittedEvents.FrameDetached, frame);
frame.emit(FrameEmittedEvents.FrameDetached, frame);

View File

@ -293,12 +293,6 @@ export class IsolatedWorld extends Realm {
}
};
async title(): Promise<string> {
return this.evaluate(() => {
return document.title;
});
}
async adoptBackendNode(
backendNodeId?: Protocol.DOM.BackendNodeId
): Promise<JSHandle<Node>> {

View File

@ -4,10 +4,10 @@ import ProtocolMapping from 'devtools-protocol/types/protocol-mapping.js';
import {WaitForOptions} from '../../api/Page.js';
import {assert} from '../../util/assert.js';
import {Deferred} from '../../util/Deferred.js';
import {CDPSession, Connection as CDPConnection} from '../Connection.js';
import {Connection as CDPConnection, CDPSession} from '../Connection.js';
import {ProtocolError, TargetCloseError, TimeoutError} from '../Errors.js';
import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js';
import {getPageContent, setPageContent, waitWithTimeout} from '../util.js';
import {setPageContent, waitWithTimeout} from '../util.js';
import {Connection} from './Connection.js';
import {Realm} from './Realm.js';
@ -277,10 +277,6 @@ export class BrowsingContext extends Realm {
]);
}
async content(): Promise<string> {
return await this.evaluate(getPageContent);
}
async sendCDPCommand<T extends keyof ProtocolMapping.Commands>(
method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType']

View File

@ -16,15 +16,13 @@
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {ElementHandle} from '../../api/ElementHandle.js';
import {Frame as BaseFrame} from '../../api/Frame.js';
import {Deferred} from '../../util/Deferred.js';
import {CDPSession} from '../Connection.js';
import {UTILITY_WORLD_NAME} from '../FrameManager.js';
import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js';
import {TimeoutSettings} from '../TimeoutSettings.js';
import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from '../types.js';
import {waitForEvent, withSourcePuppeteerURLIfNone} from '../util.js';
import {waitForEvent} from '../util.js';
import {
BrowsingContext,
@ -92,10 +90,6 @@ export class Frame extends BaseFrame {
return this.#page;
}
override name(): string {
return this._name || '';
}
override url(): string {
return this.#context.url;
}
@ -108,26 +102,6 @@ export class Frame extends BaseFrame {
return this.#page.childFrames(this.#context.id);
}
override async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
return this.#context.evaluateHandle(pageFunction, ...args);
}
override async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
return this.#context.evaluate(pageFunction, ...args);
}
override async goto(
url: string,
options?: {
@ -157,66 +131,10 @@ export class Frame extends BaseFrame {
});
}
override content(): Promise<string> {
return this.#context.content();
}
override title(): Promise<string> {
return this.#context.title();
}
context(): BrowsingContext {
return this.#context;
}
override $<Selector extends string>(
selector: Selector
): Promise<ElementHandle<NodeFor<Selector>> | null> {
return this.mainRealm().$(selector);
}
override $$<Selector extends string>(
selector: Selector
): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
return this.mainRealm().$$(selector);
}
override $eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
NodeFor<Selector>,
Params
>,
>(
selector: Selector,
pageFunction: string | Func,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
return this.mainRealm().$eval(selector, pageFunction, ...args);
}
override $$eval<
Selector extends string,
Params extends unknown[],
Func extends EvaluateFuncWith<
Array<NodeFor<Selector>>,
Params
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
>(
selector: Selector,
pageFunction: string | Func,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
return this.mainRealm().$$eval(selector, pageFunction, ...args);
}
override $x(expression: string): Promise<Array<ElementHandle<Node>>> {
return this.mainRealm().$x(expression);
}
override async waitForNavigation(
options: {
timeout?: number;
@ -275,7 +193,7 @@ export class Frame extends BaseFrame {
return this.#detached;
}
dispose(): void {
[Symbol.dispose](): void {
this.#detached = true;
this.#abortDeferred.reject(new Error('Frame detached'));
this.#context.dispose();

View File

@ -330,7 +330,7 @@ export class Page extends PageBase {
for (const child of frame.childFrames()) {
this.#removeFramesRecursively(child);
}
frame.dispose();
frame[Symbol.dispose]();
this.#networkManager.clearMapAfterFrameDispose(frame);
this.#frameTree.removeFrame(frame);
this.emit(PageEmittedEvents.FrameDetached, frame);