Skip to content

Commit 43bc40f

Browse files
cyfung1031CodFrm
andauthored
♻️ userScripts / scripting API 调整,增强兼容性 ( 重做 #704 ) (#925)
* userScripts / scripting API 调整,增强兼容性 * 恢复sourceMap处理 * 根据copilot意见修改 --------- Co-authored-by: 王一之 <yz@ggnb.top>
1 parent 723e64c commit 43bc40f

File tree

3 files changed

+89
-32
lines changed

3 files changed

+89
-32
lines changed

src/app/service/service_worker/runtime.ts

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
getMetadataStr,
2323
getUserConfigStr,
2424
obtainBlackList,
25+
isFirefox,
2526
sourceMapTo,
2627
} from "@App/pkg/utils/utils";
2728
import { cacheInstance } from "@App/app/cache";
@@ -336,8 +337,8 @@ export class RuntimeService {
336337

337338
let registered = false;
338339
try {
339-
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-content", "scriptcat-inject"] });
340-
registered = res.length === 2;
340+
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-inject"] });
341+
registered = res.length === 1;
341342
} finally {
342343
// 考虑 UserScripts API 不可使用等情况
343344
runtimeGlobal.registered = registered;
@@ -663,6 +664,7 @@ export class RuntimeService {
663664
runtimeGlobal.messageFlag = this.generateMessageFlag();
664665
await Promise.allSettled([
665666
chrome.userScripts.unregister(),
667+
chrome.scripting.unregisterContentScripts(),
666668
this.localStorageDAO.save({ key: "scriptInjectMessageFlag", value: runtimeGlobal.messageFlag }),
667669
]);
668670
}
@@ -830,34 +832,51 @@ export class RuntimeService {
830832
// do nothing
831833
}
832834
}
833-
const retScript: chrome.userScripts.RegisteredUserScript[] = [];
834-
const contentJs = await this.getContentJsCode();
835-
if (contentJs) {
836-
const codeBody = `(function (MessageFlag) {\n${contentJs}\n})('${messageFlag}')`;
837-
const code = `${codeBody}${sourceMapTo("scriptcat-content.js")}\n`;
838-
retScript.push({
839-
id: "scriptcat-content",
840-
js: [{ code }],
841-
matches: ["<all_urls>"],
842-
allFrames: true,
843-
runAt: "document_start",
844-
world: "USER_SCRIPT",
845-
excludeMatches,
846-
excludeGlobs,
847-
});
848-
}
849835

836+
let retContent: chrome.scripting.RegisteredContentScript[] = [];
837+
let retInject: chrome.userScripts.RegisteredUserScript[] = [];
850838
// inject.js
851839
const injectJs = await this.getInjectJsCode();
852840
if (injectJs) {
853-
const apiScripts = this.compileInjectUserScript(injectJs, messageFlag, {
841+
// 构建inject.js的脚本注册信息
842+
retInject = this.compileInjectUserScript(injectJs, messageFlag, {
854843
excludeMatches,
855844
excludeGlobs,
856845
});
857-
retScript.push(...apiScripts);
846+
}
847+
// Note: Chrome does not support file.js?query
848+
// 注意:Chrome 不支持 file.js?query
849+
if (isFirefox()) {
850+
// 使用 URLSearchParams 避免字符编码问题
851+
retContent = [
852+
{
853+
id: "scriptcat-content",
854+
js: [`/src/content.js?${new URLSearchParams({ usp_flag: messageFlag })}&usp_end`],
855+
matches: ["<all_urls>"],
856+
allFrames: true,
857+
runAt: "document_start",
858+
excludeMatches,
859+
} satisfies chrome.scripting.RegisteredContentScript,
860+
];
861+
} else {
862+
const contentJs = await this.getContentJsCode();
863+
if (contentJs) {
864+
const codeBody = `(function (MessageFlag) {\n${contentJs}\n})('${messageFlag}')`;
865+
const code = `${codeBody}${sourceMapTo("scriptcat-content.js")}\n`;
866+
retInject.push({
867+
id: "scriptcat-content",
868+
js: [{ code }],
869+
matches: ["<all_urls>"],
870+
allFrames: true,
871+
runAt: "document_start",
872+
world: "USER_SCRIPT",
873+
excludeMatches,
874+
excludeGlobs,
875+
} satisfies chrome.userScripts.RegisteredUserScript);
876+
}
858877
}
859878

860-
return retScript;
879+
return { content: retContent, inject: retInject };
861880
}
862881

863882
// 如果是重复注册,需要先调用 unregisterUserscripts
@@ -869,8 +888,8 @@ export class RuntimeService {
869888
if (runtimeGlobal.registered) {
870889
// 异常情况
871890
// 检查scriptcat-content和scriptcat-inject是否存在
872-
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-content", "scriptcat-inject"] });
873-
if (res.length === 2) {
891+
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-inject"] });
892+
if (res.length === 1) {
874893
return;
875894
}
876895
// scriptcat-content/scriptcat-inject不存在的情况
@@ -894,9 +913,9 @@ export class RuntimeService {
894913
const particularScriptList = await this.getParticularScriptList(options);
895914
// getContentAndInjectScript依赖loadScriptMatchInfo
896915
// 需要等getParticularScriptList完成后再执行
897-
const generalScriptList = await this.getContentAndInjectScript(options);
916+
const { inject: injectScriptList, content: contentScriptList } = await this.getContentAndInjectScript(options);
898917

899-
const list: chrome.userScripts.RegisteredUserScript[] = [...particularScriptList, ...generalScriptList];
918+
const list: chrome.userScripts.RegisteredUserScript[] = [...particularScriptList, ...injectScriptList];
900919

901920
runtimeGlobal.registered = true;
902921
try {
@@ -921,6 +940,13 @@ export class RuntimeService {
921940
}
922941
}
923942
}
943+
if (contentScriptList.length > 0) {
944+
try {
945+
await chrome.scripting.registerContentScripts(contentScriptList);
946+
} catch (e: any) {
947+
this.logger.error("register content.js error", Logger.E(e));
948+
}
949+
}
924950
}
925951

926952
// 给指定tab发送消息

src/content.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@ import { CustomEventMessage } from "@Packages/message/custom_event_message";
55
import { Server } from "@Packages/message/server";
66
import ContentRuntime from "./app/service/content/content";
77
import { ScriptExecutor } from "./app/service/content/script_executor";
8-
import { randomMessageFlag } from "./pkg/utils/utils";
8+
import { randomMessageFlag, getUspMessageFlag } from "./pkg/utils/utils";
99
import type { Message } from "@Packages/message/types";
1010

11-
/* global MessageFlag */
11+
// @ts-ignore
12+
const MessageFlag: string | null = (typeof arguments === "object" && arguments?.[0]) || getUspMessageFlag();
1213

13-
if (typeof chrome?.runtime?.onMessage?.addListener !== "function") {
14-
// Firefox MV3 之类好像没有 chrome.runtime.onMessage.addListener ?
14+
if (!MessageFlag) {
15+
console.error("MessageFlag is unavailable.");
16+
} else if (typeof chrome?.runtime?.onMessage?.addListener !== "function") {
17+
// Firefox userScripts.RegisteredUserScript does not provide chrome.runtime.onMessage.addListener
18+
// Firefox scripting.RegisteredContentScript does provide chrome.runtime.onMessage.addListener
19+
// Firefox 的 userScripts.RegisteredUserScript 不提供 chrome.runtime.onMessage.addListener
20+
// Firefox 的 scripting.RegisteredContentScript 提供 chrome.runtime.onMessage.addListener
1521
console.error("chrome.runtime.onMessage.addListener is not a function");
1622
} else {
1723
// 建立与service_worker页面的连接

src/pkg/utils/utils.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ export const deferred = <T = void>(): Deferred<T> => {
3939
return { promise, resolve, reject };
4040
};
4141

42+
export const getUspMessageFlag = () => {
43+
const s = new Error().stack;
44+
if (s) {
45+
const search1 = "content.js?usp_flag=";
46+
const len1 = search1.length;
47+
const idx1 = s.indexOf(search1);
48+
if (idx1 > 0) {
49+
const search2 = "&usp_end";
50+
const idx2 = s.indexOf(search2, idx1 + len1);
51+
if (idx2 > 0) {
52+
const param = s.substring(idx1 + len1, idx2);
53+
try {
54+
// 使用 URLSearchParams 避免字符编码问题
55+
const uspString = `usp_flag=${param}`;
56+
const usp = new URLSearchParams(uspString);
57+
if (usp.size === 1) return usp.get("usp_flag") || null;
58+
} catch (e) {
59+
console.error(e);
60+
}
61+
}
62+
}
63+
}
64+
return null;
65+
};
66+
4267
export function isFirefox() {
4368
//@ts-ignore
4469
return typeof mozInnerScreenX !== "undefined";
@@ -206,7 +231,7 @@ export async function checkUserScriptsAvailable() {
206231
// Method call which throws if API permission or toggle is not enabled.
207232
chrome.userScripts;
208233
const ret: chrome.userScripts.RegisteredUserScript[] | any = await chrome.userScripts.getScripts({
209-
ids: ["scriptcat-content", "undefined-id-3"],
234+
ids: ["scriptcat-inject", "undefined-id-3"],
210235
});
211236
// 返回结果不是阵列的话表示API不可使用
212237
if (ret === undefined || ret === null || typeof ret[Symbol.iterator] !== "function") {
@@ -215,10 +240,10 @@ export async function checkUserScriptsAvailable() {
215240

216241
if (ret[0]) {
217242
// API内部处理实际给予扩展权限才会有返回Script
218-
// 含有 "scriptcat-content" 或 "undefined-id-3"
243+
// 含有 "scriptcat-inject" 或 "undefined-id-3"
219244
return true;
220245
} else {
221-
// 没有 "scriptcat-content" 和 "undefined-id-3"
246+
// 没有 "scriptcat-inject" 和 "undefined-id-3"
222247
// 进行 "undefined-id-3" 的注册反注册测试
223248
// Chrome MV3 的一部分浏览器(如 Vivaldi )没正确处理 MV3 UserScripts API 权限问题 (API内部处理没有给予扩展权限)
224249
// 此时会无法注册 (1. register 报错)

0 commit comments

Comments
 (0)