Skip to content

Commit 9f7daab

Browse files
committed
userScripts / scripting API 调整,增强兼容性
1 parent 7d85856 commit 9f7daab

File tree

3 files changed

+89
-54
lines changed

3 files changed

+89
-54
lines changed

src/app/service/service_worker/runtime.ts

Lines changed: 58 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
getMetadataStr,
2323
getUserConfigStr,
2424
obtainBlackList,
25+
isFirefox,
2526
} from "@App/pkg/utils/utils";
2627
import { cacheInstance } from "@App/app/cache";
2728
import { UrlMatch } from "@App/pkg/utils/match";
@@ -333,8 +334,8 @@ export class RuntimeService {
333334

334335
let registered = false;
335336
try {
336-
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-content", "scriptcat-inject"] });
337-
registered = res.length === 2;
337+
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-inject"] });
338+
registered = res.length === 1;
338339
} finally {
339340
// 考虑 UserScripts API 不可使用等情况
340341
runtimeGlobal.registered = registered;
@@ -660,6 +661,7 @@ export class RuntimeService {
660661
runtimeGlobal.messageFlag = this.generateMessageFlag();
661662
await Promise.allSettled([
662663
chrome.userScripts.unregister(),
664+
chrome.scripting.unregisterContentScripts(),
663665
this.localStorageDAO.save({ key: "scriptInjectMessageFlag", value: runtimeGlobal.messageFlag }),
664666
]);
665667
}
@@ -827,32 +829,57 @@ export class RuntimeService {
827829
// do nothing
828830
}
829831
}
830-
const retScript: chrome.userScripts.RegisteredUserScript[] = [];
831-
const contentJs = await this.getContentJsCode();
832-
if (contentJs) {
833-
retScript.push({
834-
id: "scriptcat-content",
835-
js: [{ code: `(function (MessageFlag) {\n${contentJs}\n})('${messageFlag}')` }],
836-
matches: ["<all_urls>"],
837-
allFrames: true,
838-
runAt: "document_start",
839-
world: "USER_SCRIPT",
840-
excludeMatches,
841-
excludeGlobs,
842-
});
843-
}
844832

833+
let retContent: chrome.scripting.RegisteredContentScript[] = [];
834+
let retInject: chrome.userScripts.RegisteredUserScript[] = [];
845835
// inject.js
846836
const injectJs = await this.getInjectJsCode();
847837
if (injectJs) {
848-
const apiScripts = this.compileInjectUserScript(injectJs, messageFlag, {
849-
excludeMatches,
850-
excludeGlobs,
851-
});
852-
retScript.push(...apiScripts);
838+
// 构建inject.js的脚本注册信息
839+
const code = `(function (MessageFlag) {\n${injectJs}\n})('${messageFlag}')`;
840+
retInject = [
841+
{
842+
id: "scriptcat-inject",
843+
js: [{ code }],
844+
matches: ["<all_urls>"],
845+
allFrames: true,
846+
world: "MAIN",
847+
runAt: "document_start",
848+
excludeMatches: excludeMatches,
849+
excludeGlobs: excludeGlobs,
850+
} satisfies chrome.userScripts.RegisteredUserScript,
851+
];
852+
}
853+
// Note: Chrome does not support file.js?query
854+
// 注意:Chrome 不支持 file.js?query
855+
if (isFirefox()) {
856+
retContent = [
857+
{
858+
id: "scriptcat-content",
859+
js: [`/src/content.js?FlagsStart&${messageFlag}&FlagsEnd`],
860+
matches: ["<all_urls>"],
861+
allFrames: true,
862+
runAt: "document_start",
863+
excludeMatches,
864+
} satisfies chrome.scripting.RegisteredContentScript,
865+
];
866+
} else {
867+
const contentJs = await this.getContentJsCode();
868+
if (contentJs) {
869+
retInject.push({
870+
id: "scriptcat-content",
871+
js: [{ code: `(function (MessageFlag) {\n${contentJs}\n})('${messageFlag}')` }],
872+
matches: ["<all_urls>"],
873+
allFrames: true,
874+
runAt: "document_start",
875+
world: "USER_SCRIPT",
876+
excludeMatches,
877+
excludeGlobs,
878+
} satisfies chrome.userScripts.RegisteredUserScript);
879+
}
853880
}
854881

855-
return retScript;
882+
return { content: retContent, inject: retInject };
856883
}
857884

858885
// 如果是重复注册,需要先调用 unregisterUserscripts
@@ -864,8 +891,8 @@ export class RuntimeService {
864891
if (runtimeGlobal.registered) {
865892
// 异常情况
866893
// 检查scriptcat-content和scriptcat-inject是否存在
867-
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-content", "scriptcat-inject"] });
868-
if (res.length === 2) {
894+
const res = await chrome.userScripts.getScripts({ ids: ["scriptcat-inject"] });
895+
if (res.length === 1) {
869896
return;
870897
}
871898
// scriptcat-content/scriptcat-inject不存在的情况
@@ -889,9 +916,9 @@ export class RuntimeService {
889916
const particularScriptList = await this.getParticularScriptList(options);
890917
// getContentAndInjectScript依赖loadScriptMatchInfo
891918
// 需要等getParticularScriptList完成后再执行
892-
const generalScriptList = await this.getContentAndInjectScript(options);
919+
const { inject: injectScripList, content: contentScriptList } = await this.getContentAndInjectScript(options);
893920

894-
const list: chrome.userScripts.RegisteredUserScript[] = [...particularScriptList, ...generalScriptList];
921+
const list: chrome.userScripts.RegisteredUserScript[] = [...particularScriptList, ...injectScripList];
895922

896923
runtimeGlobal.registered = true;
897924
try {
@@ -916,6 +943,11 @@ export class RuntimeService {
916943
}
917944
}
918945
}
946+
try {
947+
await chrome.scripting.registerContentScripts(contentScriptList);
948+
} catch (e: any) {
949+
this.logger.error("register content.js error", Logger.E(e));
950+
}
919951
}
920952

921953
// 给指定tab发送消息
@@ -1285,27 +1317,6 @@ export class RuntimeService {
12851317
return await runScript(this.msgSender, res);
12861318
}
12871319

1288-
compileInjectUserScript(
1289-
injectJs: string,
1290-
messageFlag: string,
1291-
{ excludeMatches, excludeGlobs }: { excludeMatches: string[] | undefined; excludeGlobs: string[] | undefined }
1292-
) {
1293-
// 构建inject.js的脚本注册信息
1294-
const code = `(function (MessageFlag) {\n${injectJs}\n})('${messageFlag}')`;
1295-
const script: chrome.userScripts.RegisteredUserScript = {
1296-
id: "scriptcat-inject",
1297-
js: [{ code }],
1298-
matches: ["<all_urls>"],
1299-
allFrames: true,
1300-
world: "MAIN",
1301-
runAt: "document_start",
1302-
excludeMatches: excludeMatches,
1303-
excludeGlobs: excludeGlobs,
1304-
};
1305-
1306-
return [script] as chrome.userScripts.RegisteredUserScript[];
1307-
}
1308-
13091320
scriptMatchEntry(
13101321
scriptRes: ScriptRunResource,
13111322
o: {

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: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ export const deferred = <T = void>(): Deferred<T> => {
3737
reject = rej;
3838
});
3939
return { promise, resolve, reject };
40+
}
41+
42+
export const getUspMessageFlag = () => {
43+
const s = new Error().stack;
44+
if (s) {
45+
const search1 = "content.js?FlagsStart&";
46+
const len1 = search1.length;
47+
const idx1 = s.indexOf(search1);
48+
if (idx1 > 0) {
49+
const search2 = "&FlagsEnd";
50+
const idx2 = s.indexOf(search2, idx1 + len1);
51+
if (idx2 > 0) {
52+
const param = s.substring(idx1 + len1, idx2);
53+
return param;
54+
}
55+
}
56+
}
57+
return null;
4058
};
4159

4260
export function isFirefox() {
@@ -206,7 +224,7 @@ export async function checkUserScriptsAvailable() {
206224
// Method call which throws if API permission or toggle is not enabled.
207225
chrome.userScripts;
208226
const ret: chrome.userScripts.RegisteredUserScript[] | any = await chrome.userScripts.getScripts({
209-
ids: ["scriptcat-content", "undefined-id-3"],
227+
ids: ["scriptcat-inject", "undefined-id-3"],
210228
});
211229
// 返回结果不是阵列的话表示API不可使用
212230
if (ret === undefined || ret === null || typeof ret[Symbol.iterator] !== "function") {
@@ -215,10 +233,10 @@ export async function checkUserScriptsAvailable() {
215233

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

0 commit comments

Comments
 (0)