From b66839b039713382f5b877551f4c6deb35240534 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 15 Nov 2023 17:15:25 +0100 Subject: [PATCH] fix(exposeFunction): exposeFunction should not leak client side BindingCalls (#28163) This should already make it a bit better. There is more going on tho. https://github.com/microsoft/playwright/issues/28146 --- .../src/server/dispatchers/pageDispatcher.ts | 2 + tests/library/channels.spec.ts | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 60e53a7ea9..48f042f902 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -364,9 +364,11 @@ export class BindingCallDispatcher extends Dispatcher<{ guid: string }, channels async resolve(params: channels.BindingCallResolveParams, metadata: CallMetadata): Promise { this._resolve!(parseArgument(params.result)); + this._dispose(); } async reject(params: channels.BindingCallRejectParams, metadata: CallMetadata): Promise { this._reject!(parseError(params.error)); + this._dispose(); } } diff --git a/tests/library/channels.spec.ts b/tests/library/channels.spec.ts index 0527ef6cf0..c46d463c5b 100644 --- a/tests/library/channels.spec.ts +++ b/tests/library/channels.spec.ts @@ -256,6 +256,89 @@ it('should work with the domain module', async ({ browserType, server, browserNa throw err; }); +it('exposeFunction should not leak', async ({ page, expectScopeState, server }) => { + await page.goto(server.EMPTY_PAGE); + let called = 0; + await page.exposeFunction('myFunction', () => ++called); + for (let i = 0; i < 10; ++i) + await page.evaluate(() => (window as any).myFunction({ foo: 'bar' })); + expect(called).toBe(10); + expectScopeState(page, { + '_guid': '', + 'objects': [ + { + '_guid': 'android', + 'objects': [], + }, + { + '_guid': 'browser-type', + 'objects': [], + }, + { + '_guid': 'browser-type', + 'objects': [], + }, + { + '_guid': 'browser-type', + 'objects': [ + { + '_guid': 'browser', + 'objects': [ + { + '_guid': 'browser-context', + 'objects': [ + { + '_guid': 'page', + 'objects': [ + { + '_guid': 'frame', + 'objects': [], + }, + { + '_guid': 'request', + 'objects': [ + { + '_guid': 'response', + 'objects': [], + }, + ], + }, + ], + }, + { + '_guid': 'request-context', + 'objects': [], + }, + { + '_guid': 'tracing', + 'objects': [], + }, + ], + }, + ], + }, + ], + }, + { + '_guid': 'electron', + 'objects': [], + }, + { + '_guid': 'localUtils', + 'objects': [], + }, + { + '_guid': 'Playwright', + 'objects': [], + }, + { + '_guid': 'selectors', + 'objects': [], + }, + ], + }); +}); + function compareObjects(a, b) { if (a._guid !== b._guid) return a._guid.localeCompare(b._guid);