diff --git a/src/app/service/content/gm_api/gm_api.test.ts b/src/app/service/content/gm_api/gm_api.test.ts index 64c1a26f4..fd737de4a 100644 --- a/src/app/service/content/gm_api/gm_api.test.ts +++ b/src/app/service/content/gm_api/gm_api.test.ts @@ -461,6 +461,158 @@ describe.concurrent("GM_value", () => { expect(ret).toEqual({ ret1: 123, ret2: 456 }); }); + it.concurrent("value引用问题 #1141", async () => { + const script = Object.assign({}, scriptRes) as ScriptLoadInfo; + script.metadata.grant = ["GM_getValue", "GM_setValue", "GM_getValues"]; + script.code = ` +const value1 = { + arr: [1], + obj: { + a: "1" + }, + str: "123", +} +GM_setValue("abc", value1); + +const allValues1 = GM_getValues(); + +allValues1.abc.arr.push(8); +allValues1.n1 = 5; +allValues1.n2 = {c: 8}; +delete allValues1.abc.obj.a; +allValues1.abc.str = "0"; + +const value2 = GM_getValue("abc"); + +value2.arr.push(2); +value2.obj.b = 2; +value2.str = "456"; + +value1.arr.push(3); +value1.obj.b = 3; +value1.str = "789"; + +const value3 = GM_getValue("abc"); + +const values1 = GM_getValues(["abc", "n3"]); + +const values2 = GM_getValues({"abc":{}, "n4":{}, "n5":"hi"}); + +values2.abc.arr.push(2); +values2.abc.obj.b = 2; +values2.abc.str = "456"; + +const allValues2 = GM_getValues(); + + +const value4 = GM_getValue("abc"); +const value5 = GM_getValue("abc"); +value5.arr[0] = 9; +GM_setValue("abc", value5); + +const value6 = GM_getValue("abc"); + +return { value1, value2, value3, values1,values2, allValues1, allValues2, value4, value5, value6 }; + `; + const mockSendMessage = vi.fn().mockResolvedValue({ code: 0 }); + const mockMessage = { + sendMessage: mockSendMessage, + } as unknown as Message; + // @ts-ignore + const exec = new ExecScript(script, "content", mockMessage, nilFn, envInfo); + exec.scriptFunc = compileScript(compileScriptCode(script)); + const ret = await exec.exec(); + + expect(mockSendMessage).toHaveBeenCalled(); + expect(mockSendMessage).toHaveBeenCalledTimes(2); + + expect(ret).toEqual({ + value1: { + arr: [1, 3], + obj: { + a: "1", + b: 3, + }, + str: "789", + }, + value2: { + arr: [1, 2], + obj: { + a: "1", + b: 2, + }, + str: "456", + }, + value3: { + arr: [1], + obj: { + a: "1", + }, + str: "123", + }, + values1: { + abc: { + arr: [1], + obj: { + a: "1", + }, + str: "123", + }, + }, + values2: { + abc: { + arr: [1, 2], + obj: { + a: "1", + b: 2, + }, + str: "456", + }, + n4: {}, + n5: "hi", + }, + allValues1: { + abc: { + arr: [1, 8], + obj: {}, + str: "0", + }, + n1: 5, + n2: { c: 8 }, + }, + allValues2: { + abc: { + arr: [1], + obj: { + a: "1", + }, + str: "123", + }, + }, + value4: { + arr: [1], + obj: { + a: "1", + }, + str: "123", + }, + value5: { + arr: [9], + obj: { + a: "1", + }, + str: "123", + }, + value6: { + arr: [9], + obj: { + a: "1", + }, + str: "123", + }, + }); + }); + it.concurrent("GM_setValues", async () => { const script = Object.assign({}, scriptRes) as ScriptLoadInfo; script.metadata.grant = ["GM_getValues", "GM_setValues"]; @@ -493,7 +645,7 @@ describe.concurrent("GM_value", () => { api: "GM_setValues", params: [ // event id - expect.stringMatching(/^.+::\d$/), + expect.stringMatching(/^.+::\d+$/), // the object payload expect.objectContaining({ k: expect.stringMatching(/^##[\d.]+##$/), @@ -519,7 +671,7 @@ describe.concurrent("GM_value", () => { api: "GM_setValues", params: [ // event id - expect.stringMatching(/^.+::\d$/), + expect.stringMatching(/^.+::\d+$/), // the object payload expect.objectContaining({ k: expect.stringMatching(/^##[\d.]+##$/), @@ -570,7 +722,7 @@ describe.concurrent("GM_value", () => { api: "GM_setValues", params: [ // event id - expect.stringMatching(/^.+::\d$/), + expect.stringMatching(/^.+::\d+$/), // the object payload expect.objectContaining({ k: expect.stringMatching(/^##[\d.]+##$/), @@ -596,7 +748,7 @@ describe.concurrent("GM_value", () => { api: "GM_setValue", params: [ // event id - expect.stringMatching(/^.+::\d$/), + expect.stringMatching(/^.+::\d+$/), // the string payload "b", ], @@ -641,7 +793,7 @@ describe.concurrent("GM_value", () => { api: "GM_setValues", params: [ // event id - expect.stringMatching(/^.+::\d$/), + expect.stringMatching(/^.+::\d+$/), // the object payload expect.objectContaining({ k: expect.stringMatching(/^##[\d.]+##$/), @@ -667,7 +819,7 @@ describe.concurrent("GM_value", () => { api: "GM_setValues", params: [ // event id - expect.stringMatching(/^.+::\d$/), + expect.stringMatching(/^.+::\d+$/), // the string payload expect.objectContaining({ k: expect.stringMatching(/^##[\d.]+##$/), diff --git a/src/app/service/content/gm_api/gm_api.ts b/src/app/service/content/gm_api/gm_api.ts index 09c3bb205..539184992 100644 --- a/src/app/service/content/gm_api/gm_api.ts +++ b/src/app/service/content/gm_api/gm_api.ts @@ -235,6 +235,9 @@ export default class GMApi extends GM_Base { if (!a.scriptRes) return undefined; const ret = a.scriptRes.value[key]; if (ret !== undefined) { + if (ret && typeof ret === "object") { + return structuredClone(ret); + } return ret; } return defaultValue; @@ -266,14 +269,14 @@ export default class GMApi extends GM_Base { if (promise) { valueChangePromiseMap.set(id, promise); } - // 对object的value进行一次转化 - if (value && typeof value === "object") { - value = JSON.parse(JSON.stringify(value)); - } if (value === undefined) { delete a.scriptRes.value[key]; a.sendMessage("GM_setValue", [id, key]); } else { + // 对object的value进行一次转化 + if (value && typeof value === "object") { + value = structuredClone(value); + } a.scriptRes.value[key] = value; a.sendMessage("GM_setValue", [id, key, value]); } @@ -294,13 +297,13 @@ export default class GMApi extends GM_Base { const valueStore = a.scriptRes.value; for (const [key, value] of Object.entries(values)) { let value_ = value; - // 对object的value进行一次转化 - if (value_ && typeof value_ === "object") { - value_ = JSON.parse(JSON.stringify(value_)); - } if (value_ === undefined) { if (valueStore[key]) delete valueStore[key]; } else { + // 对object的value进行一次转化 + if (value_ && typeof value_ === "object") { + value_ = structuredClone(value_); + } valueStore[key] = value_; } } @@ -366,7 +369,7 @@ export default class GMApi extends GM_Base { if (!this.scriptRes) return {}; if (!keysOrDefaults) { // Returns all values - return this.scriptRes.value; + return structuredClone(this.scriptRes.value); } const result: TGMKeyValue = {}; if (Array.isArray(keysOrDefaults)) { @@ -375,7 +378,12 @@ export default class GMApi extends GM_Base { for (let index = 0; index < keysOrDefaults.length; index++) { const key = keysOrDefaults[index]; if (key in this.scriptRes.value) { - result[key] = this.scriptRes.value[key]; + // 对object的value进行一次转化 + let value = this.scriptRes.value[key]; + if (value && typeof value === "object") { + value = structuredClone(value); + } + result[key] = value; } } } else {