diff --git a/docs/api/puppeteer.httprequest.initiator.md b/docs/api/puppeteer.httprequest.initiator.md index f66f174a53143..cd98a3fa67adc 100644 --- a/docs/api/puppeteer.httprequest.initiator.md +++ b/docs/api/puppeteer.httprequest.initiator.md @@ -10,10 +10,10 @@ The initiator of the request. ```typescript class HTTPRequest { - initiator(): Protocol.Network.Initiator; + initiator(): Protocol.Network.Initiator | undefined; } ``` **Returns:** -Protocol.Network.Initiator +Protocol.Network.Initiator \| undefined diff --git a/docs/index.md b/docs/index.md index ecd7eab401b12..e55470ad84a7a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -197,8 +197,8 @@ version of Chrome or Chromium, pass in the executable's path when creating a const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'}); ``` -You can also use Puppeteer with Firefox Nightly (experimental support). See -[`Puppeteer.launch`](https://pptr.dev/api/puppeteer.puppeteernode.launch) for +You can also use Puppeteer with Firefox. See +[status of cross-browser support](https://pptr.dev/faq/#q-what-is-the-status-of-cross-browser-support) for more information. See diff --git a/packages/puppeteer-core/src/api/HTTPRequest.ts b/packages/puppeteer-core/src/api/HTTPRequest.ts index fb16cc7b005b6..460077568e7c8 100644 --- a/packages/puppeteer-core/src/api/HTTPRequest.ts +++ b/packages/puppeteer-core/src/api/HTTPRequest.ts @@ -272,7 +272,7 @@ export class HTTPRequest { /** * The initiator of the request. */ - initiator(): Protocol.Network.Initiator { + initiator(): Protocol.Network.Initiator | undefined { throw new Error('Not implemented'); } diff --git a/packages/puppeteer-core/src/common/HTTPRequest.ts b/packages/puppeteer-core/src/common/HTTPRequest.ts index 7eeee5ee2391a..3016df70540fa 100644 --- a/packages/puppeteer-core/src/common/HTTPRequest.ts +++ b/packages/puppeteer-core/src/common/HTTPRequest.ts @@ -63,7 +63,7 @@ export class HTTPRequest extends BaseHTTPRequest { action: InterceptResolutionAction.None, }; #interceptHandlers: Array<() => void | PromiseLike>; - #initiator: Protocol.Network.Initiator; + #initiator?: Protocol.Network.Initiator; override get client(): CDPSession { return this.#client; @@ -74,27 +74,52 @@ export class HTTPRequest extends BaseHTTPRequest { frame: Frame | null, interceptionId: string | undefined, allowInterception: boolean, - event: Protocol.Network.RequestWillBeSentEvent, + data: { + /** + * Request identifier. + */ + requestId: Protocol.Network.RequestId; + /** + * Loader identifier. Empty string if the request is fetched from worker. + */ + loaderId?: Protocol.Network.LoaderId; + /** + * URL of the document this request is loaded for. + */ + documentURL?: string; + /** + * Request data. + */ + request: Protocol.Network.Request; + /** + * Request initiator. + */ + initiator?: Protocol.Network.Initiator; + /** + * Type of this resource. + */ + type?: Protocol.Network.ResourceType; + }, redirectChain: HTTPRequest[] ) { super(); this.#client = client; - this._requestId = event.requestId; + this._requestId = data.requestId; this.#isNavigationRequest = - event.requestId === event.loaderId && event.type === 'Document'; + data.requestId === data.loaderId && data.type === 'Document'; this._interceptionId = interceptionId; this.#allowInterception = allowInterception; - this.#url = event.request.url; - this.#resourceType = (event.type || 'other').toLowerCase() as ResourceType; - this.#method = event.request.method; - this.#postData = event.request.postData; + this.#url = data.request.url; + this.#resourceType = (data.type || 'other').toLowerCase() as ResourceType; + this.#method = data.request.method; + this.#postData = data.request.postData; this.#frame = frame; this._redirectChain = redirectChain; this.#continueRequestOverrides = {}; this.#interceptHandlers = []; - this.#initiator = event.initiator; + this.#initiator = data.initiator; - for (const [key, value] of Object.entries(event.request.headers)) { + for (const [key, value] of Object.entries(data.request.headers)) { this.#headers[key.toLowerCase()] = value; } } @@ -184,7 +209,7 @@ export class HTTPRequest extends BaseHTTPRequest { return this.#isNavigationRequest; } - override initiator(): Protocol.Network.Initiator { + override initiator(): Protocol.Network.Initiator | undefined { return this.#initiator; } diff --git a/packages/puppeteer-core/src/common/NetworkManager.ts b/packages/puppeteer-core/src/common/NetworkManager.ts index 4d05f4ed91df4..6ef7d2244d31a 100644 --- a/packages/puppeteer-core/src/common/NetworkManager.ts +++ b/packages/puppeteer-core/src/common/NetworkManager.ts @@ -336,6 +336,7 @@ export class NetworkManager extends EventEmitter { const {networkId: networkRequestId, requestId: fetchRequestId} = event; if (!networkRequestId) { + this.#onRequestWithoutNetworkInstrumentation(event); return; } @@ -374,6 +375,27 @@ export class NetworkManager extends EventEmitter { }; } + #onRequestWithoutNetworkInstrumentation( + event: Protocol.Fetch.RequestPausedEvent + ): void { + // If an event has no networkId it should not have any network events. We + // still want to dispatch it for the interception by the user. + const frame = event.frameId + ? this.#frameManager.frame(event.frameId) + : null; + + const request = new HTTPRequest( + this.#client, + frame, + event.requestId, + this.#userRequestInterceptionEnabled, + event, + [] + ); + this.emit(NetworkManagerEmittedEvents.Request, request); + request.finalizeInterceptions(); + } + #onRequest( event: Protocol.Network.RequestWillBeSentEvent, fetchRequestId?: FetchRequestId diff --git a/test/src/requestinterception.spec.ts b/test/src/requestinterception.spec.ts index 0a0dbf709b29c..2c55fe67c4703 100644 --- a/test/src/requestinterception.spec.ts +++ b/test/src/requestinterception.spec.ts @@ -127,6 +127,22 @@ describe('request interception', function () { expect(requests[1]!.url()).toContain('/one-style.css'); expect(requests[1]!.headers()['referer']).toContain('/one-style.html'); }); + it('should work with requests without networkId', async () => { + const {page, server} = getTestState(); + await page.goto(server.EMPTY_PAGE); + await page.setRequestInterception(true); + + const cdp = await page.target().createCDPSession(); + await cdp.send('DOM.enable'); + const urls: string[] = []; + page.on('request', request => { + urls.push(request.url()); + return request.continue(); + }); + // This causes network requests without networkId. + await cdp.send('CSS.enable'); + expect(urls).toStrictEqual([server.EMPTY_PAGE]); + }); it('should properly return navigation response when URL has cookies', async () => { const {page, server} = getTestState();