From 183c451a54d3f5dd69bd1076f45fd6485aa234c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Fri, 28 Aug 2026 17:02:13 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=20Popup?= =?UTF-8?q?=20=E5=85=A8=E5=B1=80=E5=85=B3=E9=97=AD=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=97=B6=E6=9C=89=E6=97=B6=E6=97=A0=E3=80=81=E4=B8=94=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E9=A1=B5=E8=84=9A=E6=9C=AC=E5=88=97=E8=A1=A8=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Popup 的页面状态判定把「本 tab 有没有 content script 报到」排在全局开关之前, 于是一个全局事实被标签页级、且会陈旧的证据否决: - 注入证据存放在 session cache 的 tabLoaded:,只在标签页关闭时、 或 onBeforeNavigate 且 runCountMap.has(tabId) && !isLoadScripts 时才清理。 runCountMap 是 Service Worker 的内存 Map,SW 一被回收就空了。 - 结果:关掉全局开关后刷新页面,SW 还活着 → 记录被清 → 有提示; SW 中途重启过 → 记录留存 → 判为 ok → 没提示。新标签页则总是有提示。 同一个开关状态下提示时有时无。 改为把 userscripts-unavailable / scripts-disabled 两个「扩展整体没跑起来」的 判据移到注入证据之前——它们与哪个标签页无关,不该被某个 tab 的历史否决。 同时收窄 #1687 引入的空列表规则:原本任何非 ok 状态都返回空的 scriptList, 现在只有 restricted(浏览器保留页、自家扩展商店)保持空——那里脚本猫永远触及 不到。其余状态的抑制原因都可以解除(开开关、开开发者模式、移出黑名单、给文件 访问权限、刷新页面),照常列出按网址匹配的脚本,用户才知道解除后哪些会生效; 顶部提示已经说明了它们现在为什么没跑。 --- src/app/service/service_worker/popup.test.ts | 47 ++++++++++++++++---- src/app/service/service_worker/popup.ts | 16 ++++--- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/app/service/service_worker/popup.test.ts b/src/app/service/service_worker/popup.test.ts index d42d19a21..fdf4f5750 100644 --- a/src/app/service/service_worker/popup.test.ts +++ b/src/app/service/service_worker/popup.test.ts @@ -456,7 +456,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.backScriptList.map((s) => s.uuid)).toEqual([bgUuid]); }); - it("黑名单页应返回 blacklist,且不列出脚本(黑名单页同样不会注入)", async () => { + it("黑名单页应返回 blacklist,并列出匹配脚本(移出黑名单后它们就会跑)", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { @@ -470,7 +470,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("blacklist"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("可注入页收到 content script 报到后返回 ok,正常列出脚本", async () => { @@ -487,7 +487,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("可注入页但从未收到报到(页面比扩展旧 / 被策略拦下)应返回 not-injected", async () => { + it("可注入页但从未收到报到(页面比扩展旧 / 被策略拦下)应返回 not-injected,并列出匹配脚本", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -497,7 +497,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("not-injected"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("同 origin 内的后续导航(SPA 换页)仍算已注入", async () => { @@ -544,13 +544,18 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("file:// 页未授权文件访问时应返回 file-access-denied", async () => { + it("file:// 页未授权文件访问时应返回 file-access-denied,并列出匹配脚本", async () => { vi.spyOn(extensionMock, "isAllowedFileSchemeAccess").mockResolvedValue(false); - const { service } = createService(); + const uuid = "allsite"; + const { service } = createService({ + runtime: { getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, + scriptDAO: { gets: vi.fn().mockResolvedValue([createScript(uuid)]) }, + }); const result = await service.getPopupData({ tabId: 1, url: "file:///tmp/a.html" }); expect(result.pageStatus).toBe("file-access-denied"); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("未注入且全局脚本开关已关闭时应指出开关,而不是让用户白刷新", async () => { @@ -569,7 +574,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.pageStatus).toBe("userscripts-unavailable"); }); - it("关掉开关不会杀死已注入页面上正在跑的脚本,该页仍应为 ok", async () => { + it("全局开关已关闭时,本 tab 报到过也应报 scripts-disabled:提示不能取决于标签页新旧", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { isLoadScripts: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -579,7 +584,33 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); - expect(result.pageStatus).toBe("ok"); + expect(result.pageStatus).toBe("scripts-disabled"); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + }); + + it("全局开关已关闭的新标签页仍应列出按网址匹配的脚本", async () => { + const uuid = "allsite"; + const { service } = createService({ + runtime: { isLoadScripts: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, + scriptDAO: { gets: vi.fn().mockResolvedValue([createScript(uuid)]) }, + }); + + const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); + + expect(result.pageStatus).toBe("scripts-disabled"); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + }); + + it("UserScripts API 不可用时同样列出匹配脚本:解除后这些脚本就会生效", async () => { + const uuid = "allsite"; + const { service } = createService({ + runtime: { isUserScriptsAvailable: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, + scriptDAO: { gets: vi.fn().mockResolvedValue([createScript(uuid)]) }, + }); + + const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); + + expect(result.pageStatus).toBe("userscripts-unavailable"); expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index 1c6b3fe77..f921137e3 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -376,9 +376,11 @@ export class PopupService { async getPopupData(req: GetPopupDataReq): Promise { const { url, tabId } = req; const pageStatus = await this.getPageStatus(tabId, url); - if (pageStatus !== "ok") { - // 页面上不会有任何脚本运行,列出「匹配到的」脚本只会让人以为它们在跑(#1687); - // 后台脚本与当前页无关,照常返回。 + if (pageStatus === "restricted") { + // 浏览器保留页与自家扩展商店上脚本猫永远触及不到,列出「匹配到的」脚本只会让人以为 + // 它们在跑(#1687)。其余状态的抑制原因都可以解除——开开关、开开发者模式、移出黑名单、 + // 给文件访问权限、刷新页面——照常列出匹配脚本,用户才知道解除后哪些会生效;顶部提示 + // 已经说明了它们现在为什么没跑。后台脚本与当前页无关,照常返回。 return { pageStatus, scriptList: [], @@ -443,6 +445,9 @@ export class PopupService { * 判断当前页脚本猫是否触及得到。 * * 顺序有意为之:浏览器保留页与黑名单是「无论如何都不会注入」的确定结论,先判; + * 接着是扩展整体没跑起来的两种情况(全局开关关闭、UserScripts API 不可用),它们与具体 + * 标签页无关,必须先于注入证据 —— 注入证据只是「本 tab 曾经报到过」,页面刷新与关开关都 + * 不会让它失效,用它去否定全局状态会使同一开关状态下老标签页没提示、新标签页有提示。 * 其余情况以「本 tab 有没有 content script 报到」为准 —— 它是运行时证据, * 比协议白名单准(企业策略、扩展商店等都拦不住白名单)。file:// 的权限查询只用来 * 给未注入的情况一个更准确的原因,不能反过来否定已经注入成功的事实(Firefox 上该 @@ -452,13 +457,12 @@ export class PopupService { const kind = getPageAccessKind(url); if (kind === "restricted") return "restricted"; if (this.runtime.isUrlBlacklist(url)) return "blacklist"; - if (await this.isTabInjected(tabId, url)) return "ok"; - // 以下都是「确认没注入」,只为给出更准确的原因。 // 脚本功能整体没开时 content script 根本没注册(registerUserscripts 直接 return), // 此时说「刷新页面后生效」是错的——刷新永远不会生效,得先开开关/开发者模式。 - // 同样放在注入证据之后:关掉开关不会杀死已注入页面上正在跑的脚本。 if (!this.runtime.isUserScriptsAvailable) return "userscripts-unavailable"; if (!this.runtime.isLoadScripts) return "scripts-disabled"; + if (await this.isTabInjected(tabId, url)) return "ok"; + // 以下都是「确认没注入」,只为给出更准确的原因。 // 两项判据都与浏览器有关(Edge 商店在 Chrome 里是普通网页;Firefox 的文件访问开关语义也不同), // 放在注入证据之后才不会误伤实际能运行的页面。 if (isExtensionStoreUrl(url)) return "restricted"; From a07dfd432e8f864a8600997c4770edd7e4b32417 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:33:09 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=9D=E6=8C=81=20Popup?= =?UTF-8?q?=20=E9=9D=9E=E8=BF=90=E8=A1=8C=E9=A1=B5=E9=9D=A2=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/popup.test.ts | 24 +++++++++++--------- src/app/service/service_worker/popup.ts | 8 +++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/app/service/service_worker/popup.test.ts b/src/app/service/service_worker/popup.test.ts index fdf4f5750..1af9f8b35 100644 --- a/src/app/service/service_worker/popup.test.ts +++ b/src/app/service/service_worker/popup.test.ts @@ -456,7 +456,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.backScriptList.map((s) => s.uuid)).toEqual([bgUuid]); }); - it("黑名单页应返回 blacklist,并列出匹配脚本(移出黑名单后它们就会跑)", async () => { + it("黑名单页应返回 blacklist,且不列出脚本(黑名单页同样不会注入)", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { @@ -470,7 +470,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("blacklist"); - expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + expect(result.scriptList).toEqual([]); }); it("可注入页收到 content script 报到后返回 ok,正常列出脚本", async () => { @@ -487,7 +487,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("可注入页但从未收到报到(页面比扩展旧 / 被策略拦下)应返回 not-injected,并列出匹配脚本", async () => { + it("可注入页但从未收到报到(页面比扩展旧 / 被策略拦下)应返回 not-injected", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -497,7 +497,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("not-injected"); - expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + expect(result.scriptList).toEqual([]); }); it("同 origin 内的后续导航(SPA 换页)仍算已注入", async () => { @@ -544,7 +544,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("file:// 页未授权文件访问时应返回 file-access-denied,并列出匹配脚本", async () => { + it("file:// 页未授权文件访问时应返回 file-access-denied", async () => { vi.spyOn(extensionMock, "isAllowedFileSchemeAccess").mockResolvedValue(false); const uuid = "allsite"; const { service } = createService({ @@ -555,7 +555,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: "file:///tmp/a.html" }); expect(result.pageStatus).toBe("file-access-denied"); - expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + expect(result.scriptList).toEqual([]); }); it("未注入且全局脚本开关已关闭时应指出开关,而不是让用户白刷新", async () => { @@ -564,6 +564,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("scripts-disabled"); + expect(result.scriptList).toEqual([]); }); it("未注入且 UserScripts API 不可用时应指出浏览器设置,而不是让用户白刷新", async () => { @@ -572,6 +573,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("userscripts-unavailable"); + expect(result.scriptList).toEqual([]); }); it("全局开关已关闭时,本 tab 报到过也应报 scripts-disabled:提示不能取决于标签页新旧", async () => { @@ -585,10 +587,10 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("scripts-disabled"); - expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + expect(result.scriptList).toEqual([]); }); - it("全局开关已关闭的新标签页仍应列出按网址匹配的脚本", async () => { + it("全局开关已关闭的新标签页仍应隐藏当前页脚本列表", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { isLoadScripts: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -598,10 +600,10 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("scripts-disabled"); - expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + expect(result.scriptList).toEqual([]); }); - it("UserScripts API 不可用时同样列出匹配脚本:解除后这些脚本就会生效", async () => { + it("UserScripts API 不可用时同样隐藏当前页脚本列表", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { isUserScriptsAvailable: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -611,7 +613,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("userscripts-unavailable"); - expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); + expect(result.scriptList).toEqual([]); }); it("file:// 页已实际注入时按 ok 处理,不因权限查询结果误报", async () => { diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index f921137e3..8e4a324a1 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -376,11 +376,9 @@ export class PopupService { async getPopupData(req: GetPopupDataReq): Promise { const { url, tabId } = req; const pageStatus = await this.getPageStatus(tabId, url); - if (pageStatus === "restricted") { - // 浏览器保留页与自家扩展商店上脚本猫永远触及不到,列出「匹配到的」脚本只会让人以为 - // 它们在跑(#1687)。其余状态的抑制原因都可以解除——开开关、开开发者模式、移出黑名单、 - // 给文件访问权限、刷新页面——照常列出匹配脚本,用户才知道解除后哪些会生效;顶部提示 - // 已经说明了它们现在为什么没跑。后台脚本与当前页无关,照常返回。 + if (pageStatus !== "ok") { + // 页面上不会有任何脚本运行,列出「匹配到的」脚本只会让人以为它们在跑(#1687); + // 后台脚本与当前页无关,照常返回。 return { pageStatus, scriptList: [], From 3ad52df7f5fd0c370a568c70c718ab325c46c4b2 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:52:03 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Revert=20"=F0=9F=90=9B=20=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=20Popup=20=E9=9D=9E=E8=BF=90=E8=A1=8C=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA=E8=84=9A=E6=9C=AC"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a07dfd432e8f864a8600997c4770edd7e4b32417. --- src/app/service/service_worker/popup.test.ts | 24 +++++++++----------- src/app/service/service_worker/popup.ts | 8 ++++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/app/service/service_worker/popup.test.ts b/src/app/service/service_worker/popup.test.ts index 1af9f8b35..fdf4f5750 100644 --- a/src/app/service/service_worker/popup.test.ts +++ b/src/app/service/service_worker/popup.test.ts @@ -456,7 +456,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.backScriptList.map((s) => s.uuid)).toEqual([bgUuid]); }); - it("黑名单页应返回 blacklist,且不列出脚本(黑名单页同样不会注入)", async () => { + it("黑名单页应返回 blacklist,并列出匹配脚本(移出黑名单后它们就会跑)", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { @@ -470,7 +470,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("blacklist"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("可注入页收到 content script 报到后返回 ok,正常列出脚本", async () => { @@ -487,7 +487,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("可注入页但从未收到报到(页面比扩展旧 / 被策略拦下)应返回 not-injected", async () => { + it("可注入页但从未收到报到(页面比扩展旧 / 被策略拦下)应返回 not-injected,并列出匹配脚本", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -497,7 +497,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("not-injected"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("同 origin 内的后续导航(SPA 换页)仍算已注入", async () => { @@ -544,7 +544,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("file:// 页未授权文件访问时应返回 file-access-denied", async () => { + it("file:// 页未授权文件访问时应返回 file-access-denied,并列出匹配脚本", async () => { vi.spyOn(extensionMock, "isAllowedFileSchemeAccess").mockResolvedValue(false); const uuid = "allsite"; const { service } = createService({ @@ -555,7 +555,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: "file:///tmp/a.html" }); expect(result.pageStatus).toBe("file-access-denied"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("未注入且全局脚本开关已关闭时应指出开关,而不是让用户白刷新", async () => { @@ -564,7 +564,6 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("scripts-disabled"); - expect(result.scriptList).toEqual([]); }); it("未注入且 UserScripts API 不可用时应指出浏览器设置,而不是让用户白刷新", async () => { @@ -573,7 +572,6 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("userscripts-unavailable"); - expect(result.scriptList).toEqual([]); }); it("全局开关已关闭时,本 tab 报到过也应报 scripts-disabled:提示不能取决于标签页新旧", async () => { @@ -587,10 +585,10 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("scripts-disabled"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("全局开关已关闭的新标签页仍应隐藏当前页脚本列表", async () => { + it("全局开关已关闭的新标签页仍应列出按网址匹配的脚本", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { isLoadScripts: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -600,10 +598,10 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("scripts-disabled"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); - it("UserScripts API 不可用时同样隐藏当前页脚本列表", async () => { + it("UserScripts API 不可用时同样列出匹配脚本:解除后这些脚本就会生效", async () => { const uuid = "allsite"; const { service } = createService({ runtime: { isUserScriptsAvailable: false, getPopupPageScriptMatchingResultByUrl: matchOne(uuid) }, @@ -613,7 +611,7 @@ describe("PopupService getPopupData 页面可达性(脚本猫无法触及的 const result = await service.getPopupData({ tabId: 1, url: WEB_URL }); expect(result.pageStatus).toBe("userscripts-unavailable"); - expect(result.scriptList).toEqual([]); + expect(result.scriptList.map((s) => s.uuid)).toEqual([uuid]); }); it("file:// 页已实际注入时按 ok 处理,不因权限查询结果误报", async () => { diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index 8e4a324a1..f921137e3 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -376,9 +376,11 @@ export class PopupService { async getPopupData(req: GetPopupDataReq): Promise { const { url, tabId } = req; const pageStatus = await this.getPageStatus(tabId, url); - if (pageStatus !== "ok") { - // 页面上不会有任何脚本运行,列出「匹配到的」脚本只会让人以为它们在跑(#1687); - // 后台脚本与当前页无关,照常返回。 + if (pageStatus === "restricted") { + // 浏览器保留页与自家扩展商店上脚本猫永远触及不到,列出「匹配到的」脚本只会让人以为 + // 它们在跑(#1687)。其余状态的抑制原因都可以解除——开开关、开开发者模式、移出黑名单、 + // 给文件访问权限、刷新页面——照常列出匹配脚本,用户才知道解除后哪些会生效;顶部提示 + // 已经说明了它们现在为什么没跑。后台脚本与当前页无关,照常返回。 return { pageStatus, scriptList: [],