// 主世界钩子(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); }, }); } })();