feat(network): expose network events via browser context (#6370)

- fix #6340
- Exposes all the network related events (request, response, requestfailed, requestfinished) through the browser context to allow for managing network activity even if the is any navigations through popups or to new tabs which could result in creation of multiple page objects.
This commit is contained in:
Vignesh Shanmugam 2021-05-13 10:29:14 -07:00 committed by GitHub
parent 30dd02409b
commit 4b3e5e5c17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 498 additions and 79 deletions

View file

@ -143,6 +143,41 @@ Use [`method: Page.waitForLoadState`] to wait until the page gets to a particula
cases).
:::
## event: BrowserContext.request
- argument: <[Request]>
Emitted when a request is issued from any pages created through this context.
The [request] object is read-only. To only listen for requests from a particular
page, use [`event: Page.request`].
In order to intercept and mutate requests, see [`method: BrowserContext.route`]
or [`method: Page.route`].
## event: BrowserContext.requestFailed
- argument: <[Request]>
Emitted when a request fails, for example by timing out. To only listen for
failed requests from a particular page, use [`event: Page.requestFailed`].
:::note
HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will complete
with [`event: BrowserContext.requestFinished`] event and not with [`event: BrowserContext.requestFailed`].
:::
## event: BrowserContext.requestFinished
- argument: <[Request]>
Emitted when a request finishes successfully after downloading the response body. For a successful response, the
sequence of events is `request`, `response` and `requestfinished`. To listen for
successful requests from a particular page, use [`event: Page.requestFinished`].
## event: BrowserContext.response
- argument: <[Response]>
Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
is `request`, `response` and `requestfinished`. To listen for response events
from a particular page, use [`event: Page.response`].
## event: BrowserContext.serviceWorker
* langs: js, python
- argument: <[Worker]>

View file

@ -86,6 +86,10 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
this._serviceWorkers.add(serviceWorker);
this.emit(Events.BrowserContext.ServiceWorker, serviceWorker);
});
this._channel.on('request', ({ request, page }) => this._onRequest(network.Request.from(request), Page.fromNullable(page)));
this._channel.on('requestFailed', ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, Page.fromNullable(page)));
this._channel.on('requestFinished', ({ request, responseEndTiming, page }) => this._onRequestFinished(network.Request.from(request), responseEndTiming, Page.fromNullable(page)));
this._channel.on('response', ({ response, page }) => this._onResponse(network.Response.from(response), Page.fromNullable(page)));
this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f));
}
@ -96,6 +100,35 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
page._opener.emit(Events.Page.Popup, page);
}
private _onRequest(request: network.Request, page: Page | null) {
this.emit(Events.BrowserContext.Request, request);
if (page)
page.emit(Events.Page.Request, request);
}
private _onResponse(response: network.Response, page: Page | null) {
this.emit(Events.BrowserContext.Response, response);
if (page)
page.emit(Events.Page.Response, response);
}
private _onRequestFailed(request: network.Request, responseEndTiming: number, failureText: string | undefined, page: Page | null) {
request._failureText = failureText || null;
if (request._timing)
request._timing.responseEnd = responseEndTiming;
this.emit(Events.BrowserContext.RequestFailed, request);
if (page)
page.emit(Events.Page.RequestFailed, request);
}
private _onRequestFinished(request: network.Request, responseEndTiming: number, page: Page | null) {
if (request._timing)
request._timing.responseEnd = responseEndTiming;
this.emit(Events.BrowserContext.RequestFinished, request);
if (page)
page.emit(Events.Page.RequestFinished, request);
}
_onRoute(route: network.Route, request: network.Request) {
for (const {url, handler} of this._routes) {
if (urlMatches(request.url(), url)) {

View file

@ -39,6 +39,10 @@ export const Events = {
Page: 'page',
BackgroundPage: 'backgroundpage',
ServiceWorker: 'serviceworker',
Request: 'request',
Response: 'response',
RequestFailed: 'requestfailed',
RequestFinished: 'requestfinished',
},
BrowserServer: {

View file

@ -132,10 +132,6 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
this._channel.on('frameDetached', ({ frame }) => this._onFrameDetached(Frame.from(frame)));
this._channel.on('load', () => this.emit(Events.Page.Load, this));
this._channel.on('pageError', ({ error }) => this.emit(Events.Page.PageError, parseError(error)));
this._channel.on('request', ({ request }) => this.emit(Events.Page.Request, Request.from(request)));
this._channel.on('requestFailed', ({ request, failureText, responseEndTiming }) => this._onRequestFailed(Request.from(request), responseEndTiming, failureText));
this._channel.on('requestFinished', ({ request, responseEndTiming }) => this._onRequestFinished(Request.from(request), responseEndTiming));
this._channel.on('response', ({ response }) => this.emit(Events.Page.Response, Response.from(response)));
this._channel.on('route', ({ route, request }) => this._onRoute(Route.from(route), Request.from(request)));
this._channel.on('video', ({ artifact }) => {
const artifactObject = Artifact.from(artifact);
@ -152,19 +148,6 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
]);
}
private _onRequestFailed(request: Request, responseEndTiming: number, failureText: string | undefined) {
request._failureText = failureText || null;
if (request._timing)
request._timing.responseEnd = responseEndTiming;
this.emit(Events.Page.RequestFailed, request);
}
private _onRequestFinished(request: Request, responseEndTiming: number) {
if (request._timing)
request._timing.responseEnd = responseEndTiming;
this.emit(Events.Page.RequestFinished, request);
}
private _onFrameAttached(frame: Frame) {
frame._page = this;
this._frames.add(frame);

View file

@ -18,13 +18,14 @@ import { BrowserContext } from '../server/browserContext';
import { Dispatcher, DispatcherScope, lookupDispatcher } from './dispatcher';
import { PageDispatcher, BindingCallDispatcher, WorkerDispatcher } from './pageDispatcher';
import * as channels from '../protocol/channels';
import { RouteDispatcher, RequestDispatcher } from './networkDispatchers';
import { RouteDispatcher, RequestDispatcher, ResponseDispatcher } from './networkDispatchers';
import { CRBrowserContext } from '../server/chromium/crBrowser';
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
import { RecorderSupplement } from '../server/supplements/recorderSupplement';
import { CallMetadata } from '../server/instrumentation';
import { ArtifactDispatcher } from './artifactDispatcher';
import { Artifact } from '../server/artifact';
import { Request, Response } from '../server/network';
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextInitializer> implements channels.BrowserContextChannel {
private _context: BrowserContext;
@ -63,6 +64,27 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker)});
context.on(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) }));
}
context.on(BrowserContext.Events.Request, (request: Request) => {
return this._dispatchEvent('request', {
request: RequestDispatcher.from(this._scope, request),
page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined())
});
});
context.on(BrowserContext.Events.Response, (response: Response) => this._dispatchEvent('response', {
response: ResponseDispatcher.from(this._scope, response),
page: PageDispatcher.fromNullable(this._scope, response.frame()._page.initializedOrUndefined())
}));
context.on(BrowserContext.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
request: RequestDispatcher.from(this._scope, request),
failureText: request._failureText,
responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined())
}));
context.on(BrowserContext.Events.RequestFinished, (request: Request) => this._dispatchEvent('requestFinished', {
request: RequestDispatcher.from(scope, request),
responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined())
}));
}
async setDefaultNavigationTimeoutNoReply(params: channels.BrowserContextSetDefaultNavigationTimeoutNoReplyParams) {

View file

@ -16,7 +16,6 @@
import { BrowserContext } from '../server/browserContext';
import { Frame } from '../server/frames';
import { Request } from '../server/network';
import { Page, Worker } from '../server/page';
import * as channels from '../protocol/channels';
import { Dispatcher, DispatcherScope, existingDispatcher, lookupDispatcher, lookupNullableDispatcher } from './dispatcher';
@ -39,7 +38,7 @@ import { createGuid } from '../utils/utils';
export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> implements channels.PageChannel {
private _page: Page;
private static fromNullable(scope: DispatcherScope, page: Page | undefined): PageDispatcher | undefined {
static fromNullable(scope: DispatcherScope, page: Page | undefined): PageDispatcher | undefined {
if (!page)
return undefined;
const result = existingDispatcher<PageDispatcher>(page);
@ -75,17 +74,6 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
page.on(Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
page.on(Page.Events.Load, () => this._dispatchEvent('load'));
page.on(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
page.on(Page.Events.Request, request => this._dispatchEvent('request', { request: RequestDispatcher.from(this._scope, request) }));
page.on(Page.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
request: RequestDispatcher.from(this._scope, request),
failureText: request._failureText,
responseEndTiming: request._responseEndTiming
}));
page.on(Page.Events.RequestFinished, (request: Request) => this._dispatchEvent('requestFinished', {
request: RequestDispatcher.from(scope, request),
responseEndTiming: request._responseEndTiming
}));
page.on(Page.Events.Response, response => this._dispatchEvent('response', { response: ResponseDispatcher.from(this._scope, response) }));
page.on(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) }));
page.on(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
page.on(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));

View file

@ -590,6 +590,10 @@ export interface BrowserContextChannel extends Channel {
on(event: 'video', callback: (params: BrowserContextVideoEvent) => void): this;
on(event: 'backgroundPage', callback: (params: BrowserContextBackgroundPageEvent) => void): this;
on(event: 'serviceWorker', callback: (params: BrowserContextServiceWorkerEvent) => void): this;
on(event: 'request', callback: (params: BrowserContextRequestEvent) => void): this;
on(event: 'requestFailed', callback: (params: BrowserContextRequestFailedEvent) => void): this;
on(event: 'requestFinished', callback: (params: BrowserContextRequestFinishedEvent) => void): this;
on(event: 'response', callback: (params: BrowserContextResponseEvent) => void): this;
addCookies(params: BrowserContextAddCookiesParams, metadata?: Metadata): Promise<BrowserContextAddCookiesResult>;
addInitScript(params: BrowserContextAddInitScriptParams, metadata?: Metadata): Promise<BrowserContextAddInitScriptResult>;
clearCookies(params?: BrowserContextClearCookiesParams, metadata?: Metadata): Promise<BrowserContextClearCookiesResult>;
@ -634,6 +638,25 @@ export type BrowserContextBackgroundPageEvent = {
export type BrowserContextServiceWorkerEvent = {
worker: WorkerChannel,
};
export type BrowserContextRequestEvent = {
request: RequestChannel,
page?: PageChannel,
};
export type BrowserContextRequestFailedEvent = {
request: RequestChannel,
failureText?: string,
responseEndTiming: number,
page?: PageChannel,
};
export type BrowserContextRequestFinishedEvent = {
request: RequestChannel,
responseEndTiming: number,
page?: PageChannel,
};
export type BrowserContextResponseEvent = {
response: ResponseChannel,
page?: PageChannel,
};
export type BrowserContextAddCookiesParams = {
cookies: SetNetworkCookie[],
};
@ -832,10 +855,6 @@ export interface PageChannel extends Channel {
on(event: 'frameDetached', callback: (params: PageFrameDetachedEvent) => void): this;
on(event: 'load', callback: (params: PageLoadEvent) => void): this;
on(event: 'pageError', callback: (params: PagePageErrorEvent) => void): this;
on(event: 'request', callback: (params: PageRequestEvent) => void): this;
on(event: 'requestFailed', callback: (params: PageRequestFailedEvent) => void): this;
on(event: 'requestFinished', callback: (params: PageRequestFinishedEvent) => void): this;
on(event: 'response', callback: (params: PageResponseEvent) => void): this;
on(event: 'route', callback: (params: PageRouteEvent) => void): this;
on(event: 'video', callback: (params: PageVideoEvent) => void): this;
on(event: 'webSocket', callback: (params: PageWebSocketEvent) => void): this;
@ -903,21 +922,6 @@ export type PageLoadEvent = {};
export type PagePageErrorEvent = {
error: SerializedError,
};
export type PageRequestEvent = {
request: RequestChannel,
};
export type PageRequestFailedEvent = {
request: RequestChannel,
failureText?: string,
responseEndTiming: number,
};
export type PageRequestFinishedEvent = {
request: RequestChannel,
responseEndTiming: number,
};
export type PageResponseEvent = {
response: ResponseChannel,
};
export type PageRouteEvent = {
route: RouteChannel,
request: RequestChannel,

View file

@ -642,6 +642,29 @@ BrowserContext:
parameters:
worker: Worker
request:
parameters:
request: Request
page: Page?
requestFailed:
parameters:
request: Request
failureText: string?
responseEndTiming: number
page: Page?
requestFinished:
parameters:
request: Request
responseEndTiming: number
page: Page?
response:
parameters:
response: Response
page: Page?
Page:
type: interface
@ -964,25 +987,6 @@ Page:
parameters:
error: SerializedError
request:
parameters:
request: Request
requestFailed:
parameters:
request: Request
failureText: string?
responseEndTiming: number
requestFinished:
parameters:
request: Request
responseEndTiming: number
response:
parameters:
response: Response
route:
parameters:
route: Route

View file

@ -39,6 +39,10 @@ export abstract class BrowserContext extends SdkObject {
static Events = {
Close: 'close',
Page: 'page',
Request: 'request',
Response: 'response',
RequestFailed: 'requestfailed',
RequestFinished: 'requestfinished',
BeforeClose: 'beforeclose',
VideoStarted: 'videostarted',
};

View file

@ -207,10 +207,10 @@ export class CRNetworkManager {
frame = this._page._frameManager.frame(requestPausedEvent.frameId);
// Check if it's main resource request interception (targetId === main frame id).
if (!frame && requestPausedEvent && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) {
if (!frame && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) {
// Main resource request for the page is being intercepted so the Frame is not created
// yet. Precreate it here for the purposes of request interception. It will be updated
// later as soon as the request contnues and we receive frame tree from the page.
// later as soon as the request continues and we receive frame tree from the page.
frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
}

View file

@ -263,6 +263,7 @@ export class FrameManager {
route.continue();
return;
}
this._page._browserContext.emit(BrowserContext.Events.Request, request);
this._page._requestStarted(request);
}
@ -271,12 +272,15 @@ export class FrameManager {
return;
this._responses.push(response);
this._page.emit(Page.Events.Response, response);
this._page._browserContext.emit(BrowserContext.Events.Response, response);
}
requestFinished(request: network.Request) {
this._inflightRequestFinished(request);
if (!request._isFavicon)
this._page.emit(Page.Events.RequestFinished, request);
if (request._isFavicon)
return;
this._page._browserContext.emit(BrowserContext.Events.RequestFinished, request);
this._page.emit(Page.Events.RequestFinished, request);
}
requestFailed(request: network.Request, canceled: boolean) {
@ -288,8 +292,10 @@ export class FrameManager {
errorText += '; maybe frame was detached?';
this.frameAbortedNavigation(frame._id, errorText, frame.pendingDocument()!.documentId);
}
if (!request._isFavicon)
this._page.emit(Page.Events.RequestFailed, request);
if (request._isFavicon)
return;
this._page._browserContext.emit(BrowserContext.Events.RequestFailed, request);
this._page.emit(Page.Events.RequestFailed, request);
}
removeChildFramesRecursively(frame: Frame) {

View file

@ -122,6 +122,7 @@ export class Page extends SdkObject {
private _closedCallback: () => void;
private _closedPromise: Promise<void>;
private _disconnected = false;
private _initialized = false;
private _disconnectedCallback: (e: Error) => void;
readonly _disconnectedPromise: Promise<Error>;
private _crashedCallback: (e: Error) => void;
@ -195,6 +196,7 @@ export class Page extends SdkObject {
return;
this._setIsError(error);
}
this._initialized = true;
this._browserContext.emit(BrowserContext.Events.Page, this);
// I may happen that page iniatialization finishes after Close event has already been sent,
// in that case we fire another Close event to ensure that each reported Page will have
@ -203,6 +205,10 @@ export class Page extends SdkObject {
this.emit(Page.Events.Close);
}
initializedOrUndefined() {
return this._initialized ? this : undefined;
}
async _doSlowMo() {
const slowMo = this._browserContext._browser.options.slowMo;
if (!slowMo)

View file

@ -0,0 +1,109 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { browserTest as it, expect } from './config/browserTest';
it('BrowserContext.Events.Request', async ({browser, server}) => {
const context = await browser.newContext();
const page = await context.newPage();
const requests = [];
context.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
const [page1] = await Promise.all([
context.waitForEvent('page'),
page.click('a'),
]);
await page1.waitForLoadState();
const urls = requests.map(r => r.url());
expect(urls).toEqual([
server.EMPTY_PAGE,
`${server.PREFIX}/one-style.html`,
`${server.PREFIX}/one-style.css`
]);
});
it('BrowserContext.Events.Response', async ({browser, server}) => {
const context = await browser.newContext();
const page = await context.newPage();
const responses = [];
context.on('response', response => responses.push(response));
await page.goto(server.EMPTY_PAGE);
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
const [page1] = await Promise.all([
context.waitForEvent('page'),
page.click('a'),
]);
await page1.waitForLoadState();
const urls = responses.map(r => r.url());
expect(urls).toEqual([
server.EMPTY_PAGE,
`${server.PREFIX}/one-style.html`,
`${server.PREFIX}/one-style.css`
]);
});
it('BrowserContext.Events.RequestFailed', async ({browser, server}) => {
server.setRoute('/one-style.css', (_, res) => {
res.setHeader('Content-Type', 'text/css');
res.connection.destroy();
});
const context = await browser.newContext();
const page = await context.newPage();
const failedRequests = [];
context.on('requestfailed', request => failedRequests.push(request));
await page.goto(server.PREFIX + '/one-style.html');
expect(failedRequests.length).toBe(1);
expect(failedRequests[0].url()).toContain('one-style.css');
expect(await failedRequests[0].response()).toBe(null);
expect(failedRequests[0].resourceType()).toBe('stylesheet');
expect(failedRequests[0].frame()).toBeTruthy();
});
it('BrowserContext.Events.RequestFinished', async ({browser, server}) => {
const context = await browser.newContext();
const page = await context.newPage();
const [response] = await Promise.all([
page.goto(server.EMPTY_PAGE),
context.waitForEvent('requestfinished')
]);
const request = response.request();
expect(request.url()).toBe(server.EMPTY_PAGE);
expect(await request.response()).toBeTruthy();
expect(request.frame() === page.mainFrame()).toBe(true);
expect(request.frame().url()).toBe(server.EMPTY_PAGE);
expect(request.failure()).toBe(null);
});
it('should fire events in proper order', async ({browser, server}) => {
const context = await browser.newContext();
const page = await context.newPage();
const events = [];
context.on('request', () => events.push('request'));
context.on('response', () => events.push('response'));
context.on('requestfinished', () => events.push('requestfinished'));
await Promise.all([
page.goto(server.EMPTY_PAGE),
context.waitForEvent('requestfinished')
]);
expect(events).toEqual([
'request',
'response',
'requestfinished',
]);
});

View file

@ -49,10 +49,9 @@ it('should scope context handles', async ({browserType, browserOptions, server})
{ _guid: 'browser', objects: [
{ _guid: 'browser-context', objects: [
{ _guid: 'frame', objects: [] },
{ _guid: 'page', objects: [
{ _guid: 'request', objects: [] },
{ _guid: 'response', objects: [] },
]},
{ _guid: 'page', objects: []},
{ _guid: 'request', objects: [] },
{ _guid: 'response', objects: [] },
]},
] },
] },

222
types/types.d.ts vendored
View file

@ -4838,6 +4838,43 @@ export interface BrowserContext {
*/
on(event: 'page', listener: (page: Page) => void): this;
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
* listen for requests from a particular page, use
* [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest).
*
* In order to intercept and mutate requests, see
* [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler)
* or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler).
*/
on(event: 'request', listener: (request: Request) => void): this;
/**
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
* [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed).
*
* > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
* complete with
* [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished)
* event and not with
* [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed).
*/
on(event: 'requestfailed', listener: (request: Request) => void): this;
/**
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
* sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular
* page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished).
*/
on(event: 'requestfinished', listener: (request: Request) => void): this;
/**
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
* is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use
* [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse).
*/
on(event: 'response', listener: (response: Response) => void): this;
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*
@ -4888,6 +4925,43 @@ export interface BrowserContext {
*/
once(event: 'page', listener: (page: Page) => void): this;
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
* listen for requests from a particular page, use
* [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest).
*
* In order to intercept and mutate requests, see
* [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler)
* or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler).
*/
once(event: 'request', listener: (request: Request) => void): this;
/**
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
* [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed).
*
* > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
* complete with
* [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished)
* event and not with
* [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed).
*/
once(event: 'requestfailed', listener: (request: Request) => void): this;
/**
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
* sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular
* page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished).
*/
once(event: 'requestfinished', listener: (request: Request) => void): this;
/**
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
* is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use
* [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse).
*/
once(event: 'response', listener: (response: Response) => void): this;
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*
@ -4938,6 +5012,43 @@ export interface BrowserContext {
*/
addListener(event: 'page', listener: (page: Page) => void): this;
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
* listen for requests from a particular page, use
* [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest).
*
* In order to intercept and mutate requests, see
* [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler)
* or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler).
*/
addListener(event: 'request', listener: (request: Request) => void): this;
/**
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
* [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed).
*
* > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
* complete with
* [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished)
* event and not with
* [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed).
*/
addListener(event: 'requestfailed', listener: (request: Request) => void): this;
/**
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
* sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular
* page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished).
*/
addListener(event: 'requestfinished', listener: (request: Request) => void): this;
/**
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
* is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use
* [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse).
*/
addListener(event: 'response', listener: (response: Response) => void): this;
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*
@ -4988,6 +5099,43 @@ export interface BrowserContext {
*/
removeListener(event: 'page', listener: (page: Page) => void): this;
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
* listen for requests from a particular page, use
* [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest).
*
* In order to intercept and mutate requests, see
* [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler)
* or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler).
*/
removeListener(event: 'request', listener: (request: Request) => void): this;
/**
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
* [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed).
*
* > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
* complete with
* [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished)
* event and not with
* [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed).
*/
removeListener(event: 'requestfailed', listener: (request: Request) => void): this;
/**
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
* sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular
* page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished).
*/
removeListener(event: 'requestfinished', listener: (request: Request) => void): this;
/**
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
* is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use
* [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse).
*/
removeListener(event: 'response', listener: (response: Response) => void): this;
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*
@ -5038,6 +5186,43 @@ export interface BrowserContext {
*/
off(event: 'page', listener: (page: Page) => void): this;
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
* listen for requests from a particular page, use
* [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest).
*
* In order to intercept and mutate requests, see
* [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler)
* or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler).
*/
off(event: 'request', listener: (request: Request) => void): this;
/**
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
* [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed).
*
* > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
* complete with
* [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished)
* event and not with
* [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed).
*/
off(event: 'requestfailed', listener: (request: Request) => void): this;
/**
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
* sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular
* page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished).
*/
off(event: 'requestfinished', listener: (request: Request) => void): this;
/**
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
* is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use
* [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse).
*/
off(event: 'response', listener: (response: Response) => void): this;
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*
@ -5509,6 +5694,43 @@ export interface BrowserContext {
*/
waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise<boolean>, timeout?: number } | ((page: Page) => boolean | Promise<boolean>)): Promise<Page>;
/**
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
* listen for requests from a particular page, use
* [page.on('request')](https://playwright.dev/docs/api/class-page#pageonrequest).
*
* In order to intercept and mutate requests, see
* [browserContext.route(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browsercontextrouteurl-handler)
* or [page.route(url, handler)](https://playwright.dev/docs/api/class-page#pagerouteurl-handler).
*/
waitForEvent(event: 'request', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise<boolean>, timeout?: number } | ((request: Request) => boolean | Promise<boolean>)): Promise<Request>;
/**
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
* [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#pageonrequestfailed).
*
* > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
* complete with
* [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfinished)
* event and not with
* [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browsercontextonrequestfailed).
*/
waitForEvent(event: 'requestfailed', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise<boolean>, timeout?: number } | ((request: Request) => boolean | Promise<boolean>)): Promise<Request>;
/**
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
* sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular
* page, use [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#pageonrequestfinished).
*/
waitForEvent(event: 'requestfinished', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise<boolean>, timeout?: number } | ((request: Request) => boolean | Promise<boolean>)): Promise<Request>;
/**
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
* is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use
* [page.on('response')](https://playwright.dev/docs/api/class-page#pageonresponse).
*/
waitForEvent(event: 'response', optionsOrPredicate?: { predicate?: (response: Response) => boolean | Promise<boolean>, timeout?: number } | ((response: Response) => boolean | Promise<boolean>)): Promise<Response>;
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*