Skip to content

Commit cca11e0

Browse files
CodFrmcyfung1031Copilot
authored
♻️ 重构 EarlyStart 实现 (#882)
* ♻️ 重构 EarlyStart 实现 * 修复loadScriptPromise * 调整flag判断 * Update src/app/service/content/utils.ts Co-authored-by: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> * Update src/app/service/content/script_executor.ts Co-authored-by: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> * 重构messageFlag * 通过单元测试 * 修改代码 * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * 根据copilot修改 * 调整测试 --------- Co-authored-by: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 6104427 commit cca11e0

File tree

16 files changed

+207
-209
lines changed

16 files changed

+207
-209
lines changed

example/tests/early_inject_content_test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// @grant GM_log
1212
// @grant GM_info
1313
// @grant GM_setValue
14+
// @grant GM.setValue
1415
// @grant GM_getValue
1516
// @grant GM_deleteValue
1617
// @grant GM_listValues
@@ -154,8 +155,8 @@
154155
// ============ GM 存储 API 测试 ============
155156
console.log("\n%c--- GM 存储 API 测试 ---", "color: orange; font-weight: bold;");
156157

157-
test("GM_setValue - 字符串", () => {
158-
GM_setValue("test_key", "content环境测试值");
158+
await test("GM_setValue - 字符串", async () => {
159+
await GM.setValue("test_key", "content环境测试值");
159160
const value = GM_getValue("test_key");
160161
assert("content环境测试值", value, "应该正确保存和读取字符串");
161162
});

example/tests/early_test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// @grant GM_log
1111
// @grant GM_info
1212
// @grant GM_setValue
13+
// @grant GM.setValue
1314
// @grant GM_getValue
1415
// @grant GM_deleteValue
1516
// @grant GM_listValues
@@ -182,8 +183,8 @@
182183
// ============ GM 存储 API 测试 ============
183184
console.log("\n%c--- GM 存储 API 测试 ---", "color: orange; font-weight: bold;");
184185

185-
await test("GM_setValue - 字符串", () => {
186-
GM_setValue("test_key", "早期脚本测试值");
186+
await test("GM_setValue - 字符串", async () => {
187+
await GM.setValue("test_key", "早期脚本测试值");
187188
const value = GM_getValue("test_key");
188189
assert("早期脚本测试值", value, "应该正确保存和读取字符串");
189190
});

example/tests/inject_content_test.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
// @grant GM_log
1111
// @grant GM_info
1212
// @grant GM_setValue
13+
// @grant GM.setValue
1314
// @grant GM_getValue
1415
// @grant GM_deleteValue
1516
// @grant GM_listValues
1617
// @run-at document-start
1718
// ==/UserScript==
1819

19-
(function () {
20+
(async function () {
2021
"use strict";
2122

2223
console.log("%c=== Content环境 GM API 测试开始 ===", "color: blue; font-size: 16px; font-weight: bold;");
@@ -28,10 +29,10 @@
2829
};
2930

3031
// 测试辅助函数
31-
function test(name, fn) {
32+
async function test(name, fn) {
3233
testResults.total++;
3334
try {
34-
fn();
35+
await fn();
3536
testResults.passed++;
3637
console.log(`%c✓ ${name}`, "color: green;");
3738
return true;
@@ -116,8 +117,8 @@
116117
// ============ GM 存储 API 测试 ============
117118
console.log("\n%c--- GM 存储 API 测试 ---", "color: orange; font-weight: bold;");
118119

119-
test("GM_setValue - 字符串", () => {
120-
GM_setValue("test_key", "content环境测试值");
120+
await test("GM_setValue - 字符串", async () => {
121+
await GM.setValue("test_key", "content环境测试值");
121122
const value = GM_getValue("test_key");
122123
assert("content环境测试值", value, "应该正确保存和读取字符串");
123124
});

packages/message/custom_event_message.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,19 @@ export class CustomEventMessage implements Message {
1919
// 关联dom目标
2020
relatedTarget: Map<number, EventTarget> = new Map();
2121

22+
protected flags: {
23+
injectFlag: string;
24+
contentFlag: string;
25+
messageFlag: string;
26+
};
27+
2228
constructor(
23-
protected flag: string,
29+
flags: typeof CustomEventMessage.prototype.flags | string,
2430
protected isContent: boolean
2531
) {
26-
window.addEventListener((isContent ? "ct" : "fd") + flag, (event) => {
32+
flags = typeof flags === "string" ? { injectFlag: "inject", contentFlag: "content", messageFlag: flags } : flags;
33+
this.flags = flags;
34+
window.addEventListener((isContent ? flags.contentFlag : flags.injectFlag) + flags.messageFlag, (event) => {
2735
if (event instanceof MouseEvent && event.movementX && event.relatedTarget) {
2836
this.relatedTarget.set(event.movementX, event.relatedTarget!);
2937
} else if (event instanceof CustomEvent) {
@@ -95,12 +103,16 @@ export class CustomEventMessage implements Message {
95103
}
96104
}
97105

98-
const ev = new CustomEvent((this.isContent ? "fd" : "ct") + this.flag, {
106+
const ev = new CustomEvent(this.sendEventName(), {
99107
detail,
100108
});
101109
window.dispatchEvent(ev);
102110
}
103111

112+
sendEventName(): string {
113+
return (this.isContent ? this.flags.injectFlag : this.flags.contentFlag) + this.flags.messageFlag;
114+
}
115+
104116
sendMessage<T = any>(data: TMessage): Promise<T> {
105117
return new Promise((resolve: ((value: T) => void) | null) => {
106118
const messageId = uuidv4();
@@ -148,7 +160,7 @@ export class CustomEventMessage implements Message {
148160
// 先将relatedTarget转换成id发送过去
149161
const id = ++this.relateId;
150162
// 可以使用此种方式交互element
151-
const ev = new MouseEvent((this.isContent ? "fd" : "ct") + this.flag, {
163+
const ev = new MouseEvent(this.sendEventName(), {
152164
movementX: id,
153165
relatedTarget: target,
154166
});

packages/message/server.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ describe("Server", () => {
2121
writable: true,
2222
});
2323

24+
const flags = {
25+
contentFlag: "ct",
26+
injectFlag: "fd",
27+
messageFlag: "test",
28+
};
2429
// 创建 content 和 inject 之间的消息通道
25-
contentMessage = new CustomEventMessage("test", true); // content 端
26-
injectMessage = new CustomEventMessage("test", false); // inject 端
30+
contentMessage = new CustomEventMessage(flags, true); // content 端
31+
injectMessage = new CustomEventMessage(flags, false); // inject 端
2732

2833
// 服务端使用 content 消息
2934
server = new Server("api", contentMessage);

src/app/service/content/content.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { MessageSend } from "@Packages/message/types";
55
import type { GMInfoEnv } from "./types";
66
import type { ScriptLoadInfo } from "../service_worker/types";
77
import type { ScriptExecutor } from "./script_executor";
8-
import { definePropertyListener, isInjectIntoContent } from "./utils";
8+
import { isInjectIntoContent } from "./utils";
99
import { RuntimeClient } from "../service_worker/client";
1010

1111
// content页的处理
@@ -126,11 +126,8 @@ export default class ContentRuntime {
126126
);
127127
}
128128

129-
pageLoad() {
130-
// 处理content EarlyScript
131-
definePropertyListener(window, "EarlyScriptFlag", (flag: string[]) => {
132-
this.scriptExecutor.checkEarlyStartScript(flag);
133-
});
129+
pageLoad(messageFlags: MessageFlags) {
130+
this.scriptExecutor.checkEarlyStartScript("content", messageFlags);
134131

135132
const client = new RuntimeClient(this.senderToExt);
136133
// 向service_worker请求脚本列表

src/app/service/content/script_executor.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ export type ExecScriptEntry = {
1414

1515
// 脚本执行器
1616
export class ScriptExecutor {
17+
earlyScriptFlag: Set<string> = new Set();
1718
execMap: Map<string, ExecScript> = new Map();
1819

1920
envInfo: GMInfoEnv | undefined;
20-
earlyScriptFlag: string[] = [];
2121

2222
constructor(private msg: Message) {}
2323

@@ -51,41 +51,53 @@ export class ScriptExecutor {
5151
envInfo: this.envInfo!,
5252
});
5353
};
54+
// 监听脚本加载
5455
scripts.forEach((script) => {
5556
const flag = script.flag;
5657
// 如果是EarlyScriptFlag,处理沙盒环境
57-
if (this.earlyScriptFlag.includes(flag)) {
58+
if (this.earlyScriptFlag.has(flag)) {
5859
for (const val of this.execMap.values()) {
5960
if (val.scriptRes.flag === flag) {
6061
// 处理早期脚本的沙盒环境
6162
val.updateEarlyScriptGMInfo(this.envInfo!);
62-
break;
63+
return;
6364
}
6465
}
65-
return;
6666
}
67-
6867
definePropertyListener(window, flag, (val: ScriptFunc) => {
6968
loadExec(script, val);
7069
});
7170
});
7271
}
7372

74-
checkEarlyStartScript(earlyStarFlag: string[]) {
75-
this.earlyScriptFlag = earlyStarFlag;
76-
const loadExec = (flag: string, scriptFunc: any) => {
77-
this.execScriptEntry({
78-
scriptLoadInfo: scriptFunc.scriptInfo,
79-
scriptFunc: scriptFunc.func,
80-
scriptFlag: flag,
81-
envInfo: {},
82-
});
83-
};
84-
this.earlyScriptFlag.forEach((flag) => {
85-
definePropertyListener(window, flag, (val: PreScriptFunc) => {
86-
loadExec(flag, val);
87-
});
73+
checkEarlyStartScript(env: "content" | "inject", messageFlags: MessageFlags) {
74+
const eventNamePrefix = env === "content" ? messageFlags.contentFlag : messageFlags.injectFlag;
75+
// 监听 脚本加载
76+
// 适用于此「通知环境加载完成」代码执行后的脚本加载
77+
window.addEventListener(`${eventNamePrefix}${messageFlags.scriptLoadComplete}`, (event) => {
78+
if (event instanceof CustomEvent) {
79+
if (typeof event.detail.scriptFlag === "string") {
80+
event.preventDefault(); // dispatchEvent 会回传 false -> 分离环境也能得知环境加载代码已执行
81+
const scriptFlag = event.detail.scriptFlag;
82+
this.execEarlyScript(scriptFlag);
83+
}
84+
}
85+
});
86+
// 通知 环境 加载完成
87+
// 适用于此「通知环境加载完成」代码执行前的脚本加载
88+
const ev = new CustomEvent(eventNamePrefix + messageFlags.envLoadComplete);
89+
window.dispatchEvent(ev);
90+
}
91+
92+
execEarlyScript(flag: string) {
93+
const scriptFunc = (window as any)[flag] as PreScriptFunc;
94+
this.execScriptEntry({
95+
scriptLoadInfo: scriptFunc.scriptInfo,
96+
scriptFunc: scriptFunc.func,
97+
scriptFlag: flag,
98+
envInfo: {},
8899
});
100+
this.earlyScriptFlag.add(flag);
89101
}
90102

91103
execScriptEntry(scriptEntry: ExecScriptEntry) {

src/app/service/content/utils.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,29 @@ export function compileInjectScriptByFlag(
9999
* 将脚本函数编译为预注入脚本代码
100100
*/
101101
export function compilePreInjectScript(
102+
messageFlags: MessageFlags,
102103
script: ScriptLoadInfo,
103104
scriptCode: string,
104105
autoDeleteMountFunction: boolean = false
105106
): string {
107+
const eventNamePrefix = isInjectIntoContent(script.metadata) ? messageFlags.contentFlag : messageFlags.injectFlag;
106108
const autoDeleteMountCode = autoDeleteMountFunction ? `try{delete window['${script.flag}']}catch(e){}` : "";
107109
return `window['${script.flag}'] = {
108110
scriptInfo: ${JSON.stringify(script)},
109111
func: function(){${autoDeleteMountCode}${scriptCode}}
110-
}`;
112+
};
113+
(() => {
114+
const f = () => {
115+
const event = new CustomEvent('${eventNamePrefix}${messageFlags.scriptLoadComplete}',
116+
{ cancelable: true, detail: { scriptFlag: '${script.flag}' } });
117+
return window.dispatchEvent(event);
118+
};
119+
const noCheckEarlyStartScript = f();
120+
if (noCheckEarlyStartScript) {
121+
window.addEventListener('${eventNamePrefix}${messageFlags.envLoadComplete}', f, { once: true });
122+
}
123+
})();
124+
`;
111125
}
112126

113127
export function addStyle(css: string): HTMLStyleElement {

src/app/service/queue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export type TInstallScriptParams = {
2020

2121
export type TInstallScript = { script: TInstallScriptParams; update: boolean; upsertBy?: InstallSource };
2222

23-
export type TDeleteScript = { uuid: string; storageName: string; type: SCRIPT_TYPE; isEarlyStart: boolean };
23+
export type TDeleteScript = { uuid: string; storageName: string; type: SCRIPT_TYPE };
2424

2525
export type TSortedScript = { uuid: string; sort: number };
2626

0 commit comments

Comments
 (0)