diff --git a/rspack.config.ts b/rspack.config.ts
index 91969281c..691277ecb 100644
--- a/rspack.config.ts
+++ b/rspack.config.ts
@@ -52,6 +52,7 @@ export default {
service_worker: `${src}/service_worker.ts`,
offscreen: `${src}/offscreen.ts`,
sandbox: `${src}/sandbox.ts`,
+ persistent_frame: `${src}/persistent_frame.ts`,
content: `${src}/content.ts`,
scripting: `${src}/scripting.ts`,
inject: `${src}/inject.ts`,
@@ -233,6 +234,13 @@ export default {
minify: true,
chunks: ["sandbox"],
}),
+ new rspack.HtmlRspackPlugin({
+ filename: `${dist}/ext/src/persistent_frame.html`,
+ template: `${src}/pages/persistent_frame.html`,
+ inject: "head",
+ minify: true,
+ chunks: ["persistent_frame"],
+ }),
new ZipExecutionPlugin(),
].filter(Boolean),
experiments: {
diff --git a/src/pages/persistent_frame.html b/src/pages/persistent_frame.html
new file mode 100644
index 000000000..bb5973e67
--- /dev/null
+++ b/src/pages/persistent_frame.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ persistent_frame
+
+
+
+
+
diff --git a/src/persistent_frame.ts b/src/persistent_frame.ts
new file mode 100644
index 000000000..18d26945e
--- /dev/null
+++ b/src/persistent_frame.ts
@@ -0,0 +1,44 @@
+// persistent_frame.ts
+const WAKE_UP_INTERVAL = 2000;
+const RUNNER_RATE = 496.75;
+let waitState = 0;
+let lastNow = 0;
+if (typeof frameElement === "object" && frameElement) {
+ const cNode = document.createComment("0");
+ let cVal = 0;
+ /**
+ * scheduler 用于 Service Worker 或 Event Page, Chrome 94+, Firefox 142+
+ */
+ //@ts-ignore
+ const scheduler_ = typeof scheduler !== "undefined" && typeof scheduler?.postTask === "function" ? scheduler : null;
+ const runner = (ts: number) => {
+ waitState = 1;
+ cVal = cVal > 8 ? 1 : cVal + 1;
+ cNode.data = `${cVal}`;
+ const now = ts;
+ if (now - lastNow > WAKE_UP_INTERVAL) {
+ lastNow = now;
+ chrome.storage.session.set({ persistentWakeup: `${now}` });
+ document.title = `wakup at ${now}`; // debug
+ }
+ };
+ window.addEventListener("message", (ev) => {
+ if (waitState === 2) {
+ if (typeof ev.data === "object" && ev.data?.myCustomAction === "waked-up") {
+ if (scheduler_) {
+ scheduler_.postTask(() => runner(Date.now()), { priority: "background", delay: RUNNER_RATE });
+ } else {
+ runner(ev.timeStamp);
+ }
+ }
+ }
+ });
+ const mutObserver = new MutationObserver(() => {
+ if (waitState === 1) {
+ waitState = 2;
+ window?.postMessage({ myCustomAction: "waked-up" }, "*");
+ }
+ });
+ mutObserver.observe(cNode, { characterData: true });
+ runner(0);
+}
diff --git a/src/pkg/utils/persistent.ts b/src/pkg/utils/persistent.ts
new file mode 100644
index 000000000..e6066b408
--- /dev/null
+++ b/src/pkg/utils/persistent.ts
@@ -0,0 +1,13 @@
+export const keepEventPageRunning = () => {
+ if (typeof frames !== "object") return;
+ if (typeof document === "undefined") return;
+ if (typeof document.documentElement === "undefined") return;
+ if (document.getElementById("persistent_frame")) return;
+ chrome.storage.session.onChanged.addListener((obj) => {
+ typeof obj.persistentWakeup !== "undefined";
+ });
+ const iframe = document.createElement("iframe");
+ iframe.id = "persistent_frame";
+ iframe.src = chrome.runtime.getURL("/src/persistent_frame.html");
+ document.documentElement.appendChild(iframe);
+};
diff --git a/src/service_worker.ts b/src/service_worker.ts
index 407a27922..3e465b26c 100644
--- a/src/service_worker.ts
+++ b/src/service_worker.ts
@@ -8,9 +8,11 @@ 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 { keepEventPageRunning } from "@App/pkg/utils/persistent";
migrate();
migrateChromeStorage();
+keepEventPageRunning();
const OFFSCREEN_DOCUMENT_PATH = "src/offscreen.html";