From 0d86dae7105b93e709223c7c5e3c0b59b3afa3ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Wed, 9 Apr 2025 18:05:59 +0800 Subject: [PATCH] =?UTF-8?q?value=E9=97=AE=E9=A2=98=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/gm_cookie.js | 2 +- example/gm_value.js | 5 +-- example/sotrage_name/gm_value_1.js | 18 +++++++++ example/sotrage_name/gm_value_2.js | 28 ++++++++++++++ packages/message/server.ts | 3 +- src/app/cache.ts | 15 +++++--- src/app/logger/core.ts | 2 +- src/app/service/offscreen/gm_api.ts | 2 + src/app/service/sandbox/runtime.ts | 1 - src/app/service/service_worker/gm_api.ts | 9 +++-- src/app/service/service_worker/popup.ts | 7 +--- src/app/service/service_worker/runtime.ts | 2 + src/app/service/service_worker/value.ts | 47 +++++++++++++---------- src/pages/store/subscribe.ts | 1 - src/runtime/content/exec_script.ts | 12 +++--- src/runtime/content/gm_api.ts | 20 +++++----- src/runtime/content/inject.ts | 14 ++++--- src/runtime/utils.ts | 2 +- 18 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 example/sotrage_name/gm_value_1.js create mode 100644 example/sotrage_name/gm_value_2.js diff --git a/example/gm_cookie.js b/example/gm_cookie.js index 2df9151..926438a 100644 --- a/example/gm_cookie.js +++ b/example/gm_cookie.js @@ -9,7 +9,7 @@ // @connect example.com // ==/UserScript== -// GM_cookie("store") 方法请看gm_value.js的例子, 可用于隐身窗口的操作 +// GM_cookie("store") 方法请看storage_name/gm_value.js的例子, 可用于隐身窗口的操作 GM_cookie("set", { url: "http://example.com/cookie", diff --git a/example/gm_value.js b/example/gm_value.js index d0ffcc6..4ad13cf 100644 --- a/example/gm_value.js +++ b/example/gm_value.js @@ -11,13 +11,10 @@ // @grant GM_addValueChangeListener // @grant GM_listValues // @grant GM_deleteValue -// @grant GM_cookie // ==/UserScript== GM_addValueChangeListener("test_set", function (name, oldval, newval, remote, tabid) { - GM_cookie("store", tabid, (storeId) => { - console.log("store", storeId); - }); + console.log("test_set change", name, oldval, newval, remote, tabid); }); setInterval(() => { diff --git a/example/sotrage_name/gm_value_1.js b/example/sotrage_name/gm_value_1.js new file mode 100644 index 0000000..a30fa02 --- /dev/null +++ b/example/sotrage_name/gm_value_1.js @@ -0,0 +1,18 @@ +// ==UserScript== +// @name gm value storage 设置方 +// @namespace https://bbs.tampermonkey.net.cn/ +// @version 0.1.0 +// @description 多个脚本之间共享数据 设置方 +// @author You +// @match https://bbs.tampermonkey.net.cn/ +// @run-at document-start +// @grant GM_setValue +// @grant GM_deleteValue +// @storageName example +// ==/UserScript== + +setTimeout(() => { + GM_deleteValue("test_set"); +}, 3000); + +GM_setValue("test_set", new Date().getTime()); diff --git a/example/sotrage_name/gm_value_2.js b/example/sotrage_name/gm_value_2.js new file mode 100644 index 0000000..d4cf8b5 --- /dev/null +++ b/example/sotrage_name/gm_value_2.js @@ -0,0 +1,28 @@ +// ==UserScript== +// @name gm value storage 读取与监听方 +// @namespace https://bbs.tampermonkey.net.cn/ +// @version 0.1.0 +// @description 多个脚本之间共享数据 读取与监听方 +// @author You +// @match https://bbs.tampermonkey.net.cn/ +// @run-at document-start +// @grant GM_getValue +// @grant GM_addValueChangeListener +// @grant GM_listValues +// @grant GM_cookie +// @storageName example +// ==/UserScript== + +GM_addValueChangeListener("test_set", function (name, oldval, newval, remote, tabid) { + console.log("test_set change", name, oldval, newval, remote, tabid); + // 可以通过tabid获取到触发变化的tab + // GM_cookie.store可以获取到对应的cookie storeId + GM_cookie("store", tabid, (storeId) => { + console.log("store", storeId); + }); +}); + +setInterval(() => { + console.log(GM_getValue("test_set")); + console.log(GM_listValues()); +}, 2000); diff --git a/packages/message/server.ts b/packages/message/server.ts index 0bf8f12..0fc1eab 100644 --- a/packages/message/server.ts +++ b/packages/message/server.ts @@ -1,4 +1,5 @@ import LoggerCore from "@App/app/logger/core"; +import { connect } from "./client"; export interface Message extends MessageSend { onConnect(callback: (data: any, con: MessageConnect) => void): void; @@ -124,7 +125,7 @@ export function forwardMessage(prefix: string, path: string, from: Server, to: M } const fromConnect = fromCon.getConnect(); if (fromConnect) { - to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => { + connect(to, prefix + "/" + path, params).then((toCon) => { fromConnect.onMessage((data) => { toCon.sendMessage(data); }); diff --git a/src/app/cache.ts b/src/app/cache.ts index b36c949..0222344 100644 --- a/src/app/cache.ts +++ b/src/app/cache.ts @@ -90,11 +90,11 @@ export class MapCache { } export async function incr(cache: Cache, key: string, increase: number): Promise { - const value = await cache.get(key); - let num = value || 0; - num += increase; - await cache.set(key, num); - return num; + return cache.tx(key, async (value) => { + let num = value || 0; + num += increase; + return num; + }); } export default class Cache { @@ -138,15 +138,17 @@ export default class Cache { private txPromise: Map> = new Map(); // 事务处理,如果有事务正在进行,则等待 - public async tx(key: string, set: (result: any) => Promise): Promise { + public async tx(key: string, set: (result: T) => Promise): Promise { let promise = this.txPromise.get(key); if (promise) { await promise; } + let newValue: T; promise = this.get(key) .then((result) => set(result)) .then((value) => { if (value) { + newValue = value; return this.set(key, value); } return Promise.resolve(); @@ -154,5 +156,6 @@ export default class Cache { this.txPromise.set(key, promise); await promise; this.txPromise.delete(key); + return newValue!; } } diff --git a/src/app/logger/core.ts b/src/app/logger/core.ts index 530bf56..4e83fb5 100644 --- a/src/app/logger/core.ts +++ b/src/app/logger/core.ts @@ -3,7 +3,7 @@ import Logger from "./logger"; export type LogLevel = "trace" | "debug" | "info" | "warn" | "error"; export interface LogLabel { - [key: string]: string | string[] | boolean | number | undefined; + [key: string]: string | string[] | boolean | number | object | undefined; component?: string; } diff --git a/src/app/service/offscreen/gm_api.ts b/src/app/service/offscreen/gm_api.ts index 88f2f80..f7a6c5f 100644 --- a/src/app/service/offscreen/gm_api.ts +++ b/src/app/service/offscreen/gm_api.ts @@ -3,6 +3,8 @@ import Logger from "@App/app/logger/logger"; import { GetSender, Group, MessageConnect } from "@Packages/message/server"; export default class GMApi { + logger: Logger = LoggerCore.logger().with({ service: "gmApi" }); + constructor(private group: Group) {} async dealXhrResponse( diff --git a/src/app/service/sandbox/runtime.ts b/src/app/service/sandbox/runtime.ts index b774187..9e7d9b9 100644 --- a/src/app/service/sandbox/runtime.ts +++ b/src/app/service/sandbox/runtime.ts @@ -282,7 +282,6 @@ export class Runtime { } async runScript(script: ScriptRunResouce) { - console.log("runScript", script); const exec = this.execScripts.get(script.uuid); // 如果正在运行,先释放 if (exec) { diff --git a/src/app/service/service_worker/gm_api.ts b/src/app/service/service_worker/gm_api.ts index 7fc7881..0c36ed5 100644 --- a/src/app/service/service_worker/gm_api.ts +++ b/src/app/service/service_worker/gm_api.ts @@ -71,13 +71,15 @@ export default class GMApi { } @PermissionVerify.API() - async GM_setValue(request: Request) { - console.log("setValue", request); + async GM_setValue(request: Request, sender: GetSender) { if (!request.params || request.params.length !== 2) { return Promise.reject(new Error("param is failed")); } const [key, value] = request.params; - await this.value.setValue(request.script.uuid, key, value); + await this.value.setValue(request.script.uuid, key, value, { + runFlag: request.runFlag, + tabId: sender.getSender().tab?.id, + }); } // 根据header生成dnr规则 @@ -183,7 +185,6 @@ export default class GMApi { @PermissionVerify.API() GM_registerMenuCommand(request: Request, sender: GetSender) { - console.log("registerMenuCommand", request.params, sender.getSender(), sender.getSender().tab!.id!); const [id, name, accessKey] = request.params; // 触发菜单注册, 在popup中处理 this.mq.emit("registerMenuCommand", { diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index 08b53b6..6d6618e 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -20,7 +20,7 @@ import { subscribeScriptMenuRegister, subscribeScriptRunStatus, } from "../queue"; -import { storageKey } from "@App/runtime/utils"; +import { getStorageName } from "@App/runtime/utils"; export type ScriptMenuItem = { id: number; @@ -87,7 +87,6 @@ export class PopupService { // 移除之前所有的菜单 chrome.contextMenus.removeAll(); const [menu, backgroundMenu] = await Promise.all([this.getScriptMenu(tabId), this.getScriptMenu(-1)]); - console.log(menu, backgroundMenu, tabId); if (!menu.length && !backgroundMenu.length) { return; } @@ -140,7 +139,6 @@ export class PopupService { if (script) { script.menus = script.menus.filter((item) => item.id !== id); } - console.log("unregister menu", data); this.updateScriptMenu(); return data; }); @@ -149,7 +147,6 @@ export class PopupService { updateScriptMenu() { // 获取当前页面并更新菜单 chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { - console.log("query", tabs); if (!tabs.length) { return; } @@ -163,7 +160,7 @@ export class PopupService { return { uuid: script.uuid, name: script.name, - storageName: storageKey(script), + storageName: getStorageName(script), enable: script.status === SCRIPT_STATUS_ENABLE, updatetime: script.updatetime || 0, hasUserConfig: !!script.config, diff --git a/src/app/service/service_worker/runtime.ts b/src/app/service/service_worker/runtime.ts index ce7947b..7af774b 100644 --- a/src/app/service/service_worker/runtime.ts +++ b/src/app/service/service_worker/runtime.ts @@ -194,6 +194,8 @@ export class RuntimeService { }) ); + console.log("pageLoad", enableScript); + 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 6a9fccb..0dd75ed 100644 --- a/src/app/service/service_worker/value.ts +++ b/src/app/service/service_worker/value.ts @@ -2,12 +2,13 @@ import LoggerCore from "@App/app/logger/core"; import Logger from "@App/app/logger/logger"; 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 { getStorageName } from "@App/runtime/utils"; import { Group, MessageSend } from "@Packages/message/server"; import { RuntimeService } from "./runtime"; import { PopupService } from "./popup"; -import { ValueUpdateData } from "@App/runtime/content/exec_script"; +import { ValueUpdateData, ValueUpdateSender } from "@App/runtime/content/exec_script"; import { sendMessage } from "@Packages/message/client"; +import Cache from "@App/app/cache"; export class ValueService { logger: Logger; @@ -24,46 +25,50 @@ export class ValueService { } async getScriptValue(script: Script) { - const ret = await this.valueDAO.get(storageKey(script)); + const ret = await this.valueDAO.get(getStorageName(script)); if (!ret) { return {}; } return ret.data; } - async setValue(uuid: string, key: string, value: any, sender?: any): Promise { + async setValue(uuid: string, key: string, value: any, sender: ValueUpdateSender): Promise { // 查询出脚本 const script = await this.scriptDAO.get(uuid); if (!script) { return Promise.reject(new Error("script not found")); } // 查询老的值 - const storageName = storageKey(script); - const valueModel = await this.valueDAO.get(storageName); + const storageName = getStorageName(script); let oldValue; - if (!valueModel) { - this.valueDAO.save(storageName, { - uuid: script.uuid, - storageName: storageName, - data: { [key]: value }, - createtime: Date.now(), - updatetime: Date.now(), - }); - } else { - oldValue = valueModel.data[key]; - valueModel.data[key] = value; - this.valueDAO.save(storageName, valueModel); - } + // 使用事务来保证数据一致性 + await Cache.getInstance().tx("setValue:" + storageName, async () => { + const valueModel = await this.valueDAO.get(storageName); + if (!valueModel) { + await this.valueDAO.save(storageName, { + uuid: script.uuid, + storageName: storageName, + data: { [key]: value }, + createtime: Date.now(), + updatetime: Date.now(), + }); + } else { + oldValue = valueModel.data[key]; + valueModel.data[key] = value; + await this.valueDAO.save(storageName, valueModel); + } + console.log(valueModel); + return true; + }); const sendData: ValueUpdateData = { oldValue, sender, value, key, uuid, - storageKey: storageName, + storageName: storageName, }; // 判断是后台脚本还是前台脚本 - console.log("value update", script, sendData); if (script.type === SCRIPT_TYPE_NORMAL) { chrome.tabs.query({}, (tabs) => { // 推送到所有加载了本脚本的tab中 diff --git a/src/pages/store/subscribe.ts b/src/pages/store/subscribe.ts index 9a547df..7406321 100644 --- a/src/pages/store/subscribe.ts +++ b/src/pages/store/subscribe.ts @@ -5,7 +5,6 @@ import { deleteScript, scriptSlice, upsertScript } from "./features/script"; export default function storeSubscribe() { subscribeScriptRunStatus(messageQueue, (data) => { - console.log("subscribeScriptRunStatus", data); store.dispatch(scriptSlice.actions.updateRunStatus(data)); }); subscribeScriptInstall(messageQueue, (message) => { diff --git a/src/runtime/content/exec_script.ts b/src/runtime/content/exec_script.ts index f35758f..0e9e793 100644 --- a/src/runtime/content/exec_script.ts +++ b/src/runtime/content/exec_script.ts @@ -5,16 +5,18 @@ import GMApi from "./gm_api"; import { compileScript, createContext, proxyContext, ScriptFunc } from "./utils"; import { Message } from "@Packages/message/server"; +export type ValueUpdateSender = { + runFlag: string; + tabId?: number; +}; + export type ValueUpdateData = { oldValue: any; value: any; key: string; // 值key uuid: string; - storageKey: string; // 储存key - sender: { - runFlag: string; - tabId?: number; - }; + storageName: string; // 储存name + sender: ValueUpdateSender; }; export class RuntimeMessage {} diff --git a/src/runtime/content/gm_api.ts b/src/runtime/content/gm_api.ts index 541dedd..e2c3f00 100644 --- a/src/runtime/content/gm_api.ts +++ b/src/runtime/content/gm_api.ts @@ -2,7 +2,7 @@ import { ScriptRunResouce } from "@App/app/repo/scripts"; import { getMetadataStr, getUserConfigStr, parseUserConfig } from "@App/pkg/utils/script"; import { ValueUpdateData } from "./exec_script"; import { ExtVersion } from "@App/app/const"; -import { storageKey } from "../utils"; +import { getStorageName } from "../utils"; import { Message, MessageConnect } from "@Packages/message/server"; import { CustomEventMessage } from "@Packages/message/custom_event_message"; import LoggerCore from "@App/app/logger/core"; @@ -86,22 +86,18 @@ export default class GMApi { } public valueUpdate(data: ValueUpdateData) { - if (data.uuid === this.scriptRes.uuid || data.storageKey === storageKey(this.scriptRes)) { + if (data.uuid === this.scriptRes.uuid || data.storageName === getStorageName(this.scriptRes)) { // 触发,并更新值 if (data.value === undefined) { - delete this.scriptRes.value[data.value]; + if (this.scriptRes.value[data.key] !== undefined) { + delete this.scriptRes.value[data.key]; + } } else { this.scriptRes.value[data.key] = data.value; } this.valueChangeListener.forEach((item) => { - if (item.name === data.value.key) { - item.listener( - data.value.key, - data.oldValue, - data.value, - data.sender.runFlag !== this.runFlag, - data.sender.tabId - ); + if (item.name === data.key) { + item.listener(data.key, data.oldValue, data.value, data.sender.runFlag !== this.runFlag, data.sender.tabId); } }); } @@ -345,6 +341,8 @@ export default class GMApi { // 处理blob param.dataType = "Blob"; param.data = await this.CAT_createBlobUrl(details.data); + } else { + param.data = details.data; } // 处理返回数据 diff --git a/src/runtime/content/inject.ts b/src/runtime/content/inject.ts index dbbac26..b430572 100644 --- a/src/runtime/content/inject.ts +++ b/src/runtime/content/inject.ts @@ -1,7 +1,8 @@ import { ScriptRunResouce } from "@App/app/repo/scripts"; import { Message, Server } from "@Packages/message/server"; -import ExecScript from "./exec_script"; +import ExecScript, { ValueUpdateData } from "./exec_script"; import { addStyle, ScriptFunc } from "./utils"; +import { getStorageName } from "../utils"; export class InjectRuntime { execList: ExecScript[] = []; @@ -35,11 +36,12 @@ export class InjectRuntime { 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,); - } + this.server.on("runtime/valueUpdate", (data: ValueUpdateData) => { + this.execList + .filter((val) => val.scriptRes.uuid === data.uuid || getStorageName(val.scriptRes) === data.storageName) + .forEach((val) => { + val.valueUpdate(data); + }); }); } diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts index 9a1c556..febb764 100644 --- a/src/runtime/utils.ts +++ b/src/runtime/utils.ts @@ -29,7 +29,7 @@ export const unsafeHeaders: { [key: string]: boolean } = { via: true, }; -export function storageKey(script: Script): string { +export function getStorageName(script: Script): string { if (script.metadata && script.metadata.storagename) { return script.metadata.storagename[0]; }