Skip to content

Commit 3e91876

Browse files
authored
fix: replace all about:blank with data: URI to prevent New Tab Override interception (#257)
Root cause: getAutomationWindow and resolveTabId used about:blank which New Tab Override extensions intercept immediately, replacing it with chrome-extension:// URLs that cannot be debugged. Changes: - Window creation: about:blank → data:text/html - reuseTab fallback: about:blank → data:text/html - newTab handler: about:blank → data:text/html - Added diagnostic logging to resolveTabId for debugging - Synced extension version to 1.2.4 Ref: #249
1 parent 7c02588 commit 3e91876

File tree

6 files changed

+27
-16
lines changed

6 files changed

+27
-16
lines changed

extension/dist/background.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ async function getAutomationWindow(workspace) {
228228
}
229229
}
230230
const win = await chrome.windows.create({
231-
url: "about:blank",
231+
url: "data:text/html,<html></html>",
232232
focused: false,
233233
width: 1280,
234234
height: 900,
@@ -309,6 +309,7 @@ async function resolveTabId(tabId, workspace) {
309309
if (tabId !== void 0) {
310310
try {
311311
const tab = await chrome.tabs.get(tabId);
312+
console.log(`[opencli] resolveTabId: explicit tabId=${tabId}, url=${tab.url}`);
312313
if (isDebuggableUrl(tab.url)) return tabId;
313314
console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
314315
} catch {
@@ -318,10 +319,14 @@ async function resolveTabId(tabId, workspace) {
318319
const windowId = await getAutomationWindow(workspace);
319320
const tabs = await chrome.tabs.query({ windowId });
320321
const debuggableTab = tabs.find((t) => t.id && isDebuggableUrl(t.url));
321-
if (debuggableTab?.id) return debuggableTab.id;
322+
if (debuggableTab?.id) {
323+
console.log(`[opencli] resolveTabId: found debuggable tab ${debuggableTab.id} (${debuggableTab.url})`);
324+
return debuggableTab.id;
325+
}
326+
console.warn(`[opencli] resolveTabId: no debuggable tabs found, tabs: ${tabs.map((t) => `${t.id}=${t.url}`).join(", ")}`);
322327
const reuseTab = tabs.find((t) => t.id);
323328
if (reuseTab?.id) {
324-
await chrome.tabs.update(reuseTab.id, { url: "about:blank" });
329+
await chrome.tabs.update(reuseTab.id, { url: "data:text/html,<html></html>" });
325330
await new Promise((resolve) => setTimeout(resolve, 300));
326331
try {
327332
const updated = await chrome.tabs.get(reuseTab.id);
@@ -335,7 +340,7 @@ async function resolveTabId(tabId, workspace) {
335340
} catch {
336341
}
337342
}
338-
const newTab = await chrome.tabs.create({ windowId, url: "about:blank", active: true });
343+
const newTab = await chrome.tabs.create({ windowId, url: "data:text/html,<html></html>", active: true });
339344
if (!newTab.id) throw new Error("Failed to create tab in automation window");
340345
return newTab.id;
341346
}
@@ -423,7 +428,7 @@ async function handleTabs(cmd, workspace) {
423428
}
424429
case "new": {
425430
const windowId = await getAutomationWindow(workspace);
426-
const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? "about:blank", active: true });
431+
const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? "data:text/html,<html></html>", active: true });
427432
return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } };
428433
}
429434
case "close": {

extension/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "OpenCLI",
4-
"version": "0.2.0",
4+
"version": "1.2.4",
55
"description": "Bridge between opencli CLI and your browser — execute commands, read cookies, manage tabs.",
66
"permissions": [
77
"debugger",

extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opencli-extension",
3-
"version": "0.2.0",
3+
"version": "1.2.4",
44
"private": true,
55
"type": "module",
66
"scripts": {

extension/src/background.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,10 @@ async function getAutomationWindow(workspace: string): Promise<number> {
135135
}
136136
}
137137

138-
// Create a new window with about:blank (not chrome://newtab which blocks scripting)
138+
// Create a new window with a data: URI that New Tab Override extensions cannot intercept.
139+
// Using about:blank would be hijacked by extensions like "New Tab Override".
139140
const win = await chrome.windows.create({
140-
url: 'about:blank',
141+
url: 'data:text/html,<html></html>',
141142
focused: false,
142143
width: 1280,
143144
height: 900,
@@ -244,6 +245,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
244245
if (tabId !== undefined) {
245246
try {
246247
const tab = await chrome.tabs.get(tabId);
248+
console.log(`[opencli] resolveTabId: explicit tabId=${tabId}, url=${tab.url}`);
247249
if (isDebuggableUrl(tab.url)) return tabId;
248250
// Tab exists but URL is not debuggable — fall through to auto-resolve
249251
console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
@@ -259,15 +261,19 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
259261
// Prefer an existing debuggable tab (about:blank, http://, https://, etc.)
260262
const tabs = await chrome.tabs.query({ windowId });
261263
const debuggableTab = tabs.find(t => t.id && isDebuggableUrl(t.url));
262-
if (debuggableTab?.id) return debuggableTab.id;
264+
if (debuggableTab?.id) {
265+
console.log(`[opencli] resolveTabId: found debuggable tab ${debuggableTab.id} (${debuggableTab.url})`);
266+
return debuggableTab.id;
267+
}
268+
console.warn(`[opencli] resolveTabId: no debuggable tabs found, tabs: ${tabs.map(t => `${t.id}=${t.url}`).join(', ')}`);
263269

264270
// No debuggable tab found — this typically happens when a "New Tab Override"
265271
// extension replaces about:blank with a chrome-extension:// page.
266272
// Reuse the first existing tab by navigating it to about:blank (avoids
267273
// accumulating orphan tabs if chrome.tabs.create is also intercepted).
268274
const reuseTab = tabs.find(t => t.id);
269275
if (reuseTab?.id) {
270-
await chrome.tabs.update(reuseTab.id, { url: 'about:blank' });
276+
await chrome.tabs.update(reuseTab.id, { url: 'data:text/html,<html></html>' });
271277
// Wait for the navigation to take effect
272278
await new Promise(resolve => setTimeout(resolve, 300));
273279
// Verify the URL is actually debuggable (New Tab Override may have intercepted)
@@ -288,7 +294,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
288294
}
289295

290296
// Window has no debuggable tabs — create one
291-
const newTab = await chrome.tabs.create({ windowId, url: 'about:blank', active: true });
297+
const newTab = await chrome.tabs.create({ windowId, url: 'data:text/html,<html></html>', active: true });
292298
if (!newTab.id) throw new Error('Failed to create tab in automation window');
293299
return newTab.id;
294300
}
@@ -397,7 +403,7 @@ async function handleTabs(cmd: Command, workspace: string): Promise<Result> {
397403
}
398404
case 'new': {
399405
const windowId = await getAutomationWindow(workspace);
400-
const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? 'about:blank', active: true });
406+
const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? 'data:text/html,<html></html>', active: true });
401407
return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } };
402408
}
403409
case 'close': {

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jackwener/opencli",
3-
"version": "1.2.3",
3+
"version": "1.2.4",
44
"publishConfig": {
55
"access": "public"
66
},

0 commit comments

Comments
 (0)