Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/repo/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export type ScriptAndCode = Script & ScriptCode;

// 脚本运行时的资源,包含已经编译好的脚本与脚本需要的资源
export interface ScriptRunResource extends Script {
code: string;
code: string; // 原始代码
value: { [key: string]: any };
flag: string;
resource: { [key: string]: { base64?: string } & Omit<Resource, "base64"> }; // 资源列表,包含脚本需要的资源
Expand Down
18 changes: 16 additions & 2 deletions src/app/service/content/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type { MessageSend } from "@Packages/message/types";
import type { GMInfoEnv } from "./types";
import type { ScriptLoadInfo } from "../service_worker/types";
import type { ScriptExecutor } from "./script_executor";
import { isInjectIntoContent } from "./utils";
import { definePropertyListener, isInjectIntoContent } from "./utils";
import { RuntimeClient } from "../service_worker/client";

// content页的处理
export default class ContentRuntime {
Expand Down Expand Up @@ -75,7 +76,7 @@ export default class ContentRuntime {
let parentNode: EventTarget | undefined;
// 判断是不是content脚本发过来的
let msg: CustomEventMessage;
if (this.contentScript.has(data.uuid)) {
if (this.contentScript.has(data.uuid) || this.scriptExecutor.execMap.has(data.uuid)) {
msg = this.scriptExecutorMsg;
} else {
msg = this.senderToInject;
Expand Down Expand Up @@ -125,6 +126,19 @@ export default class ContentRuntime {
);
}

pageLoad() {
// 处理content EarlyScript
definePropertyListener(window, "EarlyScriptFlag", (flag: string[]) => {
this.scriptExecutor.checkEarlyStartScript(flag);
});

const client = new RuntimeClient(this.senderToExt);
// 向service_worker请求脚本列表
client.pageLoad().then((data) => {
this.start(data.scripts, data.envInfo);
});
}

start(scripts: ScriptLoadInfo[], envInfo: GMInfoEnv) {
// 启动脚本
const client = new Client(this.senderToInject, "inject");
Expand Down
59 changes: 15 additions & 44 deletions src/app/service/content/script_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { getStorageName } from "@App/pkg/utils/utils";
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
import ExecScript from "./exec_script";
import type { GMInfoEnv, ScriptFunc, PreScriptFunc, ValueUpdateDataEncoded } from "./types";
import { addStyle } from "./utils";
import { addStyle, definePropertyListener } from "./utils";

export type ExecScriptEntry = {
scriptLoadInfo: any;
scriptLoadInfo: ScriptLoadInfo;
scriptFlag: string;
envInfo: any;
scriptFunc: any;
};

// 脚本执行器
export class ScriptExecutor {
execList: ExecScript[] = [];
execMap: Map<string, ExecScript> = new Map();

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

emitEvent(data: EmitEventRequest) {
// 转发给脚本
const exec = this.execList.find((val) => val.scriptRes.uuid === data.uuid);
const exec = this.execMap.get(data.uuid);
if (exec) {
exec.emitEvent(data.event, data.eventId, data.data);
}
}

valueUpdate(data: ValueUpdateDataEncoded) {
const { uuid, storageName } = data;
for (const val of this.execList) {
for (const val of this.execMap.values()) {
if (val.scriptRes.uuid === uuid || getStorageName(val.scriptRes) === storageName) {
val.valueUpdate(data);
}
Expand All @@ -55,7 +55,7 @@ export class ScriptExecutor {
const flag = script.flag;
// 如果是EarlyScriptFlag,处理沙盒环境
if (this.earlyScriptFlag.includes(flag)) {
for (const val of this.execList) {
for (const val of this.execMap.values()) {
if (val.scriptRes.flag === flag) {
// 处理早期脚本的沙盒环境
val.updateEarlyScriptGMInfo(this.envInfo!);
Expand All @@ -64,23 +64,10 @@ export class ScriptExecutor {
}
return;
}
// @ts-ignore
const scriptFunc = window[flag];
if (scriptFunc) {
// @ts-ignore
window[flag] = null; // 释放物件参考
loadExec(script, scriptFunc);
} else {
// 监听脚本加载,和屏蔽读取
Object.defineProperty(window, flag, {
configurable: true,
set: (val: ScriptFunc) => {
// @ts-ignore
delete window[flag]; // 删除 property setter 避免重复呼叫
loadExec(script, val);
},
});
}

definePropertyListener(window, flag, (val: ScriptFunc) => {
loadExec(script, val);
});
});
}

Expand All @@ -95,33 +82,17 @@ export class ScriptExecutor {
});
};
this.earlyScriptFlag.forEach((flag) => {
// @ts-ignore
const scriptFunc = window[flag] as PreScriptFunc;
if (scriptFunc) {
// @ts-ignore
window[flag] = null; // 释放物件参考
loadExec(flag, scriptFunc);
} else {
// 监听脚本加载,和屏蔽读取
Object.defineProperty(window, flag, {
configurable: true,
set: (val: PreScriptFunc) => {
// @ts-ignore
delete window[flag]; // 取消 property setter 避免重复呼叫
loadExec(flag, val);
},
});
}
definePropertyListener(window, flag, (val: PreScriptFunc) => {
loadExec(flag, val);
});
});
}

execScriptEntry(scriptEntry: ExecScriptEntry) {
const { scriptFlag, scriptLoadInfo, scriptFunc, envInfo } = scriptEntry;
const { scriptLoadInfo, scriptFunc, envInfo } = scriptEntry;

// @ts-ignore
delete window[scriptFlag];
const exec = new ExecScript(scriptLoadInfo, "content", this.msg, scriptFunc, envInfo);
this.execList.push(exec);
this.execMap.set(scriptLoadInfo.uuid, exec);
const metadata = scriptLoadInfo.metadata || {};
const resource = scriptLoadInfo.resource;
// 注入css
Expand Down
16 changes: 16 additions & 0 deletions src/app/service/content/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,19 @@ export const getScriptFlag = (uuid: string) => {
// 实作内容有待检讨
return `#-${uuid}`;
};

// 监听属性设置
export function definePropertyListener<T>(obj: any, prop: string, listener: (val: T) => void) {
if (obj[prop] !== undefined) {
listener(obj[prop]);
delete obj[prop];
return;
}
Object.defineProperty(obj, prop, {
configurable: true,
set: (val: any) => {
delete obj[prop]; // 删除 property setter
listener(val);
},
});
}
2 changes: 1 addition & 1 deletion src/app/service/service_worker/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export class RuntimeClient extends Client {
return this.do("stopScript", uuid);
}

pageLoad(): Promise<{ flag: string; scripts: ScriptLoadInfo[]; envInfo: GMInfoEnv }> {
pageLoad(): Promise<{ scripts: ScriptLoadInfo[]; envInfo: GMInfoEnv }> {
return this.doThrow("pageLoad");
}

Expand Down
23 changes: 8 additions & 15 deletions src/app/service/service_worker/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { sendMessage } from "@Packages/message/client";
import type { CompileScriptCodeResource } from "../content/utils";
import {
compileInjectScriptByFlag,
compileScriptCode,
compileScriptCodeByResource,
getScriptFlag,
isEarlyStartScript,
Expand Down Expand Up @@ -304,7 +303,7 @@ export class RuntimeService {

async updateResourceOnScriptChange(script: Script) {
if (script.type !== SCRIPT_TYPE_NORMAL || script.status !== SCRIPT_STATUS_ENABLE) {
throw "Invalid Calling of updateResourceOnScriptChange";
throw new Error("Invalid Calling of updateResourceOnScriptChange");
}
// 安装,启用,或earlyStartScript的value更新
const ret = await this.buildAndSaveCompiledResourceFromScript(script, true);
Expand Down Expand Up @@ -645,8 +644,7 @@ export class RuntimeService {

let jsCode = "";
if (withCode) {
const scriptCode = scriptRes.code;
const code = compileInjectionCode(scriptMatchInfo, scriptCode);
const code = compileInjectionCode(scriptRes, scriptRes.code);
registerScript.js[0].code = jsCode = code;
}

Expand All @@ -662,7 +660,7 @@ export class RuntimeService {
const scriptUrlPatterns = scriptMatchInfo.scriptUrlPatterns;
const originalUrlPatterns = scriptMatchInfo.originalUrlPatterns;
const result = {
flag: scriptMatchInfo.flag,
flag: scriptRes.flag,
name: script.name,
require: resourceUrls, // 仅储存url
uuid: script.uuid,
Expand All @@ -682,15 +680,14 @@ export class RuntimeService {
return { compiledResource: result, jsCode, apiScript: registerScript };
}

// 从CompiledResource中还原脚本代码
async restoreJSCodeFromCompiledResource(script: Script, result: CompiledResource) {
const earlyScript = isEarlyStartScript(script.metadata);
// 如果是预加载脚本,需要另外的处理方式
if (earlyScript) {
const scriptRes = await this.script.buildScriptRunResource(script);
if (!scriptRes) return "";
const scriptMatchInfo = await this.applyScriptMatchInfo(scriptRes);
if (!scriptMatchInfo) return "";
return compileInjectionCode(scriptMatchInfo, scriptRes.code);
return compileInjectionCode(scriptRes, scriptRes.code);
}

const originalCode = await this.script.scriptCodeDAO.get(result.uuid);
Expand Down Expand Up @@ -975,8 +972,6 @@ export class RuntimeService {
return { flag: "", scripts: [] };
}

const scriptFlag = this.getMessageFlag();

// 匹配当前页面的脚本(只包含有效脚本。自定义排除了的不包含)
const matchingResult = this.getPageScriptMatchingResultByUrl(chromeSender.url!, false, false);

Expand Down Expand Up @@ -1141,8 +1136,7 @@ export class RuntimeService {
const scriptRes = scriptsWithUpdatedResources.get(targetUUID);
const scriptDAOCode = scriptCodes[targetUUID];
if (scriptRes && scriptDAOCode) {
const scriptCode = compileScriptCode(scriptRes, scriptDAOCode);
const scriptInjectCode = compileInjectionCode(scriptRes, scriptCode);
const scriptInjectCode = compileInjectionCode(scriptRes, scriptDAOCode);
scriptRegisterInfo.js = [
{
code: scriptInjectCode,
Expand Down Expand Up @@ -1184,7 +1178,6 @@ export class RuntimeService {
}

return {
flag: scriptFlag,
scripts: enableScript,
envInfo: {
sandboxMode: "raw",
Expand Down Expand Up @@ -1236,7 +1229,7 @@ export class RuntimeService {
return [
{
id: "scriptcat-early-start-flag",
js: [{ code: "window.EarlyScriptFlag=" + flagParam + ";" }],
js: [{ code: `window.EarlyScriptFlag=${flagParam};window.MessageFlag="${messageFlag}"` }],
matches: ["<all_urls>"],
allFrames: true,
world: "USER_SCRIPT",
Expand Down Expand Up @@ -1301,7 +1294,7 @@ export class RuntimeService {
}

/**
* applyScriptMatchInfo 会进行 scriptMatch 的更新
* applyScriptMatchInfo 对脚本进行URL匹配信息的处理
*/
async applyScriptMatchInfo(scriptRes: ScriptRunResource) {
const o = scriptURLPatternResults(scriptRes);
Expand Down
3 changes: 1 addition & 2 deletions src/app/service/service_worker/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { type IMessageQueue } from "@Packages/message/message_queue";
import { createScriptInfo, type ScriptInfo, type InstallSource } from "@App/pkg/utils/scriptInstall";
import { type ResourceService } from "./resource";
import { type ValueService } from "./value";
import { compileScriptCode, isEarlyStartScript } from "../content/utils";
import { isEarlyStartScript } from "../content/utils";
import { type SystemConfig } from "@App/pkg/config/config";
import { localePath } from "@App/locales/locales";
import { arrayMove } from "@dnd-kit/sortable";
Expand Down Expand Up @@ -530,7 +530,6 @@ export class ScriptService {
ret.value = value;
ret.resource = resource;
ret.code = code.code;
ret.code = compileScriptCode(ret);
return ret;
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/service/service_worker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type SearchType = "auto" | "name" | "script_code";
* 脚本匹配信息。
* 扩展自 ScriptRunResource。
*/
export interface ScriptMatchInfo extends ScriptRunResource {
export interface ScriptMatchInfo extends Script {
/** 已被自定义覆盖的 UrlPatterns */
scriptUrlPatterns: URLRuleEntry[];
/** 脚本原本的 UrlPatterns */
Expand Down
8 changes: 5 additions & 3 deletions src/app/service/service_worker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { ScriptLoadInfo, ScriptMatchInfo } from "./types";
import {
compileInjectScript,
compilePreInjectScript,
compileScriptCode,
getScriptFlag,
isEarlyStartScript,
isInjectIntoContent,
Expand Down Expand Up @@ -178,13 +179,14 @@ export function parseScriptLoadInfo(script: ScriptRunResource): ScriptLoadInfo {
};
}

export function compileInjectionCode(scriptRes: ScriptLoadInfo | ScriptMatchInfo, scriptCode: string) {
export function compileInjectionCode(scriptRes: ScriptRunResource, scriptCode: string) {
const preDocumentStartScript = isEarlyStartScript(scriptRes.metadata);
let scriptInjectCode;
scriptCode = compileScriptCode(scriptRes, scriptCode);
if (preDocumentStartScript) {
scriptInjectCode = compilePreInjectScript(parseScriptLoadInfo(scriptRes), scriptCode, true);
scriptInjectCode = compilePreInjectScript(parseScriptLoadInfo(scriptRes), scriptCode);
} else {
scriptInjectCode = compileInjectScript(scriptRes, scriptCode, true);
scriptInjectCode = compileInjectScript(scriptRes, scriptCode);
}
return scriptInjectCode;
}
Expand Down
Loading
Loading