Skip to content

Commit c2b3626

Browse files
committed
新增API - GM.runExclusive
1 parent be818dc commit c2b3626

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

src/app/service/content/gm_api/gm_api.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,63 @@ export default class GMApi extends GM_Base {
13831383
CAT_scriptLoaded() {
13841384
return this.loadScriptPromise;
13851385
}
1386+
1387+
@GMContext.API({ alias: "GM_runExclusive" })
1388+
["GM.runExclusive"](lockKey: string, cb: (...args: any) => any, timeout: number = -1) {
1389+
lockKey = `${lockKey}`; // 轉化為字串
1390+
if (!lockKey || !this.scriptRes) {
1391+
throw new Error("Invalid Calling");
1392+
}
1393+
const key = `${getStorageName(this.scriptRes).replace(/:/g, ":_")}::${lockKey.replace(/:/g, ":_")}`;
1394+
return new Promise((resolve) => {
1395+
let disconnectFn: (() => any) | null | undefined;
1396+
let error: any;
1397+
let result: any;
1398+
let done = false;
1399+
const onDisconnected = () => {
1400+
disconnectFn = null;
1401+
resolve({
1402+
result,
1403+
error,
1404+
done,
1405+
});
1406+
};
1407+
const onStart = async (con: MessageConnect) => {
1408+
try {
1409+
result = await cb();
1410+
} catch (e) {
1411+
error = e;
1412+
}
1413+
done = true;
1414+
con.sendMessage({
1415+
action: "done",
1416+
data: error ? false : typeof result,
1417+
});
1418+
con.disconnect();
1419+
onDisconnected(); // in case .disconnect() not working
1420+
};
1421+
this.connect("runExclusive", [key]).then((con) => {
1422+
disconnectFn = () => {
1423+
con.disconnect();
1424+
onDisconnected(); // in case .disconnect() not working
1425+
};
1426+
con.onDisconnect(onDisconnected);
1427+
con.onMessage((data) => {
1428+
switch (data.action) {
1429+
case "start":
1430+
onStart(con);
1431+
break;
1432+
}
1433+
});
1434+
});
1435+
if (timeout > 0) {
1436+
setTimeout(() => {
1437+
error = new Error("timeout");
1438+
disconnectFn?.();
1439+
}, timeout);
1440+
}
1441+
});
1442+
}
13861443
}
13871444

13881445
// 从 GM_Base 对象中解构出 createGMBase 函数并导出(可供其他模块使用)

src/app/service/service_worker/gm_api/gm_api.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import PermissionVerify, { PermissionVerifyApiGet } from "../permission_verify";
1111
import { cacheInstance } from "@App/app/cache";
1212
import { type RuntimeService } from "../runtime";
1313
import { getIcon, isFirefox, getCurrentTab, openInCurrentTab, cleanFileName, makeBlobURL } from "@App/pkg/utils/utils";
14+
import { deferred, type Deferred } from "@App/pkg/utils/utils";
1415
import { type SystemConfig } from "@App/pkg/config/config";
1516
import i18next, { i18nName } from "@App/locales/locales";
1617
import FileSystemFactory from "@Packages/filesystem/factory";
@@ -44,6 +45,10 @@ import { headerModifierMap, headersReceivedMap } from "./gm_xhr";
4445
import { BgGMXhr } from "@App/pkg/utils/xhr/bg_gm_xhr";
4546
import { mightPrepareSetClipboard, setClipboard } from "../clipboard";
4647
import { nativePageWindowOpen } from "../../offscreen/gm_api";
48+
import { stackAsyncTask } from "@App/pkg/utils/async_queue";
49+
import { uuidv4 } from "@App/pkg/utils/uuid";
50+
51+
const runExclusiveKeys = new Map<string, Deferred<void>>();
4752

4853
let generatedUniqueMarkerIDs = "";
4954
let generatedUniqueMarkerIDWhen = "";
@@ -1305,6 +1310,43 @@ export default class GMApi {
13051310
}
13061311
}
13071312

1313+
@PermissionVerify.API({ link: ["GM.runExclusive", "GM_runExclusive"] })
1314+
runExclusive(request: GMApiRequest<[string, string, any?]>, sender: IGetSender) {
1315+
if (!request.params || request.params.length < 1) {
1316+
throw new Error("param is failed");
1317+
}
1318+
const [lockKey] = request.params as [string, string, any];
1319+
if (!sender.isType(GetSenderType.CONNECT)) {
1320+
throw new Error("GM_download ERROR: sender is not MessageConnect");
1321+
}
1322+
const msgConn = sender.getConnect();
1323+
if (!msgConn) {
1324+
throw new Error("GM_download ERROR: msgConn is undefined");
1325+
}
1326+
let isConnDisconnected = false;
1327+
const d = deferred<boolean>();
1328+
let done: boolean = false;
1329+
const onDisconnected = () => {
1330+
if (isConnDisconnected) return;
1331+
isConnDisconnected = true;
1332+
d.resolve(done);
1333+
};
1334+
msgConn.onDisconnect(onDisconnected);
1335+
msgConn.onMessage((data) => {
1336+
if (data.action === "done") {
1337+
done = true;
1338+
msgConn.disconnect();
1339+
onDisconnected(); // in case .disconnect() not working
1340+
}
1341+
});
1342+
stackAsyncTask(`${lockKey}`, async () => {
1343+
msgConn.sendMessage({
1344+
action: "start",
1345+
});
1346+
return d.promise;
1347+
});
1348+
}
1349+
13081350
handlerNotification() {
13091351
const send = async (
13101352
event: NotificationMessageOption["event"],

0 commit comments

Comments
 (0)