Web Inspector: Timelines UI redesign: show content tree outline records in timeline...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ResourceTimelineDataGridNode.js
1 /*
2  * Copyright (C) 2013, 2015 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 WebInspector.ResourceTimelineDataGridNode = class ResourceTimelineDataGridNode extends WebInspector.TimelineDataGridNode
27 {
28     constructor(resourceTimelineRecord, includesGraph, graphDataSource)
29     {
30         super(includesGraph, graphDataSource);
31
32         this._resource = resourceTimelineRecord.resource;
33         this._record = resourceTimelineRecord;
34
35         this._resource.addEventListener(WebInspector.Resource.Event.LoadingDidFinish, this._needsRefresh, this);
36         this._resource.addEventListener(WebInspector.Resource.Event.LoadingDidFail, this._needsRefresh, this);
37         this._resource.addEventListener(WebInspector.Resource.Event.URLDidChange, this._needsRefresh, this);
38
39         if (includesGraph)
40             this._record.addEventListener(WebInspector.TimelineRecord.Event.Updated, this._timelineRecordUpdated, this);
41         else {
42             this._resource.addEventListener(WebInspector.Resource.Event.TypeDidChange, this._needsRefresh, this);
43             this._resource.addEventListener(WebInspector.Resource.Event.SizeDidChange, this._needsRefresh, this);
44             this._resource.addEventListener(WebInspector.Resource.Event.TransferSizeDidChange, this._needsRefresh, this);
45         }
46     }
47
48     // Public
49
50     get records()
51     {
52         return [this._record];
53     }
54
55     get resource()
56     {
57         return this._resource;
58     }
59
60     get data()
61     {
62         if (this._cachedData)
63             return this._cachedData;
64
65         var resource = this._resource;
66         var data = {};
67
68         if (!this._includesGraph) {
69             var zeroTime = this.graphDataSource ? this.graphDataSource.zeroTime : 0;
70
71             data.domain = WebInspector.displayNameForHost(resource.urlComponents.host);
72             data.scheme = resource.urlComponents.scheme ? resource.urlComponents.scheme.toUpperCase() : "";
73             data.method = resource.requestMethod;
74             data.type = resource.type;
75             data.statusCode = resource.statusCode;
76             data.cached = resource.cached;
77             data.size = resource.size;
78             data.transferSize = resource.transferSize;
79             data.requestSent = resource.requestSentTimestamp - zeroTime;
80             data.duration = resource.receiveDuration;
81             data.latency = resource.latency;
82         }
83
84         data.graph = this._record.startTime;
85
86         this._cachedData = data;
87         return data;
88     }
89
90     createCellContent(columnIdentifier, cell)
91     {
92         var resource = this._resource;
93
94         if (resource.failed || resource.canceled || resource.statusCode >= 400)
95             cell.classList.add("error");
96
97         var value = this.data[columnIdentifier];
98
99         switch (columnIdentifier) {
100         case "name":
101             cell.classList.add(WebInspector.ResourceTreeElement.ResourceIconStyleClassName, resource.type);
102             cell.title = resource.displayURL;
103             this._updateStatus(cell);
104             return this._createNameCellDocumentFragment();
105
106         case "type":
107             return WebInspector.Resource.displayNameForType(value);
108
109         case "statusCode":
110             cell.title = resource.statusText || "";
111             return value || emDash;
112
113         case "cached":
114             return value ? WebInspector.UIString("Yes") : WebInspector.UIString("No");
115
116         case "domain":
117             return value || emDash;
118
119         case "size":
120         case "transferSize":
121             return isNaN(value) ? emDash : Number.bytesToString(value, true);
122
123         case "requestSent":
124         case "latency":
125         case "duration":
126             return isNaN(value) ? emDash : Number.secondsToString(value, true);
127         }
128
129         return super.createCellContent(columnIdentifier, cell);
130     }
131
132     refresh()
133     {
134         if (this._scheduledRefreshIdentifier) {
135             cancelAnimationFrame(this._scheduledRefreshIdentifier);
136             this._scheduledRefreshIdentifier = undefined;
137         }
138
139         this._cachedData = null;
140
141         super.refresh();
142     }
143
144     // Private
145
146     _createNameCellDocumentFragment()
147     {
148         let fragment = document.createDocumentFragment();
149         let mainTitle = WebInspector.TimelineTabContentView.displayNameForRecord(this._record);
150         fragment.append(mainTitle);
151
152         // Show the host as the subtitle if it is different from the main resource or if this is the main frame's main resource.
153         let frame = this._resource.parentFrame;
154         let isMainResource = this._resource.isMainResource();
155         let parentResourceHost;
156         if (frame && isMainResource) {
157             // When the resource is a main resource, get the host from the current frame's parent frame instead of the current frame.
158             parentResourceHost = frame.parentFrame ? frame.parentFrame.mainResource.urlComponents.host : null;
159         } else if (frame) {
160             // When the resource is a normal sub-resource, get the host from the current frame's main resource.
161             parentResourceHost = frame.mainResource.urlComponents.host;
162         }
163
164         if (parentResourceHost !== this._resource.urlComponents.host || frame.isMainFrame() && isMainResource) {
165             let subtitle = WebInspector.displayNameForHost(this._resource.urlComponents.host);
166             if (mainTitle !== subtitle) {
167                 let subtitleElement = document.createElement("span");
168                 subtitleElement.classList.add("subtitle");
169                 subtitleElement.textContent = subtitle;
170                 fragment.append(subtitleElement);
171             }
172         }
173
174         return fragment;
175     }
176
177     _needsRefresh()
178     {
179         if (this.dataGrid instanceof WebInspector.TimelineDataGrid) {
180             this.dataGrid.dataGridNodeNeedsRefresh(this);
181             return;
182         }
183
184         if (this._scheduledRefreshIdentifier)
185             return;
186
187         this._scheduledRefreshIdentifier = requestAnimationFrame(this.refresh.bind(this));
188     }
189
190     _timelineRecordUpdated(event)
191     {
192         if (this.isRecordVisible(this._record))
193             this.needsGraphRefresh();
194     }
195
196     _dataGridNodeGoToArrowClicked()
197     {
198         WebInspector.showSourceCode(this._resource);
199     }
200
201     _updateStatus(cell)
202     {
203         if (this._resource.failed)
204             cell.classList.add("error");
205         else {
206             cell.classList.remove("error");
207
208             if (this._resource.finished)
209                 this.createGoToArrowButton(cell, this._dataGridNodeGoToArrowClicked.bind(this));
210         }
211
212         if (this._spinner)
213             this._spinner.element.remove();
214
215         if (this._resource.finished || this._resource.failed)
216             return;
217
218         if (!this._spinner)
219             this._spinner = new WebInspector.IndeterminateProgressSpinner;
220
221         let contentElement = cell.firstChild;
222         contentElement.appendChild(this._spinner.element);
223     }
224 };