Skip to content

Commit 94dca40

Browse files
committed
perf_hooks: add missing resource timing attributes
Add the finalResponseHeadersStart, firstInterimResponseStart, renderBlockingStatus, contentType and contentEncoding getters to PerformanceResourceTiming and update the WPT status accordingly. Signed-off-by: greenhead <shren0812@gmail.com>
1 parent 31cde9f commit 94dca40

4 files changed

Lines changed: 156 additions & 13 deletions

File tree

‎lib/internal/perf/resource_timing.js‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const { enqueue, bufferResourceTiming } = require('internal/perf/observe');
1717
const { validateThisInternalField } = require('internal/validators');
1818
const { kEnumerableProperty } = require('internal/util');
1919

20+
const kBodyInfo = Symbol('kBodyInfo');
2021
const kCacheMode = Symbol('kCacheMode');
2122
const kRequestedUrl = Symbol('kRequestedUrl');
2223
const kTimingInfo = Symbol('kTimingInfo');
@@ -110,6 +111,16 @@ class PerformanceResourceTiming extends PerformanceEntry {
110111
return this[kTimingInfo].finalNetworkRequestStartTime;
111112
}
112113

114+
get finalResponseHeadersStart() {
115+
validateThisInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
116+
return this[kTimingInfo].finalNetworkResponseStartTime;
117+
}
118+
119+
get firstInterimResponseStart() {
120+
validateThisInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
121+
return this[kTimingInfo].firstInterimNetworkResponseStartTime ?? 0;
122+
}
123+
113124
get responseStart() {
114125
validateThisInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
115126
return this[kTimingInfo].finalNetworkResponseStartTime;
@@ -148,6 +159,22 @@ class PerformanceResourceTiming extends PerformanceEntry {
148159
return this[kResponseStatus];
149160
}
150161

162+
get renderBlockingStatus() {
163+
validateThisInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
164+
return this[kTimingInfo].renderBlocking === true ?
165+
'blocking' : 'non-blocking';
166+
}
167+
168+
get contentType() {
169+
validateThisInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
170+
return this[kBodyInfo]?.contentType ?? '';
171+
}
172+
173+
get contentEncoding() {
174+
validateThisInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
175+
return this[kBodyInfo]?.contentEncoding ?? '';
176+
}
177+
151178
toJSON() {
152179
validateThisInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
153180
return {
@@ -167,13 +194,18 @@ class PerformanceResourceTiming extends PerformanceEntry {
167194
connectEnd: this.connectEnd,
168195
secureConnectionStart: this.secureConnectionStart,
169196
requestStart: this.requestStart,
197+
finalResponseHeadersStart: this.finalResponseHeadersStart,
198+
firstInterimResponseStart: this.firstInterimResponseStart,
170199
responseStart: this.responseStart,
171200
responseEnd: this.responseEnd,
172201
transferSize: this.transferSize,
173202
encodedBodySize: this.encodedBodySize,
174203
decodedBodySize: this.decodedBodySize,
175204
deliveryType: this.deliveryType,
176205
responseStatus: this.responseStatus,
206+
renderBlockingStatus: this.renderBlockingStatus,
207+
contentType: this.contentType,
208+
contentEncoding: this.contentEncoding,
177209
};
178210
}
179211
}
@@ -191,13 +223,18 @@ ObjectDefineProperties(PerformanceResourceTiming.prototype, {
191223
connectEnd: kEnumerableProperty,
192224
secureConnectionStart: kEnumerableProperty,
193225
requestStart: kEnumerableProperty,
226+
finalResponseHeadersStart: kEnumerableProperty,
227+
firstInterimResponseStart: kEnumerableProperty,
194228
responseStart: kEnumerableProperty,
195229
responseEnd: kEnumerableProperty,
196230
transferSize: kEnumerableProperty,
197231
encodedBodySize: kEnumerableProperty,
198232
decodedBodySize: kEnumerableProperty,
199233
deliveryType: kEnumerableProperty,
200234
responseStatus: kEnumerableProperty,
235+
renderBlockingStatus: kEnumerableProperty,
236+
contentType: kEnumerableProperty,
237+
contentEncoding: kEnumerableProperty,
201238
toJSON: kEnumerableProperty,
202239
[SymbolToStringTag]: {
203240
__proto__: null,
@@ -224,6 +261,7 @@ function createPerformanceResourceTiming(
224261
// The spec doesn't say to validate it in the class construction.
225262
resourceTiming[kTimingInfo] = timingInfo;
226263
resourceTiming[kCacheMode] = cacheMode;
264+
resourceTiming[kBodyInfo] = bodyInfo;
227265
resourceTiming[kDeliveryType] = deliveryType;
228266
resourceTiming[kResponseStatus] = responseStatus;
229267

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
const {
6+
PerformanceResourceTiming,
7+
performance,
8+
} = require('perf_hooks');
9+
10+
// Covers the IDL attributes finalResponseHeadersStart,
11+
// firstInterimResponseStart, renderBlockingStatus, contentType and contentEncoding.
12+
13+
function createTimingInfo(overrides = {}) {
14+
return {
15+
startTime: 0,
16+
redirectStartTime: 0,
17+
redirectEndTime: 0,
18+
postRedirectStartTime: 0,
19+
finalServiceWorkerStartTime: 0,
20+
finalNetworkRequestStartTime: 0,
21+
finalNetworkResponseStartTime: 0,
22+
endTime: 0,
23+
encodedBodySize: 0,
24+
decodedBodySize: 0,
25+
finalConnectionTimingInfo: null,
26+
...overrides,
27+
};
28+
}
29+
30+
function markResourceTiming(timingInfo, bodyInfo) {
31+
return performance.markResourceTiming(
32+
timingInfo,
33+
'http://localhost:8080',
34+
'fetch',
35+
{},
36+
'',
37+
bodyInfo,
38+
200,
39+
'',
40+
);
41+
}
42+
43+
// Default values with an empty body info, mirroring what the fetch
44+
// implementation passes for a response with no body metadata.
45+
{
46+
const resource = markResourceTiming(createTimingInfo(), {});
47+
48+
assert.strictEqual(resource.finalResponseHeadersStart, 0);
49+
assert.strictEqual(resource.firstInterimResponseStart, 0);
50+
assert.strictEqual(resource.renderBlockingStatus, 'non-blocking');
51+
assert.strictEqual(resource.contentType, '');
52+
assert.strictEqual(resource.contentEncoding, '');
53+
}
54+
55+
// Values reflected from timing info and body info.
56+
{
57+
const timingInfo = createTimingInfo({
58+
finalNetworkResponseStartTime: 123,
59+
firstInterimNetworkResponseStartTime: 45,
60+
renderBlocking: true,
61+
});
62+
const bodyInfo = {
63+
contentType: 'text/html',
64+
contentEncoding: 'gzip',
65+
};
66+
const resource = markResourceTiming(timingInfo, bodyInfo);
67+
68+
assert.strictEqual(resource.finalResponseHeadersStart, 123);
69+
assert.strictEqual(resource.firstInterimResponseStart, 45);
70+
assert.strictEqual(resource.renderBlockingStatus, 'blocking');
71+
assert.strictEqual(resource.contentType, 'text/html');
72+
assert.strictEqual(resource.contentEncoding, 'gzip');
73+
74+
const json = resource.toJSON();
75+
assert.strictEqual(json.finalResponseHeadersStart, 123);
76+
assert.strictEqual(json.firstInterimResponseStart, 45);
77+
assert.strictEqual(json.renderBlockingStatus, 'blocking');
78+
assert.strictEqual(json.contentType, 'text/html');
79+
assert.strictEqual(json.contentEncoding, 'gzip');
80+
}
81+
82+
// The attributes are enumerable getters on the prototype and perform a
83+
// brand check like the other PerformanceResourceTiming attributes.
84+
for (const name of [
85+
'finalResponseHeadersStart',
86+
'firstInterimResponseStart',
87+
'renderBlockingStatus',
88+
'contentType',
89+
'contentEncoding',
90+
]) {
91+
const desc = Object.getOwnPropertyDescriptor(
92+
PerformanceResourceTiming.prototype, name);
93+
assert.strictEqual(desc.enumerable, true, name);
94+
assert.strictEqual(typeof desc.get, 'function', name);
95+
assert.throws(() => desc.get.call({}), {
96+
code: 'ERR_INVALID_THIS',
97+
}, name);
98+
}
99+
100+
performance.clearResourceTimings();

‎test/parallel/test-perf-hooks-resourcetiming.js‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,18 @@ function createTimingInfo({
174174
connectEnd: 0,
175175
secureConnectionStart: 0,
176176
requestStart: 0,
177+
finalResponseHeadersStart: 0,
178+
firstInterimResponseStart: 0,
177179
responseStart: 0,
178180
responseEnd: 0,
179181
transferSize: 0,
180182
encodedBodySize: 0,
181183
decodedBodySize: 0,
182184
responseStatus: 200,
183185
deliveryType: '',
186+
renderBlockingStatus: 'non-blocking',
187+
contentType: '',
188+
contentEncoding: '',
184189
});
185190
assert.strictEqual(util.inspect(performance.getEntries()), `[
186191
PerformanceResourceTiming {
@@ -200,13 +205,18 @@ function createTimingInfo({
200205
connectEnd: 0,
201206
secureConnectionStart: 0,
202207
requestStart: 0,
208+
finalResponseHeadersStart: 0,
209+
firstInterimResponseStart: 0,
203210
responseStart: 0,
204211
responseEnd: 0,
205212
transferSize: 0,
206213
encodedBodySize: 0,
207214
decodedBodySize: 0,
208215
deliveryType: '',
209-
responseStatus: 200
216+
responseStatus: 200,
217+
renderBlockingStatus: 'non-blocking',
218+
contentType: '',
219+
contentEncoding: ''
210220
}
211221
]`);
212222
assert.strictEqual(util.inspect(resource), `PerformanceResourceTiming {
@@ -226,13 +236,18 @@ function createTimingInfo({
226236
connectEnd: 0,
227237
secureConnectionStart: 0,
228238
requestStart: 0,
239+
finalResponseHeadersStart: 0,
240+
firstInterimResponseStart: 0,
229241
responseStart: 0,
230242
responseEnd: 0,
231243
transferSize: 0,
232244
encodedBodySize: 0,
233245
decodedBodySize: 0,
234246
deliveryType: '',
235-
responseStatus: 200
247+
responseStatus: 200,
248+
renderBlockingStatus: 'non-blocking',
249+
contentType: '',
250+
contentEncoding: ''
236251
}`);
237252

238253
assert(resource instanceof PerformanceEntry);

‎test/wpt/status/resource-timing.json‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,15 @@
2323
"idlharness.any.js": {
2424
"fail": {
2525
"expected": [
26-
"PerformanceResourceTiming interface: attribute firstInterimResponseStart",
27-
"PerformanceResourceTiming interface: attribute finalResponseHeadersStart",
28-
"PerformanceResourceTiming interface: resource must inherit property \"finalResponseHeadersStart\" with the proper type",
29-
"PerformanceResourceTiming interface: attribute renderBlockingStatus",
30-
"PerformanceResourceTiming interface: attribute contentType",
31-
"PerformanceResourceTiming interface: resource must inherit property \"firstInterimResponseStart\" with the proper type",
32-
"PerformanceResourceTiming interface: resource must inherit property \"renderBlockingStatus\" with the proper type",
33-
"PerformanceResourceTiming interface: resource must inherit property \"contentType\" with the proper type",
3426
"PerformanceResourceTiming interface: default toJSON operation on resource",
3527
"PerformanceResourceTiming interface: attribute workerRouterEvaluationStart",
3628
"PerformanceResourceTiming interface: attribute workerCacheLookupStart",
3729
"PerformanceResourceTiming interface: attribute workerMatchedRouterSource",
3830
"PerformanceResourceTiming interface: attribute workerFinalRouterSource",
39-
"PerformanceResourceTiming interface: attribute contentEncoding",
4031
"PerformanceResourceTiming interface: resource must inherit property \"workerRouterEvaluationStart\" with the proper type",
4132
"PerformanceResourceTiming interface: resource must inherit property \"workerCacheLookupStart\" with the proper type",
4233
"PerformanceResourceTiming interface: resource must inherit property \"workerMatchedRouterSource\" with the proper type",
43-
"PerformanceResourceTiming interface: resource must inherit property \"workerFinalRouterSource\" with the proper type",
44-
"PerformanceResourceTiming interface: resource must inherit property \"contentEncoding\" with the proper type"
34+
"PerformanceResourceTiming interface: resource must inherit property \"workerFinalRouterSource\" with the proper type"
4535
]
4636
}
4737
}

0 commit comments

Comments
 (0)