From a2d8ecbfc156c532f49e1404146c5c788833d552 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Tue, 15 Feb 2022 05:14:03 +0100 Subject: [PATCH 1/4] fix: ensure dom binding is not called after detatch Fixes #7814 --- src/common/DOMWorld.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index cfacc28734666..ba3690f72b7c3 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -139,7 +139,7 @@ export class DOMWorld { } _hasContext(): boolean { - return !this._contextResolveCallback; + return !this._contextResolveCallback && !this._detached; } _detach(): void { From cdb8b4a30543dc70f5c76ee332e200c84681645d Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Thu, 17 Feb 2022 02:55:39 +0100 Subject: [PATCH 2/4] refactor: detach listeners instead --- src/common/DOMWorld.ts | 3 ++- test/page.spec.ts | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index ba3690f72b7c3..3215cc395ee26 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -139,11 +139,12 @@ export class DOMWorld { } _hasContext(): boolean { - return !this._contextResolveCallback && !this._detached; + return !this._contextResolveCallback; } _detach(): void { this._detached = true; + this._client.removeAllListeners('Runtime.bindingCalled'); for (const waitTask of this._waitTasks) waitTask.terminate( new Error('waitForFunction failed: frame got detached.') diff --git a/test/page.spec.ts b/test/page.spec.ts index 9fb6809b4d5d7..a2b9e278cbe3f 100644 --- a/test/page.spec.ts +++ b/test/page.spec.ts @@ -1035,6 +1035,26 @@ describe('Page', function () { }); expect(result).toBe(15); }); + it('should not throw when frames detach', async () => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + await page.exposeFunction('compute', function (a, b) { + return Promise.resolve(a * b); + }); + await utils.detachFrame(page, 'frame1'); + + let result = undefined; + expect( + (async () => { + result = await page.evaluate(async function () { + return await globalThis.compute(3, 5); + }); + })() + ).not.toThrow(); + expect(result).toBe(15); + }); it('should work with complex objects', async () => { const { page } = getTestState(); From 9ad896b5ef4e106569adc9d0cf5b8f32cf488b63 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Thu, 17 Feb 2022 02:58:38 +0100 Subject: [PATCH 3/4] refactor: safer approach --- src/common/DOMWorld.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index 3215cc395ee26..eb56d83325df0 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -111,9 +111,8 @@ export class DOMWorld { this._frame = frame; this._timeoutSettings = timeoutSettings; this._setContext(null); - this._client.on('Runtime.bindingCalled', (event) => - this._onBindingCalled(event) - ); + this._onBindingCalled = this._onBindingCalled.bind(this); + this._client.on('Runtime.bindingCalled', this._onBindingCalled); } frame(): Frame { @@ -144,7 +143,7 @@ export class DOMWorld { _detach(): void { this._detached = true; - this._client.removeAllListeners('Runtime.bindingCalled'); + this._client.off('Runtime.bindingCalled', this._onBindingCalled); for (const waitTask of this._waitTasks) waitTask.terminate( new Error('waitForFunction failed: frame got detached.') From ee22219d38a5c07fbb8b059540c7ab21fea2379f Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 17 Feb 2022 16:34:23 +0100 Subject: [PATCH 4/4] fix: test in test/page.spec.ts --- test/page.spec.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/page.spec.ts b/test/page.spec.ts index a2b9e278cbe3f..68fb6d87419f4 100644 --- a/test/page.spec.ts +++ b/test/page.spec.ts @@ -1045,15 +1045,11 @@ describe('Page', function () { }); await utils.detachFrame(page, 'frame1'); - let result = undefined; - expect( - (async () => { - result = await page.evaluate(async function () { - return await globalThis.compute(3, 5); - }); - })() - ).not.toThrow(); - expect(result).toBe(15); + await expect( + page.evaluate(async function () { + return await globalThis.compute(3, 5); + }) + ).resolves.toEqual(15); }); it('should work with complex objects', async () => { const { page } = getTestState();