gm 菜单响应
Some checks failed
test / Run tests (push) Failing after 8s
build / Build (push) Failing after 10s

This commit is contained in:
王一之 2025-04-08 13:59:08 +08:00
parent f26aecd10f
commit 42975d47cf
9 changed files with 208 additions and 145 deletions

View File

@ -134,4 +134,27 @@ export default class Cache {
public list(): Promise<string[]> {
return this.storage.list();
}
private txPromise: Map<string, Promise<any>> = new Map();
// 事务处理,如果有事务正在进行,则等待
public async tx(key: string, set: (result: any) => Promise<any>): Promise<void> {
let promise = this.txPromise.get(key);
if (promise) {
await promise;
}
promise = this.get(key)
.then((result) => set(result))
.then((value) => {
console.log("tx", key, value);
if (value) {
return this.set(key, value);
}
return Promise.resolve();
});
this.txPromise.set(key, promise);
await promise;
this.txPromise.delete(key);
}
}

View File

@ -103,7 +103,8 @@ export class PopupService {
async registerMenuCommand(message: ScriptMenuRegisterCallbackValue) {
// 给脚本添加菜单
const data = await this.getScriptMenu(message.tabId);
return this.txUpdateScriptMenu(message.tabId, async (data) => {
console.log("register menu", message, data);
const script = data.find((item) => item.uuid === message.uuid);
if (script) {
const menu = script.menus.find((item) => item.id === message.id);
@ -120,28 +121,31 @@ export class PopupService {
menu.name = message.name;
menu.accessKey = message.accessKey;
menu.tabId = message.tabId;
menu.frameId = message.frameId;
menu.documentId = message.documentId;
}
}
console.log("set menu", data);
await Cache.getInstance().set("tabScript:" + message.tabId, data);
console.log("update menu");
this.updateScriptMenu();
return data;
});
}
async unregisterMenuCommand({ id, uuid, tabId }: { id: number; uuid: string; tabId: number }) {
const data = await this.getScriptMenu(tabId);
return this.txUpdateScriptMenu(tabId, async (data) => {
// 删除脚本菜单
const script = data.find((item) => item.uuid === uuid);
if (script) {
script.menus = script.menus.filter((item) => item.id !== id);
}
await Cache.getInstance().set("tabScript:" + tabId, data);
console.log("unregister menu", data);
this.updateScriptMenu();
return data;
});
}
updateScriptMenu() {
// 获取当前页面并更新菜单
chrome.tabs.query({ active: true }, (tabs) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
console.log("query", tabs);
if (!tabs.length) {
return;
@ -192,6 +196,13 @@ export class PopupService {
return ((await Cache.getInstance().get("tabScript:" + tabId)) || []) as ScriptMenu[];
}
// 事务更新脚本菜单
txUpdateScriptMenu(tabId: number, callback: (menu: ScriptMenu[]) => Promise<any>) {
return Cache.getInstance().tx("tabScript:" + tabId, async (menu) => {
return callback(menu || []);
});
}
async addScriptRunNumber({
tabId,
frameId,
@ -201,12 +212,9 @@ export class PopupService {
frameId: number;
scripts: ScriptMatchInfo[];
}) {
if (frameId === undefined) {
// 清理数据
await Cache.getInstance().del("tabScript:" + tabId);
}
// 设置数据
const data = await this.getScriptMenu(tabId);
return this.txUpdateScriptMenu(tabId, async (data) => {
data = [];
// 设置脚本运行次数
scripts.forEach((script) => {
const scriptMenu = data.find((item) => item.uuid === script.uuid);
@ -224,7 +232,8 @@ export class PopupService {
data.push(item);
}
});
Cache.getInstance().set("tabScript:" + tabId, data);
return data;
});
}
dealBackgroundScriptInstall() {
@ -233,7 +242,7 @@ export class PopupService {
if (script.type === SCRIPT_TYPE_NORMAL) {
return;
}
const menu = await this.getScriptMenu(-1);
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === script.uuid);
if (script.status === SCRIPT_STATUS_ENABLE) {
// 加入菜单
@ -241,13 +250,9 @@ export class PopupService {
const item = this.scriptToMenu(script);
menu.push(item);
}
} else {
// 移出菜单
if (scriptMenu) {
menu.splice(menu.indexOf(scriptMenu), 1);
}
}
Cache.getInstance().set("tabScript:" + -1, menu);
return menu;
});
});
subscribeScriptEnable(this.mq, async ({ uuid }) => {
const script = await this.scriptDAO.get(uuid);
@ -257,7 +262,7 @@ export class PopupService {
if (script.type === SCRIPT_TYPE_NORMAL) {
return;
}
const menu = await this.getScriptMenu(-1);
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === uuid);
if (script.status === SCRIPT_STATUS_ENABLE) {
// 加入菜单
@ -271,23 +276,28 @@ export class PopupService {
menu.splice(menu.indexOf(scriptMenu), 1);
}
}
Cache.getInstance().set("tabScript:" + -1, menu);
return menu;
});
});
subscribeScriptDelete(this.mq, async ({ uuid }) => {
const menu = await this.getScriptMenu(-1);
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === uuid);
if (scriptMenu) {
menu.splice(menu.indexOf(scriptMenu), 1);
Cache.getInstance().set("tabScript:" + -1, menu);
return menu;
}
return null;
});
});
subscribeScriptRunStatus(this.mq, async ({ uuid, runStatus }) => {
const menu = await this.getScriptMenu(-1);
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === uuid);
if (scriptMenu) {
scriptMenu.runStatus = runStatus;
Cache.getInstance().set("tabScript:" + -1, menu);
return menu;
}
return null;
});
});
}
@ -305,7 +315,6 @@ export class PopupService {
documentId: string;
}) {
// 菜单点击事件
console.log("click menu", uuid, id, tabId, frameId, documentId);
this.runtime.sendMessageToTab(
tabId,
"menuClick",
@ -333,7 +342,9 @@ export class PopupService {
// 监听tab开关
chrome.tabs.onRemoved.addListener((tabId) => {
// 清理数据
Cache.getInstance().del("tabScript:" + tabId);
this.txUpdateScriptMenu(tabId, async () => {
return [];
});
});
// 监听页面切换加载菜单
chrome.tabs.onActivated.addListener((activeInfo) => {

View File

@ -212,6 +212,7 @@ export class RuntimeService {
return runScript(this.sender, res);
}
// 注册inject.js
registerInjectScript() {
chrome.userScripts.getScripts({ ids: ["scriptcat-inject"] }).then((res) => {
if (res.length == 0) {

View File

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

View File

@ -1,5 +1,5 @@
import { ScriptRunResouce } from "@App/app/repo/scripts";
import { Client } from "@Packages/message/client";
import { Client, sendMessage } from "@Packages/message/client";
import { forwardMessage, Message, MessageSend, Server } from "@Packages/message/server";
// content页的处理
@ -7,20 +7,25 @@ export default class ContentRuntime {
constructor(
private extServer: Server,
private server: Server,
private send: MessageSend,
private extSend: MessageSend,
private msg: Message
) {}
start(scripts: ScriptRunResouce[]) {
this.extServer.on("runtime/menuClick", (action, data) => {
// gm菜单点击
console.log("runtime/menuClick", action, data);
this.extServer.on("runtime/menuClick", (data) => {
// 转发给inject
return sendMessage(this.msg, "inject/runtime/menuClick", data);
});
this.extServer.on("runtime/valueUpdate", (action, data) => {
// gm value变化
console.log(action, data);
this.extServer.on("runtime/valueUpdate", (data) => {
// 转发给inject
return sendMessage(this.msg, "inject/runtime/valueUpdate", data);
});
forwardMessage("serviceWorker", "runtime/gmApi", this.server, this.send, (data: { api: string; params: any }) => {
forwardMessage(
"serviceWorker",
"runtime/gmApi",
this.server,
this.extSend,
(data: { api: string; params: any }) => {
// 拦截关注的api
switch (data.api) {
case "CAT_createBlobUrl": {
@ -49,7 +54,8 @@ export default class ContentRuntime {
}
}
return Promise.resolve(false);
});
}
);
const client = new Client(this.msg, "inject");
client.do("pageLoad", { scripts });
}

View File

@ -72,6 +72,10 @@ export default class ExecScript {
this.sandboxContent?.valueUpdate(data);
}
menuClick(id: number) {
this.sandboxContent?.menuClick(id);
}
exec() {
this.logger.debug("script start");
return this.scriptFunc.apply(this.proxyContent, [this.proxyContent, this.GM_info]);

View File

@ -7,6 +7,7 @@ import { Message, MessageConnect } from "@Packages/message/server";
import { CustomEventMessage } from "@Packages/message/custom_event_message";
import LoggerCore from "@App/app/logger/core";
import { connect, sendMessage } from "@Packages/message/client";
import EventEmitter from "eventemitter3";
interface ApiParam {
depend?: string[];
@ -106,6 +107,10 @@ export default class GMApi {
}
}
menuClick(id: number) {
this.EE.emit("menuClick" + id);
}
// 获取脚本信息和管理器信息
static GM_info(script: ScriptRunResouce) {
const metadataStr = getMetadataStr(script.code);
@ -202,18 +207,21 @@ export default class GMApi {
menuMap: Map<number, string> | undefined;
EE: EventEmitter = new EventEmitter();
@GMContext.API()
GM_registerMenuCommand(name: string, listener: () => void, accessKey?: string): number {
if (!this.menuMap) {
this.menuMap = new Map();
}
let flag = 0;
this.menuMap.forEach((val, key) => {
this.menuMap.forEach((val, menuId) => {
if (val === name) {
flag = key;
flag = menuId;
}
});
if (flag) {
this.EE.addListener("menuClick" + flag, listener);
return flag;
}
if (!this.menuId) {
@ -222,18 +230,9 @@ export default class GMApi {
this.menuId += 1;
}
const id = this.menuId;
this.sendMessage("GM_registerMenuCommand", [id, name, accessKey]);
// .then((con) => {
// con.onMessage((data: { action: string; data: any }) => {
// if (data.action === "onClick") {
// listener();
// }
// });
// con.onDisconnect(() => {
// this.menuMap?.delete(id);
// });
// });
this.menuMap.set(id, name);
this.EE.addListener("menuClick" + id, listener);
this.sendMessage("GM_registerMenuCommand", [id, name, accessKey]);
return id;
}
@ -243,6 +242,7 @@ export default class GMApi {
this.menuMap = new Map();
}
this.menuMap.delete(id);
this.EE.removeAllListeners("menuClick" + id);
this.sendMessage("GM_unregisterMenuCommand", [id]);
}

View File

@ -1,5 +1,5 @@
import { ScriptRunResouce } from "@App/app/repo/scripts";
import { Message } from "@Packages/message/server";
import { Message, Server } from "@Packages/message/server";
import ExecScript from "./exec_script";
import { addStyle, ScriptFunc } from "./utils";
@ -7,6 +7,7 @@ export class InjectRuntime {
execList: ExecScript[] = [];
constructor(
private server: Server,
private msg: Message,
private scripts: ScriptRunResouce[]
) {}
@ -27,6 +28,19 @@ export class InjectRuntime {
});
}
});
this.server.on("runtime/menuClick", (data: { id: number; uuid: string }) => {
// 转发给脚本
const exec = this.execList.find((val) => val.scriptRes.uuid === data.uuid);
if (exec) {
exec.menuClick(data.id);
}
});
this.server.on("runtime/valueUpdate", (data: { uuid: string; key: string; value: any }) => {
const exec = this.execList.find((val) => val.scriptRes.uuid === data.uuid);
if (exec) {
// exec.valueUpdate(data.key,);
}
});
}
execScript(script: ScriptRunResouce, scriptFunc: ScriptFunc) {

View File

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from "uuid";
import GMApi, { ApiValue, GMContext } from "./gm_api";
import { has } from "@App/pkg/utils/lodash";
import { Message } from "@Packages/message/server";
import EventEmitter from "eventemitter3";
// 构建脚本运行代码
export function compileScriptCode(scriptRes: ScriptRunResouce): string {
@ -64,6 +65,8 @@ export function createContext(scriptRes: ScriptRunResouce, GMInfo: any, envPrefi
connect: GMApi.prototype.connect,
runFlag: uuidv4(),
valueUpdate: GMApi.prototype.valueUpdate,
menuClick: GMApi.prototype.menuClick,
EE: new EventEmitter(),
GM: { Info: GMInfo },
GM_info: GMInfo,
};