@@ -11,6 +11,7 @@ import PermissionVerify, { PermissionVerifyApiGet } from "../permission_verify";
1111import { cacheInstance } from "@App/app/cache" ;
1212import { type RuntimeService } from "../runtime" ;
1313import { getIcon , isFirefox , getCurrentTab , openInCurrentTab , cleanFileName , makeBlobURL } from "@App/pkg/utils/utils" ;
14+ import { deferred , type Deferred } from "@App/pkg/utils/utils" ;
1415import { type SystemConfig } from "@App/pkg/config/config" ;
1516import i18next , { i18nName } from "@App/locales/locales" ;
1617import FileSystemFactory from "@Packages/filesystem/factory" ;
@@ -44,6 +45,10 @@ import { headerModifierMap, headersReceivedMap } from "./gm_xhr";
4445import { BgGMXhr } from "@App/pkg/utils/xhr/bg_gm_xhr" ;
4546import { mightPrepareSetClipboard , setClipboard } from "../clipboard" ;
4647import { 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
4853let generatedUniqueMarkerIDs = "" ;
4954let 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