diff --git a/packages/chrome-extension-mock/declarativ_net_request.ts b/packages/chrome-extension-mock/declarativ_net_request.ts index abbf15c3e..c7610a93f 100644 --- a/packages/chrome-extension-mock/declarativ_net_request.ts +++ b/packages/chrome-extension-mock/declarativ_net_request.ts @@ -30,9 +30,10 @@ export default class DeclarativeNetRequest { OTHER: "other", }; - updateSessionRules() { + updateSessionRules(rule: any, callback?: any) { return new Promise((resolve) => { resolve(); + callback?.(); }); } } diff --git a/src/app/service/service_worker/dnr.ts b/src/app/service/service_worker/dnr.ts new file mode 100644 index 000000000..43feef7d0 --- /dev/null +++ b/src/app/service/service_worker/dnr.ts @@ -0,0 +1,58 @@ +/** + * scheduler 用于 Service Worker 或 Event Page, Chrome 94+, Firefox 142+ + */ +const scheduler_ = + typeof scheduler !== "undefined" && + typeof scheduler?.postTask === "function" && + typeof scheduler?.yield === "function" + ? scheduler + : null; + +// 用于扩充初始化时新增 SessionRules. FireFox 需要等一等才加,否则会失效。 +export const addSessionRules = async (rules: chrome.declarativeNetRequest.Rule[], resolve?: ResolveFn) => { + await scheduler_?.yield?.(); + chrome.declarativeNetRequest.updateSessionRules( + { + removeRuleIds: [...rules.map((rule) => rule.id)], + addRules: rules, + }, + () => { + const lastError = chrome.runtime.lastError; + if (lastError) { + console.error("chrome.declarativeNetRequest.updateSessionRules:", lastError); + } + resolve?.(); + } + ); +}; + +export const sessionRuleDynamicAdd = (rule: chrome.declarativeNetRequest.Rule, resolve?: ResolveFn) => { + chrome.declarativeNetRequest.updateSessionRules( + { + removeRuleIds: [rule.id], + addRules: [rule], + }, + () => { + const lastError = chrome.runtime.lastError; + if (lastError) { + console.error("chrome.declarativeNetRequest.updateSessionRules:", lastError); + } + resolve?.(); + } + ); +}; + +export const sessionRuleDynamicRemove = (ruleId: number, resolve?: ResolveFn) => { + chrome.declarativeNetRequest.updateSessionRules( + { + removeRuleIds: [ruleId], + }, + () => { + const lastError = chrome.runtime.lastError; + if (lastError) { + console.error("chrome.declarativeNetRequest.updateSessionRules:", lastError); + } + resolve?.(); + } + ); +}; diff --git a/src/app/service/service_worker/gm_api/gm_api.ts b/src/app/service/service_worker/gm_api/gm_api.ts index 755096d21..f4c89820d 100644 --- a/src/app/service/service_worker/gm_api/gm_api.ts +++ b/src/app/service/service_worker/gm_api/gm_api.ts @@ -54,6 +54,7 @@ import { headerModifierMap, headersReceivedMap } from "./gm_xhr"; import { BgGMXhr } from "@App/pkg/utils/xhr/bg_gm_xhr"; import { mightPrepareSetClipboard, setClipboard } from "../clipboard"; import { nativePageWindowOpen } from "../../offscreen/gm_api"; +import { addSessionRules, sessionRuleDynamicAdd, sessionRuleDynamicRemove } from "../dnr"; let generatedUniqueMarkerIDs = ""; let generatedUniqueMarkerIDWhen = ""; @@ -712,10 +713,7 @@ export default class GMApi { }, } as chrome.declarativeNetRequest.Rule; headerModifierMap.set(markerID, { rule, redirectNotManual }); - await chrome.declarativeNetRequest.updateSessionRules({ - removeRuleIds: [ruleId], - addRules: [rule], - }); + await new Promise((resolve) => sessionRuleDynamicAdd(rule, resolve)); } return true; } @@ -1594,32 +1592,11 @@ export default class GMApi { }, }; headerModifierMap.set(markerID, { rule: newRule, redirectNotManual }); - chrome.declarativeNetRequest.updateSessionRules( - { - removeRuleIds: [rule.id], - addRules: [newRule], - }, - () => { - const lastError = chrome.runtime.lastError; - if (lastError) { - console.error("chrome.declarativeNetRequest.updateSessionRules:", lastError); - } - } - ); + sessionRuleDynamicAdd(newRule); } else { // 删除关联与DNR headerModifierMap.delete(markerID); - chrome.declarativeNetRequest.updateSessionRules( - { - removeRuleIds: [rule.id], - }, - () => { - const lastError = chrome.runtime.lastError; - if (lastError) { - console.error("chrome.declarativeNetRequest.updateSessionRules:", lastError); - } - } - ); + sessionRuleDynamicRemove(rule.id); } } } @@ -1650,18 +1627,7 @@ export default class GMApi { tabIds: [chrome.tabs.TAB_ID_NONE], // 只限于后台 service_worker / offscreen }, } as chrome.declarativeNetRequest.Rule; - chrome.declarativeNetRequest.updateSessionRules( - { - removeRuleIds: [ruleId], - addRules: [rule], - }, - () => { - const lastError = chrome.runtime.lastError; - if (lastError) { - console.error("chrome.declarativeNetRequest.updateSessionRules:", lastError); - } - } - ); + addSessionRules([rule]); } start() { diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index ca6b6da2b..b2aeb931e 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -47,6 +47,7 @@ import { getSimilarityScore, ScriptUpdateCheck } from "./script_update_check"; import { LocalStorageDAO } from "@App/app/repo/localStorage"; import { CompiledResourceDAO } from "@App/app/repo/resource"; import { initRegularUpdateCheck } from "./regular_updatecheck"; +import { addSessionRules } from "./dnr"; export type TCheckScriptUpdateOption = Partial< { checkType: "user"; noUpdateCheck?: number } | ({ checkType: "system" } & Record) @@ -299,20 +300,7 @@ export class ScriptService { } } ); - chrome.declarativeNetRequest.updateSessionRules( - { - removeRuleIds: [...rules.map((rule) => rule.id)], - addRules: rules, - }, - () => { - if (chrome.runtime.lastError) { - console.error( - "chrome.runtime.lastError in chrome.declarativeNetRequest.updateSessionRules:", - chrome.runtime.lastError - ); - } - } - ); + addSessionRules(rules); } public async openInstallPageByUrl( diff --git a/src/types/main.d.ts b/src/types/main.d.ts index 0b9624821..a50ba249d 100644 --- a/src/types/main.d.ts +++ b/src/types/main.d.ts @@ -7,6 +7,20 @@ declare module "@App/app/types.d.ts"; type Override = Omit & U; type ValueOf = T[keyof T]; type ReactStateSetter = (value: T | ((prev: T) => T)) => void; +type ResolveFn = (val: T) => void; + +interface SchedulerPostTaskOptions { + delay?: number; + priority?: "user-blocking" | "user-visible" | "background"; + signal?: AbortSignal; +} + +interface Scheduler { + postTask(callback: () => T | Promise, options?: SchedulerPostTaskOptions): Promise; + yield(): Promise; +} + +declare let scheduler: Scheduler | undefined; declare const sandbox: Window;