From 9f70b7eb7a3446b2cf94ae475efc8c391abd9825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Tue, 8 Apr 2025 23:54:06 +0800 Subject: [PATCH] =?UTF-8?q?value=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/gm_value.js | 8 ++- src/app/service/service_worker/gm_api.ts | 9 ++-- src/app/service/service_worker/index.ts | 4 +- src/app/service/service_worker/popup.ts | 20 ++++--- src/app/service/service_worker/runtime.ts | 11 +++- src/app/service/service_worker/value.ts | 63 ++++++++++++++++++----- src/runtime/content/gm_api.ts | 23 +++++++++ 7 files changed, 110 insertions(+), 28 deletions(-) diff --git a/example/gm_value.js b/example/gm_value.js index 0b1c1ba..d0ffcc6 100644 --- a/example/gm_value.js +++ b/example/gm_value.js @@ -15,8 +15,8 @@ // ==/UserScript== GM_addValueChangeListener("test_set", function (name, oldval, newval, remote, tabid) { - GM_cookie("store", tabid,(storeId) => { - console.log("store",storeId); + GM_cookie("store", tabid, (storeId) => { + console.log("store", storeId); }); }); @@ -30,3 +30,7 @@ setTimeout(() => { }, 3000); GM_setValue("test_set", new Date().getTime()); + +console.log(GM_getValue("test_set2")); + +GM_setValue("test_set2", new Date().getTime()); diff --git a/src/app/service/service_worker/gm_api.ts b/src/app/service/service_worker/gm_api.ts index 542b4c1..7fc7881 100644 --- a/src/app/service/service_worker/gm_api.ts +++ b/src/app/service/service_worker/gm_api.ts @@ -9,6 +9,7 @@ import Cache, { incr } from "@App/app/cache"; import { unsafeHeaders } from "@App/runtime/utils"; import EventEmitter from "eventemitter3"; import { MessageQueue } from "@Packages/message/message_queue"; +import { RuntimeService } from "./runtime"; // GMApi,处理脚本的GM API调用请求 @@ -36,7 +37,8 @@ export default class GMApi { private group: Group, private send: MessageSend, private mq: MessageQueue, - private value: ValueService + private value: ValueService, + private runtime: RuntimeService ) { this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" }); } @@ -69,12 +71,13 @@ export default class GMApi { } @PermissionVerify.API() - GM_setValue(request: Request): Promise { + async GM_setValue(request: Request) { + console.log("setValue", request); if (!request.params || request.params.length !== 2) { return Promise.reject(new Error("param is failed")); } const [key, value] = request.params; - return this.value.setValue(request.script.uuid, key, value); + await this.value.setValue(request.script.uuid, key, value); } // 根据header生成dnr规则 diff --git a/src/app/service/service_worker/index.ts b/src/app/service/service_worker/index.ts index 98df8a4..587035a 100644 --- a/src/app/service/service_worker/index.ts +++ b/src/app/service/service_worker/index.ts @@ -26,13 +26,13 @@ export default class ServiceWorkerManager { const resource = new ResourceService(this.api.group("resource"), this.mq); resource.init(); - const value = new ValueService(this.api.group("value")); - value.init(); + const value = new ValueService(this.api.group("value"), this.sender); const script = new ScriptService(this.api.group("script"), this.mq, value, resource); script.init(); const runtime = new RuntimeService(this.api.group("runtime"), this.sender, this.mq, value, script); runtime.init(); const popup = new PopupService(this.api.group("popup"), this.mq, runtime); popup.init(); + value.init(runtime, popup); } } diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index d161270..08b53b6 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -20,6 +20,7 @@ import { subscribeScriptMenuRegister, subscribeScriptRunStatus, } from "../queue"; +import { storageKey } from "@App/runtime/utils"; export type ScriptMenuItem = { id: number; @@ -33,6 +34,7 @@ export type ScriptMenuItem = { export type ScriptMenu = { uuid: string; // 脚本uuid name: string; // 脚本名称 + storageName: string; // 脚本存储名称 enable: boolean; // 脚本是否启用 updatetime: number; // 脚本更新时间 hasUserConfig: boolean; // 是否有用户配置 @@ -124,12 +126,6 @@ export class PopupService { frameId: message.frameId, documentId: message.documentId, }); - } else { - menu.name = message.name; - menu.accessKey = message.accessKey; - menu.tabId = message.tabId; - menu.frameId = message.frameId; - menu.documentId = message.documentId; } } this.updateScriptMenu(); @@ -167,6 +163,7 @@ export class PopupService { return { uuid: script.uuid, name: script.name, + storageName: storageKey(script), enable: script.status === SCRIPT_STATUS_ENABLE, updatetime: script.updatetime || 0, hasUserConfig: !!script.config, @@ -195,6 +192,13 @@ export class PopupService { } return this.scriptToMenu(script); }); + runScript.forEach((script) => { + const index = scriptMenu.findIndex((item) => item.uuid === script.uuid); + // 把运行了但是不在匹配中的脚本加入菜单 + if (index === -1) { + scriptMenu.push(script); + } + }); // 后台脚本只显示开启或者运行中的脚本 return { scriptList: scriptMenu, backScriptList: await this.getScriptMenu(-1) }; } @@ -221,7 +225,9 @@ export class PopupService { }) { // 设置数据 return this.txUpdateScriptMenu(tabId, async (data) => { - data = []; + if (!frameId) { + data = []; + } // 设置脚本运行次数 scripts.forEach((script) => { const scriptMenu = data.find((item) => item.uuid === script.uuid); diff --git a/src/app/service/service_worker/runtime.ts b/src/app/service/service_worker/runtime.ts index f50475d..ce7947b 100644 --- a/src/app/service/service_worker/runtime.ts +++ b/src/app/service/service_worker/runtime.ts @@ -46,7 +46,7 @@ export class RuntimeService { async init() { // 启动gm api - const gmApi = new GMApi(this.group, this.sender, this.mq, this.value); + const gmApi = new GMApi(this.group, this.sender, this.mq, this.value, this); gmApi.start(); this.group.on("stopScript", this.stopScript.bind(this)); @@ -147,7 +147,6 @@ export class RuntimeService { match.add(uuid); }); // 转化为数组 - console.log("matchScriptUuid", matchScriptUuid); return Array.from(match); } return matchScriptUuid; @@ -187,6 +186,14 @@ export class RuntimeService { const enableScript = scripts.filter((item) => item); + // 加载value + await Promise.all( + enableScript.map(async (script) => { + const value = await this.value.getScriptValue(script!); + script!.value = value; + }) + ); + this.mq.emit("pageLoad", { tabId: chromeSender.tab?.id, frameId: chromeSender.frameId, diff --git a/src/app/service/service_worker/value.ts b/src/app/service/service_worker/value.ts index a2a9da3..6a9fccb 100644 --- a/src/app/service/service_worker/value.ts +++ b/src/app/service/service_worker/value.ts @@ -1,16 +1,25 @@ import LoggerCore from "@App/app/logger/core"; import Logger from "@App/app/logger/logger"; -import { Script, ScriptDAO } from "@App/app/repo/scripts"; +import { Script, SCRIPT_TYPE_NORMAL, ScriptDAO } from "@App/app/repo/scripts"; import { ValueDAO } from "@App/app/repo/value"; import { storageKey } from "@App/runtime/utils"; -import { Group } from "@Packages/message/server"; +import { Group, MessageSend } from "@Packages/message/server"; +import { RuntimeService } from "./runtime"; +import { PopupService } from "./popup"; +import { ValueUpdateData } from "@App/runtime/content/exec_script"; +import { sendMessage } from "@Packages/message/client"; export class ValueService { logger: Logger; scriptDAO: ScriptDAO = new ScriptDAO(); valueDAO: ValueDAO = new ValueDAO(); + private popup: PopupService | undefined; + private runtime: RuntimeService | undefined; - constructor(private group: Group) { + constructor( + private group: Group, + private send: MessageSend + ) { this.logger = LoggerCore.logger().with({ service: "value" }); } @@ -19,33 +28,63 @@ export class ValueService { if (!ret) { return {}; } - return Promise.resolve(ret?.data); + return ret.data; } - async setValue(uuid: string, key: string, value: any): Promise { + async setValue(uuid: string, key: string, value: any, sender?: any): Promise { // 查询出脚本 const script = await this.scriptDAO.get(uuid); if (!script) { return Promise.reject(new Error("script not found")); } // 查询老的值 - const oldValue = await this.valueDAO.get(storageKey(script)); - if (!oldValue) { - this.valueDAO.save(storageKey(script), { + const storageName = storageKey(script); + const valueModel = await this.valueDAO.get(storageName); + let oldValue; + if (!valueModel) { + this.valueDAO.save(storageName, { uuid: script.uuid, - storageName: storageKey(script), + storageName: storageName, data: { [key]: value }, createtime: Date.now(), updatetime: Date.now(), }); } else { - oldValue.data[key] = value; - this.valueDAO.save(storageKey(script), oldValue); + oldValue = valueModel.data[key]; + valueModel.data[key] = value; + this.valueDAO.save(storageName, valueModel); } + const sendData: ValueUpdateData = { + oldValue, + sender, + value, + key, + uuid, + storageKey: storageName, + }; + // 判断是后台脚本还是前台脚本 + console.log("value update", script, sendData); + if (script.type === SCRIPT_TYPE_NORMAL) { + chrome.tabs.query({}, (tabs) => { + // 推送到所有加载了本脚本的tab中 + tabs.forEach(async (tab) => { + const scriptMenu = await this.popup!.getScriptMenu(tab.id!); + if (scriptMenu.find((item) => item.storageName === storageName)) { + this.runtime!.sendMessageToTab(tab.id!, "valueUpdate", sendData); + } + }); + }); + } else { + // 推送到offscreen中 + sendMessage(this.send, "offscreen/runtime/valueUpdate", sendData); + } + return Promise.resolve(true); } - init() { + init(runtime: RuntimeService, popup: PopupService) { + this.popup = popup; + this.runtime = runtime; this.group.on("getScriptValue", this.getScriptValue.bind(this)); } } diff --git a/src/runtime/content/gm_api.ts b/src/runtime/content/gm_api.ts index f5c3738..541dedd 100644 --- a/src/runtime/content/gm_api.ts +++ b/src/runtime/content/gm_api.ts @@ -178,6 +178,29 @@ export default class GMApi { this.GM_setValue(name, undefined); } + valueChangeId: number | undefined; + + @GMContext.API() + public GM_addValueChangeListener(name: string, listener: GMTypes.ValueChangeListener): number { + if (!this.valueChangeId) { + this.valueChangeId = 1; + } else { + this.valueChangeId += 1; + } + this.valueChangeListener.set(this.valueChangeId, { name, listener }); + return this.valueChangeId; + } + + @GMContext.API() + public GM_removeValueChangeListener(listenerId: number): void { + this.valueChangeListener.delete(listenerId); + } + + @GMContext.API() + public GM_listValues(): string[] { + return Object.keys(this.scriptRes.value); + } + @GMContext.API() GM_log(message: string, level?: GMTypes.LoggerLevel, labels?: GMTypes.LoggerLabel) { if (typeof message !== "string") {