node/lib/internal/perf/resource_timing.js
Vinicius Lourenço e6e320ecc7
perf_hooks: reduce overhead of new resource timings
PR-URL: https://github.com/nodejs/node/pull/49837
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2023-09-30 21:44:02 +00:00

236 lines
7.3 KiB
JavaScript

'use strict';
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
const {
ObjectDefineProperties,
Symbol,
SymbolToStringTag,
} = primordials;
const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,
},
} = require('internal/errors');
const { PerformanceEntry, kSkipThrow } = require('internal/perf/performance_entry');
const assert = require('internal/assert');
const { enqueue, bufferResourceTiming } = require('internal/perf/observe');
const { validateInternalField } = require('internal/validators');
const { kEnumerableProperty } = require('internal/util');
const kCacheMode = Symbol('kCacheMode');
const kRequestedUrl = Symbol('kRequestedUrl');
const kTimingInfo = Symbol('kTimingInfo');
const kInitiatorType = Symbol('kInitiatorType');
class PerformanceResourceTiming extends PerformanceEntry {
constructor(skipThrowSymbol = undefined, name = undefined, type = undefined) {
if (skipThrowSymbol !== kSkipThrow) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
super(skipThrowSymbol, name, type);
}
get name() {
validateInternalField(this, kRequestedUrl, 'PerformanceResourceTiming');
return this[kRequestedUrl];
}
get startTime() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].startTime;
}
get duration() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].endTime - this[kTimingInfo].startTime;
}
get initiatorType() {
validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
return this[kInitiatorType];
}
get workerStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalServiceWorkerStartTime;
}
get redirectStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].redirectStartTime;
}
get redirectEnd() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].redirectEndTime;
}
get fetchStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].postRedirectStartTime;
}
get domainLookupStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupStartTime;
}
get domainLookupEnd() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupEndTime;
}
get connectStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalConnectionTimingInfo?.connectionStartTime;
}
get connectEnd() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalConnectionTimingInfo?.connectionEndTime;
}
get secureConnectionStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo]
.finalConnectionTimingInfo?.secureConnectionStartTime;
}
get nextHopProtocol() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo]
.finalConnectionTimingInfo?.ALPNNegotiatedProtocol;
}
get requestStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalNetworkRequestStartTime;
}
get responseStart() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].finalNetworkResponseStartTime;
}
get responseEnd() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].endTime;
}
get encodedBodySize() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].encodedBodySize;
}
get decodedBodySize() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kTimingInfo].decodedBodySize;
}
get transferSize() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
if (this[kCacheMode] === 'local') return 0;
if (this[kCacheMode] === 'validated') return 300;
return this[kTimingInfo].encodedBodySize + 300;
}
toJSON() {
validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
return {
name: this.name,
entryType: this.entryType,
startTime: this.startTime,
duration: this.duration,
initiatorType: this[kInitiatorType],
nextHopProtocol: this.nextHopProtocol,
workerStart: this.workerStart,
redirectStart: this.redirectStart,
redirectEnd: this.redirectEnd,
fetchStart: this.fetchStart,
domainLookupStart: this.domainLookupStart,
domainLookupEnd: this.domainLookupEnd,
connectStart: this.connectStart,
connectEnd: this.connectEnd,
secureConnectionStart: this.secureConnectionStart,
requestStart: this.requestStart,
responseStart: this.responseStart,
responseEnd: this.responseEnd,
transferSize: this.transferSize,
encodedBodySize: this.encodedBodySize,
decodedBodySize: this.decodedBodySize,
};
}
}
ObjectDefineProperties(PerformanceResourceTiming.prototype, {
initiatorType: kEnumerableProperty,
nextHopProtocol: kEnumerableProperty,
workerStart: kEnumerableProperty,
redirectStart: kEnumerableProperty,
redirectEnd: kEnumerableProperty,
fetchStart: kEnumerableProperty,
domainLookupStart: kEnumerableProperty,
domainLookupEnd: kEnumerableProperty,
connectStart: kEnumerableProperty,
connectEnd: kEnumerableProperty,
secureConnectionStart: kEnumerableProperty,
requestStart: kEnumerableProperty,
responseStart: kEnumerableProperty,
responseEnd: kEnumerableProperty,
transferSize: kEnumerableProperty,
encodedBodySize: kEnumerableProperty,
decodedBodySize: kEnumerableProperty,
toJSON: kEnumerableProperty,
[SymbolToStringTag]: {
__proto__: null,
configurable: true,
value: 'PerformanceResourceTiming',
},
});
function createPerformanceResourceTiming(requestedUrl, initiatorType, timingInfo, cacheMode = '') {
const resourceTiming = new PerformanceResourceTiming(kSkipThrow, requestedUrl, 'resource');
resourceTiming[kInitiatorType] = initiatorType;
resourceTiming[kRequestedUrl] = requestedUrl;
// https://fetch.spec.whatwg.org/#fetch-timing-info
// This class is using timingInfo assuming it's already validated.
// The spec doesn't say to validate it in the class construction.
resourceTiming[kTimingInfo] = timingInfo;
resourceTiming[kCacheMode] = cacheMode;
return resourceTiming;
}
// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
function markResourceTiming(
timingInfo,
requestedUrl,
initiatorType,
global,
cacheMode,
) {
// https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry
assert(
cacheMode === '' || cacheMode === 'local',
'cache must be an empty string or \'local\'',
);
const resource = createPerformanceResourceTiming(
requestedUrl,
initiatorType,
timingInfo,
cacheMode,
);
enqueue(resource);
bufferResourceTiming(resource);
return resource;
}
module.exports = {
PerformanceResourceTiming,
markResourceTiming,
};