diff --git a/src/app/service/service_worker/utils.test.ts b/src/app/service/service_worker/utils.test.ts index e45749bc8..9b3ce107b 100644 --- a/src/app/service/service_worker/utils.test.ts +++ b/src/app/service/service_worker/utils.test.ts @@ -309,6 +309,27 @@ describe.concurrent("getUserScriptRegister", () => { expect(result.registerScript.runAt).toBe("document_end"); }); + it.concurrent("不应把 userScripts 不支持的 @match scheme 交给注册 API", () => { + const mockScriptMatchInfo: ScriptMatchInfo = { + uuid: "test-invalid-match-scheme", + name: "Invalid Match Scheme", + metadata: {}, + scriptUrlPatterns: extractUrlPatterns(["@match https://*/*", "@match http://*/*", "@match notsupported://*/*"]), + originalUrlPatterns: [], + namespace: "test", + type: 1, + status: 1, + sort: 0, + runStatus: "running", + createtime: Date.now(), + checktime: Date.now(), + }; + + const { registerScript } = getUserScriptRegister(mockScriptMatchInfo); + + expect(registerScript.matches).toEqual(["https://*/*", "http://*/*"]); + }); + it.concurrent("excludeMatches 丢弃浏览器不支持的 scheme", () => { const scriptMatchInfo = { uuid: "exclude-scheme", diff --git a/src/pkg/utils/url_matcher.match-scheme.test.ts b/src/pkg/utils/url_matcher.match-scheme.test.ts new file mode 100644 index 000000000..3f82d2622 --- /dev/null +++ b/src/pkg/utils/url_matcher.match-scheme.test.ts @@ -0,0 +1,32 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { isApiSupportedMatchPattern } from "./url_matcher"; + +const patterns = [ + "https://example.com/*", + "ws://example.com/*", + "wss://example.com/*", + "ftp://example.com/*", + "notsupported://example.com/*", +]; + +describe("isApiSupportedMatchPattern", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("保留 Chromium userScripts 支持的 scheme,并过滤其他 scheme", () => { + expect(patterns.filter(isApiSupportedMatchPattern)).toEqual(["https://example.com/*", "ftp://example.com/*"]); + }); + + it("保留 Firefox userScripts 额外支持的 scheme,并过滤未知 scheme", () => { + // isFirefox() 以 mozInnerScreenX 全局判断 Firefox + vi.stubGlobal("mozInnerScreenX", 0); + + expect(patterns.filter(isApiSupportedMatchPattern)).toEqual([ + "https://example.com/*", + "ws://example.com/*", + "wss://example.com/*", + "ftp://example.com/*", + ]); + }); +}); diff --git a/src/pkg/utils/url_matcher.ts b/src/pkg/utils/url_matcher.ts index 8a2b804f2..ace965fab 100644 --- a/src/pkg/utils/url_matcher.ts +++ b/src/pkg/utils/url_matcher.ts @@ -1,4 +1,5 @@ import { regexToGlob } from "./regex_to_glob"; +import { isFirefox } from "./utils"; export const enum RuleType { MATCH_INCLUDE = 1, @@ -437,12 +438,15 @@ export const extractSchemesOfGlobs = (globs: string[]): string[] => { return [...set]; }; -// chrome.userScripts 与 Firefox 都接受的 match pattern scheme;其余(chrome://、edge://、about: 等) -// 注册时会抛 Invalid scheme,而批量注册是原子的,一个脚本出错会让整批失败 -const API_SUPPORTED_MATCH_SCHEMES = new Set(["*", "http", "https", "file", "ftp"]); +// userScripts API 接受的 match pattern scheme;其余(chrome://、edge://、about: 等)注册时会抛 Invalid scheme, +// 而批量注册是原子的,一个脚本出错会让整批失败。Firefox 的 manifest.MatchPattern 额外接受 ws/wss,Chromium 不接受 +const CHROMIUM_API_MATCH_SCHEMES = new Set(["*", "http", "https", "file", "ftp"]); +const FIREFOX_API_MATCH_SCHEMES = new Set(["*", "http", "https", "file", "ftp", "ws", "wss"]); export const isApiSupportedMatchPattern = (pattern: string) => - API_SUPPORTED_MATCH_SCHEMES.has(pattern.substring(0, pattern.indexOf("://"))); + (isFirefox() ? FIREFOX_API_MATCH_SCHEMES : CHROMIUM_API_MATCH_SCHEMES).has( + pattern.substring(0, pattern.indexOf("://")) + ); // 用户输入的 @match 值能否产生可注册的规则:解析失败的会被静默忽略,不支持的 scheme 会在注册时被丢弃 export const isRegistrableMatchPattern = (pattern: string) => {