实现gmxhr
Some checks failed
build / Build (push) Failing after 6s
test / Run tests (push) Failing after 8s
Some checks failed
build / Build (push) Failing after 6s
test / Run tests (push) Failing after 8s
This commit is contained in:
parent
a8054451ac
commit
1a55bb348f
@ -13,14 +13,21 @@ const data = new FormData();
|
||||
|
||||
data.append("username", "admin");
|
||||
|
||||
data.append(
|
||||
"file",
|
||||
new File(["foo"], "foo.txt", {
|
||||
type: "text/plain",
|
||||
})
|
||||
);
|
||||
|
||||
GM_xmlhttpRequest({
|
||||
url: "https://bbs.tampermonkey.net.cn/",
|
||||
method: "POST",
|
||||
responseType: "blob",
|
||||
data: data,
|
||||
headers: {
|
||||
"referer": "http://www.example.com/",
|
||||
"origin": "www.example.com",
|
||||
referer: "http://www.example.com/",
|
||||
origin: "www.example.com",
|
||||
// 为空将不会发送此header
|
||||
"sec-ch-ua-mobile": "",
|
||||
},
|
||||
|
21
package.json
21
package.json
@ -31,7 +31,7 @@
|
||||
"pako": "^2.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^15.1.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-icons": "^5.3.0",
|
||||
"react-joyride": "^2.9.3",
|
||||
"react-redux": "^9.1.2",
|
||||
@ -41,10 +41,10 @@
|
||||
"yaml": "^2.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.6",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@rspack/cli": "^1.2.3",
|
||||
"@rspack/core": "^1.2.3",
|
||||
"@eslint/compat": "^1.2.8",
|
||||
"@eslint/js": "^9.24.0",
|
||||
"@rspack/cli": "^1.3.2",
|
||||
"@rspack/core": "^1.3.2",
|
||||
"@rspack/plugin-react-refresh": "^1.0.1",
|
||||
"@types/chrome": "^0.0.279",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
@ -57,22 +57,21 @@
|
||||
"@unocss/postcss": "0.65.0-beta.2",
|
||||
"@vitest/coverage-v8": "2.1.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
"globals": "^15.14.0",
|
||||
"globals": "^16.0.0",
|
||||
"jsdom": "^25.0.1",
|
||||
"mock-xmlhttprequest": "^8.4.1",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier": "^3.5.3",
|
||||
"react-refresh": "^0.16.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.22.0",
|
||||
"typescript-eslint": "^8.29.0",
|
||||
"unocss": "0.65.0-beta.2",
|
||||
"vitest": "^2.1.4"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export class CustomEventMessage implements Message {
|
||||
EE: EventEmitter = new EventEmitter();
|
||||
|
||||
// 关联dom目标
|
||||
relatedTarget: Map<number, Element> = new Map();
|
||||
relatedTarget: Map<number, Document> = new Map();
|
||||
|
||||
constructor(
|
||||
protected flag: string,
|
||||
@ -25,7 +25,7 @@ export class CustomEventMessage implements Message {
|
||||
) {
|
||||
window.addEventListener((isContent ? "ct" : "fd") + flag, (event) => {
|
||||
if (event instanceof MouseEvent) {
|
||||
this.relatedTarget.set(event.clientX, <Element>event.relatedTarget);
|
||||
this.relatedTarget.set(event.clientX, <Document>event.relatedTarget);
|
||||
return;
|
||||
} else if (event instanceof CustomEvent) {
|
||||
this.messageHandle(event.detail, new CustomEventPostMessage(this));
|
||||
@ -130,4 +130,10 @@ export class CustomEventMessage implements Message {
|
||||
this.nativeSend(body);
|
||||
});
|
||||
}
|
||||
|
||||
getAndDelRelatedTarget(id: number) {
|
||||
const target = this.relatedTarget.get(id);
|
||||
this.relatedTarget.delete(id);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
@ -114,9 +114,15 @@ export class Group {
|
||||
}
|
||||
|
||||
// 转发消息
|
||||
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend) {
|
||||
from.on(path, (params, fromCon) => {
|
||||
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) {
|
||||
from.on(path, async (params, fromCon) => {
|
||||
console.log("forwardMessage", path, prefix, params);
|
||||
if (middleware) {
|
||||
const resp = await middleware(params, new GetSender(fromCon));
|
||||
if (resp !== false) {
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
if (fromCon) {
|
||||
const fromConnect = fromCon.getConnect();
|
||||
to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => {
|
||||
|
1288
pnpm-lock.yaml
generated
1288
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,6 @@ import * as path from "path";
|
||||
import { defineConfig } from "@rspack/cli";
|
||||
import { rspack } from "@rspack/core";
|
||||
import { version } from "./package.json";
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const isBeta = version.includes("-");
|
||||
@ -174,11 +172,6 @@ export default defineConfig({
|
||||
minify: true,
|
||||
chunks: ["sandbox"],
|
||||
}),
|
||||
new CompressionPlugin({
|
||||
test: /ts.worker.js$/,
|
||||
filename: () => "ts.worker.js",
|
||||
deleteOriginalAssets: true,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
optimization: {
|
||||
minimizer: [
|
||||
@ -187,7 +180,6 @@ export default defineConfig({
|
||||
minimizerOptions: { targets },
|
||||
}),
|
||||
],
|
||||
realContentHash: true,
|
||||
},
|
||||
experiments: {
|
||||
css: true,
|
||||
|
@ -1,9 +1,17 @@
|
||||
import LoggerCore from "@App/app/logger/core";
|
||||
import Logger from "@App/app/logger/logger";
|
||||
import { GetSender, Group, MessageConnect } from "@Packages/message/server";
|
||||
|
||||
export default class GMApi {
|
||||
constructor(private group: Group) {}
|
||||
|
||||
dealXhrResponse(con: MessageConnect, details: GMSend.XHRDetails, event: string, xhr: XMLHttpRequest, data?: any) {
|
||||
async dealXhrResponse(
|
||||
con: MessageConnect,
|
||||
details: GMSend.XHRDetails,
|
||||
event: string,
|
||||
xhr: XMLHttpRequest,
|
||||
data?: any
|
||||
) {
|
||||
const finalUrl = xhr.responseURL || details.url;
|
||||
// 判断是否有headerFlag-final-url,有则替换finalUrl
|
||||
let response: GMTypes.XHRResponse = {
|
||||
@ -11,9 +19,56 @@ export default class GMApi {
|
||||
readyState: <any>xhr.readyState,
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText,
|
||||
// header由service_worker处理
|
||||
// responseHeaders: xhr.getAllResponseHeaders().replace(removeXCat, ""),
|
||||
responseType: details.responseType,
|
||||
};
|
||||
if (xhr.readyState === 4) {
|
||||
const responseType = details.responseType?.toLowerCase();
|
||||
if (responseType === "arraybuffer" || responseType === "blob") {
|
||||
let blob: Blob;
|
||||
if (xhr.response instanceof ArrayBuffer) {
|
||||
blob = new Blob([xhr.response]);
|
||||
response.response = URL.createObjectURL(blob);
|
||||
} else {
|
||||
blob = <Blob>xhr.response;
|
||||
response.response = URL.createObjectURL(blob);
|
||||
}
|
||||
try {
|
||||
if (xhr.getResponseHeader("Content-Type")?.indexOf("text") !== -1) {
|
||||
// 如果是文本类型,则尝试转换为文本
|
||||
response.responseText = await blob.text();
|
||||
}
|
||||
} catch (e) {
|
||||
LoggerCore.logger(Logger.E(e)).error("GM XHR getResponseHeader error");
|
||||
}
|
||||
setTimeout(() => {
|
||||
URL.revokeObjectURL(<string>response.response);
|
||||
}, 60 * 1000);
|
||||
} else if (response.responseType === "json") {
|
||||
try {
|
||||
response.response = JSON.parse(xhr.responseText);
|
||||
} catch (e) {
|
||||
LoggerCore.logger(Logger.E(e)).error("GM XHR JSON parse error");
|
||||
}
|
||||
try {
|
||||
response.responseText = xhr.responseText;
|
||||
} catch (e) {
|
||||
LoggerCore.logger(Logger.E(e)).error("GM XHR getResponseText error");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
response.response = xhr.response;
|
||||
} catch (e) {
|
||||
LoggerCore.logger(Logger.E(e)).error("GM XHR response error");
|
||||
}
|
||||
try {
|
||||
response.responseText = xhr.responseText || undefined;
|
||||
} catch (e) {
|
||||
LoggerCore.logger(Logger.E(e)).error("GM XHR getResponseText error");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data) {
|
||||
response = Object.assign(response, data);
|
||||
}
|
||||
@ -24,16 +79,36 @@ export default class GMApi {
|
||||
return response;
|
||||
}
|
||||
|
||||
xmlHttpRequest(details: GMSend.XHRDetails, sender: GetSender) {
|
||||
CAT_fetch(details: GMSend.XHRDetails, sender: GetSender) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
async xmlHttpRequest(details: GMSend.XHRDetails, sender: GetSender) {
|
||||
if (details.responseType === "stream") {
|
||||
// 只有fetch支持ReadableStream
|
||||
return this.CAT_fetch(details, sender);
|
||||
}
|
||||
const xhr = new XMLHttpRequest();
|
||||
const con = sender.getConnect();
|
||||
xhr.open(details.method || "GET", details.url);
|
||||
xhr.open(details.method || "GET", details.url, true, details.user || "", details.password || "");
|
||||
// 添加header
|
||||
if (details.headers) {
|
||||
for (const key in details.headers) {
|
||||
xhr.setRequestHeader(key, details.headers[key]);
|
||||
}
|
||||
}
|
||||
//超时时间
|
||||
if (details.timeout) {
|
||||
xhr.timeout = details.timeout;
|
||||
}
|
||||
if (details.overrideMimeType) {
|
||||
xhr.overrideMimeType(details.overrideMimeType);
|
||||
}
|
||||
//设置响应类型
|
||||
if (details.responseType !== "json") {
|
||||
xhr.responseType = details.responseType || "";
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
this.dealXhrResponse(con, details, "onload", xhr);
|
||||
};
|
||||
@ -65,14 +140,32 @@ export default class GMApi {
|
||||
xhr.ontimeout = () => {
|
||||
con?.sendMessage({ action: "ontimeout", data: {} });
|
||||
};
|
||||
//处理数据
|
||||
if (details.dataType === "FormData") {
|
||||
const data = new FormData();
|
||||
if (details.data && details.data instanceof Array) {
|
||||
await Promise.all(
|
||||
details.data.map(async (val: GMSend.XHRFormData) => {
|
||||
if (val.type === "file") {
|
||||
const file = new File([await (await fetch(val.val)).blob()], val.filename!);
|
||||
data.append(val.key, file, val.filename);
|
||||
} else {
|
||||
data.append(val.key, val.val);
|
||||
}
|
||||
})
|
||||
);
|
||||
xhr.send(data);
|
||||
}
|
||||
} else if (details.dataType === "Blob") {
|
||||
if (!details.data) {
|
||||
throw new Error("Blob data is empty");
|
||||
}
|
||||
const resp = await (await fetch(<string>details.data)).blob();
|
||||
xhr.send(resp);
|
||||
} else {
|
||||
xhr.send(<string>details.data);
|
||||
}
|
||||
|
||||
if (details.timeout) {
|
||||
xhr.timeout = details.timeout;
|
||||
}
|
||||
if (details.overrideMimeType) {
|
||||
xhr.overrideMimeType(details.overrideMimeType);
|
||||
}
|
||||
xhr.send();
|
||||
con?.onDisconnect(() => {
|
||||
xhr.abort();
|
||||
});
|
||||
|
@ -52,20 +52,5 @@ export class OffscreenManager {
|
||||
|
||||
const gmApi = new GMApi(this.windowApi.group("gmApi"));
|
||||
gmApi.init();
|
||||
|
||||
// // 处理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();
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ export class Runtime {
|
||||
crontabScript(script: ScriptRunResouce) {
|
||||
// 执行定时脚本 运行表达式
|
||||
if (!script.metadata.crontab) {
|
||||
throw new Error("错误的crontab表达式");
|
||||
throw new Error(script.name + " - 错误的crontab表达式");
|
||||
}
|
||||
// 如果有nextruntime,则加入重试队列
|
||||
this.joinRetryList(script);
|
||||
|
@ -95,11 +95,21 @@ export default class GMApi {
|
||||
] as chrome.declarativeNetRequest.ModifyHeaderInfo[];
|
||||
Object.keys(headers).forEach((key) => {
|
||||
const lowKey = key.toLowerCase();
|
||||
if (unsafeHeaders[lowKey] || lowKey.startsWith("sec-") || lowKey.startsWith("proxy-")) {
|
||||
if (headers[key]) {
|
||||
if (unsafeHeaders[lowKey] || lowKey.startsWith("sec-") || lowKey.startsWith("proxy-")) {
|
||||
if (headers[key]) {
|
||||
requestHeaders.push({
|
||||
header: key,
|
||||
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
||||
value: headers[key],
|
||||
});
|
||||
}
|
||||
delete headers[key];
|
||||
}
|
||||
} else {
|
||||
requestHeaders.push({
|
||||
header: key,
|
||||
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
||||
value: headers[key],
|
||||
operation: chrome.declarativeNetRequest.HeaderOperation.REMOVE,
|
||||
});
|
||||
delete headers[key];
|
||||
}
|
||||
@ -125,7 +135,6 @@ export default class GMApi {
|
||||
requestMethods: [(params.method || "GET").toLowerCase() as chrome.declarativeNetRequest.RequestMethod],
|
||||
excludedTabIds: excludedTabIds,
|
||||
};
|
||||
console.log(rule);
|
||||
await chrome.declarativeNetRequest.updateSessionRules({
|
||||
removeRuleIds: [ruleId],
|
||||
addRules: [rule],
|
||||
@ -135,9 +144,9 @@ export default class GMApi {
|
||||
|
||||
gmXhrHeadersReceived: EventEmitter = new EventEmitter();
|
||||
|
||||
// TODO: maxRedirects实现
|
||||
@PermissionVerify.API()
|
||||
async GM_xmlhttpRequest(request: Request, con: GetSender) {
|
||||
console.log("GM XHR", request);
|
||||
if (request.params.length === 0) {
|
||||
return Promise.reject(new Error("param is failed"));
|
||||
}
|
||||
@ -151,7 +160,6 @@ export default class GMApi {
|
||||
}
|
||||
params.headers["X-Scriptcat-GM-XHR-Request-Id"] = requestId.toString();
|
||||
params.headers = await this.buildDNRRule(requestId, request.params[0]);
|
||||
console.log(" params.headers", params.headers);
|
||||
let responseHeader = "";
|
||||
// 等待response
|
||||
this.gmXhrHeadersReceived.addListener(
|
||||
@ -160,6 +168,7 @@ export default class GMApi {
|
||||
details.responseHeaders?.forEach((header) => {
|
||||
responseHeader += header.name + ": " + header.value + "\n";
|
||||
});
|
||||
this.gmXhrHeadersReceived.removeAllListeners("headersReceived:" + requestId);
|
||||
}
|
||||
);
|
||||
// 再发送到offscreen, 处理请求
|
||||
|
@ -2,7 +2,7 @@ import dts from "@App/types/scriptcat.d.ts";
|
||||
import { languages } from "monaco-editor";
|
||||
import pako from "pako";
|
||||
import Cache from "@App/app/cache";
|
||||
import { isFirefox } from "./utils";
|
||||
import { isDebug, isFirefox } from "./utils";
|
||||
import EventEmitter from "eventemitter3";
|
||||
|
||||
// 注册eslint
|
||||
@ -15,7 +15,12 @@ export default function registerEditor() {
|
||||
fetch(chrome.runtime.getURL(`/src/ts.worker.js${isFirefox() ? ".gz" : ""}`))
|
||||
.then((resp) => resp.blob())
|
||||
.then(async (blob) => {
|
||||
const result = pako.inflate(await blob.arrayBuffer());
|
||||
let result: ArrayBuffer;
|
||||
if (isDebug()) {
|
||||
result = await blob.arrayBuffer();
|
||||
} else {
|
||||
result = pako.inflate(await blob.arrayBuffer());
|
||||
}
|
||||
// @ts-ignore
|
||||
window.tsUrl = URL.createObjectURL(new Blob([result]));
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
||||
import { Client } from "@Packages/message/client";
|
||||
import { forwardMessage, Message, MessageSend, Server } from "@Packages/message/server";
|
||||
import { forwardMessage, GetSender, Message, MessageSend, Server } from "@Packages/message/server";
|
||||
|
||||
// content页的处理
|
||||
export default class ContentRuntime {
|
||||
@ -17,7 +17,43 @@ export default class ContentRuntime {
|
||||
this.msg.onConnect((msg, connect) => {
|
||||
console.log(msg, connect);
|
||||
});
|
||||
forwardMessage("serviceWorker", "runtime/gmApi", this.server, this.send);
|
||||
forwardMessage(
|
||||
"serviceWorker",
|
||||
"runtime/gmApi",
|
||||
this.server,
|
||||
this.send,
|
||||
(data: { api: string; params: any }, con: GetSender) => {
|
||||
// 拦截关注的action
|
||||
console.log("拦截", data);
|
||||
switch (data.api) {
|
||||
case "CAT_createBlobUrl": {
|
||||
const file = data.params[0] as File;
|
||||
const url = URL.createObjectURL(file);
|
||||
setTimeout(() => {
|
||||
URL.revokeObjectURL(url);
|
||||
}, 60 * 1000);
|
||||
return Promise.resolve(url);
|
||||
}
|
||||
case "CAT_fetchBlob": {
|
||||
return fetch(data.params[0]).then((res) => res.blob());
|
||||
}
|
||||
case "CAT_fetchDocument": {
|
||||
return new Promise((resolve) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "document";
|
||||
xhr.open("GET", data.params[0]);
|
||||
xhr.onload = () => {
|
||||
resolve({
|
||||
relatedTarget: xhr.response,
|
||||
});
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
);
|
||||
// 由content到background
|
||||
// 转发gmApi消息
|
||||
// this.contentMessage.setHandler("gmApi", (action, data) => {
|
||||
|
@ -4,6 +4,9 @@ import { ValueUpdateData } from "./exec_script";
|
||||
import { ExtVersion } from "@App/app/const";
|
||||
import { storageKey } from "../utils";
|
||||
import { Message, MessageConnect } from "@Packages/message/server";
|
||||
import { CustomEventMessage } from "@Packages/message/custom_event_message";
|
||||
import LoggerCore from "@App/app/logger/core";
|
||||
import { connect, sendMessage } from "@Packages/message/client";
|
||||
|
||||
interface ApiParam {
|
||||
depend?: string[];
|
||||
@ -65,25 +68,19 @@ export default class GMApi {
|
||||
|
||||
// 单次回调使用
|
||||
public sendMessage(api: string, params: any[]) {
|
||||
return this.message.sendMessage({
|
||||
action: this.prefix + "/runtime/gmApi",
|
||||
data: {
|
||||
uuid: this.scriptRes.uuid,
|
||||
api,
|
||||
params,
|
||||
},
|
||||
return sendMessage(this.message, this.prefix + "/runtime/gmApi", {
|
||||
uuid: this.scriptRes.uuid,
|
||||
api,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 长连接使用,connect只用于接受消息,不发送消息
|
||||
public connect(api: string, params: any[]) {
|
||||
return this.message.connect({
|
||||
action: this.prefix + "/runtime/gmApi",
|
||||
data: {
|
||||
uuid: this.scriptRes.uuid,
|
||||
api,
|
||||
params,
|
||||
},
|
||||
return connect(this.message, this.prefix + "/runtime/gmApi", {
|
||||
uuid: this.scriptRes.uuid,
|
||||
api,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@ -184,6 +181,23 @@ export default class GMApi {
|
||||
return this.sendMessage("GM_log", [message, level, labels]);
|
||||
}
|
||||
|
||||
@GMContext.API()
|
||||
public CAT_createBlobUrl(blob: Blob): Promise<string> {
|
||||
return this.sendMessage("CAT_createBlobUrl", [blob]);
|
||||
}
|
||||
|
||||
// 辅助GM_xml获取blob数据
|
||||
@GMContext.API()
|
||||
public CAT_fetchBlob(url: string): Promise<Blob> {
|
||||
return this.sendMessage("CAT_fetchBlob", [url]);
|
||||
}
|
||||
|
||||
@GMContext.API()
|
||||
public async CAT_fetchDocument(url: string): Promise<Document | undefined> {
|
||||
const data = await this.sendMessage("CAT_fetchDocument", [url]);
|
||||
return (<CustomEventMessage>this.message).getAndDelRelatedTarget(data.relatedTarget);
|
||||
}
|
||||
|
||||
// 用于脚本跨域请求,需要@connect domain指定允许的域名
|
||||
@GMContext.API({
|
||||
depend: ["CAT_fetchBlob", "CAT_createBlobUrl", "CAT_fetchDocument"],
|
||||
@ -220,42 +234,154 @@ export default class GMApi {
|
||||
param.headers["Cache-Control"] = "no-cache";
|
||||
}
|
||||
let connect: MessageConnect;
|
||||
this.connect("GM_xmlhttpRequest", [param]).then((con) => {
|
||||
connect = con;
|
||||
con.onMessage((data: { action: string; data: any }) => {
|
||||
// 处理返回
|
||||
switch (data.action) {
|
||||
case "onload":
|
||||
details.onload?.(data.data);
|
||||
break;
|
||||
case "onloadend":
|
||||
details.onloadend?.(data.data);
|
||||
break;
|
||||
case "onloadstart":
|
||||
details.onloadstart?.(data.data);
|
||||
break;
|
||||
case "onprogress":
|
||||
details.onprogress?.(data.data);
|
||||
break;
|
||||
case "onreadystatechange":
|
||||
details.onreadystatechange && details.onreadystatechange(data.data);
|
||||
break;
|
||||
case "ontimeout":
|
||||
details.ontimeout?.();
|
||||
break;
|
||||
case "onerror":
|
||||
details.onerror?.("");
|
||||
break;
|
||||
case "onabort":
|
||||
details.onabort?.();
|
||||
break;
|
||||
case "onstream":
|
||||
// controller?.enqueue(new Uint8Array(resp.data));
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
const handler = async () => {
|
||||
// 处理数据
|
||||
if (details.data instanceof FormData) {
|
||||
// 处理FormData
|
||||
param.dataType = "FormData";
|
||||
const data: Array<GMSend.XHRFormData> = [];
|
||||
const keys: { [key: string]: boolean } = {};
|
||||
details.data.forEach((val, key) => {
|
||||
keys[key] = true;
|
||||
});
|
||||
// 处理FormData中的数据
|
||||
await Promise.all(
|
||||
Object.keys(keys).map((key) => {
|
||||
const values = (<FormData>details.data).getAll(key);
|
||||
return Promise.all(
|
||||
values.map(async (val) => {
|
||||
if (val instanceof File) {
|
||||
const url = await this.CAT_createBlobUrl(val);
|
||||
console.log(url);
|
||||
data.push({
|
||||
key,
|
||||
type: "file",
|
||||
val: url,
|
||||
filename: val.name,
|
||||
});
|
||||
} else {
|
||||
data.push({
|
||||
key,
|
||||
type: "text",
|
||||
val,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
param.data = data;
|
||||
} else if (details.data instanceof Blob) {
|
||||
// 处理blob
|
||||
param.dataType = "Blob";
|
||||
param.data = await this.CAT_createBlobUrl(details.data);
|
||||
}
|
||||
|
||||
// 处理返回数据
|
||||
let readerStream: ReadableStream<Uint8Array> | undefined;
|
||||
let controller: ReadableStreamDefaultController<Uint8Array> | undefined;
|
||||
// 如果返回类型是arraybuffer或者blob的情况下,需要将返回的数据转化为blob
|
||||
// 在background通过URL.createObjectURL转化为url,然后在content页读取url获取blob对象
|
||||
const responseType = details.responseType?.toLocaleLowerCase();
|
||||
const warpResponse = (old: (xhr: GMTypes.XHRResponse) => void) => {
|
||||
if (responseType === "stream") {
|
||||
readerStream = new ReadableStream<Uint8Array>({
|
||||
start(ctrl) {
|
||||
controller = ctrl;
|
||||
},
|
||||
});
|
||||
}
|
||||
return async (xhr: GMTypes.XHRResponse) => {
|
||||
if (xhr.response) {
|
||||
if (responseType === "document") {
|
||||
xhr.response = await this.CAT_fetchDocument(<string>xhr.response);
|
||||
xhr.responseXML = xhr.response;
|
||||
xhr.responseType = "document";
|
||||
} else {
|
||||
const resp = await this.CAT_fetchBlob(<string>xhr.response);
|
||||
if (responseType === "arraybuffer") {
|
||||
xhr.response = await resp.arrayBuffer();
|
||||
} else {
|
||||
xhr.response = resp;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (responseType === "stream") {
|
||||
xhr.response = readerStream;
|
||||
}
|
||||
old(xhr);
|
||||
};
|
||||
};
|
||||
if (
|
||||
responseType === "arraybuffer" ||
|
||||
responseType === "blob" ||
|
||||
responseType === "document" ||
|
||||
responseType === "stream"
|
||||
) {
|
||||
if (details.onload) {
|
||||
details.onload = warpResponse(details.onload);
|
||||
}
|
||||
if (details.onreadystatechange) {
|
||||
details.onreadystatechange = warpResponse(details.onreadystatechange);
|
||||
}
|
||||
if (details.onloadend) {
|
||||
details.onloadend = warpResponse(details.onloadend);
|
||||
}
|
||||
// document类型读取blob,然后在content页转化为document对象
|
||||
if (responseType === "document") {
|
||||
param.responseType = "blob";
|
||||
}
|
||||
if (responseType === "stream") {
|
||||
if (details.onloadstart) {
|
||||
details.onloadstart = warpResponse(details.onloadstart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 发送信息
|
||||
this.connect("GM_xmlhttpRequest", [param]).then((con) => {
|
||||
connect = con;
|
||||
con.onMessage((data: { action: string; data: any }) => {
|
||||
// 处理返回
|
||||
switch (data.action) {
|
||||
case "onload":
|
||||
details.onload?.(data.data);
|
||||
break;
|
||||
case "onloadend":
|
||||
details.onloadend?.(data.data);
|
||||
break;
|
||||
case "onloadstart":
|
||||
details.onloadstart?.(data.data);
|
||||
break;
|
||||
case "onprogress":
|
||||
details.onprogress?.(data.data);
|
||||
break;
|
||||
case "onreadystatechange":
|
||||
details.onreadystatechange && details.onreadystatechange(data.data);
|
||||
break;
|
||||
case "ontimeout":
|
||||
details.ontimeout?.();
|
||||
break;
|
||||
case "onerror":
|
||||
details.onerror?.("");
|
||||
break;
|
||||
case "onabort":
|
||||
details.onabort?.();
|
||||
break;
|
||||
case "onstream":
|
||||
controller?.enqueue(new Uint8Array(data.data));
|
||||
break;
|
||||
default:
|
||||
LoggerCore.logger().warn("GM_xmlhttpRequest resp is error", {
|
||||
action: data.action,
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
// 由于需要同步返回一个abort,但是一些操作是异步的,所以需要在这里处理
|
||||
handler();
|
||||
return {
|
||||
abort: () => {
|
||||
if (connect) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user