feat: implement typed events (#10889)
This commit is contained in:
parent
a25527a223
commit
9b6f1de8b9
@ -46,12 +46,12 @@ sidebar_label: API
|
||||
## Enumerations
|
||||
|
||||
| Enumeration | Description |
|
||||
| ------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| [BrowserContextEmittedEvents](./puppeteer.browsercontextemittedevents.md) | |
|
||||
| [BrowserEmittedEvents](./puppeteer.browseremittedevents.md) | All the events a [browser instance](./puppeteer.browser.md) may emit. |
|
||||
| --------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| [BrowserContextEvent](./puppeteer.browsercontextevent.md) | |
|
||||
| [BrowserEvent](./puppeteer.browserevent.md) | All the events a [browser instance](./puppeteer.browser.md) may emit. |
|
||||
| [InterceptResolutionAction](./puppeteer.interceptresolutionaction.md) | |
|
||||
| [LocatorEmittedEvents](./puppeteer.locatoremittedevents.md) | All the events that a locator instance may emit. |
|
||||
| [PageEmittedEvents](./puppeteer.pageemittedevents.md) | All the events that a page instance may emit. |
|
||||
| [LocatorEvent](./puppeteer.locatorevent.md) | All the events that a locator instance may emit. |
|
||||
| [PageEvent](./puppeteer.pageevent.md) | All the events that a page instance may emit. |
|
||||
| [TargetType](./puppeteer.targettype.md) | |
|
||||
|
||||
## Functions
|
||||
@ -66,14 +66,17 @@ sidebar_label: API
|
||||
## Interfaces
|
||||
|
||||
| Interface | Description |
|
||||
| --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [ActionOptions](./puppeteer.actionoptions.md) | |
|
||||
| [AutofillData](./puppeteer.autofilldata.md) | |
|
||||
| [BoundingBox](./puppeteer.boundingbox.md) | |
|
||||
| [BoxModel](./puppeteer.boxmodel.md) | |
|
||||
| [BrowserConnectOptions](./puppeteer.browserconnectoptions.md) | Generic browser options that can be passed when launching any browser or when connecting to an existing browser instance. |
|
||||
| [BrowserContextEvents](./puppeteer.browsercontextevents.md) | |
|
||||
| [BrowserContextOptions](./puppeteer.browsercontextoptions.md) | BrowserContext options. |
|
||||
| [BrowserEvents](./puppeteer.browserevents.md) | |
|
||||
| [BrowserLaunchArgumentOptions](./puppeteer.browserlaunchargumentoptions.md) | Launcher options that only apply to Chrome. |
|
||||
| [CDPSessionEvents](./puppeteer.cdpsessionevents.md) | |
|
||||
| [ClickOptions](./puppeteer.clickoptions.md) | |
|
||||
| [CommonEventEmitter](./puppeteer.commoneventemitter.md) | |
|
||||
| [Configuration](./puppeteer.configuration.md) | <p>Defines options to configure Puppeteer's behavior during installation and runtime.</p><p>See individual properties for more information.</p> |
|
||||
@ -88,6 +91,7 @@ sidebar_label: API
|
||||
| [Device](./puppeteer.device.md) | |
|
||||
| [FrameAddScriptTagOptions](./puppeteer.frameaddscripttagoptions.md) | |
|
||||
| [FrameAddStyleTagOptions](./puppeteer.frameaddstyletagoptions.md) | |
|
||||
| [FrameEvents](./puppeteer.frameevents.md) | |
|
||||
| [FrameWaitForFunctionOptions](./puppeteer.framewaitforfunctionoptions.md) | |
|
||||
| [GeolocationOptions](./puppeteer.geolocationoptions.md) | |
|
||||
| [InterceptResolutionState](./puppeteer.interceptresolutionstate.md) | |
|
||||
@ -97,7 +101,7 @@ sidebar_label: API
|
||||
| [KeyboardTypeOptions](./puppeteer.keyboardtypeoptions.md) | |
|
||||
| [KeyDownOptions](./puppeteer.keydownoptions.md) | |
|
||||
| [LaunchOptions](./puppeteer.launchoptions.md) | Generic launch options that can be passed when launching any browser. |
|
||||
| [LocatorEventObject](./puppeteer.locatoreventobject.md) | |
|
||||
| [LocatorEvents](./puppeteer.locatorevents.md) | |
|
||||
| [LocatorOptions](./puppeteer.locatoroptions.md) | |
|
||||
| [LocatorScrollOptions](./puppeteer.locatorscrolloptions.md) | |
|
||||
| [MediaFeature](./puppeteer.mediafeature.md) | |
|
||||
@ -110,7 +114,7 @@ sidebar_label: API
|
||||
| [NetworkConditions](./puppeteer.networkconditions.md) | |
|
||||
| [NewDocumentScriptEvaluation](./puppeteer.newdocumentscriptevaluation.md) | |
|
||||
| [Offset](./puppeteer.offset.md) | |
|
||||
| [PageEventObject](./puppeteer.pageeventobject.md) | <p>Denotes the objects received by callback functions for page events.</p><p>See [PageEmittedEvents](./puppeteer.pageemittedevents.md) for more detail on the events and when they are emitted.</p> |
|
||||
| [PageEvents](./puppeteer.pageevents.md) | <p>Denotes the objects received by callback functions for page events.</p><p>See [PageEvent](./puppeteer.pageevent.md) for more detail on the events and when they are emitted.</p> |
|
||||
| [PDFMargin](./puppeteer.pdfmargin.md) | |
|
||||
| [PDFOptions](./puppeteer.pdfoptions.md) | Valid options to configure PDF generation via [Page.pdf()](./puppeteer.page.pdf.md). |
|
||||
| [Point](./puppeteer.point.md) | |
|
||||
@ -155,13 +159,14 @@ sidebar_label: API
|
||||
| [Awaitable](./puppeteer.awaitable.md) | |
|
||||
| [AwaitableIterable](./puppeteer.awaitableiterable.md) | |
|
||||
| [AwaitedLocator](./puppeteer.awaitedlocator.md) | |
|
||||
| [CDPEvents](./puppeteer.cdpevents.md) | |
|
||||
| [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | |
|
||||
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
|
||||
| [ElementFor](./puppeteer.elementfor.md) | |
|
||||
| [ErrorCode](./puppeteer.errorcode.md) | |
|
||||
| [EvaluateFunc](./puppeteer.evaluatefunc.md) | |
|
||||
| [EvaluateFuncWith](./puppeteer.evaluatefuncwith.md) | |
|
||||
| [EventType](./puppeteer.eventtype.md) | |
|
||||
| [EventsWithWildcard](./puppeteer.eventswithwildcard.md) | |
|
||||
| [ExperimentsConfiguration](./puppeteer.experimentsconfiguration.md) | <p>Defines experiment options for Puppeteer.</p><p>See individual properties for more information.</p> |
|
||||
| [FlattenHandle](./puppeteer.flattenhandle.md) | |
|
||||
| [HandleFor](./puppeteer.handlefor.md) | |
|
||||
|
@ -9,16 +9,16 @@ A Browser is created when Puppeteer connects to a browser instance, either throu
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare class Browser extends EventEmitter implements AsyncDisposable, Disposable
|
||||
export declare class Browser extends EventEmitter<BrowserEvents> implements AsyncDisposable, Disposable
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[BrowserEvents](./puppeteer.browserevents.md)>
|
||||
|
||||
**Implements:** AsyncDisposable, Disposable
|
||||
|
||||
## Remarks
|
||||
|
||||
The Browser class extends from Puppeteer's [EventEmitter](./puppeteer.eventemitter.md) class and will emit various events which are documented in the [BrowserEmittedEvents](./puppeteer.browseremittedevents.md) enum.
|
||||
The Browser class extends from Puppeteer's [EventEmitter](./puppeteer.eventemitter.md) class and will emit various events which are documented in the [BrowserEvent](./puppeteer.browserevent.md) enum.
|
||||
|
||||
The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `Browser` class.
|
||||
|
||||
|
@ -9,14 +9,14 @@ BrowserContexts provide a way to operate multiple independent browser sessions.
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare class BrowserContext extends EventEmitter
|
||||
export declare class BrowserContext extends EventEmitter<BrowserContextEvents>
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[BrowserContextEvents](./puppeteer.browsercontextevents.md)>
|
||||
|
||||
## Remarks
|
||||
|
||||
The Browser class extends from Puppeteer's [EventEmitter](./puppeteer.eventemitter.md) class and will emit various events which are documented in the [BrowserContextEmittedEvents](./puppeteer.browsercontextemittedevents.md) enum.
|
||||
The Browser class extends from Puppeteer's [EventEmitter](./puppeteer.eventemitter.md) class and will emit various events which are documented in the [BrowserContextEvents](./puppeteer.browsercontextevents.md) enum.
|
||||
|
||||
If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser context.
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
---
|
||||
sidebar_label: BrowserContextEmittedEvents
|
||||
sidebar_label: BrowserContextEvent
|
||||
---
|
||||
|
||||
# BrowserContextEmittedEvents enum
|
||||
# BrowserContextEvent enum
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare const enum BrowserContextEmittedEvents
|
||||
export declare const enum BrowserContextEvent
|
||||
```
|
||||
|
||||
## Enumeration Members
|
21
docs/api/puppeteer.browsercontextevents.md
Normal file
21
docs/api/puppeteer.browsercontextevents.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
sidebar_label: BrowserContextEvents
|
||||
---
|
||||
|
||||
# BrowserContextEvents interface
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface BrowserContextEvents extends Record<EventType, unknown>
|
||||
```
|
||||
|
||||
**Extends:** Record<EventType, unknown>
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| --------------- | --------- | ------------------------------- | ----------- | ------- |
|
||||
| targetchanged | | [Target](./puppeteer.target.md) | | |
|
||||
| targetcreated | | [Target](./puppeteer.target.md) | | |
|
||||
| targetdestroyed | | [Target](./puppeteer.target.md) | | |
|
@ -1,15 +1,15 @@
|
||||
---
|
||||
sidebar_label: BrowserEmittedEvents
|
||||
sidebar_label: BrowserEvent
|
||||
---
|
||||
|
||||
# BrowserEmittedEvents enum
|
||||
# BrowserEvent enum
|
||||
|
||||
All the events a [browser instance](./puppeteer.browser.md) may emit.
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare const enum BrowserEmittedEvents
|
||||
export declare const enum BrowserEvent
|
||||
```
|
||||
|
||||
## Enumeration Members
|
22
docs/api/puppeteer.browserevents.md
Normal file
22
docs/api/puppeteer.browserevents.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
sidebar_label: BrowserEvents
|
||||
---
|
||||
|
||||
# BrowserEvents interface
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface BrowserEvents extends Record<EventType, unknown>
|
||||
```
|
||||
|
||||
**Extends:** Record<EventType, unknown>
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| --------------- | --------- | ------------------------------- | ----------- | ------- |
|
||||
| disconnected | | undefined | | |
|
||||
| targetchanged | | [Target](./puppeteer.target.md) | | |
|
||||
| targetcreated | | [Target](./puppeteer.target.md) | | |
|
||||
| targetdestroyed | | [Target](./puppeteer.target.md) | | |
|
13
docs/api/puppeteer.cdpevents.md
Normal file
13
docs/api/puppeteer.cdpevents.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
sidebar_label: CDPEvents
|
||||
---
|
||||
|
||||
# CDPEvents type
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export type CDPEvents = {
|
||||
[Property in keyof ProtocolMapping.Events]: ProtocolMapping.Events[Property][0];
|
||||
};
|
||||
```
|
@ -9,10 +9,10 @@ The `CDPSession` instances are used to talk raw Chrome Devtools Protocol.
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare class CDPSession extends EventEmitter
|
||||
export declare abstract class CDPSession extends EventEmitter<CDPSessionEvents>
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[CDPSessionEvents](./puppeteer.cdpsessionevents.md)>
|
||||
|
||||
## Remarks
|
||||
|
||||
|
13
docs/api/puppeteer.cdpsessionevents.md
Normal file
13
docs/api/puppeteer.cdpsessionevents.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
sidebar_label: CDPSessionEvents
|
||||
---
|
||||
|
||||
# CDPSessionEvents interface
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface CDPSessionEvents extends CDPEvents, Record<EventType, unknown>
|
||||
```
|
||||
|
||||
**Extends:** [CDPEvents](./puppeteer.cdpevents.md), Record<EventType, unknown>
|
@ -8,16 +8,19 @@ sidebar_label: CommonEventEmitter.addListener
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
addListener(event: EventType, handler: Handler): this;
|
||||
addListener<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md) | |
|
||||
| --------- | ------------------------------------------------------ | ----------- |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<Events\[Key\]> | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -8,16 +8,16 @@ sidebar_label: CommonEventEmitter.emit
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
emit(event: EventType, eventData?: unknown): boolean;
|
||||
emit<Key extends keyof Events>(type: Key, event: Events[Key]): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ------------ |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| eventData | unknown | _(Optional)_ |
|
||||
| --------- | ------------- | ----------- |
|
||||
| type | Key | |
|
||||
| event | Events\[Key\] | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -8,15 +8,15 @@ sidebar_label: CommonEventEmitter.listenerCount
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
listenerCount(event: string): number;
|
||||
listenerCount(event: keyof Events): number;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------ | ----------- |
|
||||
| event | string | |
|
||||
| --------- | ------------ | ----------- |
|
||||
| event | keyof Events | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -7,18 +7,18 @@ sidebar_label: CommonEventEmitter
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface CommonEventEmitter
|
||||
export interface CommonEventEmitter<Events extends Record<EventType, unknown>>
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Description |
|
||||
| ---------------------------------------------------------------------------------- | ----------- |
|
||||
| [addListener(event, handler)](./puppeteer.commoneventemitter.addlistener.md) | |
|
||||
| [emit(event, eventData)](./puppeteer.commoneventemitter.emit.md) | |
|
||||
| --------------------------------------------------------------------------------- | ----------- |
|
||||
| [addListener(type, handler)](./puppeteer.commoneventemitter.addlistener.md) | |
|
||||
| [emit(type, event)](./puppeteer.commoneventemitter.emit.md) | |
|
||||
| [listenerCount(event)](./puppeteer.commoneventemitter.listenercount.md) | |
|
||||
| [off(event, handler)](./puppeteer.commoneventemitter.off.md) | |
|
||||
| [on(event, handler)](./puppeteer.commoneventemitter.on.md) | |
|
||||
| [once(event, handler)](./puppeteer.commoneventemitter.once.md) | |
|
||||
| [off(type, handler)](./puppeteer.commoneventemitter.off.md) | |
|
||||
| [on(type, handler)](./puppeteer.commoneventemitter.on.md) | |
|
||||
| [once(type, handler)](./puppeteer.commoneventemitter.once.md) | |
|
||||
| [removeAllListeners(event)](./puppeteer.commoneventemitter.removealllisteners.md) | |
|
||||
| [removeListener(event, handler)](./puppeteer.commoneventemitter.removelistener.md) | |
|
||||
| [removeListener(type, handler)](./puppeteer.commoneventemitter.removelistener.md) | |
|
||||
|
@ -8,16 +8,19 @@ sidebar_label: CommonEventEmitter.off
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
off(event: EventType, handler: Handler): this;
|
||||
off<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler?: Handler<Events[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md) | |
|
||||
| --------- | ------------------------------------------------------ | ------------ |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<Events\[Key\]> | _(Optional)_ |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -8,16 +8,16 @@ sidebar_label: CommonEventEmitter.on
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
on(event: EventType, handler: Handler): this;
|
||||
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md) | |
|
||||
| --------- | ------------------------------------------------------ | ----------- |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<Events\[Key\]> | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -8,16 +8,19 @@ sidebar_label: CommonEventEmitter.once
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
once(event: EventType, handler: Handler): this;
|
||||
once<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md) | |
|
||||
| --------- | ------------------------------------------------------ | ----------- |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<Events\[Key\]> | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -8,15 +8,15 @@ sidebar_label: CommonEventEmitter.removeAllListeners
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
removeAllListeners(event?: EventType): this;
|
||||
removeAllListeners(event?: keyof Events): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ------------ |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | _(Optional)_ |
|
||||
| --------- | ------------ | ------------ |
|
||||
| event | keyof Events | _(Optional)_ |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -8,16 +8,19 @@ sidebar_label: CommonEventEmitter.removeListener
|
||||
|
||||
```typescript
|
||||
interface CommonEventEmitter {
|
||||
removeListener(event: EventType, handler: Handler): this;
|
||||
removeListener<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md) | |
|
||||
| --------- | ------------------------------------------------------ | ----------- |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<Events\[Key\]> | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -7,10 +7,10 @@ sidebar_label: Connection
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare class Connection extends EventEmitter
|
||||
export declare class Connection extends EventEmitter<CDPSessionEvents>
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[CDPSessionEvents](./puppeteer.cdpsessionevents.md)>
|
||||
|
||||
## Constructors
|
||||
|
||||
|
@ -14,16 +14,19 @@ Add an event listener.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
addListener(event: EventType, handler: Handler<any>): this;
|
||||
addListener<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<any> | |
|
||||
| --------- | --------------------------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<[EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>\[Key\]> | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -10,16 +10,19 @@ Emit an event and call any associated listeners.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
emit(event: EventType, eventData?: unknown): boolean;
|
||||
emit<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
event: EventsWithWildcard<Events>[Key]
|
||||
): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ------------------------------------------------------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | the event you'd like to emit |
|
||||
| eventData | unknown | _(Optional)_ any data you'd like to emit with the event |
|
||||
| --------- | ---------------------------------------------------------------------------- | ---------------------------- |
|
||||
| type | Key | the event you'd like to emit |
|
||||
| event | [EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>\[Key\] | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -10,15 +10,15 @@ Gets the number of listeners for a given event.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
listenerCount(event: EventType): number;
|
||||
listenerCount(type: keyof EventsWithWildcard<Events>): number;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | --------------------------------------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | the event to get the listener count for |
|
||||
| --------- | --------------------------------------------------------------------------- | --------------------------------------- |
|
||||
| type | keyof [EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events> | the event to get the listener count for |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -9,10 +9,10 @@ The EventEmitter class that many Puppeteer classes extend.
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare class EventEmitter implements CommonEventEmitter
|
||||
export declare class EventEmitter<Events extends Record<EventType, unknown>> implements CommonEventEmitter<EventsWithWildcard<Events>>
|
||||
```
|
||||
|
||||
**Implements:** [CommonEventEmitter](./puppeteer.commoneventemitter.md)
|
||||
**Implements:** [CommonEventEmitter](./puppeteer.commoneventemitter.md)<[EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>>
|
||||
|
||||
## Remarks
|
||||
|
||||
@ -23,12 +23,12 @@ The constructor for this class is marked as internal. Third-party code should no
|
||||
## Methods
|
||||
|
||||
| Method | Modifiers | Description |
|
||||
| ---------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------ |
|
||||
| [addListener(event, handler)](./puppeteer.eventemitter.addlistener.md) | | Add an event listener. |
|
||||
| [emit(event, eventData)](./puppeteer.eventemitter.emit.md) | | Emit an event and call any associated listeners. |
|
||||
| [listenerCount(event)](./puppeteer.eventemitter.listenercount.md) | | Gets the number of listeners for a given event. |
|
||||
| [off(event, handler)](./puppeteer.eventemitter.off.md) | | Remove an event listener from firing. |
|
||||
| [on(event, handler)](./puppeteer.eventemitter.on.md) | | Bind an event listener to fire when an event occurs. |
|
||||
| [once(event, handler)](./puppeteer.eventemitter.once.md) | | Like <code>on</code> but the listener will only be fired once and then it will be removed. |
|
||||
| [removeAllListeners(event)](./puppeteer.eventemitter.removealllisteners.md) | | Removes all listeners. If given an event argument, it will remove only listeners for that event. |
|
||||
| [removeListener(event, handler)](./puppeteer.eventemitter.removelistener.md) | | Remove an event listener. |
|
||||
| --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------ |
|
||||
| [addListener(type, handler)](./puppeteer.eventemitter.addlistener.md) | | Add an event listener. |
|
||||
| [emit(type, event)](./puppeteer.eventemitter.emit.md) | | Emit an event and call any associated listeners. |
|
||||
| [listenerCount(type)](./puppeteer.eventemitter.listenercount.md) | | Gets the number of listeners for a given event. |
|
||||
| [off(type, handler)](./puppeteer.eventemitter.off.md) | | Remove an event listener from firing. |
|
||||
| [on(type, handler)](./puppeteer.eventemitter.on.md) | | Bind an event listener to fire when an event occurs. |
|
||||
| [once(type, handler)](./puppeteer.eventemitter.once.md) | | Like <code>on</code> but the listener will only be fired once and then it will be removed. |
|
||||
| [removeAllListeners(type)](./puppeteer.eventemitter.removealllisteners.md) | | Removes all listeners. If given an event argument, it will remove only listeners for that event. |
|
||||
| [removeListener(type, handler)](./puppeteer.eventemitter.removelistener.md) | | Remove an event listener. |
|
||||
|
@ -10,16 +10,19 @@ Remove an event listener from firing.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
off(event: EventType, handler: Handler<any>): this;
|
||||
off<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler?: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------- | ----------------------------------------------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | the event type you'd like to stop listening to. |
|
||||
| handler | [Handler](./puppeteer.handler.md)<any> | the function that should be removed. |
|
||||
| --------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
|
||||
| type | Key | the event type you'd like to stop listening to. |
|
||||
| handler | [Handler](./puppeteer.handler.md)<[EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>\[Key\]> | _(Optional)_ the function that should be removed. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -10,16 +10,19 @@ Bind an event listener to fire when an event occurs.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
on(event: EventType, handler: Handler<any>): this;
|
||||
on<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | the event type you'd like to listen to. Can be a string or symbol. |
|
||||
| handler | [Handler](./puppeteer.handler.md)<any> | the function to be called when the event occurs. |
|
||||
| --------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| type | Key | the event type you'd like to listen to. Can be a string or symbol. |
|
||||
| handler | [Handler](./puppeteer.handler.md)<[EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>\[Key\]> | the function to be called when the event occurs. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -10,16 +10,19 @@ Like `on` but the listener will only be fired once and then it will be removed.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
once(event: EventType, handler: Handler<any>): this;
|
||||
once<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------- | ------------------------------------------------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | the event you'd like to listen to |
|
||||
| handler | [Handler](./puppeteer.handler.md)<any> | the handler function to run when the event occurs |
|
||||
| --------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
|
||||
| type | Key | the event you'd like to listen to |
|
||||
| handler | [Handler](./puppeteer.handler.md)<[EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>\[Key\]> | the handler function to run when the event occurs |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -10,15 +10,15 @@ Removes all listeners. If given an event argument, it will remove only listeners
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
removeAllListeners(event?: EventType): this;
|
||||
removeAllListeners(type?: keyof EventsWithWildcard<Events>): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------------------------------- | ----------------------------------------------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | _(Optional)_ the event to remove listeners for. |
|
||||
| --------- | --------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| type | keyof [EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events> | _(Optional)_ the event to remove listeners for. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
@ -14,16 +14,19 @@ Remove an event listener.
|
||||
|
||||
```typescript
|
||||
class EventEmitter {
|
||||
removeListener(event: EventType, handler: Handler<any>): this;
|
||||
removeListener<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------- | ----------- |
|
||||
| event | [EventType](./puppeteer.eventtype.md) | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<any> | |
|
||||
| --------- | --------------------------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| type | Key | |
|
||||
| handler | [Handler](./puppeteer.handler.md)<[EventsWithWildcard](./puppeteer.eventswithwildcard.md)<Events>\[Key\]> | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
14
docs/api/puppeteer.eventswithwildcard.md
Normal file
14
docs/api/puppeteer.eventswithwildcard.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
sidebar_label: EventsWithWildcard
|
||||
---
|
||||
|
||||
# EventsWithWildcard type
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export type EventsWithWildcard<Events extends Record<EventType, unknown>> =
|
||||
Events & {
|
||||
'*': Events[keyof Events];
|
||||
};
|
||||
```
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
sidebar_label: EventType
|
||||
---
|
||||
|
||||
# EventType type
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export type EventType = string | symbol;
|
||||
```
|
@ -11,16 +11,16 @@ To understand frames, you can think of frames as `<iframe>` elements. Just like
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare abstract class Frame extends EventEmitter
|
||||
export declare abstract class Frame extends EventEmitter<FrameEvents>
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[FrameEvents](./puppeteer.frameevents.md)>
|
||||
|
||||
## Remarks
|
||||
|
||||
Frame lifecycles are controlled by three events that are all dispatched on the parent [page](./puppeteer.frame.page.md):
|
||||
|
||||
- [PageEmittedEvents.FrameAttached](./puppeteer.pageemittedevents.md) - [PageEmittedEvents.FrameNavigated](./puppeteer.pageemittedevents.md) - [PageEmittedEvents.FrameDetached](./puppeteer.pageemittedevents.md)
|
||||
- [PageEvent.FrameAttached](./puppeteer.pageevent.md) - [PageEvent.FrameNavigated](./puppeteer.pageevent.md) - [PageEvent.FrameDetached](./puppeteer.pageevent.md)
|
||||
|
||||
The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `Frame` class.
|
||||
|
||||
|
13
docs/api/puppeteer.frameevents.md
Normal file
13
docs/api/puppeteer.frameevents.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
sidebar_label: FrameEvents
|
||||
---
|
||||
|
||||
# FrameEvents interface
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface FrameEvents extends Record<EventType, unknown>
|
||||
```
|
||||
|
||||
**Extends:** Record<EventType, unknown>
|
@ -9,10 +9,10 @@ Locators describe a strategy of locating objects and performing an action on the
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare abstract class Locator<T> extends EventEmitter
|
||||
export declare abstract class Locator<T> extends EventEmitter<LocatorEvents>
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[LocatorEvents](./puppeteer.locatorevents.md)>
|
||||
|
||||
## Properties
|
||||
|
||||
@ -31,9 +31,6 @@ export declare abstract class Locator<T> extends EventEmitter
|
||||
| [filter(predicate)](./puppeteer.locator.filter.md) | | <p>Creates an expectation that is evaluated against located values.</p><p>If the expectations do not match, then the locator will retry.</p> |
|
||||
| [hover(this, options)](./puppeteer.locator.hover.md) | | |
|
||||
| [map(mapper)](./puppeteer.locator.map.md) | | Maps the locator using the provided mapper. |
|
||||
| [off(eventName, handler)](./puppeteer.locator.off.md) | | |
|
||||
| [on(eventName, handler)](./puppeteer.locator.on.md) | | |
|
||||
| [once(eventName, handler)](./puppeteer.locator.once.md) | | |
|
||||
| [race(locators)](./puppeteer.locator.race.md) | <code>static</code> | Creates a race between multiple locators but ensures that only a single one acts. |
|
||||
| [scroll(this, options)](./puppeteer.locator.scroll.md) | | |
|
||||
| [setEnsureElementIsInTheViewport(this, value)](./puppeteer.locator.setensureelementisintheviewport.md) | | |
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
sidebar_label: Locator.off
|
||||
---
|
||||
|
||||
# Locator.off() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class Locator {
|
||||
off<K extends keyof LocatorEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: LocatorEventObject[K]) => void
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------------------------------------------- | ----------- |
|
||||
| eventName | K | |
|
||||
| handler | (event: [LocatorEventObject](./puppeteer.locatoreventobject.md)\[K\]) => void | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
this
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
sidebar_label: Locator.on
|
||||
---
|
||||
|
||||
# Locator.on() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class Locator {
|
||||
on<K extends keyof LocatorEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: LocatorEventObject[K]) => void
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------------------------------------------- | ----------- |
|
||||
| eventName | K | |
|
||||
| handler | (event: [LocatorEventObject](./puppeteer.locatoreventobject.md)\[K\]) => void | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
this
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
sidebar_label: Locator.once
|
||||
---
|
||||
|
||||
# Locator.once() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class Locator {
|
||||
once<K extends keyof LocatorEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: LocatorEventObject[K]) => void
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------------------------------------------- | ----------- |
|
||||
| eventName | K | |
|
||||
| handler | (event: [LocatorEventObject](./puppeteer.locatoreventobject.md)\[K\]) => void | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
this
|
@ -1,15 +1,15 @@
|
||||
---
|
||||
sidebar_label: LocatorEmittedEvents
|
||||
sidebar_label: LocatorEvent
|
||||
---
|
||||
|
||||
# LocatorEmittedEvents enum
|
||||
# LocatorEvent enum
|
||||
|
||||
All the events that a locator instance may emit.
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare enum LocatorEmittedEvents
|
||||
export declare enum LocatorEvent
|
||||
```
|
||||
|
||||
## Enumeration Members
|
@ -1,17 +0,0 @@
|
||||
---
|
||||
sidebar_label: LocatorEventObject
|
||||
---
|
||||
|
||||
# LocatorEventObject interface
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface LocatorEventObject
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| -------- | --------- | ----- | ----------- | ------- |
|
||||
| action | | never | | |
|
19
docs/api/puppeteer.locatorevents.md
Normal file
19
docs/api/puppeteer.locatorevents.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
sidebar_label: LocatorEvents
|
||||
---
|
||||
|
||||
# LocatorEvents interface
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface LocatorEvents extends Record<EventType, unknown>
|
||||
```
|
||||
|
||||
**Extends:** Record<EventType, unknown>
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| -------- | --------- | --------- | ----------- | ------- |
|
||||
| action | | undefined | | |
|
@ -15,10 +15,10 @@ One Browser instance might have multiple Page instances.
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare abstract class Page extends EventEmitter implements AsyncDisposable, Disposable
|
||||
export declare abstract class Page extends EventEmitter<PageEvents> implements AsyncDisposable, Disposable
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<[PageEvents](./puppeteer.pageevents.md)>
|
||||
|
||||
**Implements:** AsyncDisposable, Disposable
|
||||
|
||||
@ -42,7 +42,7 @@ import puppeteer from 'puppeteer';
|
||||
})();
|
||||
```
|
||||
|
||||
The Page class extends from Puppeteer's [EventEmitter](./puppeteer.eventemitter.md) class and will emit various events which are documented in the [PageEmittedEvents](./puppeteer.pageemittedevents.md) enum.
|
||||
The Page class extends from Puppeteer's [EventEmitter](./puppeteer.eventemitter.md) class and will emit various events which are documented in the [PageEvent](./puppeteer.pageevent.md) enum.
|
||||
|
||||
## Example 2
|
||||
|
||||
@ -52,7 +52,7 @@ This example logs a message for a single page `load` event:
|
||||
page.once('load', () => console.log('Page loaded!'));
|
||||
```
|
||||
|
||||
To unsubscribe from events use the [Page.off()](./puppeteer.page.off.md) method:
|
||||
To unsubscribe from events use the [EventEmitter.off()](./puppeteer.eventemitter.off.md) method:
|
||||
|
||||
```ts
|
||||
function logRequest(interceptedRequest) {
|
||||
@ -126,9 +126,6 @@ page.off('request', logRequest);
|
||||
| [locator(func)](./puppeteer.page.locator_1.md) | | Creates a locator for the provided function. See [Locator](./puppeteer.locator.md) for details and supported actions. |
|
||||
| [mainFrame()](./puppeteer.page.mainframe.md) | | The page's main frame. |
|
||||
| [metrics()](./puppeteer.page.metrics.md) | | Object containing metrics as key/value pairs. |
|
||||
| [off(eventName, handler)](./puppeteer.page.off.md) | | |
|
||||
| [on(eventName, handler)](./puppeteer.page.on.md) | | <p>Listen to page events.</p><p>:::note</p><p>This method exists to define event typings and handle proper wireup of cooperative request interception. Actual event listening and dispatching is delegated to [EventEmitter](./puppeteer.eventemitter.md).</p><p>:::</p> |
|
||||
| [once(eventName, handler)](./puppeteer.page.once.md) | | |
|
||||
| [pdf(options)](./puppeteer.page.pdf.md) | | Generates a PDF of the page with the <code>print</code> CSS media type. |
|
||||
| [queryObjects(prototypeHandle)](./puppeteer.page.queryobjects.md) | | This method iterates the JavaScript heap and finds all objects with the given prototype. |
|
||||
| [reload(options)](./puppeteer.page.reload.md) | | |
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
sidebar_label: Page.off
|
||||
---
|
||||
|
||||
# Page.off() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class Page {
|
||||
off<K extends keyof PageEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: PageEventObject[K]) => void
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------------------------------------- | ----------- |
|
||||
| eventName | K | |
|
||||
| handler | (event: [PageEventObject](./puppeteer.pageeventobject.md)\[K\]) => void | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
this
|
@ -1,35 +0,0 @@
|
||||
---
|
||||
sidebar_label: Page.on
|
||||
---
|
||||
|
||||
# Page.on() method
|
||||
|
||||
Listen to page events.
|
||||
|
||||
:::note
|
||||
|
||||
This method exists to define event typings and handle proper wireup of cooperative request interception. Actual event listening and dispatching is delegated to [EventEmitter](./puppeteer.eventemitter.md).
|
||||
|
||||
:::
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class Page {
|
||||
on<K extends keyof PageEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: PageEventObject[K]) => void
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------------------------------------- | ----------- |
|
||||
| eventName | K | |
|
||||
| handler | (event: [PageEventObject](./puppeteer.pageeventobject.md)\[K\]) => void | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
this
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
sidebar_label: Page.once
|
||||
---
|
||||
|
||||
# Page.once() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class Page {
|
||||
once<K extends keyof PageEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: PageEventObject[K]) => void
|
||||
): this;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------------------------------------------------------------------------- | ----------- |
|
||||
| eventName | K | |
|
||||
| handler | (event: [PageEventObject](./puppeteer.pageeventobject.md)\[K\]) => void | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
this
|
@ -1,15 +1,15 @@
|
||||
---
|
||||
sidebar_label: PageEmittedEvents
|
||||
sidebar_label: PageEvent
|
||||
---
|
||||
|
||||
# PageEmittedEvents enum
|
||||
# PageEvent enum
|
||||
|
||||
All the events that a page instance may emit.
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare const enum PageEmittedEvents
|
||||
export declare const enum PageEvent
|
||||
```
|
||||
|
||||
## Enumeration Members
|
@ -1,35 +1,37 @@
|
||||
---
|
||||
sidebar_label: PageEventObject
|
||||
sidebar_label: PageEvents
|
||||
---
|
||||
|
||||
# PageEventObject interface
|
||||
# PageEvents interface
|
||||
|
||||
Denotes the objects received by callback functions for page events.
|
||||
|
||||
See [PageEmittedEvents](./puppeteer.pageemittedevents.md) for more detail on the events and when they are emitted.
|
||||
See [PageEvent](./puppeteer.pageevent.md) for more detail on the events and when they are emitted.
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export interface PageEventObject
|
||||
export interface PageEvents extends Record<EventType, unknown>
|
||||
```
|
||||
|
||||
**Extends:** Record<EventType, unknown>
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| ---------------------- | --------- | -------------------------------------------------------------- | ----------- | ------- |
|
||||
| close | | never | | |
|
||||
| close | | undefined | | |
|
||||
| console | | [ConsoleMessage](./puppeteer.consolemessage.md) | | |
|
||||
| dialog | | [Dialog](./puppeteer.dialog.md) | | |
|
||||
| domcontentloaded | | never | | |
|
||||
| domcontentloaded | | undefined | | |
|
||||
| error | | Error | | |
|
||||
| frameattached | | [Frame](./puppeteer.frame.md) | | |
|
||||
| framedetached | | [Frame](./puppeteer.frame.md) | | |
|
||||
| framenavigated | | [Frame](./puppeteer.frame.md) | | |
|
||||
| load | | never | | |
|
||||
| load | | undefined | | |
|
||||
| metrics | | { title: string; metrics: [Metrics](./puppeteer.metrics.md); } | | |
|
||||
| pageerror | | Error | | |
|
||||
| popup | | [Page](./puppeteer.page.md) | | |
|
||||
| popup | | [Page](./puppeteer.page.md) \| null | | |
|
||||
| request | | [HTTPRequest](./puppeteer.httprequest.md) | | |
|
||||
| requestfailed | | [HTTPRequest](./puppeteer.httprequest.md) | | |
|
||||
| requestfinished | | [HTTPRequest](./puppeteer.httprequest.md) | | |
|
@ -9,10 +9,10 @@ This class represents a [WebWorker](https://developer.mozilla.org/en-US/docs/Web
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
export declare class WebWorker extends EventEmitter
|
||||
export declare class WebWorker extends EventEmitter<Record<EventType, unknown>>
|
||||
```
|
||||
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)<Record<EventType, unknown>>
|
||||
|
||||
## Remarks
|
||||
|
||||
|
32
package-lock.json
generated
32
package-lock.json
generated
@ -3434,11 +3434,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chromium-bidi": {
|
||||
"version": "0.4.26",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.26.tgz",
|
||||
"integrity": "sha512-lukBGfogAI4T0y3acc86RaacqgKQve47/8pV2c+Hr1PjcICj2K4OkL3qfX3qrqxxnd4ddurFC0WBA3VCQqYeUQ==",
|
||||
"version": "0.4.27",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.27.tgz",
|
||||
"integrity": "sha512-8Irq0FbKYN8Xmj8M62kta6wk5MyDKeYIFtNavxQ2M3xf2v5MCC4ntf+FxitQu1iHaQvGU6t5O+Nrep0RNNS0EQ==",
|
||||
"dependencies": {
|
||||
"mitt": "3.0.1"
|
||||
"mitt": "3.0.1",
|
||||
"urlpattern-polyfill": "9.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"devtools-protocol": "*"
|
||||
@ -10668,6 +10669,11 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/urlpattern-polyfill": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz",
|
||||
"integrity": "sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g=="
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"license": "MIT"
|
||||
@ -11108,7 +11114,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "1.7.1",
|
||||
"chromium-bidi": "0.4.26",
|
||||
"chromium-bidi": "0.4.27",
|
||||
"cross-fetch": "4.0.0",
|
||||
"debug": "4.3.4",
|
||||
"devtools-protocol": "0.0.1159816",
|
||||
@ -13450,11 +13456,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"chromium-bidi": {
|
||||
"version": "0.4.26",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.26.tgz",
|
||||
"integrity": "sha512-lukBGfogAI4T0y3acc86RaacqgKQve47/8pV2c+Hr1PjcICj2K4OkL3qfX3qrqxxnd4ddurFC0WBA3VCQqYeUQ==",
|
||||
"version": "0.4.27",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.27.tgz",
|
||||
"integrity": "sha512-8Irq0FbKYN8Xmj8M62kta6wk5MyDKeYIFtNavxQ2M3xf2v5MCC4ntf+FxitQu1iHaQvGU6t5O+Nrep0RNNS0EQ==",
|
||||
"requires": {
|
||||
"mitt": "3.0.1"
|
||||
"mitt": "3.0.1",
|
||||
"urlpattern-polyfill": "9.0.0"
|
||||
}
|
||||
},
|
||||
"ci-info": {
|
||||
@ -17241,7 +17248,7 @@
|
||||
"version": "file:packages/puppeteer-core",
|
||||
"requires": {
|
||||
"@puppeteer/browsers": "1.7.1",
|
||||
"chromium-bidi": "0.4.26",
|
||||
"chromium-bidi": "0.4.27",
|
||||
"cross-fetch": "4.0.0",
|
||||
"debug": "4.3.4",
|
||||
"devtools-protocol": "0.0.1159816",
|
||||
@ -18455,6 +18462,11 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"urlpattern-polyfill": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz",
|
||||
"integrity": "sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g=="
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
|
@ -141,7 +141,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "1.7.1",
|
||||
"chromium-bidi": "0.4.26",
|
||||
"chromium-bidi": "0.4.27",
|
||||
"cross-fetch": "4.0.0",
|
||||
"debug": "4.3.4",
|
||||
"devtools-protocol": "0.0.1159816",
|
||||
|
@ -21,7 +21,7 @@ import {ChildProcess} from 'child_process';
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {Symbol} from '../../third_party/disposablestack/disposablestack.js';
|
||||
import {EventEmitter} from '../common/EventEmitter.js';
|
||||
import {EventEmitter, EventType} from '../common/EventEmitter.js';
|
||||
import {debugError, waitWithTimeout} from '../common/util.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
@ -130,7 +130,7 @@ export interface WaitForTargetOptions {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const enum BrowserEmittedEvents {
|
||||
export const enum BrowserEvent {
|
||||
/**
|
||||
* Emitted when Puppeteer gets disconnected from the browser instance. This
|
||||
* might happen because of one of the following:
|
||||
@ -140,7 +140,6 @@ export const enum BrowserEmittedEvents {
|
||||
* - The {@link Browser.disconnect | browser.disconnect } method was called.
|
||||
*/
|
||||
Disconnected = 'disconnected',
|
||||
|
||||
/**
|
||||
* Emitted when the url of a target changes. Contains a {@link Target} instance.
|
||||
*
|
||||
@ -149,7 +148,6 @@ export const enum BrowserEmittedEvents {
|
||||
* Note that this includes target changes in incognito browser contexts.
|
||||
*/
|
||||
TargetChanged = 'targetchanged',
|
||||
|
||||
/**
|
||||
* Emitted when a target is created, for example when a new page is opened by
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
|
||||
@ -171,6 +169,31 @@ export const enum BrowserEmittedEvents {
|
||||
* Note that this includes target destructions in incognito browser contexts.
|
||||
*/
|
||||
TargetDestroyed = 'targetdestroyed',
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
TargetDiscovered = 'targetdiscovered',
|
||||
}
|
||||
|
||||
export {
|
||||
/**
|
||||
* @deprecated Use {@link BrowserEvent}.
|
||||
*/
|
||||
BrowserEvent as BrowserEmittedEvents,
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface BrowserEvents extends Record<EventType, unknown> {
|
||||
[BrowserEvent.Disconnected]: undefined;
|
||||
[BrowserEvent.TargetCreated]: Target;
|
||||
[BrowserEvent.TargetDestroyed]: Target;
|
||||
[BrowserEvent.TargetChanged]: Target;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
[BrowserEvent.TargetDiscovered]: Protocol.Target.TargetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,7 +203,7 @@ export const enum BrowserEmittedEvents {
|
||||
* @remarks
|
||||
*
|
||||
* The Browser class extends from Puppeteer's {@link EventEmitter} class and will
|
||||
* emit various events which are documented in the {@link BrowserEmittedEvents} enum.
|
||||
* emit various events which are documented in the {@link BrowserEvent} enum.
|
||||
*
|
||||
* @example
|
||||
* An example of using a {@link Browser} to create a {@link Page}:
|
||||
@ -219,7 +242,7 @@ export const enum BrowserEmittedEvents {
|
||||
* @public
|
||||
*/
|
||||
export class Browser
|
||||
extends EventEmitter
|
||||
extends EventEmitter<BrowserEvents>
|
||||
implements AsyncDisposable, Disposable
|
||||
{
|
||||
/**
|
||||
@ -389,8 +412,8 @@ export class Browser
|
||||
const {timeout = 30000} = options;
|
||||
const targetDeferred = Deferred.create<Target | PromiseLike<Target>>();
|
||||
|
||||
this.on(BrowserEmittedEvents.TargetCreated, check);
|
||||
this.on(BrowserEmittedEvents.TargetChanged, check);
|
||||
this.on(BrowserEvent.TargetCreated, check);
|
||||
this.on(BrowserEvent.TargetChanged, check);
|
||||
try {
|
||||
this.targets().forEach(check);
|
||||
if (!timeout) {
|
||||
@ -402,8 +425,8 @@ export class Browser
|
||||
timeout
|
||||
);
|
||||
} finally {
|
||||
this.off(BrowserEmittedEvents.TargetCreated, check);
|
||||
this.off(BrowserEmittedEvents.TargetChanged, check);
|
||||
this.off(BrowserEvent.TargetCreated, check);
|
||||
this.off(BrowserEvent.TargetChanged, check);
|
||||
}
|
||||
|
||||
async function check(target: Target): Promise<void> {
|
||||
@ -491,28 +514,3 @@ export class Browser
|
||||
return this.close();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const enum BrowserContextEmittedEvents {
|
||||
/**
|
||||
* Emitted when the url of a target inside the browser context changes.
|
||||
* Contains a {@link Target} instance.
|
||||
*/
|
||||
TargetChanged = 'targetchanged',
|
||||
|
||||
/**
|
||||
* Emitted when a target is created within the browser context, for example
|
||||
* when a new page is opened by
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
|
||||
* or by {@link BrowserContext.newPage | browserContext.newPage}
|
||||
*
|
||||
* Contains a {@link Target} instance.
|
||||
*/
|
||||
TargetCreated = 'targetcreated',
|
||||
/**
|
||||
* Emitted when a target is destroyed within the browser context, for example
|
||||
* when a page is closed. Contains a {@link Target} instance.
|
||||
*/
|
||||
TargetDestroyed = 'targetdestroyed',
|
||||
}
|
||||
|
@ -14,12 +14,54 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {EventEmitter} from '../common/EventEmitter.js';
|
||||
import {EventEmitter, EventType} from '../common/EventEmitter.js';
|
||||
|
||||
import type {Permission, Browser} from './Browser.js';
|
||||
import type {Browser, Permission} from './Browser.js';
|
||||
import {Page} from './Page.js';
|
||||
import type {Target} from './Target.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const enum BrowserContextEvent {
|
||||
/**
|
||||
* Emitted when the url of a target inside the browser context changes.
|
||||
* Contains a {@link Target} instance.
|
||||
*/
|
||||
TargetChanged = 'targetchanged',
|
||||
|
||||
/**
|
||||
* Emitted when a target is created within the browser context, for example
|
||||
* when a new page is opened by
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
|
||||
* or by {@link BrowserContext.newPage | browserContext.newPage}
|
||||
*
|
||||
* Contains a {@link Target} instance.
|
||||
*/
|
||||
TargetCreated = 'targetcreated',
|
||||
/**
|
||||
* Emitted when a target is destroyed within the browser context, for example
|
||||
* when a page is closed. Contains a {@link Target} instance.
|
||||
*/
|
||||
TargetDestroyed = 'targetdestroyed',
|
||||
}
|
||||
|
||||
export {
|
||||
/**
|
||||
* @deprecated Use {@link BrowserContextEvent}
|
||||
*/
|
||||
BrowserContextEvent as BrowserContextEmittedEvents,
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface BrowserContextEvents extends Record<EventType, unknown> {
|
||||
[BrowserContextEvent.TargetChanged]: Target;
|
||||
[BrowserContextEvent.TargetCreated]: Target;
|
||||
[BrowserContextEvent.TargetDestroyed]: Target;
|
||||
}
|
||||
|
||||
/**
|
||||
* BrowserContexts provide a way to operate multiple independent browser
|
||||
* sessions. When a browser is launched, it has a single BrowserContext used by
|
||||
@ -30,7 +72,7 @@ import type {Target} from './Target.js';
|
||||
*
|
||||
* The Browser class extends from Puppeteer's {@link EventEmitter} class and
|
||||
* will emit various events which are documented in the
|
||||
* {@link BrowserContextEmittedEvents} enum.
|
||||
* {@link BrowserContextEvents} enum.
|
||||
*
|
||||
* If a page opens another page, e.g. with a `window.open` call, the popup will
|
||||
* belong to the parent page's browser context.
|
||||
@ -55,7 +97,7 @@ import type {Target} from './Target.js';
|
||||
* @public
|
||||
*/
|
||||
|
||||
export class BrowserContext extends EventEmitter {
|
||||
export class BrowserContext extends EventEmitter<BrowserContextEvents> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
122
packages/puppeteer-core/src/api/CDPSession.ts
Normal file
122
packages/puppeteer-core/src/api/CDPSession.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||
|
||||
import type {Connection} from '../common/Connection.js';
|
||||
import {EventEmitter, EventType} from '../common/EventEmitter.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CDPEvents = {
|
||||
[Property in keyof ProtocolMapping.Events]: ProtocolMapping.Events[Property][0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal events that the CDPSession class emits.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace CDPSessionEvent {
|
||||
export const Disconnected = Symbol('CDPSession.Disconnected');
|
||||
export const Swapped = Symbol('CDPSession.Swapped');
|
||||
/**
|
||||
* Emitted when the session is ready to be configured during the auto-attach
|
||||
* process. Right after the event is handled, the session will be resumed.
|
||||
*/
|
||||
export const Ready = Symbol('CDPSession.Ready');
|
||||
export const SessionAttached = Symbol('CDPSession.SessionAttached');
|
||||
export const SessionDetached = Symbol('CDPSession.SessionDetached');
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CDPSessionEvents
|
||||
extends CDPEvents,
|
||||
Record<EventType, unknown> {
|
||||
/** @internal */
|
||||
[CDPSessionEvent.Disconnected]: undefined;
|
||||
/** @internal */
|
||||
[CDPSessionEvent.Swapped]: CDPSession;
|
||||
/** @internal */
|
||||
[CDPSessionEvent.Ready]: CDPSession;
|
||||
/** @internal */
|
||||
[CDPSessionEvent.SessionAttached]: CDPSession;
|
||||
/** @internal */
|
||||
[CDPSessionEvent.SessionDetached]: CDPSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `CDPSession` instances are used to talk raw Chrome Devtools Protocol.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Protocol methods can be called with {@link CDPSession.send} method and protocol
|
||||
* events can be subscribed to with `CDPSession.on` method.
|
||||
*
|
||||
* Useful links: {@link https://chromedevtools.github.io/devtools-protocol/ | DevTools Protocol Viewer}
|
||||
* and {@link https://github.com/aslushnikov/getting-started-with-cdp/blob/HEAD/README.md | Getting Started with DevTools Protocol}.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const client = await page.target().createCDPSession();
|
||||
* await client.send('Animation.enable');
|
||||
* client.on('Animation.animationCreated', () =>
|
||||
* console.log('Animation created!')
|
||||
* );
|
||||
* const response = await client.send('Animation.getPlaybackRate');
|
||||
* console.log('playback rate is ' + response.playbackRate);
|
||||
* await client.send('Animation.setPlaybackRate', {
|
||||
* playbackRate: response.playbackRate / 2,
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export abstract class CDPSession extends EventEmitter<CDPSessionEvents> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connection(): Connection | undefined {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent session in terms of CDP's auto-attach mechanism.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
parentSession(): CDPSession | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
send<T extends keyof ProtocolMapping.Commands>(
|
||||
method: T,
|
||||
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||
): Promise<ProtocolMapping.Commands[T]['returnType']>;
|
||||
send<T extends keyof ProtocolMapping.Commands>(): Promise<
|
||||
ProtocolMapping.Commands[T]['returnType']
|
||||
> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the cdpSession from the target. Once detached, the cdpSession object
|
||||
* won't emit any events and can't be used to send messages.
|
||||
*/
|
||||
async detach(): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session's id.
|
||||
*/
|
||||
id(): string {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {CDPSession} from '../common/Connection.js';
|
||||
|
||||
import {CDPSession} from './CDPSession.js';
|
||||
import {Realm} from './Realm.js';
|
||||
|
||||
/**
|
||||
|
@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Protocol from 'devtools-protocol';
|
||||
|
||||
import {ClickOptions, ElementHandle} from '../api/ElementHandle.js';
|
||||
import {HTTPResponse} from '../api/HTTPResponse.js';
|
||||
import {Page, WaitTimeoutOptions} from '../api/Page.js';
|
||||
import {CDPSession} from '../common/Connection.js';
|
||||
import {DeviceRequestPrompt} from '../common/DeviceRequestPrompt.js';
|
||||
import {EventEmitter} from '../common/EventEmitter.js';
|
||||
import {EventEmitter, EventType} from '../common/EventEmitter.js';
|
||||
import {getQueryHandlerAndSelector} from '../common/GetQueryHandler.js';
|
||||
import {transposeIterableHandle} from '../common/HandleIterator.js';
|
||||
import {
|
||||
@ -43,6 +44,7 @@ import {
|
||||
import {assert} from '../util/assert.js';
|
||||
import {throwIfDisposed} from '../util/decorators.js';
|
||||
|
||||
import {CDPSession} from './CDPSession.js';
|
||||
import {KeyboardTypeOptions} from './Input.js';
|
||||
import {FunctionLocator, Locator, NodeLocator} from './locators/locators.js';
|
||||
import {Realm} from './Realm.js';
|
||||
@ -127,6 +129,44 @@ export interface FrameAddStyleTagOptions {
|
||||
content?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface FrameEvents extends Record<EventType, unknown> {
|
||||
/** @internal */
|
||||
[FrameEvent.FrameNavigated]: Protocol.Page.NavigationType;
|
||||
/** @internal */
|
||||
[FrameEvent.FrameSwapped]: undefined;
|
||||
/** @internal */
|
||||
[FrameEvent.LifecycleEvent]: undefined;
|
||||
/** @internal */
|
||||
[FrameEvent.FrameNavigatedWithinDocument]: undefined;
|
||||
/** @internal */
|
||||
[FrameEvent.FrameDetached]: Frame;
|
||||
/** @internal */
|
||||
[FrameEvent.FrameSwappedByActivation]: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* We use symbols to prevent external parties listening to these events.
|
||||
* They are internal to Puppeteer.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace FrameEvent {
|
||||
export const FrameNavigated = Symbol('Frame.FrameNavigated');
|
||||
export const FrameSwapped = Symbol('Frame.FrameSwapped');
|
||||
export const LifecycleEvent = Symbol('Frame.LifecycleEvent');
|
||||
export const FrameNavigatedWithinDocument = Symbol(
|
||||
'Frame.FrameNavigatedWithinDocument'
|
||||
);
|
||||
export const FrameDetached = Symbol('Frame.FrameDetached');
|
||||
export const FrameSwappedByActivation = Symbol(
|
||||
'Frame.FrameSwappedByActivation'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -181,13 +221,13 @@ export const throwIfDetached = throwIfDisposed<Frame>(frame => {
|
||||
* Frame lifecycles are controlled by three events that are all dispatched on
|
||||
* the parent {@link Frame.page | page}:
|
||||
*
|
||||
* - {@link PageEmittedEvents.FrameAttached}
|
||||
* - {@link PageEmittedEvents.FrameNavigated}
|
||||
* - {@link PageEmittedEvents.FrameDetached}
|
||||
* - {@link PageEvent.FrameAttached}
|
||||
* - {@link PageEvent.FrameNavigated}
|
||||
* - {@link PageEvent.FrameDetached}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export abstract class Frame extends EventEmitter {
|
||||
export abstract class Frame extends EventEmitter<FrameEvents> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -15,8 +15,7 @@
|
||||
*/
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../common/Connection.js';
|
||||
|
||||
import {CDPSession} from './CDPSession.js';
|
||||
import {Frame} from './Frame.js';
|
||||
import {HTTPResponse} from './HTTPResponse.js';
|
||||
|
||||
|
@ -19,6 +19,8 @@ import type {Readable} from 'stream';
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {
|
||||
delay,
|
||||
filter,
|
||||
filterAsync,
|
||||
first,
|
||||
firstValueFrom,
|
||||
@ -27,30 +29,34 @@ import {
|
||||
map,
|
||||
merge,
|
||||
Observable,
|
||||
raceWith,
|
||||
delay,
|
||||
filter,
|
||||
of,
|
||||
switchMap,
|
||||
raceWith,
|
||||
startWith,
|
||||
switchMap,
|
||||
} from '../../third_party/rxjs/rxjs.js';
|
||||
import type {HTTPRequest} from '../api/HTTPRequest.js';
|
||||
import type {HTTPResponse} from '../api/HTTPResponse.js';
|
||||
import type {Accessibility} from '../common/Accessibility.js';
|
||||
import type {CDPSession} from '../common/Connection.js';
|
||||
import type {BidiNetworkManager} from '../common/bidi/NetworkManager.js';
|
||||
import type {ConsoleMessage} from '../common/ConsoleMessage.js';
|
||||
import type {Coverage} from '../common/Coverage.js';
|
||||
import {Device} from '../common/Device.js';
|
||||
import {DeviceRequestPrompt} from '../common/DeviceRequestPrompt.js';
|
||||
import {TargetCloseError} from '../common/Errors.js';
|
||||
import {EventEmitter, Handler} from '../common/EventEmitter.js';
|
||||
import {
|
||||
EventEmitter,
|
||||
EventsWithWildcard,
|
||||
EventType,
|
||||
Handler,
|
||||
} from '../common/EventEmitter.js';
|
||||
import type {FileChooser} from '../common/FileChooser.js';
|
||||
import type {WaitForSelectorOptions} from '../common/IsolatedWorld.js';
|
||||
import type {PuppeteerLifeCycleEvent} from '../common/LifecycleWatcher.js';
|
||||
import {
|
||||
NetworkManager as CdpNetworkManager,
|
||||
Credentials,
|
||||
NetworkConditions,
|
||||
NetworkManagerEmittedEvents,
|
||||
NetworkManagerEvent,
|
||||
} from '../common/NetworkManager.js';
|
||||
import {
|
||||
LowerCasePaperFormat,
|
||||
@ -81,6 +87,7 @@ import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import type {Browser} from './Browser.js';
|
||||
import type {BrowserContext} from './BrowserContext.js';
|
||||
import type {CDPSession} from './CDPSession.js';
|
||||
import type {Dialog} from './Dialog.js';
|
||||
import type {ClickOptions, ElementHandle} from './ElementHandle.js';
|
||||
import type {
|
||||
@ -249,7 +256,7 @@ export interface ScreenshotOptions {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const enum PageEmittedEvents {
|
||||
export const enum PageEvent {
|
||||
/**
|
||||
* Emitted when the page closes.
|
||||
*/
|
||||
@ -396,36 +403,52 @@ export const enum PageEmittedEvents {
|
||||
WorkerDestroyed = 'workerdestroyed',
|
||||
}
|
||||
|
||||
export {
|
||||
/**
|
||||
* All the events that a page instance may emit.
|
||||
*
|
||||
* @deprecated Use {@link PageEvent}.
|
||||
*/
|
||||
PageEvent as PageEmittedEvents,
|
||||
};
|
||||
|
||||
/**
|
||||
* Denotes the objects received by callback functions for page events.
|
||||
*
|
||||
* See {@link PageEmittedEvents} for more detail on the events and when they are
|
||||
* See {@link PageEvent} for more detail on the events and when they are
|
||||
* emitted.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface PageEventObject {
|
||||
close: never;
|
||||
console: ConsoleMessage;
|
||||
dialog: Dialog;
|
||||
domcontentloaded: never;
|
||||
error: Error;
|
||||
frameattached: Frame;
|
||||
framedetached: Frame;
|
||||
framenavigated: Frame;
|
||||
load: never;
|
||||
metrics: {title: string; metrics: Metrics};
|
||||
pageerror: Error;
|
||||
popup: Page;
|
||||
request: HTTPRequest;
|
||||
response: HTTPResponse;
|
||||
requestfailed: HTTPRequest;
|
||||
requestfinished: HTTPRequest;
|
||||
requestservedfromcache: HTTPRequest;
|
||||
workercreated: WebWorker;
|
||||
workerdestroyed: WebWorker;
|
||||
export interface PageEvents extends Record<EventType, unknown> {
|
||||
[PageEvent.Close]: undefined;
|
||||
[PageEvent.Console]: ConsoleMessage;
|
||||
[PageEvent.Dialog]: Dialog;
|
||||
[PageEvent.DOMContentLoaded]: undefined;
|
||||
[PageEvent.Error]: Error;
|
||||
[PageEvent.FrameAttached]: Frame;
|
||||
[PageEvent.FrameDetached]: Frame;
|
||||
[PageEvent.FrameNavigated]: Frame;
|
||||
[PageEvent.Load]: undefined;
|
||||
[PageEvent.Metrics]: {title: string; metrics: Metrics};
|
||||
[PageEvent.PageError]: Error;
|
||||
[PageEvent.Popup]: Page | null;
|
||||
[PageEvent.Request]: HTTPRequest;
|
||||
[PageEvent.Response]: HTTPResponse;
|
||||
[PageEvent.RequestFailed]: HTTPRequest;
|
||||
[PageEvent.RequestFinished]: HTTPRequest;
|
||||
[PageEvent.RequestServedFromCache]: HTTPRequest;
|
||||
[PageEvent.WorkerCreated]: WebWorker;
|
||||
[PageEvent.WorkerDestroyed]: WebWorker;
|
||||
}
|
||||
|
||||
export {
|
||||
/**
|
||||
* @deprecated Use {@link PageEvents}.
|
||||
*/
|
||||
PageEvents as PageEventObject,
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -460,7 +483,7 @@ export interface NewDocumentScriptEvaluation {
|
||||
* ```
|
||||
*
|
||||
* The Page class extends from Puppeteer's {@link EventEmitter} class and will
|
||||
* emit various events which are documented in the {@link PageEmittedEvents} enum.
|
||||
* emit various events which are documented in the {@link PageEvent} enum.
|
||||
*
|
||||
* @example
|
||||
* This example logs a message for a single page `load` event:
|
||||
@ -469,7 +492,7 @@ export interface NewDocumentScriptEvaluation {
|
||||
* page.once('load', () => console.log('Page loaded!'));
|
||||
* ```
|
||||
*
|
||||
* To unsubscribe from events use the {@link Page.off} method:
|
||||
* To unsubscribe from events use the {@link EventEmitter.off} method:
|
||||
*
|
||||
* ```ts
|
||||
* function logRequest(interceptedRequest) {
|
||||
@ -483,10 +506,10 @@ export interface NewDocumentScriptEvaluation {
|
||||
* @public
|
||||
*/
|
||||
export abstract class Page
|
||||
extends EventEmitter
|
||||
extends EventEmitter<PageEvents>
|
||||
implements AsyncDisposable, Disposable
|
||||
{
|
||||
#handlerMap = new WeakMap<Handler<any>, Handler<any>>();
|
||||
#requestHandlers = new WeakMap<Handler<HTTPRequest>, Handler<HTTPRequest>>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -519,52 +542,56 @@ export abstract class Page
|
||||
/**
|
||||
* Listen to page events.
|
||||
*
|
||||
* :::note
|
||||
*
|
||||
* @remarks
|
||||
* This method exists to define event typings and handle proper wireup of
|
||||
* cooperative request interception. Actual event listening and dispatching is
|
||||
* delegated to {@link EventEmitter}.
|
||||
*
|
||||
* :::
|
||||
* @internal
|
||||
*/
|
||||
override on<K extends keyof PageEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: PageEventObject[K]) => void
|
||||
override on<K extends keyof EventsWithWildcard<PageEvents>>(
|
||||
type: K,
|
||||
handler: (event: EventsWithWildcard<PageEvents>[K]) => void
|
||||
): this {
|
||||
if (eventName === 'request') {
|
||||
const wrap =
|
||||
this.#handlerMap.get(handler) ||
|
||||
((event: HTTPRequest) => {
|
||||
if (type !== PageEvent.Request) {
|
||||
return super.on(type, handler);
|
||||
}
|
||||
let wrapper = this.#requestHandlers.get(
|
||||
handler as (event: PageEvents[PageEvent.Request]) => void
|
||||
);
|
||||
if (wrapper === undefined) {
|
||||
wrapper = (event: HTTPRequest) => {
|
||||
event.enqueueInterceptAction(() => {
|
||||
return handler(event as PageEventObject[K]);
|
||||
return handler(event as EventsWithWildcard<PageEvents>[K]);
|
||||
});
|
||||
});
|
||||
|
||||
this.#handlerMap.set(handler, wrap);
|
||||
|
||||
return super.on(eventName, wrap);
|
||||
};
|
||||
this.#requestHandlers.set(
|
||||
handler as (event: PageEvents[PageEvent.Request]) => void,
|
||||
wrapper
|
||||
);
|
||||
}
|
||||
return super.on(eventName, handler);
|
||||
return super.on(
|
||||
type,
|
||||
wrapper as (event: EventsWithWildcard<PageEvents>[K]) => void
|
||||
);
|
||||
}
|
||||
|
||||
override once<K extends keyof PageEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: PageEventObject[K]) => void
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
override off<K extends keyof EventsWithWildcard<PageEvents>>(
|
||||
type: K,
|
||||
handler: (event: EventsWithWildcard<PageEvents>[K]) => void
|
||||
): this {
|
||||
// Note: this method only exists to define the types; we delegate the impl
|
||||
// to EventEmitter.
|
||||
return super.once(eventName, handler);
|
||||
if (type === PageEvent.Request) {
|
||||
handler =
|
||||
(this.#requestHandlers.get(
|
||||
handler as (
|
||||
event: EventsWithWildcard<PageEvents>[PageEvent.Request]
|
||||
) => void
|
||||
) as (event: EventsWithWildcard<PageEvents>[K]) => void) || handler;
|
||||
}
|
||||
|
||||
override off<K extends keyof PageEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: PageEventObject[K]) => void
|
||||
): this {
|
||||
if (eventName === 'request') {
|
||||
handler = this.#handlerMap.get(handler) || handler;
|
||||
}
|
||||
|
||||
return super.off(eventName, handler);
|
||||
return super.off(type, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1696,9 +1723,7 @@ export abstract class Page
|
||||
* @internal
|
||||
*/
|
||||
protected async _waitForNetworkIdle(
|
||||
networkManager: EventEmitter & {
|
||||
inFlightRequestsCount: () => number;
|
||||
},
|
||||
networkManager: BidiNetworkManager | CdpNetworkManager,
|
||||
idleTime: number,
|
||||
ms: number,
|
||||
closedDeferred: Deferred<TargetCloseError>
|
||||
@ -1707,15 +1732,15 @@ export abstract class Page
|
||||
merge(
|
||||
fromEvent(
|
||||
networkManager,
|
||||
NetworkManagerEmittedEvents.Request as unknown as string
|
||||
NetworkManagerEvent.Request as unknown as string
|
||||
),
|
||||
fromEvent(
|
||||
networkManager,
|
||||
NetworkManagerEmittedEvents.Response as unknown as string
|
||||
NetworkManagerEvent.Response as unknown as string
|
||||
),
|
||||
fromEvent(
|
||||
networkManager,
|
||||
NetworkManagerEmittedEvents.RequestFailed as unknown as string
|
||||
NetworkManagerEvent.RequestFailed as unknown as string
|
||||
)
|
||||
).pipe(
|
||||
startWith(null),
|
||||
@ -1755,15 +1780,15 @@ export abstract class Page
|
||||
|
||||
return await firstValueFrom(
|
||||
merge(
|
||||
fromEvent(this, PageEmittedEvents.FrameAttached) as Observable<Frame>,
|
||||
fromEvent(this, PageEmittedEvents.FrameNavigated) as Observable<Frame>,
|
||||
fromEvent(this, PageEvent.FrameAttached) as Observable<Frame>,
|
||||
fromEvent(this, PageEvent.FrameNavigated) as Observable<Frame>,
|
||||
from(this.frames())
|
||||
).pipe(
|
||||
filterAsync(urlOrPredicate),
|
||||
first(),
|
||||
raceWith(
|
||||
timeout(ms),
|
||||
fromEvent(this, PageEmittedEvents.Close).pipe(
|
||||
fromEvent(this, PageEvent.Close).pipe(
|
||||
map(() => {
|
||||
throw new TargetCloseError('Page closed.');
|
||||
})
|
||||
|
@ -17,9 +17,10 @@
|
||||
import type {Browser} from '../api/Browser.js';
|
||||
import type {BrowserContext} from '../api/BrowserContext.js';
|
||||
import {Page} from '../api/Page.js';
|
||||
import {CDPSession} from '../common/Connection.js';
|
||||
import {WebWorker} from '../common/WebWorker.js';
|
||||
|
||||
import {CDPSession} from './CDPSession.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -28,3 +28,4 @@ export * from './locators/locators.js';
|
||||
export * from './Page.js';
|
||||
export * from './Realm.js';
|
||||
export * from './Target.js';
|
||||
export * from './CDPSession.js';
|
||||
|
@ -37,7 +37,7 @@ import {
|
||||
retry,
|
||||
tap,
|
||||
} from '../../../third_party/rxjs/rxjs.js';
|
||||
import {EventEmitter} from '../../common/EventEmitter.js';
|
||||
import {EventEmitter, EventType} from '../../common/EventEmitter.js';
|
||||
import {HandleFor} from '../../common/types.js';
|
||||
import {debugError, timeout} from '../../common/util.js';
|
||||
import {BoundingBox, ClickOptions, ElementHandle} from '../ElementHandle.js';
|
||||
@ -130,20 +130,34 @@ export interface LocatorScrollOptions extends ActionOptions {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum LocatorEmittedEvents {
|
||||
export enum LocatorEvent {
|
||||
/**
|
||||
* Emitted every time before the locator performs an action on the located element(s).
|
||||
*/
|
||||
Action = 'action',
|
||||
}
|
||||
|
||||
export {
|
||||
/**
|
||||
* @deprecated Use {@link LocatorEvent}.
|
||||
*/
|
||||
LocatorEvent as LocatorEmittedEvents,
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LocatorEventObject {
|
||||
[LocatorEmittedEvents.Action]: never;
|
||||
export interface LocatorEvents extends Record<EventType, unknown> {
|
||||
[LocatorEvent.Action]: undefined;
|
||||
}
|
||||
|
||||
export {
|
||||
/**
|
||||
* @deprecated Use {@link LocatorEvents}.
|
||||
*/
|
||||
LocatorEvents as LocatorEventObject,
|
||||
};
|
||||
|
||||
/**
|
||||
* Locators describe a strategy of locating objects and performing an action on
|
||||
* them. If the action fails because the object is not ready for the action, the
|
||||
@ -152,7 +166,7 @@ export interface LocatorEventObject {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export abstract class Locator<T> extends EventEmitter {
|
||||
export abstract class Locator<T> extends EventEmitter<LocatorEvents> {
|
||||
/**
|
||||
* Creates a race between multiple locators but ensures that only a single one
|
||||
* acts.
|
||||
@ -224,27 +238,6 @@ export abstract class Locator<T> extends EventEmitter {
|
||||
return this._timeout;
|
||||
}
|
||||
|
||||
override on<K extends keyof LocatorEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: LocatorEventObject[K]) => void
|
||||
): this {
|
||||
return super.on(eventName, handler);
|
||||
}
|
||||
|
||||
override once<K extends keyof LocatorEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: LocatorEventObject[K]) => void
|
||||
): this {
|
||||
return super.once(eventName, handler);
|
||||
}
|
||||
|
||||
override off<K extends keyof LocatorEventObject>(
|
||||
eventName: K,
|
||||
handler: (event: LocatorEventObject[K]) => void
|
||||
): this {
|
||||
return super.off(eventName, handler);
|
||||
}
|
||||
|
||||
setTimeout(timeout: number): Locator<T> {
|
||||
const locator = this._clone();
|
||||
locator._timeout = timeout;
|
||||
@ -426,7 +419,7 @@ export abstract class Locator<T> extends EventEmitter {
|
||||
signal
|
||||
),
|
||||
tap(() => {
|
||||
return this.emit(LocatorEmittedEvents.Action);
|
||||
return this.emit(LocatorEvent.Action, undefined);
|
||||
}),
|
||||
mergeMap(handle => {
|
||||
return from(handle.click(options)).pipe(
|
||||
@ -456,7 +449,7 @@ export abstract class Locator<T> extends EventEmitter {
|
||||
signal
|
||||
),
|
||||
tap(() => {
|
||||
return this.emit(LocatorEmittedEvents.Action);
|
||||
return this.emit(LocatorEvent.Action, undefined);
|
||||
}),
|
||||
mergeMap(handle => {
|
||||
return from(
|
||||
@ -587,7 +580,7 @@ export abstract class Locator<T> extends EventEmitter {
|
||||
signal
|
||||
),
|
||||
tap(() => {
|
||||
return this.emit(LocatorEmittedEvents.Action);
|
||||
return this.emit(LocatorEvent.Action, undefined);
|
||||
}),
|
||||
mergeMap(handle => {
|
||||
return from(handle.hover()).pipe(
|
||||
@ -615,7 +608,7 @@ export abstract class Locator<T> extends EventEmitter {
|
||||
signal
|
||||
),
|
||||
tap(() => {
|
||||
return this.emit(LocatorEmittedEvents.Action);
|
||||
return this.emit(LocatorEvent.Action, undefined);
|
||||
}),
|
||||
mergeMap(handle => {
|
||||
return from(
|
||||
|
@ -16,10 +16,9 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {ElementHandle} from '../api/ElementHandle.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
|
||||
/**
|
||||
* Represents a Node and the properties of it that are relevant to Accessibility.
|
||||
* @public
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {ElementHandle} from '../api/ElementHandle.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
import {QueryHandler, QuerySelector} from './QueryHandler.js';
|
||||
import {AwaitableIterable} from './types.js';
|
||||
|
@ -21,33 +21,33 @@ import {Protocol} from 'devtools-protocol';
|
||||
import {
|
||||
Browser as BrowserBase,
|
||||
BrowserCloseCallback,
|
||||
TargetFilterCallback,
|
||||
IsPageTargetCallback,
|
||||
BrowserEmittedEvents,
|
||||
BrowserContextEmittedEvents,
|
||||
BrowserContextOptions,
|
||||
WEB_PERMISSION_TO_PROTOCOL_PERMISSION,
|
||||
BrowserEvent,
|
||||
IsPageTargetCallback,
|
||||
Permission,
|
||||
TargetFilterCallback,
|
||||
WEB_PERMISSION_TO_PROTOCOL_PERMISSION,
|
||||
} from '../api/Browser.js';
|
||||
import {BrowserContext} from '../api/BrowserContext.js';
|
||||
import {BrowserContext, BrowserContextEvent} from '../api/BrowserContext.js';
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {Page} from '../api/Page.js';
|
||||
import {Target} from '../api/Target.js';
|
||||
import {USE_TAB_TARGET} from '../environment.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import {ChromeTargetManager} from './ChromeTargetManager.js';
|
||||
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
|
||||
import {Connection} from './Connection.js';
|
||||
import {FirefoxTargetManager} from './FirefoxTargetManager.js';
|
||||
import {Viewport} from './PuppeteerViewport.js';
|
||||
import {
|
||||
CDPTarget,
|
||||
DevToolsTarget,
|
||||
InitializationStatus,
|
||||
OtherTarget,
|
||||
PageTarget,
|
||||
CDPTarget,
|
||||
WorkerTarget,
|
||||
DevToolsTarget,
|
||||
} from './Target.js';
|
||||
import {TargetManager, TargetManagerEmittedEvents} from './TargetManager.js';
|
||||
import {TargetManager, TargetManagerEvent} from './TargetManager.js';
|
||||
import {TaskQueue} from './TaskQueue.js';
|
||||
|
||||
/**
|
||||
@ -151,52 +151,46 @@ export class CDPBrowser extends BrowserBase {
|
||||
}
|
||||
|
||||
#emitDisconnected = () => {
|
||||
this.emit(BrowserEmittedEvents.Disconnected);
|
||||
this.emit(BrowserEvent.Disconnected, undefined);
|
||||
};
|
||||
|
||||
override async _attach(): Promise<void> {
|
||||
this.#connection.on(
|
||||
ConnectionEmittedEvents.Disconnected,
|
||||
this.#emitDisconnected
|
||||
);
|
||||
this.#connection.on(CDPSessionEvent.Disconnected, this.#emitDisconnected);
|
||||
this.#targetManager.on(
|
||||
TargetManagerEmittedEvents.TargetAvailable,
|
||||
TargetManagerEvent.TargetAvailable,
|
||||
this.#onAttachedToTarget
|
||||
);
|
||||
this.#targetManager.on(
|
||||
TargetManagerEmittedEvents.TargetGone,
|
||||
TargetManagerEvent.TargetGone,
|
||||
this.#onDetachedFromTarget
|
||||
);
|
||||
this.#targetManager.on(
|
||||
TargetManagerEmittedEvents.TargetChanged,
|
||||
TargetManagerEvent.TargetChanged,
|
||||
this.#onTargetChanged
|
||||
);
|
||||
this.#targetManager.on(
|
||||
TargetManagerEmittedEvents.TargetDiscovered,
|
||||
TargetManagerEvent.TargetDiscovered,
|
||||
this.#onTargetDiscovered
|
||||
);
|
||||
await this.#targetManager.initialize();
|
||||
}
|
||||
|
||||
override _detach(): void {
|
||||
this.#connection.off(
|
||||
ConnectionEmittedEvents.Disconnected,
|
||||
this.#emitDisconnected
|
||||
);
|
||||
this.#connection.off(CDPSessionEvent.Disconnected, this.#emitDisconnected);
|
||||
this.#targetManager.off(
|
||||
TargetManagerEmittedEvents.TargetAvailable,
|
||||
TargetManagerEvent.TargetAvailable,
|
||||
this.#onAttachedToTarget
|
||||
);
|
||||
this.#targetManager.off(
|
||||
TargetManagerEmittedEvents.TargetGone,
|
||||
TargetManagerEvent.TargetGone,
|
||||
this.#onDetachedFromTarget
|
||||
);
|
||||
this.#targetManager.off(
|
||||
TargetManagerEmittedEvents.TargetChanged,
|
||||
TargetManagerEvent.TargetChanged,
|
||||
this.#onTargetChanged
|
||||
);
|
||||
this.#targetManager.off(
|
||||
TargetManagerEmittedEvents.TargetDiscovered,
|
||||
TargetManagerEvent.TargetDiscovered,
|
||||
this.#onTargetDiscovered
|
||||
);
|
||||
}
|
||||
@ -367,10 +361,8 @@ export class CDPBrowser extends BrowserBase {
|
||||
(await target._initializedDeferred.valueOrThrow()) ===
|
||||
InitializationStatus.SUCCESS
|
||||
) {
|
||||
this.emit(BrowserEmittedEvents.TargetCreated, target);
|
||||
target
|
||||
.browserContext()
|
||||
.emit(BrowserContextEmittedEvents.TargetCreated, target);
|
||||
this.emit(BrowserEvent.TargetCreated, target);
|
||||
target.browserContext().emit(BrowserContextEvent.TargetCreated, target);
|
||||
}
|
||||
};
|
||||
|
||||
@ -381,22 +373,18 @@ export class CDPBrowser extends BrowserBase {
|
||||
(await target._initializedDeferred.valueOrThrow()) ===
|
||||
InitializationStatus.SUCCESS
|
||||
) {
|
||||
this.emit(BrowserEmittedEvents.TargetDestroyed, target);
|
||||
target
|
||||
.browserContext()
|
||||
.emit(BrowserContextEmittedEvents.TargetDestroyed, target);
|
||||
this.emit(BrowserEvent.TargetDestroyed, target);
|
||||
target.browserContext().emit(BrowserContextEvent.TargetDestroyed, target);
|
||||
}
|
||||
};
|
||||
|
||||
#onTargetChanged = ({target}: {target: CDPTarget}): void => {
|
||||
this.emit(BrowserEmittedEvents.TargetChanged, target);
|
||||
target
|
||||
.browserContext()
|
||||
.emit(BrowserContextEmittedEvents.TargetChanged, target);
|
||||
this.emit(BrowserEvent.TargetChanged, target);
|
||||
target.browserContext().emit(BrowserContextEvent.TargetChanged, target);
|
||||
};
|
||||
|
||||
#onTargetDiscovered = (targetInfo: Protocol.Target.TargetInfo): void => {
|
||||
this.emit('targetdiscovered', targetInfo);
|
||||
this.emit(BrowserEvent.TargetDiscovered, targetInfo);
|
||||
};
|
||||
|
||||
/**
|
||||
|
154
packages/puppeteer-core/src/common/CDPSession.ts
Normal file
154
packages/puppeteer-core/src/common/CDPSession.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||
|
||||
import {CDPEvents, CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import {
|
||||
CallbackRegistry,
|
||||
Connection,
|
||||
createProtocolErrorMessage,
|
||||
} from './Connection.js';
|
||||
import {TargetCloseError} from './Errors.js';
|
||||
import {CDPTarget} from './Target.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export class CDPCDPSession extends CDPSession {
|
||||
#sessionId: string;
|
||||
#targetType: string;
|
||||
#callbacks = new CallbackRegistry();
|
||||
#connection?: Connection;
|
||||
#parentSessionId?: string;
|
||||
#target?: CDPTarget;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(
|
||||
connection: Connection,
|
||||
targetType: string,
|
||||
sessionId: string,
|
||||
parentSessionId: string | undefined
|
||||
) {
|
||||
super();
|
||||
this.#connection = connection;
|
||||
this.#targetType = targetType;
|
||||
this.#sessionId = sessionId;
|
||||
this.#parentSessionId = parentSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CDPTarget associated with the session instance.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_setTarget(target: CDPTarget): void {
|
||||
this.#target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CDPTarget associated with the session instance.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_target(): CDPTarget {
|
||||
assert(this.#target, 'Target must exist');
|
||||
return this.#target;
|
||||
}
|
||||
|
||||
override connection(): Connection | undefined {
|
||||
return this.#connection;
|
||||
}
|
||||
|
||||
override parentSession(): CDPSession | undefined {
|
||||
if (!this.#parentSessionId) {
|
||||
return;
|
||||
}
|
||||
const parent = this.#connection?.session(this.#parentSessionId);
|
||||
return parent ?? undefined;
|
||||
}
|
||||
|
||||
override send<T extends keyof ProtocolMapping.Commands>(
|
||||
method: T,
|
||||
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||
): Promise<ProtocolMapping.Commands[T]['returnType']> {
|
||||
if (!this.#connection) {
|
||||
return Promise.reject(
|
||||
new TargetCloseError(
|
||||
`Protocol error (${method}): Session closed. Most likely the ${
|
||||
this.#targetType
|
||||
} has been closed.`
|
||||
)
|
||||
);
|
||||
}
|
||||
// See the comment in Connection#send explaining why we do this.
|
||||
const params = paramArgs.length ? paramArgs[0] : undefined;
|
||||
return this.#connection._rawSend(
|
||||
this.#callbacks,
|
||||
method,
|
||||
params,
|
||||
this.#sessionId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_onMessage(object: {
|
||||
id?: number;
|
||||
method: keyof CDPEvents;
|
||||
params: CDPEvents[keyof CDPEvents];
|
||||
error: {message: string; data: any; code: number};
|
||||
result?: any;
|
||||
}): void {
|
||||
if (object.id) {
|
||||
if (object.error) {
|
||||
this.#callbacks.reject(
|
||||
object.id,
|
||||
createProtocolErrorMessage(object),
|
||||
object.error.message
|
||||
);
|
||||
} else {
|
||||
this.#callbacks.resolve(object.id, object.result);
|
||||
}
|
||||
} else {
|
||||
assert(!object.id);
|
||||
this.emit(object.method, object.params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the cdpSession from the target. Once detached, the cdpSession object
|
||||
* won't emit any events and can't be used to send messages.
|
||||
*/
|
||||
override async detach(): Promise<void> {
|
||||
if (!this.#connection) {
|
||||
throw new Error(
|
||||
`Session already detached. Most likely the ${
|
||||
this.#targetType
|
||||
} has been closed.`
|
||||
);
|
||||
}
|
||||
await this.#connection.send('Target.detachFromTarget', {
|
||||
sessionId: this.#sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_onClosed(): void {
|
||||
this.#callbacks.clear();
|
||||
this.#connection = undefined;
|
||||
this.emit(CDPSessionEvent.Disconnected, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session's id.
|
||||
*/
|
||||
override id(): string {
|
||||
return this.#sessionId;
|
||||
}
|
||||
}
|
@ -17,17 +17,19 @@
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {TargetFilterCallback} from '../api/Browser.js';
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {TargetType} from '../api/Target.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {CDPSession, CDPSessionEmittedEvents, Connection} from './Connection.js';
|
||||
import {Connection} from './Connection.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {InitializationStatus, CDPTarget} from './Target.js';
|
||||
import {CDPTarget, InitializationStatus} from './Target.js';
|
||||
import {
|
||||
TargetFactory,
|
||||
TargetManager,
|
||||
TargetManagerEmittedEvents,
|
||||
TargetManagerEvent,
|
||||
TargetManagerEvents,
|
||||
} from './TargetManager.js';
|
||||
import {debugError} from './util.js';
|
||||
|
||||
@ -49,7 +51,10 @@ function isPageTargetBecomingPrimary(
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
export class ChromeTargetManager
|
||||
extends EventEmitter<TargetManagerEvents>
|
||||
implements TargetManager
|
||||
{
|
||||
#connection: Connection;
|
||||
/**
|
||||
* Keeps track of the following events: 'Target.targetCreated',
|
||||
@ -81,7 +86,7 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
|
||||
#attachedToTargetListenersBySession = new WeakMap<
|
||||
CDPSession | Connection,
|
||||
(event: Protocol.Target.AttachedToTargetEvent) => Promise<void>
|
||||
(event: Protocol.Target.AttachedToTargetEvent) => void
|
||||
>();
|
||||
#detachedFromTargetListenersBySession = new WeakMap<
|
||||
CDPSession | Connection,
|
||||
@ -116,7 +121,10 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
this.#connection.on('Target.targetCreated', this.#onTargetCreated);
|
||||
this.#connection.on('Target.targetDestroyed', this.#onTargetDestroyed);
|
||||
this.#connection.on('Target.targetInfoChanged', this.#onTargetInfoChanged);
|
||||
this.#connection.on('sessiondetached', this.#onSessionDetached);
|
||||
this.#connection.on(
|
||||
CDPSessionEvent.SessionDetached,
|
||||
this.#onSessionDetached
|
||||
);
|
||||
this.#setupAttachmentListeners(this.#connection);
|
||||
|
||||
this.#connection
|
||||
@ -176,7 +184,10 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
this.#connection.off('Target.targetCreated', this.#onTargetCreated);
|
||||
this.#connection.off('Target.targetDestroyed', this.#onTargetDestroyed);
|
||||
this.#connection.off('Target.targetInfoChanged', this.#onTargetInfoChanged);
|
||||
this.#connection.off('sessiondetached', this.#onSessionDetached);
|
||||
this.#connection.off(
|
||||
CDPSessionEvent.SessionDetached,
|
||||
this.#onSessionDetached
|
||||
);
|
||||
|
||||
this.#removeAttachmentListeners(this.#connection);
|
||||
}
|
||||
@ -193,7 +204,7 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
|
||||
#setupAttachmentListeners(session: CDPSession | Connection): void {
|
||||
const listener = (event: Protocol.Target.AttachedToTargetEvent) => {
|
||||
return this.#onAttachedToTarget(session, event);
|
||||
void this.#onAttachedToTarget(session, event);
|
||||
};
|
||||
assert(!this.#attachedToTargetListenersBySession.has(session));
|
||||
this.#attachedToTargetListenersBySession.set(session, listener);
|
||||
@ -210,11 +221,9 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
}
|
||||
|
||||
#removeAttachmentListeners(session: CDPSession | Connection): void {
|
||||
if (this.#attachedToTargetListenersBySession.has(session)) {
|
||||
session.off(
|
||||
'Target.attachedToTarget',
|
||||
this.#attachedToTargetListenersBySession.get(session)!
|
||||
);
|
||||
const listener = this.#attachedToTargetListenersBySession.get(session);
|
||||
if (listener) {
|
||||
session.off('Target.attachedToTarget', listener);
|
||||
this.#attachedToTargetListenersBySession.delete(session);
|
||||
}
|
||||
|
||||
@ -237,7 +246,7 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
event.targetInfo
|
||||
);
|
||||
|
||||
this.emit(TargetManagerEmittedEvents.TargetDiscovered, event.targetInfo);
|
||||
this.emit(TargetManagerEvent.TargetDiscovered, event.targetInfo);
|
||||
|
||||
// The connection is already attached to the browser target implicitly,
|
||||
// therefore, no new CDPSession is created and we have special handling
|
||||
@ -263,9 +272,11 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
// Special case for service workers: report TargetGone event when
|
||||
// the worker is destroyed.
|
||||
const target = this.#attachedTargetsByTargetId.get(event.targetId);
|
||||
this.emit(TargetManagerEmittedEvents.TargetGone, target);
|
||||
if (target) {
|
||||
this.emit(TargetManagerEvent.TargetGone, target);
|
||||
this.#attachedTargetsByTargetId.delete(event.targetId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#onTargetInfoChanged = (event: Protocol.Target.TargetInfoChangedEvent) => {
|
||||
@ -301,14 +312,14 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
session,
|
||||
'Target that is being activated is missing a CDPSession.'
|
||||
);
|
||||
session.parentSession()?.emit(CDPSessionEmittedEvents.Swapped, session);
|
||||
session.parentSession()?.emit(CDPSessionEvent.Swapped, session);
|
||||
}
|
||||
|
||||
target._targetInfoChanged(event.targetInfo);
|
||||
|
||||
if (wasInitialized && previousURL !== target.url()) {
|
||||
this.emit(TargetManagerEmittedEvents.TargetChanged, {
|
||||
target: target,
|
||||
this.emit(TargetManagerEvent.TargetChanged, {
|
||||
target,
|
||||
wasInitialized,
|
||||
previousURL,
|
||||
});
|
||||
@ -359,7 +370,7 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
const target = this.#targetFactory(targetInfo);
|
||||
target._initialize();
|
||||
this.#attachedTargetsByTargetId.set(targetInfo.targetId, target);
|
||||
this.emit(TargetManagerEmittedEvents.TargetAvailable, target);
|
||||
this.emit(TargetManagerEvent.TargetAvailable, target);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -398,11 +409,15 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
this.#attachedTargetsBySessionId.set(session.id(), target);
|
||||
}
|
||||
|
||||
parentSession.emit(CDPSessionEmittedEvents.Ready, session);
|
||||
if (parentSession instanceof CDPSession) {
|
||||
parentSession.emit(CDPSessionEvent.Ready, session);
|
||||
} else {
|
||||
parentSession.emit(CDPSessionEvent.Ready, session);
|
||||
}
|
||||
|
||||
this.#targetsIdsForInit.delete(target._targetId);
|
||||
if (!isExistingTarget && isTargetExposed(target)) {
|
||||
this.emit(TargetManagerEmittedEvents.TargetAvailable, target);
|
||||
this.emit(TargetManagerEvent.TargetAvailable, target);
|
||||
}
|
||||
this.#finishInitializationIfReady();
|
||||
|
||||
@ -440,7 +455,7 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
|
||||
|
||||
this.#attachedTargetsByTargetId.delete(target._targetId);
|
||||
if (isTargetExposed(target)) {
|
||||
this.emit(TargetManagerEmittedEvents.TargetGone, target);
|
||||
this.emit(TargetManagerEvent.TargetGone, target);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -17,14 +17,18 @@
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||
|
||||
import {assert} from '../util/assert.js';
|
||||
import {
|
||||
CDPSession,
|
||||
CDPSessionEvent,
|
||||
CDPSessionEvents,
|
||||
} from '../api/CDPSession.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {CDPCDPSession} from './CDPSession.js';
|
||||
import {ConnectionTransport} from './ConnectionTransport.js';
|
||||
import {debug} from './Debug.js';
|
||||
import {TargetCloseError, ProtocolError} from './Errors.js';
|
||||
import {ProtocolError, TargetCloseError} from './Errors.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {CDPTarget} from './Target.js';
|
||||
import {debugError} from './util.js';
|
||||
|
||||
const debugProtocolSend = debug('puppeteer:protocol:SEND ►');
|
||||
@ -35,15 +39,6 @@ const debugProtocolReceive = debug('puppeteer:protocol:RECV ◀');
|
||||
*/
|
||||
export {ConnectionTransport, ProtocolMapping};
|
||||
|
||||
/**
|
||||
* Internal events that the Connection class emits.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const ConnectionEmittedEvents = {
|
||||
Disconnected: Symbol('Connection.Disconnected'),
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -200,12 +195,12 @@ export class CallbackRegistry {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export class Connection extends EventEmitter {
|
||||
export class Connection extends EventEmitter<CDPSessionEvents> {
|
||||
#url: string;
|
||||
#transport: ConnectionTransport;
|
||||
#delay: number;
|
||||
#timeout: number;
|
||||
#sessions = new Map<string, CDPSessionImpl>();
|
||||
#sessions = new Map<string, CDPCDPSession>();
|
||||
#closed = false;
|
||||
#manuallyAttached = new Set<string>();
|
||||
#callbacks = new CallbackRegistry();
|
||||
@ -315,27 +310,27 @@ export class Connection extends EventEmitter {
|
||||
const object = JSON.parse(message);
|
||||
if (object.method === 'Target.attachedToTarget') {
|
||||
const sessionId = object.params.sessionId;
|
||||
const session = new CDPSessionImpl(
|
||||
const session = new CDPCDPSession(
|
||||
this,
|
||||
object.params.targetInfo.type,
|
||||
sessionId,
|
||||
object.sessionId
|
||||
);
|
||||
this.#sessions.set(sessionId, session);
|
||||
this.emit('sessionattached', session);
|
||||
this.emit(CDPSessionEvent.SessionAttached, session);
|
||||
const parentSession = this.#sessions.get(object.sessionId);
|
||||
if (parentSession) {
|
||||
parentSession.emit('sessionattached', session);
|
||||
parentSession.emit(CDPSessionEvent.SessionAttached, session);
|
||||
}
|
||||
} else if (object.method === 'Target.detachedFromTarget') {
|
||||
const session = this.#sessions.get(object.params.sessionId);
|
||||
if (session) {
|
||||
session._onClosed();
|
||||
this.#sessions.delete(object.params.sessionId);
|
||||
this.emit('sessiondetached', session);
|
||||
this.emit(CDPSessionEvent.SessionDetached, session);
|
||||
const parentSession = this.#sessions.get(object.sessionId);
|
||||
if (parentSession) {
|
||||
parentSession.emit('sessiondetached', session);
|
||||
parentSession.emit(CDPSessionEvent.SessionDetached, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -371,7 +366,7 @@ export class Connection extends EventEmitter {
|
||||
session._onClosed();
|
||||
}
|
||||
this.#sessions.clear();
|
||||
this.emit(ConnectionEmittedEvents.Disconnected);
|
||||
this.emit(CDPSessionEvent.Disconnected, undefined);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@ -422,240 +417,7 @@ export class Connection extends EventEmitter {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface CDPSessionOnMessageObject {
|
||||
id?: number;
|
||||
method: string;
|
||||
params: Record<string, unknown>;
|
||||
error: {message: string; data: any; code: number};
|
||||
result?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal events that the CDPSession class emits.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const CDPSessionEmittedEvents = {
|
||||
Disconnected: Symbol('CDPSession.Disconnected'),
|
||||
Swapped: Symbol('CDPSession.Swapped'),
|
||||
/**
|
||||
* Emitted when the session is ready to be configured during the auto-attach
|
||||
* process. Right after the event is handled, the session will be resumed.
|
||||
*/
|
||||
Ready: Symbol('CDPSession.Ready'),
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* The `CDPSession` instances are used to talk raw Chrome Devtools Protocol.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Protocol methods can be called with {@link CDPSession.send} method and protocol
|
||||
* events can be subscribed to with `CDPSession.on` method.
|
||||
*
|
||||
* Useful links: {@link https://chromedevtools.github.io/devtools-protocol/ | DevTools Protocol Viewer}
|
||||
* and {@link https://github.com/aslushnikov/getting-started-with-cdp/blob/HEAD/README.md | Getting Started with DevTools Protocol}.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const client = await page.target().createCDPSession();
|
||||
* await client.send('Animation.enable');
|
||||
* client.on('Animation.animationCreated', () =>
|
||||
* console.log('Animation created!')
|
||||
* );
|
||||
* const response = await client.send('Animation.getPlaybackRate');
|
||||
* console.log('playback rate is ' + response.playbackRate);
|
||||
* await client.send('Animation.setPlaybackRate', {
|
||||
* playbackRate: response.playbackRate / 2,
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class CDPSession extends EventEmitter {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connection(): Connection | undefined {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent session in terms of CDP's auto-attach mechanism.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
parentSession(): CDPSession | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
send<T extends keyof ProtocolMapping.Commands>(
|
||||
method: T,
|
||||
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||
): Promise<ProtocolMapping.Commands[T]['returnType']>;
|
||||
send<T extends keyof ProtocolMapping.Commands>(): Promise<
|
||||
ProtocolMapping.Commands[T]['returnType']
|
||||
> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the cdpSession from the target. Once detached, the cdpSession object
|
||||
* won't emit any events and can't be used to send messages.
|
||||
*/
|
||||
async detach(): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session's id.
|
||||
*/
|
||||
id(): string {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class CDPSessionImpl extends CDPSession {
|
||||
#sessionId: string;
|
||||
#targetType: string;
|
||||
#callbacks = new CallbackRegistry();
|
||||
#connection?: Connection;
|
||||
#parentSessionId?: string;
|
||||
#target?: CDPTarget;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(
|
||||
connection: Connection,
|
||||
targetType: string,
|
||||
sessionId: string,
|
||||
parentSessionId: string | undefined
|
||||
) {
|
||||
super();
|
||||
this.#connection = connection;
|
||||
this.#targetType = targetType;
|
||||
this.#sessionId = sessionId;
|
||||
this.#parentSessionId = parentSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CDPTarget associated with the session instance.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_setTarget(target: CDPTarget): void {
|
||||
this.#target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CDPTarget associated with the session instance.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_target(): CDPTarget {
|
||||
assert(this.#target, 'Target must exist');
|
||||
return this.#target;
|
||||
}
|
||||
|
||||
override connection(): Connection | undefined {
|
||||
return this.#connection;
|
||||
}
|
||||
|
||||
override parentSession(): CDPSession | undefined {
|
||||
if (!this.#parentSessionId) {
|
||||
return;
|
||||
}
|
||||
const parent = this.#connection?.session(this.#parentSessionId);
|
||||
return parent ?? undefined;
|
||||
}
|
||||
|
||||
override send<T extends keyof ProtocolMapping.Commands>(
|
||||
method: T,
|
||||
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||
): Promise<ProtocolMapping.Commands[T]['returnType']> {
|
||||
if (!this.#connection) {
|
||||
return Promise.reject(
|
||||
new TargetCloseError(
|
||||
`Protocol error (${method}): Session closed. Most likely the ${
|
||||
this.#targetType
|
||||
} has been closed.`
|
||||
)
|
||||
);
|
||||
}
|
||||
// See the comment in Connection#send explaining why we do this.
|
||||
const params = paramArgs.length ? paramArgs[0] : undefined;
|
||||
return this.#connection._rawSend(
|
||||
this.#callbacks,
|
||||
method,
|
||||
params,
|
||||
this.#sessionId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_onMessage(object: CDPSessionOnMessageObject): void {
|
||||
if (object.id) {
|
||||
if (object.error) {
|
||||
this.#callbacks.reject(
|
||||
object.id,
|
||||
createProtocolErrorMessage(object),
|
||||
object.error.message
|
||||
);
|
||||
} else {
|
||||
this.#callbacks.resolve(object.id, object.result);
|
||||
}
|
||||
} else {
|
||||
assert(!object.id);
|
||||
this.emit(object.method, object.params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the cdpSession from the target. Once detached, the cdpSession object
|
||||
* won't emit any events and can't be used to send messages.
|
||||
*/
|
||||
override async detach(): Promise<void> {
|
||||
if (!this.#connection) {
|
||||
throw new Error(
|
||||
`Session already detached. Most likely the ${
|
||||
this.#targetType
|
||||
} has been closed.`
|
||||
);
|
||||
}
|
||||
await this.#connection.send('Target.detachFromTarget', {
|
||||
sessionId: this.#sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_onClosed(): void {
|
||||
this.#callbacks.clear();
|
||||
this.#connection = undefined;
|
||||
this.emit(CDPSessionEmittedEvents.Disconnected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session's id.
|
||||
*/
|
||||
override id(): string {
|
||||
return this.#sessionId;
|
||||
}
|
||||
}
|
||||
|
||||
function createProtocolErrorMessage(object: {
|
||||
export function createProtocolErrorMessage(object: {
|
||||
error: {message: string; data: any; code: number};
|
||||
}): string {
|
||||
let message = `${object.error.message}`;
|
||||
|
@ -16,21 +16,11 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {
|
||||
addEventListener,
|
||||
debugError,
|
||||
PuppeteerEventListener,
|
||||
PuppeteerURL,
|
||||
removeEventListeners,
|
||||
} from './util.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export {PuppeteerEventListener};
|
||||
import {EventSubscription} from './EventEmitter.js';
|
||||
import {debugError, PuppeteerURL} from './util.js';
|
||||
|
||||
/**
|
||||
* The CoverageEntry class represents one entry of the coverage report.
|
||||
@ -211,7 +201,7 @@ export class JSCoverage {
|
||||
#enabled = false;
|
||||
#scriptURLs = new Map<string, string>();
|
||||
#scriptSources = new Map<string, string>();
|
||||
#eventListeners: PuppeteerEventListener[] = [];
|
||||
#subscriptions?: DisposableStack;
|
||||
#resetOnNavigation = false;
|
||||
#reportAnonymousScripts = false;
|
||||
#includeRawScriptCoverage = false;
|
||||
@ -248,18 +238,21 @@ export class JSCoverage {
|
||||
this.#enabled = true;
|
||||
this.#scriptURLs.clear();
|
||||
this.#scriptSources.clear();
|
||||
this.#eventListeners = [
|
||||
addEventListener(
|
||||
this.#subscriptions = new DisposableStack();
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
this.#client,
|
||||
'Debugger.scriptParsed',
|
||||
this.#onScriptParsed.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
this.#client,
|
||||
'Runtime.executionContextsCleared',
|
||||
this.#onExecutionContextsCleared.bind(this)
|
||||
),
|
||||
];
|
||||
)
|
||||
);
|
||||
await Promise.all([
|
||||
this.#client.send('Profiler.enable'),
|
||||
this.#client.send('Profiler.startPreciseCoverage', {
|
||||
@ -313,7 +306,7 @@ export class JSCoverage {
|
||||
this.#client.send('Debugger.disable'),
|
||||
]);
|
||||
|
||||
removeEventListeners(this.#eventListeners);
|
||||
this.#subscriptions?.dispose();
|
||||
|
||||
const coverage = [];
|
||||
const profileResponse = result[0];
|
||||
@ -350,7 +343,7 @@ export class CSSCoverage {
|
||||
#enabled = false;
|
||||
#stylesheetURLs = new Map<string, string>();
|
||||
#stylesheetSources = new Map<string, string>();
|
||||
#eventListeners: PuppeteerEventListener[] = [];
|
||||
#eventListeners?: DisposableStack;
|
||||
#resetOnNavigation = false;
|
||||
|
||||
constructor(client: CDPSession) {
|
||||
@ -371,18 +364,21 @@ export class CSSCoverage {
|
||||
this.#enabled = true;
|
||||
this.#stylesheetURLs.clear();
|
||||
this.#stylesheetSources.clear();
|
||||
this.#eventListeners = [
|
||||
addEventListener(
|
||||
this.#eventListeners = new DisposableStack();
|
||||
this.#eventListeners.use(
|
||||
new EventSubscription(
|
||||
this.#client,
|
||||
'CSS.styleSheetAdded',
|
||||
this.#onStyleSheet.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#eventListeners.use(
|
||||
new EventSubscription(
|
||||
this.#client,
|
||||
'Runtime.executionContextsCleared',
|
||||
this.#onExecutionContextsCleared.bind(this)
|
||||
),
|
||||
];
|
||||
)
|
||||
);
|
||||
await Promise.all([
|
||||
this.#client.send('DOM.enable'),
|
||||
this.#client.send('CSS.enable'),
|
||||
@ -426,7 +422,7 @@ export class CSSCoverage {
|
||||
this.#client.send('CSS.disable'),
|
||||
this.#client.send('DOM.disable'),
|
||||
]);
|
||||
removeEventListeners(this.#eventListeners);
|
||||
this.#eventListeners?.dispose();
|
||||
|
||||
// aggregate by styleSheetId
|
||||
const styleSheetIdToCoverage = new Map();
|
||||
|
@ -18,6 +18,8 @@ import {describe, it} from 'node:test';
|
||||
|
||||
import expect from 'expect';
|
||||
|
||||
import {CDPSessionEvents} from '../api/CDPSession.js';
|
||||
|
||||
import {
|
||||
DeviceRequestPrompt,
|
||||
DeviceRequestPromptDevice,
|
||||
@ -27,7 +29,7 @@ import {TimeoutError} from './Errors.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||
|
||||
class MockCDPSession extends EventEmitter {
|
||||
class MockCDPSession extends EventEmitter<CDPSessionEvents> {
|
||||
async send(): Promise<any> {}
|
||||
connection() {
|
||||
return undefined;
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
import Protocol from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {WaitTimeoutOptions} from '../api/Page.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||
|
||||
/**
|
||||
|
@ -16,10 +16,9 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Dialog} from '../api/Dialog.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {AutofillData, ElementHandle, Point} from '../api/ElementHandle.js';
|
||||
import {Page, ScreenshotOptions} from '../api/Page.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {throwIfDisposed} from '../util/decorators.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {CDPFrame} from './Frame.js';
|
||||
import {FrameManager} from './FrameManager.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
|
@ -15,12 +15,12 @@
|
||||
*/
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {GeolocationOptions, MediaFeature} from '../api/Page.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {invokeAtMostOnceForArguments} from '../util/decorators.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
|
||||
import {CDPSession, CDPSessionEmittedEvents} from './Connection.js';
|
||||
import {Viewport} from './PuppeteerViewport.js';
|
||||
import {debugError} from './util.js';
|
||||
|
||||
@ -168,8 +168,8 @@ export class EmulationManager {
|
||||
|
||||
async registerSpeculativeSession(client: CDPSession): Promise<void> {
|
||||
this.#secondaryClients.add(client);
|
||||
client.once(CDPSessionEmittedEvents.Disconnected, () => {
|
||||
return this.#secondaryClients.delete(client);
|
||||
client.once(CDPSessionEvent.Disconnected, () => {
|
||||
this.#secondaryClients.delete(client);
|
||||
});
|
||||
// We don't await here because we want to register all state changes before
|
||||
// the target is unpaused.
|
||||
|
@ -22,7 +22,7 @@ import sinon from 'sinon';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
|
||||
describe('EventEmitter', () => {
|
||||
let emitter: EventEmitter;
|
||||
let emitter: EventEmitter<Record<string, unknown>>;
|
||||
|
||||
beforeEach(() => {
|
||||
emitter = new EventEmitter();
|
||||
@ -33,7 +33,7 @@ describe('EventEmitter', () => {
|
||||
it(`${methodName}: adds an event listener that is fired when the event is emitted`, () => {
|
||||
const listener = sinon.spy();
|
||||
emitter[methodName]('foo', listener);
|
||||
emitter.emit('foo');
|
||||
emitter.emit('foo', undefined);
|
||||
expect(listener.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
@ -62,10 +62,10 @@ describe('EventEmitter', () => {
|
||||
it(`${methodName}: removes the listener so it is no longer called`, () => {
|
||||
const listener = sinon.spy();
|
||||
emitter.on('foo', listener);
|
||||
emitter.emit('foo');
|
||||
emitter.emit('foo', undefined);
|
||||
expect(listener.callCount).toEqual(1);
|
||||
emitter.off('foo', listener);
|
||||
emitter.emit('foo');
|
||||
emitter.emit('foo', undefined);
|
||||
expect(listener.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
@ -85,9 +85,9 @@ describe('EventEmitter', () => {
|
||||
it('only calls the listener once and then removes it', () => {
|
||||
const listener = sinon.spy();
|
||||
emitter.once('foo', listener);
|
||||
emitter.emit('foo');
|
||||
emitter.emit('foo', undefined);
|
||||
expect(listener.callCount).toEqual(1);
|
||||
emitter.emit('foo');
|
||||
emitter.emit('foo', undefined);
|
||||
expect(listener.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
@ -105,7 +105,7 @@ describe('EventEmitter', () => {
|
||||
const listener3 = sinon.spy();
|
||||
emitter.on('foo', listener1).on('foo', listener2).on('bar', listener3);
|
||||
|
||||
emitter.emit('foo');
|
||||
emitter.emit('foo', undefined);
|
||||
|
||||
expect(listener1.callCount).toEqual(1);
|
||||
expect(listener2.callCount).toEqual(1);
|
||||
@ -125,13 +125,13 @@ describe('EventEmitter', () => {
|
||||
it('returns true if the event has listeners', () => {
|
||||
const listener = sinon.spy();
|
||||
emitter.on('foo', listener);
|
||||
expect(emitter.emit('foo')).toBe(true);
|
||||
expect(emitter.emit('foo', undefined)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if the event has listeners', () => {
|
||||
const listener = sinon.spy();
|
||||
emitter.on('foo', listener);
|
||||
expect(emitter.emit('notFoo')).toBe(false);
|
||||
expect(emitter.emit('notFoo', undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -151,8 +151,8 @@ describe('EventEmitter', () => {
|
||||
emitter.on('foo', () => {}).on('bar', () => {});
|
||||
|
||||
emitter.removeAllListeners();
|
||||
expect(emitter.emit('foo')).toBe(false);
|
||||
expect(emitter.emit('bar')).toBe(false);
|
||||
expect(emitter.emit('foo', undefined)).toBe(false);
|
||||
expect(emitter.emit('bar', undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns the emitter for chaining', () => {
|
||||
@ -166,8 +166,8 @@ describe('EventEmitter', () => {
|
||||
.on('bar', () => {});
|
||||
|
||||
emitter.removeAllListeners('bar');
|
||||
expect(emitter.emit('foo')).toBe(true);
|
||||
expect(emitter.emit('bar')).toBe(false);
|
||||
expect(emitter.emit('foo', undefined)).toBe(true);
|
||||
expect(emitter.emit('bar', undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -14,12 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mitt, {Emitter, EventHandlerMap} from '../../third_party/mitt/index.js';
|
||||
import {Symbol} from '../../third_party/disposablestack/disposablestack.js';
|
||||
import mitt, {
|
||||
Emitter,
|
||||
EventHandlerMap,
|
||||
EventType,
|
||||
} from '../../third_party/mitt/index.js';
|
||||
|
||||
export {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type EventType = string | symbol;
|
||||
EventType,
|
||||
} from '../../third_party/mitt/index.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -28,22 +36,42 @@ export type Handler<T = unknown> = (event: T) => void;
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CommonEventEmitter {
|
||||
on(event: EventType, handler: Handler): this;
|
||||
off(event: EventType, handler: Handler): this;
|
||||
export interface CommonEventEmitter<Events extends Record<EventType, unknown>> {
|
||||
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): this;
|
||||
off<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler?: Handler<Events[Key]>
|
||||
): this;
|
||||
emit<Key extends keyof Events>(type: Key, event: Events[Key]): boolean;
|
||||
/* To maintain parity with the built in NodeJS event emitter which uses removeListener
|
||||
* rather than `off`.
|
||||
* If you're implementing new code you should use `off`.
|
||||
*/
|
||||
addListener(event: EventType, handler: Handler): this;
|
||||
removeListener(event: EventType, handler: Handler): this;
|
||||
emit(event: EventType, eventData?: unknown): boolean;
|
||||
once(event: EventType, handler: Handler): this;
|
||||
listenerCount(event: string): number;
|
||||
addListener<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
): this;
|
||||
removeListener<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
): this;
|
||||
once<Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
): this;
|
||||
listenerCount(event: keyof Events): number;
|
||||
|
||||
removeAllListeners(event?: EventType): this;
|
||||
removeAllListeners(event?: keyof Events): this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type EventsWithWildcard<Events extends Record<EventType, unknown>> =
|
||||
Events & {
|
||||
'*': Events[keyof Events];
|
||||
};
|
||||
|
||||
/**
|
||||
* The EventEmitter class that many Puppeteer classes extend.
|
||||
*
|
||||
@ -56,110 +84,153 @@ export interface CommonEventEmitter {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class EventEmitter implements CommonEventEmitter {
|
||||
private emitter: Emitter<Record<string | symbol, any>>;
|
||||
private eventsMap: EventHandlerMap<Record<string | symbol, any>> = new Map();
|
||||
export class EventEmitter<Events extends Record<EventType, unknown>>
|
||||
implements CommonEventEmitter<EventsWithWildcard<Events>>
|
||||
{
|
||||
#emitter: Emitter<Events & {'*': Events[keyof Events]}>;
|
||||
#handlers: EventHandlerMap<Events & {'*': Events[keyof Events]}> = new Map();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {
|
||||
this.emitter = mitt(this.eventsMap);
|
||||
this.#emitter = mitt(this.#handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an event listener to fire when an event occurs.
|
||||
* @param event - the event type you'd like to listen to. Can be a string or symbol.
|
||||
* @param type - the event type you'd like to listen to. Can be a string or symbol.
|
||||
* @param handler - the function to be called when the event occurs.
|
||||
* @returns `this` to enable you to chain method calls.
|
||||
*/
|
||||
on(event: EventType, handler: Handler<any>): this {
|
||||
this.emitter.on(event, handler);
|
||||
on<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this {
|
||||
this.#emitter.on(type, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event listener from firing.
|
||||
* @param event - the event type you'd like to stop listening to.
|
||||
* @param type - the event type you'd like to stop listening to.
|
||||
* @param handler - the function that should be removed.
|
||||
* @returns `this` to enable you to chain method calls.
|
||||
*/
|
||||
off(event: EventType, handler: Handler<any>): this {
|
||||
this.emitter.off(event, handler);
|
||||
off<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler?: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this {
|
||||
this.#emitter.off(type, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event listener.
|
||||
*
|
||||
* @deprecated please use {@link EventEmitter.off} instead.
|
||||
*/
|
||||
removeListener(event: EventType, handler: Handler<any>): this {
|
||||
this.off(event, handler);
|
||||
removeListener<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this {
|
||||
this.off(type, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event listener.
|
||||
*
|
||||
* @deprecated please use {@link EventEmitter.on} instead.
|
||||
*/
|
||||
addListener(event: EventType, handler: Handler<any>): this {
|
||||
this.on(event, handler);
|
||||
addListener<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this {
|
||||
this.on(type, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event and call any associated listeners.
|
||||
*
|
||||
* @param event - the event you'd like to emit
|
||||
* @param type - the event you'd like to emit
|
||||
* @param eventData - any data you'd like to emit with the event
|
||||
* @returns `true` if there are any listeners, `false` if there are not.
|
||||
*/
|
||||
emit(event: EventType, eventData?: unknown): boolean {
|
||||
this.emitter.emit(event, eventData);
|
||||
return this.eventListenersCount(event) > 0;
|
||||
emit<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
event: EventsWithWildcard<Events>[Key]
|
||||
): boolean {
|
||||
this.#emitter.emit(type, event);
|
||||
return this.listenerCount(type) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `on` but the listener will only be fired once and then it will be removed.
|
||||
* @param event - the event you'd like to listen to
|
||||
* @param type - the event you'd like to listen to
|
||||
* @param handler - the handler function to run when the event occurs
|
||||
* @returns `this` to enable you to chain method calls.
|
||||
*/
|
||||
once(event: EventType, handler: Handler<any>): this {
|
||||
const onceHandler: Handler<any> = eventData => {
|
||||
once<Key extends keyof EventsWithWildcard<Events>>(
|
||||
type: Key,
|
||||
handler: Handler<EventsWithWildcard<Events>[Key]>
|
||||
): this {
|
||||
const onceHandler: Handler<EventsWithWildcard<Events>[Key]> = eventData => {
|
||||
handler(eventData);
|
||||
this.off(event, onceHandler);
|
||||
this.off(type, onceHandler);
|
||||
};
|
||||
|
||||
return this.on(event, onceHandler);
|
||||
return this.on(type, onceHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of listeners for a given event.
|
||||
*
|
||||
* @param event - the event to get the listener count for
|
||||
* @param type - the event to get the listener count for
|
||||
* @returns the number of listeners bound to the given event
|
||||
*/
|
||||
listenerCount(event: EventType): number {
|
||||
return this.eventListenersCount(event);
|
||||
listenerCount(type: keyof EventsWithWildcard<Events>): number {
|
||||
return this.#handlers.get(type)?.length || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners. If given an event argument, it will remove only
|
||||
* listeners for that event.
|
||||
* @param event - the event to remove listeners for.
|
||||
*
|
||||
* @param type - the event to remove listeners for.
|
||||
* @returns `this` to enable you to chain method calls.
|
||||
*/
|
||||
removeAllListeners(event?: EventType): this {
|
||||
if (event) {
|
||||
this.eventsMap.delete(event);
|
||||
removeAllListeners(type?: keyof EventsWithWildcard<Events>): this {
|
||||
if (type === undefined || type === '*') {
|
||||
this.#handlers.clear();
|
||||
} else {
|
||||
this.eventsMap.clear();
|
||||
this.#handlers.delete(type);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private eventListenersCount(event: EventType): number {
|
||||
return this.eventsMap.get(event)?.length || 0;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class EventSubscription<
|
||||
Target extends CommonEventEmitter<Record<Type, Event>>,
|
||||
Type extends EventType = EventType,
|
||||
Event = unknown,
|
||||
> {
|
||||
#target: Target;
|
||||
#type: Type;
|
||||
#handler: Handler<Event>;
|
||||
|
||||
constructor(target: Target, type: Type, handler: Handler<Event>) {
|
||||
this.#target = target;
|
||||
this.#type = type;
|
||||
this.#handler = handler;
|
||||
this.#target.on(this.#type, this.#handler);
|
||||
}
|
||||
|
||||
[Symbol.dispose](): void {
|
||||
this.#target.off(this.#type, this.#handler);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import type {ElementHandle} from '../api/ElementHandle.js';
|
||||
import {JSHandle} from '../api/JSHandle.js';
|
||||
import type PuppeteerUtil from '../injected/injected.js';
|
||||
@ -24,7 +25,6 @@ import {stringifyFunction} from '../util/Function.js';
|
||||
|
||||
import {ARIAQueryHandler} from './AriaQueryHandler.js';
|
||||
import {Binding} from './Binding.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {CDPElementHandle} from './ElementHandle.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
import {CDPJSHandle} from './JSHandle.js';
|
||||
@ -47,25 +47,6 @@ const getSourceUrlComment = (url: string) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a context for JavaScript execution.
|
||||
*
|
||||
* @example
|
||||
* A {@link Page} can have several execution contexts:
|
||||
*
|
||||
* - Each {@link Frame} of a {@link Page | page} has a "default" execution
|
||||
* context that is always created after frame is attached to DOM. This context
|
||||
* is returned by the {@link Frame.realm} method.
|
||||
* - Each {@link https://developer.chrome.com/extensions | Chrome extensions}
|
||||
* creates additional execution contexts to isolate their code.
|
||||
*
|
||||
* @remarks
|
||||
* By definition, each context is isolated from one another, however they are
|
||||
* all able to manipulate non-JavaScript resources (such as DOM).
|
||||
*
|
||||
* @remarks
|
||||
* Besides pages, execution contexts can be found in
|
||||
* {@link WebWorker | workers}.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class ExecutionContext {
|
||||
|
@ -17,16 +17,18 @@
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {TargetFilterCallback} from '../api/Browser.js';
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {CDPSession, CDPSessionEmittedEvents, Connection} from './Connection.js';
|
||||
import {Connection} from './Connection.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {CDPTarget} from './Target.js';
|
||||
import {
|
||||
TargetFactory,
|
||||
TargetManagerEmittedEvents,
|
||||
TargetManagerEvent,
|
||||
TargetManager,
|
||||
TargetManagerEvents,
|
||||
} from './TargetManager.js';
|
||||
|
||||
/**
|
||||
@ -44,7 +46,7 @@ import {
|
||||
* @internal
|
||||
*/
|
||||
export class FirefoxTargetManager
|
||||
extends EventEmitter
|
||||
extends EventEmitter<TargetManagerEvents>
|
||||
implements TargetManager
|
||||
{
|
||||
#connection: Connection;
|
||||
@ -98,7 +100,10 @@ export class FirefoxTargetManager
|
||||
|
||||
this.#connection.on('Target.targetCreated', this.#onTargetCreated);
|
||||
this.#connection.on('Target.targetDestroyed', this.#onTargetDestroyed);
|
||||
this.#connection.on('sessiondetached', this.#onSessionDetached);
|
||||
this.#connection.on(
|
||||
CDPSessionEvent.SessionDetached,
|
||||
this.#onSessionDetached
|
||||
);
|
||||
this.setupAttachmentListeners(this.#connection);
|
||||
}
|
||||
|
||||
@ -172,7 +177,7 @@ export class FirefoxTargetManager
|
||||
}
|
||||
target._initialize();
|
||||
this.#availableTargetsByTargetId.set(event.targetInfo.targetId, target);
|
||||
this.emit(TargetManagerEmittedEvents.TargetAvailable, target);
|
||||
this.emit(TargetManagerEvent.TargetAvailable, target);
|
||||
this.#finishInitializationIfReady(target._targetId);
|
||||
};
|
||||
|
||||
@ -181,7 +186,7 @@ export class FirefoxTargetManager
|
||||
this.#finishInitializationIfReady(event.targetId);
|
||||
const target = this.#availableTargetsByTargetId.get(event.targetId);
|
||||
if (target) {
|
||||
this.emit(TargetManagerEmittedEvents.TargetGone, target);
|
||||
this.emit(TargetManagerEvent.TargetGone, target);
|
||||
this.#availableTargetsByTargetId.delete(event.targetId);
|
||||
}
|
||||
};
|
||||
@ -207,7 +212,7 @@ export class FirefoxTargetManager
|
||||
this.#availableTargetsByTargetId.get(targetInfo.targetId)!
|
||||
);
|
||||
|
||||
parentSession.emit(CDPSessionEmittedEvents.Ready, session);
|
||||
parentSession.emit(CDPSessionEvent.Ready, session);
|
||||
};
|
||||
|
||||
#finishInitializationIfReady(targetId: string): void {
|
||||
|
@ -16,14 +16,14 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {Frame, throwIfDetached} from '../api/Frame.js';
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Frame, FrameEvent, throwIfDetached} from '../api/Frame.js';
|
||||
import {HTTPResponse} from '../api/HTTPResponse.js';
|
||||
import {Page, WaitTimeoutOptions} from '../api/Page.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {
|
||||
DeviceRequestPrompt,
|
||||
DeviceRequestPromptManager,
|
||||
@ -34,21 +34,6 @@ import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||
import {setPageContent} from './util.js';
|
||||
|
||||
/**
|
||||
* We use symbols to prevent external parties listening to these events.
|
||||
* They are internal to Puppeteer.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const FrameEmittedEvents = {
|
||||
FrameNavigated: Symbol('Frame.FrameNavigated'),
|
||||
FrameSwapped: Symbol('Frame.FrameSwapped'),
|
||||
LifecycleEvent: Symbol('Frame.LifecycleEvent'),
|
||||
FrameNavigatedWithinDocument: Symbol('Frame.FrameNavigatedWithinDocument'),
|
||||
FrameDetached: Symbol('Frame.FrameDetached'),
|
||||
FrameSwappedByActivation: Symbol('Frame.FrameSwappedByActivation'),
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -80,7 +65,7 @@ export class CDPFrame extends Frame {
|
||||
|
||||
this.updateClient(client);
|
||||
|
||||
this.on(FrameEmittedEvents.FrameSwappedByActivation, () => {
|
||||
this.on(FrameEvent.FrameSwappedByActivation, () => {
|
||||
// Emulate loading process for swapped frames.
|
||||
this._onLoadingStarted();
|
||||
this._onLoadingStopped();
|
||||
|
@ -16,21 +16,19 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {FrameEvent} from '../api/Frame.js';
|
||||
import {Page} from '../api/Page.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
|
||||
import {
|
||||
CDPSession,
|
||||
CDPSessionEmittedEvents,
|
||||
CDPSessionImpl,
|
||||
isTargetClosedError,
|
||||
} from './Connection.js';
|
||||
import {CDPCDPSession} from './CDPSession.js';
|
||||
import {isTargetClosedError} from './Connection.js';
|
||||
import {DeviceRequestPromptManager} from './DeviceRequestPrompt.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {EventEmitter, EventType} from './EventEmitter.js';
|
||||
import {ExecutionContext} from './ExecutionContext.js';
|
||||
import {CDPFrame, FrameEmittedEvents} from './Frame.js';
|
||||
import {CDPFrame} from './Frame.js';
|
||||
import {FrameTree} from './FrameTree.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||
@ -50,16 +48,30 @@ export const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const FrameManagerEmittedEvents = {
|
||||
FrameAttached: Symbol('FrameManager.FrameAttached'),
|
||||
FrameNavigated: Symbol('FrameManager.FrameNavigated'),
|
||||
FrameDetached: Symbol('FrameManager.FrameDetached'),
|
||||
FrameSwapped: Symbol('FrameManager.FrameSwapped'),
|
||||
LifecycleEvent: Symbol('FrameManager.LifecycleEvent'),
|
||||
FrameNavigatedWithinDocument: Symbol(
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace FrameManagerEvent {
|
||||
export const FrameAttached = Symbol('FrameManager.FrameAttached');
|
||||
export const FrameNavigated = Symbol('FrameManager.FrameNavigated');
|
||||
export const FrameDetached = Symbol('FrameManager.FrameDetached');
|
||||
export const FrameSwapped = Symbol('FrameManager.FrameSwapped');
|
||||
export const LifecycleEvent = Symbol('FrameManager.LifecycleEvent');
|
||||
export const FrameNavigatedWithinDocument = Symbol(
|
||||
'FrameManager.FrameNavigatedWithinDocument'
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export interface FrameManagerEvents extends Record<EventType, unknown> {
|
||||
[FrameManagerEvent.FrameAttached]: CDPFrame;
|
||||
[FrameManagerEvent.FrameNavigated]: CDPFrame;
|
||||
[FrameManagerEvent.FrameDetached]: CDPFrame;
|
||||
[FrameManagerEvent.FrameSwapped]: CDPFrame;
|
||||
[FrameManagerEvent.LifecycleEvent]: CDPFrame;
|
||||
[FrameManagerEvent.FrameNavigatedWithinDocument]: CDPFrame;
|
||||
}
|
||||
|
||||
const TIME_FOR_WAITING_FOR_SWAP = 100; // ms.
|
||||
|
||||
@ -68,7 +80,7 @@ const TIME_FOR_WAITING_FOR_SWAP = 100; // ms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class FrameManager extends EventEmitter {
|
||||
export class FrameManager extends EventEmitter<FrameManagerEvents> {
|
||||
#page: Page;
|
||||
#networkManager: NetworkManager;
|
||||
#timeoutSettings: TimeoutSettings;
|
||||
@ -114,7 +126,7 @@ export class FrameManager extends EventEmitter {
|
||||
this.#networkManager = new NetworkManager(ignoreHTTPSErrors, this);
|
||||
this.#timeoutSettings = timeoutSettings;
|
||||
this.setupEventListeners(this.#client);
|
||||
client.once(CDPSessionEmittedEvents.Disconnected, () => {
|
||||
client.once(CDPSessionEvent.Disconnected, () => {
|
||||
this.#onClientDisconnect().catch(debugError);
|
||||
});
|
||||
}
|
||||
@ -136,7 +148,7 @@ export class FrameManager extends EventEmitter {
|
||||
timeout: TIME_FOR_WAITING_FOR_SWAP,
|
||||
message: 'Frame was not swapped',
|
||||
});
|
||||
mainFrame.once(FrameEmittedEvents.FrameSwappedByActivation, () => {
|
||||
mainFrame.once(FrameEvent.FrameSwappedByActivation, () => {
|
||||
swapped.resolve();
|
||||
});
|
||||
try {
|
||||
@ -156,7 +168,7 @@ export class FrameManager extends EventEmitter {
|
||||
|
||||
this.#client = client;
|
||||
assert(
|
||||
this.#client instanceof CDPSessionImpl,
|
||||
this.#client instanceof CDPCDPSession,
|
||||
'CDPSession is not an instance of CDPSessionImpl.'
|
||||
);
|
||||
const frame = this._frameTree.getMainFrame();
|
||||
@ -170,17 +182,17 @@ export class FrameManager extends EventEmitter {
|
||||
frame.updateClient(client, true);
|
||||
}
|
||||
this.setupEventListeners(client);
|
||||
client.once(CDPSessionEmittedEvents.Disconnected, () => {
|
||||
client.once(CDPSessionEvent.Disconnected, () => {
|
||||
this.#onClientDisconnect().catch(debugError);
|
||||
});
|
||||
await this.initialize(client);
|
||||
await this.#networkManager.addClient(client);
|
||||
if (frame) {
|
||||
frame.emit(FrameEmittedEvents.FrameSwappedByActivation);
|
||||
frame.emit(FrameEvent.FrameSwappedByActivation, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
async registerSpeculativeSession(client: CDPSessionImpl): Promise<void> {
|
||||
async registerSpeculativeSession(client: CDPCDPSession): Promise<void> {
|
||||
await this.#networkManager.addClient(client);
|
||||
}
|
||||
|
||||
@ -312,8 +324,8 @@ export class FrameManager extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
frame._onLifecycleEvent(event.loaderId, event.name);
|
||||
this.emit(FrameManagerEmittedEvents.LifecycleEvent, frame);
|
||||
frame.emit(FrameEmittedEvents.LifecycleEvent);
|
||||
this.emit(FrameManagerEvent.LifecycleEvent, frame);
|
||||
frame.emit(FrameEvent.LifecycleEvent, undefined);
|
||||
}
|
||||
|
||||
#onFrameStartedLoading(frameId: string): void {
|
||||
@ -330,8 +342,8 @@ export class FrameManager extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
frame._onLoadingStopped();
|
||||
this.emit(FrameManagerEmittedEvents.LifecycleEvent, frame);
|
||||
frame.emit(FrameEmittedEvents.LifecycleEvent);
|
||||
this.emit(FrameManagerEvent.LifecycleEvent, frame);
|
||||
frame.emit(FrameEvent.LifecycleEvent, undefined);
|
||||
}
|
||||
|
||||
#handleFrameTree(
|
||||
@ -378,7 +390,7 @@ export class FrameManager extends EventEmitter {
|
||||
|
||||
frame = new CDPFrame(this, frameId, parentFrameId, session);
|
||||
this._frameTree.addFrame(frame);
|
||||
this.emit(FrameManagerEmittedEvents.FrameAttached, frame);
|
||||
this.emit(FrameManagerEvent.FrameAttached, frame);
|
||||
}
|
||||
|
||||
async #onFrameNavigated(
|
||||
@ -412,8 +424,8 @@ export class FrameManager extends EventEmitter {
|
||||
|
||||
frame = await this._frameTree.waitForFrame(frameId);
|
||||
frame._navigated(framePayload);
|
||||
this.emit(FrameManagerEmittedEvents.FrameNavigated, frame);
|
||||
frame.emit(FrameEmittedEvents.FrameNavigated, navigationType);
|
||||
this.emit(FrameManagerEvent.FrameNavigated, frame);
|
||||
frame.emit(FrameEvent.FrameNavigated, navigationType);
|
||||
}
|
||||
|
||||
async #createIsolatedWorld(session: CDPSession, name: string): Promise<void> {
|
||||
@ -455,10 +467,10 @@ export class FrameManager extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
frame._navigatedWithinDocument(url);
|
||||
this.emit(FrameManagerEmittedEvents.FrameNavigatedWithinDocument, frame);
|
||||
frame.emit(FrameEmittedEvents.FrameNavigatedWithinDocument);
|
||||
this.emit(FrameManagerEmittedEvents.FrameNavigated, frame);
|
||||
frame.emit(FrameEmittedEvents.FrameNavigated);
|
||||
this.emit(FrameManagerEvent.FrameNavigatedWithinDocument, frame);
|
||||
frame.emit(FrameEvent.FrameNavigatedWithinDocument, undefined);
|
||||
this.emit(FrameManagerEvent.FrameNavigated, frame);
|
||||
frame.emit(FrameEvent.FrameNavigated, 'Navigation');
|
||||
}
|
||||
|
||||
#onFrameDetached(
|
||||
@ -466,16 +478,20 @@ export class FrameManager extends EventEmitter {
|
||||
reason: Protocol.Page.FrameDetachedEventReason
|
||||
): void {
|
||||
const frame = this.frame(frameId);
|
||||
if (reason === 'remove') {
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
switch (reason) {
|
||||
case 'remove':
|
||||
// Only remove the frame if the reason for the detached event is
|
||||
// an actual removement of the frame.
|
||||
// For frames that become OOP iframes, the reason would be 'swap'.
|
||||
if (frame) {
|
||||
this.#removeFramesRecursively(frame);
|
||||
}
|
||||
} else if (reason === 'swap') {
|
||||
this.emit(FrameManagerEmittedEvents.FrameSwapped, frame);
|
||||
frame?.emit(FrameEmittedEvents.FrameSwapped);
|
||||
break;
|
||||
case 'swap':
|
||||
this.emit(FrameManagerEvent.FrameSwapped, frame);
|
||||
frame.emit(FrameEvent.FrameSwapped, undefined);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -555,7 +571,7 @@ export class FrameManager extends EventEmitter {
|
||||
}
|
||||
frame[Symbol.dispose]();
|
||||
this._frameTree.removeFrame(frame);
|
||||
this.emit(FrameManagerEmittedEvents.FrameDetached, frame);
|
||||
frame.emit(FrameEmittedEvents.FrameDetached, frame);
|
||||
this.emit(FrameManagerEvent.FrameDetached, frame);
|
||||
frame.emit(FrameEvent.FrameDetached, frame);
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,13 @@
|
||||
*/
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Frame} from '../api/Frame.js';
|
||||
import {
|
||||
ContinueRequestOverrides,
|
||||
ErrorCode,
|
||||
headersArray,
|
||||
HTTPRequest as BaseHTTPRequest,
|
||||
HTTPRequest,
|
||||
InterceptResolutionAction,
|
||||
InterceptResolutionState,
|
||||
ResourceType,
|
||||
@ -30,20 +31,19 @@ import {
|
||||
import {HTTPResponse} from '../api/HTTPResponse.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ProtocolError} from './Errors.js';
|
||||
import {debugError, isString} from './util.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class HTTPRequest extends BaseHTTPRequest {
|
||||
export class CDPHTTPRequest extends HTTPRequest {
|
||||
override _requestId: string;
|
||||
override _interceptionId: string | undefined;
|
||||
override _failureText: string | null = null;
|
||||
override _response: HTTPResponse | null = null;
|
||||
override _fromMemoryCache = false;
|
||||
override _redirectChain: HTTPRequest[];
|
||||
override _redirectChain: CDPHTTPRequest[];
|
||||
|
||||
#client: CDPSession;
|
||||
#isNavigationRequest: boolean;
|
||||
@ -100,7 +100,7 @@ export class HTTPRequest extends BaseHTTPRequest {
|
||||
*/
|
||||
type?: Protocol.Network.ResourceType;
|
||||
},
|
||||
redirectChain: HTTPRequest[]
|
||||
redirectChain: CDPHTTPRequest[]
|
||||
) {
|
||||
super();
|
||||
this.#client = client;
|
||||
@ -213,7 +213,7 @@ export class HTTPRequest extends BaseHTTPRequest {
|
||||
return this.#initiator;
|
||||
}
|
||||
|
||||
override redirectChain(): HTTPRequest[] {
|
||||
override redirectChain(): CDPHTTPRequest[] {
|
||||
return this._redirectChain.slice();
|
||||
}
|
||||
|
||||
|
@ -15,24 +15,21 @@
|
||||
*/
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Frame} from '../api/Frame.js';
|
||||
import {
|
||||
HTTPResponse as BaseHTTPResponse,
|
||||
RemoteAddress,
|
||||
} from '../api/HTTPResponse.js';
|
||||
import {HTTPResponse, RemoteAddress} from '../api/HTTPResponse.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ProtocolError} from './Errors.js';
|
||||
import {HTTPRequest} from './HTTPRequest.js';
|
||||
import {CDPHTTPRequest} from './HTTPRequest.js';
|
||||
import {SecurityDetails} from './SecurityDetails.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class HTTPResponse extends BaseHTTPResponse {
|
||||
export class CDPHTTPResponse extends HTTPResponse {
|
||||
#client: CDPSession;
|
||||
#request: HTTPRequest;
|
||||
#request: CDPHTTPRequest;
|
||||
#contentPromise: Promise<Buffer> | null = null;
|
||||
#bodyLoadedDeferred = Deferred.create<Error | void>();
|
||||
#remoteAddress: RemoteAddress;
|
||||
@ -47,7 +44,7 @@ export class HTTPResponse extends BaseHTTPResponse {
|
||||
|
||||
constructor(
|
||||
client: CDPSession,
|
||||
request: HTTPRequest,
|
||||
request: CDPHTTPRequest,
|
||||
responsePayload: Protocol.Network.Response,
|
||||
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
||||
) {
|
||||
@ -171,7 +168,7 @@ export class HTTPResponse extends BaseHTTPResponse {
|
||||
return this.#contentPromise;
|
||||
}
|
||||
|
||||
override request(): HTTPRequest {
|
||||
override request(): CDPHTTPRequest {
|
||||
return this.#request;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Point} from '../api/ElementHandle.js';
|
||||
import {
|
||||
Keyboard,
|
||||
@ -32,7 +33,6 @@ import {
|
||||
} from '../api/Input.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {_keyDefinitions, KeyDefinition, KeyInput} from './USKeyboardLayout.js';
|
||||
|
||||
type KeyDescription = Required<
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {JSHandle} from '../api/JSHandle.js';
|
||||
import {Realm} from '../api/Realm.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {Binding} from './Binding.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ExecutionContext} from './ExecutionContext.js';
|
||||
import {CDPFrame} from './Frame.js';
|
||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {JSHandle} from '../api/JSHandle.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import type {CDPElementHandle} from './ElementHandle.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
import {releaseObject, valueFromRemoteObject} from './util.js';
|
||||
|
@ -16,20 +16,17 @@
|
||||
|
||||
import Protocol from 'devtools-protocol';
|
||||
|
||||
import {Frame, FrameEvent} from '../api/Frame.js';
|
||||
import {HTTPResponse} from '../api/HTTPResponse.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {TimeoutError} from './Errors.js';
|
||||
import {CDPFrame, FrameEmittedEvents} from './Frame.js';
|
||||
import {FrameManagerEmittedEvents} from './FrameManager.js';
|
||||
import {HTTPRequest} from './HTTPRequest.js';
|
||||
import {NetworkManager, NetworkManagerEmittedEvents} from './NetworkManager.js';
|
||||
import {
|
||||
addEventListener,
|
||||
PuppeteerEventListener,
|
||||
removeEventListeners,
|
||||
} from './util.js';
|
||||
import {EventSubscription} from './EventEmitter.js';
|
||||
import {CDPFrame} from './Frame.js';
|
||||
import {FrameManagerEvent} from './FrameManager.js';
|
||||
import {CDPHTTPRequest} from './HTTPRequest.js';
|
||||
import {NetworkManager, NetworkManagerEvent} from './NetworkManager.js';
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -65,8 +62,8 @@ export class LifecycleWatcher {
|
||||
#expectedLifecycle: ProtocolLifeCycleEvent[];
|
||||
#frame: CDPFrame;
|
||||
#timeout: number;
|
||||
#navigationRequest: HTTPRequest | null = null;
|
||||
#eventListeners: PuppeteerEventListener[];
|
||||
#navigationRequest: CDPHTTPRequest | null = null;
|
||||
#subscriptions = new DisposableStack();
|
||||
#initialLoaderId: string;
|
||||
|
||||
#terminationDeferred: Deferred<Error>;
|
||||
@ -99,55 +96,70 @@ export class LifecycleWatcher {
|
||||
|
||||
this.#frame = frame;
|
||||
this.#timeout = timeout;
|
||||
this.#eventListeners = [
|
||||
addEventListener(
|
||||
this.#subscriptions.use(
|
||||
// Revert if TODO #1 is done
|
||||
new EventSubscription(
|
||||
frame._frameManager,
|
||||
FrameManagerEmittedEvents.LifecycleEvent,
|
||||
FrameManagerEvent.LifecycleEvent,
|
||||
this.#checkLifecycleComplete.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
frame,
|
||||
FrameEmittedEvents.FrameNavigatedWithinDocument,
|
||||
FrameEvent.FrameNavigatedWithinDocument,
|
||||
this.#navigatedWithinDocument.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
frame,
|
||||
FrameEmittedEvents.FrameNavigated,
|
||||
FrameEvent.FrameNavigated,
|
||||
this.#navigated.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
frame,
|
||||
FrameEmittedEvents.FrameSwapped,
|
||||
FrameEvent.FrameSwapped,
|
||||
this.#frameSwapped.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
frame,
|
||||
FrameEmittedEvents.FrameSwappedByActivation,
|
||||
FrameEvent.FrameSwappedByActivation,
|
||||
this.#frameSwapped.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
frame,
|
||||
FrameEmittedEvents.FrameDetached,
|
||||
FrameEvent.FrameDetached,
|
||||
this.#onFrameDetached.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
networkManager,
|
||||
NetworkManagerEmittedEvents.Request,
|
||||
NetworkManagerEvent.Request,
|
||||
this.#onRequest.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
networkManager,
|
||||
NetworkManagerEmittedEvents.Response,
|
||||
NetworkManagerEvent.Response,
|
||||
this.#onResponse.bind(this)
|
||||
),
|
||||
addEventListener(
|
||||
)
|
||||
);
|
||||
this.#subscriptions.use(
|
||||
new EventSubscription(
|
||||
networkManager,
|
||||
NetworkManagerEmittedEvents.RequestFailed,
|
||||
NetworkManagerEvent.RequestFailed,
|
||||
this.#onRequestFailed.bind(this)
|
||||
),
|
||||
];
|
||||
|
||||
)
|
||||
);
|
||||
this.#terminationDeferred = Deferred.create<Error>({
|
||||
timeout: this.#timeout,
|
||||
message: `Navigation timeout of ${this.#timeout} ms exceeded`,
|
||||
@ -156,7 +168,7 @@ export class LifecycleWatcher {
|
||||
this.#checkLifecycleComplete();
|
||||
}
|
||||
|
||||
#onRequest(request: HTTPRequest): void {
|
||||
#onRequest(request: CDPHTTPRequest): void {
|
||||
if (request.frame() !== this.#frame || !request.isNavigationRequest()) {
|
||||
return;
|
||||
}
|
||||
@ -171,7 +183,7 @@ export class LifecycleWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
#onRequestFailed(request: HTTPRequest): void {
|
||||
#onRequestFailed(request: CDPHTTPRequest): void {
|
||||
if (this.#navigationRequest?._requestId !== request._requestId) {
|
||||
return;
|
||||
}
|
||||
@ -185,7 +197,7 @@ export class LifecycleWatcher {
|
||||
this.#navigationResponseReceived?.resolve();
|
||||
}
|
||||
|
||||
#onFrameDetached(frame: CDPFrame): void {
|
||||
#onFrameDetached(frame: Frame): void {
|
||||
if (this.#frame === frame) {
|
||||
this.#terminationDeferred.resolve(
|
||||
new Error('Navigating frame was detached')
|
||||
@ -273,7 +285,7 @@ export class LifecycleWatcher {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
removeEventListeners(this.#eventListeners);
|
||||
this.#subscriptions.dispose();
|
||||
this.#terminationDeferred.resolve(new Error('LifecycleWatcher disposed'));
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {HTTPRequest} from './HTTPRequest.js';
|
||||
import {CDPHTTPRequest} from './HTTPRequest.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -92,7 +92,7 @@ export class NetworkEventManager {
|
||||
NetworkRequestId,
|
||||
Protocol.Fetch.RequestPausedEvent
|
||||
>();
|
||||
#httpRequestsMap = new Map<NetworkRequestId, HTTPRequest>();
|
||||
#httpRequestsMap = new Map<NetworkRequestId, CDPHTTPRequest>();
|
||||
|
||||
/*
|
||||
* The below maps are used to reconcile Network.responseReceivedExtraInfo
|
||||
@ -193,11 +193,14 @@ export class NetworkEventManager {
|
||||
this.#requestPausedMap.set(networkRequestId, event);
|
||||
}
|
||||
|
||||
getRequest(networkRequestId: NetworkRequestId): HTTPRequest | undefined {
|
||||
getRequest(networkRequestId: NetworkRequestId): CDPHTTPRequest | undefined {
|
||||
return this.#httpRequestsMap.get(networkRequestId);
|
||||
}
|
||||
|
||||
storeRequest(networkRequestId: NetworkRequestId, request: HTTPRequest): void {
|
||||
storeRequest(
|
||||
networkRequestId: NetworkRequestId,
|
||||
request: CDPHTTPRequest
|
||||
): void {
|
||||
this.#httpRequestsMap.set(networkRequestId, request);
|
||||
}
|
||||
|
||||
|
@ -18,17 +18,18 @@ import {describe, it} from 'node:test';
|
||||
|
||||
import expect from 'expect';
|
||||
|
||||
import {CDPSessionEvents} from '../api/CDPSession.js';
|
||||
import {HTTPRequest} from '../api/HTTPRequest.js';
|
||||
import {HTTPResponse} from '../api/HTTPResponse.js';
|
||||
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {CDPFrame} from './Frame.js';
|
||||
import {NetworkManager, NetworkManagerEmittedEvents} from './NetworkManager.js';
|
||||
import {NetworkManager, NetworkManagerEvent} from './NetworkManager.js';
|
||||
|
||||
// TODO: develop a helper to generate fake network events for attributes that
|
||||
// are not relevant for the network manager to make tests shorter.
|
||||
|
||||
class MockCDPSession extends EventEmitter {
|
||||
class MockCDPSession extends EventEmitter<CDPSessionEvents> {
|
||||
async send(): Promise<any> {}
|
||||
connection() {
|
||||
return undefined;
|
||||
@ -153,6 +154,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 162,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 2111.557593,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -241,6 +243,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 162,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 2111.559346,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -344,6 +347,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 178,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 2111.560482,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -448,6 +452,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 197,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 2111.561759,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -486,13 +491,10 @@ describe('NetworkManager', () => {
|
||||
await manager.setRequestInterception(true);
|
||||
|
||||
const requests: HTTPRequest[] = [];
|
||||
manager.on(
|
||||
NetworkManagerEmittedEvents.Request,
|
||||
async (request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.Request, async (request: HTTPRequest) => {
|
||||
requests.push(request);
|
||||
await request.continue();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* This sequence was taken from an actual CDP session produced by the following
|
||||
@ -519,7 +521,7 @@ describe('NetworkManager', () => {
|
||||
request: {
|
||||
url: 'https://www.google.com/',
|
||||
method: 'GET',
|
||||
headers: [Object],
|
||||
headers: {},
|
||||
mixedContentType: 'none',
|
||||
initialPriority: 'VeryHigh',
|
||||
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||
@ -538,7 +540,7 @@ describe('NetworkManager', () => {
|
||||
request: {
|
||||
url: 'https://www.google.com/',
|
||||
method: 'GET',
|
||||
headers: [Object],
|
||||
headers: {},
|
||||
initialPriority: 'VeryHigh',
|
||||
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||
},
|
||||
@ -551,7 +553,7 @@ describe('NetworkManager', () => {
|
||||
request: {
|
||||
url: 'https://www.google.com/',
|
||||
method: 'GET',
|
||||
headers: [Object],
|
||||
headers: {},
|
||||
initialPriority: 'VeryHigh',
|
||||
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||
},
|
||||
@ -572,12 +574,9 @@ describe('NetworkManager', () => {
|
||||
await manager.addClient(mockCDPSession);
|
||||
|
||||
const requests: HTTPRequest[] = [];
|
||||
manager.on(
|
||||
NetworkManagerEmittedEvents.RequestFinished,
|
||||
(request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.RequestFinished, (request: HTTPRequest) => {
|
||||
requests.push(request);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
mockCDPSession.emit('Network.requestWillBeSent', {
|
||||
requestId: '1360.2',
|
||||
@ -633,6 +632,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 66,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 10959.023904,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -692,14 +692,11 @@ describe('NetworkManager', () => {
|
||||
|
||||
const finishedRequests: HTTPRequest[] = [];
|
||||
const pendingRequests: HTTPRequest[] = [];
|
||||
manager.on(
|
||||
NetworkManagerEmittedEvents.RequestFinished,
|
||||
(request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.RequestFinished, (request: HTTPRequest) => {
|
||||
finishedRequests.push(request);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
manager.on(NetworkManagerEmittedEvents.Request, (request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.Request, (request: HTTPRequest) => {
|
||||
pendingRequests.push(request);
|
||||
});
|
||||
|
||||
@ -756,6 +753,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 197,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 671.232585,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -845,14 +843,11 @@ describe('NetworkManager', () => {
|
||||
|
||||
const responses: HTTPResponse[] = [];
|
||||
const requests: HTTPRequest[] = [];
|
||||
manager.on(
|
||||
NetworkManagerEmittedEvents.Response,
|
||||
(response: HTTPResponse) => {
|
||||
manager.on(NetworkManagerEvent.Response, (response: HTTPResponse) => {
|
||||
responses.push(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
manager.on(NetworkManagerEmittedEvents.Request, (request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.Request, (request: HTTPRequest) => {
|
||||
requests.push(request);
|
||||
});
|
||||
|
||||
@ -966,6 +961,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 197,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 504904.000422,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -1009,14 +1005,11 @@ describe('NetworkManager', () => {
|
||||
|
||||
const responses: HTTPResponse[] = [];
|
||||
const requests: HTTPRequest[] = [];
|
||||
manager.on(
|
||||
NetworkManagerEmittedEvents.Response,
|
||||
(response: HTTPResponse) => {
|
||||
manager.on(NetworkManagerEvent.Response, (response: HTTPResponse) => {
|
||||
responses.push(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
manager.on(NetworkManagerEmittedEvents.Request, (request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.Request, (request: HTTPRequest) => {
|
||||
requests.push(request);
|
||||
});
|
||||
|
||||
@ -1072,6 +1065,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 197,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 510294.106734,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -1156,14 +1150,11 @@ describe('NetworkManager', () => {
|
||||
|
||||
const responses: HTTPResponse[] = [];
|
||||
const requests: HTTPRequest[] = [];
|
||||
manager.on(
|
||||
NetworkManagerEmittedEvents.Response,
|
||||
(response: HTTPResponse) => {
|
||||
manager.on(NetworkManagerEvent.Response, (response: HTTPResponse) => {
|
||||
responses.push(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
manager.on(NetworkManagerEmittedEvents.Request, (request: HTTPRequest) => {
|
||||
manager.on(NetworkManagerEvent.Request, (request: HTTPRequest) => {
|
||||
requests.push(request);
|
||||
});
|
||||
|
||||
@ -1260,6 +1251,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 197,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 31949.959838,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -1432,6 +1424,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 182,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 31949.983605,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
@ -1492,6 +1485,7 @@ describe('NetworkManager', () => {
|
||||
fromPrefetchCache: false,
|
||||
encodedDataLength: 0,
|
||||
timing: {
|
||||
receiveHeadersStart: 0,
|
||||
requestTime: 31949.988855,
|
||||
proxyStart: -1,
|
||||
proxyEnd: -1,
|
||||
|
@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import type {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {Frame} from '../api/Frame.js';
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import type {Frame} from '../api/Frame.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import {CDPSession, CDPSessionEmittedEvents} from './Connection.js';
|
||||
import {EventEmitter, Handler} from './EventEmitter.js';
|
||||
import {HTTPRequest} from './HTTPRequest.js';
|
||||
import {HTTPResponse} from './HTTPResponse.js';
|
||||
import {EventEmitter, EventSubscription, EventType} from './EventEmitter.js';
|
||||
import {CDPHTTPRequest} from './HTTPRequest.js';
|
||||
import {CDPHTTPResponse} from './HTTPResponse.js';
|
||||
import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
|
||||
import {debugError, isString} from './util.js';
|
||||
|
||||
@ -58,13 +58,27 @@ export interface InternalNetworkConditions extends NetworkConditions {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const NetworkManagerEmittedEvents = {
|
||||
Request: Symbol('NetworkManager.Request'),
|
||||
RequestServedFromCache: Symbol('NetworkManager.RequestServedFromCache'),
|
||||
Response: Symbol('NetworkManager.Response'),
|
||||
RequestFailed: Symbol('NetworkManager.RequestFailed'),
|
||||
RequestFinished: Symbol('NetworkManager.RequestFinished'),
|
||||
} as const;
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace NetworkManagerEvent {
|
||||
export const Request = Symbol('NetworkManager.Request');
|
||||
export const RequestServedFromCache = Symbol(
|
||||
'NetworkManager.RequestServedFromCache'
|
||||
);
|
||||
export const Response = Symbol('NetworkManager.Response');
|
||||
export const RequestFailed = Symbol('NetworkManager.RequestFailed');
|
||||
export const RequestFinished = Symbol('NetworkManager.RequestFinished');
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface NetworkManagerEvents extends Record<EventType, unknown> {
|
||||
[NetworkManagerEvent.Request]: CDPHTTPRequest;
|
||||
[NetworkManagerEvent.RequestServedFromCache]: CDPHTTPRequest | undefined;
|
||||
[NetworkManagerEvent.Response]: CDPHTTPResponse;
|
||||
[NetworkManagerEvent.RequestFailed]: CDPHTTPRequest;
|
||||
[NetworkManagerEvent.RequestFinished]: CDPHTTPRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -76,7 +90,7 @@ export interface FrameProvider {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class NetworkManager extends EventEmitter {
|
||||
export class NetworkManager extends EventEmitter<NetworkManagerEvents> {
|
||||
#ignoreHTTPSErrors: boolean;
|
||||
#frameManager: FrameProvider;
|
||||
#networkEventManager = new NetworkEventManager();
|
||||
@ -90,7 +104,7 @@ export class NetworkManager extends EventEmitter {
|
||||
#userAgent?: string;
|
||||
#userAgentMetadata?: Protocol.Emulation.UserAgentMetadata;
|
||||
|
||||
#handlers = new Map<string, Function>([
|
||||
#handlers = Object.freeze([
|
||||
['Fetch.requestPaused', this.#onRequestPaused],
|
||||
['Fetch.authRequired', this.#onAuthRequired],
|
||||
['Network.requestWillBeSent', this.#onRequestWillBeSent],
|
||||
@ -99,12 +113,10 @@ export class NetworkManager extends EventEmitter {
|
||||
['Network.loadingFinished', this.#onLoadingFinished],
|
||||
['Network.loadingFailed', this.#onLoadingFailed],
|
||||
['Network.responseReceivedExtraInfo', this.#onResponseReceivedExtraInfo],
|
||||
]);
|
||||
[CDPSessionEvent.Disconnected, this.#removeClient],
|
||||
] as const);
|
||||
|
||||
#clients = new Map<
|
||||
CDPSession,
|
||||
Array<{event: string | symbol; handler: Handler}>
|
||||
>();
|
||||
#clients = new Map<CDPSession, DisposableStack>();
|
||||
|
||||
constructor(ignoreHTTPSErrors: boolean, frameManager: FrameProvider) {
|
||||
super();
|
||||
@ -116,20 +128,16 @@ export class NetworkManager extends EventEmitter {
|
||||
if (this.#clients.has(client)) {
|
||||
return;
|
||||
}
|
||||
const listeners: Array<{event: string | symbol; handler: Handler}> = [];
|
||||
this.#clients.set(client, listeners);
|
||||
const subscriptions = new DisposableStack();
|
||||
this.#clients.set(client, subscriptions);
|
||||
for (const [event, handler] of this.#handlers) {
|
||||
listeners.push({
|
||||
event,
|
||||
handler: handler.bind(this, client),
|
||||
});
|
||||
client.on(event, listeners.at(-1)!.handler);
|
||||
subscriptions.use(
|
||||
// TODO: Remove any here.
|
||||
new EventSubscription(client, event, (arg: any) => {
|
||||
return handler.bind(this)(client, arg);
|
||||
})
|
||||
);
|
||||
}
|
||||
listeners.push({
|
||||
event: CDPSessionEmittedEvents.Disconnected,
|
||||
handler: this.#removeClient.bind(this, client),
|
||||
});
|
||||
client.on(CDPSessionEmittedEvents.Disconnected, listeners.at(-1)!.handler);
|
||||
await Promise.all([
|
||||
this.#ignoreHTTPSErrors
|
||||
? client.send('Security.setIgnoreCertificateErrors', {
|
||||
@ -146,10 +154,7 @@ export class NetworkManager extends EventEmitter {
|
||||
}
|
||||
|
||||
async #removeClient(client: CDPSession) {
|
||||
const listeners = this.#clients.get(client);
|
||||
for (const {event, handler} of listeners || []) {
|
||||
client.off(event, handler);
|
||||
}
|
||||
this.#clients.get(client)?.dispose();
|
||||
this.#clients.delete(client);
|
||||
}
|
||||
|
||||
@ -447,7 +452,7 @@ export class NetworkManager extends EventEmitter {
|
||||
? this.#frameManager.frame(event.frameId)
|
||||
: null;
|
||||
|
||||
const request = new HTTPRequest(
|
||||
const request = new CDPHTTPRequest(
|
||||
client,
|
||||
frame,
|
||||
event.requestId,
|
||||
@ -455,7 +460,7 @@ export class NetworkManager extends EventEmitter {
|
||||
event,
|
||||
[]
|
||||
);
|
||||
this.emit(NetworkManagerEmittedEvents.Request, request);
|
||||
this.emit(NetworkManagerEvent.Request, request);
|
||||
void request.finalizeInterceptions();
|
||||
}
|
||||
|
||||
@ -464,7 +469,7 @@ export class NetworkManager extends EventEmitter {
|
||||
event: Protocol.Network.RequestWillBeSentEvent,
|
||||
fetchRequestId?: FetchRequestId
|
||||
): void {
|
||||
let redirectChain: HTTPRequest[] = [];
|
||||
let redirectChain: CDPHTTPRequest[] = [];
|
||||
if (event.redirectResponse) {
|
||||
// We want to emit a response and requestfinished for the
|
||||
// redirectResponse, but we can't do so unless we have a
|
||||
@ -504,7 +509,7 @@ export class NetworkManager extends EventEmitter {
|
||||
? this.#frameManager.frame(event.frameId)
|
||||
: null;
|
||||
|
||||
const request = new HTTPRequest(
|
||||
const request = new CDPHTTPRequest(
|
||||
client,
|
||||
frame,
|
||||
fetchRequestId,
|
||||
@ -513,7 +518,7 @@ export class NetworkManager extends EventEmitter {
|
||||
redirectChain
|
||||
);
|
||||
this.#networkEventManager.storeRequest(event.requestId, request);
|
||||
this.emit(NetworkManagerEmittedEvents.Request, request);
|
||||
this.emit(NetworkManagerEvent.Request, request);
|
||||
void request.finalizeInterceptions();
|
||||
}
|
||||
|
||||
@ -525,16 +530,16 @@ export class NetworkManager extends EventEmitter {
|
||||
if (request) {
|
||||
request._fromMemoryCache = true;
|
||||
}
|
||||
this.emit(NetworkManagerEmittedEvents.RequestServedFromCache, request);
|
||||
this.emit(NetworkManagerEvent.RequestServedFromCache, request);
|
||||
}
|
||||
|
||||
#handleRequestRedirect(
|
||||
client: CDPSession,
|
||||
request: HTTPRequest,
|
||||
request: CDPHTTPRequest,
|
||||
responsePayload: Protocol.Network.Response,
|
||||
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
||||
): void {
|
||||
const response = new HTTPResponse(
|
||||
const response = new CDPHTTPResponse(
|
||||
client,
|
||||
request,
|
||||
responsePayload,
|
||||
@ -546,8 +551,8 @@ export class NetworkManager extends EventEmitter {
|
||||
new Error('Response body is unavailable for redirect responses')
|
||||
);
|
||||
this.#forgetRequest(request, false);
|
||||
this.emit(NetworkManagerEmittedEvents.Response, response);
|
||||
this.emit(NetworkManagerEmittedEvents.RequestFinished, request);
|
||||
this.emit(NetworkManagerEvent.Response, response);
|
||||
this.emit(NetworkManagerEvent.RequestFinished, request);
|
||||
}
|
||||
|
||||
#emitResponseEvent(
|
||||
@ -582,14 +587,14 @@ export class NetworkManager extends EventEmitter {
|
||||
extraInfo = null;
|
||||
}
|
||||
|
||||
const response = new HTTPResponse(
|
||||
const response = new CDPHTTPResponse(
|
||||
client,
|
||||
request,
|
||||
responseReceived.response,
|
||||
extraInfo
|
||||
);
|
||||
request._response = response;
|
||||
this.emit(NetworkManagerEmittedEvents.Response, response);
|
||||
this.emit(NetworkManagerEvent.Response, response);
|
||||
}
|
||||
|
||||
#onResponseReceived(
|
||||
@ -654,7 +659,7 @@ export class NetworkManager extends EventEmitter {
|
||||
this.#networkEventManager.responseExtraInfo(event.requestId).push(event);
|
||||
}
|
||||
|
||||
#forgetRequest(request: HTTPRequest, events: boolean): void {
|
||||
#forgetRequest(request: CDPHTTPRequest, events: boolean): void {
|
||||
const requestId = request._requestId;
|
||||
const interceptionId = request._interceptionId;
|
||||
|
||||
@ -697,7 +702,7 @@ export class NetworkManager extends EventEmitter {
|
||||
request.response()?._resolveBody(null);
|
||||
}
|
||||
this.#forgetRequest(request, true);
|
||||
this.emit(NetworkManagerEmittedEvents.RequestFinished, request);
|
||||
this.emit(NetworkManagerEvent.RequestFinished, request);
|
||||
}
|
||||
|
||||
#onLoadingFailed(
|
||||
@ -729,6 +734,6 @@ export class NetworkManager extends EventEmitter {
|
||||
response._resolveBody(null);
|
||||
}
|
||||
this.#forgetRequest(request, true);
|
||||
this.emit(NetworkManagerEmittedEvents.RequestFailed, request);
|
||||
this.emit(NetworkManagerEvent.RequestFailed, request);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import type {Browser} from '../api/Browser.js';
|
||||
import type {BrowserContext} from '../api/BrowserContext.js';
|
||||
import {CDPSession, CDPSessionEvent} from '../api/CDPSession.js';
|
||||
import {ElementHandle} from '../api/ElementHandle.js';
|
||||
import {Frame} from '../api/Frame.js';
|
||||
import {HTTPRequest} from '../api/HTTPRequest.js';
|
||||
@ -31,7 +32,7 @@ import {
|
||||
Metrics,
|
||||
NewDocumentScriptEvaluation,
|
||||
Page,
|
||||
PageEmittedEvents,
|
||||
PageEvent,
|
||||
ScreenshotClip,
|
||||
ScreenshotOptions,
|
||||
WaitForOptions,
|
||||
@ -43,33 +44,28 @@ import {isErrorLike} from '../util/ErrorLike.js';
|
||||
|
||||
import {Accessibility} from './Accessibility.js';
|
||||
import {Binding} from './Binding.js';
|
||||
import {
|
||||
CDPSession,
|
||||
CDPSessionEmittedEvents,
|
||||
CDPSessionImpl,
|
||||
isTargetClosedError,
|
||||
} from './Connection.js';
|
||||
import {CDPCDPSession} from './CDPSession.js';
|
||||
import {isTargetClosedError} from './Connection.js';
|
||||
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
|
||||
import {Coverage} from './Coverage.js';
|
||||
import {DeviceRequestPrompt} from './DeviceRequestPrompt.js';
|
||||
import {CDPDialog} from './Dialog.js';
|
||||
import {EmulationManager} from './EmulationManager.js';
|
||||
import {TargetCloseError} from './Errors.js';
|
||||
import {Handler} from './EventEmitter.js';
|
||||
import {FileChooser} from './FileChooser.js';
|
||||
import {CDPFrame} from './Frame.js';
|
||||
import {FrameManager, FrameManagerEmittedEvents} from './FrameManager.js';
|
||||
import {FrameManager, FrameManagerEvent} from './FrameManager.js';
|
||||
import {CDPKeyboard, CDPMouse, CDPTouchscreen} from './Input.js';
|
||||
import {MAIN_WORLD} from './IsolatedWorlds.js';
|
||||
import {
|
||||
Credentials,
|
||||
NetworkConditions,
|
||||
NetworkManagerEmittedEvents,
|
||||
NetworkManagerEvent,
|
||||
} from './NetworkManager.js';
|
||||
import {PDFOptions} from './PDFOptions.js';
|
||||
import {Viewport} from './PuppeteerViewport.js';
|
||||
import {CDPTarget} from './Target.js';
|
||||
import {TargetManagerEmittedEvents} from './TargetManager.js';
|
||||
import {TargetManagerEvent} from './TargetManager.js';
|
||||
import {TaskQueue} from './TaskQueue.js';
|
||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||
import {Tracing} from './Tracing.js';
|
||||
@ -146,58 +142,81 @@ export class CDPPage extends Page {
|
||||
#serviceWorkerBypassed = false;
|
||||
#userDragInterceptionEnabled = false;
|
||||
|
||||
#frameManagerHandlers = new Map<symbol, Handler<any>>([
|
||||
#frameManagerHandlers = Object.freeze([
|
||||
[
|
||||
FrameManagerEmittedEvents.FrameAttached,
|
||||
this.emit.bind(this, PageEmittedEvents.FrameAttached),
|
||||
FrameManagerEvent.FrameAttached,
|
||||
(frame: CDPFrame) => {
|
||||
this.emit(PageEvent.FrameAttached, frame);
|
||||
},
|
||||
],
|
||||
[
|
||||
FrameManagerEmittedEvents.FrameDetached,
|
||||
this.emit.bind(this, PageEmittedEvents.FrameDetached),
|
||||
FrameManagerEvent.FrameDetached,
|
||||
(frame: CDPFrame) => {
|
||||
this.emit(PageEvent.FrameDetached, frame);
|
||||
},
|
||||
],
|
||||
[
|
||||
FrameManagerEmittedEvents.FrameNavigated,
|
||||
this.emit.bind(this, PageEmittedEvents.FrameNavigated),
|
||||
FrameManagerEvent.FrameNavigated,
|
||||
(frame: CDPFrame) => {
|
||||
this.emit(PageEvent.FrameNavigated, frame);
|
||||
},
|
||||
],
|
||||
]);
|
||||
] as const);
|
||||
|
||||
#networkManagerHandlers = new Map<symbol, Handler<any>>([
|
||||
#networkManagerHandlers = Object.freeze([
|
||||
[
|
||||
NetworkManagerEmittedEvents.Request,
|
||||
this.emit.bind(this, PageEmittedEvents.Request),
|
||||
NetworkManagerEvent.Request,
|
||||
(request: HTTPRequest) => {
|
||||
this.emit(PageEvent.Request, request);
|
||||
},
|
||||
],
|
||||
[
|
||||
NetworkManagerEmittedEvents.RequestServedFromCache,
|
||||
this.emit.bind(this, PageEmittedEvents.RequestServedFromCache),
|
||||
NetworkManagerEvent.RequestServedFromCache,
|
||||
(request: HTTPRequest) => {
|
||||
this.emit(PageEvent.RequestServedFromCache, request);
|
||||
},
|
||||
],
|
||||
[
|
||||
NetworkManagerEmittedEvents.Response,
|
||||
this.emit.bind(this, PageEmittedEvents.Response),
|
||||
NetworkManagerEvent.Response,
|
||||
(response: HTTPResponse) => {
|
||||
this.emit(PageEvent.Response, response);
|
||||
},
|
||||
],
|
||||
[
|
||||
NetworkManagerEmittedEvents.RequestFailed,
|
||||
this.emit.bind(this, PageEmittedEvents.RequestFailed),
|
||||
NetworkManagerEvent.RequestFailed,
|
||||
(request: HTTPRequest) => {
|
||||
this.emit(PageEvent.RequestFailed, request);
|
||||
},
|
||||
],
|
||||
[
|
||||
NetworkManagerEmittedEvents.RequestFinished,
|
||||
this.emit.bind(this, PageEmittedEvents.RequestFinished),
|
||||
NetworkManagerEvent.RequestFinished,
|
||||
(request: HTTPRequest) => {
|
||||
this.emit(PageEvent.RequestFinished, request);
|
||||
},
|
||||
],
|
||||
]);
|
||||
] as const);
|
||||
|
||||
#sessionHandlers = new Map<symbol | string, Handler<any>>([
|
||||
#sessionHandlers = Object.freeze([
|
||||
[
|
||||
CDPSessionEmittedEvents.Disconnected,
|
||||
CDPSessionEvent.Disconnected,
|
||||
() => {
|
||||
return this.#sessionCloseDeferred.resolve(
|
||||
this.#sessionCloseDeferred.resolve(
|
||||
new TargetCloseError('Target closed')
|
||||
);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Page.domContentEventFired',
|
||||
this.emit.bind(this, PageEmittedEvents.DOMContentLoaded),
|
||||
() => {
|
||||
return this.emit(PageEvent.DOMContentLoaded, undefined);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Page.loadEventFired',
|
||||
() => {
|
||||
return this.emit(PageEvent.Load, undefined);
|
||||
},
|
||||
],
|
||||
['Page.loadEventFired', this.emit.bind(this, PageEmittedEvents.Load)],
|
||||
['Runtime.consoleAPICalled', this.#onConsoleAPI.bind(this)],
|
||||
['Runtime.bindingCalled', this.#onBindingCalled.bind(this)],
|
||||
['Page.javascriptDialogOpening', this.#onDialog.bind(this)],
|
||||
@ -206,7 +225,7 @@ export class CDPPage extends Page {
|
||||
['Performance.metrics', this.#emitMetrics.bind(this)],
|
||||
['Log.entryAdded', this.#onLogEntryAdded.bind(this)],
|
||||
['Page.fileChooserOpened', this.#onFileChooser.bind(this)],
|
||||
]);
|
||||
] as const);
|
||||
|
||||
constructor(
|
||||
client: CDPSession,
|
||||
@ -236,10 +255,10 @@ export class CDPPage extends Page {
|
||||
|
||||
this.#setupEventListeners();
|
||||
|
||||
this.#tabSession?.on(CDPSessionEmittedEvents.Swapped, async newSession => {
|
||||
this.#tabSession?.on(CDPSessionEvent.Swapped, async newSession => {
|
||||
this.#client = newSession;
|
||||
assert(
|
||||
this.#client instanceof CDPSessionImpl,
|
||||
this.#client instanceof CDPCDPSession,
|
||||
'CDPSession is not instance of CDPSessionImpl'
|
||||
);
|
||||
this.#target = this.#client._target();
|
||||
@ -254,57 +273,49 @@ export class CDPPage extends Page {
|
||||
await this.#frameManager.swapFrameTree(newSession);
|
||||
this.#setupEventListeners();
|
||||
});
|
||||
this.#tabSession?.on(
|
||||
CDPSessionEmittedEvents.Ready,
|
||||
(session: CDPSessionImpl) => {
|
||||
this.#tabSession?.on(CDPSessionEvent.Ready, session => {
|
||||
assert(session instanceof CDPCDPSession);
|
||||
if (session._target()._subtype() !== 'prerender') {
|
||||
return;
|
||||
}
|
||||
this.#frameManager
|
||||
.registerSpeculativeSession(session)
|
||||
.catch(debugError);
|
||||
this.#frameManager.registerSpeculativeSession(session).catch(debugError);
|
||||
this.#emulationManager
|
||||
.registerSpeculativeSession(session)
|
||||
.catch(debugError);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#setupEventListeners() {
|
||||
this.#client.on(CDPSessionEmittedEvents.Ready, this.#onAttachedToTarget);
|
||||
this.#client.on(CDPSessionEvent.Ready, this.#onAttachedToTarget);
|
||||
|
||||
this.#target
|
||||
._targetManager()
|
||||
.on(TargetManagerEmittedEvents.TargetGone, this.#onDetachedFromTarget);
|
||||
.on(TargetManagerEvent.TargetGone, this.#onDetachedFromTarget);
|
||||
|
||||
for (const [eventName, handler] of this.#frameManagerHandlers) {
|
||||
this.#frameManager.on(eventName, handler);
|
||||
}
|
||||
|
||||
for (const [eventName, handler] of this.#networkManagerHandlers) {
|
||||
this.#frameManager.networkManager.on(eventName, handler);
|
||||
// TODO: Remove any.
|
||||
this.#frameManager.networkManager.on(eventName, handler as any);
|
||||
}
|
||||
|
||||
for (const [eventName, handler] of this.#sessionHandlers) {
|
||||
this.#client.on(eventName, handler);
|
||||
// TODO: Remove any.
|
||||
this.#client.on(eventName, handler as any);
|
||||
}
|
||||
|
||||
this.#target._isClosedDeferred
|
||||
.valueOrThrow()
|
||||
.then(() => {
|
||||
this.#client.off(
|
||||
CDPSessionEmittedEvents.Ready,
|
||||
this.#onAttachedToTarget
|
||||
);
|
||||
this.#client.off(CDPSessionEvent.Ready, this.#onAttachedToTarget);
|
||||
|
||||
this.#target
|
||||
._targetManager()
|
||||
.off(
|
||||
TargetManagerEmittedEvents.TargetGone,
|
||||
this.#onDetachedFromTarget
|
||||
);
|
||||
.off(TargetManagerEvent.TargetGone, this.#onDetachedFromTarget);
|
||||
|
||||
this.emit(PageEmittedEvents.Close);
|
||||
this.emit(PageEvent.Close, undefined);
|
||||
this.#closed = true;
|
||||
})
|
||||
.catch(debugError);
|
||||
@ -317,10 +328,11 @@ export class CDPPage extends Page {
|
||||
return;
|
||||
}
|
||||
this.#workers.delete(sessionId!);
|
||||
this.emit(PageEmittedEvents.WorkerDestroyed, worker);
|
||||
this.emit(PageEvent.WorkerDestroyed, worker);
|
||||
};
|
||||
|
||||
#onAttachedToTarget = (session: CDPSessionImpl) => {
|
||||
#onAttachedToTarget = (session: CDPSession) => {
|
||||
assert(session instanceof CDPCDPSession);
|
||||
this.#frameManager.onAttachedToTarget(session._target());
|
||||
if (session._target()._getTargetInfo().type === 'worker') {
|
||||
const worker = new WebWorker(
|
||||
@ -330,9 +342,9 @@ export class CDPPage extends Page {
|
||||
this.#handleException.bind(this)
|
||||
);
|
||||
this.#workers.set(session.id(), worker);
|
||||
this.emit(PageEmittedEvents.WorkerCreated, worker);
|
||||
this.emit(PageEvent.WorkerCreated, worker);
|
||||
}
|
||||
session.on(CDPSessionEmittedEvents.Ready, this.#onAttachedToTarget);
|
||||
session.on(CDPSessionEvent.Ready, this.#onAttachedToTarget);
|
||||
};
|
||||
|
||||
async #initialize(): Promise<void> {
|
||||
@ -434,7 +446,7 @@ export class CDPPage extends Page {
|
||||
}
|
||||
|
||||
#onTargetCrashed(): void {
|
||||
this.emit('error', new Error('Page crashed!'));
|
||||
this.emit(PageEvent.Error, new Error('Page crashed!'));
|
||||
}
|
||||
|
||||
#onLogEntryAdded(event: Protocol.Log.EntryAddedEvent): void {
|
||||
@ -446,7 +458,7 @@ export class CDPPage extends Page {
|
||||
}
|
||||
if (source !== 'worker') {
|
||||
this.emit(
|
||||
PageEmittedEvents.Console,
|
||||
PageEvent.Console,
|
||||
new ConsoleMessage(level, text, [], [{url, lineNumber}])
|
||||
);
|
||||
}
|
||||
@ -703,7 +715,7 @@ export class CDPPage extends Page {
|
||||
}
|
||||
|
||||
#emitMetrics(event: Protocol.Performance.MetricsEvent): void {
|
||||
this.emit(PageEmittedEvents.Metrics, {
|
||||
this.emit(PageEvent.Metrics, {
|
||||
title: event.title,
|
||||
metrics: this.#buildMetricsObject(event.metrics),
|
||||
});
|
||||
@ -724,7 +736,7 @@ export class CDPPage extends Page {
|
||||
|
||||
#handleException(exception: Protocol.Runtime.ExceptionThrownEvent): void {
|
||||
this.emit(
|
||||
PageEmittedEvents.PageError,
|
||||
PageEvent.PageError,
|
||||
createClientError(exception.exceptionDetails)
|
||||
);
|
||||
}
|
||||
@ -801,7 +813,7 @@ export class CDPPage extends Page {
|
||||
args: JSHandle[],
|
||||
stackTrace?: Protocol.Runtime.StackTrace
|
||||
): void {
|
||||
if (!this.listenerCount(PageEmittedEvents.Console)) {
|
||||
if (!this.listenerCount(PageEvent.Console)) {
|
||||
args.forEach(arg => {
|
||||
return arg.dispose();
|
||||
});
|
||||
@ -834,7 +846,7 @@ export class CDPPage extends Page {
|
||||
args,
|
||||
stackTraceLocations
|
||||
);
|
||||
this.emit(PageEmittedEvents.Console, message);
|
||||
this.emit(PageEvent.Console, message);
|
||||
}
|
||||
|
||||
#onDialog(event: Protocol.Page.JavascriptDialogOpeningEvent): void {
|
||||
@ -845,7 +857,7 @@ export class CDPPage extends Page {
|
||||
event.message,
|
||||
event.defaultPrompt
|
||||
);
|
||||
this.emit(PageEmittedEvents.Dialog, dialog);
|
||||
this.emit(PageEvent.Dialog, dialog);
|
||||
}
|
||||
|
||||
override async reload(
|
||||
@ -870,7 +882,7 @@ export class CDPPage extends Page {
|
||||
const {timeout = this.#timeoutSettings.timeout()} = options;
|
||||
return await waitForEvent(
|
||||
this.#frameManager.networkManager,
|
||||
NetworkManagerEmittedEvents.Request,
|
||||
NetworkManagerEvent.Request,
|
||||
async request => {
|
||||
if (isString(urlOrPredicate)) {
|
||||
return urlOrPredicate === request.url();
|
||||
@ -894,7 +906,7 @@ export class CDPPage extends Page {
|
||||
const {timeout = this.#timeoutSettings.timeout()} = options;
|
||||
return await waitForEvent(
|
||||
this.#frameManager.networkManager,
|
||||
NetworkManagerEmittedEvents.Response,
|
||||
NetworkManagerEvent.Response,
|
||||
async response => {
|
||||
if (isString(urlOrPredicate)) {
|
||||
return urlOrPredicate === response.url();
|
||||
|
@ -18,11 +18,12 @@ import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import type {Browser} from '../api/Browser.js';
|
||||
import type {BrowserContext} from '../api/BrowserContext.js';
|
||||
import {Page, PageEmittedEvents} from '../api/Page.js';
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Page, PageEvent} from '../api/Page.js';
|
||||
import {Target, TargetType} from '../api/Target.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
import {CDPSession, CDPSessionImpl} from './Connection.js';
|
||||
import {CDPCDPSession} from './CDPSession.js';
|
||||
import {CDPPage} from './Page.js';
|
||||
import {Viewport} from './PuppeteerViewport.js';
|
||||
import {TargetManager} from './TargetManager.js';
|
||||
@ -75,7 +76,7 @@ export class CDPTarget extends Target {
|
||||
this.#browserContext = browserContext;
|
||||
this._targetId = targetInfo.targetId;
|
||||
this.#sessionFactory = sessionFactory;
|
||||
if (this.#session && this.#session instanceof CDPSessionImpl) {
|
||||
if (this.#session && this.#session instanceof CDPCDPSession) {
|
||||
this.#session._setTarget(this);
|
||||
}
|
||||
}
|
||||
@ -102,7 +103,7 @@ export class CDPTarget extends Target {
|
||||
throw new Error('sessionFactory is not initialized');
|
||||
}
|
||||
return this.#sessionFactory(false).then(session => {
|
||||
(session as CDPSessionImpl)._setTarget(this);
|
||||
(session as CDPCDPSession)._setTarget(this);
|
||||
return session;
|
||||
});
|
||||
}
|
||||
@ -222,11 +223,11 @@ export class PageTarget extends CDPTarget {
|
||||
return true;
|
||||
}
|
||||
const openerPage = await opener.pagePromise;
|
||||
if (!openerPage.listenerCount(PageEmittedEvents.Popup)) {
|
||||
if (!openerPage.listenerCount(PageEvent.Popup)) {
|
||||
return true;
|
||||
}
|
||||
const popupPage = await this.page();
|
||||
openerPage.emit(PageEmittedEvents.Popup, popupPage);
|
||||
openerPage.emit(PageEvent.Popup, popupPage);
|
||||
return true;
|
||||
})
|
||||
.catch(debugError);
|
||||
|
@ -16,8 +16,9 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
|
||||
import {EventEmitter, EventType} from './EventEmitter.js';
|
||||
import {CDPTarget} from './Target.js';
|
||||
|
||||
/**
|
||||
@ -29,6 +30,33 @@ export type TargetFactory = (
|
||||
parentSession?: CDPSession
|
||||
) => CDPTarget;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const enum TargetManagerEvent {
|
||||
TargetDiscovered = 'targetDiscovered',
|
||||
TargetAvailable = 'targetAvailable',
|
||||
TargetGone = 'targetGone',
|
||||
/**
|
||||
* Emitted after a target has been initialized and whenever its URL changes.
|
||||
*/
|
||||
TargetChanged = 'targetChanged',
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface TargetManagerEvents extends Record<EventType, unknown> {
|
||||
[TargetManagerEvent.TargetAvailable]: CDPTarget;
|
||||
[TargetManagerEvent.TargetDiscovered]: Protocol.Target.TargetInfo;
|
||||
[TargetManagerEvent.TargetGone]: CDPTarget;
|
||||
[TargetManagerEvent.TargetChanged]: {
|
||||
target: CDPTarget;
|
||||
wasInitialized: true;
|
||||
previousURL: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TargetManager encapsulates all interactions with CDP targets and is
|
||||
* responsible for coordinating the configuration of targets with the rest of
|
||||
@ -40,21 +68,8 @@ export type TargetFactory = (
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface TargetManager extends EventEmitter {
|
||||
export interface TargetManager extends EventEmitter<TargetManagerEvents> {
|
||||
getAvailableTargets(): Map<string, CDPTarget>;
|
||||
initialize(): Promise<void>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const enum TargetManagerEmittedEvents {
|
||||
TargetDiscovered = 'targetDiscovered',
|
||||
TargetAvailable = 'targetAvailable',
|
||||
TargetGone = 'targetGone',
|
||||
/**
|
||||
* Emitted after a target has been initialized and whenever its URL changes.
|
||||
*/
|
||||
TargetChanged = 'targetChanged',
|
||||
}
|
||||
|
@ -13,11 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {getReadableAsBuffer, getReadableFromProtocolStream} from './util.js';
|
||||
|
||||
/**
|
||||
@ -126,6 +126,7 @@ export class Tracing {
|
||||
const contentDeferred = Deferred.create<Buffer | undefined>();
|
||||
this.#client.once('Tracing.tracingComplete', async event => {
|
||||
try {
|
||||
assert(event.stream, 'Missing "stream"');
|
||||
const readable = await getReadableFromProtocolStream(
|
||||
this.#client,
|
||||
event.stream
|
||||
|
@ -15,11 +15,11 @@
|
||||
*/
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
import {CDPSession} from '../api/CDPSession.js';
|
||||
import {Realm} from '../api/Realm.js';
|
||||
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ConsoleMessageType} from './ConsoleMessage.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {EventEmitter, EventType} from './EventEmitter.js';
|
||||
import {ExecutionContext} from './ExecutionContext.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
import {CDPJSHandle} from './JSHandle.js';
|
||||
@ -33,7 +33,7 @@ import {debugError, withSourcePuppeteerURLIfNone} from './util.js';
|
||||
export type ConsoleAPICalledCallback = (
|
||||
eventType: ConsoleMessageType,
|
||||
handles: CDPJSHandle[],
|
||||
trace: Protocol.Runtime.StackTrace
|
||||
trace?: Protocol.Runtime.StackTrace
|
||||
) => void;
|
||||
|
||||
/**
|
||||
@ -69,7 +69,7 @@ export type ExceptionThrownCallback = (
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class WebWorker extends EventEmitter {
|
||||
export class WebWorker extends EventEmitter<Record<EventType, unknown>> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -18,22 +18,19 @@ import * as BidiMapper from 'chromium-bidi/lib/cjs/bidiMapper/bidiMapper.js';
|
||||
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||
import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||
|
||||
import {CDPSession, Connection as CDPPPtrConnection} from '../Connection.js';
|
||||
import {CDPEvents, CDPSession} from '../../api/CDPSession.js';
|
||||
import {Connection as CDPConnection} from '../Connection.js';
|
||||
import {TargetCloseError} from '../Errors.js';
|
||||
import {EventEmitter, Handler} from '../EventEmitter.js';
|
||||
import {Handler} from '../EventEmitter.js';
|
||||
|
||||
import {Connection as BidiPPtrConnection} from './Connection.js';
|
||||
|
||||
type CdpEvents = {
|
||||
[Property in keyof ProtocolMapping.Events]: ProtocolMapping.Events[Property][0];
|
||||
};
|
||||
import {BidiConnection} from './Connection.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export async function connectBidiOverCDP(
|
||||
cdp: CDPPPtrConnection
|
||||
): Promise<BidiPPtrConnection> {
|
||||
cdp: CDPConnection
|
||||
): Promise<BidiConnection> {
|
||||
const transportBiDi = new NoOpTransport();
|
||||
const cdpConnectionAdapter = new CDPConnectionAdapter(cdp);
|
||||
const pptrTransport = {
|
||||
@ -53,7 +50,7 @@ export async function connectBidiOverCDP(
|
||||
// Forwards a BiDi event sent by BidiServer to Puppeteer.
|
||||
pptrTransport.onmessage(JSON.stringify(message));
|
||||
});
|
||||
const pptrBiDiConnection = new BidiPPtrConnection(cdp.url(), pptrTransport);
|
||||
const pptrBiDiConnection = new BidiConnection(cdp.url(), pptrTransport);
|
||||
const bidiServer = await BidiMapper.BidiServer.createAndStart(
|
||||
transportBiDi,
|
||||
cdpConnectionAdapter,
|
||||
@ -67,16 +64,16 @@ export async function connectBidiOverCDP(
|
||||
* @internal
|
||||
*/
|
||||
class CDPConnectionAdapter {
|
||||
#cdp: CDPPPtrConnection;
|
||||
#cdp: CDPConnection;
|
||||
#adapters = new Map<CDPSession, CDPClientAdapter<CDPSession>>();
|
||||
#browser: CDPClientAdapter<CDPPPtrConnection>;
|
||||
#browser: CDPClientAdapter<CDPConnection>;
|
||||
|
||||
constructor(cdp: CDPPPtrConnection) {
|
||||
constructor(cdp: CDPConnection) {
|
||||
this.#cdp = cdp;
|
||||
this.#browser = new CDPClientAdapter(cdp);
|
||||
}
|
||||
|
||||
browserClient(): CDPClientAdapter<CDPPPtrConnection> {
|
||||
browserClient(): CDPClientAdapter<CDPConnection> {
|
||||
return this.#browser;
|
||||
}
|
||||
|
||||
@ -107,8 +104,8 @@ class CDPConnectionAdapter {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CDPClientAdapter<T extends EventEmitter & Pick<CDPPPtrConnection, 'send'>>
|
||||
extends BidiMapper.EventEmitter<CdpEvents>
|
||||
class CDPClientAdapter<T extends CDPSession | CDPConnection>
|
||||
extends BidiMapper.EventEmitter<CDPEvents>
|
||||
implements BidiMapper.CdpClient
|
||||
{
|
||||
#closed = false;
|
||||
@ -132,9 +129,9 @@ class CDPClientAdapter<T extends EventEmitter & Pick<CDPPPtrConnection, 'send'>>
|
||||
return this.#browserClient!;
|
||||
}
|
||||
|
||||
#forwardMessage = <T extends keyof CdpEvents>(
|
||||
#forwardMessage = <T extends keyof CDPEvents>(
|
||||
method: T,
|
||||
event: CdpEvents[T]
|
||||
event: CDPEvents[T]
|
||||
) => {
|
||||
this.emit(method, event);
|
||||
};
|
||||
|
@ -21,21 +21,18 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||
import {
|
||||
Browser,
|
||||
BrowserCloseCallback,
|
||||
BrowserContextEmittedEvents,
|
||||
BrowserContextOptions,
|
||||
BrowserEmittedEvents,
|
||||
BrowserEvent,
|
||||
} from '../../api/Browser.js';
|
||||
import {BrowserContextEvent} from '../../api/BrowserContext.js';
|
||||
import {Page} from '../../api/Page.js';
|
||||
import {Target} from '../../api/Target.js';
|
||||
import {Handler} from '../EventEmitter.js';
|
||||
import {Viewport} from '../PuppeteerViewport.js';
|
||||
|
||||
import {BidiBrowserContext} from './BrowserContext.js';
|
||||
import {
|
||||
BrowsingContext,
|
||||
BrowsingContextEmittedEvents,
|
||||
} from './BrowsingContext.js';
|
||||
import {Connection} from './Connection.js';
|
||||
import {BrowsingContext, BrowsingContextEvent} from './BrowsingContext.js';
|
||||
import {BidiConnection} from './Connection.js';
|
||||
import {
|
||||
BiDiBrowserTarget,
|
||||
BiDiBrowsingContextTarget,
|
||||
@ -44,6 +41,17 @@ import {
|
||||
} from './Target.js';
|
||||
import {debugError} from './utils.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Options {
|
||||
process?: ChildProcess;
|
||||
closeCallback?: BrowserCloseCallback;
|
||||
connection: BidiConnection;
|
||||
defaultViewport: Viewport | null;
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -108,7 +116,7 @@ export class BidiBrowser extends Browser {
|
||||
#browserVersion = '';
|
||||
#process?: ChildProcess;
|
||||
#closeCallback?: BrowserCloseCallback;
|
||||
#connection: Connection;
|
||||
#connection: BidiConnection;
|
||||
#defaultViewport: Viewport | null;
|
||||
#defaultContext: BidiBrowserContext;
|
||||
#targets = new Map<string, BidiTarget>();
|
||||
@ -142,7 +150,7 @@ export class BidiBrowser extends Browser {
|
||||
|
||||
this.#process?.once('close', () => {
|
||||
this.#connection.dispose();
|
||||
this.emit(BrowserEmittedEvents.Disconnected);
|
||||
this.emit(BrowserEvent.Disconnected, undefined);
|
||||
});
|
||||
this.#defaultContext = new BidiBrowserContext(this, {
|
||||
defaultViewport: this.#defaultViewport,
|
||||
@ -159,17 +167,15 @@ export class BidiBrowser extends Browser {
|
||||
#onContextDomLoaded(event: Bidi.BrowsingContext.Info) {
|
||||
const target = this.#targets.get(event.context);
|
||||
if (target) {
|
||||
this.emit(BrowserEmittedEvents.TargetChanged, target);
|
||||
this.emit(BrowserEvent.TargetChanged, target);
|
||||
}
|
||||
}
|
||||
|
||||
#onContextNavigation(event: Bidi.BrowsingContext.NavigationInfo) {
|
||||
const target = this.#targets.get(event.context);
|
||||
if (target) {
|
||||
this.emit(BrowserEmittedEvents.TargetChanged, target);
|
||||
target
|
||||
.browserContext()
|
||||
.emit(BrowserContextEmittedEvents.TargetChanged, target);
|
||||
this.emit(BrowserEvent.TargetChanged, target);
|
||||
target.browserContext().emit(BrowserContextEvent.TargetChanged, target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,14 +198,12 @@ export class BidiBrowser extends Browser {
|
||||
: new BiDiBrowsingContextTarget(browserContext, context);
|
||||
this.#targets.set(event.context, target);
|
||||
|
||||
this.emit(BrowserEmittedEvents.TargetCreated, target);
|
||||
target
|
||||
.browserContext()
|
||||
.emit(BrowserContextEmittedEvents.TargetCreated, target);
|
||||
this.emit(BrowserEvent.TargetCreated, target);
|
||||
target.browserContext().emit(BrowserContextEvent.TargetCreated, target);
|
||||
|
||||
if (context.parent) {
|
||||
const topLevel = this.#connection.getTopLevelContext(context.parent);
|
||||
topLevel.emit(BrowsingContextEmittedEvents.Created, context);
|
||||
topLevel.emit(BrowsingContextEvent.Created, context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,20 +219,18 @@ export class BidiBrowser extends Browser {
|
||||
) {
|
||||
const context = this.#connection.getBrowsingContext(event.context);
|
||||
const topLevelContext = this.#connection.getTopLevelContext(event.context);
|
||||
topLevelContext.emit(BrowsingContextEmittedEvents.Destroyed, context);
|
||||
topLevelContext.emit(BrowsingContextEvent.Destroyed, context);
|
||||
const target = this.#targets.get(event.context);
|
||||
const page = await target?.page();
|
||||
await page?.close().catch(debugError);
|
||||
this.#targets.delete(event.context);
|
||||
if (target) {
|
||||
this.emit(BrowserEmittedEvents.TargetDestroyed, target);
|
||||
target
|
||||
.browserContext()
|
||||
.emit(BrowserContextEmittedEvents.TargetDestroyed, target);
|
||||
this.emit(BrowserEvent.TargetDestroyed, target);
|
||||
target.browserContext().emit(BrowserContextEvent.TargetDestroyed, target);
|
||||
}
|
||||
}
|
||||
|
||||
get connection(): Connection {
|
||||
get connection(): BidiConnection {
|
||||
return this.#connection;
|
||||
}
|
||||
|
||||
@ -321,11 +323,3 @@ export class BidiBrowser extends Browser {
|
||||
return this.#browserTarget;
|
||||
}
|
||||
}
|
||||
|
||||
interface Options {
|
||||
process?: ChildProcess;
|
||||
closeCallback?: BrowserCloseCallback;
|
||||
connection: Connection;
|
||||
defaultViewport: Viewport | null;
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import {Target} from '../../api/Target.js';
|
||||
import {Viewport} from '../PuppeteerViewport.js';
|
||||
|
||||
import {BidiBrowser} from './Browser.js';
|
||||
import {Connection} from './Connection.js';
|
||||
import {BidiConnection} from './Connection.js';
|
||||
import {BidiPage} from './Page.js';
|
||||
|
||||
interface BrowserContextOptions {
|
||||
@ -35,7 +35,7 @@ interface BrowserContextOptions {
|
||||
*/
|
||||
export class BidiBrowserContext extends BrowserContext {
|
||||
#browser: BidiBrowser;
|
||||
#connection: Connection;
|
||||
#connection: BidiConnection;
|
||||
#defaultViewport: Viewport | null;
|
||||
#isDefault = false;
|
||||
|
||||
@ -62,7 +62,7 @@ export class BidiBrowserContext extends BrowserContext {
|
||||
}, options);
|
||||
}
|
||||
|
||||
get connection(): Connection {
|
||||
get connection(): BidiConnection {
|
||||
return this.#connection;
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,17 @@
|
||||
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||
import ProtocolMapping from 'devtools-protocol/types/protocol-mapping.js';
|
||||
|
||||
import {CDPSession} from '../../api/CDPSession.js';
|
||||
import {WaitForOptions} from '../../api/Page.js';
|
||||
import {assert} from '../../util/assert.js';
|
||||
import {Deferred} from '../../util/Deferred.js';
|
||||
import {Connection as CDPConnection, CDPSession} from '../Connection.js';
|
||||
import {Connection as CDPConnection} from '../Connection.js';
|
||||
import {ProtocolError, TargetCloseError, TimeoutError} from '../Errors.js';
|
||||
import {EventType} from '../EventEmitter.js';
|
||||
import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js';
|
||||
import {setPageContent, waitWithTimeout} from '../util.js';
|
||||
|
||||
import {Connection} from './Connection.js';
|
||||
import {BidiConnection} from './Connection.js';
|
||||
import {Realm} from './Realm.js';
|
||||
import {debugError} from './utils.js';
|
||||
|
||||
@ -120,17 +122,26 @@ export class CDPSessionWrapper extends CDPSession {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const BrowsingContextEmittedEvents = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace BrowsingContextEvent {
|
||||
/**
|
||||
* Emitted on the top-level context, when a descendant context is created.
|
||||
*/
|
||||
Created: Symbol('BrowsingContext.created'),
|
||||
export const Created = Symbol('BrowsingContext.created');
|
||||
/**
|
||||
* Emitted on the top-level context, when a descendant context or the
|
||||
* top-level context itself is destroyed.
|
||||
*/
|
||||
Destroyed: Symbol('BrowsingContext.destroyed'),
|
||||
} as const;
|
||||
export const Destroyed = Symbol('BrowsingContext.destroyed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface BrowsingContextEvents extends Record<EventType, unknown> {
|
||||
[BrowsingContextEvent.Created]: BrowsingContext;
|
||||
[BrowsingContextEvent.Destroyed]: BrowsingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -143,7 +154,7 @@ export class BrowsingContext extends Realm {
|
||||
#browserName = '';
|
||||
|
||||
constructor(
|
||||
connection: Connection,
|
||||
connection: BidiConnection,
|
||||
info: Bidi.BrowsingContext.Info,
|
||||
browserName: string
|
||||
) {
|
||||
|
@ -20,7 +20,7 @@ import expect from 'expect';
|
||||
|
||||
import {ConnectionTransport} from '../ConnectionTransport.js';
|
||||
|
||||
import {Connection} from './Connection.js';
|
||||
import {BidiConnection} from './Connection.js';
|
||||
|
||||
describe('WebDriver BiDi Connection', () => {
|
||||
class TestConnectionTransport implements ConnectionTransport {
|
||||
@ -38,7 +38,7 @@ describe('WebDriver BiDi Connection', () => {
|
||||
|
||||
it('should work', async () => {
|
||||
const transport = new TestConnectionTransport();
|
||||
const connection = new Connection('ws://127.0.0.1', transport);
|
||||
const connection = new BidiConnection('ws://127.0.0.1', transport);
|
||||
const responsePromise = connection.send('session.new', {
|
||||
capabilities: {},
|
||||
});
|
||||
@ -48,6 +48,7 @@ describe('WebDriver BiDi Connection', () => {
|
||||
const id = JSON.parse(transport.sent[0]!).id;
|
||||
const rawResponse = {
|
||||
id,
|
||||
type: 'success',
|
||||
result: {ready: false, message: 'already connected'},
|
||||
};
|
||||
(transport as ConnectionTransport).onmessage?.(JSON.stringify(rawResponse));
|
||||
|
@ -127,7 +127,17 @@ interface Commands {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class Connection extends EventEmitter {
|
||||
export type BidiEvents = {
|
||||
[K in Bidi.ChromiumBidi.Event['method']]: Extract<
|
||||
Bidi.ChromiumBidi.Event,
|
||||
{method: K}
|
||||
>['params'];
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class BidiConnection extends EventEmitter<BidiEvents> {
|
||||
#url: string;
|
||||
#transport: ConnectionTransport;
|
||||
#delay: number;
|
||||
@ -185,35 +195,45 @@ export class Connection extends EventEmitter {
|
||||
});
|
||||
}
|
||||
debugProtocolReceive(message);
|
||||
const object = JSON.parse(message) as Bidi.ChromiumBidi.Message;
|
||||
|
||||
if ('id' in object && object.id) {
|
||||
if ('error' in object) {
|
||||
const object: Bidi.ChromiumBidi.Message = JSON.parse(message);
|
||||
if ('type' in object) {
|
||||
switch (object.type) {
|
||||
case 'success':
|
||||
this.#callbacks.resolve(object.id, object);
|
||||
return;
|
||||
case 'error':
|
||||
if (object.id === null) {
|
||||
break;
|
||||
}
|
||||
this.#callbacks.reject(
|
||||
object.id,
|
||||
createProtocolError(object as Bidi.ErrorResponse),
|
||||
createProtocolError(object),
|
||||
object.message
|
||||
);
|
||||
} else {
|
||||
this.#callbacks.resolve(object.id, object);
|
||||
}
|
||||
} else {
|
||||
if ('error' in object || 'id' in object || 'launched' in object) {
|
||||
debugError(object);
|
||||
} else {
|
||||
return;
|
||||
case 'event':
|
||||
this.#maybeEmitOnContext(object);
|
||||
this.emit(object.method, object.params);
|
||||
// SAFETY: We know the method and parameter still match here.
|
||||
this.emit(
|
||||
object.method,
|
||||
object.params as BidiEvents[keyof BidiEvents]
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
debugError(object);
|
||||
}
|
||||
|
||||
#maybeEmitOnContext(event: Bidi.ChromiumBidi.Event) {
|
||||
let context: BrowsingContext | undefined;
|
||||
// Context specific events
|
||||
if ('context' in event.params && event.params.context) {
|
||||
if ('context' in event.params && event.params.context !== null) {
|
||||
context = this.#browsingContexts.get(event.params.context);
|
||||
// `log.entryAdded` specific context
|
||||
} else if ('source' in event.params && event.params.source.context) {
|
||||
} else if (
|
||||
'source' in event.params &&
|
||||
event.params.source.context !== undefined
|
||||
) {
|
||||
context = this.#browsingContexts.get(event.params.source.context);
|
||||
} else if (isCDPEvent(event)) {
|
||||
cdpSessions
|
||||
|
@ -21,7 +21,7 @@ import {Deferred} from '../../util/Deferred.js';
|
||||
import {interpolateFunction, stringifyFunction} from '../../util/Function.js';
|
||||
import {Awaitable, FlattenHandle} from '../types.js';
|
||||
|
||||
import {Connection} from './Connection.js';
|
||||
import {BidiConnection} from './Connection.js';
|
||||
import {BidiFrame} from './Frame.js';
|
||||
import {BidiSerializer} from './Serializer.js';
|
||||
import {debugError} from './utils.js';
|
||||
@ -39,6 +39,9 @@ interface RemotePromiseCallbacks {
|
||||
reject: Deferred<Bidi.Script.RemoteValue>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class ExposeableFunction<Args extends unknown[], Ret> {
|
||||
readonly #frame;
|
||||
|
||||
@ -200,7 +203,7 @@ export class ExposeableFunction<Args extends unknown[], Ret> {
|
||||
}
|
||||
};
|
||||
|
||||
get #connection(): Connection {
|
||||
get #connection(): BidiConnection {
|
||||
return this.#frame.context().connection;
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||
|
||||
import {CDPSession} from '../../api/CDPSession.js';
|
||||
import {Frame, throwIfDetached} 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';
|
||||
@ -31,7 +31,7 @@ import {
|
||||
lifeCycleToSubscribedEvent,
|
||||
} from './BrowsingContext.js';
|
||||
import {ExposeableFunction} from './ExposedFunction.js';
|
||||
import {HTTPResponse} from './HTTPResponse.js';
|
||||
import {BidiHTTPResponse} from './HTTPResponse.js';
|
||||
import {BidiPage} from './Page.js';
|
||||
import {
|
||||
MAIN_SANDBOX,
|
||||
@ -114,7 +114,7 @@ export class BidiFrame extends Frame {
|
||||
timeout?: number;
|
||||
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
|
||||
}
|
||||
): Promise<HTTPResponse | null> {
|
||||
): Promise<BidiHTTPResponse | null> {
|
||||
const navigationId = await this.#context.goto(url, {
|
||||
...options,
|
||||
timeout: options?.timeout ?? this.#timeoutSettings.navigationTimeout(),
|
||||
@ -146,7 +146,7 @@ export class BidiFrame extends Frame {
|
||||
timeout?: number;
|
||||
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
|
||||
} = {}
|
||||
): Promise<HTTPResponse | null> {
|
||||
): Promise<BidiHTTPResponse | null> {
|
||||
const {
|
||||
waitUntil = 'load',
|
||||
timeout = this.#timeoutSettings.navigationTimeout(),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user