44375813ecc22041af7228c5b552f564adff3206
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ScriptDetailsTimelineView.js
1 /*
2  * Copyright (C) 2014, 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.ScriptDetailsTimelineView = class ScriptDetailsTimelineView extends WebInspector.TimelineView
27 {
28     constructor(timeline, extraArguments)
29     {
30         super(timeline, extraArguments);
31
32         console.assert(timeline.type === WebInspector.TimelineRecord.Type.Script);
33
34         this.navigationSidebarTreeOutline.element.classList.add("script");
35
36         let columns = {name: {}, location: {}, callCount: {}, startTime: {}, totalTime: {}, selfTime: {}, averageTime: {}};
37
38         columns.name.title = WebInspector.UIString("Name");
39         columns.name.width = "10%";
40         columns.name.icon = true;
41         columns.name.disclosure = true;
42
43         columns.location.title = WebInspector.UIString("Location");
44         columns.location.icon = true;
45         columns.location.width = "15%";
46
47         let isSamplingProfiler = !!window.ScriptProfilerAgent;
48         if (isSamplingProfiler)
49             columns.callCount.title = WebInspector.UIString("Samples");
50         else {
51             // COMPATIBILITY(iOS 9): ScriptProfilerAgent did not exist yet, we had call counts, not samples.
52             columns.callCount.title = WebInspector.UIString("Calls");
53         }
54         columns.callCount.width = "5%";
55         columns.callCount.aligned = "right";
56
57         columns.startTime.title = WebInspector.UIString("Start Time");
58         columns.startTime.width = "10%";
59         columns.startTime.aligned = "right";
60
61         columns.totalTime.title = WebInspector.UIString("Total Time");
62         columns.totalTime.width = "10%";
63         columns.totalTime.aligned = "right";
64
65         columns.selfTime.title = WebInspector.UIString("Self Time");
66         columns.selfTime.width = "10%";
67         columns.selfTime.aligned = "right";
68
69         columns.averageTime.title = WebInspector.UIString("Average Time");
70         columns.averageTime.width = "10%";
71         columns.averageTime.aligned = "right";
72
73         for (var column in columns)
74             columns[column].sortable = true;
75
76         this._dataGrid = new WebInspector.ScriptTimelineDataGrid(this.navigationSidebarTreeOutline, columns, this);
77         this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
78         this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
79         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("script-timeline-view-sort", "startTime");
80         this._dataGrid.sortOrderSetting = new WebInspector.Setting("script-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
81
82         this.element.classList.add("script");
83         this.addSubview(this._dataGrid);
84
85         timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._scriptTimelineRecordAdded, this);
86         timeline.addEventListener(WebInspector.Timeline.Event.Refreshed, this._scriptTimelineRecordRefreshed, this);
87
88         this._pendingRecords = [];
89     }
90
91     // Public
92
93     get navigationSidebarTreeOutlineLabel()
94     {
95         return WebInspector.UIString("Records");
96     }
97
98     shown()
99     {
100         super.shown();
101
102         this._dataGrid.shown();
103     }
104
105     hidden()
106     {
107         this._dataGrid.hidden();
108
109         super.hidden();
110     }
111
112     closed()
113     {
114         console.assert(this.representedObject instanceof WebInspector.Timeline);
115         this.representedObject.removeEventListener(null, null, this);
116
117         this._dataGrid.closed();
118     }
119
120     get selectionPathComponents()
121     {
122         var dataGridNode = this._dataGrid.selectedNode;
123         if (!dataGridNode)
124             return null;
125
126         var pathComponents = [];
127
128         while (dataGridNode && !dataGridNode.root) {
129             var treeElement = this._dataGrid.treeElementForDataGridNode(dataGridNode);
130             console.assert(treeElement);
131             if (!treeElement)
132                 break;
133
134             if (treeElement.hidden)
135                 return null;
136
137             var pathComponent = new WebInspector.GeneralTreeElementPathComponent(treeElement);
138             pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this.treeElementPathComponentSelected, this);
139             pathComponents.unshift(pathComponent);
140             dataGridNode = dataGridNode.parent;
141         }
142
143         return pathComponents;
144     }
145
146     reset()
147     {
148         super.reset();
149
150         this._dataGrid.reset();
151
152         this._pendingRecords = [];
153     }
154
155     // Protected
156
157     canShowContentViewForTreeElement(treeElement)
158     {
159         if (treeElement instanceof WebInspector.ProfileNodeTreeElement)
160             return !!treeElement.profileNode.sourceCodeLocation;
161         return super.canShowContentViewForTreeElement(treeElement);
162     }
163
164     showContentViewForTreeElement(treeElement)
165     {
166         if (treeElement instanceof WebInspector.ProfileNodeTreeElement) {
167             if (treeElement.profileNode.sourceCodeLocation)
168                 WebInspector.showOriginalOrFormattedSourceCodeLocation(treeElement.profileNode.sourceCodeLocation);
169             return;
170         }
171
172         super.showContentViewForTreeElement(treeElement);
173     }
174
175     treeElementPathComponentSelected(event)
176     {
177         var dataGridNode = this._dataGrid.dataGridNodeForTreeElement(event.data.pathComponent.generalTreeElement);
178         if (!dataGridNode)
179             return;
180         dataGridNode.revealAndSelect();
181     }
182
183     treeElementSelected(treeElement, selectedByUser)
184     {
185         if (this._dataGrid.shouldIgnoreSelectionEvent())
186             return;
187
188         super.treeElementSelected(treeElement, selectedByUser);
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     populateProfileNodeTreeElement(treeElement)
199     {
200         var zeroTime = this.zeroTime;
201         var startTime = this.startTime;
202         var endTime = this.endTime;
203
204         for (var childProfileNode of treeElement.profileNode.childNodes) {
205             var profileNodeTreeElement = new WebInspector.ProfileNodeTreeElement(childProfileNode, this);
206             var profileNodeDataGridNode = new WebInspector.ProfileNodeDataGridNode(childProfileNode, zeroTime, startTime, endTime);
207             this._dataGrid.addRowInSortOrder(profileNodeTreeElement, profileNodeDataGridNode, treeElement);
208         }
209     }
210
211     layout()
212     {
213         if (this.startTime !== this._oldStartTime || this.endTime !== this._oldEndTime) {
214             let dataGridNode = this._dataGrid.children[0];
215             while (dataGridNode) {
216                 dataGridNode.updateRangeTimes(this.startTime, this.endTime);
217                 if (dataGridNode.revealed)
218                     dataGridNode.refreshIfNeeded();
219                 dataGridNode = dataGridNode.traverseNextNode(false, null, true);
220             }
221
222             this._oldStartTime = this.startTime;
223             this._oldEndTime = this.endTime;
224         }
225
226         this._processPendingRecords();
227     }
228
229     // Private
230
231     _processPendingRecords()
232     {
233         if (WebInspector.timelineManager.scriptProfilerIsTracking())
234             return;
235
236         if (!this._pendingRecords.length)
237             return;
238
239         let zeroTime = this.zeroTime;
240         let startTime = this.startTime;
241         let endTime = this.endTime;
242
243         for (var scriptTimelineRecord of this._pendingRecords) {
244             var rootNodes = [];
245             if (scriptTimelineRecord.profile) {
246                 // FIXME: Support using the bottom-up tree once it is implemented.
247                 rootNodes = scriptTimelineRecord.profile.topDownRootNodes;
248             }
249
250             var treeElement = new WebInspector.TimelineRecordTreeElement(scriptTimelineRecord, WebInspector.SourceCodeLocation.NameStyle.Short, true);
251             var dataGridNode = new WebInspector.ScriptTimelineDataGridNode(scriptTimelineRecord, zeroTime);
252
253             this._dataGrid.addRowInSortOrder(treeElement, dataGridNode);
254
255             for (var profileNode of rootNodes) {
256                 var profileNodeTreeElement = new WebInspector.ProfileNodeTreeElement(profileNode, this);
257                 var profileNodeDataGridNode = new WebInspector.ProfileNodeDataGridNode(profileNode, zeroTime, startTime, endTime);
258                 this._dataGrid.addRowInSortOrder(profileNodeTreeElement, profileNodeDataGridNode, treeElement);
259             }
260         }
261
262         this._pendingRecords = [];
263     }
264
265     _scriptTimelineRecordAdded(event)
266     {
267         var scriptTimelineRecord = event.data.record;
268         console.assert(scriptTimelineRecord instanceof WebInspector.ScriptTimelineRecord);
269
270         this._pendingRecords.push(scriptTimelineRecord);
271
272         this.needsLayout();
273     }
274
275     _scriptTimelineRecordRefreshed(event)
276     {
277         this.needsLayout();
278     }
279
280     _dataGridFiltersDidChange(event)
281     {
282         this.timelineSidebarPanel.updateFilter();
283     }
284
285     _dataGridNodeSelected(event)
286     {
287         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
288     }
289 };