From 65b231911e66e7e39b18c5c17ed03c95a3a4eb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Wed, 5 Feb 2025 18:01:59 +0800 Subject: [PATCH] =?UTF-8?q?xhr=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/message/client.ts | 2 +- packages/message/server.ts | 7 +++ src/app/service/offscreen/client.ts | 2 +- src/app/service/offscreen/index.ts | 16 ++++- src/app/service/service_worker/index.ts | 74 ++++++++++++++++++++++++ src/app/service/service_worker/script.ts | 17 ++++-- src/manifest.json | 1 + src/runtime/content/gm_api.ts | 51 +++++++++++++++- src/runtime/service_worker/gm_api.ts | 10 +++- src/service_worker.ts | 2 +- src/types/main.d.ts | 29 ++++++++++ 11 files changed, 199 insertions(+), 12 deletions(-) diff --git a/packages/message/client.ts b/packages/message/client.ts index efb64f0..ec41def 100644 --- a/packages/message/client.ts +++ b/packages/message/client.ts @@ -3,7 +3,7 @@ import { Message, MessageConnect } from "./server"; export async function sendMessage(msg: Message, action: string, data?: any): Promise { const res = await msg.sendMessage({ action, data }); - LoggerCore.getInstance().logger().debug("sendMessage", { action, data, res }); + LoggerCore.getInstance().logger().debug("sendMessage", { action, data, response: res }); if (res && res.code) { console.error(res); return Promise.reject(res.message); diff --git a/packages/message/server.ts b/packages/message/server.ts index 6befae3..e8c4acd 100644 --- a/packages/message/server.ts +++ b/packages/message/server.ts @@ -1,3 +1,5 @@ +import LoggerCore from "@App/app/logger/core"; + export interface Message { onConnect(callback: (data: any, con: MessageConnect) => void): void; onMessage(callback: (data: any, sendResponse: (data: any) => void) => void): void; @@ -50,6 +52,8 @@ export class Server { } private messageHandle(msg: string, params: any, sendResponse: (response: any) => void) { + const logger = LoggerCore.getInstance().logger({ env: this.env, msg }); + logger.debug("messageHandle", { params }); const func = this.apiFunctionMap.get(msg); if (func) { try { @@ -65,6 +69,9 @@ export class Server { } catch (e: any) { sendResponse({ code: -1, message: e.message }); } + } else { + sendResponse({ code: -1, message: "no such api" }); + logger.error("no such api"); } } } diff --git a/src/app/service/offscreen/client.ts b/src/app/service/offscreen/client.ts index 6fe3f31..25b5ee1 100644 --- a/src/app/service/offscreen/client.ts +++ b/src/app/service/offscreen/client.ts @@ -20,5 +20,5 @@ export function proxyUpdateRunStatus( msg: WindowMessage, data: { uuid: string; runStatus: SCRIPT_RUN_STATUS; error?: any; nextruntime?: number } ) { - return sendMessageToServiceWorker(msg, "updateRunStatus", data); + return sendMessageToServiceWorker(msg, "serviceWorker/script/updateRunStatus", data); } diff --git a/src/app/service/offscreen/index.ts b/src/app/service/offscreen/index.ts index a23a89e..7adfd51 100644 --- a/src/app/service/offscreen/index.ts +++ b/src/app/service/offscreen/index.ts @@ -37,7 +37,7 @@ export class OffscreenManager { return sendMessage(this.extensionMessage, data.action, data.data); } - initManager() { + async initManager() { // 监听消息 const group = this.api.group("offscreen"); this.windowApi.on("logger", this.logger.bind(this)); @@ -53,5 +53,19 @@ export class OffscreenManager { script.init(); // 转发gm api请求 forwardMessage("serviceWorker/runtime/gmApi", this.windowApi, this.extensionMessage); + // 处理gm xhr请求 + this.api.on("gmXhr", (data) => { + console.log("123"); + }); + // 测试xhr + const ret = await sendMessage(this.extensionMessage, "serviceWorker/testGmApi"); + console.log("test xhr", ret); + const xhr = new XMLHttpRequest(); + xhr.onload = () => { + console.log(xhr); + }; + xhr.open("GET", "https://scriptcat.org/zh-CN"); + + xhr.send(); } } diff --git a/src/app/service/service_worker/index.ts b/src/app/service/service_worker/index.ts index b066ae8..079db03 100644 --- a/src/app/service/service_worker/index.ts +++ b/src/app/service/service_worker/index.ts @@ -31,5 +31,79 @@ export default class ServiceWorkerManager { script.init(); const runtime = new RuntimeService(group.group("runtime"), this.mq, value); runtime.init(); + + // 测试xhr + setTimeout(() => { + chrome.tabs.query( + { + url: chrome.runtime.getURL("src/offscreen.html"), + }, + (result) => { + console.log(result); + } + ); + }, 2000); + group.on("testGmApi", () => { + console.log(chrome.runtime.getURL("src/offscreen.html")); + return new Promise((resolve) => { + chrome.tabs.query({}, (tabs) => { + const excludedTabIds: number[] = []; + tabs.forEach((tab) => { + if (tab.id) { + excludedTabIds.push(tab.id); + } + }); + chrome.declarativeNetRequest.updateSessionRules( + { + removeRuleIds: [100], + addRules: [ + { + id: 100, + priority: 1, + action: { + type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS, + requestHeaders: [ + { + header: "cookie", + operation: chrome.declarativeNetRequest.HeaderOperation.SET, + value: "test=1234314", + }, + { + header: "origin", + operation: chrome.declarativeNetRequest.HeaderOperation.SET, + value: "https://learn.scriptcat.org", + }, + { + header: "user-agent", + operation: chrome.declarativeNetRequest.HeaderOperation.SET, + value: "test", + }, + ], + }, + condition: { + resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST], + urlFilter: "https://scriptcat.org/zh-CN", + excludedTabIds: excludedTabIds, + }, + }, + ], + }, + () => { + resolve(1); + } + ); + }); + }); + }); + chrome.webRequest.onHeadersReceived.addListener( + (details) => { + console.log(details); + }, + { + urls: [""], + types: ["xmlhttprequest"], + }, + ["responseHeaders", "extraHeaders"] + ); } } diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index 183963c..aa80e39 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -236,12 +236,17 @@ export class ScriptService { async updateRunStatus(params: { uuid: string; runStatus: SCRIPT_RUN_STATUS; error?: string; nextruntime?: number }) { this.mq.publish("updateRunStatus", params); - return this.scriptDAO.update(params.uuid, { - runStatus: params.runStatus, - lastruntime: new Date().getTime(), - error: params.error, - nextruntime: params.nextruntime, - }); + if ( + (await this.scriptDAO.update(params.uuid, { + runStatus: params.runStatus, + lastruntime: new Date().getTime(), + error: params.error, + nextruntime: params.nextruntime, + })) === false + ) { + return Promise.reject("update error"); + } + return Promise.resolve(true); } getCode(uuid: string) { diff --git a/src/manifest.json b/src/manifest.json index a4ad442..0d38c42 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -22,6 +22,7 @@ }, "default_locale": "zh_CN", "permissions": [ + "tabs", "storage", "offscreen", "scripting", diff --git a/src/runtime/content/gm_api.ts b/src/runtime/content/gm_api.ts index 9688c18..222cdfb 100644 --- a/src/runtime/content/gm_api.ts +++ b/src/runtime/content/gm_api.ts @@ -3,7 +3,7 @@ import { getMetadataStr, getUserConfigStr, parseUserConfig } from "@App/pkg/util import { ValueUpdateData } from "./exec_script"; import { ExtVersion } from "@App/app/const"; import { storageKey } from "../utils"; -import { Message } from "@Packages/message/server"; +import { Message, MessageConnect } from "@Packages/message/server"; interface ApiParam { depend?: string[]; @@ -178,4 +178,53 @@ export default class GMApi { } return this.sendMessage("GM_log", [message, level, labels]); } + + // 用于脚本跨域请求,需要@connect domain指定允许的域名 + @GMContext.API({ + depend: ["CAT_fetchBlob", "CAT_createBlobUrl", "CAT_fetchDocument"], + }) + public GM_xmlhttpRequest(details: GMTypes.XHRDetails) { + const u = new URL(details.url, window.location.href); + if (details.headers) { + Object.keys(details.headers).forEach((key) => { + if (key.toLowerCase() === "cookie") { + details.cookie = details.headers![key]; + delete details.headers![key]; + } + }); + } + + const param: GMSend.XHRDetails = { + method: details.method, + timeout: details.timeout, + url: u.href, + headers: details.headers, + cookie: details.cookie, + context: details.context, + responseType: details.responseType, + overrideMimeType: details.overrideMimeType, + anonymous: details.anonymous, + user: details.user, + password: details.password, + maxRedirects: details.maxRedirects, + }; + if (!param.headers) { + param.headers = {}; + } + if (details.nocache) { + param.headers["Cache-Control"] = "no-cache"; + } + let connect: MessageConnect; + this.connect("GM_xmlhttpRequest", [param]).then((con) => { + connect = con; + }); + + return { + abort: () => { + if (connect) { + connect.disconnect(); + } + }, + }; + } } diff --git a/src/runtime/service_worker/gm_api.ts b/src/runtime/service_worker/gm_api.ts index 13f27be..fc1cbfe 100644 --- a/src/runtime/service_worker/gm_api.ts +++ b/src/runtime/service_worker/gm_api.ts @@ -29,7 +29,8 @@ export default class GMApi { } handlerRequest(params: Request) { - console.log(params); + console.log(params, arguments); + return null; } @PermissionVerify.API() @@ -43,5 +44,12 @@ export default class GMApi { return this.value.setValue(request.script.uuid, key, value); } + @PermissionVerify.API() + GM_xmlhttpRequest(request: Request) { + // 发送到offscreen, 处理请求 + console.log(request, arguments); + return null; + } + start() {} } diff --git a/src/service_worker.ts b/src/service_worker.ts index 96154a3..b5e9643 100644 --- a/src/service_worker.ts +++ b/src/service_worker.ts @@ -14,7 +14,6 @@ async function hasDocument() { contextTypes: [chrome.runtime.ContextType.OFFSCREEN_DOCUMENT], documentUrls: [offscreenUrl], }); - return existingContexts.length > 0; } @@ -28,6 +27,7 @@ async function setupOffscreenDocument() { creating = chrome.offscreen.createDocument({ url: OFFSCREEN_DOCUMENT_PATH, reasons: [ + chrome.offscreen.Reason.BLOBS, chrome.offscreen.Reason.CLIPBOARD, chrome.offscreen.Reason.DOM_SCRAPING, chrome.offscreen.Reason.LOCAL_STORAGE, diff --git a/src/types/main.d.ts b/src/types/main.d.ts index b271ac0..1663fdf 100644 --- a/src/types/main.d.ts +++ b/src/types/main.d.ts @@ -4,3 +4,32 @@ declare module "*.json"; declare module "*.yaml"; declare let sandbox: Window; + +declare namespace GMSend { + interface XHRDetails { + method?: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS"; + url: string; + headers?: { [key: string]: string }; + data?: string | Array; + cookie?: string; + binary?: boolean; + timeout?: number; + context?: CONTEXT_TYPE; + responseType?: "text" | "arraybuffer" | "blob" | "json" | "document" | "stream"; + overrideMimeType?: string; + anonymous?: boolean; + fetch?: boolean; + user?: string; + password?: string; + nocache?: boolean; + dataType?: "FormData" | "Blob"; + maxRedirects?: number; + } + + interface XHRFormData { + type?: "file" | "text"; + key: string; + val: string; + filename?: string; + } +}