Skip to content

Commit ed57bf3

Browse files
authored
Merge pull request #410 from WhiteSevs/fix-gm-api
✨ feat: 新增Api支持GM_setValues、GM_getValues、GM_deleteValues
2 parents 6cd49ea + 04bf703 commit ed57bf3

6 files changed

Lines changed: 218 additions & 38 deletions

File tree

packages/message/server.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,15 @@ export class Server {
9898
.then((data) => {
9999
sendResponse({ code: 0, data });
100100
})
101-
.catch((err) => {
102-
sendResponse({ code: -1, message: err });
101+
.catch((e: Error) => {
102+
sendResponse({ code: -1, message: e.message || e.toString() });
103103
});
104104
return true;
105105
} else {
106106
sendResponse({ code: 0, data: ret });
107107
}
108108
} catch (e: any) {
109-
sendResponse({ code: -1, message: e.message });
109+
sendResponse({ code: -1, message: e.message || e.toString() });
110110
}
111111
} else {
112112
sendResponse({ code: -1, message: "no such api " + action });

src/app/service/content/gm_api.ts

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,15 @@ export default class GMApi {
126126
grant: script.metadata.grant || [],
127127
connects: script.metadata.connect || [],
128128
};
129-
130129
return {
131130
downloadMode: "browser",
132131
// isIncognito
133132
// relaxedCsp
134133
// sandboxMode
135134
scriptWillUpdate: true,
136135
scriptHandler: "ScriptCat",
137-
scriptUpdateURL: script.downloadUrl,
136+
// "" => null
137+
scriptUpdateURL: script.downloadUrl || null,
138138
scriptMetaStr: script.metadataStr,
139139
userConfig: parseUserConfig(script.userConfigStr),
140140
userConfigStr: script.userConfigStr,
@@ -146,6 +146,9 @@ export default class GMApi {
146146
namespace: script.namespace,
147147
version: script.metadata.version?.[0],
148148
author: script.author,
149+
lastModified: script.updatetime,
150+
downloadURL: script.downloadUrl || null,
151+
updateURL: script.checkUpdateUrl || null,
149152
...options,
150153
},
151154
};
@@ -181,6 +184,57 @@ export default class GMApi {
181184
this.GM_setValue(name, undefined);
182185
}
183186

187+
@GMContext.API({ depend: ["GM_setValue"] })
188+
public GM_setValues(values: object) {
189+
if (values == null) {
190+
throw new Error("GM_ setValues: values must not be null or undefined");
191+
}
192+
if (typeof values !== "object") {
193+
throw new Error("GM_setValues: values must be an object");
194+
}
195+
Object.keys(values).forEach((key) => {
196+
let value = values[key as keyof typeof values];
197+
return this.GM_setValue(key, value);
198+
});
199+
}
200+
201+
@GMContext.API({ depend: ["GM_getValue"] })
202+
public GM_getValues(keysOrDefaults: object | string[] | null | undefined) {
203+
if (keysOrDefaults == null) {
204+
// returns all
205+
return this.scriptRes.value;
206+
}
207+
let result = <{ [key: string]: any }>{};
208+
if (Array.isArray(keysOrDefaults)) {
209+
// 键名数组
210+
for (let index = 0; index < keysOrDefaults.length; index++) {
211+
const key = keysOrDefaults[index];
212+
if (key in this.scriptRes.value) {
213+
result[key] = this.scriptRes.value[key];
214+
}
215+
}
216+
} else {
217+
// 对象 键: 默认值
218+
Object.keys(keysOrDefaults).forEach((key) => {
219+
let defaultValue = keysOrDefaults[key as keyof typeof keysOrDefaults];
220+
result[key] = this.GM_getValue(key, defaultValue);
221+
});
222+
}
223+
224+
return result;
225+
}
226+
227+
@GMContext.API({ depend: ["GM_deleteValue"] })
228+
public GM_deleteValues(keys: string[]) {
229+
if (!Array.isArray(keys)) {
230+
console.warn(" GM_deleteValues: keys must be string[]");
231+
return;
232+
}
233+
keys.forEach((key) => {
234+
this.GM_deleteValue(key);
235+
});
236+
}
237+
184238
eventId: number = 0;
185239

186240
menuMap: Map<number, string> | undefined;
@@ -736,6 +790,9 @@ export default class GMApi {
736790
close: () => {
737791
tabid && this.GM_closeInTab(tabid);
738792
},
793+
closed: false,
794+
// 占位
795+
onclose() {},
739796
};
740797

741798
this.sendMessage("GM_openInTab", [url, option]).then((id) => {
@@ -775,7 +832,7 @@ export default class GMApi {
775832
@GMContext.API()
776833
GM_getTab(callback: (data: any) => void) {
777834
this.sendMessage("GM_getTab", []).then((data) => {
778-
callback(data);
835+
callback(data ?? {});
779836
});
780837
}
781838

@@ -795,8 +852,18 @@ export default class GMApi {
795852
}
796853

797854
@GMContext.API()
798-
GM_setClipboard(data: string, info?: string | { type?: string; minetype?: string }) {
799-
this.sendMessage("GM_setClipboard", [data, info]);
855+
GM_setClipboard(data: string, info?: string | { type?: string; minetype?: string }, cb?: () => void) {
856+
this.sendMessage("GM_setClipboard", [data, info])
857+
.then((resp) => {
858+
if (typeof cb === "function") {
859+
cb();
860+
}
861+
})
862+
.catch(() => {
863+
if (typeof cb === "function") {
864+
cb();
865+
}
866+
});
800867
}
801868

802869
@GMContext.API()

src/app/service/content/utils.ts

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,38 @@ import { Message } from "@Packages/message/server";
66
import EventEmitter from "eventemitter3";
77

88
// 构建脚本运行代码
9-
export function compileScriptCode(scriptRes: ScriptRunResouce): string {
10-
let require = "";
11-
if (scriptRes.metadata.require) {
12-
scriptRes.metadata.require.forEach((val) => {
13-
const res = scriptRes.resource[val];
14-
if (res) {
15-
require = `${require}\n${res.content}`;
16-
}
17-
});
9+
export function compileScriptCode(scriptRes: ScriptRunResouce, scriptCode?: string): string {
10+
scriptCode = scriptCode ?? scriptRes.code;
11+
let requireCode = "";
12+
if (Array.isArray(scriptRes.metadata.require)) {
13+
requireCode += scriptRes.metadata.require
14+
.map((val) => {
15+
const res = scriptRes.resource[val];
16+
if (res) {
17+
return res.content;
18+
}
19+
})
20+
.join("\n");
1821
}
19-
const code = require + scriptRes.code;
20-
return `with (context) return (async ()=>{\n${code}\n//# sourceURL=${chrome.runtime.getURL(
21-
`/${encodeURI(scriptRes.name)}.user.js`
22-
)}\n})()`;
22+
const sourceURL = `//# sourceURL=${chrome.runtime.getURL(`/${encodeURI(scriptRes.name)}.user.js`)}`;
23+
const code = [requireCode, scriptCode, sourceURL].join("\n");
24+
return ` with(context){
25+
(async (factory) => {
26+
try {
27+
await factory();
28+
} catch (e) {
29+
if (e.message && e.stack) {
30+
console.error("ERROR: Execution of script '${scriptRes.name}' failed! " + e.message);
31+
console.log(e.stack);
32+
} else {
33+
console.error(e);
34+
}
35+
}
36+
37+
})(async function(){
38+
${code}
39+
})
40+
}`;
2341
}
2442

2543
export type ScriptFunc = (context: any, GM_info: any) => any;
@@ -28,9 +46,20 @@ export type ScriptFunc = (context: any, GM_info: any) => any;
2846
export function compileScript(code: string): ScriptFunc {
2947
return <ScriptFunc>new Function("context", "GM_info", code);
3048
}
31-
32-
export function compileInjectScript(script: ScriptRunResouce): string {
33-
return `window['${script.flag}']=function(context,GM_info){\n${script.code}\n}`;
49+
/**
50+
* 将脚本函数编译为注入脚本代码
51+
* @param script
52+
* @param scriptCode
53+
* @param [autoDeleteMountFunction=false] 是否自动删除挂载的函数
54+
*/
55+
export function compileInjectScript(
56+
script: ScriptRunResouce,
57+
scriptCode?: string,
58+
autoDeleteMountFunction: boolean = false
59+
): string {
60+
scriptCode = scriptCode ?? script.code;
61+
return `window['${script.flag}'] = function(context, GM_info){
62+
${autoDeleteMountFunction ? ` try{delete window['${script.flag}'];}catch(e){};` : ""}${scriptCode}}`;
3463
}
3564

3665
// 设置api依赖
@@ -70,7 +99,9 @@ export function createContext(scriptRes: ScriptRunResouce, GMInfo: any, envPrefi
7099
EE: new EventEmitter(),
71100
GM: { info: GMInfo },
72101
GM_info: GMInfo,
73-
window: {},
102+
window: {
103+
onurlchange: null,
104+
},
74105
};
75106
if (scriptRes.metadata.grant) {
76107
const GM_cookie = function (action: string) {
@@ -248,6 +279,7 @@ export function proxyContext(global: any, context: any, thisContext?: { [key: st
248279
return global.top;
249280
case "close":
250281
case "focus":
282+
case "onurlchange":
251283
if (context["window"][name]) {
252284
return context["window"][name];
253285
}

src/app/service/service_worker/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { SynchronizeService } from "./synchronize";
1111
import { SubscribeService } from "./subscribe";
1212
import { ExtServer, ExtVersion } from "@App/app/const";
1313
import { systemConfig } from "@App/pages/store/global";
14-
import { ScriptDAO } from "@App/app/repo/scripts";
14+
import { ScriptCodeDAO, ScriptDAO } from "@App/app/repo/scripts";
1515

1616
export type InstallSource = "user" | "system" | "sync" | "subscribe" | "vscode";
1717

src/app/service/service_worker/resource.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,32 @@ export class ResourceService {
4444
}
4545
const ret: { [key: string]: Resource } = {};
4646
await Promise.allSettled(
47-
script.metadata[type].map(async (u) => {
47+
script.metadata[type].map(async (uri) => {
48+
/** 资源键名 */
49+
let resourceKey = uri;
50+
/** 文件路径 */
51+
let path: string | null = uri;
4852
if (type === "resource") {
49-
const split = u.split(/\s+/);
53+
// @resource xxx https://...
54+
const split = uri.split(/\s+/);
5055
if (split.length === 2) {
51-
const res = await this.getResource(script.uuid, split[1], "resource");
56+
resourceKey = split[0];
57+
path = split[1].trim();
58+
} else {
59+
path = null;
60+
}
61+
}
62+
if (path) {
63+
if (uri.startsWith("file://")) {
64+
// 如果是file://协议,则每次请求更新一下文件
65+
const res = await this.updateResource(script.uuid, path, type);
66+
ret[resourceKey] = res;
67+
} else {
68+
const res = await this.getResource(script.uuid, path, type);
5269
if (res) {
53-
ret[split[0]] = res;
70+
ret[resourceKey] = res;
5471
}
5572
}
56-
} else {
57-
const res = await this.getResource(script.uuid, u, type);
58-
if (res) {
59-
ret[u] = res;
60-
}
6173
}
6274
})
6375
);
@@ -108,7 +120,7 @@ export class ResourceService {
108120
}
109121
}
110122
try {
111-
res = await this.updateResource(url, uuid, type);
123+
res = await this.updateResource(uuid, url, type);
112124
if (res) {
113125
return Promise.resolve(res);
114126
}
@@ -119,7 +131,7 @@ export class ResourceService {
119131
return Promise.resolve(undefined);
120132
}
121133

122-
async updateResource(url: string, uuid: string, type: ResourceType) {
134+
async updateResource(uuid: string, url: string, type: ResourceType) {
123135
// 重新加载
124136
const u = this.parseUrl(url);
125137
let result = await this.getResourceModel(u.url);

0 commit comments

Comments
 (0)