Skip to content

Commit 2ebef57

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 2ebef57

3 files changed

Lines changed: 127 additions & 11 deletions

File tree

lib/internal/perf/resource_timing.js

Lines changed: 33 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 {
@@ -191,13 +218,18 @@ ObjectDefineProperties(PerformanceResourceTiming.prototype, {
191218
connectEnd: kEnumerableProperty,
192219
secureConnectionStart: kEnumerableProperty,
193220
requestStart: kEnumerableProperty,
221+
finalResponseHeadersStart: kEnumerableProperty,
222+
firstInterimResponseStart: kEnumerableProperty,
194223
responseStart: kEnumerableProperty,
195224
responseEnd: kEnumerableProperty,
196225
transferSize: kEnumerableProperty,
197226
encodedBodySize: kEnumerableProperty,
198227
decodedBodySize: kEnumerableProperty,
199228
deliveryType: kEnumerableProperty,
200229
responseStatus: kEnumerableProperty,
230+
renderBlockingStatus: kEnumerableProperty,
231+
contentType: kEnumerableProperty,
232+
contentEncoding: kEnumerableProperty,
201233
toJSON: kEnumerableProperty,
202234
[SymbolToStringTag]: {
203235
__proto__: null,
@@ -224,6 +256,7 @@ function createPerformanceResourceTiming(
224256
// The spec doesn't say to validate it in the class construction.
225257
resourceTiming[kTimingInfo] = timingInfo;
226258
resourceTiming[kCacheMode] = cacheMode;
259+
resourceTiming[kBodyInfo] = bodyInfo;
227260
resourceTiming[kDeliveryType] = deliveryType;
228261
resourceTiming[kResponseStatus] = responseStatus;
229262

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
75+
// The attributes are enumerable getters on the prototype and perform a
76+
// brand check like the other PerformanceResourceTiming attributes.
77+
for (const name of [
78+
'finalResponseHeadersStart',
79+
'firstInterimResponseStart',
80+
'renderBlockingStatus',
81+
'contentType',
82+
'contentEncoding',
83+
]) {
84+
const desc = Object.getOwnPropertyDescriptor(
85+
PerformanceResourceTiming.prototype, name);
86+
assert.strictEqual(desc.enumerable, true, name);
87+
assert.strictEqual(typeof desc.get, 'function', name);
88+
assert.throws(() => desc.get.call({}), {
89+
code: 'ERR_INVALID_THIS',
90+
}, name);
91+
}
92+
93+
performance.clearResourceTimings();

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)