diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index 73fe02568f..8c936d7853 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -386,6 +386,8 @@ class FrameSession { readonly _crPage: CRPage; readonly _page: Page; readonly _networkManager: CRNetworkManager; + private readonly _parentSession: FrameSession | null; + private readonly _childSessions = new Set(); private readonly _contextIdToContext = new Map(); private _eventListeners: RegisteredListener[] = []; readonly _targetId: string; @@ -408,6 +410,9 @@ class FrameSession { this._page = crPage._page; this._targetId = targetId; this._networkManager = new CRNetworkManager(client, this._page, parentSession ? parentSession._networkManager : null); + this._parentSession = parentSession; + if (parentSession) + parentSession._childSessions.add(this); this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => { this._firstNonInitialNavigationCommittedFulfill = f; this._firstNonInitialNavigationCommittedReject = r; @@ -569,6 +574,10 @@ class FrameSession { } dispose() { + for (const childSession of this._childSessions) + childSession.dispose(); + if (this._parentSession) + this._parentSession._childSessions.delete(this); eventsHelper.removeEventListeners(this._eventListeners); this._networkManager.dispose(); this._crPage._sessions.delete(this._targetId); diff --git a/tests/assets/inner-oopif.html b/tests/assets/inner-oopif.html new file mode 100644 index 0000000000..13ac63801b --- /dev/null +++ b/tests/assets/inner-oopif.html @@ -0,0 +1,10 @@ + diff --git a/tests/assets/outer-oopif.html b/tests/assets/outer-oopif.html new file mode 100644 index 0000000000..4bf2526ddb --- /dev/null +++ b/tests/assets/outer-oopif.html @@ -0,0 +1,14 @@ + diff --git a/tests/library/chromium/oopif.spec.ts b/tests/library/chromium/oopif.spec.ts index 49855fb3f0..51ba446255 100644 --- a/tests/library/chromium/oopif.spec.ts +++ b/tests/library/chromium/oopif.spec.ts @@ -94,6 +94,16 @@ it('should expose function', async ({ page, browser, server }) => { expect(result).toBe(36); }); +it('should not hang in exposeBinding when nested oopifs navigate', async ({ page, browser, server }) => { + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13637' }); + await page.goto(server.PREFIX + '/outer-oopif.html'); + await page.context().exposeBinding('mul', (source, a, b) => a * b); + const result = await page.evaluate(async function() { + return await window['mul'](9, 4); + }); + expect(result).toBe(36); +}); + it('should emulate media', async ({ page, browser, server }) => { await page.goto(server.PREFIX + '/dynamic-oopif.html'); expect(page.frames().length).toBe(2);