From 19651371914c82be6d4751a06c7adee576ae7335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Mon, 14 Apr 2025 15:02:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E5=8A=A0=E8=BD=BD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/logger/db_writer.ts | 20 +++--- src/app/logger/logger.ts | 15 +++-- src/app/service/content/content.ts | 5 ++ src/app/service/content/exec_script.ts | 2 +- src/app/service/service_worker/popup.ts | 1 - src/app/service/service_worker/resource.ts | 24 ++++++-- src/app/service/service_worker/runtime.ts | 29 +++++++-- src/offscreen.ts | 5 +- src/pkg/utils/match.test.ts | 71 ++++++++++++++++++---- src/pkg/utils/match.ts | 34 +++++++++-- src/service_worker.ts | 5 +- 11 files changed, 163 insertions(+), 48 deletions(-) diff --git a/src/app/logger/db_writer.ts b/src/app/logger/db_writer.ts index 7be008c..87fda24 100644 --- a/src/app/logger/db_writer.ts +++ b/src/app/logger/db_writer.ts @@ -9,13 +9,17 @@ export default class DBWriter implements Writer { this.dao = dao; } - write(level: LogLevel, message: string, label: LogLabel): void { - this.dao.save({ - id: 0, - level, - message, - label, - createtime: new Date().getTime(), - }); + async write(level: LogLevel, message: string, label: LogLabel): Promise { + try { + await this.dao.save({ + id: 0, + level, + message, + label, + createtime: new Date().getTime(), + }); + } catch (e) { + console.error("DBWriter error", e); + } } } diff --git a/src/app/logger/logger.ts b/src/app/logger/logger.ts index 9d67d9f..ed946f1 100644 --- a/src/app/logger/logger.ts +++ b/src/app/logger/logger.ts @@ -32,28 +32,27 @@ export default class Logger { } log(level: LogLevel, message: string, ...label: LogLabel[]) { + const newLabel = buildLabel(this.label, label); if (levelNumber[level] >= levelNumber[this.core.level]) { - this.core.writer.write(level, message, buildLabel(this.label, label)); + this.core.writer.write(level, message, newLabel); } if (this.core.debug !== "none" && levelNumber[level] >= levelNumber[this.core.debug]) { if (typeof message === "object") { message = JSON.stringify(message); } - const msg = `${dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss")} [${level}] msg=${message} label=${JSON.stringify( - buildLabel(this.label, label) - )}`; + const msg = `${dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss")} [${level}] ${message}`; switch (level) { case "error": - console.error(msg); + console.error(msg, newLabel); break; case "warn": - console.warn(msg); + console.warn(msg, newLabel); break; case "trace": - console.info(msg); + console.info(msg, newLabel); break; default: - console.info(msg); + console.info(msg, newLabel); break; } } diff --git a/src/app/service/content/content.ts b/src/app/service/content/content.ts index bc35274..fd7a408 100644 --- a/src/app/service/content/content.ts +++ b/src/app/service/content/content.ts @@ -79,6 +79,11 @@ export default class ContentRuntime { const nodeId = (this.msg as CustomEventMessage).sendRelatedTarget(el); return nodeId; } + case "GM_log": + // 拦截GM_log,打印到控制台 + // 由于某些页面会处理掉console.log,所以丢到这里来打印 + console.log(...data.params); + break; } return false; } diff --git a/src/app/service/content/exec_script.ts b/src/app/service/content/exec_script.ts index 5149a3c..8b4ecf7 100644 --- a/src/app/service/content/exec_script.ts +++ b/src/app/service/content/exec_script.ts @@ -46,7 +46,7 @@ export default class ExecScript { this.scriptRes = scriptRes; this.logger = LoggerCore.getInstance().logger({ component: "exec", - script: this.scriptRes.uuid, + uuid: this.scriptRes.uuid, name: this.scriptRes.name, }); this.GM_info = GMApi.GM_info(this.scriptRes); diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index 677ca1d..21027dd 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -198,7 +198,6 @@ export class PopupService { scriptMenu.push(script); } }); - console.log("popup脚本菜单", runScript); // 后台脚本只显示开启或者运行中的脚本 return { scriptList: scriptMenu, backScriptList: await this.getScriptMenu(-1) }; } diff --git a/src/app/service/service_worker/resource.ts b/src/app/service/service_worker/resource.ts index 81cbd91..722807b 100644 --- a/src/app/service/service_worker/resource.ts +++ b/src/app/service/service_worker/resource.ts @@ -193,6 +193,19 @@ export class ResourceService { (u.hash.sha512 && u.hash.sha512 !== resource.hash.sha512) ) { resource.content = `console.warn("ScriptCat: couldn't load resource from URL ${url} due to a SRI error ");`; + // 尝试重新加载 + this.loadByUrl(u.url, resource.type).then((reloadRes) => { + this.logger.info("reload resource success", { + url: u.url, + hash: { + expected: u.hash, + old: resource.hash, + new: reloadRes.hash, + }, + }); + reloadRes.updatetime = new Date().getTime(); + this.resourceDAO.save(reloadRes); + }); } } return Promise.resolve(resource); @@ -214,12 +227,13 @@ export class ResourceService { sha512: "", }); } else { + const wordArray = crypto.lib.WordArray.create(reader.result); resolve({ - md5: crypto.MD5(reader.result).toString(), - sha1: crypto.SHA1(reader.result).toString(), - sha256: crypto.SHA256(reader.result).toString(), - sha384: crypto.SHA384(reader.result).toString(), - sha512: crypto.SHA512(reader.result).toString(), + md5: crypto.MD5(wordArray).toString(), + sha1: crypto.SHA1(wordArray).toString(), + sha256: crypto.SHA256(wordArray).toString(), + sha384: crypto.SHA384(wordArray).toString(), + sha512: crypto.SHA512(wordArray).toString(), }); } }; diff --git a/src/app/service/service_worker/runtime.ts b/src/app/service/service_worker/runtime.ts index 39b6a1e..fc338fd 100644 --- a/src/app/service/service_worker/runtime.ts +++ b/src/app/service/service_worker/runtime.ts @@ -22,6 +22,8 @@ import { ExtensionContentMessageSend } from "@Packages/message/extension_message import { sendMessage } from "@Packages/message/client"; import { compileInjectScript } from "../content/utils"; import { PopupService } from "./popup"; +import Logger from "@App/app/logger/logger"; +import LoggerCore from "@App/app/logger/core"; // 为了优化性能,存储到缓存时删除了code与value export interface ScriptMatchInfo extends ScriptRunResouce { @@ -389,22 +391,27 @@ export class RuntimeService { world: "MAIN", }; + // 排除由loadPage时决定, 不使用userScript的excludeMatches处理 if (script.metadata["exclude"]) { const excludeMatches = script.metadata["exclude"]; - const result = dealPatternMatches(excludeMatches); + const result = dealPatternMatches(excludeMatches, { + exclude: true, + }); - registerScript.excludeMatches = result.patternResult; + // registerScript.excludeMatches = result.patternResult; scriptMatchInfo.excludeMatches = result.result; } // 自定义排除 if (script.selfMetadata && script.selfMetadata.exclude) { const excludeMatches = script.selfMetadata.exclude; - const result = dealPatternMatches(excludeMatches); + const result = dealPatternMatches(excludeMatches, { + exclude: true, + }); if (!registerScript.excludeMatches) { registerScript.excludeMatches = []; } - registerScript.excludeMatches.push(...result.patternResult); + // registerScript.excludeMatches.push(...result.patternResult); scriptMatchInfo.customizeExcludeMatches = result.result; } @@ -419,10 +426,22 @@ export class RuntimeService { if (script.metadata["run-at"]) { registerScript.runAt = getRunAt(script.metadata["run-at"]); } + console.log("registerScript", script.name, registerScript, scriptMatchInfo); if (await Cache.getInstance().get("registryScript:" + script.uuid)) { await chrome.userScripts.update([registerScript]); } else { - await chrome.userScripts.register([registerScript]); + await chrome.userScripts.register([registerScript], () => { + if (chrome.runtime.lastError) { + LoggerCore.logger().error("registerScript error", { + error: chrome.runtime.lastError, + name: script.name, + registerMatch: { + matches: registerScript.matches, + excludeMatches: registerScript.excludeMatches, + }, + }); + } + }); } await Cache.getInstance().set("registryScript:" + script.uuid, true); } diff --git a/src/offscreen.ts b/src/offscreen.ts index 5e529b9..ca8bc1d 100644 --- a/src/offscreen.ts +++ b/src/offscreen.ts @@ -4,9 +4,10 @@ import DBWriter from "./app/logger/db_writer"; import { LoggerDAO } from "./app/repo/logger"; import { OffscreenManager } from "./app/service/offscreen"; +// 初始化数据库 +migrate(); + function main() { - // 初始化数据库 - migrate(); // 初始化日志组件 const loggerCore = new LoggerCore({ writer: new DBWriter(new LoggerDAO()), diff --git a/src/pkg/utils/match.test.ts b/src/pkg/utils/match.test.ts index a0fb675..52cb910 100644 --- a/src/pkg/utils/match.test.ts +++ b/src/pkg/utils/match.test.ts @@ -43,10 +43,10 @@ describe("UrlMatch-google-error", () => { url.add("https://*foo/bar", "ok1"); }).toThrow(Error); }); + // 从v0.17.0开始允许这种 it("error-2", () => { - expect(() => { - url.add("https://foo.*.bar/baz", "ok1"); - }).toThrow(Error); + url.add("https://foo.*.bar/baz", "ok1"); + expect(url.match("https://foo.api.bar/baz")).toEqual(["ok1"]); }); it("error-3", () => { expect(() => { @@ -70,6 +70,13 @@ describe("UrlMatch-search", () => { expect(url.match("https://bbs.tampermonkey.net.cn/")).toEqual(["ok2"]); expect(url.match("https://bbs.tampermonkey.net.cn/foo/bar.html")).toEqual([]); }); + it("http://api.*.example.com/*", () => { + const url = new UrlMatch(); + url.add("http://api.*.example.com/*", "ok1"); + expect(url.match("http://api.foo.example.com/")).toEqual(["ok1"]); + expect(url.match("http://api.bar.example.com/")).toEqual(["ok1"]); + expect(url.match("http://api.example.com/")).toEqual([]); + }); }); describe("UrlMatch-port1", () => { @@ -106,13 +113,47 @@ describe("UrlMatch-port2", () => { // https://developer.chrome.com/docs/extensions/mv3/match_patterns/ describe("dealPatternMatches", () => { it("https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns?hl=zh-cn#examples", () => { - const matches = dealPatternMatches(["https://*/*", "http://127.0.0.1/*", "http://127.0.0.1/"]); - expect(matches.patternResult).toEqual(["https://*/*", "http://127.0.0.1/*", "http://127.0.0.1/"]); + const matches = dealPatternMatches([ + "https://*/*", + "http://127.0.0.1/*", + "http://127.0.0.1/", + "https://*.example.com/*", + ]); + expect(matches.patternResult).toEqual([ + "https://*/*", + "http://127.0.0.1/*", + "http://127.0.0.1/", + "https://*.example.com/*", + ]); }); // 处理一些特殊情况 - it("*://link.17173.com*", () => { - const matches = dealPatternMatches(["*://link.17173.com*"]); - expect(matches.patternResult).toEqual(["*://link.17173.com/*"]); + it("特殊情况", () => { + const matches = dealPatternMatches([ + "*://www.example.com*", + "*://api.*.example.com/*", + "*://api.*.*.example.com/*", + "*://*example.com/*", + ]); + expect(matches.patternResult).toEqual([ + "*://www.example.com/*", + "*://*.example.com/*", + "*://*.example.com/*", + "*://example.com/*", + ]); + expect(matches.result).toEqual([ + "*://www.example.com*", + "*://api.*.example.com/*", + "*://api.*.*.example.com/*", + "*://*example.com/*", + ]); + }); + it("特殊情况-exclude", () => { + const matches = dealPatternMatches(["*://api.*.example.com/*", "*://api.*.*.example.com/*"], { + exclude: true, + }); + console.log(matches); + expect(matches.patternResult).toEqual(["*://example.com/*", "*://example.com/*"]); + expect(matches.result).toEqual(["*://api.*.example.com/*", "*://api.*.*.example.com/*"]); }); }); @@ -146,11 +187,19 @@ describe("parsePatternMatchesURL", () => { path: "*", }); }); - it("*://link.17173.com*", () => { - const matches = parsePatternMatchesURL("*://link.17173.com*"); + it("*://www.example.com*", () => { + const matches = parsePatternMatchesURL("*://www.example.com*"); expect(matches).toEqual({ scheme: "*", - host: "link.17173.com", + host: "www.example.com", + path: "*", + }); + }); + it("*://api.*.example.com/*", () => { + const matches = parsePatternMatchesURL("*://api.*.example.com/*"); + expect(matches).toEqual({ + scheme: "*", + host: "*.example.com", path: "*", }); }); diff --git a/src/pkg/utils/match.ts b/src/pkg/utils/match.ts index 20664ad..68406ce 100644 --- a/src/pkg/utils/match.ts +++ b/src/pkg/utils/match.ts @@ -69,8 +69,6 @@ export default class Match { if (!u.host.endsWith(":*")) { u.host = u.host.substring(0, u.host.length - 1); } - } else if (pos !== -1 && pos !== 0) { - return ""; } u.host = u.host.replace(/\*/g, "[^/]*?"); // 处理 *.开头 @@ -225,7 +223,12 @@ export interface PatternMatchesUrl { } // 解析URL, 根据https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns?hl=zh-cn进行处理 -export function parsePatternMatchesURL(url: string): PatternMatchesUrl | undefined { +export function parsePatternMatchesURL( + url: string, + options?: { + exclude?: boolean; + } +): PatternMatchesUrl | undefined { let result: PatternMatchesUrl | undefined; const match = /^(.+?):\/\/(.*?)(\/(.*?)(\?.*?|)|)$/.exec(url); if (match) { @@ -260,17 +263,38 @@ export function parsePatternMatchesURL(url: string): PatternMatchesUrl | undefin if (result.host.endsWith("*")) { result.host = result.host.slice(0, -1); } + // 处理 www.*.example.com 的情况为 *.example.com + const pos = result.host.lastIndexOf("*"); + if (pos > 0 && pos < result.host.length - 1) { + if (options && options.exclude) { + // 如果是exclude, 按最小匹配处理 + // 包括*也去掉 + result.host = result.host.substring(pos + 1); + if (result.host.startsWith(".")) { + result.host = result.host.substring(1); + } + } else { + // 如果不是exclude + // 将*前面的全部去掉 + result.host = result.host.substring(pos); + } + } } } return result; } // 处理油猴的match和include为chrome的pattern-matche -export function dealPatternMatches(matches: string[]) { +export function dealPatternMatches( + matches: string[], + options?: { + exclude?: boolean; + } +) { const patternResult: string[] = []; const result: string[] = []; for (let i = 0; i < matches.length; i++) { - const url = parsePatternMatchesURL(matches[i]); + const url = parsePatternMatchesURL(matches[i], options); if (url) { patternResult.push(`${url.scheme}://${url.host}/${url.path}`); result.push(matches[i]); diff --git a/src/service_worker.ts b/src/service_worker.ts index cb90b48..42f350f 100644 --- a/src/service_worker.ts +++ b/src/service_worker.ts @@ -8,6 +8,9 @@ import { Server } from "@Packages/message/server"; import { MessageQueue } from "@Packages/message/message_queue"; import { ServiceWorkerMessageSend } from "@Packages/message/window_message"; +// 初始化数据库 +migrate(); + const OFFSCREEN_DOCUMENT_PATH = "src/offscreen.html"; let creating: Promise | null; @@ -46,8 +49,6 @@ async function setupOffscreenDocument() { } async function main() { - // 初始化数据库 - migrate(); // 初始化日志组件 const loggerCore = new LoggerCore({ writer: new DBWriter(new LoggerDAO()),