Skip to content

Commit 74a9a8a

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 74a9a8a

3 files changed

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