diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 6c8ee75cbc..3113c25511 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -228,9 +228,20 @@ export class Route extends ChannelOwner): Promise { + const page = this.request().frame()._page; + // When page closes or crashes, we catch any potential rejects from this Route. + // Note that page could be missing when routing popup's initial request that + // does not have a Page initialized just yet. + return Promise.race([ + promise, + page ? page._closedOrCrashedPromise : Promise.resolve(), + ]); + } + async abort(errorCode?: string) { return this._wrapApiCall(async (channel: channels.RouteChannel) => { - await channel.abort({ errorCode }); + await this._raceWithPageClose(channel.abort({ errorCode })); }); } @@ -271,13 +282,13 @@ export class Route extends ChannelOwner { const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData; - await channel.continue({ + await this._raceWithPageClose(channel.continue({ url: options.url, method: options.method, headers: options.headers ? headersObjectToArray(options.headers) : undefined, postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined, - }); + })); }, undefined, isInternal); } } diff --git a/tests/page/page-request-continue.spec.ts b/tests/page/page-request-continue.spec.ts index 6a7836c1fd..1ef212cf0e 100644 --- a/tests/page/page-request-continue.spec.ts +++ b/tests/page/page-request-continue.spec.ts @@ -77,6 +77,30 @@ it('should not allow changing protocol when overriding url', async ({ page, serv expect(error.message).toContain('New URL must have same protocol as overridden URL'); }); +it('should not throw when continuing while page is closing', async ({ page, server }) => { + let done; + await page.route('**/*', async route => { + done = Promise.all([ + route.continue(), + page.close(), + ]); + }); + const error = await page.goto(server.EMPTY_PAGE).catch(e => e); + await done; + expect(error).toBeInstanceOf(Error); +}); + +it('should not throw when continuing after page is closed', async ({ page, server }) => { + let done; + await page.route('**/*', async route => { + await page.close(); + done = route.continue(); + }); + const error = await page.goto(server.EMPTY_PAGE).catch(e => e); + await done; + expect(error).toBeInstanceOf(Error); +}); + it('should override method along with url', async ({ page, server }) => { const request = server.waitForRequest('/empty.html'); await page.route('**/foo', route => {