210 lines
6.7 KiB
JavaScript
210 lines
6.7 KiB
JavaScript
'use strict';
|
||
/**
|
||
* 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 { nativeMethod: M } = require('./native');
|
||
const {
|
||
Performance,
|
||
PerformanceEntry,
|
||
PerformanceResourceTiming,
|
||
PerformanceNavigationTiming,
|
||
} = require('./class_registry');
|
||
|
||
const NAV_START = Date.now() - 1200;
|
||
|
||
const timingData = {
|
||
navigationStart: NAV_START,
|
||
fetchStart: NAV_START + 11,
|
||
domainLookupStart: NAV_START + 11,
|
||
domainLookupEnd: NAV_START + 11,
|
||
connectStart: NAV_START + 11,
|
||
secureConnectionStart: NAV_START + 11,
|
||
connectEnd: NAV_START + 11,
|
||
requestStart: NAV_START + 37,
|
||
responseStart: NAV_START + 47,
|
||
responseEnd: NAV_START + 114,
|
||
domLoading: NAV_START + 203,
|
||
domInteractive: NAV_START + 399,
|
||
domContentLoadedEventStart: NAV_START + 399,
|
||
domContentLoadedEventEnd: NAV_START + 399,
|
||
domComplete: NAV_START + 399,
|
||
loadEventStart: NAV_START + 399,
|
||
loadEventEnd: NAV_START + 399,
|
||
redirectStart: 0,
|
||
redirectEnd: 0,
|
||
unloadEventStart: 0,
|
||
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',
|
||
startTime: 399.2,
|
||
duration: 643.1,
|
||
fetchStart: 399.2,
|
||
responseEnd: 1042.3,
|
||
transferSize: 0,
|
||
encodedBodySize: 0,
|
||
decodedBodySize: 0,
|
||
responseStatus: 200,
|
||
deliveryType: '',
|
||
nextHopProtocol: '',
|
||
contentEncoding: 'br',
|
||
workerStart: 0,
|
||
redirectStart: 0,
|
||
redirectEnd: 0,
|
||
domainLookupStart: 0,
|
||
domainLookupEnd: 0,
|
||
connectStart: 0,
|
||
secureConnectionStart: 0,
|
||
connectEnd: 0,
|
||
requestStart: 0,
|
||
responseStart: 0,
|
||
firstInterimResponseStart: 0,
|
||
finalResponseHeadersStart: 0,
|
||
serverTiming: [],
|
||
renderBlockingStatus: 'non-blocking',
|
||
}),
|
||
makeResourceEntry({
|
||
name: 'https://newassets.hcaptcha.com/c/xxx/hsw.js',
|
||
entryType: 'resource',
|
||
initiatorType: 'script',
|
||
deliveryType: 'cache',
|
||
nextHopProtocol: 'h2',
|
||
startTime: 1043.8,
|
||
duration: 5.7,
|
||
fetchStart: 1043.8,
|
||
domainLookupStart: 1043.8,
|
||
domainLookupEnd: 1043.8,
|
||
connectStart: 1043.8,
|
||
secureConnectionStart: 1043.8,
|
||
connectEnd: 1043.8,
|
||
requestStart: 1044.6,
|
||
responseStart: 1044.6,
|
||
firstInterimResponseStart: 1044.6,
|
||
finalResponseHeadersStart: 0,
|
||
responseEnd: 1049.5,
|
||
transferSize: 0,
|
||
encodedBodySize: 359059,
|
||
decodedBodySize: 829689,
|
||
responseStatus: 200,
|
||
contentEncoding: 'gzip',
|
||
workerStart: 0,
|
||
redirectStart: 0,
|
||
redirectEnd: 0,
|
||
serverTiming: [],
|
||
renderBlockingStatus: 'non-blocking',
|
||
}),
|
||
];
|
||
|
||
// 模拟 navigation 条目
|
||
const navigationEntry = makeNavEntry({
|
||
name: 'https://newassets.hcaptcha.com/captcha/v1/xxx/static/hcaptcha.html',
|
||
entryType: 'navigation',
|
||
initiatorType: 'navigation',
|
||
deliveryType: 'cache',
|
||
nextHopProtocol: 'h2',
|
||
startTime: 0,
|
||
duration: 399.9,
|
||
fetchStart: 11.6,
|
||
domainLookupStart: 11.6,
|
||
domainLookupEnd: 11.6,
|
||
connectStart: 11.6,
|
||
secureConnectionStart: 11.6,
|
||
connectEnd: 11.6,
|
||
requestStart: 37.6,
|
||
responseStart: 47.4,
|
||
firstInterimResponseStart: 47.4,
|
||
finalResponseHeadersStart: 0,
|
||
responseEnd: 114.2,
|
||
transferSize: 0,
|
||
encodedBodySize: 167487,
|
||
decodedBodySize: 567885,
|
||
responseStatus: 200,
|
||
redirectStart: 0,
|
||
redirectEnd: 0,
|
||
unloadEventStart: 0,
|
||
unloadEventEnd: 0,
|
||
domInteractive: 399.4,
|
||
domContentLoadedEventStart: 399.5,
|
||
domContentLoadedEventEnd: 399.5,
|
||
domComplete: 399.8,
|
||
loadEventStart: 399.9,
|
||
loadEventEnd: 399.9,
|
||
type: 'navigate',
|
||
redirectCount: 0,
|
||
activationStart: 0,
|
||
criticalCHRestart: 0,
|
||
notRestoredReasons: null,
|
||
confidence: null,
|
||
serverTiming: [],
|
||
workerStart: 0,
|
||
contentEncoding: 'br',
|
||
renderBlockingStatus: 'non-blocking',
|
||
});
|
||
|
||
// ── 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 },
|
||
|
||
// 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: M('getEntriesByName', 1, (name) => {
|
||
return [...resourceEntries, navigationEntry].filter(e => e.name === name);
|
||
}),
|
||
|
||
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;
|