Skip to content

Commit e23178d

Browse files
committed
perf_hooks: add PerformanceResourceTiming base impl
perf_hooks: create clearResourceTimings perf_hooks: add resourcetiming test parallel perf_hooks: add markResourceTiming perf_hooks: fix observable when using resource perf_hooks: remove todo comments
1 parent b98386c commit e23178d

File tree

5 files changed

+450
-3
lines changed

5 files changed

+450
-3
lines changed

lib/internal/perf/observe.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,18 @@ const kSupportedEntryTypes = ObjectFreeze([
8585
'mark',
8686
'measure',
8787
'net',
88+
'resource',
8889
]);
8990

9091
// Performance timeline entry Buffers
9192
let markEntryBuffer = [];
9293
let measureEntryBuffer = [];
94+
let resourceTimingBuffer = [];
9395
const kMaxPerformanceEntryBuffers = 1e6;
9496
const kClearPerformanceEntryBuffers = ObjectFreeze({
9597
'mark': 'performance.clearMarks',
9698
'measure': 'performance.clearMeasures',
99+
'resource': 'performance.clearResourceTimings',
97100
});
98101
const kWarnedEntryTypes = new SafeMap();
99102

@@ -340,6 +343,8 @@ function enqueue(entry) {
340343
buffer = markEntryBuffer;
341344
} else if (entryType === 'measure') {
342345
buffer = measureEntryBuffer;
346+
} else if (entryType === 'resource') {
347+
buffer = resourceTimingBuffer;
343348
} else {
344349
return;
345350
}
@@ -365,16 +370,19 @@ function enqueue(entry) {
365370
}
366371

367372
function clearEntriesFromBuffer(type, name) {
368-
if (type !== 'mark' && type !== 'measure') {
373+
if (type !== 'mark' && type !== 'measure' && type !== 'resource') {
369374
return;
370375
}
371376

372377
if (type === 'mark') {
373378
markEntryBuffer = name === undefined ?
374379
[] : ArrayPrototypeFilter(markEntryBuffer, (entry) => entry.name !== name);
375-
} else {
380+
} else if (type === 'measure') {
376381
measureEntryBuffer = name === undefined ?
377382
[] : ArrayPrototypeFilter(measureEntryBuffer, (entry) => entry.name !== name);
383+
} else {
384+
resourceTimingBuffer = name === undefined ?
385+
[] : ArrayPrototypeFilter(resourceTimingBuffer, (entry) => entry.name !== name);
378386
}
379387
}
380388

@@ -384,11 +392,13 @@ function filterBufferMapByNameAndType(name, type) {
384392
bufferList = markEntryBuffer;
385393
} else if (type === 'measure') {
386394
bufferList = measureEntryBuffer;
395+
} else if (type === 'resource') {
396+
bufferList = resourceTimingBuffer;
387397
} else if (type !== undefined) {
388398
// Unrecognized type;
389399
return [];
390400
} else {
391-
bufferList = ArrayPrototypeConcat(markEntryBuffer, measureEntryBuffer);
401+
bufferList = ArrayPrototypeConcat(markEntryBuffer, measureEntryBuffer, resourceTimingBuffer);
392402
}
393403
if (name !== undefined) {
394404
bufferList = ArrayPrototypeFilter(bufferList, (buffer) => buffer.name === name);

lib/internal/perf/performance.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const {
1919

2020
const { now } = require('internal/perf/utils');
2121

22+
const { markResourceTiming } = require('internal/perf/resource_timing');
23+
2224
const {
2325
mark,
2426
measure,
@@ -82,6 +84,13 @@ function clearMeasures(name) {
8284
clearEntriesFromBuffer('measure', name);
8385
}
8486

87+
function clearResourceTimings(name) {
88+
if (name !== undefined) {
89+
name = `${name}`;
90+
}
91+
clearEntriesFromBuffer('resource', name);
92+
}
93+
8594
function getEntries() {
8695
return filterBufferMapByNameAndType();
8796
}
@@ -117,6 +126,11 @@ ObjectDefineProperties(Performance.prototype, {
117126
enumerable: false,
118127
value: clearMeasures,
119128
},
129+
clearResourceTimings: {
130+
configurable: true,
131+
enumerable: false,
132+
value: clearResourceTimings,
133+
},
120134
eventLoopUtilization: {
121135
configurable: true,
122136
enumerable: false,
@@ -152,6 +166,11 @@ ObjectDefineProperties(Performance.prototype, {
152166
enumerable: false,
153167
value: nodeTiming,
154168
},
169+
markResourceTiming: {
170+
configurable: true,
171+
enumerable: false,
172+
value: markResourceTiming,
173+
},
155174
now: {
156175
configurable: true,
157176
enumerable: false,

lib/internal/perf/resource_timing.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
'use strict';
2+
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
3+
4+
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
5+
const { SymbolToStringTag } = primordials;
6+
const assert = require('assert');
7+
const { enqueue } = require('internal/perf/observe');
8+
const { Symbol } = primordials;
9+
10+
const kCacheMode = Symbol('kCacheMode');
11+
const kRequestedUrl = Symbol('kRequestedUrl');
12+
const kTimingInfo = Symbol('kTimingInfo');
13+
const kInitiatorType = Symbol('kInitiatorType');
14+
15+
class PerformanceResourceTiming extends InternalPerformanceEntry {
16+
constructor(requestedUrl, initiatorType, timingInfo, cacheMode = '') {
17+
super(requestedUrl, 'resource');
18+
this[kInitiatorType] = initiatorType;
19+
this[kRequestedUrl] = requestedUrl;
20+
// https://fetch.spec.whatwg.org/#fetch-timing-info
21+
this[kTimingInfo] = timingInfo;
22+
this[kCacheMode] = cacheMode;
23+
}
24+
25+
get [SymbolToStringTag]() {
26+
return 'PerformanceResourceTiming';
27+
}
28+
29+
get name() {
30+
return this[kRequestedUrl];
31+
}
32+
33+
get startTime() {
34+
return this[kTimingInfo].startTime;
35+
}
36+
37+
get duration() {
38+
return this[kTimingInfo].endTime - this[kTimingInfo].startTime;
39+
}
40+
41+
get workerStart() {
42+
return this[kTimingInfo].finalServiceWorkerStartTime;
43+
}
44+
45+
get redirectStart() {
46+
return this[kTimingInfo].redirectStartTime;
47+
}
48+
49+
get redirectEnd() {
50+
return this[kTimingInfo].redirectEndTime;
51+
}
52+
53+
get fetchStart() {
54+
return this[kTimingInfo].postRedirectStartTime;
55+
}
56+
57+
get domainLookupStart() {
58+
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupStartTime;
59+
}
60+
61+
get domainLookupEnd() {
62+
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupEndTime;
63+
}
64+
65+
get connectStart() {
66+
return this[kTimingInfo].finalConnectionTimingInfo?.connectionStartTime;
67+
}
68+
69+
get connectEnd() {
70+
return this[kTimingInfo].finalConnectionTimingInfo?.connectionEndTime;
71+
}
72+
73+
get secureConnectionStart() {
74+
return this[kTimingInfo]
75+
.finalConnectionTimingInfo?.secureConnectionStartTime;
76+
}
77+
78+
get nextHopProtocol() {
79+
return this[kTimingInfo]
80+
.finalConnectionTimingInfo?.ALPNNegotiatedProtocol;
81+
}
82+
83+
get requestStart() {
84+
return this[kTimingInfo].finalNetworkRequestStartTime;
85+
}
86+
87+
get responseStart() {
88+
return this[kTimingInfo].finalNetworkResponseStartTime;
89+
}
90+
91+
get responseEnd() {
92+
return this[kTimingInfo].endTime;
93+
}
94+
95+
get encodedBodySize() {
96+
return this[kTimingInfo].encodedBodySize;
97+
}
98+
99+
get decodedBodySize() {
100+
return this[kTimingInfo].decodedBodySize;
101+
}
102+
103+
get transferSize() {
104+
if (this[kCacheMode] === 'local') return 0;
105+
if (this[kCacheMode] === 'validated') return 300;
106+
107+
return this[kTimingInfo].encodedBodySize + 300;
108+
}
109+
110+
toJSON() {
111+
return {
112+
name: this.name,
113+
entryType: this.entryType,
114+
startTime: this.startTime,
115+
duration: this.duration,
116+
initiatorType: this[kInitiatorType],
117+
nextHopProtocol: this.nextHopProtocol,
118+
workerStart: this.workerStart,
119+
redirectStart: this.redirectStart,
120+
redirectEnd: this.redirectEnd,
121+
fetchStart: this.fetchStart,
122+
domainLookupStart: this.domainLookupStart,
123+
domainLookupEnd: this.domainLookupEnd,
124+
connectStart: this.connectStart,
125+
connectEnd: this.connectEnd,
126+
secureConnectionStart: this.secureConnectionStart,
127+
requestStart: this.requestStart,
128+
responseStart: this.responseStart,
129+
responseEnd: this.responseEnd,
130+
transferSize: this.transferSize,
131+
encodedBodySize: this.encodedBodySize,
132+
decodedBodySize: this.decodedBodySize,
133+
};
134+
}
135+
}
136+
137+
// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
138+
function markResourceTiming(
139+
timingInfo,
140+
requestedUrl,
141+
initiatorType,
142+
global,
143+
cacheMode,
144+
) {
145+
// https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry
146+
assert.ok(cacheMode === '' || cacheMode === 'local');
147+
const resource = new PerformanceResourceTiming(
148+
requestedUrl,
149+
initiatorType,
150+
timingInfo,
151+
cacheMode,
152+
);
153+
enqueue(resource);
154+
return resource;
155+
}
156+
157+
module.exports = {
158+
PerformanceResourceTiming,
159+
markResourceTiming,
160+
};

lib/perf_hooks.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
} = internalBinding('performance');
1010

1111
const { PerformanceEntry } = require('internal/perf/performance_entry');
12+
const { PerformanceResourceTiming } = require('internal/perf/resource_timing');
1213
const {
1314
PerformanceObserver,
1415
PerformanceObserverEntryList,
@@ -31,6 +32,7 @@ module.exports = {
3132
PerformanceMeasure,
3233
PerformanceObserver,
3334
PerformanceObserverEntryList,
35+
PerformanceResourceTiming,
3436
monitorEventLoopDelay,
3537
createHistogram,
3638
performance: new InternalPerformance(),

0 commit comments

Comments
 (0)