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