102 lines
3.3 KiB
JavaScript
102 lines
3.3 KiB
JavaScript
// 主世界钩子(document_start):被动观察页面自己发出的请求,把命中的响应体副本
|
|||
|
|
// postMessage 给 content.js。参照 FetchV / 猫抓一类捕获扩展的通行模式。
|
||
|
|
//
|
||
|
|
// 铁律:只观察,不改写请求与响应;任何异常都必须静默,绝不能影响页面本身。
|
||
|
|
|
||
|
|
(() => {
|
||
|
|
"use strict";
|
||
|
|
|
||
|
|
const MARKER = "__hexi_bf2042_capture__";
|
||
|
|
const API_PREFIX =
|
||
|
|
"https://api.tracker.gg/api/v2/bf2042/standard/profile/";
|
||
|
|
|
||
|
|
function match(url) {
|
||
|
|
return typeof url === "string" && url.startsWith(API_PREFIX);
|
||
|
|
}
|
||
|
|
|
||
|
|
function report(url, status, body) {
|
||
|
|
try {
|
||
|
|
window.postMessage({ [MARKER]: true, url, status, body }, "*");
|
||
|
|
} catch (_) {
|
||
|
|
/* 忽略:上报失败不能影响页面 */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---- fetch ----
|
||
|
|
// 用 Proxy 包一层:Function.prototype.toString.call() 显示为 native
|
||
|
|
// (Proxy 的 target 是原生函数),比"改写后补 toString"更难被识破。
|
||
|
|
if (typeof window.fetch === "function") {
|
||
|
|
const orig = window.fetch;
|
||
|
|
window.fetch = new Proxy(orig, {
|
||
|
|
apply(target, thisArg, args) {
|
||
|
|
const input = args[0];
|
||
|
|
const url =
|
||
|
|
typeof input === "string" ? input : (input && input.url) || "";
|
||
|
|
const result = Reflect.apply(target, thisArg, args);
|
||
|
|
if (match(url)) {
|
||
|
|
result
|
||
|
|
.then((resp) => {
|
||
|
|
try {
|
||
|
|
const ctype = resp.headers.get("content-type") || "";
|
||
|
|
if (!ctype.includes("json")) return;
|
||
|
|
// 立刻 clone,免得页面随后消费了原响应体
|
||
|
|
resp
|
||
|
|
.clone()
|
||
|
|
.json()
|
||
|
|
.then((body) => report(url, resp.status, body))
|
||
|
|
.catch(() => {});
|
||
|
|
} catch (_) {}
|
||
|
|
})
|
||
|
|
.catch(() => {});
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---- XMLHttpRequest ----
|
||
|
|
const XHR = window.XMLHttpRequest;
|
||
|
|
if (XHR && XHR.prototype) {
|
||
|
|
const origOpen = XHR.prototype.open;
|
||
|
|
const origSend = XHR.prototype.send;
|
||
|
|
const urls = new WeakMap(); // 挂在实例上会留可见的自定义属性,用 WeakMap 更干净
|
||
|
|
|
||
|
|
XHR.prototype.open = new Proxy(origOpen, {
|
||
|
|
apply(target, thisArg, args) {
|
||
|
|
try {
|
||
|
|
urls.set(thisArg, String(args[1] ?? ""));
|
||
|
|
} catch (_) {}
|
||
|
|
return Reflect.apply(target, thisArg, args);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
XHR.prototype.send = new Proxy(origSend, {
|
||
|
|
apply(target, thisArg, args) {
|
||
|
|
try {
|
||
|
|
const url = urls.get(thisArg);
|
||
|
|
if (match(url)) {
|
||
|
|
thisArg.addEventListener(
|
||
|
|
"loadend",
|
||
|
|
() => {
|
||
|
|
try {
|
||
|
|
const rt = thisArg.responseType;
|
||
|
|
let body = null;
|
||
|
|
if (rt === "" || rt === "text") {
|
||
|
|
body = JSON.parse(thisArg.responseText);
|
||
|
|
} else if (rt === "json") {
|
||
|
|
body = thisArg.response;
|
||
|
|
} else {
|
||
|
|
return; // arraybuffer / blob 等类型不处理
|
||
|
|
}
|
||
|
|
if (body != null) report(url, thisArg.status, body);
|
||
|
|
} catch (_) {}
|
||
|
|
},
|
||
|
|
{ once: true }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
} catch (_) {}
|
||
|
|
return Reflect.apply(target, thisArg, args);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
})();
|