From 5d2ef89e0367b2301e2a5125e7e644277a803fa7 Mon Sep 17 00:00:00 2001 From: Shadow Date: Sun, 25 Jan 2026 21:04:41 -0600 Subject: [PATCH] Browser: add URL fallback for relay tab matching (#1999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Paulo Furtado --- CHANGELOG.md | 1 + src/browser/pw-session.ts | 52 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19cea8844..23d5d51b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Status: unreleased. - Docs: add Render deployment guide. (#1975) Thanks @anurag. - CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi. - macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn. +- Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev. ## 2026.1.24-3 diff --git a/src/browser/pw-session.ts b/src/browser/pw-session.ts index 0c7fa9f48..e1dbcf7a1 100644 --- a/src/browser/pw-session.ts +++ b/src/browser/pw-session.ts @@ -337,12 +337,56 @@ async function pageTargetId(page: Page): Promise { } } -async function findPageByTargetId(browser: Browser, targetId: string): Promise { +async function findPageByTargetId( + browser: Browser, + targetId: string, + cdpUrl?: string, +): Promise { const pages = await getAllPages(browser); + // First, try the standard CDP session approach for (const page of pages) { const tid = await pageTargetId(page).catch(() => null); if (tid && tid === targetId) return page; } + // If CDP sessions fail (e.g., extension relay blocks Target.attachToBrowserTarget), + // fall back to URL-based matching using the /json/list endpoint + if (cdpUrl) { + try { + const baseUrl = cdpUrl + .replace(/\/+$/, "") + .replace(/^ws:/, "http:") + .replace(/\/cdp$/, ""); + const response = await fetch(`${baseUrl}/json/list`); + if (response.ok) { + const targets = (await response.json()) as Array<{ + id: string; + url: string; + title?: string; + }>; + const target = targets.find((t) => t.id === targetId); + if (target) { + // Try to find a page with matching URL + const urlMatch = pages.filter((p) => p.url() === target.url); + if (urlMatch.length === 1) { + return urlMatch[0]; + } + // If multiple URL matches, use index-based matching as fallback + // This works when Playwright and the relay enumerate tabs in the same order + if (urlMatch.length > 1) { + const sameUrlTargets = targets.filter((t) => t.url === target.url); + if (sameUrlTargets.length === urlMatch.length) { + const idx = sameUrlTargets.findIndex((t) => t.id === targetId); + if (idx >= 0 && idx < urlMatch.length) { + return urlMatch[idx]; + } + } + } + } + } + } catch { + // Ignore fetch errors and fall through to return null + } + } return null; } @@ -355,7 +399,7 @@ export async function getPageForTargetId(opts: { if (!pages.length) throw new Error("No pages available in the connected browser."); const first = pages[0]; if (!opts.targetId) return first; - const found = await findPageByTargetId(browser, opts.targetId); + const found = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl); if (!found) { // Extension relays can block CDP attachment APIs (e.g. Target.attachToBrowserTarget), // which prevents us from resolving a page's targetId via newCDPSession(). If Playwright @@ -496,7 +540,7 @@ export async function closePageByTargetIdViaPlaywright(opts: { targetId: string; }): Promise { const { browser } = await connectBrowser(opts.cdpUrl); - const page = await findPageByTargetId(browser, opts.targetId); + const page = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl); if (!page) { throw new Error("tab not found"); } @@ -512,7 +556,7 @@ export async function focusPageByTargetIdViaPlaywright(opts: { targetId: string; }): Promise { const { browser } = await connectBrowser(opts.cdpUrl); - const page = await findPageByTargetId(browser, opts.targetId); + const page = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl); if (!page) { throw new Error("tab not found"); }