perf_hooks: add PerformanceResourceTiming

perf_hooks: create clearResourceTimings

perf_hooks: add resourcetiming test parallel

perf_hooks: add markResourceTiming

perf_hooks: fix observable when using resource

perf_hooks: fix observable when using resource

perf_hooks: add class comments

perf_hooks: add PerformanceResourceTiming

perf_hooks: create clearResourceTimings

perf_hooks: add resourcetiming test parallel

perf_hooks: add markResourceTiming

perf_hooks: fix observable when using resource

perf_hooks: fix observable when using resource

perf_hooks: add class comments

perf_hooks: add Resource Timing documentation

benchmark: measure resource timing module

perf_hooks: add check avoiding new PerformanceResourceTiming

perf_hooks: adjust doc

PR-URL: https://github.com/nodejs/node/pull/42725
Fixes: https://github.com/nodejs/undici/issues/952
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
RafaelGSS
2022-04-09 23:37:03 -03:00
parent 5e6f9c3e34
commit 78c8d2a8c8
8 changed files with 781 additions and 3 deletions

View File

@@ -0,0 +1,77 @@
'use strict';
const common = require('../common.js');
const {
PerformanceObserver,
performance,
} = require('perf_hooks');
function createTimingInfo({
startTime = 0,
redirectStartTime = 0,
redirectEndTime = 0,
postRedirectStartTime = 0,
finalServiceWorkerStartTime = 0,
finalNetworkRequestStartTime = 0,
finalNetworkResponseStartTime = 0,
endTime = 0,
encodedBodySize = 0,
decodedBodySize = 0,
finalConnectionTimingInfo = null
}) {
if (finalConnectionTimingInfo !== null) {
finalConnectionTimingInfo.domainLookupStartTime =
finalConnectionTimingInfo.domainLookupStartTime || 0;
finalConnectionTimingInfo.domainLookupEndTime =
finalConnectionTimingInfo.domainLookupEndTime || 0;
finalConnectionTimingInfo.connectionStartTime =
finalConnectionTimingInfo.connectionStartTime || 0;
finalConnectionTimingInfo.connectionEndTime =
finalConnectionTimingInfo.connectionEndTime || 0;
finalConnectionTimingInfo.secureConnectionStartTime =
finalConnectionTimingInfo.secureConnectionStartTime || 0;
finalConnectionTimingInfo.ALPNNegotiatedProtocol =
finalConnectionTimingInfo.ALPNNegotiatedProtocol || [];
}
return {
startTime,
redirectStartTime,
redirectEndTime,
postRedirectStartTime,
finalServiceWorkerStartTime,
finalNetworkRequestStartTime,
finalNetworkResponseStartTime,
endTime,
encodedBodySize,
decodedBodySize,
finalConnectionTimingInfo,
};
}
const bench = common.createBenchmark(main, {
n: [1e5],
observe: ['resource'],
});
function test() {
const timingInfo = createTimingInfo({ finalConnectionTimingInfo: {} });
performance.markResourceTiming(
timingInfo,
'http://localhost:8080',
'fetch',
{},
''
);
}
function main({ n, observe }) {
const obs = new PerformanceObserver(() => {
bench.end(n);
});
obs.observe({ entryTypes: [observe], buffered: true });
bench.start();
for (let i = 0; i < 1e5; i++)
test();
}

View File

@@ -15,6 +15,7 @@ Node.js supports the following [Web Performance APIs][]:
* [High Resolution Time][]
* [Performance Timeline][]
* [User Timing][]
* [Resource Timing][]
```js
const { PerformanceObserver, performance } = require('node:perf_hooks');
@@ -66,6 +67,17 @@ added: v16.7.0
If `name` is not provided, removes all `PerformanceMeasure` objects from the
Performance Timeline. If `name` is provided, removes only the named mark.
### `performance.clearResourceTimings([name])`
<!-- YAML
added: REPLACEME
-->
* `name` {string}
If `name` is not provided, removes all `PerformanceResourceTiming` objects from
the Resource Timeline. If `name` is provided, removes only the named resource.
### `performance.eventLoopUtilization([utilization1[, utilization2]])`
<!-- YAML
@@ -198,6 +210,33 @@ and can be queried with `performance.getEntries`,
observation is performed, the entries should be cleared from the global
Performance Timeline manually with `performance.clearMarks`.
### \`performance.markResourceTiming(timingInfo, requestedUrl, initiatorType,
global, cacheMode)\`
<!-- YAML
added: REPLACEME
-->
* `timingInfo` {Object} [Fetch Timing Info][]
* `requestedUrl` {string} The resource url
* `initiatorType` {string} The initiator name, e.g: 'fetch'
* `global` {Object}
* `cacheMode` {string} The cache mode must be an empty string ('') or 'local'
_This property is an extension by Node.js. It is not available in Web browsers._
Creates a new `PerformanceResourceTiming` entry in the Resource Timeline. A
`PerformanceResourceTiming` is a subclass of `PerformanceEntry` whose
`performanceEntry.entryType` is always `'resource'`. Performance resources
are used to mark moments in the Resource Timeline.
The created `PerformanceMark` entry is put in the global Resource Timeline
and can be queried with `performance.getEntries`,
`performance.getEntriesByName`, and `performance.getEntriesByType`. When the
observation is performed, the entries should be cleared from the global
Performance Timeline manually with `performance.clearResourceTimings`.
### `performance.measure(name[, startMarkOrOptions[, endMark]])`
<!-- YAML
@@ -653,6 +692,188 @@ added: v8.5.0
The high resolution millisecond timestamp at which the V8 platform was
initialized.
## Class: `PerformanceResourceTiming`
<!-- YAML
added: REPLACEME
-->
* Extends: {PerformanceEntry}
Provides detailed network timing data regarding the loading of an application's
resources.
The constructor of this class is not exposed to users directly.
### `performanceResourceTiming.workerStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp at immediately before dispatching
the `fetch` request. If the resource is not intercepted by a worker the property
will always return 0.
### `performanceResourceTiming.redirectStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp that represents the start time
of the fetch which initiates the redirect.
### `performanceResourceTiming.redirectEnd`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp that will be created immediately after
receiving the last byte of the response of the last redirect.
### `performanceResourceTiming.fetchStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp immediately before the Node.js starts
to fetch the resource.
### `performanceResourceTiming.domainLookupStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp immediately before the Node.js starts
the domain name lookup for the resource.
### `performanceResourceTiming.domainLookupEnd`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp representing the time immediately
after the Node.js finished the domain name lookup for the resource.
### `performanceResourceTiming.connectStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp representing the time immediately
before Node.js starts to establish the connection to the server to retrieve
the resource.
### `performanceResourceTiming.connectEnd`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp representing the time immediately
after Node.js finishes establishing the connection to the server to retrieve
the resource.
### `performanceResourceTiming.secureConnectionStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp representing the time immediately
before Node.js starts the handshake process to secure the current connection.
### `performanceResourceTiming.requestStart`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp representing the time immediately
before Node.js receives the first byte of the response from the server.
### `performanceResourceTiming.responseEnd`
<!-- YAML
added: REPLACEME
-->
* {number}
The high resolution millisecond timestamp representing the time immediately
after Node.js receives the last byte of the resource or immediately before
the transport connection is closed, whichever comes first.
### `performanceResourceTiming.transferSize`
<!-- YAML
added: REPLACEME
-->
* {number}
A number representing the size (in octets) of the fetched resource. The size
includes the response header fields plus the response payload body.
### `performanceResourceTiming.encodedBodySize`
<!-- YAML
added: REPLACEME
-->
* {number}
A number representing the size (in octets) received from the fetch
(HTTP or cache), of the payload body, before removing any applied
content-codings.
### `performanceResourceTiming.decodedBodySize`
<!-- YAML
added: REPLACEME
-->
* {number}
A number representing the size (in octets) received from the fetch
(HTTP or cache), of the message body, after removing any applied
content-codings.
### `performanceResourceTiming.toJSON()`
<!-- YAML
added: REPLACEME
-->
Returns a `object` that is the JSON representation of the
`PerformanceResourceTiming` object
## Class: `perf_hooks.PerformanceObserver`
### `new PerformanceObserver(callback)`
@@ -1367,8 +1588,10 @@ dns.promises.resolve('localhost');
```
[Async Hooks]: async_hooks.md
[Fetch Timing Info]: https://fetch.spec.whatwg.org/#fetch-timing-info
[High Resolution Time]: https://www.w3.org/TR/hr-time-2
[Performance Timeline]: https://w3c.github.io/performance-timeline/
[Resource Timing]: https://www.w3.org/TR/resource-timing-2/
[User Timing]: https://www.w3.org/TR/user-timing/
[Web Performance APIs]: https://w3c.github.io/perf-timing-primer/
[Worker threads]: worker_threads.md#worker-threads

View File

@@ -85,15 +85,18 @@ const kSupportedEntryTypes = ObjectFreeze([
'mark',
'measure',
'net',
'resource',
]);
// Performance timeline entry Buffers
let markEntryBuffer = [];
let measureEntryBuffer = [];
let resourceTimingBuffer = [];
const kMaxPerformanceEntryBuffers = 1e6;
const kClearPerformanceEntryBuffers = ObjectFreeze({
'mark': 'performance.clearMarks',
'measure': 'performance.clearMeasures',
'resource': 'performance.clearResourceTimings',
});
const kWarnedEntryTypes = new SafeMap();
@@ -340,6 +343,8 @@ function enqueue(entry) {
buffer = markEntryBuffer;
} else if (entryType === 'measure') {
buffer = measureEntryBuffer;
} else if (entryType === 'resource') {
buffer = resourceTimingBuffer;
} else {
return;
}
@@ -365,16 +370,19 @@ function enqueue(entry) {
}
function clearEntriesFromBuffer(type, name) {
if (type !== 'mark' && type !== 'measure') {
if (type !== 'mark' && type !== 'measure' && type !== 'resource') {
return;
}
if (type === 'mark') {
markEntryBuffer = name === undefined ?
[] : ArrayPrototypeFilter(markEntryBuffer, (entry) => entry.name !== name);
} else {
} else if (type === 'measure') {
measureEntryBuffer = name === undefined ?
[] : ArrayPrototypeFilter(measureEntryBuffer, (entry) => entry.name !== name);
} else {
resourceTimingBuffer = name === undefined ?
[] : ArrayPrototypeFilter(resourceTimingBuffer, (entry) => entry.name !== name);
}
}
@@ -384,11 +392,13 @@ function filterBufferMapByNameAndType(name, type) {
bufferList = markEntryBuffer;
} else if (type === 'measure') {
bufferList = measureEntryBuffer;
} else if (type === 'resource') {
bufferList = resourceTimingBuffer;
} else if (type !== undefined) {
// Unrecognized type;
return [];
} else {
bufferList = ArrayPrototypeConcat(markEntryBuffer, measureEntryBuffer);
bufferList = ArrayPrototypeConcat(markEntryBuffer, measureEntryBuffer, resourceTimingBuffer);
}
if (name !== undefined) {
bufferList = ArrayPrototypeFilter(bufferList, (buffer) => buffer.name === name);

View File

@@ -19,6 +19,8 @@ const {
const { now } = require('internal/perf/utils');
const { markResourceTiming } = require('internal/perf/resource_timing');
const {
mark,
measure,
@@ -82,6 +84,13 @@ function clearMeasures(name) {
clearEntriesFromBuffer('measure', name);
}
function clearResourceTimings(name) {
if (name !== undefined) {
name = `${name}`;
}
clearEntriesFromBuffer('resource', name);
}
function getEntries() {
return filterBufferMapByNameAndType();
}
@@ -117,6 +126,11 @@ ObjectDefineProperties(Performance.prototype, {
enumerable: false,
value: clearMeasures,
},
clearResourceTimings: {
configurable: true,
enumerable: false,
value: clearResourceTimings,
},
eventLoopUtilization: {
configurable: true,
enumerable: false,
@@ -152,6 +166,13 @@ ObjectDefineProperties(Performance.prototype, {
enumerable: false,
value: nodeTiming,
},
// In the browser, this function is not public. However, it must be used inside fetch
// which is a Node.js dependency, not a internal module
markResourceTiming: {
configurable: true,
enumerable: false,
value: markResourceTiming,
},
now: {
configurable: true,
enumerable: false,

View File

@@ -0,0 +1,179 @@
'use strict';
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
const { SymbolToStringTag } = primordials;
const assert = require('internal/assert');
const { enqueue } = require('internal/perf/observe');
const { Symbol, ObjectSetPrototypeOf } = primordials;
const kCacheMode = Symbol('kCacheMode');
const kRequestedUrl = Symbol('kRequestedUrl');
const kTimingInfo = Symbol('kTimingInfo');
const kInitiatorType = Symbol('kInitiatorType');
const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,
}
} = require('internal/errors');
class InternalPerformanceResourceTiming extends InternalPerformanceEntry {
constructor(requestedUrl, initiatorType, timingInfo, cacheMode = '') {
super(requestedUrl, 'resource');
this[kInitiatorType] = initiatorType;
this[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.
this[kTimingInfo] = timingInfo;
this[kCacheMode] = cacheMode;
}
get [SymbolToStringTag]() {
return 'PerformanceResourceTiming';
}
get name() {
return this[kRequestedUrl];
}
get startTime() {
return this[kTimingInfo].startTime;
}
get duration() {
return this[kTimingInfo].endTime - this[kTimingInfo].startTime;
}
get workerStart() {
return this[kTimingInfo].finalServiceWorkerStartTime;
}
get redirectStart() {
return this[kTimingInfo].redirectStartTime;
}
get redirectEnd() {
return this[kTimingInfo].redirectEndTime;
}
get fetchStart() {
return this[kTimingInfo].postRedirectStartTime;
}
get domainLookupStart() {
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupStartTime;
}
get domainLookupEnd() {
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupEndTime;
}
get connectStart() {
return this[kTimingInfo].finalConnectionTimingInfo?.connectionStartTime;
}
get connectEnd() {
return this[kTimingInfo].finalConnectionTimingInfo?.connectionEndTime;
}
get secureConnectionStart() {
return this[kTimingInfo]
.finalConnectionTimingInfo?.secureConnectionStartTime;
}
get nextHopProtocol() {
return this[kTimingInfo]
.finalConnectionTimingInfo?.ALPNNegotiatedProtocol;
}
get requestStart() {
return this[kTimingInfo].finalNetworkRequestStartTime;
}
get responseStart() {
return this[kTimingInfo].finalNetworkResponseStartTime;
}
get responseEnd() {
return this[kTimingInfo].endTime;
}
get encodedBodySize() {
return this[kTimingInfo].encodedBodySize;
}
get decodedBodySize() {
return this[kTimingInfo].decodedBodySize;
}
get transferSize() {
if (this[kCacheMode] === 'local') return 0;
if (this[kCacheMode] === 'validated') return 300;
return this[kTimingInfo].encodedBodySize + 300;
}
toJSON() {
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,
};
}
}
class PerformanceResourceTiming extends InternalPerformanceResourceTiming {
constructor() {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
}
// 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 = new InternalPerformanceResourceTiming(
requestedUrl,
initiatorType,
timingInfo,
cacheMode,
);
ObjectSetPrototypeOf(resource, PerformanceResourceTiming.prototype);
enqueue(resource);
return resource;
}
module.exports = {
PerformanceResourceTiming,
markResourceTiming,
};

View File

@@ -9,6 +9,7 @@ const {
} = internalBinding('performance');
const { PerformanceEntry } = require('internal/perf/performance_entry');
const { PerformanceResourceTiming } = require('internal/perf/resource_timing');
const {
PerformanceObserver,
PerformanceObserverEntryList,
@@ -31,6 +32,7 @@ module.exports = {
PerformanceMeasure,
PerformanceObserver,
PerformanceObserverEntryList,
PerformanceResourceTiming,
monitorEventLoopDelay,
createHistogram,
performance: new InternalPerformance(),

View File

@@ -99,6 +99,7 @@ const expectedModules = new Set([
'NativeModule internal/perf/performance',
'NativeModule internal/perf/timerify',
'NativeModule internal/perf/usertiming',
'NativeModule internal/perf/resource_timing',
'NativeModule internal/perf/utils',
'NativeModule internal/priority_queue',
'NativeModule internal/process/esm_loader',

View File

@@ -0,0 +1,265 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const {
PerformanceObserver,
PerformanceEntry,
PerformanceResourceTiming,
performance: {
clearResourceTimings,
markResourceTiming,
},
} = require('perf_hooks');
assert(PerformanceObserver);
assert(PerformanceEntry);
assert(PerformanceResourceTiming);
assert(clearResourceTimings);
assert(markResourceTiming);
function createTimingInfo({
startTime = 0,
redirectStartTime = 0,
redirectEndTime = 0,
postRedirectStartTime = 0,
finalServiceWorkerStartTime = 0,
finalNetworkRequestStartTime = 0,
finalNetworkResponseStartTime = 0,
endTime = 0,
encodedBodySize = 0,
decodedBodySize = 0,
finalConnectionTimingInfo = null
}) {
if (finalConnectionTimingInfo !== null) {
finalConnectionTimingInfo.domainLookupStartTime =
finalConnectionTimingInfo.domainLookupStartTime || 0;
finalConnectionTimingInfo.domainLookupEndTime =
finalConnectionTimingInfo.domainLookupEndTime || 0;
finalConnectionTimingInfo.connectionStartTime =
finalConnectionTimingInfo.connectionStartTime || 0;
finalConnectionTimingInfo.connectionEndTime =
finalConnectionTimingInfo.connectionEndTime || 0;
finalConnectionTimingInfo.secureConnectionStartTime =
finalConnectionTimingInfo.secureConnectionStartTime || 0;
finalConnectionTimingInfo.ALPNNegotiatedProtocol =
finalConnectionTimingInfo.ALPNNegotiatedProtocol || [];
}
return {
startTime,
redirectStartTime,
redirectEndTime,
postRedirectStartTime,
finalServiceWorkerStartTime,
finalNetworkRequestStartTime,
finalNetworkResponseStartTime,
endTime,
encodedBodySize,
decodedBodySize,
finalConnectionTimingInfo,
};
}
// PerformanceResourceTiming should not be initialized externally
{
assert.throws(() => new PerformanceResourceTiming(), {
name: 'TypeError',
message: 'Illegal constructor',
code: 'ERR_ILLEGAL_CONSTRUCTOR',
});
}
// Using performance.getEntries*()
{
const timingInfo = createTimingInfo({ finalConnectionTimingInfo: {} });
const customGlobal = {};
const requestedUrl = 'http://localhost:8080';
const cacheMode = 'local';
const initiatorType = 'fetch';
const resource = markResourceTiming(
timingInfo,
requestedUrl,
initiatorType,
customGlobal,
cacheMode,
);
assert(resource instanceof PerformanceEntry);
assert(resource instanceof PerformanceResourceTiming);
{
const entries = performance.getEntries();
assert.strictEqual(entries.length, 1);
assert(entries[0] instanceof PerformanceResourceTiming);
}
{
const entries = performance.getEntriesByType('resource');
assert.strictEqual(entries.length, 1);
assert(entries[0] instanceof PerformanceResourceTiming);
}
{
const entries = performance.getEntriesByName(resource.name);
assert.strictEqual(entries.length, 1);
assert(entries[0] instanceof PerformanceResourceTiming);
}
clearResourceTimings();
assert.strictEqual(performance.getEntries().length, 0);
}
// Assert resource data based in timingInfo
// default values
{
const timingInfo = createTimingInfo({ finalConnectionTimingInfo: {} });
const customGlobal = {};
const requestedUrl = 'http://localhost:8080';
const cacheMode = 'local';
const initiatorType = 'fetch';
const resource = markResourceTiming(
timingInfo,
requestedUrl,
initiatorType,
customGlobal,
cacheMode,
);
assert(resource instanceof PerformanceEntry);
assert(resource instanceof PerformanceResourceTiming);
assert.strictEqual(resource.entryType, 'resource');
assert.strictEqual(resource.name, requestedUrl);
assert.ok(typeof resource.cacheMode === 'undefined', 'cacheMode does not have a getter');
assert.strictEqual(resource.startTime, timingInfo.startTime);
assert.strictEqual(resource.duration, 0);
assert.strictEqual(resource.workerStart, 0);
assert.strictEqual(resource.redirectStart, 0);
assert.strictEqual(resource.redirectEnd, 0);
assert.strictEqual(resource.fetchStart, 0);
assert.strictEqual(resource.domainLookupStart, 0);
assert.strictEqual(resource.domainLookupEnd, 0);
assert.strictEqual(resource.connectStart, 0);
assert.strictEqual(resource.connectEnd, 0);
assert.strictEqual(resource.secureConnectionStart, 0);
assert.deepStrictEqual(resource.nextHopProtocol, []);
assert.strictEqual(resource.requestStart, 0);
assert.strictEqual(resource.responseStart, 0);
assert.strictEqual(resource.responseEnd, 0);
assert.strictEqual(resource.encodedBodySize, 0);
assert.strictEqual(resource.decodedBodySize, 0);
assert.strictEqual(resource.transferSize, 0);
assert.deepStrictEqual(resource.toJSON(), {
name: requestedUrl,
entryType: 'resource',
startTime: 0,
duration: 0,
initiatorType,
nextHopProtocol: [],
workerStart: 0,
redirectStart: 0,
redirectEnd: 0,
fetchStart: 0,
domainLookupStart: 0,
domainLookupEnd: 0,
connectStart: 0,
connectEnd: 0,
secureConnectionStart: 0,
requestStart: 0,
responseStart: 0,
responseEnd: 0,
transferSize: 0,
encodedBodySize: 0,
decodedBodySize: 0,
});
assert(resource instanceof PerformanceEntry);
assert(resource instanceof PerformanceResourceTiming);
clearResourceTimings();
const entries = performance.getEntries();
assert.strictEqual(entries.length, 0);
}
// custom getters math
{
const timingInfo = createTimingInfo({
endTime: 100,
startTime: 50,
encodedBodySize: 150,
});
const customGlobal = {};
const requestedUrl = 'http://localhost:8080';
const cacheMode = '';
const initiatorType = 'fetch';
const resource = markResourceTiming(
timingInfo,
requestedUrl,
initiatorType,
customGlobal,
cacheMode,
);
assert(resource instanceof PerformanceEntry);
assert(resource instanceof PerformanceResourceTiming);
assert.strictEqual(resource.entryType, 'resource');
assert.strictEqual(resource.name, requestedUrl);
assert.ok(typeof resource.cacheMode === 'undefined', 'cacheMode does not have a getter');
assert.strictEqual(resource.startTime, timingInfo.startTime);
// Duration should be the timingInfo endTime - startTime
assert.strictEqual(resource.duration, 50);
// TransferSize should be encodedBodySize + 300 when cacheMode is empty
assert.strictEqual(resource.transferSize, 450);
assert(resource instanceof PerformanceEntry);
assert(resource instanceof PerformanceResourceTiming);
clearResourceTimings();
const entries = performance.getEntries();
assert.strictEqual(entries.length, 0);
}
// Using PerformanceObserver
{
const obs = new PerformanceObserver(common.mustCall((list) => {
{
const entries = list.getEntries();
assert.strictEqual(entries.length, 1);
assert(entries[0] instanceof PerformanceResourceTiming);
}
{
const entries = list.getEntriesByType('resource');
assert.strictEqual(entries.length, 1);
assert(entries[0] instanceof PerformanceResourceTiming);
}
{
const entries = list.getEntriesByName('http://localhost:8080');
assert.strictEqual(entries.length, 1);
assert(entries[0] instanceof PerformanceResourceTiming);
}
obs.disconnect();
}));
obs.observe({ entryTypes: ['resource'] });
const timingInfo = createTimingInfo({ finalConnectionTimingInfo: {} });
const customGlobal = {};
const requestedUrl = 'http://localhost:8080';
const cacheMode = 'local';
const initiatorType = 'fetch';
const resource = markResourceTiming(
timingInfo,
requestedUrl,
initiatorType,
customGlobal,
cacheMode,
);
assert(resource instanceof PerformanceEntry);
assert(resource instanceof PerformanceResourceTiming);
clearResourceTimings();
const entries = performance.getEntries();
assert.strictEqual(entries.length, 0);
}