Skip to content

Commit b76f253

Browse files
authored
🐛 修复外部扩展API无效的问题 (#1217)
* 修复 87c0a19 重构代码时不正确的修改 * Update script_runtime.ts * 结构化 * 移至 external.ts
1 parent 932f88e commit b76f253

2 files changed

Lines changed: 88 additions & 56 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { ExternalWhitelist } from "@App/app/const";
2+
import { sendMessage } from "@Packages/message/client";
3+
import type { Message } from "@Packages/message/types";
4+
5+
// ================================
6+
// 对外接口:external 注入
7+
// ================================
8+
9+
// 判断当前 hostname 是否命中白名单(含子域名)
10+
const isExternalWhitelisted = (hostname: string) => {
11+
return ExternalWhitelist.some(
12+
(t) => hostname.endsWith(t) && (hostname.length === t.length || hostname.endsWith(`.${t}`))
13+
);
14+
};
15+
16+
// 生成暴露给页面的 Scriptcat 外部接口
17+
const createScriptcatExpose = (msg: Message) => {
18+
const scriptExpose: App.ExternalScriptCat = {
19+
isInstalled(name: string, namespace: string, callback: (res: App.IsInstalledResponse | undefined) => unknown) {
20+
sendMessage<App.IsInstalledResponse>(msg, "scripting/script/isInstalled", { name, namespace }).then(callback);
21+
},
22+
};
23+
return scriptExpose;
24+
};
25+
26+
// 尝试写入 external,失败则忽略
27+
const safeSetExternal = <T extends object>(external: any, key: string, value: T) => {
28+
try {
29+
external[key] = value;
30+
return true;
31+
} catch {
32+
// 无法注入到 external,忽略
33+
return false;
34+
}
35+
};
36+
37+
// 当 TM 与 SC 同时存在时的兼容处理:TM 未安装脚本时回退查询 SC
38+
const patchTampermonkeyIsInstalled = (external: any, scriptExpose: App.ExternalScriptCat) => {
39+
const exposedTM = external.Tampermonkey;
40+
const isInstalledTM = exposedTM?.isInstalled;
41+
const isInstalledSC = scriptExpose.isInstalled;
42+
43+
// 满足这些字段时,认为是较完整的 TM 对象
44+
if (isInstalledTM && exposedTM?.getVersion && exposedTM.openOptions) {
45+
try {
46+
exposedTM.isInstalled = (
47+
name: string,
48+
namespace: string,
49+
callback: (res: App.IsInstalledResponse | undefined) => unknown
50+
) => {
51+
isInstalledTM(name, namespace, (res: App.IsInstalledResponse | undefined) => {
52+
if (res?.installed) callback(res);
53+
else isInstalledSC(name, namespace, callback);
54+
});
55+
};
56+
} catch {
57+
// 忽略错误
58+
}
59+
return true;
60+
}
61+
62+
return false;
63+
};
64+
65+
// inject 环境 pageLoad 后执行:按白名单对页面注入 external 接口
66+
export const onInjectPageLoaded = (msg: Message) => {
67+
const hostname = window.location.hostname;
68+
69+
// 不在白名单则不对外暴露接口
70+
if (!isExternalWhitelisted(hostname)) return;
71+
72+
// 确保 external 存在
73+
const external: External = (window.external || (window.external = {} as External)) as External;
74+
75+
// 创建 Scriptcat 暴露对象
76+
const scriptExpose = createScriptcatExpose(msg);
77+
78+
// 尝试设置 external.Scriptcat
79+
safeSetExternal(external, "Scriptcat", scriptExpose);
80+
81+
// 如果页面已有 Tampermonkey,则做兼容补丁;否则将 Tampermonkey 也指向 Scriptcat 接口
82+
const patched = patchTampermonkeyIsInstalled(external, scriptExpose);
83+
if (!patched) {
84+
safeSetExternal(external, "Tampermonkey", scriptExpose);
85+
}
86+
};
Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { type Server } from "@Packages/message/server";
22
import type { Message } from "@Packages/message/types";
3-
import { ExternalWhitelist } from "@App/app/const";
4-
import { sendMessage } from "@Packages/message/client";
53
import { initEnvInfo, type ScriptExecutor } from "./script_executor";
64
import type { TScriptInfo } from "@App/app/repo/scripts";
75
import type { EmitEventRequest } from "../service_worker/types";
86
import type { GMInfoEnv, ValueUpdateDataEncoded } from "./types";
97
import type { ScriptEnvTag } from "@Packages/message/consts";
8+
import { onInjectPageLoaded } from "./external";
109

1110
export class ScriptRuntime {
1211
constructor(
@@ -40,59 +39,6 @@ export class ScriptRuntime {
4039
}
4140

4241
externalMessage() {
43-
// 对外接口白名单
44-
const hostname = window.location.hostname;
45-
if (
46-
ExternalWhitelist.some(
47-
// 如果当前页面的 hostname 是白名单的网域或其子网域
48-
(t) => hostname.endsWith(t) && (hostname.length === t.length || hostname.endsWith(`.${t}`))
49-
)
50-
) {
51-
const msg = this.msg;
52-
// 注入
53-
const external: External = window.external || (window.external = {} as External);
54-
const scriptExpose: App.ExternalScriptCat = {
55-
isInstalled(name: string, namespace: string, callback: (res: App.IsInstalledResponse | undefined) => unknown) {
56-
sendMessage<App.IsInstalledResponse>(msg, "content/script/isInstalled", {
57-
name,
58-
namespace,
59-
}).then(callback);
60-
},
61-
};
62-
try {
63-
external.Scriptcat = scriptExpose;
64-
} catch {
65-
// 无法注入到 external,忽略
66-
}
67-
const exposedTM = external.Tampermonkey;
68-
const isInstalledTM = exposedTM?.isInstalled;
69-
const isInstalledSC = scriptExpose.isInstalled;
70-
if (isInstalledTM && exposedTM?.getVersion && exposedTM.openOptions) {
71-
// 当TM和SC同时启动的特殊处理:如TM没有安装,则查SC的安装状态
72-
try {
73-
exposedTM.isInstalled = (
74-
name: string,
75-
namespace: string,
76-
callback: (res: App.IsInstalledResponse | undefined) => unknown
77-
) => {
78-
isInstalledTM(name, namespace, (res) => {
79-
if (res?.installed) callback(res);
80-
else
81-
isInstalledSC(name, namespace, (res) => {
82-
callback(res);
83-
});
84-
});
85-
};
86-
} catch {
87-
// 忽略错误
88-
}
89-
} else {
90-
try {
91-
external.Tampermonkey = scriptExpose;
92-
} catch {
93-
// 无法注入到 external,忽略
94-
}
95-
}
96-
}
42+
onInjectPageLoaded(this.msg);
9743
}
9844
}

0 commit comments

Comments
 (0)