diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index 5f0a07a7f1..fe74ae5b0b 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -196,6 +196,18 @@ export class CRBrowser extends Browser { context.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker); return; } + + // Detach from any targets we are not interested in, to avoid side-effects. + // + // One example of a side effect: upon shared worker restart, we receive + // Inspector.targetReloadedAfterCrash and backend waits for Runtime.runIfWaitingForDebugger + // from any attached client. If we do not resume, shared worker will stall. + // + // Ideally, detaching should resume any target, but there is a bug in the backend, + // so we must Runtime.runIfWaitingForDebugger first. + session._sendMayFail('Runtime.runIfWaitingForDebugger').then(() => { + this._session._sendMayFail('Target.detachFromTarget', { sessionId }); + }); } _onDetachedFromTarget(payload: Protocol.Target.detachFromTargetParameters) { diff --git a/packages/playwright-core/src/server/chromium/crServiceWorker.ts b/packages/playwright-core/src/server/chromium/crServiceWorker.ts index 6c3c060018..55033c6e2e 100644 --- a/packages/playwright-core/src/server/chromium/crServiceWorker.ts +++ b/packages/playwright-core/src/server/chromium/crServiceWorker.ts @@ -49,6 +49,10 @@ export class CRServiceWorker extends Worker { session.send('Runtime.enable', {}).catch(e => { }); session.send('Runtime.runIfWaitingForDebugger').catch(e => { }); + session.on('Inspector.targetReloadedAfterCrash', () => { + // Resume service worker after restart. + session._sendMayFail('Runtime.runIfWaitingForDebugger', {}); + }); } async updateOffline(initial: boolean): Promise { diff --git a/tests/assets/shared-worker/shared-worker.html b/tests/assets/shared-worker/shared-worker.html new file mode 100644 index 0000000000..0a6fb67199 --- /dev/null +++ b/tests/assets/shared-worker/shared-worker.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/tests/assets/shared-worker/shared-worker.js b/tests/assets/shared-worker/shared-worker.js new file mode 100644 index 0000000000..5e599482f5 --- /dev/null +++ b/tests/assets/shared-worker/shared-worker.js @@ -0,0 +1,4 @@ +onconnect = event => { + const port = event.ports[0]; + port.onmessage = e => port.postMessage('echo:' + e.data); +}; diff --git a/tests/library/shared-worker.spec.ts b/tests/library/shared-worker.spec.ts new file mode 100644 index 0000000000..de356a6279 --- /dev/null +++ b/tests/library/shared-worker.spec.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { contextTest as test, expect } from '../config/browserTest'; + +test('should survive shared worker restart', async ({ context, server }) => { + const page1 = await context.newPage(); + await page1.goto(server.PREFIX + '/shared-worker/shared-worker.html'); + expect(await page1.evaluate('window.sharedWorkerResponsePromise')).toBe('echo:hello'); + await page1.close(); + + const page2 = await context.newPage(); + await page2.goto(server.PREFIX + '/shared-worker/shared-worker.html'); + expect(await page2.evaluate('window.sharedWorkerResponsePromise')).toBe('echo:hello'); + await page2.close(); +});