Skip to content

Commit 0554159

Browse files
cyfung1031CodFrm
andauthored
♻️ 整理 inject & content,修改 pageLoad 资讯量传递 (#952)
* 整理 inject & content * 不需判断是否 CustomEvent, 命名简化 * 修正 加载logger组件 次序 * 优化EarlyStart注入代码 * 改个版本号 * 重构 pageLoad 处理,减少不需要的资讯传递 1 * 修改为message flag * 修复单元测试 * 修改注释简中 --------- Co-authored-by: 王一之 <yz@ggnb.top>
1 parent 3d92bfb commit 0554159

23 files changed

Lines changed: 351 additions & 244 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "scriptcat",
3-
"version": "1.2.0-beta.4",
3+
"version": "1.2.0-beta.5",
44
"description": "脚本猫,一个可以执行用户脚本的浏览器扩展,万物皆可脚本化,让你的浏览器可以做更多的事情!",
55
"author": "CodFrm",
66
"license": "GPLv3",

packages/message/custom_event_message.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ export class CustomEventMessage implements Message {
2323
relatedTarget: Map<number, EventTarget> = new Map();
2424

2525
constructor(
26-
flags: MessageFlags | string,
26+
messageFlag: string,
2727
protected readonly isContent: boolean
2828
) {
29-
const messageFlag = typeof flags === "string" ? flags : flags.messageFlag;
3029
this.receiveFlag = `evt${messageFlag}${isContent ? DefinedFlags.contentFlag : DefinedFlags.injectFlag}${DefinedFlags.domEvent}`;
3130
this.sendFlag = `evt${messageFlag}${isContent ? DefinedFlags.injectFlag : DefinedFlags.contentFlag}${DefinedFlags.domEvent}`;
3231
window.addEventListener(this.receiveFlag, (event) => {

packages/message/server.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ let client: CustomEventMessage;
1212
const nextTick = () => Promise.resolve().then(() => {});
1313

1414
const setupGlobal = () => {
15-
const flags = { messageFlag: "-test.server" };
15+
const flags = "-test.server";
1616
// 创建 content 和 inject 之间的消息通道
1717
contentMessage = new CustomEventMessage(flags, true); // content 端
1818
injectMessage = new CustomEventMessage(flags, false); // inject 端

src/app/repo/scripts.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Repo } from "./repo";
22
import type { Resource } from "./resource";
33
import type { SCMetadata } from "./metadata";
4+
import type { GMInfoEnv } from "../service/content/types";
45

56
// 脚本模型
67
export type SCRIPT_TYPE = 1 | 2 | 3;
@@ -101,6 +102,46 @@ export interface ScriptRunResource extends Script {
101102
originalMetadata: SCMetadata; // 原本的 Metadata (目前只需要 match, include, exclude)
102103
}
103104

105+
/**
106+
* 脚本加载信息。( service_worker / sandbox / popup 环境用 )
107+
* 包含脚本元数据与用户配置。
108+
*/
109+
export interface ScriptLoadInfo extends ScriptRunResource {
110+
/** 脚本元数据字符串 */
111+
metadataStr: string;
112+
/** 用户配置字符串 */
113+
userConfigStr: string;
114+
/** 用户配置对象(可选) */
115+
userConfig?: UserConfig;
116+
}
117+
118+
/**
119+
* 脚本加载信息。( Inject / Content 环境用,避免过多不必要资讯公开,减少页面加载资讯储存量 )
120+
* 包含脚本元数据与用户配置。
121+
*/
122+
export type TScriptInfo = Override<
123+
ScriptLoadInfo,
124+
{
125+
originalMetadata?: Partial<Record<string, string[]>>;
126+
resource: Record<string, { base64?: string; content: string; contentType: string }>;
127+
code: "" | string;
128+
sort?: number;
129+
flag: string;
130+
runStatus?: SCRIPT_RUN_STATUS;
131+
type?: SCRIPT_TYPE;
132+
status?: SCRIPT_STATUS;
133+
}
134+
>;
135+
136+
export type TClientPageLoadInfo =
137+
| {
138+
ok: true;
139+
injectScriptList: TScriptInfo[];
140+
contentScriptList: TScriptInfo[];
141+
envInfo: GMInfoEnv;
142+
}
143+
| { ok: false };
144+
104145
export class ScriptDAO extends Repo<Script> {
105146
scriptCodeDAO: ScriptCodeDAO = new ScriptCodeDAO();
106147

src/app/service/content/content.ts

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,26 @@ import { Client, sendMessage } from "@Packages/message/client";
22
import { type CustomEventMessage } from "@Packages/message/custom_event_message";
33
import { forwardMessage, type Server } from "@Packages/message/server";
44
import type { MessageSend } from "@Packages/message/types";
5-
import type { GMInfoEnv } from "./types";
6-
import type { ScriptLoadInfo } from "../service_worker/types";
75
import type { ScriptExecutor } from "./script_executor";
8-
import { isInjectIntoContent } from "./utils";
96
import { RuntimeClient } from "../service_worker/client";
107

118
// content页的处理
129
export default class ContentRuntime {
1310
// 运行在content页面的脚本
14-
contentScript: Map<string, ScriptLoadInfo> = new Map();
11+
private readonly contentScriptSet: Set<string> = new Set();
1512

1613
constructor(
1714
// 监听来自service_worker的消息
18-
private extServer: Server,
15+
private readonly extServer: Server,
1916
// 监听来自inject的消息
20-
private server: Server,
17+
private readonly server: Server,
2118
// 发送给扩展service_worker的通信接口
22-
private senderToExt: MessageSend,
19+
private readonly senderToExt: MessageSend,
2320
// 发送给inject的消息接口
24-
private senderToInject: CustomEventMessage,
21+
private readonly senderToInject: CustomEventMessage,
2522
// 脚本执行器消息接口
26-
private scriptExecutorMsg: CustomEventMessage,
27-
private scriptExecutor: ScriptExecutor
23+
private readonly scriptExecutorMsg: CustomEventMessage,
24+
private readonly scriptExecutor: ScriptExecutor
2825
) {}
2926

3027
init() {
@@ -76,7 +73,7 @@ export default class ContentRuntime {
7673
let parentNode: EventTarget | undefined;
7774
// 判断是不是content脚本发过来的
7875
let msg: CustomEventMessage;
79-
if (this.contentScript.has(data.uuid) || this.scriptExecutor.execMap.has(data.uuid)) {
76+
if (this.contentScriptSet.has(data.uuid) || this.scriptExecutor.execMap.has(data.uuid)) {
8077
msg = this.scriptExecutorMsg;
8178
} else {
8279
msg = this.senderToInject;
@@ -126,38 +123,25 @@ export default class ContentRuntime {
126123
);
127124
}
128125

129-
pageLoad(messageFlags: MessageFlags) {
130-
this.scriptExecutor.checkEarlyStartScript("content", messageFlags);
131-
126+
pageLoad(messageFlag: string) {
127+
this.scriptExecutor.checkEarlyStartScript("content", messageFlag);
132128
const client = new RuntimeClient(this.senderToExt);
133-
// 向service_worker请求脚本列表
134-
client.pageLoad().then((data) => {
135-
this.start(data.scripts, data.envInfo);
136-
});
137-
}
138-
139-
start(scripts: ScriptLoadInfo[], envInfo: GMInfoEnv) {
140-
// 启动脚本
141-
const client = new Client(this.senderToInject, "inject");
142-
// 根据@inject-into content过滤脚本
143-
const injectScript: ScriptLoadInfo[] = [];
144-
const contentScript: ScriptLoadInfo[] = [];
145-
for (const script of scripts) {
146-
if (isInjectIntoContent(script.metadata)) {
147-
contentScript.push(script);
148-
continue;
129+
// 向service_worker请求脚本列表及环境信息
130+
client.pageLoad().then((o) => {
131+
if (!o.ok) return;
132+
const { injectScriptList, contentScriptList, envInfo } = o;
133+
// 启动脚本:向 inject页面 发送脚本列表及环境信息
134+
const client = new Client(this.senderToInject, "inject");
135+
// 根据@inject-into content过滤脚本
136+
client.do("pageLoad", { injectScriptList, envInfo });
137+
// 处理注入到content环境的脚本
138+
for (const script of contentScriptList) {
139+
this.contentScriptSet.add(script.uuid);
149140
}
150-
injectScript.push(script);
151-
}
152-
client.do("pageLoad", { scripts: injectScript, envInfo });
153-
154-
// 处理注入到content环境的脚本
155-
for (const script of contentScript) {
156-
this.contentScript.set(script.uuid, script);
157-
}
158-
// 监听事件
159-
this.scriptExecutor.init(envInfo);
160-
// 启动脚本
161-
this.scriptExecutor.start(contentScript);
141+
// 监听事件
142+
this.scriptExecutor.setEnvInfo(envInfo);
143+
// 启动脚本
144+
this.scriptExecutor.startScripts(contentScriptList);
145+
});
162146
}
163147
}

src/app/service/content/create_context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ScriptRunResource } from "@App/app/repo/scripts";
1+
import type { TScriptInfo } from "@App/app/repo/scripts";
22
import { v4 as uuidv4 } from "uuid";
33
import type { Message } from "@Packages/message/types";
44
import EventEmitter from "eventemitter3";
@@ -10,7 +10,7 @@ import { createGMBase } from "./gm_api/gm_api";
1010

1111
// 构建沙盒上下文
1212
export const createContext = (
13-
scriptRes: ScriptRunResource,
13+
scriptRes: TScriptInfo,
1414
GMInfo: any,
1515
envPrefix: string,
1616
message: Message,

src/app/service/content/exec_script.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { createContext, createProxyContext } from "./create_context";
44
import type { GMInfoEnv, ScriptFunc } from "./types";
55
import { compileScript } from "./utils";
66
import type { Message } from "@Packages/message/types";
7-
import type { ScriptLoadInfo } from "../service_worker/types";
87
import type { ValueUpdateDataEncoded } from "./types";
98
import { evaluateGMInfo } from "./gm_api/gm_info";
109
import type { IGM_Base } from "./gm_api/gm_api";
10+
import type { TScriptInfo } from "@App/app/repo/scripts";
1111

1212
// 执行脚本,控制脚本执行与停止
1313
export default class ExecScript {
14-
scriptRes: ScriptLoadInfo;
14+
scriptRes: TScriptInfo;
1515

1616
scriptFunc: ScriptFunc;
1717

@@ -24,7 +24,7 @@ export default class ExecScript {
2424
named?: { [key: string]: any };
2525

2626
constructor(
27-
scriptRes: ScriptLoadInfo,
27+
scriptRes: TScriptInfo,
2828
envPrefix: "content" | "offscreen",
2929
message: Message,
3030
code: string | ScriptFunc,

src/app/service/content/gm_api/gm_info.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ExtVersion } from "@App/app/const";
22
import type { GMInfoEnv } from "../types";
3-
import type { ScriptLoadInfo } from "@App/app/service/service_worker/types";
3+
import type { TScriptInfo } from "@App/app/repo/scripts";
44

55
// 获取脚本信息和管理器信息
6-
export function evaluateGMInfo(envInfo: GMInfoEnv, script: ScriptLoadInfo) {
6+
export function evaluateGMInfo(envInfo: GMInfoEnv, script: TScriptInfo) {
77
const options = {
88
description: script.metadata.description?.[0] || null,
99
matches: script.metadata.match || [],
@@ -21,15 +21,14 @@ export function evaluateGMInfo(envInfo: GMInfoEnv, script: ScriptLoadInfo) {
2121
isIncognito: envInfo.isIncognito,
2222
// relaxedCsp
2323
sandboxMode: envInfo.sandboxMode,
24-
scriptWillUpdate: true,
24+
scriptWillUpdate: !!script.checkUpdate,
2525
scriptHandler: "ScriptCat",
2626
userAgentData: envInfo.userAgentData,
2727
// "" => null
2828
scriptUpdateURL: script.downloadUrl || null,
2929
scriptMetaStr: script.metadataStr,
3030
userConfig: script.userConfig,
3131
userConfigStr: script.userConfigStr,
32-
// scriptSource: script.sourceCode,
3332
version: ExtVersion,
3433
script: {
3534
// TODO: 更多完整的信息(为了兼容Tampermonkey,后续待定)

src/app/service/content/inject.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,38 @@ import type { Message } from "@Packages/message/types";
33
import { ExternalWhitelist } from "@App/app/const";
44
import { sendMessage } from "@Packages/message/client";
55
import type { ScriptExecutor } from "./script_executor";
6-
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
6+
import type { TScriptInfo } from "@App/app/repo/scripts";
7+
import type { EmitEventRequest } from "../service_worker/types";
78
import type { GMInfoEnv, ValueUpdateDataEncoded } from "./types";
89

910
export class InjectRuntime {
1011
constructor(
11-
private server: Server,
12-
private msg: Message,
13-
private scriptExecutor: ScriptExecutor
12+
private readonly server: Server,
13+
private readonly msg: Message,
14+
private readonly scriptExecutor: ScriptExecutor
1415
) {}
1516

16-
init(envInfo: GMInfoEnv) {
17-
this.scriptExecutor.init(envInfo);
18-
17+
init() {
1918
this.server.on("runtime/emitEvent", (data: EmitEventRequest) => {
2019
// 转发给脚本
2120
this.scriptExecutor.emitEvent(data);
2221
});
2322
this.server.on("runtime/valueUpdate", (data: ValueUpdateDataEncoded) => {
2423
this.scriptExecutor.valueUpdate(data);
2524
});
25+
}
2626

27-
// 注入允许外部调用
28-
this.externalMessage();
27+
setEnvInfo(envInfo: GMInfoEnv) {
28+
this.scriptExecutor.setEnvInfo(envInfo);
2929
}
3030

31-
start(scripts: ScriptLoadInfo[]) {
32-
this.scriptExecutor.start(scripts);
31+
startScripts(injectScriptList: TScriptInfo[]) {
32+
this.scriptExecutor.startScripts(injectScriptList);
33+
}
34+
35+
onInjectPageLoaded() {
36+
// 注入允许外部调用
37+
this.externalMessage();
3338
}
3439

3540
externalMessage() {

src/app/service/content/script_executor.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import type { Message } from "@Packages/message/types";
22
import { getStorageName } from "@App/pkg/utils/utils";
3-
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
3+
import type { EmitEventRequest } from "../service_worker/types";
44
import ExecScript from "./exec_script";
5-
import type { GMInfoEnv, ScriptFunc, PreScriptFunc, ValueUpdateDataEncoded } from "./types";
5+
import type { GMInfoEnv, ScriptFunc, ValueUpdateDataEncoded } from "./types";
66
import { addStyle, definePropertyListener } from "./utils";
7+
import type { TScriptInfo } from "@App/app/repo/scripts";
78
import { DefinedFlags } from "../service_worker/runtime.consts";
89

910
export type ExecScriptEntry = {
10-
scriptLoadInfo: ScriptLoadInfo;
11+
scriptLoadInfo: TScriptInfo;
1112
scriptFlag: string;
1213
envInfo: any;
1314
scriptFunc: any;
@@ -22,7 +23,7 @@ export class ScriptExecutor {
2223

2324
constructor(private msg: Message) {}
2425

25-
init(envInfo: GMInfoEnv) {
26+
setEnvInfo(envInfo: GMInfoEnv) {
2627
this.envInfo = envInfo;
2728
}
2829

@@ -43,8 +44,8 @@ export class ScriptExecutor {
4344
}
4445
}
4546

46-
start(scripts: ScriptLoadInfo[]) {
47-
const loadExec = (script: ScriptLoadInfo, scriptFunc: any) => {
47+
startScripts(scripts: TScriptInfo[]) {
48+
const loadExec = (script: TScriptInfo, scriptFunc: any) => {
4849
this.execScriptEntry({
4950
scriptLoadInfo: script,
5051
scriptFlag: script.flag,
@@ -71,21 +72,19 @@ export class ScriptExecutor {
7172
});
7273
}
7374

74-
checkEarlyStartScript(env: "content" | "inject", messageFlags: MessageFlags) {
75+
checkEarlyStartScript(env: "content" | "inject", messageFlag: string) {
7576
const isContent = env === "content";
76-
const messageFlag = messageFlags.messageFlag;
7777
const eventNamePrefix = `evt${messageFlag}${isContent ? DefinedFlags.contentFlag : DefinedFlags.injectFlag}`;
7878
const scriptLoadCompleteEvtName = `${eventNamePrefix}${DefinedFlags.scriptLoadComplete}`;
7979
const envLoadCompleteEvtName = `${eventNamePrefix}${DefinedFlags.envLoadComplete}`;
8080
// 监听 脚本加载
8181
// 适用于此「通知环境加载完成」代码执行后的脚本加载
82-
window.addEventListener(scriptLoadCompleteEvtName, (event) => {
83-
if (event instanceof CustomEvent) {
84-
if (typeof event.detail.scriptFlag === "string") {
85-
event.preventDefault(); // dispatchEvent 会回传 false -> 分离环境也能得知环境加载代码已执行
86-
const scriptFlag = event.detail.scriptFlag;
87-
this.execEarlyScript(scriptFlag);
88-
}
82+
window.addEventListener(scriptLoadCompleteEvtName, (ev) => {
83+
const detail = (ev as CustomEvent).detail;
84+
const scriptFlag = detail?.scriptFlag;
85+
if (typeof scriptFlag === "string") {
86+
ev.preventDefault(); // dispatchEvent 会回传 false -> 分离环境也能得知环境加载代码已执行
87+
this.execEarlyScript(scriptFlag, detail.scriptInfo);
8988
}
9089
});
9190
// 通知 环境 加载完成
@@ -94,11 +93,11 @@ export class ScriptExecutor {
9493
window.dispatchEvent(ev);
9594
}
9695

97-
execEarlyScript(flag: string) {
98-
const scriptFunc = (window as any)[flag] as PreScriptFunc;
96+
execEarlyScript(flag: string, scriptInfo: TScriptInfo) {
97+
const scriptFunc = (window as any)[flag] as ScriptFunc;
9998
this.execScriptEntry({
100-
scriptLoadInfo: scriptFunc.scriptInfo,
101-
scriptFunc: scriptFunc.func,
99+
scriptLoadInfo: scriptInfo,
100+
scriptFunc: scriptFunc,
102101
scriptFlag: flag,
103102
envInfo: {},
104103
});

0 commit comments

Comments
 (0)