Web Inspector: Network Table - Redesign the waterfall popover showing timing data
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ResourceTimingBreakdownView.js
1 /*
2  * Copyright (C) 2017 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WI.ResourceTimingBreakdownView = class ResourceTimingBreakdownView extends WI.View
27 {
28     constructor(resource, fixedWidth)
29     {
30         super(null);
31
32         console.assert(resource.timingData.startTime && resource.timingData.responseEnd, "Timing breakdown view requires a resource with timing data.");
33         console.assert(!fixedWidth || fixedWidth >= 100, "fixedWidth must be at least wide enough for strings.");
34
35         this._resource = resource;
36
37         this.element.classList.add("resource-timing-breakdown");
38
39         if (fixedWidth)
40             this.element.style.width = fixedWidth + "px";
41     }
42
43     // Protected
44
45     _appendEmptyRow()
46     {
47         let row = this._tableElement.appendChild(document.createElement("tr"));
48         row.className = "empty";
49         return row;
50     }
51
52     _appendHeaderRow(label, time, additionalClassName)
53     {
54         let row = this._tableElement.appendChild(document.createElement("tr"));
55         row.className = "header";
56         if (additionalClassName)
57             row.classList.add(additionalClassName);
58
59         let labelCell = row.appendChild(document.createElement("td"));
60         labelCell.className = "label";
61         labelCell.textContent = label;
62         labelCell.colSpan = 2;
63
64         let timeCell = row.appendChild(document.createElement("td"));
65         timeCell.className = "time";
66         if (time)
67             timeCell.textContent = time;
68         else if (time === undefined)
69             timeCell.appendChild(document.createElement("hr"));
70
71         return row;
72     }
73
74     _appendRow(label, type, startTime, endTime)
75     {
76         let row = this._tableElement.appendChild(document.createElement("tr"));
77
78         let labelCell = row.appendChild(document.createElement("td"));
79         labelCell.className = "label";
80         labelCell.textContent = label;
81
82         let duration = endTime - startTime;
83         let graphWidth = (duration / this._graphDuration) * 100;
84         let graphOffset = ((startTime - this._graphStartTime) / this._graphDuration) * 100;
85         let positionProperty = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? "right" : "left";
86         let graphCell = row.appendChild(document.createElement("td"));
87         graphCell.className = "graph";
88         let block = graphCell.appendChild(document.createElement("div"));
89         block.classList.add("block", type);
90         block.style.width = graphWidth + "%";
91         block.style[positionProperty] = graphOffset + "%";
92
93         let timeCell = row.appendChild(document.createElement("td"));
94         timeCell.className = "time";
95         timeCell.textContent = Number.secondsToMillisecondsString(duration);
96
97         return row;
98     }
99
100     initialLayout()
101     {
102         super.initialLayout();
103
104         let {startTime, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, responseStart, responseEnd} = this._resource.timingData;
105
106         this._tableElement = this.element.appendChild(document.createElement("table"));
107         this._tableElement.className = "waterfall";
108
109         this._graphStartTime = startTime;
110         this._graphEndTime = responseEnd;
111         this._graphDuration = this._graphEndTime - this._graphStartTime;
112
113         this._appendHeaderRow(WI.UIString("Scheduling:"));
114         this._appendRow(WI.UIString("Queued"), "queue", startTime, domainLookupStart || connectStart || requestStart);
115
116         if (domainLookupStart || connectStart) {
117             this._appendEmptyRow();
118             this._appendHeaderRow(WI.UIString("Connection:"));
119             if (domainLookupStart)
120                 this._appendRow(WI.UIString("DNS"), "dns", domainLookupStart, domainLookupEnd || connectStart || requestStart);
121             if (connectStart)
122                 this._appendRow(WI.UIString("TCP"), "connect", connectStart, connectEnd || requestStart);
123             if (secureConnectionStart)
124                 this._appendRow(WI.UIString("Secure"), "secure", secureConnectionStart, connectEnd || requestStart);
125         }
126
127         this._appendEmptyRow();
128         this._appendHeaderRow(WI.UIString("Response:"));
129         this._appendRow(WI.UIString("Waiting"), "request", requestStart, responseStart);
130         this._appendRow(WI.UIString("Download"), "response", responseStart, responseEnd);
131
132         this._appendEmptyRow();
133         this._appendHeaderRow(WI.UIString("Totals:"));
134         this._appendHeaderRow(WI.UIString("Time to First Byte"), Number.secondsToMillisecondsString(responseStart - startTime), "total-row");
135         this._appendHeaderRow(WI.UIString("Start to Finish"), Number.secondsToMillisecondsString(responseEnd - startTime), "total-row");
136     }
137 };