Browser: add URL fallback for relay tab matching (#1999)

Co-authored-by: João Paulo Furtado <jonit-dev@users.noreply.github.com>
This commit is contained in:
Shadow
2026-01-25 21:04:41 -06:00
parent 159f6bfddd
commit 5d2ef89e03
2 changed files with 49 additions and 4 deletions

View File

@@ -337,12 +337,56 @@ async function pageTargetId(page: Page): Promise<string | null> {
}
}
async function findPageByTargetId(browser: Browser, targetId: string): Promise<Page | null> {
async function findPageByTargetId(
browser: Browser,
targetId: string,
cdpUrl?: string,
): Promise<Page | null> {
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<void> {
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<void> {
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");
}