blob: 06250360cb6c95a06f27ed46e2b5610d15bc6187 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
Tim van der Lippe0ed1d2b2020-02-04 13:45:1331import * as Common from '../common/common.js';
Simon Zünd58919b42020-10-08 09:23:2832import * as ObjectUI from '../object_ui/object_ui.js';
Tim van der Lippe0ed1d2b2020-02-04 13:45:1333import * as SDK from '../sdk/sdk.js';
34import * as UI from '../ui/ui.js';
35
Tim van der Lippe119690c2020-01-13 12:31:3036import {Events, NetworkTimeCalculator} from './NetworkTimeCalculator.js'; // eslint-disable-line no-unused-vars
37
Blink Reformat4c46d092018-04-07 15:32:3738/**
39 * @unrestricted
40 */
Tim van der Lippe0ed1d2b2020-02-04 13:45:1341export class RequestTimingView extends UI.Widget.VBox {
Blink Reformat4c46d092018-04-07 15:32:3742 /**
Tim van der Lippe0ed1d2b2020-02-04 13:45:1343 * @param {!SDK.NetworkRequest.NetworkRequest} request
Tim van der Lippe119690c2020-01-13 12:31:3044 * @param {!NetworkTimeCalculator} calculator
Blink Reformat4c46d092018-04-07 15:32:3745 */
46 constructor(request, calculator) {
47 super();
48 this.element.classList.add('resource-timing-view');
49
50 this._request = request;
51 this._calculator = calculator;
52 }
53
54 /**
Paul Lewis56509652019-12-06 12:51:5855 * @param {!RequestTimeRangeNames} name
Blink Reformat4c46d092018-04-07 15:32:3756 * @return {string}
57 */
58 static _timeRangeTitle(name) {
59 switch (name) {
Paul Lewis56509652019-12-06 12:51:5860 case RequestTimeRangeNames.Push:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1361 return Common.UIString.UIString('Receiving Push');
Paul Lewis56509652019-12-06 12:51:5862 case RequestTimeRangeNames.Queueing:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1363 return Common.UIString.UIString('Queueing');
Paul Lewis56509652019-12-06 12:51:5864 case RequestTimeRangeNames.Blocking:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1365 return Common.UIString.UIString('Stalled');
Paul Lewis56509652019-12-06 12:51:5866 case RequestTimeRangeNames.Connecting:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1367 return Common.UIString.UIString('Initial connection');
Paul Lewis56509652019-12-06 12:51:5868 case RequestTimeRangeNames.DNS:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1369 return Common.UIString.UIString('DNS Lookup');
Paul Lewis56509652019-12-06 12:51:5870 case RequestTimeRangeNames.Proxy:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1371 return Common.UIString.UIString('Proxy negotiation');
Paul Lewis56509652019-12-06 12:51:5872 case RequestTimeRangeNames.ReceivingPush:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1373 return Common.UIString.UIString('Reading Push');
Paul Lewis56509652019-12-06 12:51:5874 case RequestTimeRangeNames.Receiving:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1375 return Common.UIString.UIString('Content Download');
Paul Lewis56509652019-12-06 12:51:5876 case RequestTimeRangeNames.Sending:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1377 return Common.UIString.UIString('Request sent');
Paul Lewis56509652019-12-06 12:51:5878 case RequestTimeRangeNames.ServiceWorker:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1379 return Common.UIString.UIString('Request to ServiceWorker');
Paul Lewis56509652019-12-06 12:51:5880 case RequestTimeRangeNames.ServiceWorkerPreparation:
Anubha Mathur88bc40d2020-06-02 20:56:5981 return Common.UIString.UIString('Startup');
82 case RequestTimeRangeNames.ServiceWorkerRespondWith:
83 return Common.UIString.UIString('respondWith');
Paul Lewis56509652019-12-06 12:51:5884 case RequestTimeRangeNames.SSL:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1385 return Common.UIString.UIString('SSL');
Paul Lewis56509652019-12-06 12:51:5886 case RequestTimeRangeNames.Total:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1387 return Common.UIString.UIString('Total');
Paul Lewis56509652019-12-06 12:51:5888 case RequestTimeRangeNames.Waiting:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1389 return Common.UIString.UIString('Waiting (TTFB)');
Blink Reformat4c46d092018-04-07 15:32:3790 default:
Tim van der Lippe0ed1d2b2020-02-04 13:45:1391 return Common.UIString.UIString(name);
Blink Reformat4c46d092018-04-07 15:32:3792 }
93 }
94
95 /**
Tim van der Lippe0ed1d2b2020-02-04 13:45:1396 * @param {!SDK.NetworkRequest.NetworkRequest} request
Blink Reformat4c46d092018-04-07 15:32:3797 * @param {number} navigationStart
Tim van der Lippeb1f2b6c2020-02-17 13:00:1698 * @return {!Array.<!RequestTimeRange>}
Blink Reformat4c46d092018-04-07 15:32:3799 */
100 static calculateRequestTimeRanges(request, navigationStart) {
Simon Zünd58919b42020-10-08 09:23:28101 /** @type {!Array.<!RequestTimeRange>} */
Blink Reformat4c46d092018-04-07 15:32:37102 const result = [];
103 /**
Paul Lewis56509652019-12-06 12:51:58104 * @param {!RequestTimeRangeNames} name
Blink Reformat4c46d092018-04-07 15:32:37105 * @param {number} start
106 * @param {number} end
107 */
108 function addRange(name, start, end) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34109 if (start < Number.MAX_VALUE && start <= end) {
Blink Reformat4c46d092018-04-07 15:32:37110 result.push({name: name, start: start, end: end});
Tim van der Lippe1d6e57a2019-09-30 11:55:34111 }
Blink Reformat4c46d092018-04-07 15:32:37112 }
113
114 /**
115 * @param {!Array.<number>} numbers
116 * @return {number|undefined}
117 */
118 function firstPositive(numbers) {
119 for (let i = 0; i < numbers.length; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34120 if (numbers[i] > 0) {
Blink Reformat4c46d092018-04-07 15:32:37121 return numbers[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34122 }
Blink Reformat4c46d092018-04-07 15:32:37123 }
124 return undefined;
125 }
126
127 /**
Paul Lewis56509652019-12-06 12:51:58128 * @param {!RequestTimeRangeNames} name
Blink Reformat4c46d092018-04-07 15:32:37129 * @param {number} start
130 * @param {number} end
131 */
132 function addOffsetRange(name, start, end) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34133 if (start >= 0 && end >= 0) {
Blink Reformat4c46d092018-04-07 15:32:37134 addRange(name, startTime + (start / 1000), startTime + (end / 1000));
Tim van der Lippe1d6e57a2019-09-30 11:55:34135 }
Blink Reformat4c46d092018-04-07 15:32:37136 }
137
138 const timing = request.timing;
139 if (!timing) {
140 const start = request.issueTime() !== -1 ? request.issueTime() : request.startTime !== -1 ? request.startTime : 0;
141 const middle = (request.responseReceivedTime === -1) ? Number.MAX_VALUE : request.responseReceivedTime;
142 const end = (request.endTime === -1) ? Number.MAX_VALUE : request.endTime;
Paul Lewis56509652019-12-06 12:51:58143 addRange(RequestTimeRangeNames.Total, start, end);
144 addRange(RequestTimeRangeNames.Blocking, start, middle);
145 addRange(RequestTimeRangeNames.Receiving, middle, end);
Blink Reformat4c46d092018-04-07 15:32:37146 return result;
147 }
148
149 const issueTime = request.issueTime();
150 const startTime = timing.requestTime;
151 const endTime = firstPositive([request.endTime, request.responseReceivedTime]) || startTime;
152
Paul Lewis56509652019-12-06 12:51:58153 addRange(RequestTimeRangeNames.Total, issueTime < startTime ? issueTime : startTime, endTime);
Blink Reformat4c46d092018-04-07 15:32:37154 if (timing.pushStart) {
155 const pushEnd = timing.pushEnd || endTime;
156 // Only show the part of push that happened after the navigation/reload.
157 // Pushes that happened on the same connection before we started main request will not be shown.
Tim van der Lippe1d6e57a2019-09-30 11:55:34158 if (pushEnd > navigationStart) {
Paul Lewis56509652019-12-06 12:51:58159 addRange(RequestTimeRangeNames.Push, Math.max(timing.pushStart, navigationStart), pushEnd);
Tim van der Lippe1d6e57a2019-09-30 11:55:34160 }
Blink Reformat4c46d092018-04-07 15:32:37161 }
Tim van der Lippe1d6e57a2019-09-30 11:55:34162 if (issueTime < startTime) {
Paul Lewis56509652019-12-06 12:51:58163 addRange(RequestTimeRangeNames.Queueing, issueTime, startTime);
Tim van der Lippe1d6e57a2019-09-30 11:55:34164 }
Blink Reformat4c46d092018-04-07 15:32:37165
166 const responseReceived = (request.responseReceivedTime - startTime) * 1000;
167 if (request.fetchedViaServiceWorker) {
Paul Lewis56509652019-12-06 12:51:58168 addOffsetRange(RequestTimeRangeNames.Blocking, 0, timing.workerStart);
169 addOffsetRange(RequestTimeRangeNames.ServiceWorkerPreparation, timing.workerStart, timing.workerReady);
Anubha Mathur88bc40d2020-06-02 20:56:59170 addOffsetRange(
171 RequestTimeRangeNames.ServiceWorkerRespondWith, timing.workerFetchStart, timing.workerRespondWithSettled);
Paul Lewis56509652019-12-06 12:51:58172 addOffsetRange(RequestTimeRangeNames.ServiceWorker, timing.workerReady, timing.sendEnd);
173 addOffsetRange(RequestTimeRangeNames.Waiting, timing.sendEnd, responseReceived);
Blink Reformat4c46d092018-04-07 15:32:37174 } else if (!timing.pushStart) {
175 const blockingEnd =
176 firstPositive([timing.dnsStart, timing.connectStart, timing.sendStart, responseReceived]) || 0;
Paul Lewis56509652019-12-06 12:51:58177 addOffsetRange(RequestTimeRangeNames.Blocking, 0, blockingEnd);
178 addOffsetRange(RequestTimeRangeNames.Proxy, timing.proxyStart, timing.proxyEnd);
179 addOffsetRange(RequestTimeRangeNames.DNS, timing.dnsStart, timing.dnsEnd);
180 addOffsetRange(RequestTimeRangeNames.Connecting, timing.connectStart, timing.connectEnd);
181 addOffsetRange(RequestTimeRangeNames.SSL, timing.sslStart, timing.sslEnd);
182 addOffsetRange(RequestTimeRangeNames.Sending, timing.sendStart, timing.sendEnd);
Blink Reformat4c46d092018-04-07 15:32:37183 addOffsetRange(
Paul Lewis56509652019-12-06 12:51:58184 RequestTimeRangeNames.Waiting,
Blink Reformat4c46d092018-04-07 15:32:37185 Math.max(timing.sendEnd, timing.connectEnd, timing.dnsEnd, timing.proxyEnd, blockingEnd), responseReceived);
186 }
187
188 if (request.endTime !== -1) {
189 addRange(
Paul Lewis56509652019-12-06 12:51:58190 timing.pushStart ? RequestTimeRangeNames.ReceivingPush : RequestTimeRangeNames.Receiving,
Blink Reformat4c46d092018-04-07 15:32:37191 request.responseReceivedTime, endTime);
192 }
193
194 return result;
195 }
196
197 /**
Tim van der Lippe0ed1d2b2020-02-04 13:45:13198 * @param {!SDK.NetworkRequest.NetworkRequest} request
Tim van der Lippe119690c2020-01-13 12:31:30199 * @param {!NetworkTimeCalculator} calculator
Blink Reformat4c46d092018-04-07 15:32:37200 * @return {!Element}
201 */
202 static createTimingTable(request, calculator) {
Tim van der Lippef49e2322020-05-01 15:03:09203 const tableElement = document.createElement('table');
204 tableElement.classList.add('network-timing-table');
Jack Franklin71519f82020-11-03 12:08:59205 UI.Utils.appendStyle(tableElement, 'network/networkTimingTable.css', {enableLegacyPatching: true});
Blink Reformat4c46d092018-04-07 15:32:37206 const colgroup = tableElement.createChild('colgroup');
207 colgroup.createChild('col', 'labels');
208 colgroup.createChild('col', 'bars');
209 colgroup.createChild('col', 'duration');
210
Paul Lewis56509652019-12-06 12:51:58211 const timeRanges = RequestTimingView.calculateRequestTimeRanges(request, calculator.minimumBoundary());
Blink Reformat4c46d092018-04-07 15:32:37212 const startTime = timeRanges.map(r => r.start).reduce((a, b) => Math.min(a, b));
213 const endTime = timeRanges.map(r => r.end).reduce((a, b) => Math.max(a, b));
214 const scale = 100 / (endTime - startTime);
215
216 let connectionHeader;
Anubha Mathur88bc40d2020-06-02 20:56:59217 let serviceworkerHeader;
Blink Reformat4c46d092018-04-07 15:32:37218 let dataHeader;
219 let queueingHeader;
220 let totalDuration = 0;
221
222 const startTimeHeader = tableElement.createChild('thead', 'network-timing-start');
Brandon Goddard61d33dd2019-09-05 16:31:53223 const tableHeaderRow = startTimeHeader.createChild('tr');
Simon Zünd58919b42020-10-08 09:23:28224 /** @type {!HTMLTableCellElement} */
225 const activityHeaderCell = /** @type {!HTMLTableCellElement} */ (tableHeaderRow.createChild('th'));
Brandon Goddard61d33dd2019-09-05 16:31:53226 activityHeaderCell.createChild('span', 'network-timing-hidden-header').textContent = ls`Label`;
227 activityHeaderCell.scope = 'col';
Simon Zünd58919b42020-10-08 09:23:28228 /** @type {!HTMLTableCellElement} */
229 const waterfallHeaderCell = /** @type {!HTMLTableCellElement} */ (tableHeaderRow.createChild('th'));
Brandon Goddard61d33dd2019-09-05 16:31:53230 waterfallHeaderCell.createChild('span', 'network-timing-hidden-header').textContent = ls`Waterfall`;
231 waterfallHeaderCell.scope = 'col';
Simon Zünd58919b42020-10-08 09:23:28232 /** @type {!HTMLTableCellElement} */
233 const durationHeaderCell = /** @type {!HTMLTableCellElement} */ (tableHeaderRow.createChild('th'));
Brandon Goddard61d33dd2019-09-05 16:31:53234 durationHeaderCell.createChild('span', 'network-timing-hidden-header').textContent = ls`Duration`;
235 durationHeaderCell.scope = 'col';
236
Simon Zünd58919b42020-10-08 09:23:28237 /** @type {!HTMLTableCellElement} */
238 const queuedCell = /** @type {!HTMLTableCellElement} */ (startTimeHeader.createChild('tr').createChild('td'));
239 /** @type {!HTMLTableCellElement} */
240 const startedCell = /** @type {!HTMLTableCellElement} */ (startTimeHeader.createChild('tr').createChild('td'));
Brandon Goddard61d33dd2019-09-05 16:31:53241 queuedCell.colSpan = startedCell.colSpan = 3;
Sigurd Schneider23c52972020-10-13 09:31:14242 UI.UIUtils.createTextChild(
243 queuedCell, Common.UIString.UIString('Queued at %s', calculator.formatValue(request.issueTime(), 2)));
244 UI.UIUtils.createTextChild(
245 startedCell, Common.UIString.UIString('Started at %s', calculator.formatValue(request.startTime, 2)));
Blink Reformat4c46d092018-04-07 15:32:37246
247 let right;
248 for (let i = 0; i < timeRanges.length; ++i) {
249 const range = timeRanges[i];
250 const rangeName = range.name;
Paul Lewis56509652019-12-06 12:51:58251 if (rangeName === RequestTimeRangeNames.Total) {
Blink Reformat4c46d092018-04-07 15:32:37252 totalDuration = range.end - range.start;
253 continue;
254 }
Paul Lewis56509652019-12-06 12:51:58255 if (rangeName === RequestTimeRangeNames.Push) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:13256 createHeader(Common.UIString.UIString('Server Push'));
Paul Lewis56509652019-12-06 12:51:58257 } else if (rangeName === RequestTimeRangeNames.Queueing) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34258 if (!queueingHeader) {
Brandon Goddard61d33dd2019-09-05 16:31:53259 queueingHeader = createHeader(ls`Resource Scheduling`);
Tim van der Lippe1d6e57a2019-09-30 11:55:34260 }
Paul Lewis56509652019-12-06 12:51:58261 } else if (ConnectionSetupRangeNames.has(rangeName)) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34262 if (!connectionHeader) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:13263 connectionHeader = createHeader(Common.UIString.UIString('Connection Start'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34264 }
Anubha Mathur88bc40d2020-06-02 20:56:59265 } else if (ServiceWorkerRangeNames.has(rangeName)) {
266 if (!serviceworkerHeader) {
267 serviceworkerHeader = createHeader(ls`Service Worker`);
268 }
Blink Reformat4c46d092018-04-07 15:32:37269 } else {
Tim van der Lippe1d6e57a2019-09-30 11:55:34270 if (!dataHeader) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:13271 dataHeader = createHeader(Common.UIString.UIString('Request/Response'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34272 }
Blink Reformat4c46d092018-04-07 15:32:37273 }
274
275 const left = (scale * (range.start - startTime));
276 right = (scale * (endTime - range.end));
277 const duration = range.end - range.start;
278
279 const tr = tableElement.createChild('tr');
Songtao Xia1e692682020-06-19 13:56:39280 const timingBarTitleEement = tr.createChild('td');
Sigurd Schneider23c52972020-10-13 09:31:14281 UI.UIUtils.createTextChild(timingBarTitleEement, RequestTimingView._timeRangeTitle(rangeName));
Blink Reformat4c46d092018-04-07 15:32:37282
283 const row = tr.createChild('td').createChild('div', 'network-timing-row');
284 const bar = row.createChild('span', 'network-timing-bar ' + rangeName);
285 bar.style.left = left + '%';
286 bar.style.right = right + '%';
287 bar.textContent = '\u200B'; // Important for 0-time items to have 0 width.
Brandon Goddard61d33dd2019-09-05 16:31:53288 UI.ARIAUtils.setAccessibleName(row, ls`Started at ${calculator.formatValue(range.start, 2)}`);
Blink Reformat4c46d092018-04-07 15:32:37289 const label = tr.createChild('td').createChild('div', 'network-timing-bar-title');
290 label.textContent = Number.secondsToString(duration, true);
Songtao Xia1e692682020-06-19 13:56:39291
292 if (range.name === 'serviceworker-respondwith') {
293 timingBarTitleEement.classList.add('network-fetch-timing-bar-clickable');
294 tableElement.createChild('tr', 'network-fetch-timing-bar-details');
295
Simon Zünd58919b42020-10-08 09:23:28296 timingBarTitleEement.setAttribute('tabindex', '0');
Songtao Xia1e692682020-06-19 13:56:39297 timingBarTitleEement.setAttribute('role', 'switch');
298 UI.ARIAUtils.setChecked(timingBarTitleEement, false);
299 }
Blink Reformat4c46d092018-04-07 15:32:37300 }
301
302 if (!request.finished) {
Simon Zünd58919b42020-10-08 09:23:28303 /** @type {!HTMLTableCellElement} */
304 const cell = /** @type {!HTMLTableCellElement} */ (tableElement.createChild('tr').createChild('td', 'caution'));
Blink Reformat4c46d092018-04-07 15:32:37305 cell.colSpan = 3;
Sigurd Schneider23c52972020-10-13 09:31:14306 UI.UIUtils.createTextChild(cell, Common.UIString.UIString('CAUTION: request is not finished yet!'));
Blink Reformat4c46d092018-04-07 15:32:37307 }
308
309 const footer = tableElement.createChild('tr', 'network-timing-footer');
Simon Zünd58919b42020-10-08 09:23:28310 /** @type {!HTMLTableCellElement} */
311 const note = /** @type {!HTMLTableCellElement} */ (footer.createChild('td'));
Blink Reformat4c46d092018-04-07 15:32:37312 note.colSpan = 1;
Tim van der Lippe0ed1d2b2020-02-04 13:45:13313 note.appendChild(UI.UIUtils.createDocumentationLink(
314 'network-performance/reference#timing-explanation', Common.UIString.UIString('Explanation')));
Blink Reformat4c46d092018-04-07 15:32:37315 footer.createChild('td');
Sigurd Schneider23c52972020-10-13 09:31:14316 UI.UIUtils.createTextChild(footer.createChild('td'), Number.secondsToString(totalDuration, true));
Blink Reformat4c46d092018-04-07 15:32:37317
318 const serverTimings = request.serverTimings;
Blink Reformat4c46d092018-04-07 15:32:37319
320 const lastTimingRightEdge = right === undefined ? 100 : right;
321
Simon Zünd58919b42020-10-08 09:23:28322 /** @type {!HTMLTableCellElement} */
323 const breakElement = /** @type {!HTMLTableCellElement} */ (
324 tableElement.createChild('tr', 'network-timing-table-header').createChild('td'));
Blink Reformat4c46d092018-04-07 15:32:37325 breakElement.colSpan = 3;
326 breakElement.createChild('hr', 'break');
327
328 const serverHeader = tableElement.createChild('tr', 'network-timing-table-header');
Sigurd Schneider23c52972020-10-13 09:31:14329 UI.UIUtils.createTextChild(serverHeader.createChild('td'), Common.UIString.UIString('Server Timing'));
Blink Reformat4c46d092018-04-07 15:32:37330 serverHeader.createChild('td');
Sigurd Schneider23c52972020-10-13 09:31:14331 UI.UIUtils.createTextChild(serverHeader.createChild('td'), Common.UIString.UIString('TIME'));
Blink Reformat4c46d092018-04-07 15:32:37332
Jan Scheffler4569bb02020-02-19 08:51:40333 if (!serverTimings) {
334 const informationRow = tableElement.createChild('tr');
Simon Zünd58919b42020-10-08 09:23:28335 /** @type {!HTMLTableCellElement} */
336 const information = /** @type {!HTMLTableCellElement} */ (informationRow.createChild('td'));
Jan Scheffler4569bb02020-02-19 08:51:40337 information.colSpan = 3;
338
339 const link = UI.XLink.XLink.create('https://web.dev/custom-metrics/#server-timing-api', ls`the Server Timing API`);
340 information.appendChild(UI.UIUtils.formatLocalized(
341 'During development, you can use %s to add insights into the server-side timing of this request.', [link]));
342
343 return tableElement;
344 }
345
Blink Reformat4c46d092018-04-07 15:32:37346 serverTimings.filter(item => item.metric.toLowerCase() !== 'total')
347 .forEach(item => addTiming(item, lastTimingRightEdge));
348 serverTimings.filter(item => item.metric.toLowerCase() === 'total')
349 .forEach(item => addTiming(item, lastTimingRightEdge));
350
351 return tableElement;
352
353 /**
Tim van der Lippe0ed1d2b2020-02-04 13:45:13354 * @param {!SDK.ServerTiming.ServerTiming} serverTiming
Blink Reformat4c46d092018-04-07 15:32:37355 * @param {number} right
356 */
357 function addTiming(serverTiming, right) {
Simon Zünd58919b42020-10-08 09:23:28358 const colorGenerator =
359 new Common.Color.Generator({min: 0, max: 360, count: 36}, {min: 50, max: 80, count: undefined}, 80);
Blink Reformat4c46d092018-04-07 15:32:37360 const isTotal = serverTiming.metric.toLowerCase() === 'total';
361 const tr = tableElement.createChild('tr', isTotal ? 'network-timing-footer' : '');
362 const metric = tr.createChild('td', 'network-timing-metric');
Paul Irish51ba18a2019-09-06 05:36:19363 const description = serverTiming.description || serverTiming.metric;
Sigurd Schneider23c52972020-10-13 09:31:14364 UI.UIUtils.createTextChild(metric, description);
Paul Irish51ba18a2019-09-06 05:36:19365 metric.title = description;
Blink Reformat4c46d092018-04-07 15:32:37366 const row = tr.createChild('td').createChild('div', 'network-timing-row');
367
Tim van der Lippe1d6e57a2019-09-30 11:55:34368 if (serverTiming.value === null) {
Blink Reformat4c46d092018-04-07 15:32:37369 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34370 }
Blink Reformat4c46d092018-04-07 15:32:37371 const left = scale * (endTime - startTime - (serverTiming.value / 1000));
372 if (left >= 0) { // don't chart values too big or too small
373 const bar = row.createChild('span', 'network-timing-bar server-timing');
374 bar.style.left = left + '%';
375 bar.style.right = right + '%';
376 bar.textContent = '\u200B'; // Important for 0-time items to have 0 width.
Tim van der Lippe1d6e57a2019-09-30 11:55:34377 if (!isTotal) {
Blink Reformat4c46d092018-04-07 15:32:37378 bar.style.backgroundColor = colorGenerator.colorForID(serverTiming.metric);
Tim van der Lippe1d6e57a2019-09-30 11:55:34379 }
Blink Reformat4c46d092018-04-07 15:32:37380 }
381 const label = tr.createChild('td').createChild('div', 'network-timing-bar-title');
382 label.textContent = Number.millisToString(serverTiming.value, true);
383 }
384
385 /**
386 * @param {string} title
387 * @return {!Element}
388 */
389 function createHeader(title) {
390 const dataHeader = tableElement.createChild('tr', 'network-timing-table-header');
Brandon Goddard61d33dd2019-09-05 16:31:53391 const headerCell = dataHeader.createChild('td');
Sigurd Schneider23c52972020-10-13 09:31:14392 UI.UIUtils.createTextChild(headerCell, title);
Brandon Goddard61d33dd2019-09-05 16:31:53393 UI.ARIAUtils.markAsHeading(headerCell, 2);
Sigurd Schneider23c52972020-10-13 09:31:14394 UI.UIUtils.createTextChild(dataHeader.createChild('td'), '');
395 UI.UIUtils.createTextChild(dataHeader.createChild('td'), ls`DURATION`);
Blink Reformat4c46d092018-04-07 15:32:37396 return dataHeader;
397 }
398 }
399
Songtao Xia1e692682020-06-19 13:56:39400 _constructFetchDetailsView() {
401 if (!this._tableElement) {
402 return;
403 }
404
405 const document = this._tableElement.ownerDocument;
406 const fetchDetailsElement = document.querySelector('.network-fetch-timing-bar-details');
407
408 if (!fetchDetailsElement) {
409 return;
410 }
411
412 fetchDetailsElement.classList.add('network-fetch-timing-bar-details-collapsed');
413
414 self.onInvokeElement(this._tableElement, this._onToggleFetchDetails.bind(this, fetchDetailsElement));
415
416 const detailsView = new UI.TreeOutline.TreeOutlineInShadow();
417 fetchDetailsElement.appendChild(detailsView.element);
418
419 const origRequest = SDK.NetworkLog.NetworkLog.instance().originalRequestForURL(this._request.url());
420 if (origRequest) {
421 const requestObject = SDK.RemoteObject.RemoteObject.fromLocalObject(origRequest);
422 const requestTreeElement = new ObjectUI.ObjectPropertiesSection.RootElement(requestObject);
423 requestTreeElement.title = ls`Original Request`;
424 detailsView.appendChild(requestTreeElement);
425 }
426
427 const response = SDK.NetworkLog.NetworkLog.instance().originalResponseForURL(this._request.url());
428 if (response) {
429 const responseObject = SDK.RemoteObject.RemoteObject.fromLocalObject(response);
430 const responseTreeElement = new ObjectUI.ObjectPropertiesSection.RootElement(responseObject);
431 responseTreeElement.title = ls`Response Received`;
432 detailsView.appendChild(responseTreeElement);
433 }
434
435 const serviceWorkerResponseSource = document.createElementWithClass('div', 'network-fetch-details-treeitem');
436 let swResponseSourceString = ls`Unknown`;
437 const swResponseSource = this._request.serviceWorkerResponseSource();
438 if (swResponseSource) {
439 swResponseSourceString = this._getLocalizedResponseSourceForCode(swResponseSource);
440 }
441 serviceWorkerResponseSource.textContent = ls`Source of response: ${swResponseSourceString}`;
442
443 const responseSourceTreeElement = new UI.TreeOutline.TreeElement(serviceWorkerResponseSource);
444 detailsView.appendChild(responseSourceTreeElement);
445
446 const cacheNameElement = document.createElementWithClass('div', 'network-fetch-details-treeitem');
447 const responseCacheStorageName = this._request.getResponseCacheStorageCacheName();
448 if (responseCacheStorageName) {
449 cacheNameElement.textContent = ls`Cache storage cache name: ${responseCacheStorageName}`;
450 } else {
451 cacheNameElement.textContent = ls`Cache storage cache name: Unknown`;
452 }
453
454 const cacheNameTreeElement = new UI.TreeOutline.TreeElement(cacheNameElement);
455 detailsView.appendChild(cacheNameTreeElement);
456
457 const retrievalTime = this._request.getResponseRetrievalTime();
458 if (retrievalTime) {
459 const responseTimeElement = document.createElementWithClass('div', 'network-fetch-details-treeitem');
460 responseTimeElement.textContent = ls`Retrieval Time: ${retrievalTime}`;
461 const responseTimeTreeElement = new UI.TreeOutline.TreeElement(responseTimeElement);
462 detailsView.appendChild(responseTimeTreeElement);
463 }
464 }
465
466 /**
467 * @param {!Protocol.Network.ServiceWorkerResponseSource} swResponseSource
468 */
469 _getLocalizedResponseSourceForCode(swResponseSource) {
470 switch (swResponseSource) {
471 case Protocol.Network.ServiceWorkerResponseSource.CacheStorage:
472 return ls`ServiceWorker cache storage`;
473 case Protocol.Network.ServiceWorkerResponseSource.HttpCache:
474 return ls`From HTTP cache`;
475 case Protocol.Network.ServiceWorkerResponseSource.Network:
476 return ls`Network fetch`;
477 default:
478 return ls`Fallback code`;
479 }
480 }
481
482 /**
483 *
484 * @param {!Element} fetchDetailsElement
485 * @param {!Event} event
486 */
487 _onToggleFetchDetails(fetchDetailsElement, event) {
488 if (!event.target) {
489 return;
490 }
491
Simon Zünd58919b42020-10-08 09:23:28492 const target = /** @type {!Element} */ (event.target);
493 if (target.classList.contains('network-fetch-timing-bar-clickable')) {
494 const expanded = target.getAttribute('aria-checked') === 'true';
495 target.setAttribute('aria-checked', String(!expanded));
Songtao Xia1e692682020-06-19 13:56:39496
497 fetchDetailsElement.classList.toggle('network-fetch-timing-bar-details-collapsed');
498 fetchDetailsElement.classList.toggle('network-fetch-timing-bar-details-expanded');
499 }
500 }
501
502
Blink Reformat4c46d092018-04-07 15:32:37503 /**
504 * @override
505 */
506 wasShown() {
507 this._request.addEventListener(SDK.NetworkRequest.Events.TimingChanged, this._refresh, this);
508 this._request.addEventListener(SDK.NetworkRequest.Events.FinishedLoading, this._refresh, this);
Tim van der Lippe119690c2020-01-13 12:31:30509 this._calculator.addEventListener(Events.BoundariesChanged, this._refresh, this);
Blink Reformat4c46d092018-04-07 15:32:37510 this._refresh();
511 }
512
513 /**
514 * @override
515 */
516 willHide() {
517 this._request.removeEventListener(SDK.NetworkRequest.Events.TimingChanged, this._refresh, this);
518 this._request.removeEventListener(SDK.NetworkRequest.Events.FinishedLoading, this._refresh, this);
Tim van der Lippe119690c2020-01-13 12:31:30519 this._calculator.removeEventListener(Events.BoundariesChanged, this._refresh, this);
Blink Reformat4c46d092018-04-07 15:32:37520 }
521
522 _refresh() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34523 if (this._tableElement) {
Blink Reformat4c46d092018-04-07 15:32:37524 this._tableElement.remove();
Tim van der Lippe1d6e57a2019-09-30 11:55:34525 }
Blink Reformat4c46d092018-04-07 15:32:37526
Paul Lewis56509652019-12-06 12:51:58527 this._tableElement = RequestTimingView.createTimingTable(this._request, this._calculator);
Blink Reformat4c46d092018-04-07 15:32:37528 this._tableElement.classList.add('resource-timing-table');
529 this.element.appendChild(this._tableElement);
Songtao Xia1e692682020-06-19 13:56:39530
531 if (this._request.fetchedViaServiceWorker) {
532 this._constructFetchDetailsView();
533 }
Blink Reformat4c46d092018-04-07 15:32:37534 }
Paul Lewis56509652019-12-06 12:51:58535}
Blink Reformat4c46d092018-04-07 15:32:37536
537/** @enum {string} */
Paul Lewis56509652019-12-06 12:51:58538export const RequestTimeRangeNames = {
Blink Reformat4c46d092018-04-07 15:32:37539 Push: 'push',
540 Queueing: 'queueing',
541 Blocking: 'blocking',
542 Connecting: 'connecting',
543 DNS: 'dns',
544 Proxy: 'proxy',
545 Receiving: 'receiving',
546 ReceivingPush: 'receiving-push',
547 Sending: 'sending',
548 ServiceWorker: 'serviceworker',
549 ServiceWorkerPreparation: 'serviceworker-preparation',
Anubha Mathur88bc40d2020-06-02 20:56:59550 ServiceWorkerRespondWith: 'serviceworker-respondwith',
Blink Reformat4c46d092018-04-07 15:32:37551 SSL: 'ssl',
552 Total: 'total',
553 Waiting: 'waiting'
554};
555
Anubha Mathur88bc40d2020-06-02 20:56:59556export const ServiceWorkerRangeNames = new Set([
557 RequestTimeRangeNames.ServiceWorker, RequestTimeRangeNames.ServiceWorkerPreparation,
558 RequestTimeRangeNames.ServiceWorkerRespondWith
559]);
560
Paul Lewis56509652019-12-06 12:51:58561export const ConnectionSetupRangeNames = new Set([
562 RequestTimeRangeNames.Queueing, RequestTimeRangeNames.Blocking, RequestTimeRangeNames.Connecting,
563 RequestTimeRangeNames.DNS, RequestTimeRangeNames.Proxy, RequestTimeRangeNames.SSL
Blink Reformat4c46d092018-04-07 15:32:37564]);
Tim van der Lippeb1f2b6c2020-02-17 13:00:16565
566/** @typedef {{name: !RequestTimeRangeNames, start: number, end: number}} */
Simon Zünd58919b42020-10-08 09:23:28567// @ts-ignore typedef
Tim van der Lippeb1f2b6c2020-02-17 13:00:16568export let RequestTimeRange;