This commit is contained in:
dela
2026-02-22 15:05:49 +08:00
parent 5dc86ccfbf
commit 2154a648af
27 changed files with 3740 additions and 722 deletions

View File

@@ -2,9 +2,18 @@
/**
* P0: Performance mock
* hsw 检测timing / timeOrigin / getEntriesByType('resource') / getEntriesByType('navigation')
* Now with: 5µs quantization, proper prototype chain (Performance -> EventTarget -> Object),
* PerformanceEntry / PerformanceResourceTiming / PerformanceNavigationTiming classes,
* getEntries() method
*/
const { createNative } = require('./native');
const { nativeMethod: M } = require('./native');
const {
Performance,
PerformanceEntry,
PerformanceResourceTiming,
PerformanceNavigationTiming,
} = require('./class_registry');
const NAV_START = Date.now() - 1200;
@@ -32,9 +41,23 @@ const timingData = {
unloadEventEnd: 0,
};
// ── Helper: create PerformanceResourceTiming instance ────────────
const makeResourceEntry = (data) => {
const entry = Object.create(PerformanceResourceTiming.prototype);
Object.assign(entry, data);
return entry;
};
// ── Helper: create PerformanceNavigationTiming instance ──────────
const makeNavEntry = (data) => {
const entry = Object.create(PerformanceNavigationTiming.prototype);
Object.assign(entry, data);
return entry;
};
// 模拟 resource 条目hsw 会查 checksiteconfig 请求痕迹)
const resourceEntries = [
{
makeResourceEntry({
name: 'https://api.hcaptcha.com/checksiteconfig?v=xxx&host=b.stripecdn.com&sitekey=xxx&sc=1&swa=1&spst=1',
entryType: 'resource',
initiatorType: 'xmlhttprequest',
@@ -60,11 +83,11 @@ const resourceEntries = [
requestStart: 0,
responseStart: 0,
firstInterimResponseStart: 0,
finalResponseHeadersStart: 0, // P2 要求的字段
finalResponseHeadersStart: 0,
serverTiming: [],
renderBlockingStatus: 'non-blocking',
},
{
}),
makeResourceEntry({
name: 'https://newassets.hcaptcha.com/c/xxx/hsw.js',
entryType: 'resource',
initiatorType: 'script',
@@ -93,11 +116,11 @@ const resourceEntries = [
redirectEnd: 0,
serverTiming: [],
renderBlockingStatus: 'non-blocking',
},
}),
];
// 模拟 navigation 条目
const navigationEntry = {
const navigationEntry = makeNavEntry({
name: 'https://newassets.hcaptcha.com/captcha/v1/xxx/static/hcaptcha.html',
entryType: 'navigation',
initiatorType: 'navigation',
@@ -140,31 +163,47 @@ const navigationEntry = {
workerStart: 0,
contentEncoding: 'br',
renderBlockingStatus: 'non-blocking',
};
});
const performanceMock = {
// ── Build performance object with proper prototype ──────────────
const performanceMock = Object.create(Performance.prototype);
Object.assign(performanceMock, {
timeOrigin: NAV_START,
timing: timingData,
navigation: { type: 0, redirectCount: 0 },
getEntriesByType: createNative('getEntriesByType', function (type) {
// 5µs quantization (Chromium feature)
now: M('now', 0, () => {
const raw = Date.now() - NAV_START;
return Math.round(raw * 200) / 200; // 0.005ms = 5µs steps
}),
getEntries: M('getEntries', 0, () => {
return [navigationEntry, ...resourceEntries];
}),
getEntriesByType: M('getEntriesByType', 1, (type) => {
if (type === 'resource') return resourceEntries;
if (type === 'navigation') return [navigationEntry];
if (type === 'paint') return [];
if (type === 'mark') return [];
if (type === 'measure') return [];
return [];
}),
getEntriesByName: createNative('getEntriesByName', function (name) {
return resourceEntries.filter(e => e.name === name);
getEntriesByName: M('getEntriesByName', 1, (name) => {
return [...resourceEntries, navigationEntry].filter(e => e.name === name);
}),
now: createNative('now', function () {
return Date.now() - NAV_START;
}),
mark: createNative('mark', function () {}),
measure: createNative('measure', function () {}),
clearMarks: createNative('clearMarks', function () {}),
clearMeasures: createNative('clearMeasures', function () {}),
};
mark: M('mark', 1, () => {}),
measure: M('measure', 1, () => {}),
clearMarks: M('clearMarks', 0, () => {}),
clearMeasures: M('clearMeasures', 0, () => {}),
clearResourceTimings: M('clearResourceTimings', 0, () => {}),
setResourceTimingBufferSize: M('setResourceTimingBufferSize', 1, () => {}),
addEventListener: M('addEventListener', 2, () => {}),
removeEventListener: M('removeEventListener', 2, () => {}),
});
module.exports = performanceMock;