Skip to content
19 changes: 19 additions & 0 deletions src/app/service/service_worker/gm_api/gm_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { BgGMXhr } from "@App/pkg/utils/xhr/bg_gm_xhr";
import { mightPrepareSetClipboard, setClipboard } from "../clipboard";
import { nativePageWindowOpen } from "../../offscreen/gm_api";
import { nextSessionRuleId, removeSessionRuleIdEntry } from "./dnr_id_controller";
import { WakeUpCommand, wakeupPingCommand } from "@App/pkg/utils/wakeup-ping";

let generatedUniqueMarkerIDs = "";
let generatedUniqueMarkerIDWhen = "";
Expand Down Expand Up @@ -844,11 +845,27 @@ export default class GMApi {
if (!sender.isType(GetSenderType.CONNECT)) {
throw new Error("GM_xmlhttpRequest ERROR: sender is not MessageConnect");
}

// https://github.com/scriptscat/scriptcat/issues/1343
let wakeupTrigger = true;
wakeupPingCommand(WakeUpCommand.START);
const wakeupStop = () => {
try {
if (wakeupTrigger) {
wakeupTrigger = false;
wakeupPingCommand(WakeUpCommand.STOP);
}
} catch {
// ignored
}
};

const msgConn = sender.getConnect()!;

let isConnDisconnected = false;
msgConn.onDisconnect(() => {
isConnDisconnected = true;
wakeupStop();
});
Comment on lines +849 to 869

// 关联自己生成的请求id与chrome.webRequest的请求id
Expand Down Expand Up @@ -899,6 +916,7 @@ export default class GMApi {
useFetch = isFetch || !!redirect || anonymous || isBufferStream;
}
const loadendCleanUp = () => {
wakeupStop();
redirectedUrls.delete(markerID);
nwErrorResults.delete(markerID);
nwErrorResultPromises.delete(markerID);
Expand Down Expand Up @@ -951,6 +969,7 @@ export default class GMApi {
msgConn.onDisconnect(offscreenCon.disconnect.bind(offscreenCon));
}
} catch (e: any) {
wakeupStop();
const errorMsg = `GM_xmlhttpRequest ERROR: ${e?.message || e || "Unknown Error"}`;
if (!isConnDisconnected) {
msgConn.sendMessage({
Expand Down
2 changes: 2 additions & 0 deletions src/offscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import LoggerCore from "./app/logger/core";
import MessageWriter from "./app/logger/message_writer";
import { OffscreenManager } from "./app/service/offscreen";
import { ServiceWorkerClientMessage } from "@Packages/message/window_message";
import { initializeWakeupPing } from "./pkg/utils/wakeup-ping";

function main() {
// 通过postMessage与SW通信,支持结构化克隆(Blob等)
Expand All @@ -15,6 +16,7 @@ function main() {
// 初始化管理器
const manager = new OffscreenManager(swPostMessage);
manager.initManager();
initializeWakeupPing();
}

main();
97 changes: 97 additions & 0 deletions src/pkg/utils/wakeup-ping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const PING_INTERVAL_MS_1 = 13_225;
const PING_INTERVAL_MS_2 = 17_765;

export const WakeUpCommand = {
START: 0x100,
STOP: 0x200,
} as const;

export type WakeUpCommand = ValueOf<typeof WakeUpCommand>;

/**
* scheduler 用于后台排程:Chrome 94+, Firefox 142+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/postTask
*/
const nativeScheduler =
//@ts-ignore
typeof scheduler !== "undefined" && typeof scheduler?.postTask === "function" && scheduler;

// 高效的 BroadcastChannel 通讯:service worker 和 offscreen 共用同一通道
const channel = new BroadcastChannel("custom-ping"); // offscreen -> sw
const channelCommand = new BroadcastChannel("custom-ping-command"); // sw -> offscreen
let startCounter = 0;

// initializeWakeupPing only execute once in offscreen
export const initializeWakeupPing = () => {
if (typeof frameElement === "object" && typeof document === "object" && document) {
let counter = 0;
let isMutationPending = false;

const pingNode = document.createComment("0");

const incrementCounter = () => {
if (startCounter >= 1 && !isMutationPending) {
isMutationPending = true;
counter = counter & 8 ? 1 : counter + 1;
pingNode.data = `${counter}`;
}
};

const pingTask = async () => {
Comment thread
cyfung1031 marked this conversation as resolved.
if (startCounter < 1) {
return;
}
channel.postMessage(true);
incrementCounter();
};

const mutationObserver = new MutationObserver(() => {
if (isMutationPending) {
isMutationPending = false;
const pingIntervalMs = Math.random() * (PING_INTERVAL_MS_2 - PING_INTERVAL_MS_1) + PING_INTERVAL_MS_1;
if (nativeScheduler) {
nativeScheduler.postTask(pingTask, { priority: "background", delay: pingIntervalMs });
} else {
setTimeout(pingTask, pingIntervalMs);
}
}
});
mutationObserver.observe(pingNode, { characterData: true });
// incrementCounter();

channelCommand.onmessage = (e) => {
if (e.data === WakeUpCommand.START) {
startCounter++;
if (startCounter === 1) {
incrementCounter();
}
} else if (e.data === WakeUpCommand.STOP) {
if (startCounter > 0) startCounter--;
}
};
}
};

// initializeWakeupPing only execute once in service worker
export const listenWakeupPing = (onWakeupPing: (...args: any) => any) => {
chrome.storage.session.onChanged.addListener((obj) => {
// 消耗 persistentWakeup
if (typeof obj.persistentWakeup !== "undefined") {
// 执行任意 callback
onWakeupPing();
}
});
channel.onmessage = (e) => {
// 触发 chrome storage onChanged 使 service worker 保持活跃
chrome.storage.session.set({ persistentWakeup: `${e.timeStamp}` }, () => {
if (chrome.runtime.lastError) {
console.error("failed to persist wakeup ping", chrome.runtime.lastError);
}
});
};
};
Comment on lines +76 to +92
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI 错


// wakeupPingCommand only execute in service worker
export const wakeupPingCommand = (command: WakeUpCommand) => {
channelCommand.postMessage(command);
};
Comment on lines +24 to +97
7 changes: 7 additions & 0 deletions src/service_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { MessageQueue } from "@Packages/message/message_queue";
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
import migrate, { migrateChromeStorage } from "./app/migrate";
import { cleanInvalidKeys } from "./app/repo/resource";
import { listenWakeupPing } from "./pkg/utils/wakeup-ping";

migrate();
migrateChromeStorage();
Expand Down Expand Up @@ -59,6 +60,11 @@ async function setupOffscreenDocument() {
}
}

export const onWakeupPing = () => {
//@ts-ignore
self.lastWakeupPing = new Date().toLocaleString("zh"); // 僅在後台DevTools debug用
};

function main() {
cleanInvalidKeys();
// 初始化管理器
Expand All @@ -77,6 +83,7 @@ function main() {
manager.initManager();
// 初始化沙盒环境
setupOffscreenDocument();
listenWakeupPing(onWakeupPing);
}

main();
Loading