Web Inspector: hook up grid row filtering in the new Timelines UI
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / RenderingFrameTimelineView.js
1 /*
2  * Copyright (C) 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.RenderingFrameTimelineView = class RenderingFrameTimelineView extends WebInspector.TimelineView
27 {
28     constructor(timeline, extraArguments)
29     {
30         super(timeline, extraArguments);
31
32         console.assert(WebInspector.TimelineRecord.Type.RenderingFrame);
33
34         var scopeBarItems = [];
35         for (var key in WebInspector.RenderingFrameTimelineView.DurationFilter) {
36             var value = WebInspector.RenderingFrameTimelineView.DurationFilter[key];
37             scopeBarItems.push(new WebInspector.ScopeBarItem(value, WebInspector.RenderingFrameTimelineView.displayNameForDurationFilter(value)));
38         }
39
40         this._scopeBar = new WebInspector.ScopeBar("rendering-frame-scope-bar", scopeBarItems, scopeBarItems[0], true);
41         this._scopeBar.addEventListener(WebInspector.ScopeBar.Event.SelectionChanged, this._scopeBarSelectionDidChange, this);
42
43         let columns = {name: {}, totalTime: {}, scriptTime: {}, layoutTime: {}, paintTime: {}, otherTime: {}, startTime: {}, location: {}};
44
45         columns.name.title = WebInspector.UIString("Name");
46         columns.name.width = "20%";
47         columns.name.icon = true;
48         columns.name.disclosure = true;
49
50         columns.totalTime.title = WebInspector.UIString("Total Time");
51         columns.totalTime.width = "15%";
52         columns.totalTime.aligned = "right";
53
54         columns.scriptTime.title = WebInspector.RenderingFrameTimelineRecord.displayNameForTaskType(WebInspector.RenderingFrameTimelineRecord.TaskType.Script);
55         columns.scriptTime.width = "10%";
56         columns.scriptTime.aligned = "right";
57
58         columns.layoutTime.title = WebInspector.RenderingFrameTimelineRecord.displayNameForTaskType(WebInspector.RenderingFrameTimelineRecord.TaskType.Layout);
59         columns.layoutTime.width = "10%";
60         columns.layoutTime.aligned = "right";
61
62         columns.paintTime.title = WebInspector.RenderingFrameTimelineRecord.displayNameForTaskType(WebInspector.RenderingFrameTimelineRecord.TaskType.Paint);
63         columns.paintTime.width = "10%";
64         columns.paintTime.aligned = "right";
65
66         columns.otherTime.title = WebInspector.RenderingFrameTimelineRecord.displayNameForTaskType(WebInspector.RenderingFrameTimelineRecord.TaskType.Other);
67         columns.otherTime.width = "10%";
68         columns.otherTime.aligned = "right";
69
70         columns.startTime.title = WebInspector.UIString("Start Time");
71         columns.startTime.width = "15%";
72         columns.startTime.aligned = "right";
73
74         columns.location.title = WebInspector.UIString("Location");
75
76         for (var column in columns)
77             columns[column].sortable = true;
78
79         this._dataGrid = new WebInspector.TimelineDataGrid(columns);
80         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("rendering-frame-timeline-view-sort", "startTime");
81         this._dataGrid.sortOrderSetting = new WebInspector.Setting("rendering-frame-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
82
83         this.setupDataGrid(this._dataGrid);
84
85         this.element.classList.add("rendering-frame");
86         this.addSubview(this._dataGrid);
87
88         timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._renderingFrameTimelineRecordAdded, this);
89
90         this._pendingRecords = [];
91     }
92
93     static displayNameForDurationFilter(filter)
94     {
95         switch (filter) {
96             case WebInspector.RenderingFrameTimelineView.DurationFilter.All:
97                 return WebInspector.UIString("All");
98             case WebInspector.RenderingFrameTimelineView.DurationFilter.OverOneMillisecond:
99                 return WebInspector.UIString("Over 1 ms");
100             case WebInspector.RenderingFrameTimelineView.DurationFilter.OverFifteenMilliseconds:
101                 return WebInspector.UIString("Over 15 ms");
102             default:
103                 console.error("Unknown filter type", filter);
104         }
105
106         return null;
107     }
108
109     // Public
110
111     shown()
112     {
113         super.shown();
114
115         this._dataGrid.shown();
116     }
117
118     hidden()
119     {
120         this._dataGrid.hidden();
121
122         super.hidden();
123     }
124
125     closed()
126     {
127         console.assert(this.representedObject instanceof WebInspector.Timeline);
128         this.representedObject.removeEventListener(null, null, this);
129
130         this._dataGrid.closed();
131     }
132
133     get selectionPathComponents()
134     {
135         let dataGridNode = this._dataGrid.selectedNode;
136         if (!dataGridNode || dataGridNode.hidden)
137             return null;
138
139         let pathComponents = [];
140
141         while (dataGridNode && !dataGridNode.root) {
142             console.assert(dataGridNode instanceof WebInspector.TimelineDataGridNode);
143             if (dataGridNode.hidden)
144                 return null;
145
146             let pathComponent = new WebInspector.TimelineDataGridNodePathComponent(dataGridNode);
147             pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this.dataGridNodePathComponentSelected, this);
148             pathComponents.unshift(pathComponent);
149             dataGridNode = dataGridNode.parent;
150         }
151
152         return pathComponents;
153     }
154
155     get filterStartTime()
156     {
157         let records = this.representedObject.records;
158         let startIndex = this.startTime;
159         if (startIndex >= records.length)
160             return Infinity;
161
162         return records[startIndex].startTime;
163     }
164
165     get filterEndTime()
166     {
167         let records = this.representedObject.records;
168         let endIndex = this.endTime - 1;
169         if (endIndex >= records.length)
170             return Infinity;
171
172         return records[endIndex].endTime;
173     }
174
175     reset()
176     {
177         super.reset();
178
179         this._dataGrid.reset();
180
181         this._pendingRecords = [];
182     }
183
184     // Protected
185
186     dataGridNodePathComponentSelected(event)
187     {
188         let dataGridNode = event.data.pathComponent.timelineDataGridNode;
189         console.assert(dataGridNode.dataGrid === this._dataGrid);
190
191         dataGridNode.revealAndSelect();
192     }
193
194     dataGridNodeForTreeElement(treeElement)
195     {
196         if (treeElement instanceof WebInspector.ProfileNodeTreeElement)
197             return new WebInspector.ProfileNodeDataGridNode(treeElement.profileNode, this.zeroTime, this.startTime, this.endTime);
198         return null;
199     }
200
201     matchDataGridNodeAgainstCustomFilters(node)
202     {
203         if (!super.matchDataGridNodeAgainstCustomFilters(node))
204             return false;
205
206         console.assert(node instanceof WebInspector.TimelineDataGridNode);
207         console.assert(this._scopeBar.selectedItems.length === 1);
208         let selectedScopeBarItem = this._scopeBar.selectedItems[0];
209         if (!selectedScopeBarItem || selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.All)
210             return true;
211
212         while (node && !(node.record instanceof WebInspector.RenderingFrameTimelineRecord))
213             node = node.parent;
214
215         console.assert(node, "Cannot apply duration filter: no RenderingFrameTimelineRecord found.");
216         if (!node)
217             return false;
218
219         let minimumDuration = selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.OverOneMillisecond ? 0.001 : 0.015;
220         return node.record.duration > minimumDuration;
221     }
222
223     layout()
224     {
225         this._processPendingRecords();
226     }
227
228     // Private
229
230     _processPendingRecords()
231     {
232         if (!this._pendingRecords.length)
233             return;
234
235         for (let renderingFrameTimelineRecord of this._pendingRecords) {
236             console.assert(renderingFrameTimelineRecord instanceof WebInspector.RenderingFrameTimelineRecord);
237
238             let dataGridNode = new WebInspector.RenderingFrameTimelineDataGridNode(renderingFrameTimelineRecord, this.zeroTime);
239             this._dataGrid.addRowInSortOrder(null, dataGridNode);
240
241             let stack = [{children: renderingFrameTimelineRecord.children, parentDataGridNode: dataGridNode, index: 0}];
242             while (stack.length) {
243                 let entry = stack.lastValue;
244                 if (entry.index >= entry.children.length) {
245                     stack.pop();
246                     continue;
247                 }
248
249                 let childRecord = entry.children[entry.index];
250                 let childDataGridNode = null;
251                 if (childRecord.type === WebInspector.TimelineRecord.Type.Layout) {
252                     childDataGridNode = new WebInspector.LayoutTimelineDataGridNode(childRecord, this.zeroTime);
253
254                     this._dataGrid.addRowInSortOrder(null, childDataGridNode, entry.parentDataGridNode);
255                 } else if (childRecord.type === WebInspector.TimelineRecord.Type.Script) {
256                     let rootNodes = [];
257                     if (childRecord.profile) {
258                         // FIXME: Support using the bottom-up tree once it is implemented.
259                         rootNodes = childRecord.profile.topDownRootNodes;
260                     }
261
262                     childDataGridNode = new WebInspector.ScriptTimelineDataGridNode(childRecord, this.zeroTime);
263
264                     this._dataGrid.addRowInSortOrder(null, childDataGridNode, entry.parentDataGridNode);
265
266                     for (let profileNode of rootNodes) {
267                         let profileNodeDataGridNode = new WebInspector.ProfileNodeDataGridNode(profileNode, this.zeroTime, this.startTime, this.endTime);
268                         this._dataGrid.addRowInSortOrder(null, profileNodeDataGridNode, childDataGridNode);
269                     }
270                 }
271
272                 if (childDataGridNode && childRecord.children.length)
273                     stack.push({children: childRecord.children, parentDataGridNode: childDataGridNode, index: 0});
274                 ++entry.index;
275             }
276         }
277
278         this._pendingRecords = [];
279     }
280
281     _renderingFrameTimelineRecordAdded(event)
282     {
283         var renderingFrameTimelineRecord = event.data.record;
284         console.assert(renderingFrameTimelineRecord instanceof WebInspector.RenderingFrameTimelineRecord);
285         console.assert(renderingFrameTimelineRecord.children.length, "Missing child records for rendering frame.");
286
287         this._pendingRecords.push(renderingFrameTimelineRecord);
288
289         this.needsLayout();
290     }
291
292     _scopeBarSelectionDidChange()
293     {
294         this.filterDidChange();
295     }
296 };
297
298 WebInspector.RenderingFrameTimelineView.DurationFilter = {
299     All: "rendering-frame-timeline-view-duration-filter-all",
300     OverOneMillisecond: "rendering-frame-timeline-view-duration-filter-over-1-ms",
301     OverFifteenMilliseconds: "rendering-frame-timeline-view-duration-filter-over-15-ms"
302 };
303