diff --git a/.github/workflows/puppeteer.yml b/.github/workflows/puppeteer.yml index 9bc82c4a70..f507a42975 100644 --- a/.github/workflows/puppeteer.yml +++ b/.github/workflows/puppeteer.yml @@ -30,8 +30,6 @@ jobs: uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: 'bidi' - - name: Set up FFmpeg - uses: FedericoCarboni/setup-ffmpeg@36c6454b5a2348e7794ba2d82a21506605921e3d # v3.0.0 - name: Set up Node.js uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/src/bidiMapper/modules/cdp/CdpTargetManager.ts b/src/bidiMapper/modules/cdp/CdpTargetManager.ts index 74c02ce4fa..1af49d1e1e 100644 --- a/src/bidiMapper/modules/cdp/CdpTargetManager.ts +++ b/src/bidiMapper/modules/cdp/CdpTargetManager.ts @@ -43,6 +43,7 @@ const cdpToBidiTargetTypes = { export class CdpTargetManager { readonly #browserCdpClient: CdpClient; readonly #cdpConnection: CdpConnection; + readonly #targetKeysToBeIgnoredByAutoAttach = new Set(); readonly #selfTargetId: string; readonly #eventManager: EventManager; @@ -70,6 +71,7 @@ export class CdpTargetManager { ) { this.#cdpConnection = cdpConnection; this.#browserCdpClient = browserCdpClient; + this.#targetKeysToBeIgnoredByAutoAttach.add(selfTargetId); this.#selfTargetId = selfTargetId; this.#eventManager = eventManager; this.#browsingContextStorage = browsingContextStorage; @@ -151,18 +153,50 @@ export class CdpTargetManager { const {sessionId, targetInfo} = params; const targetCdpClient = this.#cdpConnection.getCdpClient(sessionId); + const detach = async () => { + // Detaches and resumes the target suppressing errors. + await targetCdpClient + .sendCommand('Runtime.runIfWaitingForDebugger') + .then(() => + parentSessionCdpClient.sendCommand('Target.detachFromTarget', params) + ) + .catch((error) => this.#logger?.(LogType.debugError, error)); + }; + + if (this.#selfTargetId !== targetInfo.targetId) { + // Service workers are special case because they attach to the + // browser target and the page target (so twice per worker) during + // the regular auto-attach and might hang if the CDP session on + // the browser level is not detached. The logic to detach the + // right session is handled in the switch below. + const targetKey = + targetInfo.type === 'service_worker' + ? `${parentSessionCdpClient.sessionId}_${targetInfo.targetId}` + : targetInfo.targetId; + + // Mapper generally only needs one session per target. If we + // receive additional auto-attached sessions, that is very likely + // coming from custom CDP sessions. + if (this.#targetKeysToBeIgnoredByAutoAttach.has(targetKey)) { + // Return to leave the session untouched. + return; + } + this.#targetKeysToBeIgnoredByAutoAttach.add(targetKey); + } + switch (targetInfo.type) { case 'page': case 'iframe': { - if (targetInfo.targetId === this.#selfTargetId) { - break; + if (this.#selfTargetId === targetInfo.targetId) { + void detach(); + return; } const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo); const maybeContext = this.#browsingContextStorage.findContext( targetInfo.targetId ); - if (maybeContext) { + if (maybeContext && targetInfo.type === 'iframe') { // OOPiF. maybeContext.updateCdpTarget(cdpTarget); } else { @@ -203,7 +237,8 @@ export class CdpTargetManager { }); // If there is no browsing context, this worker is already terminated. if (!realm) { - break; + void detach(); + return; } const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo); @@ -230,12 +265,7 @@ export class CdpTargetManager { // DevTools or some other not supported by BiDi target. Just release // debugger and ignore them. - targetCdpClient - .sendCommand('Runtime.runIfWaitingForDebugger') - .then(() => - parentSessionCdpClient.sendCommand('Target.detachFromTarget', params) - ) - .catch((error) => this.#logger?.(LogType.debugError, error)); + void detach(); } #createCdpTarget( diff --git a/tests/service_worker/test_navigate.py b/tests/service_worker/test_navigate.py index b416659228..a3b98535f9 100644 --- a/tests/service_worker/test_navigate.py +++ b/tests/service_worker/test_navigate.py @@ -24,7 +24,7 @@ }], indirect=True) async def test_serviceWorker_acceptInsecureCertsCapability_respected( - websocket, context_id, url_bad_ssl, local_server_bad_ssl): + websocket, context_id, local_server_bad_ssl): service_worker_script = local_server_bad_ssl.url_200( content='', content_type='text/javascript') service_worker_page = local_server_bad_ssl.url_200(content=f"""