feat(context): introduce BrowserContext close event (#918)
This commit is contained in:
parent
5fee93ae96
commit
72b9cf010e
16
docs/api.md
16
docs/api.md
|
|
@ -159,7 +159,7 @@ See [ChromiumBrowser], [FirefoxBrowser] and [WebKitBrowser] for browser-specific
|
||||||
#### event: 'disconnected'
|
#### event: 'disconnected'
|
||||||
Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following:
|
Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following:
|
||||||
- Browser application is closed or crashed.
|
- Browser application is closed or crashed.
|
||||||
- The [`browser.disconnect`](#browserdisconnect) method was called.
|
- The [`browser.close`](#browserclose) method was called.
|
||||||
|
|
||||||
#### browser.close()
|
#### browser.close()
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
|
|
@ -263,6 +263,7 @@ await context.close();
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- GEN:toc -->
|
<!-- GEN:toc -->
|
||||||
|
- [event: 'close'](#event-close)
|
||||||
- [browserContext.clearCookies()](#browsercontextclearcookies)
|
- [browserContext.clearCookies()](#browsercontextclearcookies)
|
||||||
- [browserContext.clearPermissions()](#browsercontextclearpermissions)
|
- [browserContext.clearPermissions()](#browsercontextclearpermissions)
|
||||||
- [browserContext.close()](#browsercontextclose)
|
- [browserContext.close()](#browsercontextclose)
|
||||||
|
|
@ -274,6 +275,13 @@ await context.close();
|
||||||
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
|
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
|
#### event: 'close'
|
||||||
|
|
||||||
|
Emitted when Browser context gets closed. This might happen because of one of the following:
|
||||||
|
- Browser context is closed.
|
||||||
|
- Browser application is closed or crashed.
|
||||||
|
- The [`browser.close`](#browserclose) method was called.
|
||||||
|
|
||||||
#### browserContext.clearCookies()
|
#### browserContext.clearCookies()
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
|
@ -426,7 +434,7 @@ page.removeListener('request', logRequest);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- GEN:toc -->
|
<!-- GEN:toc -->
|
||||||
- [event: 'close'](#event-close)
|
- [event: 'close'](#event-close-1)
|
||||||
- [event: 'console'](#event-console)
|
- [event: 'console'](#event-console)
|
||||||
- [event: 'dialog'](#event-dialog)
|
- [event: 'dialog'](#event-dialog)
|
||||||
- [event: 'domcontentloaded'](#event-domcontentloaded)
|
- [event: 'domcontentloaded'](#event-domcontentloaded)
|
||||||
|
|
@ -3168,7 +3176,7 @@ const { selectors, firefox } = require('playwright'); // Or 'chromium' or 'webk
|
||||||
The [WebSocket] class represents websocket connections in the page.
|
The [WebSocket] class represents websocket connections in the page.
|
||||||
|
|
||||||
<!-- GEN:toc -->
|
<!-- GEN:toc -->
|
||||||
- [event: 'close'](#event-close-1)
|
- [event: 'close'](#event-close-2)
|
||||||
- [event: 'error'](#event-error)
|
- [event: 'error'](#event-error)
|
||||||
- [event: 'messageReceived'](#event-messagereceived)
|
- [event: 'messageReceived'](#event-messagereceived)
|
||||||
- [event: 'messageSent'](#event-messagesent)
|
- [event: 'messageSent'](#event-messagesent)
|
||||||
|
|
@ -3423,7 +3431,7 @@ If the function passed to the `worker.evaluateHandle` returns a [Promise], then
|
||||||
### class: BrowserServer
|
### class: BrowserServer
|
||||||
|
|
||||||
<!-- GEN:toc -->
|
<!-- GEN:toc -->
|
||||||
- [event: 'close'](#event-close-2)
|
- [event: 'close'](#event-close-3)
|
||||||
- [browserServer.close()](#browserserverclose)
|
- [browserServer.close()](#browserserverclose)
|
||||||
- [browserServer.kill()](#browserserverkill)
|
- [browserServer.kill()](#browserserverkill)
|
||||||
- [browserServer.process()](#browserserverprocess)
|
- [browserServer.process()](#browserserverprocess)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ export interface Browser extends platform.EventEmitterType {
|
||||||
newPage(options?: BrowserContextOptions): Promise<Page>;
|
newPage(options?: BrowserContextOptions): Promise<Page>;
|
||||||
isConnected(): boolean;
|
isConnected(): boolean;
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
_defaultContext: BrowserContext | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectOptions = {
|
export type ConnectOptions = {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import { Page } from './page';
|
||||||
import * as network from './network';
|
import * as network from './network';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { helper } from './helper';
|
import { helper } from './helper';
|
||||||
|
import * as platform from './platform';
|
||||||
|
import { Events } from './events';
|
||||||
|
|
||||||
export interface BrowserContextDelegate {
|
export interface BrowserContextDelegate {
|
||||||
pages(): Promise<Page[]>;
|
pages(): Promise<Page[]>;
|
||||||
|
|
@ -47,12 +49,13 @@ export type BrowserContextOptions = {
|
||||||
permissions?: { [key: string]: string[] };
|
permissions?: { [key: string]: string[] };
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BrowserContext {
|
export class BrowserContext extends platform.EventEmitter {
|
||||||
private readonly _delegate: BrowserContextDelegate;
|
private readonly _delegate: BrowserContextDelegate;
|
||||||
readonly _options: BrowserContextOptions;
|
readonly _options: BrowserContextOptions;
|
||||||
private _closed = false;
|
private _closed = false;
|
||||||
|
|
||||||
constructor(delegate: BrowserContextDelegate, options: BrowserContextOptions) {
|
constructor(delegate: BrowserContextDelegate, options: BrowserContextOptions) {
|
||||||
|
super();
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
this._options = { ...options };
|
this._options = { ...options };
|
||||||
if (!this._options.viewport && this._options.viewport !== null)
|
if (!this._options.viewport && this._options.viewport !== null)
|
||||||
|
|
@ -114,12 +117,18 @@ export class BrowserContext {
|
||||||
return;
|
return;
|
||||||
await this._delegate.close();
|
await this._delegate.close();
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
|
this.emit(Events.BrowserContext.Close);
|
||||||
}
|
}
|
||||||
|
|
||||||
static validateOptions(options: BrowserContextOptions) {
|
static validateOptions(options: BrowserContextOptions) {
|
||||||
if (options.geolocation)
|
if (options.geolocation)
|
||||||
verifyGeolocation(options.geolocation);
|
verifyGeolocation(options.geolocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_browserClosed() {
|
||||||
|
this._closed = true;
|
||||||
|
this.emit(Events.BrowserContext.Close);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyGeolocation(geolocation: types.Geolocation): types.Geolocation {
|
function verifyGeolocation(geolocation: types.Geolocation): types.Geolocation {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,11 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
this._client = connection.rootSession;
|
this._client = connection.rootSession;
|
||||||
|
|
||||||
this._defaultContext = this._createBrowserContext(null, {});
|
this._defaultContext = this._createBrowserContext(null, {});
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(CommonEvents.Browser.Disconnected));
|
this._connection.on(ConnectionEvents.Disconnected, () => {
|
||||||
|
for (const context of this.contexts())
|
||||||
|
context._browserClosed();
|
||||||
|
this.emit(CommonEvents.Browser.Disconnected);
|
||||||
|
});
|
||||||
this._client.on('Target.targetCreated', this._targetCreated.bind(this));
|
this._client.on('Target.targetCreated', this._targetCreated.bind(this));
|
||||||
this._client.on('Target.targetDestroyed', this._targetDestroyed.bind(this));
|
this._client.on('Target.targetDestroyed', this._targetDestroyed.bind(this));
|
||||||
this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
|
this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ export const Events = {
|
||||||
Disconnected: 'disconnected'
|
Disconnected: 'disconnected'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
BrowserContext: {
|
||||||
|
Close: 'close'
|
||||||
|
},
|
||||||
|
|
||||||
BrowserServer: {
|
BrowserServer: {
|
||||||
Close: 'close',
|
Close: 'close',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,11 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
|
||||||
|
|
||||||
this._defaultContext = this._createBrowserContext(null, {});
|
this._defaultContext = this._createBrowserContext(null, {});
|
||||||
this._contexts = new Map();
|
this._contexts = new Map();
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
this._connection.on(ConnectionEvents.Disconnected, () => {
|
||||||
|
for (const context of this.contexts())
|
||||||
|
context._browserClosed();
|
||||||
|
this.emit(Events.Browser.Disconnected);
|
||||||
|
});
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
helper.addEventListener(this._connection, 'Target.targetCreated', this._onTargetCreated.bind(this)),
|
helper.addEventListener(this._connection, 'Target.targetCreated', this._onTargetCreated.bind(this)),
|
||||||
helper.addEventListener(this._connection, 'Target.targetDestroyed', this._onTargetDestroyed.bind(this)),
|
helper.addEventListener(this._connection, 'Target.targetDestroyed', this._onTargetDestroyed.bind(this)),
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,6 @@ export class WebKit implements BrowserType {
|
||||||
handleSIGINT = true,
|
handleSIGINT = true,
|
||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
handleSIGHUP = true,
|
handleSIGHUP = true,
|
||||||
timeout = 30000
|
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let temporaryUserDataDir: string | null = null;
|
let temporaryUserDataDir: string | null = null;
|
||||||
|
|
@ -136,7 +135,6 @@ export class WebKit implements BrowserType {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to WebKit!`);
|
|
||||||
transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream);
|
transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream);
|
||||||
browserServer = new BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? await wrapTransportWithWebSocket(transport, port || 0) : null);
|
browserServer = new BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? await wrapTransportWithWebSocket(transport, port || 0) : null);
|
||||||
return { browserServer, transport };
|
return { browserServer, transport };
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDisconnect() {
|
_onDisconnect() {
|
||||||
|
for (const context of this.contexts())
|
||||||
|
context._browserClosed();
|
||||||
for (const pageProxy of this._pageProxies.values())
|
for (const pageProxy of this._pageProxies.values())
|
||||||
pageProxy.dispose();
|
pageProxy.dispose();
|
||||||
this._pageProxies.clear();
|
this._pageProxies.clear();
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const target = await targetPromise;
|
const target = await targetPromise;
|
||||||
expect(await target.page()).toBe(page);
|
expect(await target.page()).toBe(page);
|
||||||
await page.close();
|
await page.context().close();
|
||||||
});
|
});
|
||||||
it('should fire target events', async function({browser, newContext, server}) {
|
it('should fire target events', async function({browser, newContext, server}) {
|
||||||
const context = await newContext();
|
const context = await newContext();
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,19 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
expect(error.message).toContain('has been closed');
|
expect(error.message).toContain('has been closed');
|
||||||
await browserServer.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
|
it('should emit close events on pages and contexts', async({server}) => {
|
||||||
|
const browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
|
const context = await remote.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
let contextClosed = false;
|
||||||
|
let pageClosed = false;
|
||||||
|
context.on('close', e => contextClosed = true);
|
||||||
|
page.on('close', e => pageClosed = true);
|
||||||
|
await browserServer.close();
|
||||||
|
expect(contextClosed).toBeTruthy();
|
||||||
|
expect(pageClosed).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Browser.close', function() {
|
describe('Browser.close', function() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue