Skip to content

Commit 7a8c0b3

Browse files
committed
🐛 修复早期启动脚本无法使用GM_addElement的问题 #801
1 parent ea2e906 commit 7a8c0b3

5 files changed

Lines changed: 71 additions & 84 deletions

File tree

src/app/service/content/content.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ 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 { isInjectIntoContent } from "./utils";
8+
import { definePropertyListener, isInjectIntoContent } from "./utils";
9+
import { RuntimeClient } from "../service_worker/client";
910

1011
// content页的处理
1112
export default class ContentRuntime {
@@ -75,7 +76,7 @@ export default class ContentRuntime {
7576
let parentNode: EventTarget | undefined;
7677
// 判断是不是content脚本发过来的
7778
let msg: CustomEventMessage;
78-
if (this.contentScript.has(data.uuid)) {
79+
if (this.contentScript.has(data.uuid) || this.scriptExecutor.execList.has(data.uuid)) {
7980
msg = this.scriptExecutorMsg;
8081
} else {
8182
msg = this.senderToInject;
@@ -125,6 +126,19 @@ export default class ContentRuntime {
125126
);
126127
}
127128

129+
pageLoad() {
130+
// 处理content EarlyScript
131+
definePropertyListener(window, "EarlyScriptFlag", (flag: string[]) => {
132+
this.scriptExecutor.checkEarlyStartScript(flag);
133+
});
134+
135+
const client = new RuntimeClient(this.senderToExt);
136+
// 向service_worker请求脚本列表
137+
client.pageLoad().then((data) => {
138+
this.start(data.scripts, data.envInfo);
139+
});
140+
}
141+
128142
start(scripts: ScriptLoadInfo[], envInfo: GMInfoEnv) {
129143
// 启动脚本
130144
const client = new Client(this.senderToInject, "inject");

src/app/service/content/script_executor.ts

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import { getStorageName } from "@App/pkg/utils/utils";
33
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
44
import ExecScript from "./exec_script";
55
import type { GMInfoEnv, ScriptFunc, PreScriptFunc, ValueUpdateDataEncoded } from "./types";
6-
import { addStyle } from "./utils";
6+
import { addStyle, definePropertyListener } from "./utils";
77

88
export type ExecScriptEntry = {
9-
scriptLoadInfo: any;
9+
scriptLoadInfo: ScriptLoadInfo;
1010
scriptFlag: string;
1111
envInfo: any;
1212
scriptFunc: any;
1313
};
1414

1515
// 脚本执行器
1616
export class ScriptExecutor {
17-
execList: ExecScript[] = [];
17+
execList: Map<string, ExecScript> = new Map();
1818

1919
envInfo: GMInfoEnv | undefined;
2020
earlyScriptFlag: string[] = [];
@@ -27,15 +27,15 @@ export class ScriptExecutor {
2727

2828
emitEvent(data: EmitEventRequest) {
2929
// 转发给脚本
30-
const exec = this.execList.find((val) => val.scriptRes.uuid === data.uuid);
30+
const exec = this.execList.get(data.uuid);
3131
if (exec) {
3232
exec.emitEvent(data.event, data.eventId, data.data);
3333
}
3434
}
3535

3636
valueUpdate(data: ValueUpdateDataEncoded) {
3737
const { uuid, storageName } = data;
38-
for (const val of this.execList) {
38+
for (const val of this.execList.values()) {
3939
if (val.scriptRes.uuid === uuid || getStorageName(val.scriptRes) === storageName) {
4040
val.valueUpdate(data);
4141
}
@@ -55,7 +55,7 @@ export class ScriptExecutor {
5555
const flag = script.flag;
5656
// 如果是EarlyScriptFlag,处理沙盒环境
5757
if (this.earlyScriptFlag.includes(flag)) {
58-
for (const val of this.execList) {
58+
for (const val of this.execList.values()) {
5959
if (val.scriptRes.flag === flag) {
6060
// 处理早期脚本的沙盒环境
6161
val.updateEarlyScriptGMInfo(this.envInfo!);
@@ -64,23 +64,10 @@ export class ScriptExecutor {
6464
}
6565
return;
6666
}
67-
// @ts-ignore
68-
const scriptFunc = window[flag];
69-
if (scriptFunc) {
70-
// @ts-ignore
71-
window[flag] = null; // 释放物件参考
72-
loadExec(script, scriptFunc);
73-
} else {
74-
// 监听脚本加载,和屏蔽读取
75-
Object.defineProperty(window, flag, {
76-
configurable: true,
77-
set: (val: ScriptFunc) => {
78-
// @ts-ignore
79-
delete window[flag]; // 删除 property setter 避免重复呼叫
80-
loadExec(script, val);
81-
},
82-
});
83-
}
67+
68+
definePropertyListener(window, flag, (val: ScriptFunc) => {
69+
loadExec(script, val);
70+
});
8471
});
8572
}
8673

@@ -95,33 +82,17 @@ export class ScriptExecutor {
9582
});
9683
};
9784
this.earlyScriptFlag.forEach((flag) => {
98-
// @ts-ignore
99-
const scriptFunc = window[flag] as PreScriptFunc;
100-
if (scriptFunc) {
101-
// @ts-ignore
102-
window[flag] = null; // 释放物件参考
103-
loadExec(flag, scriptFunc);
104-
} else {
105-
// 监听脚本加载,和屏蔽读取
106-
Object.defineProperty(window, flag, {
107-
configurable: true,
108-
set: (val: PreScriptFunc) => {
109-
// @ts-ignore
110-
delete window[flag]; // 取消 property setter 避免重复呼叫
111-
loadExec(flag, val);
112-
},
113-
});
114-
}
85+
definePropertyListener(window, flag, (val: PreScriptFunc) => {
86+
loadExec(flag, val);
87+
});
11588
});
11689
}
11790

11891
execScriptEntry(scriptEntry: ExecScriptEntry) {
119-
const { scriptFlag, scriptLoadInfo, scriptFunc, envInfo } = scriptEntry;
92+
const { scriptLoadInfo, scriptFunc, envInfo } = scriptEntry;
12093

121-
// @ts-ignore
122-
delete window[scriptFlag];
12394
const exec = new ExecScript(scriptLoadInfo, "content", this.msg, scriptFunc, envInfo);
124-
this.execList.push(exec);
95+
this.execList.set(scriptLoadInfo.uuid, exec);
12596
const metadata = scriptLoadInfo.metadata || {};
12697
const resource = scriptLoadInfo.resource;
12798
// 注入css

src/app/service/content/utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,19 @@ export const getScriptFlag = (uuid: string) => {
137137
// 实作内容有待检讨
138138
return `#-${uuid}`;
139139
};
140+
141+
// 监听属性设置
142+
export function definePropertyListener<T>(obj: any, prop: string, listener: (val: T) => void) {
143+
if (obj[prop] !== undefined) {
144+
listener(obj[prop]);
145+
delete obj[prop];
146+
return;
147+
}
148+
Object.defineProperty(obj, prop, {
149+
configurable: true,
150+
set: (val: any) => {
151+
delete obj[prop]; // 删除 property setter
152+
listener(val);
153+
},
154+
});
155+
}

src/app/service/service_worker/runtime.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,7 @@ export class RuntimeService {
12321232
return [
12331233
{
12341234
id: "scriptcat-early-start-flag",
1235-
js: [{ code: "window.EarlyScriptFlag=" + flagParam + ";" }],
1235+
js: [{ code: `window.EarlyScriptFlag=${flagParam};window.MessageFlag="${messageFlag}"` }],
12361236
matches: ["<all_urls>"],
12371237
allFrames: true,
12381238
world: "USER_SCRIPT",

src/content.ts

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,55 @@ import LoggerCore from "./app/logger/core";
22
import MessageWriter from "./app/logger/message_writer";
33
import { ExtensionMessage } from "@Packages/message/extension_message";
44
import { CustomEventMessage } from "@Packages/message/custom_event_message";
5-
import { RuntimeClient } from "./app/service/service_worker/client";
65
import { Server } from "@Packages/message/server";
76
import ContentRuntime from "./app/service/content/content";
87
import { ScriptExecutor } from "./app/service/content/script_executor";
98
import { randomMessageFlag } from "./pkg/utils/utils";
109
import type { Message } from "@Packages/message/types";
10+
import { definePropertyListener } from "./app/service/content/utils";
1111

1212
declare global {
1313
interface Window {
1414
EarlyScriptFlag?: string[];
15+
MessageFlag?: string;
1516
}
1617
}
1718

1819
if (typeof chrome?.runtime?.onMessage?.addListener !== "function") {
1920
// ScriptCat 未支持 Firefox MV3
2021
console.error("Firefox MV3 UserScripts is not yet supported by ScriptCat");
2122
} else {
22-
// 建立与service_worker页面的连接
23-
const extMsgComm: Message = new ExtensionMessage(false);
23+
const start = (messageFlag: string) => {
24+
// 建立与service_worker页面的连接
25+
const extMsgComm: Message = new ExtensionMessage(false);
26+
// 初始化日志组件
27+
const loggerCore = new LoggerCore({
28+
writer: new MessageWriter(extMsgComm),
29+
labels: { env: "content" },
30+
});
2431

25-
// 处理scriptExecutor
26-
const scriptExecutorFlag = randomMessageFlag();
27-
const scriptExecutorMsg = new CustomEventMessage(scriptExecutorFlag, true);
28-
const scriptExecutor = new ScriptExecutor(new CustomEventMessage(scriptExecutorFlag, false));
32+
loggerCore.logger().debug("content start");
2933

30-
const loadEarlyScriptFlag = (flag: string[]) => {
31-
scriptExecutor.checkEarlyStartScript(flag);
32-
};
33-
// 处理EarlyScript
34-
const earylyFlag = window.EarlyScriptFlag;
35-
if (earylyFlag) {
36-
// @ts-ignore
37-
window.EarlyScriptFlag = null; // 释放物件参考
38-
loadEarlyScriptFlag(earylyFlag);
39-
} else {
40-
// 监听属性设置
41-
Object.defineProperty(window, "EarlyScriptFlag", {
42-
configurable: true,
43-
set: (val: string[]) => {
44-
delete window.EarlyScriptFlag; // 删除 property setter 避免重复呼叫
45-
loadEarlyScriptFlag(val);
46-
},
47-
});
48-
}
34+
const msgInject = new CustomEventMessage(messageFlag, true);
4935

50-
// 初始化日志组件
51-
const loggerCore = new LoggerCore({
52-
writer: new MessageWriter(extMsgComm),
53-
labels: { env: "content" },
54-
});
36+
// 处理scriptExecutor
37+
const scriptExecutorFlag = randomMessageFlag();
38+
const scriptExecutorMsg = new CustomEventMessage(scriptExecutorFlag, true);
39+
const scriptExecutor = new ScriptExecutor(new CustomEventMessage(scriptExecutorFlag, false));
5540

56-
const client = new RuntimeClient(extMsgComm);
57-
client.pageLoad().then((data) => {
58-
loggerCore.logger().debug("content start");
59-
const msgInject = new CustomEventMessage(data.flag, true);
6041
const server = new Server("content", [msgInject, scriptExecutorMsg]);
42+
6143
// Opera中没有chrome.runtime.onConnect,并且content也不需要chrome.runtime.onConnect
6244
// 所以不需要处理连接,设置为false
6345
const extServer = new Server("content", extMsgComm, false);
6446
// scriptExecutor的消息接口
6547
// 初始化运行环境
6648
const runtime = new ContentRuntime(extServer, server, extMsgComm, msgInject, scriptExecutorMsg, scriptExecutor);
6749
runtime.init();
68-
runtime.start(data.scripts, data.envInfo);
69-
});
50+
// 页面加载,注入脚本
51+
runtime.pageLoad();
52+
};
53+
54+
// 监听MessageFlag
55+
definePropertyListener(window, "MessageFlag", start);
7056
}

0 commit comments

Comments
 (0)