Skip to content

Commit fc7b701

Browse files
committed
简化逻辑
1 parent c2903d4 commit fc7b701

4 files changed

Lines changed: 27 additions & 435 deletions

File tree

src/app/service/service_worker/script.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,7 @@ export class ScriptService {
266266
action: {
267267
type: "redirect" as chrome.declarativeNetRequest.RuleActionType,
268268
redirect: {
269-
/**
270-
* 核心设计:
271-
* 使用 `<,\1,>` 作为特征锚点注入到重定向 URL 中。
272-
* 1. 引导格式化:利用 \1 提取正则捕获组内容。
273-
* 2. 编码探测:通过包裹特殊的定界符(尖括号和逗号),在目标页面解析时,
274-
* 可以通过检测这些字符是否被转义(如变为 %3C, %2C)来精准判定
275-
* 浏览器底层触发的是哪种 URL 编码策略(Raw / encodeURI / encodeURIComponent)。
276-
*/
277-
regexSubstitution: `${installPageURL}?url=<,\\1,>`,
269+
regexSubstitution: `${installPageURL}?url=\\1`,
278270
},
279271
},
280272
condition: condition,

src/pages/install/App.tsx

Lines changed: 25 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { cacheInstance } from "@App/app/cache";
3434
import { formatBytes } from "@App/pkg/utils/utils";
3535
import { ScriptIcons } from "../options/routes/utils";
3636
import { bytesDecode, detectEncoding } from "@App/pkg/utils/encoding";
37-
import { toEncodedURL, prettyUrl } from "@App/pkg/utils/url-utils";
37+
import { prettyUrl } from "@App/pkg/utils/url-utils";
3838

3939
const backgroundPromptShownKey = "background_prompt_shown";
4040

@@ -57,48 +57,6 @@ const closeWindow = (shouldGoBack: boolean) => {
5757
}
5858
};
5959

60-
const getCandidateUrls = (targetUrlHref: string) => {
61-
const encodedUrl = toEncodedURL(targetUrlHref);
62-
const inputU = new URL(encodedUrl);
63-
const extraCandidateUrls = new Set<string>();
64-
extraCandidateUrls.add(inputU.href);
65-
66-
const hostname = inputU.hostname;
67-
// 兼容 .greasyfork.org, cn-greasyfork.org
68-
const hostText = `.${hostname}`.replace(/\W/g, ".");
69-
const isGreasyFork = hostText.endsWith(".greasyfork.org");
70-
const isSleazyFork = hostText.endsWith(".sleazyfork.org");
71-
72-
if (isGreasyFork || isSleazyFork) {
73-
// example:
74-
// CASE 1
75-
// raw 'https://update.greasyfork.org/scripts/550295/100%解锁CSDN文库vip文章阅读限制.user.js'
76-
// encoded 'https://update.greasyfork.org/scripts/550295/100%25%E8%A7%A3%E9%94%81CSDN%E6%96%87%E5%BA%93vip%E6%96%87%E7%AB%A0%E9%98%85%E8%AF%BB%E9%99%90%E5%88%B6.user.js'
77-
// correct 'https://update.greasyfork.org/scripts/550295/100%25%E8%A7%A3%E9%94%81CSDN%E6%96%87%E5%BA%93vip%E6%96%87%E7%AB%A0%E9%98%85%E8%AF%BB%E9%99%90%E5%88%B6.user.js'
78-
// CASE 2
79-
// raw 'https://update.greasyfork.org/scripts/519037/Nexus No Wait ++.user.js'
80-
// encoded 'https://update.greasyfork.org/scripts/519037/Nexus%20No%20Wait%20++.user.js'
81-
// correct 'https://update.greasyfork.org/scripts/519037/Nexus%20No%20Wait%20%2B%2B.user.js'
82-
try {
83-
const encodedPathname = inputU.pathname;
84-
const lastSlashIndex = encodedPathname.lastIndexOf("/");
85-
const basePath = encodedPathname.substring(0, lastSlashIndex);
86-
const fileName = encodedPathname.substring(lastSlashIndex + 1);
87-
const reEncodedFileName = encodeURIComponent(decodeURI(fileName));
88-
if (reEncodedFileName !== fileName) {
89-
const reEncodedPathName = `${basePath}/${reEncodedFileName}`;
90-
const reEncodedUrl = `${inputU.origin}${reEncodedPathName}${inputU.search}${inputU.hash}`;
91-
extraCandidateUrls.add(reEncodedUrl);
92-
}
93-
} catch (e) {
94-
// can skip if it cannot be converted using decodeURI
95-
console.warn(e); // just a warning for debug purpose.
96-
}
97-
}
98-
99-
return [...extraCandidateUrls];
100-
};
101-
10260
const fetchScriptBody = async (url: string, { onProgress }: { [key: string]: any }) => {
10361
let origin;
10462
try {
@@ -702,44 +660,24 @@ function App() {
702660

703661
const targetUrlHref = useMemo(() => {
704662
if (!hasValidSourceParam) {
705-
/**
706-
* 逻辑说明:
707-
* 在 chrome.declarativeNetRequest 规则中,我们使用 `<,\1,>` 作为占位符引导 API 进行参数填充。
708-
* 由于不同浏览器版本或配置对 URL 参数的自动编码(Auto-encoding)策略不一致,
709-
* 我们通过检测该占位符的“被编码状态”来逆推浏览器采用了哪种编码方式。
710-
*/
711-
let m;
712663
let url;
713664
try {
714-
// 场景 1:URL 完全未编码。直接匹配原始特征符号 "<", ">" 和 ","
715-
if ((m = /\burl=(<,.+,>)(&|$)/.exec(location.search)?.[1])) {
716-
url = m; // 未被编码,取原始值。
717-
}
718-
// 场景 2:URL 经过了部分编码(类似 encodeURI)。逗号 "," 未被编码,但尖括号被转义为 %3C, %3E
719-
else if ((m = /\burl=(%3C,.+,%3E)(&|$)/.exec(location.search)?.[1])) {
720-
url = decodeURI(m);
721-
}
722-
// 场景 3:URL 经过了完全编码(类似 encodeURIComponent)。逗号也被转义为 %2C
723-
else if ((m = /\burl=(%3C%2C.+%2C%3E)(&|$)/.exec(location.search)?.[1])) {
724-
url = decodeURIComponent(m);
725-
}
665+
// 取url=之后的所有内容
666+
url = location.search.match(/\?url=([^&]+)/)?.[1] || "";
726667
} catch {
727668
// ignored
728669
}
729-
// 如果正则匹配/标准解码失败,回退到标准的 searchParams 获取方式 (浏览器会自行理解和解码不规范的编码)
730-
if (!url) url = searchParams.get("url") || ""; // fallback
731-
// 移除人工注入的特征锚点 <, ,>,提取真实的 URL 内容
732-
url = url.replace(/^<,(.+),>$/, "$1"); // 去掉 <, ,>
733-
if (url) {
734-
try {
735-
const urlObject = new URL(url);
736-
// 验证解析后的 URL 是否具备核心要素,确保安全性与合法性
737-
if (urlObject.protocol && urlObject.hostname && urlObject.pathname) {
738-
return url;
739-
}
740-
} catch {
741-
// ignored
670+
if (!url) {
671+
return "";
672+
}
673+
try {
674+
const urlObject = new URL(url);
675+
// 验证解析后的 URL 是否具备核心要素,确保安全性与合法性
676+
if (urlObject.protocol && urlObject.hostname && urlObject.pathname) {
677+
return url;
742678
}
679+
} catch {
680+
// ignored
743681
}
744682
}
745683
return "";
@@ -751,39 +689,21 @@ function App() {
751689
errorStatusText: "",
752690
});
753691

754-
const loadURLAsync = async (candidateUrls: string[]) => {
755-
// 1. 定义获取单个脚本的内部逻辑,负责处理进度条与单次错误
756-
const fetchValidScript = async () => {
757-
let firstError: unknown;
758-
for (const url of candidateUrls) {
759-
try {
760-
const result = await fetchScriptBody(url, {
761-
onProgress: (info: { receivedLength: number }) => {
762-
setFetchingState((prev) => ({
763-
...prev,
764-
loadingStatusText: t("downloading_status_text", { bytes: formatBytes(info.receivedLength) }),
765-
}));
766-
},
767-
});
768-
if (result.code && result.metadata) {
769-
return { result, url }; // 找到有效的立即返回
770-
}
771-
} catch (e) {
772-
if (!firstError) firstError = e;
773-
}
774-
}
775-
// 如果循环结束都没成功,抛出第一个捕获到的错误或预设错误
776-
throw firstError || new Error(t("install_page_load_failed"));
777-
};
778-
692+
const loadURLAsync = async (urlHref: string) => {
779693
try {
780694
// 2. 执行获取
781-
const { result, url } = await fetchValidScript();
782-
const { code, metadata } = result;
695+
const { code, metadata } = await fetchScriptBody(urlHref, {
696+
onProgress: (info: { receivedLength: number }) => {
697+
setFetchingState((prev) => ({
698+
...prev,
699+
loadingStatusText: t("downloading_status_text", { bytes: `${formatBytes(info.receivedLength)}` }),
700+
}));
701+
},
702+
});
783703

784704
// 3. 处理数据与缓存
785705
const uuid = uuidv4();
786-
const scriptData = [false, createScriptInfo(uuid, code, url, "user", metadata)];
706+
const scriptData = [false, createScriptInfo(uuid, code, urlHref, "user", metadata)];
787707

788708
await cacheInstance.set(`${CACHE_KEY_SCRIPT_INFO}${uuid}`, scriptData);
789709

@@ -806,19 +726,10 @@ function App() {
806726
}
807727
};
808728

809-
const handleUrlChangeAndFetch = (targetUrlHref: string) => {
810-
setFetchingState((prev) => ({
811-
...prev,
812-
loadingStatusText: t("install_page_please_wait"),
813-
}));
814-
const candidateUrls = getCandidateUrls(targetUrlHref);
815-
loadURLAsync(candidateUrls);
816-
};
817-
818729
// 有 url 的话下载内容
819730
useEffect(() => {
820-
if (targetUrlHref) handleUrlChangeAndFetch(targetUrlHref);
821-
// eslint-disable-next-line react-hooks/exhaustive-deps
731+
if (!targetUrlHref) return;
732+
loadURLAsync(targetUrlHref);
822733
}, [targetUrlHref]);
823734

824735
if (!hasValidSourceParam) {

0 commit comments

Comments
 (0)