chore(webdriver): support for Network interception (#12106)

This commit is contained in:
Nikolay Vitkov 2024-03-20 14:55:07 +01:00 committed by GitHub
parent 4f9f060408
commit 388af91e46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 153 additions and 66 deletions

View File

@ -185,6 +185,11 @@ export class BidiBrowserContext extends BrowserContext {
}
try {
for (const page of await this.pages()) {
// Workaround for Firefox
// TODO: Remove once https://bugzilla.mozilla.org/show_bug.cgi?id=1882260 is fixed
await page.setRequestInterception(false);
}
await this.userContext.remove();
} catch (error) {
debugError(error);

View File

@ -142,8 +142,10 @@ export class BidiHTTPRequest extends HTTPRequest {
throw new UnsupportedOperation();
}
override continue(_overrides: ContinueRequestOverrides = {}): never {
throw new UnsupportedOperation();
override async continue(
_overrides: ContinueRequestOverrides = {}
): Promise<void> {
return await this.#request.continueRequest();
}
override responseForRequest(): never {
@ -166,14 +168,14 @@ export class BidiHTTPRequest extends HTTPRequest {
throw new UnsupportedOperation();
}
override abort(): never {
throw new UnsupportedOperation();
override async abort(): Promise<void> {
return await this.#request.failRequest();
}
override respond(
override async respond(
_response: Partial<ResponseForRequest>,
_priority?: number
): never {
): Promise<void> {
throw new UnsupportedOperation();
}
}

View File

@ -202,6 +202,11 @@ export class BidiPage extends Page {
override async close(options?: {runBeforeUnload?: boolean}): Promise<void> {
try {
if (this.#interception) {
// Workaround for Firefox
// TODO: Remove once https://bugzilla.mozilla.org/show_bug.cgi?id=1882260 is fixed
await this.setRequestInterception(false);
}
await this.#frame.browsingContext.close(options?.runBeforeUnload);
} catch {
return;
@ -499,8 +504,21 @@ export class BidiPage extends Page {
return [...this.#workers];
}
override setRequestInterception(): never {
throw new UnsupportedOperation();
#interception?: string;
override async setRequestInterception(enable: boolean): Promise<void> {
if (enable && !this.#interception) {
this.#interception = await this.#frame.browsingContext.addIntercept({
phases: [
Bidi.Network.InterceptPhase.BeforeRequestSent,
Bidi.Network.InterceptPhase.AuthRequired,
],
});
} else if (!enable && this.#interception) {
await this.#frame.browsingContext.userContext.browser.removeIntercept(
this.#interception
);
this.#interception = undefined;
}
}
override setDragInterception(): never {

View File

@ -199,6 +199,16 @@ export class Browser extends EventEmitter<{
return script;
}
@throwIfDisposed<Browser>(browser => {
// SAFETY: By definition of `disposed`, `#reason` is defined.
return browser.#reason!;
})
async removeIntercept(intercept: Bidi.Network.Intercept): Promise<void> {
await this.session.send('network.removeIntercept', {
intercept,
});
}
@throwIfDisposed<Browser>(browser => {
// SAFETY: By definition of `disposed`, `#reason` is defined.
return browser.#reason!;

View File

@ -18,6 +18,14 @@ import {Request} from './Request.js';
import type {UserContext} from './UserContext.js';
import {UserPrompt} from './UserPrompt.js';
/**
* @internal
*/
export type AddInterceptOptions = Omit<
Bidi.Network.AddInterceptParameters,
'contexts'
>;
/**
* @internal
*/
@ -478,11 +486,26 @@ export class BrowsingContext extends EventEmitter<{
functionDeclaration,
{
...options,
contexts: [this, ...(options.contexts ?? [])],
contexts: [this],
}
);
}
@throwIfDisposed<BrowsingContext>(context => {
// SAFETY: Disposal implies this exists.
return context.#reason!;
})
async addIntercept(options: AddInterceptOptions): Promise<string> {
const {
result: {intercept},
} = await this.userContext.browser.session.send('network.addIntercept', {
...options,
contexts: [this.id],
});
return intercept;
}
@throwIfDisposed<BrowsingContext>(context => {
// SAFETY: Disposal implies this exists.
return context.#reason!;
@ -547,6 +570,14 @@ export class BrowsingContext extends EventEmitter<{
await this.#session.subscribe(events, [this.id]);
}
@throwIfDisposed<BrowsingContext>(context => {
// SAFETY: Disposal implies this exists.
return context.#reason!;
})
async addInterception(events: [string, ...string[]]): Promise<void> {
await this.#session.subscribe(events, [this.id]);
}
[disposeSymbol](): void {
this.#reason ??=
'Browsing context already closed, probably because the user context closed.';

View File

@ -149,6 +149,23 @@ export interface Commands {
params: Bidi.Storage.SetCookieParameters;
returnType: Bidi.Storage.SetCookieParameters;
};
'network.addIntercept': {
params: Bidi.Network.AddInterceptParameters;
returnType: Bidi.Network.AddInterceptResult;
};
'network.removeIntercept': {
params: Bidi.Network.RemoveInterceptParameters;
returnType: Bidi.EmptyResult;
};
'network.continueRequest': {
params: Bidi.Network.ContinueRequestParameters;
returnType: Bidi.EmptyResult;
};
'network.failRequest': {
params: Bidi.Network.FailRequestParameters;
returnType: Bidi.EmptyResult;
};
}
/**

View File

@ -143,6 +143,32 @@ export class Request extends EventEmitter<{
}
// keep-sorted end
async continueRequest(): Promise<void> {
if (!this.#event.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url.startsWith('data:')) {
return;
}
await this.#session.send('network.continueRequest', {
request: this.id,
});
}
async failRequest(): Promise<void> {
if (!this.#event.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url.startsWith('data:')) {
return;
}
await this.#session.send('network.failRequest', {
request: this.id,
});
}
@inertIfDisposed
private dispose(): void {
this[disposeSymbol]();

View File

@ -174,6 +174,20 @@ export class Session
});
}
@throwIfDisposed<Session>(session => {
// SAFETY: By definition of `disposed`, `#reason` is defined.
return session.#reason!;
})
async addIntercepts(
events: [string, ...string[]],
contexts?: [string, ...string[]]
): Promise<void> {
await this.send('session.subscribe', {
events,
contexts,
});
}
@throwIfDisposed<Session>(session => {
// SAFETY: By definition of `disposed`, `#reason` is defined.
return session.#reason!;

View File

@ -589,6 +589,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[network.spec] network Request.isNavigationRequest should work with request interception",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[network.spec] network Request.postData should be |undefined| when there is no post data",
"platforms": ["darwin", "linux", "win32"],
@ -631,6 +637,13 @@
"expectations": ["SKIP"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[oopif.spec] OOPIF should report google.com frame",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL", "TIMEOUT"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[page.spec] Page Page.addScriptTag should throw when added with content to the CSP page",
"platforms": ["darwin", "linux", "win32"],
@ -722,13 +735,6 @@
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[page.spec] Page Page.setCacheEnabled should stay disabled when toggling request interception on/off",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[page.spec] Page Page.setOfflineMode should emulate navigator.onLine",
"platforms": ["darwin", "linux", "win32"],
@ -1098,13 +1104,6 @@
"expectations": ["SKIP"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Page Tests Page.setRequestInterception should work with intervention headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[click.spec] Page.click should click on checkbox label and toggle",
"platforms": ["darwin", "linux", "win32"],
@ -1770,13 +1769,6 @@
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception",
"platforms": ["darwin", "linux", "win32"],
@ -1784,13 +1776,6 @@
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[input.spec] input tests FileChooser.accept should accept single file",
"platforms": ["darwin", "linux", "win32"],
@ -2910,39 +2895,11 @@
"expectations": ["SKIP"],
"comment": "Failed previously and currently times out"
},
{
"testIdPattern": "[oopif.spec] OOPIF should load oopif iframes with subresources and request interception",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[oopif.spec] OOPIF should load oopif iframes with subresources and request interception",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[oopif.spec] OOPIF should report google.com frame",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[oopif.spec] OOPIF should report google.com frame",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[oopif.spec] OOPIF should support lazy OOP frames",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"expectations": ["FAIL", "TIMEOUT"],
"comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=187816"
},
{
@ -3246,6 +3203,13 @@
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[page.spec] Page Page.setCacheEnabled should stay disabled when toggling request interception on/off",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[page.spec] Page Page.setCacheEnabled should stay disabled when toggling request interception on/off",
"platforms": ["darwin", "linux", "win32"],