aboutsummaryrefslogtreecommitdiff
path: root/extensions
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-11-08 16:17:05 +0000
committerIgor Soarez <igor@soarez.org>2026-11-08 16:17:05 +0000
commit516d6f05e7f67a1ef48f1ffc28d27e136e87cd1a (patch)
treecd2f1bc29467e81a204642de9d152be01d2f7599 /extensions
parent69d7159eef27d541cf01ea3ea1471ef0ce3a7677 (diff)
overlay.clear: match the prompt's frame by the tabId in its URL, and report what it saw
The password manager's frame target can come without a tabId, so it was missed (error said 'unknown', remaining targets empty). Match by the ?tabId= in the frame URL too, and when the tab stays blocked report every foreign target and what the injected script saw, so the next step is based on data.
Diffstat (limited to 'extensions')
-rw-r--r--extensions/chrome-profile-bridge/browser-extension/service_worker.js16
-rw-r--r--extensions/chrome-profile-bridge/index.ts2
2 files changed, 13 insertions, 5 deletions
diff --git a/extensions/chrome-profile-bridge/browser-extension/service_worker.js b/extensions/chrome-profile-bridge/browser-extension/service_worker.js
index e3f5a05..de4f340 100644
--- a/extensions/chrome-profile-bridge/browser-extension/service_worker.js
+++ b/extensions/chrome-profile-bridge/browser-extension/service_worker.js
@@ -682,6 +682,12 @@ async function findForeignExtensionTargets() {
}
}
+// Other extensions' targets that belong to this tab: by the target's tabId, or by the tabId that
+// password managers put in the frame's own URL (the frame target can come without a tabId).
+function foreignTargetsForTab(all, tabId) {
+ return all.filter((t) => t.tabId === tabId || new RegExp(`[?&]tabId=${tabId}(?:[&#]|$)`).test(String(t.url || "")));
+}
+
function extractForeignExtId(targets) {
for (const t of targets) {
if (t.extensionId && t.extensionId !== chrome.runtime.id) return t.extensionId;
@@ -699,7 +705,7 @@ const FOREIGN_EXTENSION_BLOCK = /Cannot access a chrome-extension:\/\/ URL of di
// facts and lets it decide, with overlay.clear (chrome_clear_overlay) to close it when it is only
// in the way.
async function overlayBlockedError(tabId) {
- const targets = (await findForeignExtensionTargets()).filter((t) => t.tabId === tabId);
+ const targets = foreignTargetsForTab(await findForeignExtensionTargets(), tabId);
const url = String(targets[0]?.url || "").replace(/[?#].*$/, "");
const id = extractForeignExtId(targets) || "unknown";
return new Error(
@@ -736,7 +742,8 @@ function clearForeignOverlays(ownId) {
root.dispatchEvent(new PointerEvent("pointerup", { ...init, pointerId: 1, pointerType: "mouse", isPrimary: true, buttons: 0 }));
root.dispatchEvent(new MouseEvent("mouseup", { ...init, buttons: 0 }));
root.click();
- return { removed };
+ const custom = Array.from(document.querySelectorAll("*")).filter((el) => el.tagName.includes("-")).map((el) => el.tagName.toLowerCase());
+ return { removed, seen: { iframes: document.querySelectorAll("iframe").length, customElements: [...new Set(custom)].slice(0, 10), active: document.activeElement?.tagName ?? null } };
}
async function clearOverlay(params) {
@@ -757,8 +764,9 @@ async function clearOverlay(params) {
debuggerAttachable = true;
} catch {}
}
- const remaining = (await findForeignExtensionTargets()).filter((t) => t.tabId === tab.id).map((t) => String(t.url || "").replace(/[?#].*$/, ""));
- return { tab: { id: tab.id, url: tab.url, title: tab.title }, removedFrames: injected.removed, remainingForeignTargets: remaining, debuggerAttachable, cleared: debuggerAttachable && remaining.length === 0 };
+ const allForeign = await findForeignExtensionTargets();
+ const remaining = foreignTargetsForTab(allForeign, tab.id).map((t) => String(t.url || "").replace(/[?#].*$/, ""));
+ return { tab: { id: tab.id, url: tab.url, title: tab.title }, removedFrames: injected.removed, remainingForeignTargets: remaining, foreignTargetsAnywhere: allForeign.map((t) => ({ type: t.type, tabId: t.tabId, url: String(t.url || "").replace(/[?#].*$/, "") })).slice(0, 20), injectedReport: injected, debuggerAttachable, cleared: debuggerAttachable && remaining.length === 0 };
}
async function cdp(tabId, method, params, opts) {
diff --git a/extensions/chrome-profile-bridge/index.ts b/extensions/chrome-profile-bridge/index.ts
index 8442a58..4384967 100644
--- a/extensions/chrome-profile-bridge/index.ts
+++ b/extensions/chrome-profile-bridge/index.ts
@@ -2299,7 +2299,7 @@ Usage rules:
const removed = result?.removedFrames ?? [];
const text = result?.cleared
? `Cleared${removed.length ? `: removed ${removed.join(", ")}` : " (nothing to remove, the prompt had already gone)"}. Tab ${result.tab?.id} is usable again.`
- : `The tab is still blocked${result?.remainingForeignTargets?.length ? ` by ${result.remainingForeignTargets.join(", ")}` : ""}${removed.length ? ` (removed ${removed.join(", ")} but it is still there or came back)` : ""}. The prompt may be the password manager's toolbar popup or desktop app, which this tool cannot close: tell the user, or navigate the tab away.`;
+ : `The tab is still blocked${result?.remainingForeignTargets?.length ? ` by ${result.remainingForeignTargets.join(", ")}` : ""}${removed.length ? ` (removed ${removed.join(", ")} but it is still there or came back)` : ""}. The prompt may be the password manager's toolbar popup or desktop app, which this tool cannot close: tell the user, or navigate the tab away. Diagnostics: ${JSON.stringify({ foreignTargetsAnywhere: (result as any)?.foreignTargetsAnywhere, injectedReport: (result as any)?.injectedReport })}`;
return { content: [{ type: "text", text }], details: { value: (value ?? null) as Json } };
},
});