6e285c6866df23782251fc981e2349db928d2a10
[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.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
81         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("rendering-frame-timeline-view-sort", "startTime");
82         this._dataGrid.sortOrderSetting = new WebInspector.Setting("rendering-frame-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
83
84         this.element.classList.add("rendering-frame");
85         this.addSubview(this._dataGrid);
86
87         timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._renderingFrameTimelineRecordAdded, this);
88
89         this._pendingRecords = [];
90     }
91
92     static displayNameForDurationFilter(filter)
93     {
94         switch (filter) {
95             case WebInspector.RenderingFrameTimelineView.DurationFilter.All:
96                 return WebInspector.UIString("All");
97             case WebInspector.RenderingFrameTimelineView.DurationFilter.OverOneMillisecond:
98                 return WebInspector.UIString("Over 1 ms");
99             case WebInspector.RenderingFrameTimelineView.DurationFilter.OverFifteenMilliseconds:
100                 return WebInspector.UIString("Over 15 ms");
101             default:
102                 console.error("Unknown filter type", filter);
103         }
104
105         return null;
106     }
107
108     // Public
109
110     shown()
111     {
112         super.shown();
113
114         this._dataGrid.shown();
115     }
116
117     hidden()
118     {
119         this._dataGrid.hidden();
120
121         super.hidden();
122     }
123
124     closed()
125     {
126         console.assert(this.representedObject instanceof WebInspector.Timeline);
127         this.representedObject.removeEventListener(null, null, this);
128
129         this._dataGrid.closed();
130     }
131
132     get selectionPathComponents()
133     {
134         let dataGridNode = this._dataGrid.selectedNode;
135         if (!dataGridNode || dataGridNode.hidden)
136             return null;
137
138         let pathComponents = [];
139
140         while (dataGridNode && !dataGridNode.root) {
141             console.assert(dataGridNode instanceof WebInspector.TimelineDataGridNode);
142             if (dataGridNode.hidden)
143                 return null;
144
145             let pathComponent = new WebInspector.TimelineDataGridNodePathComponent(dataGridNode);
146             pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this.dataGridNodePathComponentSelected, this);
147             pathComponents.unshift(pathComponent);
148             dataGridNode = dataGridNode.parent;
149         }
150
151         return pathComponents;
152     }
153
154     matchTreeElementAgainstCustomFilters(treeElement)
155     {
156         console.assert(this._scopeBar.selectedItems.length === 1);
157         var selectedScopeBarItem = this._scopeBar.selectedItems[0];
158         if (!selectedScopeBarItem || selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.All)
159             return true;
160
161         while (treeElement && !(treeElement.record instanceof WebInspector.RenderingFrameTimelineRecord))
162             treeElement = treeElement.parent;
163
164         console.assert(treeElement, "Cannot apply duration filter: no RenderingFrameTimelineRecord found.");
165         if (!treeElement)
166             return false;
167
168         var minimumDuration = selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.OverOneMillisecond ? 0.001 : 0.015;
169         return treeElement.record.duration > minimumDuration;
170     }
171
172     reset()
173     {
174         super.reset();
175
176         this._dataGrid.reset();
177
178         this._pendingRecords = [];
179     }
180
181     // Protected
182
183     dataGridNodePathComponentSelected(event)
184     {
185         let dataGridNode = event.data.pathComponent.timelineDataGridNode;
186         console.assert(dataGridNode.dataGrid === this._dataGrid);
187
188         dataGridNode.revealAndSelect();
189     }
190
191     dataGridNodeForTreeElement(treeElement)
192     {
193         if (treeElement instanceof WebInspector.ProfileNodeTreeElement)
194             return new WebInspector.ProfileNodeDataGridNode(treeElement.profileNode, this.zeroTime, this.startTime, this.endTime);
195         return null;
196     }
197
198     layout()
199     {
200         this._processPendingRecords();
201     }
202
203     // Private
204
205     _processPendingRecords()
206     {
207         if (!this._pendingRecords.length)
208             return;
209
210         for (let renderingFrameTimelineRecord of this._pendingRecords) {
211             console.assert(renderingFrameTimelineRecord instanceof WebInspector.RenderingFrameTimelineRecord);
212
213             let dataGridNode = new WebInspector.RenderingFrameTimelineDataGridNode(renderingFrameTimelineRecord, this.zeroTime);
214             this._dataGrid.addRowInSortOrder(null, dataGridNode);
215
216             let stack = [{children: renderingFrameTimelineRecord.children, parentDataGridNode: dataGridNode, index: 0}];
217             while (stack.length) {
218                 let entry = stack.lastValue;
219                 if (entry.index >= entry.children.length) {
220                     stack.pop();
221                     continue;
222                 }
223
224                 let childRecord = entry.children[entry.index];
225                 let childDataGridNode = null;
226                 if (childRecord.type === WebInspector.TimelineRecord.Type.Layout) {
227                     childDataGridNode = new WebInspector.LayoutTimelineDataGridNode(childRecord, this.zeroTime);
228
229                     this._dataGrid.addRowInSortOrder(null, childDataGridNode, entry.parentDataGridNode);
230                 } else if (childRecord.type === WebInspector.TimelineRecord.Type.Script) {
231                     let rootNodes = [];
232                     if (childRecord.profile) {
233                         // FIXME: Support using the bottom-up tree once it is implemented.
234                         rootNodes = childRecord.profile.topDownRootNodes;
235                     }
236
237                     childDataGridNode = new WebInspector.ScriptTimelineDataGridNode(childRecord, this.zeroTime);
238
239                     this._dataGrid.addRowInSortOrder(null, childDataGridNode, entry.parentDataGridNode);
240
241                     for (let profileNode of rootNodes) {
242                         let profileNodeDataGridNode = new WebInspector.ProfileNodeDataGridNode(profileNode, this.zeroTime, this.startTime, this.endTime);
243                         this._dataGrid.addRowInSortOrder(null, profileNodeDataGridNode, childDataGridNode);
244                     }
245                 }
246
247                 if (childDataGridNode && childRecord.children.length)
248                     stack.push({children: childRecord.children, parentDataGridNode: childDataGridNode, index: 0});
249                 ++entry.index;
250             }
251         }
252
253         this._pendingRecords = [];
254     }
255
256     _renderingFrameTimelineRecordAdded(event)
257     {
258         var renderingFrameTimelineRecord = event.data.record;
259         console.assert(renderingFrameTimelineRecord instanceof WebInspector.RenderingFrameTimelineRecord);
260         console.assert(renderingFrameTimelineRecord.children.length, "Missing child records for rendering frame.");
261
262         this._pendingRecords.push(renderingFrameTimelineRecord);
263
264         this.needsLayout();
265     }
266
267     _dataGridNodeSelected(event)
268     {
269         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
270     }
271
272     _scopeBarSelectionDidChange(event)
273     {
274         // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
275     }
276 };
277
278 WebInspector.RenderingFrameTimelineView.DurationFilter = {
279     All: "rendering-frame-timeline-view-duration-filter-all",
280     OverOneMillisecond: "rendering-frame-timeline-view-duration-filter-over-1-ms",
281     OverFifteenMilliseconds: "rendering-frame-timeline-view-duration-filter-over-15-ms"
282 };
283