inject的GM API调用
Some checks failed
build / Build (push) Failing after 7s
test / Run tests (push) Failing after 8s

This commit is contained in:
2025-04-06 00:32:20 +08:00
parent 651384f12c
commit a8054451ac
13 changed files with 50 additions and 32 deletions

View File

@ -1,4 +1,4 @@
import { Group, MessageConnect } from "@Packages/message/server";
import { GetSender, Group, MessageConnect } from "@Packages/message/server";
export default class GMApi {
constructor(private group: Group) {}
@ -24,8 +24,9 @@ export default class GMApi {
return response;
}
xmlHttpRequest(details: GMSend.XHRDetails, con: MessageConnect | null) {
xmlHttpRequest(details: GMSend.XHRDetails, sender: GetSender) {
const xhr = new XMLHttpRequest();
const con = sender.getConnect();
xhr.open(details.method || "GET", details.url);
// 添加header
if (details.headers) {
@ -34,7 +35,7 @@ export default class GMApi {
}
}
xhr.onload = () => {
this.dealXhrResponse(con!, details, "onload", xhr);
this.dealXhrResponse(con, details, "onload", xhr);
};
xhr.onloadstart = () => {
this.dealXhrResponse(con!, details, "onloadstart", xhr);

View File

@ -1,7 +1,7 @@
import LoggerCore from "@App/app/logger/core";
import Logger from "@App/app/logger/logger";
import { Script, ScriptDAO } from "@App/app/repo/scripts";
import { GetSender, Group, MessageConnect, MessageSend, MessageSender } from "@Packages/message/server";
import { GetSender, Group, MessageSend, MessageSender } from "@Packages/message/server";
import { ValueService } from "@App/app/service/service_worker/value";
import PermissionVerify from "./permission_verify";
import { connect } from "@Packages/message/client";
@ -23,7 +23,7 @@ export type Request = MessageRequest & {
sender: MessageSender;
};
export type Api = (request: Request, con: MessageConnect | null) => Promise<any>;
export type Api = (request: Request, con: GetSender) => Promise<any>;
export default class GMApi {
logger: Logger;
@ -34,7 +34,7 @@ export default class GMApi {
constructor(
private group: Group,
private sender: MessageSend,
private send: MessageSend,
private value: ValueService
) {
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
@ -53,7 +53,7 @@ export default class GMApi {
this.logger.error("verify error", { api: data.api }, Logger.E(e));
return Promise.reject(e);
}
return api.api.call(this, req, con.getConnect());
return api.api.call(this, req, con);
}
// 解析请求
@ -80,11 +80,12 @@ export default class GMApi {
}
// 根据header生成dnr规则
async buildDNRRule(reqeustId: number, params: GMSend.XHRDetails) {
async buildDNRRule(reqeustId: number, params: GMSend.XHRDetails): Promise<{ [key: string]: string }> {
// 检查是否有unsafe header,有则生成dnr规则
const headers = params.headers;
console.log(headers, !headers);
if (!headers) {
return;
return Promise.resolve({});
}
const requestHeaders = [
{
@ -100,6 +101,7 @@ export default class GMApi {
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
value: headers[key],
});
delete headers[key];
}
});
const ruleId = reqeustId;
@ -119,20 +121,23 @@ export default class GMApi {
});
rule.condition = {
resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST],
urlFilter: "^" + params.url + "$",
urlFilter: params.url,
requestMethods: [(params.method || "GET").toLowerCase() as chrome.declarativeNetRequest.RequestMethod],
excludedTabIds: excludedTabIds,
};
console.log(rule);
await chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [ruleId],
addRules: [rule],
});
return ruleId;
return Promise.resolve(headers);
}
gmXhrHeadersReceived: EventEmitter = new EventEmitter();
@PermissionVerify.API()
async GM_xmlhttpRequest(request: Request, con: MessageConnect) {
async GM_xmlhttpRequest(request: Request, con: GetSender) {
console.log("GM XHR", request);
if (request.params.length === 0) {
return Promise.reject(new Error("param is failed"));
}
@ -145,7 +150,8 @@ export default class GMApi {
params.headers = {};
}
params.headers["X-Scriptcat-GM-XHR-Request-Id"] = requestId.toString();
await this.buildDNRRule(requestId, request.params[0]);
params.headers = await this.buildDNRRule(requestId, request.params[0]);
console.log(" params.headers", params.headers);
let responseHeader = "";
// 等待response
this.gmXhrHeadersReceived.addListener(
@ -157,12 +163,12 @@ export default class GMApi {
}
);
// 再发送到offscreen, 处理请求
const offscreenCon = await connect(this.sender, "gmApi/xmlHttpRequest", request.params[0]);
const offscreenCon = await connect(this.send, "offscreen/gmApi/xmlHttpRequest", request.params[0]);
offscreenCon.onMessage((msg: { action: string; data: any }) => {
// 发送到content
// 替换msg.data.responseHeaders
msg.data.responseHeaders = responseHeader;
con.sendMessage(msg);
con.getConnect().sendMessage(msg);
});
}
@ -171,6 +177,7 @@ export default class GMApi {
chrome.webRequest.onBeforeSendHeaders.addListener(
(details) => {
if (details.tabId === -1) {
console.log(details);
// 判断是否存在X-Scriptcat-GM-XHR-Request-Id
// 讲请求id与chrome.webRequest的请求id关联
if (details.requestHeaders) {

View File

@ -8,7 +8,7 @@ import { ScriptService } from "./script";
import { runScript, stopScript } from "../offscreen/client";
import { getRunAt } from "./utils";
import { randomString } from "@App/pkg/utils/utils";
import { compileInjectScript, compileScriptCode } from "@App/runtime/content/utils";
import { compileInjectScript } from "@App/runtime/content/utils";
import Cache from "@App/app/cache";
import { dealPatternMatches, UrlMatch } from "@App/pkg/utils/match";
@ -113,7 +113,6 @@ export class RuntimeService {
// 匹配当前页面的脚本
const matchScriptUuid = match.match(chromeSender.url!);
console.log("pageLoad", match.match(chromeSender.url!));
const scripts = await Promise.all(
matchScriptUuid.map(
(uuid) =>
@ -249,7 +248,6 @@ export class RuntimeService {
}
const scriptRes = await this.script.buildScriptRunResource(script);
scriptRes.code = compileScriptCode(scriptRes);
scriptRes.code = compileInjectScript(scriptRes);
matches.push(...(script.metadata["include"] || []));

View File

@ -4,6 +4,7 @@ import { ExtensionMessageSend } from "@Packages/message/extension_message";
import { CustomEventMessage } from "@Packages/message/custom_event_message";
import { RuntimeClient } from "./app/service/service_worker/client";
import ContentRuntime from "./runtime/content/content";
import { Server } from "@Packages/message/server";
// 建立与service_worker页面的连接
const send = new ExtensionMessageSend();
@ -17,9 +18,9 @@ const loggerCore = new LoggerCore({
const client = new RuntimeClient(send);
client.pageLoad().then((data) => {
loggerCore.logger().debug("content start");
console.log("content", data);
const msg = new CustomEventMessage(data.flag, true);
const server = new Server("content", msg);
// 初始化运行环境
const runtime = new ContentRuntime(send, msg);
const runtime = new ContentRuntime(server, send, msg);
runtime.start(data.scripts);
});

View File

@ -17,7 +17,6 @@ const server = new Server("inject", msg);
server.on("pageLoad", (data: { scripts: ScriptRunResouce[] }) => {
logger.logger().debug("inject start");
console.log("inject", data);
const runtime = new InjectRuntime(msg, data.scripts);
runtime.start();
});

View File

@ -149,6 +149,9 @@ export default class Match<T> {
}
protected static getId(val: any): string {
if (typeof val === "string") {
return val;
}
return (<{ uuid: string }>(<unknown>val)).uuid;
}

View File

@ -1,19 +1,23 @@
import { ScriptRunResouce } from "@App/app/repo/scripts";
import { Client } from "@Packages/message/client";
import { Message, MessageSend } from "@Packages/message/server";
import { forwardMessage, Message, MessageSend, Server } from "@Packages/message/server";
// content页的处理
export default class ContentRuntime {
constructor(
private server: Server,
private send: MessageSend,
private msg: Message
) {}
start(scripts: ScriptRunResouce[]) {
console.log("onMessage");
this.msg.onMessage((msg, sendResponse) => {
console.log("content onMessage", msg);
});
this.msg.onConnect((msg, connect) => {
console.log(msg, connect);
});
forwardMessage("serviceWorker", "runtime/gmApi", this.server, this.send);
// 由content到background
// 转发gmApi消息
// this.contentMessage.setHandler("gmApi", (action, data) => {

View File

@ -2,7 +2,8 @@ import { ScriptRunResouce } from "@App/app/repo/scripts";
import ExecScript from "./exec_script";
import { compileScript, compileScriptCode } from "./utils";
import { ExtVersion } from "@App/app/const";
import initTestEnv from "@Tests/utils";
import { initTestEnv } from "@Tests/utils";
import { describe, expect, it } from "vitest";
initTestEnv();

View File

@ -35,6 +35,7 @@ export default class ExecScript {
constructor(
scriptRes: ScriptRunResouce,
envPrefix: "content" | "offscreen",
message: Message,
code: string | ScriptFunc,
thisContext?: { [key: string]: any }
@ -61,7 +62,7 @@ export default class ExecScript {
this.proxyContent = global;
} else {
// 构建脚本GM上下文
this.sandboxContent = createContext(scriptRes, this.GM_info, message);
this.sandboxContent = createContext(scriptRes, this.GM_info, envPrefix, message);
this.proxyContent = proxyContext(global, this.sandboxContent, thisContext);
}
}
@ -76,7 +77,6 @@ export default class ExecScript {
return this.scriptFunc.apply(this.proxyContent, [this.proxyContent, this.GM_info]);
}
// TODO: 实现脚本的停止,资源释放
stop() {
this.logger.debug("script stop");
return true;

View File

@ -63,7 +63,7 @@ export class BgExecScriptWarp extends ExecScript {
};
// @ts-ignore
thisContext.CATRetryError = CATRetryError;
super(scriptRes, message, thisContext);
super(scriptRes, "offscreen", message, scriptRes.code, thisContext);
this.setTimeout = setTimeout;
this.setInterval = setInterval;
}

View File

@ -58,12 +58,15 @@ export default class GMApi {
valueChangeListener = new Map<number, { name: string; listener: GMTypes.ValueChangeListener }>();
constructor(private message: Message) {}
constructor(
private prefix: string,
private message: Message
) {}
// 单次回调使用
public sendMessage(api: string, params: any[]) {
return this.message.sendMessage({
action: "runtime/gmApi",
action: this.prefix + "/runtime/gmApi",
data: {
uuid: this.scriptRes.uuid,
api,
@ -75,7 +78,7 @@ export default class GMApi {
// 长连接使用,connect只用于接受消息,不发送消息
public connect(api: string, params: any[]) {
return this.message.connect({
action: "runtime/gmApi",
action: this.prefix + "/runtime/gmApi",
data: {
uuid: this.scriptRes.uuid,
api,

View File

@ -32,7 +32,7 @@ export class InjectRuntime {
execScript(script: ScriptRunResouce, scriptFunc: ScriptFunc) {
// @ts-ignore
delete window[script.flag];
const exec = new ExecScript(script, this.msg, scriptFunc);
const exec = new ExecScript(script, "content", this.msg, scriptFunc);
this.execList.push(exec);
// 注入css
if (script.metadata["require-css"]) {

View File

@ -53,9 +53,10 @@ function setDepend(context: { [key: string]: any }, apiVal: ApiValue) {
}
// 构建沙盒上下文
export function createContext(scriptRes: ScriptRunResouce, GMInfo: any, message: Message): GMApi {
export function createContext(scriptRes: ScriptRunResouce, GMInfo: any, envPrefix: string, message: Message): GMApi {
// 按照GMApi构建
const context: { [key: string]: any } = {
prefix: envPrefix,
message: message,
scriptRes,
valueChangeListener: new Map<number, { name: string; listener: GMTypes.ValueChangeListener }>(),