Skip to content
Merged
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
21 changes: 21 additions & 0 deletions src/app/service/service_worker/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
32 changes: 32 additions & 0 deletions src/pkg/utils/url_matcher.match-scheme.test.ts
Original file line number Diff line number Diff line change
@@ -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/*",
]);
});
});
12 changes: 8 additions & 4 deletions src/pkg/utils/url_matcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { regexToGlob } from "./regex_to_glob";
import { isFirefox } from "./utils";

export const enum RuleType {
MATCH_INCLUDE = 1,
Expand Down Expand Up @@ -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) => {
Expand Down
Loading