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

160 lines
6.1 KiB
JavaScript

const { scaffoldModule } = require("./scaffold-module");
function toFiniteNumber(value, fallback) {
return Number.isFinite(value) ? value : fallback;
}
function cloneRecord(value) {
return Object.assign({}, value || {});
}
function normalizePerformanceSeed(seed = {}) {
const performanceSeed = seed.performance && typeof seed.performance === "object" ? seed.performance : seed;
const navigationStart = toFiniteNumber(performanceSeed.navigationStart, Date.now());
const timeOrigin = toFiniteNumber(performanceSeed.timeOrigin, navigationStart);
const nowOffset = toFiniteNumber(performanceSeed.nowOffset, 12.345);
const timing = cloneRecord(performanceSeed.timing);
if (!Number.isFinite(timing.navigationStart)) timing.navigationStart = navigationStart;
if (!Number.isFinite(timing.fetchStart)) timing.fetchStart = timing.navigationStart;
if (!Number.isFinite(timing.domainLookupStart)) timing.domainLookupStart = timing.fetchStart + 1;
if (!Number.isFinite(timing.domainLookupEnd)) timing.domainLookupEnd = timing.domainLookupStart + 1;
if (!Number.isFinite(timing.connectStart)) timing.connectStart = timing.domainLookupEnd;
if (!Number.isFinite(timing.connectEnd)) timing.connectEnd = timing.connectStart + 2;
if (!Number.isFinite(timing.requestStart)) timing.requestStart = timing.connectEnd + 1;
if (!Number.isFinite(timing.responseStart)) timing.responseStart = timing.requestStart + 4;
if (!Number.isFinite(timing.responseEnd)) timing.responseEnd = timing.responseStart + 4;
if (!Number.isFinite(timing.domLoading)) timing.domLoading = timing.responseEnd + 2;
if (!Number.isFinite(timing.domInteractive)) timing.domInteractive = timing.domLoading + 5;
if (!Number.isFinite(timing.domContentLoadedEventStart)) timing.domContentLoadedEventStart = timing.domInteractive + 1;
if (!Number.isFinite(timing.domContentLoadedEventEnd)) timing.domContentLoadedEventEnd = timing.domContentLoadedEventStart + 1;
if (!Number.isFinite(timing.domComplete)) timing.domComplete = timing.domContentLoadedEventEnd + 5;
if (!Number.isFinite(timing.loadEventStart)) timing.loadEventStart = timing.domComplete + 1;
if (!Number.isFinite(timing.loadEventEnd)) timing.loadEventEnd = timing.loadEventStart + 1;
return {
timeOrigin,
navigationStart,
nowOffset,
timing,
memory: cloneRecord(performanceSeed.memory),
};
}
function renderPerformancePatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installPerformanceModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
const baseEpoch = Date.now();
const monotonicStart = seed.nowOffset;
let tick = 0;
function nextNow() {
tick += 1;
return monotonicStart + (Date.now() - baseEpoch) + tick * 0.01;
}
function now() {
return Number(nextNow().toFixed(3));
}
function clearMarks() {
return undefined;
}
function clearMeasures() {
return undefined;
}
function getEntries() {
return [];
}
function getEntriesByType() {
return [];
}
function getEntriesByName() {
return [];
}
const performanceObject = {
now: now,
timeOrigin: seed.timeOrigin,
timing: seed.timing,
clearMarks: clearMarks,
clearMeasures: clearMeasures,
getEntries: getEntries,
getEntriesByType: getEntriesByType,
getEntriesByName: getEntriesByName,
mark: function mark() {},
measure: function measure() {}
};
if (seed.memory && Object.keys(seed.memory).length) {
performanceObject.memory = seed.memory;
}
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(now, { name: "now", length: 0 });
protector.repairFunctionMeta(clearMarks, { name: "clearMarks", length: 0 });
protector.repairFunctionMeta(clearMeasures, { name: "clearMeasures", length: 0 });
protector.repairFunctionMeta(getEntries, { name: "getEntries", length: 0 });
protector.repairFunctionMeta(getEntriesByType, { name: "getEntriesByType", length: 1 });
protector.repairFunctionMeta(getEntriesByName, { name: "getEntriesByName", length: 1 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(now, "now");
protector.protectFunction(clearMarks, "clearMarks");
protector.protectFunction(clearMeasures, "clearMeasures");
protector.protectFunction(getEntries, "getEntries");
protector.protectFunction(getEntriesByType, "getEntriesByType");
protector.protectFunction(getEntriesByName, "getEntriesByName");
}
Object.defineProperty(globalObject, "performance", {
configurable: true,
enumerable: false,
writable: true,
value: performanceObject
});
return performanceObject;
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildPerformanceModule(seed = {}) {
const module = scaffoldModule("performance-module");
const normalizedSeed = normalizePerformanceSeed(seed);
module.patchPlan = [
"构建 timeOrigin/navigationStart/timing 基础树。",
"补出单调递增的 performance.now()。",
"为 getEntries/getEntriesByType/getEntriesByName 等常见检测点提供稳定返回。",
];
module.patchCode = renderPerformancePatch(normalizedSeed);
module.runtimeState = {
performance: normalizedSeed,
};
module.validation = [
"连续两次调用 performance.now(),确认返回值递增。",
"检查 performance.timeOrigin 与 performance.timing.navigationStart 的相对关系。",
"检查 performance.getEntries()/getEntriesByType()/getEntriesByName() 返回数组。",
"检查 performance.now.toString() 以及 name/length 元信息。",
];
module.residualRisk = [
"未实现 PerformanceObserver、resource timing、navigation entries 等复杂链路。",
"如果目标脚本会校验 performance.memory 或多阶段时间戳分布,仍需采集真实浏览器种子。",
];
return module;
}
module.exports = {
buildPerformanceModule,
normalizePerformanceSeed,
};