Skip to content

Commit 53dbbe8

Browse files
committed
Merge branch 'main' into release/v1.3
2 parents d1a5cb1 + 1f3a3ec commit 53dbbe8

File tree

48 files changed

+824
-464
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+824
-464
lines changed

.github/workflows/test.yaml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,29 @@ jobs:
2323
node-version: 22
2424
cache: 'pnpm'
2525

26+
- name: Setup pnpm
27+
run: corepack enable
28+
2629
- name: Install dependencies
2730
run: pnpm i --frozen-lockfile
2831

32+
- name: Cache ESLint cache
33+
uses: actions/cache@v4
34+
with:
35+
path: .eslintcache
36+
key: eslint-${{ hashFiles('pnpm-lock.yaml', '.eslintrc*') }}
37+
restore-keys: |
38+
eslint-
39+
2940
- name: Lint
3041
run: |
3142
pnpm run typecheck
32-
pnpm run lint
43+
pnpm run lint:ci
3344
3445
- name: Unit Test
3546
run: |
36-
pnpm test
37-
pnpm run coverage
47+
pnpm test:ci
48+
pnpm coverage:ci
3849
3950
- name: Upload coverage reports to Codecov with GitHub Action
4051
uses: codecov/codecov-action@v5

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
"scripts": {
99
"preinstall": "pnpm dlx only-allow pnpm",
1010
"test": "vitest --test-timeout=500 --no-coverage --isolate=false --reporter=verbose",
11+
"test:ci": "vitest run --test-timeout=500 --no-coverage --isolate=false --reporter=default --reporter.summary=false",
1112
"coverage": "vitest run --coverage",
1213
"typecheck": "tsc --noEmit",
14+
"coverage:ci": "vitest run --coverage --silent --reporter=default --reporter.default.summary=false",
1315
"build": "cross-env NODE_ENV=production rspack build",
1416
"dev": "cross-env NODE_ENV=development rspack",
1517
"dev:noMap": "cross-env NODE_ENV=development NO_MAP=true rspack",
1618
"pack": "node ./scripts/pack.js",
1719
"format": "prettier --write .",
1820
"lint": "eslint .",
21+
"lint:ci": "eslint . --cache --cache-location .eslintcache",
1922
"lint-fix": "eslint --fix .",
2023
"changlog": "node ./scripts/changlog.js",
2124
"crowdin": "crowdin",

packages/message/custom_event_message.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ import LoggerCore from "@App/app/logger/core";
55
import EventEmitter from "eventemitter3";
66
import { DefinedFlags } from "@App/app/service/service_worker/runtime.consts";
77

8+
// 避免页面载入后改动 EventTarget.prototype 的方法导致消息传递失败
9+
const pageDispatchEvent = performance.dispatchEvent.bind(performance);
10+
const pageAddEventListener = performance.addEventListener.bind(performance);
11+
12+
// 避免页面载入后改动全域物件导致消息传递失败
13+
const MouseEventClone = MouseEvent;
14+
const CustomEventClone = CustomEvent;
15+
16+
// 避免页面载入后改动 Map.prototype 导致消息传递失败
17+
const relatedTargetMap = new Map<number, EventTarget>();
18+
relatedTargetMap.set = Map.prototype.set;
19+
relatedTargetMap.get = Map.prototype.get;
20+
relatedTargetMap.delete = Map.prototype.delete;
21+
22+
let relateId = 0;
23+
const maxInteger = Number.MAX_SAFE_INTEGER;
24+
825
export class CustomEventPostMessage implements PostMessage {
926
constructor(private send: CustomEventMessage) {}
1027

@@ -28,10 +45,10 @@ export class CustomEventMessage implements Message {
2845
) {
2946
this.receiveFlag = `evt${messageFlag}${isContent ? DefinedFlags.contentFlag : DefinedFlags.injectFlag}${DefinedFlags.domEvent}`;
3047
this.sendFlag = `evt${messageFlag}${isContent ? DefinedFlags.injectFlag : DefinedFlags.contentFlag}${DefinedFlags.domEvent}`;
31-
window.addEventListener(this.receiveFlag, (event) => {
32-
if (event instanceof MouseEvent && event.movementX && event.relatedTarget) {
33-
this.relatedTarget.set(event.movementX, event.relatedTarget!);
34-
} else if (event instanceof CustomEvent) {
48+
pageAddEventListener(this.receiveFlag, (event) => {
49+
if (event instanceof MouseEventClone && event.movementX && event.relatedTarget) {
50+
relatedTargetMap.set(event.movementX, event.relatedTarget!);
51+
} else if (event instanceof CustomEventClone) {
3552
this.messageHandle(event.detail, new CustomEventPostMessage(this));
3653
}
3754
});
@@ -100,10 +117,10 @@ export class CustomEventMessage implements Message {
100117
}
101118
}
102119

103-
const ev = new CustomEvent(this.sendFlag, {
120+
const ev = new CustomEventClone(this.sendFlag, {
104121
detail,
105122
});
106-
window.dispatchEvent(ev);
123+
pageDispatchEvent(ev);
107124
}
108125

109126
sendMessage<T = any>(data: TMessage): Promise<T> {
@@ -146,24 +163,22 @@ export class CustomEventMessage implements Message {
146163
return ret;
147164
}
148165

149-
relateId = 0;
150-
151166
sendRelatedTarget(target: EventTarget): number {
152167
// 特殊处理relatedTarget,返回id进行关联
153168
// 先将relatedTarget转换成id发送过去
154-
const id = ++this.relateId;
169+
const id = (relateId = relateId === maxInteger ? 1 : relateId + 1);
155170
// 可以使用此种方式交互element
156-
const ev = new MouseEvent(this.sendFlag, {
171+
const ev = new MouseEventClone(this.sendFlag, {
157172
movementX: id,
158173
relatedTarget: target,
159174
});
160-
window.dispatchEvent(ev);
175+
pageDispatchEvent(ev);
161176
return id;
162177
}
163178

164179
getAndDelRelatedTarget(id: number) {
165-
const target = this.relatedTarget.get(id);
166-
this.relatedTarget.delete(id);
180+
const target = relatedTargetMap.get(id);
181+
relatedTargetMap.delete(id);
167182
return target;
168183
}
169184
}

packages/message/extension_message.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export class ExtensionContentMessageSend implements MessageSend {
180180

181181
sendMessage<T = any>(data: TMessage): Promise<T> {
182182
return new Promise((resolve) => {
183-
if (!this.options?.documentId || this.options?.frameId) {
183+
if (!this.options?.documentId && !this.options?.frameId) {
184184
// 发送给指定的tab
185185
chrome.tabs.sendMessage(this.tabId, data, (resp: T) => {
186186
const lastError = chrome.runtime.lastError;

packages/message/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export class SenderRuntime {
100100
}
101101
}
102102

103-
type ApiFunction = (params: any, con: IGetSender) => Promise<any> | void;
103+
type ApiFunction = (params: any, con: IGetSender) => Promise<any> | any | void;
104104
type ApiFunctionSync = (params: any, con: IGetSender) => any;
105105
type MiddlewareFunction = (params: any, con: IGetSender, next: () => Promise<any> | any) => Promise<any> | any;
106106

src/app/service/content/content.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import type { MessageSend } from "@Packages/message/types";
55
import type { ScriptExecutor } from "./script_executor";
66
import { RuntimeClient } from "../service_worker/client";
77
import { makeBlobURL } from "@App/pkg/utils/utils";
8+
import type { GMInfoEnv } from "./types";
9+
import type { Logger } from "@App/app/repo/logger";
10+
import LoggerCore from "@App/app/logger/core";
811

912
// content页的处理
1013
export default class ContentRuntime {
@@ -36,6 +39,9 @@ export default class ContentRuntime {
3639
this.scriptExecutor.valueUpdate(data);
3740
return sendMessage(this.senderToInject, "inject/runtime/valueUpdate", data);
3841
});
42+
this.server.on("logger", (data: Logger) => {
43+
LoggerCore.logger().log(data.level, data.message, data.label);
44+
});
3945
forwardMessage("serviceWorker", "script/isInstalled", this.server, this.senderToExt);
4046
forwardMessage(
4147
"serviceWorker",
@@ -121,8 +127,8 @@ export default class ContentRuntime {
121127
);
122128
}
123129

124-
pageLoad(messageFlag: string) {
125-
this.scriptExecutor.checkEarlyStartScript("content", messageFlag);
130+
pageLoad(messageFlag: string, envInfo: GMInfoEnv) {
131+
this.scriptExecutor.checkEarlyStartScript("content", messageFlag, envInfo);
126132
const client = new RuntimeClient(this.senderToExt);
127133
// 向service_worker请求脚本列表及环境信息
128134
client.pageLoad().then((o) => {
@@ -136,10 +142,8 @@ export default class ContentRuntime {
136142
for (const script of contentScriptList) {
137143
this.contentScriptSet.add(script.uuid);
138144
}
139-
// 监听事件
140-
this.scriptExecutor.setEnvInfo(envInfo);
141145
// 启动脚本
142-
this.scriptExecutor.startScripts(contentScriptList);
146+
this.scriptExecutor.startScripts(contentScriptList, envInfo);
143147
});
144148
}
145149
}

src/app/service/content/inject.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,8 @@ export class InjectRuntime {
2424
});
2525
}
2626

27-
setEnvInfo(envInfo: GMInfoEnv) {
28-
this.scriptExecutor.setEnvInfo(envInfo);
29-
}
30-
31-
startScripts(injectScriptList: TScriptInfo[]) {
32-
this.scriptExecutor.startScripts(injectScriptList);
27+
startScripts(injectScriptList: TScriptInfo[], envInfo: GMInfoEnv) {
28+
this.scriptExecutor.startScripts(injectScriptList, envInfo);
3329
}
3430

3531
onInjectPageLoaded() {

src/app/service/content/script_executor.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,30 @@ export type ExecScriptEntry = {
1414
scriptFunc: any;
1515
};
1616

17+
export let initEnvInfo: GMInfoEnv;
18+
19+
try {
20+
initEnvInfo = {
21+
userAgentData: UserAgentData, // 从全局变量获取
22+
sandboxMode: "raw", // 预留字段,当前固定为 raw
23+
isIncognito: false, // inject 环境下无法判断,固定为 false
24+
};
25+
} catch {
26+
// 如果 UserAgentData 不存在,可能是在非inject/content环境下运行
27+
initEnvInfo = {
28+
userAgentData: {},
29+
sandboxMode: "raw",
30+
isIncognito: false,
31+
};
32+
}
33+
1734
// 脚本执行器
1835
export class ScriptExecutor {
1936
earlyScriptFlag: Set<string> = new Set();
2037
execMap: Map<string, ExecScript> = new Map();
2138

22-
envInfo: GMInfoEnv | undefined;
23-
2439
constructor(private msg: Message) {}
2540

26-
setEnvInfo(envInfo: GMInfoEnv) {
27-
this.envInfo = envInfo;
28-
}
29-
3041
emitEvent(data: EmitEventRequest) {
3142
// 转发给脚本
3243
const exec = this.execMap.get(data.uuid);
@@ -44,13 +55,13 @@ export class ScriptExecutor {
4455
}
4556
}
4657

47-
startScripts(scripts: TScriptInfo[]) {
58+
startScripts(scripts: TScriptInfo[], envInfo: GMInfoEnv) {
4859
const loadExec = (script: TScriptInfo, scriptFunc: any) => {
4960
this.execScriptEntry({
5061
scriptLoadInfo: script,
5162
scriptFlag: script.flag,
5263
scriptFunc,
53-
envInfo: this.envInfo!,
64+
envInfo: envInfo,
5465
});
5566
};
5667
// 监听脚本加载
@@ -61,7 +72,7 @@ export class ScriptExecutor {
6172
for (const val of this.execMap.values()) {
6273
if (val.scriptRes.flag === flag) {
6374
// 处理早期脚本的沙盒环境
64-
val.updateEarlyScriptGMInfo(this.envInfo!);
75+
val.updateEarlyScriptGMInfo(envInfo);
6576
return;
6677
}
6778
}
@@ -72,34 +83,34 @@ export class ScriptExecutor {
7283
});
7384
}
7485

75-
checkEarlyStartScript(env: "content" | "inject", messageFlag: string) {
86+
checkEarlyStartScript(env: "content" | "inject", messageFlag: string, envInfo: GMInfoEnv) {
7687
const isContent = env === "content";
7788
const eventNamePrefix = `evt${messageFlag}${isContent ? DefinedFlags.contentFlag : DefinedFlags.injectFlag}`;
7889
const scriptLoadCompleteEvtName = `${eventNamePrefix}${DefinedFlags.scriptLoadComplete}`;
7990
const envLoadCompleteEvtName = `${eventNamePrefix}${DefinedFlags.envLoadComplete}`;
8091
// 监听 脚本加载
8192
// 适用于此「通知环境加载完成」代码执行后的脚本加载
82-
window.addEventListener(scriptLoadCompleteEvtName, (ev) => {
93+
performance.addEventListener(scriptLoadCompleteEvtName, (ev) => {
8394
const detail = (ev as CustomEvent).detail;
8495
const scriptFlag = detail?.scriptFlag;
8596
if (typeof scriptFlag === "string") {
8697
ev.preventDefault(); // dispatchEvent 会回传 false -> 分离环境也能得知环境加载代码已执行
87-
this.execEarlyScript(scriptFlag, detail.scriptInfo);
98+
this.execEarlyScript(scriptFlag, detail.scriptInfo, envInfo);
8899
}
89100
});
90101
// 通知 环境 加载完成
91102
// 适用于此「通知环境加载完成」代码执行前的脚本加载
92103
const ev = new CustomEvent(envLoadCompleteEvtName);
93-
window.dispatchEvent(ev);
104+
performance.dispatchEvent(ev);
94105
}
95106

96-
execEarlyScript(flag: string, scriptInfo: TScriptInfo) {
107+
execEarlyScript(flag: string, scriptInfo: TScriptInfo, envInfo: GMInfoEnv) {
97108
const scriptFunc = (window as any)[flag] as ScriptFunc;
98109
this.execScriptEntry({
99110
scriptLoadInfo: scriptInfo,
100111
scriptFunc: scriptFunc,
101112
scriptFlag: flag,
102-
envInfo: {},
113+
envInfo: envInfo,
103114
});
104115
this.earlyScriptFlag.add(flag);
105116
}

src/app/service/content/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ export interface ApiValue {
4848

4949
export interface GMInfoEnv {
5050
userAgentData: typeof GM_info.userAgentData;
51-
sandboxMode: typeof GM_info.sandboxMode;
51+
sandboxMode: typeof GM_info.sandboxMode; // 目前固定为 "raw",预留
5252
isIncognito: typeof GM_info.isIncognito;
5353
}

src/app/service/content/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ export function compilePreInjectScript(
151151
return `window['${flag}'] = function(){${autoDeleteMountCode}${scriptCode}};
152152
{
153153
let o = { cancelable: true, detail: { scriptFlag: '${flag}', scriptInfo: (${scriptInfoJSON}) } },
154-
f = () => window.dispatchEvent(new CustomEvent('${evScriptLoad}', o)),
154+
f = () => performance.dispatchEvent(new CustomEvent('${evScriptLoad}', o)),
155155
needWait = f();
156-
if (needWait) window.addEventListener('${evEnvLoad}', f, { once: true });
156+
if (needWait) performance.addEventListener('${evEnvLoad}', f, { once: true });
157157
}
158158
`;
159159
}

0 commit comments

Comments
 (0)