From 9e2c0ad39552a1ba48f6c28fbe8cddce1991d933 Mon Sep 17 00:00:00 2001 From: Fred Chu Date: Tue, 28 Apr 2026 15:15:44 +0800 Subject: [PATCH] fix(extension): add "tabs" permission so live tab awareness works on non-localhost sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without "tabs", chrome.tabs.query() returns tab objects with undefined url/title for any site outside host_permissions (everything except 127.0.0.1). snapshotTabs() in background.js then writes empty strings into tabs.json, and the active-tab.json gate (`if (active && active.url && ...)`) silently skips the write. The sidebar agent loses track of what page the user is actually on. The "tabs" permission is a no-warning install-time grant in MV3 (not a host permission). It exposes tab.url / tab.title / tab.favIconUrl across all tabs, but does not grant content-script injection or cross-origin fetch — a tight scope match for what snapshotTabs() actually needs. activeTab is too narrow (only after a user gesture on the extension action) for background polling. Reproduces on every gstack version since v1.14.0.0 (ed1e4be), where the chrome.tabs.onUpdated listener was added but the manifest only gained `ws://127.0.0.1:*/` for the new PTY channel — tab-related permissions were never broadened. Adds a manifest source-level test in sidebar-tabs.test.ts that asserts `permissions` contains "tabs", to guard against future regressions. Closes #1256 --- browse/test/sidebar-tabs.test.ts | 12 ++++++++++++ extension/manifest.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/browse/test/sidebar-tabs.test.ts b/browse/test/sidebar-tabs.test.ts index 31e57c4b6..91d50dcef 100644 --- a/browse/test/sidebar-tabs.test.ts +++ b/browse/test/sidebar-tabs.test.ts @@ -254,3 +254,15 @@ describe('manifest: ws permission + xterm-safe CSP', () => { } }); }); + +describe('manifest: live tab awareness needs "tabs" permission', () => { + // Without "tabs", chrome.tabs.query() returns tab objects with undefined + // url/title for any site outside host_permissions (e.g., everything except + // 127.0.0.1). snapshotTabs() then writes empty strings into tabs.json and + // active-tab.json silently skips the write — the sidebar agent loses track + // of what page the user is on. activeTab is too narrow (only after a user + // gesture on the extension action) for background polling. + test('permissions includes "tabs"', () => { + expect(MANIFEST.permissions).toContain('tabs'); + }); +}); diff --git a/extension/manifest.json b/extension/manifest.json index 502c5bb79..962562646 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -3,7 +3,7 @@ "name": "gstack browse", "version": "0.1.0", "description": "Live activity feed and @ref overlays for gstack browse", - "permissions": ["sidePanel", "storage", "activeTab", "scripting"], + "permissions": ["sidePanel", "storage", "activeTab", "scripting", "tabs"], "host_permissions": ["http://127.0.0.1:*/", "ws://127.0.0.1:*/"], "action": { "default_icon": {