实现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("username", "admin");
|
||||||
|
|
||||||
|
data.append(
|
||||||
|
"file",
|
||||||
|
new File(["foo"], "foo.txt", {
|
||||||
|
type: "text/plain",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
GM_xmlhttpRequest({
|
GM_xmlhttpRequest({
|
||||||
url: "https://bbs.tampermonkey.net.cn/",
|
url: "https://bbs.tampermonkey.net.cn/",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
data: data,
|
data: data,
|
||||||
headers: {
|
headers: {
|
||||||
"referer": "http://www.example.com/",
|
referer: "http://www.example.com/",
|
||||||
"origin": "www.example.com",
|
origin: "www.example.com",
|
||||||
// 为空将不会发送此header
|
// 为空将不会发送此header
|
||||||
"sec-ch-ua-mobile": "",
|
"sec-ch-ua-mobile": "",
|
||||||
},
|
},
|
||||||
|
21
package.json
21
package.json
@ -31,7 +31,7 @@
|
|||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-i18next": "^15.1.0",
|
"react-i18next": "^15.4.1",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-joyride": "^2.9.3",
|
"react-joyride": "^2.9.3",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
@ -41,10 +41,10 @@
|
|||||||
"yaml": "^2.6.1"
|
"yaml": "^2.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.2.6",
|
"@eslint/compat": "^1.2.8",
|
||||||
"@eslint/js": "^9.19.0",
|
"@eslint/js": "^9.24.0",
|
||||||
"@rspack/cli": "^1.2.3",
|
"@rspack/cli": "^1.3.2",
|
||||||
"@rspack/core": "^1.2.3",
|
"@rspack/core": "^1.3.2",
|
||||||
"@rspack/plugin-react-refresh": "^1.0.1",
|
"@rspack/plugin-react-refresh": "^1.0.1",
|
||||||
"@types/chrome": "^0.0.279",
|
"@types/chrome": "^0.0.279",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
@ -57,22 +57,21 @@
|
|||||||
"@unocss/postcss": "0.65.0-beta.2",
|
"@unocss/postcss": "0.65.0-beta.2",
|
||||||
"@vitest/coverage-v8": "2.1.4",
|
"@vitest/coverage-v8": "2.1.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"compression-webpack-plugin": "^11.1.0",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^9.19.0",
|
"eslint": "^9.24.0",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"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",
|
"fake-indexeddb": "^6.0.0",
|
||||||
"globals": "^15.14.0",
|
"globals": "^16.0.0",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^25.0.1",
|
||||||
"mock-xmlhttprequest": "^8.4.1",
|
"mock-xmlhttprequest": "^8.4.1",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"postcss-loader": "^8.1.1",
|
"postcss-loader": "^8.1.1",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.5.3",
|
||||||
"react-refresh": "^0.16.0",
|
"react-refresh": "^0.16.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.22.0",
|
"typescript-eslint": "^8.29.0",
|
||||||
"unocss": "0.65.0-beta.2",
|
"unocss": "0.65.0-beta.2",
|
||||||
"vitest": "^2.1.4"
|
"vitest": "^2.1.4"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export class CustomEventMessage implements Message {
|
|||||||
EE: EventEmitter = new EventEmitter();
|
EE: EventEmitter = new EventEmitter();
|
||||||
|
|
||||||
// 关联dom目标
|
// 关联dom目标
|
||||||
relatedTarget: Map<number, Element> = new Map();
|
relatedTarget: Map<number, Document> = new Map();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected flag: string,
|
protected flag: string,
|
||||||
@ -25,7 +25,7 @@ export class CustomEventMessage implements Message {
|
|||||||
) {
|
) {
|
||||||
window.addEventListener((isContent ? "ct" : "fd") + flag, (event) => {
|
window.addEventListener((isContent ? "ct" : "fd") + flag, (event) => {
|
||||||
if (event instanceof MouseEvent) {
|
if (event instanceof MouseEvent) {
|
||||||
this.relatedTarget.set(event.clientX, <Element>event.relatedTarget);
|
this.relatedTarget.set(event.clientX, <Document>event.relatedTarget);
|
||||||
return;
|
return;
|
||||||
} else if (event instanceof CustomEvent) {
|
} else if (event instanceof CustomEvent) {
|
||||||
this.messageHandle(event.detail, new CustomEventPostMessage(this));
|
this.messageHandle(event.detail, new CustomEventPostMessage(this));
|
||||||
@ -130,4 +130,10 @@ export class CustomEventMessage implements Message {
|
|||||||
this.nativeSend(body);
|
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) {
|
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) {
|
||||||
from.on(path, (params, fromCon) => {
|
from.on(path, async (params, fromCon) => {
|
||||||
console.log("forwardMessage", path, prefix, params);
|
console.log("forwardMessage", path, prefix, params);
|
||||||
|
if (middleware) {
|
||||||
|
const resp = await middleware(params, new GetSender(fromCon));
|
||||||
|
if (resp !== false) {
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (fromCon) {
|
if (fromCon) {
|
||||||
const fromConnect = fromCon.getConnect();
|
const fromConnect = fromCon.getConnect();
|
||||||
to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => {
|
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 { defineConfig } from "@rspack/cli";
|
||||||
import { rspack } from "@rspack/core";
|
import { rspack } from "@rspack/core";
|
||||||
import { version } from "./package.json";
|
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 isDev = process.env.NODE_ENV === "development";
|
||||||
const isBeta = version.includes("-");
|
const isBeta = version.includes("-");
|
||||||
@ -174,11 +172,6 @@ export default defineConfig({
|
|||||||
minify: true,
|
minify: true,
|
||||||
chunks: ["sandbox"],
|
chunks: ["sandbox"],
|
||||||
}),
|
}),
|
||||||
new CompressionPlugin({
|
|
||||||
test: /ts.worker.js$/,
|
|
||||||
filename: () => "ts.worker.js",
|
|
||||||
deleteOriginalAssets: true,
|
|
||||||
}),
|
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
optimization: {
|
optimization: {
|
||||||
minimizer: [
|
minimizer: [
|
||||||
@ -187,7 +180,6 @@ export default defineConfig({
|
|||||||
minimizerOptions: { targets },
|
minimizerOptions: { targets },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
realContentHash: true,
|
|
||||||
},
|
},
|
||||||
experiments: {
|
experiments: {
|
||||||
css: true,
|
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";
|
import { GetSender, Group, MessageConnect } from "@Packages/message/server";
|
||||||
|
|
||||||
export default class GMApi {
|
export default class GMApi {
|
||||||
constructor(private group: Group) {}
|
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;
|
const finalUrl = xhr.responseURL || details.url;
|
||||||
// 判断是否有headerFlag-final-url,有则替换finalUrl
|
// 判断是否有headerFlag-final-url,有则替换finalUrl
|
||||||
let response: GMTypes.XHRResponse = {
|
let response: GMTypes.XHRResponse = {
|
||||||
@ -11,9 +19,56 @@ export default class GMApi {
|
|||||||
readyState: <any>xhr.readyState,
|
readyState: <any>xhr.readyState,
|
||||||
status: xhr.status,
|
status: xhr.status,
|
||||||
statusText: xhr.statusText,
|
statusText: xhr.statusText,
|
||||||
|
// header由service_worker处理
|
||||||
// responseHeaders: xhr.getAllResponseHeaders().replace(removeXCat, ""),
|
// responseHeaders: xhr.getAllResponseHeaders().replace(removeXCat, ""),
|
||||||
responseType: details.responseType,
|
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) {
|
if (data) {
|
||||||
response = Object.assign(response, data);
|
response = Object.assign(response, data);
|
||||||
}
|
}
|
||||||
@ -24,16 +79,36 @@ export default class GMApi {
|
|||||||
return response;
|
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 xhr = new XMLHttpRequest();
|
||||||
const con = sender.getConnect();
|
const con = sender.getConnect();
|
||||||
xhr.open(details.method || "GET", details.url);
|
xhr.open(details.method || "GET", details.url, true, details.user || "", details.password || "");
|
||||||
// 添加header
|
// 添加header
|
||||||
if (details.headers) {
|
if (details.headers) {
|
||||||
for (const key in details.headers) {
|
for (const key in details.headers) {
|
||||||
xhr.setRequestHeader(key, details.headers[key]);
|
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 = () => {
|
xhr.onload = () => {
|
||||||
this.dealXhrResponse(con, details, "onload", xhr);
|
this.dealXhrResponse(con, details, "onload", xhr);
|
||||||
};
|
};
|
||||||
@ -65,14 +140,32 @@ export default class GMApi {
|
|||||||
xhr.ontimeout = () => {
|
xhr.ontimeout = () => {
|
||||||
con?.sendMessage({ action: "ontimeout", data: {} });
|
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(() => {
|
con?.onDisconnect(() => {
|
||||||
xhr.abort();
|
xhr.abort();
|
||||||
});
|
});
|
||||||
|
@ -52,20 +52,5 @@ export class OffscreenManager {
|
|||||||
|
|
||||||
const gmApi = new GMApi(this.windowApi.group("gmApi"));
|
const gmApi = new GMApi(this.windowApi.group("gmApi"));
|
||||||
gmApi.init();
|
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) {
|
crontabScript(script: ScriptRunResouce) {
|
||||||
// 执行定时脚本 运行表达式
|
// 执行定时脚本 运行表达式
|
||||||
if (!script.metadata.crontab) {
|
if (!script.metadata.crontab) {
|
||||||
throw new Error("错误的crontab表达式");
|
throw new Error(script.name + " - 错误的crontab表达式");
|
||||||
}
|
}
|
||||||
// 如果有nextruntime,则加入重试队列
|
// 如果有nextruntime,则加入重试队列
|
||||||
this.joinRetryList(script);
|
this.joinRetryList(script);
|
||||||
|
@ -95,11 +95,21 @@ export default class GMApi {
|
|||||||
] as chrome.declarativeNetRequest.ModifyHeaderInfo[];
|
] as chrome.declarativeNetRequest.ModifyHeaderInfo[];
|
||||||
Object.keys(headers).forEach((key) => {
|
Object.keys(headers).forEach((key) => {
|
||||||
const lowKey = key.toLowerCase();
|
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({
|
requestHeaders.push({
|
||||||
header: key,
|
header: key,
|
||||||
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
operation: chrome.declarativeNetRequest.HeaderOperation.REMOVE,
|
||||||
value: headers[key],
|
|
||||||
});
|
});
|
||||||
delete headers[key];
|
delete headers[key];
|
||||||
}
|
}
|
||||||
@ -125,7 +135,6 @@ export default class GMApi {
|
|||||||
requestMethods: [(params.method || "GET").toLowerCase() as chrome.declarativeNetRequest.RequestMethod],
|
requestMethods: [(params.method || "GET").toLowerCase() as chrome.declarativeNetRequest.RequestMethod],
|
||||||
excludedTabIds: excludedTabIds,
|
excludedTabIds: excludedTabIds,
|
||||||
};
|
};
|
||||||
console.log(rule);
|
|
||||||
await chrome.declarativeNetRequest.updateSessionRules({
|
await chrome.declarativeNetRequest.updateSessionRules({
|
||||||
removeRuleIds: [ruleId],
|
removeRuleIds: [ruleId],
|
||||||
addRules: [rule],
|
addRules: [rule],
|
||||||
@ -135,9 +144,9 @@ export default class GMApi {
|
|||||||
|
|
||||||
gmXhrHeadersReceived: EventEmitter = new EventEmitter();
|
gmXhrHeadersReceived: EventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
// TODO: maxRedirects实现
|
||||||
@PermissionVerify.API()
|
@PermissionVerify.API()
|
||||||
async GM_xmlhttpRequest(request: Request, con: GetSender) {
|
async GM_xmlhttpRequest(request: Request, con: GetSender) {
|
||||||
console.log("GM XHR", request);
|
|
||||||
if (request.params.length === 0) {
|
if (request.params.length === 0) {
|
||||||
return Promise.reject(new Error("param is failed"));
|
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["X-Scriptcat-GM-XHR-Request-Id"] = requestId.toString();
|
||||||
params.headers = await this.buildDNRRule(requestId, request.params[0]);
|
params.headers = await this.buildDNRRule(requestId, request.params[0]);
|
||||||
console.log(" params.headers", params.headers);
|
|
||||||
let responseHeader = "";
|
let responseHeader = "";
|
||||||
// 等待response
|
// 等待response
|
||||||
this.gmXhrHeadersReceived.addListener(
|
this.gmXhrHeadersReceived.addListener(
|
||||||
@ -160,6 +168,7 @@ export default class GMApi {
|
|||||||
details.responseHeaders?.forEach((header) => {
|
details.responseHeaders?.forEach((header) => {
|
||||||
responseHeader += header.name + ": " + header.value + "\n";
|
responseHeader += header.name + ": " + header.value + "\n";
|
||||||
});
|
});
|
||||||
|
this.gmXhrHeadersReceived.removeAllListeners("headersReceived:" + requestId);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// 再发送到offscreen, 处理请求
|
// 再发送到offscreen, 处理请求
|
||||||
|
@ -2,7 +2,7 @@ import dts from "@App/types/scriptcat.d.ts";
|
|||||||
import { languages } from "monaco-editor";
|
import { languages } from "monaco-editor";
|
||||||
import pako from "pako";
|
import pako from "pako";
|
||||||
import Cache from "@App/app/cache";
|
import Cache from "@App/app/cache";
|
||||||
import { isFirefox } from "./utils";
|
import { isDebug, isFirefox } from "./utils";
|
||||||
import EventEmitter from "eventemitter3";
|
import EventEmitter from "eventemitter3";
|
||||||
|
|
||||||
// 注册eslint
|
// 注册eslint
|
||||||
@ -15,7 +15,12 @@ export default function registerEditor() {
|
|||||||
fetch(chrome.runtime.getURL(`/src/ts.worker.js${isFirefox() ? ".gz" : ""}`))
|
fetch(chrome.runtime.getURL(`/src/ts.worker.js${isFirefox() ? ".gz" : ""}`))
|
||||||
.then((resp) => resp.blob())
|
.then((resp) => resp.blob())
|
||||||
.then(async (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
|
// @ts-ignore
|
||||||
window.tsUrl = URL.createObjectURL(new Blob([result]));
|
window.tsUrl = URL.createObjectURL(new Blob([result]));
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
||||||
import { Client } from "@Packages/message/client";
|
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页的处理
|
// content页的处理
|
||||||
export default class ContentRuntime {
|
export default class ContentRuntime {
|
||||||
@ -17,7 +17,43 @@ export default class ContentRuntime {
|
|||||||
this.msg.onConnect((msg, connect) => {
|
this.msg.onConnect((msg, connect) => {
|
||||||
console.log(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
|
// 由content到background
|
||||||
// 转发gmApi消息
|
// 转发gmApi消息
|
||||||
// this.contentMessage.setHandler("gmApi", (action, data) => {
|
// this.contentMessage.setHandler("gmApi", (action, data) => {
|
||||||
|
@ -4,6 +4,9 @@ import { ValueUpdateData } from "./exec_script";
|
|||||||
import { ExtVersion } from "@App/app/const";
|
import { ExtVersion } from "@App/app/const";
|
||||||
import { storageKey } from "../utils";
|
import { storageKey } from "../utils";
|
||||||
import { Message, MessageConnect } from "@Packages/message/server";
|
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 {
|
interface ApiParam {
|
||||||
depend?: string[];
|
depend?: string[];
|
||||||
@ -65,25 +68,19 @@ export default class GMApi {
|
|||||||
|
|
||||||
// 单次回调使用
|
// 单次回调使用
|
||||||
public sendMessage(api: string, params: any[]) {
|
public sendMessage(api: string, params: any[]) {
|
||||||
return this.message.sendMessage({
|
return sendMessage(this.message, this.prefix + "/runtime/gmApi", {
|
||||||
action: this.prefix + "/runtime/gmApi",
|
uuid: this.scriptRes.uuid,
|
||||||
data: {
|
api,
|
||||||
uuid: this.scriptRes.uuid,
|
params,
|
||||||
api,
|
|
||||||
params,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 长连接使用,connect只用于接受消息,不发送消息
|
// 长连接使用,connect只用于接受消息,不发送消息
|
||||||
public connect(api: string, params: any[]) {
|
public connect(api: string, params: any[]) {
|
||||||
return this.message.connect({
|
return connect(this.message, this.prefix + "/runtime/gmApi", {
|
||||||
action: this.prefix + "/runtime/gmApi",
|
uuid: this.scriptRes.uuid,
|
||||||
data: {
|
api,
|
||||||
uuid: this.scriptRes.uuid,
|
params,
|
||||||
api,
|
|
||||||
params,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +181,23 @@ export default class GMApi {
|
|||||||
return this.sendMessage("GM_log", [message, level, labels]);
|
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指定允许的域名
|
// 用于脚本跨域请求,需要@connect domain指定允许的域名
|
||||||
@GMContext.API({
|
@GMContext.API({
|
||||||
depend: ["CAT_fetchBlob", "CAT_createBlobUrl", "CAT_fetchDocument"],
|
depend: ["CAT_fetchBlob", "CAT_createBlobUrl", "CAT_fetchDocument"],
|
||||||
@ -220,42 +234,154 @@ export default class GMApi {
|
|||||||
param.headers["Cache-Control"] = "no-cache";
|
param.headers["Cache-Control"] = "no-cache";
|
||||||
}
|
}
|
||||||
let connect: MessageConnect;
|
let connect: MessageConnect;
|
||||||
this.connect("GM_xmlhttpRequest", [param]).then((con) => {
|
const handler = async () => {
|
||||||
connect = con;
|
// 处理数据
|
||||||
con.onMessage((data: { action: string; data: any }) => {
|
if (details.data instanceof FormData) {
|
||||||
// 处理返回
|
// 处理FormData
|
||||||
switch (data.action) {
|
param.dataType = "FormData";
|
||||||
case "onload":
|
const data: Array<GMSend.XHRFormData> = [];
|
||||||
details.onload?.(data.data);
|
const keys: { [key: string]: boolean } = {};
|
||||||
break;
|
details.data.forEach((val, key) => {
|
||||||
case "onloadend":
|
keys[key] = true;
|
||||||
details.onloadend?.(data.data);
|
});
|
||||||
break;
|
// 处理FormData中的数据
|
||||||
case "onloadstart":
|
await Promise.all(
|
||||||
details.onloadstart?.(data.data);
|
Object.keys(keys).map((key) => {
|
||||||
break;
|
const values = (<FormData>details.data).getAll(key);
|
||||||
case "onprogress":
|
return Promise.all(
|
||||||
details.onprogress?.(data.data);
|
values.map(async (val) => {
|
||||||
break;
|
if (val instanceof File) {
|
||||||
case "onreadystatechange":
|
const url = await this.CAT_createBlobUrl(val);
|
||||||
details.onreadystatechange && details.onreadystatechange(data.data);
|
console.log(url);
|
||||||
break;
|
data.push({
|
||||||
case "ontimeout":
|
key,
|
||||||
details.ontimeout?.();
|
type: "file",
|
||||||
break;
|
val: url,
|
||||||
case "onerror":
|
filename: val.name,
|
||||||
details.onerror?.("");
|
});
|
||||||
break;
|
} else {
|
||||||
case "onabort":
|
data.push({
|
||||||
details.onabort?.();
|
key,
|
||||||
break;
|
type: "text",
|
||||||
case "onstream":
|
val,
|
||||||
// controller?.enqueue(new Uint8Array(resp.data));
|
});
|
||||||
break;
|
}
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
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 {
|
return {
|
||||||
abort: () => {
|
abort: () => {
|
||||||
if (connect) {
|
if (connect) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user