Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions src/app/service/service_worker/popup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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 () => {
Expand All @@ -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) },
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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) },
Expand All @@ -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]);
});

Expand Down
16 changes: 10 additions & 6 deletions src/app/service/service_worker/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,11 @@ export class PopupService {
async getPopupData(req: GetPopupDataReq): Promise<GetPopupDataRes> {
const { url, tabId } = req;
const pageStatus = await this.getPageStatus(tabId, url);
if (pageStatus !== "ok") {
// 页面上不会有任何脚本运行,列出「匹配到的」脚本只会让人以为它们在跑(#1687);
// 后台脚本与当前页无关,照常返回。
if (pageStatus === "restricted") {
// 浏览器保留页与自家扩展商店上脚本猫永远触及不到,列出「匹配到的」脚本只会让人以为
// 它们在跑(#1687)。其余状态的抑制原因都可以解除——开开关、开开发者模式、移出黑名单、
// 给文件访问权限、刷新页面——照常列出匹配脚本,用户才知道解除后哪些会生效;顶部提示
// 已经说明了它们现在为什么没跑。后台脚本与当前页无关,照常返回。
return {
pageStatus,
scriptList: [],
Expand Down Expand Up @@ -443,6 +445,9 @@ export class PopupService {
* 判断当前页脚本猫是否触及得到。
*
* 顺序有意为之:浏览器保留页与黑名单是「无论如何都不会注入」的确定结论,先判;
* 接着是扩展整体没跑起来的两种情况(全局开关关闭、UserScripts API 不可用),它们与具体
* 标签页无关,必须先于注入证据 —— 注入证据只是「本 tab 曾经报到过」,页面刷新与关开关都
* 不会让它失效,用它去否定全局状态会使同一开关状态下老标签页没提示、新标签页有提示。
* 其余情况以「本 tab 有没有 content script 报到」为准 —— 它是运行时证据,
* 比协议白名单准(企业策略、扩展商店等都拦不住白名单)。file:// 的权限查询只用来
* 给未注入的情况一个更准确的原因,不能反过来否定已经注入成功的事实(Firefox 上该
Expand All @@ -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";
Expand Down
Loading