Files
reverse-skills-max/vendor-new/xbsReverseSkill/web-reverse-env/scripts/build-document-module.js

333 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { scaffoldModule } = require("./scaffold-module");
function clone(value) {
return JSON.parse(JSON.stringify(value || {}));
}
function normalizeDocumentSeed(seed = {}) {
const documentSeed = seed.document && typeof seed.document === "object" ? seed.document : seed;
return {
document: {
URL: documentSeed.URL || "https://example.invalid/",
referrer: documentSeed.referrer || "",
documentURI: documentSeed.documentURI || documentSeed.URL || "https://example.invalid/",
compatMode: documentSeed.compatMode || "CSS1Compat",
dir: documentSeed.dir || "",
title: documentSeed.title || "",
designMode: documentSeed.designMode || "off",
readyState: documentSeed.readyState || "complete",
contentType: documentSeed.contentType || "text/html",
inputEncoding: documentSeed.inputEncoding || "UTF-8",
domain: documentSeed.domain || "example.invalid",
characterSet: documentSeed.characterSet || "UTF-8",
charset: documentSeed.charset || "UTF-8",
hidden: documentSeed.hidden === true,
visibilityState: documentSeed.visibilityState || "visible",
cookie: typeof documentSeed.cookie === "string" ? documentSeed.cookie : (typeof seed.cookie === "string" ? seed.cookie : ""),
},
queryMap: clone(seed.queryMap || {}),
ids: clone(seed.ids || {}),
tags: clone(seed.tags || {}),
specialCases: clone(seed.specialCases || {}),
};
}
function renderDocumentPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installDocumentModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function defineGetter(target, key, getter, setter) {
const descriptor = {
configurable: true,
enumerable: false,
get: getter
};
if (typeof setter === "function") {
descriptor.set = setter;
}
Object.defineProperty(target, key, descriptor);
if (protector && protector.protectDescriptor) {
protector.protectDescriptor(target, key);
}
}
function applyEventTarget(target) {
defineValue(target, "__listeners__", Object.create(null));
defineValue(target, "addEventListener", function addEventListener(type, listener) {
if (!this.__listeners__[type]) {
this.__listeners__[type] = [];
}
this.__listeners__[type].push(listener);
});
defineValue(target, "removeEventListener", function removeEventListener(type, listener) {
const list = this.__listeners__[type] || [];
this.__listeners__[type] = list.filter(function filterItem(item) {
return item !== listener;
});
});
defineValue(target, "dispatchEvent", function dispatchEvent(event) {
const list = (this.__listeners__[event.type] || []).slice();
for (const listener of list) {
listener.call(this, event);
}
const handler = this["on" + event.type];
if (typeof handler === "function") {
handler.call(this, event);
}
return true;
});
}
function createElementNode(tagName) {
const normalized = String(tagName || "div").toUpperCase();
if (normalized === "CANVAS" && typeof globalObject.__envCreateCanvasElement === "function") {
return globalObject.__envCreateCanvasElement();
}
const element = {
nodeType: 1,
nodeName: normalized,
tagName: normalized,
ownerDocument: null,
style: {},
dataset: {},
children: [],
childNodes: []
};
applyEventTarget(element);
defineValue(element, "appendChild", function appendChild(child) {
this.children.push(child);
this.childNodes.push(child);
child.parentNode = this;
return child;
});
defineValue(element, "setAttribute", function setAttribute(name, value) {
this[name] = String(value);
});
defineValue(element, "getAttribute", function getAttribute(name) {
return Object.prototype.hasOwnProperty.call(this, name) ? this[name] : null;
});
return element;
}
function buildNodeMap(source, kind) {
const out = Object.create(null);
Object.keys(source || {}).forEach(function eachKey(key) {
const value = source[key];
if (Array.isArray(value)) {
out[key] = value.map(function mapTag(tagName) {
const node = createElementNode(tagName);
node.ownerDocument = documentInstance;
return node;
});
} else {
const node = createElementNode(value || kind || "div");
node.ownerDocument = documentInstance;
out[key] = node;
}
});
return out;
}
function Document() {}
function HTMLDocument() {}
HTMLDocument.prototype = Object.create(Document.prototype);
defineValue(HTMLDocument.prototype, "constructor", HTMLDocument);
const state = seed.document || {};
const documentInstance = Object.create(HTMLDocument.prototype);
let cookieValue = state.cookie || "";
applyEventTarget(documentInstance);
const queryNodes = buildNodeMap(seed.queryMap || {}, "div");
const idNodes = buildNodeMap(seed.ids || {}, "div");
const tagNodes = buildNodeMap(seed.tags || {}, "div");
const htmlNode = createElementNode("html");
const headNode = createElementNode("head");
const bodyNode = createElementNode("body");
htmlNode.ownerDocument = documentInstance;
headNode.ownerDocument = documentInstance;
bodyNode.ownerDocument = documentInstance;
htmlNode.appendChild(headNode);
htmlNode.appendChild(bodyNode);
defineGetter(Document.prototype, "URL", function getURL() {
return state.URL;
});
defineGetter(Document.prototype, "referrer", function getReferrer() {
return state.referrer;
});
defineGetter(Document.prototype, "documentURI", function getDocumentURI() {
return state.documentURI;
});
defineGetter(Document.prototype, "compatMode", function getCompatMode() {
return state.compatMode;
});
defineGetter(Document.prototype, "dir", function getDir() {
return state.dir;
});
defineGetter(Document.prototype, "title", function getTitle() {
return state.title;
}, function setTitle(value) {
state.title = String(value);
});
defineGetter(Document.prototype, "designMode", function getDesignMode() {
return state.designMode;
}, function setDesignMode(value) {
state.designMode = String(value);
});
defineGetter(Document.prototype, "readyState", function getReadyState() {
return state.readyState;
});
defineGetter(Document.prototype, "contentType", function getContentType() {
return state.contentType;
});
defineGetter(Document.prototype, "inputEncoding", function getInputEncoding() {
return state.inputEncoding;
});
defineGetter(Document.prototype, "domain", function getDomain() {
return state.domain;
});
defineGetter(Document.prototype, "characterSet", function getCharacterSet() {
return state.characterSet;
});
defineGetter(Document.prototype, "charset", function getCharset() {
return state.charset;
});
defineGetter(Document.prototype, "hidden", function getHidden() {
return !!state.hidden;
});
defineGetter(Document.prototype, "visibilityState", function getVisibilityState() {
return state.visibilityState;
});
defineGetter(Document.prototype, "cookie", function getCookie() {
return cookieValue;
}, function setCookie(value) {
cookieValue = cookieValue ? cookieValue + "; " + String(value) : String(value);
});
defineGetter(Document.prototype, "head", function getHead() {
return headNode;
});
defineGetter(Document.prototype, "body", function getBody() {
return bodyNode;
});
defineGetter(Document.prototype, "documentElement", function getDocumentElement() {
return htmlNode;
});
defineGetter(Document.prototype, "defaultView", function getDefaultView() {
return globalObject;
});
defineValue(Document.prototype, "createElement", function createElement(tagName) {
const element = createElementNode(tagName);
element.ownerDocument = documentInstance;
return element;
});
defineValue(Document.prototype, "querySelector", function querySelector(selector) {
return queryNodes[selector] || null;
});
defineValue(Document.prototype, "getElementById", function getElementById(id) {
return idNodes[id] || null;
});
defineValue(Document.prototype, "getElementsByTagName", function getElementsByTagName(tagName) {
const key = String(tagName || "").toLowerCase();
const list = tagNodes[key] || [];
return list.slice();
});
const documentAllFallback = {
length: 0,
item: function item() {
return null;
},
namedItem: function namedItem() {
return null;
}
};
defineGetter(Document.prototype, "all", function getAll() {
return documentAllFallback;
});
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(Document, { name: "Document", length: 0 });
protector.repairFunctionMeta(HTMLDocument, { name: "HTMLDocument", length: 0 });
protector.repairFunctionMeta(Document.prototype.createElement, { name: "createElement", length: 1 });
protector.repairFunctionMeta(Document.prototype.querySelector, { name: "querySelector", length: 1 });
protector.repairFunctionMeta(Document.prototype.getElementById, { name: "getElementById", length: 1 });
protector.repairFunctionMeta(Document.prototype.getElementsByTagName, { name: "getElementsByTagName", length: 1 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(Document, "Document");
protector.protectFunction(HTMLDocument, "HTMLDocument");
protector.protectFunction(Document.prototype.createElement, "createElement");
protector.protectFunction(Document.prototype.querySelector, "querySelector");
protector.protectFunction(Document.prototype.getElementById, "getElementById");
protector.protectFunction(Document.prototype.getElementsByTagName, "getElementsByTagName");
}
Object.defineProperty(globalObject, "Document", {
configurable: true,
enumerable: false,
writable: true,
value: Document
});
Object.defineProperty(globalObject, "HTMLDocument", {
configurable: true,
enumerable: false,
writable: true,
value: HTMLDocument
});
Object.defineProperty(globalObject, "document", {
configurable: true,
enumerable: false,
writable: true,
value: documentInstance
});
return documentInstance;
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildDocumentModule(seed = {}) {
const module = scaffoldModule("document-module");
const normalizedSeed = normalizeDocumentSeed(seed);
module.patchPlan = [
"构建 Document / HTMLDocument 构造器、prototype 和 document 实例。",
"把 document.cookie、URL、readyState 等高频字段补成访问器描述符。",
"补出 createElement/querySelector/getElementById/getElementsByTagName,并为 canvas 入口预留到 fingerprint 模块的接线点。",
];
module.patchCode = renderDocumentPatch(normalizedSeed);
module.runtimeState = normalizedSeed;
module.validation = [
"检查 document.URL / document.documentURI / document.readyState。",
"检查 document.cookie getter/setter 是否正常工作。",
"检查 document.createElement('canvas') 是否能接到 fingerprint 模块。",
"检查 querySelector / getElementById / getElementsByTagName 的基本返回。",
"检查 document.all 仍被标注为特殊兼容风险,而不是宣称完全等价。",
];
module.residualRisk = [
"这里只覆盖静态 DOM 壳和常见查询入口,未实现完整 Node/Element/MutationObserver/CSSStyleDeclaration 系统。",
"document.all 仅提供降级 fallback,无法在纯 JS 中完整等价原生特殊对象语义。",
];
return module;
}
module.exports = {
buildDocumentModule,
normalizeDocumentSeed,
};