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 26WebInspector.ScriptTimelineView = function(recording) 27{ 28 WebInspector.TimelineView.call(this); 29 30 this.navigationSidebarTreeOutline.onselect = this._treeElementSelected.bind(this); 31 this.navigationSidebarTreeOutline.ondeselect = this._treeElementDeselected.bind(this); 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 var scriptTimeline = recording.timelines.get(WebInspector.TimelineRecord.Type.Script); 72 scriptTimeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._scriptTimelineRecordAdded, this); 73 74 this._pendingRecords = []; 75}; 76 77WebInspector.ScriptTimelineView.StyleClassName = "script"; 78WebInspector.ScriptTimelineView.TreeOutlineStyleClassName = "script"; 79 80WebInspector.ScriptTimelineView.prototype = { 81 constructor: WebInspector.ScriptTimelineView, 82 __proto__: WebInspector.TimelineView.prototype, 83 84 // Public 85 86 get navigationSidebarTreeOutlineLabel() 87 { 88 return WebInspector.UIString("Records"); 89 }, 90 91 shown: function() 92 { 93 WebInspector.TimelineView.prototype.shown.call(this); 94 95 this._dataGrid.shown(); 96 }, 97 98 hidden: function() 99 { 100 this._dataGrid.hidden(); 101 102 WebInspector.TimelineView.prototype.hidden.call(this); 103 }, 104 105 updateLayout: function() 106 { 107 WebInspector.TimelineView.prototype.updateLayout.call(this); 108 109 this._dataGrid.updateLayout(); 110 111 if (this.startTime !== this._oldStartTime || this.endTime !== this._oldEndTime) { 112 var dataGridNode = this._dataGrid.children[0]; 113 while (dataGridNode) { 114 dataGridNode.updateRangeTimes(this.startTime, this.endTime); 115 if (dataGridNode.revealed) 116 dataGridNode.refreshIfNeeded(); 117 dataGridNode = dataGridNode.traverseNextNode(false, null, true); 118 } 119 120 this._oldStartTime = this.startTime; 121 this._oldEndTime = this.endTime; 122 } 123 124 this._processPendingRecords(); 125 }, 126 127 get selectionPathComponents() 128 { 129 var dataGridNode = this._dataGrid.selectedNode; 130 if (!dataGridNode) 131 return null; 132 133 var pathComponents = []; 134 135 while (dataGridNode && !dataGridNode.root) { 136 var treeElement = this._dataGrid.treeElementForDataGridNode(dataGridNode); 137 console.assert(treeElement); 138 if (!treeElement) 139 break; 140 141 if (treeElement.hidden) 142 return null; 143 144 var pathComponent = new WebInspector.GeneralTreeElementPathComponent(treeElement); 145 pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this.treeElementPathComponentSelected, this); 146 pathComponents.unshift(pathComponent); 147 dataGridNode = dataGridNode.parent; 148 } 149 150 return pathComponents; 151 }, 152 153 matchTreeElementAgainstCustomFilters: function(treeElement) 154 { 155 return this._dataGrid.treeElementMatchesActiveScopeFilters(treeElement); 156 }, 157 158 reset: function() 159 { 160 WebInspector.TimelineView.prototype.reset.call(this); 161 162 this._dataGrid.reset(); 163 }, 164 165 // Protected 166 167 treeElementPathComponentSelected: function(event) 168 { 169 var dataGridNode = this._dataGrid.dataGridNodeForTreeElement(event.data.pathComponent.generalTreeElement); 170 if (!dataGridNode) 171 return; 172 dataGridNode.revealAndSelect(); 173 }, 174 175 dataGridNodeForTreeElement: function(treeElement) 176 { 177 if (treeElement instanceof WebInspector.ProfileNodeTreeElement) 178 return new WebInspector.ProfileNodeDataGridNode(treeElement.profileNode, this.zeroTime, this.startTime, this.endTime); 179 return null; 180 }, 181 182 populateProfileNodeTreeElement: function(treeElement) 183 { 184 var zeroTime = this.zeroTime; 185 var startTime = this.startTime; 186 var endTime = this.endTime; 187 188 for (var childProfileNode of treeElement.profileNode.childNodes) { 189 var profileNodeTreeElement = new WebInspector.ProfileNodeTreeElement(childProfileNode, this); 190 var profileNodeDataGridNode = new WebInspector.ProfileNodeDataGridNode(childProfileNode, zeroTime, startTime, endTime); 191 this._dataGrid.addRowInSortOrder(profileNodeTreeElement, profileNodeDataGridNode, treeElement); 192 } 193 }, 194 195 // Private 196 197 _processPendingRecords: function() 198 { 199 if (!this._pendingRecords.length) 200 return; 201 202 for (var scriptTimelineRecord of this._pendingRecords) { 203 var rootNodes = []; 204 if (scriptTimelineRecord.profile) { 205 // FIXME: Support using the bottom-up tree once it is implemented. 206 rootNodes = scriptTimelineRecord.profile.topDownRootNodes; 207 } 208 209 var zeroTime = this.zeroTime; 210 var treeElement = new WebInspector.TimelineRecordTreeElement(scriptTimelineRecord, WebInspector.SourceCodeLocation.NameStyle.Short, rootNodes.length); 211 var dataGridNode = new WebInspector.ScriptTimelineDataGridNode(scriptTimelineRecord, zeroTime); 212 213 this._dataGrid.addRowInSortOrder(treeElement, dataGridNode); 214 215 var startTime = this.startTime; 216 var endTime = this.endTime; 217 218 for (var profileNode of rootNodes) { 219 var profileNodeTreeElement = new WebInspector.ProfileNodeTreeElement(profileNode, this); 220 var profileNodeDataGridNode = new WebInspector.ProfileNodeDataGridNode(profileNode, zeroTime, startTime, endTime); 221 this._dataGrid.addRowInSortOrder(profileNodeTreeElement, profileNodeDataGridNode, treeElement); 222 } 223 } 224 225 this._pendingRecords = []; 226 }, 227 228 _scriptTimelineRecordAdded: function(event) 229 { 230 var scriptTimelineRecord = event.data.record; 231 console.assert(scriptTimelineRecord instanceof WebInspector.ScriptTimelineRecord); 232 233 this._pendingRecords.push(scriptTimelineRecord); 234 235 this.needsLayout(); 236 }, 237 238 _dataGridFiltersDidChange: function(event) 239 { 240 WebInspector.timelineSidebarPanel.updateFilter(); 241 }, 242 243 _dataGridNodeSelected: function(event) 244 { 245 this.dispatchEventToListeners(WebInspector.TimelineView.Event.SelectionPathComponentsDidChange); 246 }, 247 248 _treeElementDeselected: function(treeElement) 249 { 250 if (treeElement.status) 251 treeElement.status = ""; 252 }, 253 254 _treeElementSelected: function(treeElement, selectedByUser) 255 { 256 if (this._dataGrid.shouldIgnoreSelectionEvent()) 257 return; 258 259 if (!WebInspector.timelineSidebarPanel.canShowDifferentContentView()) 260 return; 261 262 if (treeElement instanceof WebInspector.FolderTreeElement) 263 return; 264 265 var sourceCodeLocation = null; 266 if (treeElement instanceof WebInspector.TimelineRecordTreeElement) 267 sourceCodeLocation = treeElement.record.sourceCodeLocation; 268 else if (treeElement instanceof WebInspector.ProfileNodeTreeElement) 269 sourceCodeLocation = treeElement.profileNode.sourceCodeLocation; 270 else 271 console.error("Unknown tree element selected."); 272 273 if (!sourceCodeLocation) { 274 WebInspector.timelineSidebarPanel.showTimelineView(WebInspector.TimelineRecord.Type.Script); 275 return; 276 } 277 278 WebInspector.resourceSidebarPanel.showOriginalOrFormattedSourceCodeLocation(sourceCodeLocation); 279 this._updateTreeElementWithCloseButton(treeElement); 280 }, 281 282 _updateTreeElementWithCloseButton: function(treeElement) 283 { 284 if (this._closeStatusButton) { 285 treeElement.status = this._closeStatusButton.element; 286 return; 287 } 288 289 wrappedSVGDocument(platformImagePath("Close.svg"), null, WebInspector.UIString("Close resource view"), function(element) { 290 this._closeStatusButton = new WebInspector.TreeElementStatusButton(element); 291 this._closeStatusButton.addEventListener(WebInspector.TreeElementStatusButton.Event.Clicked, this._closeStatusButtonClicked, this); 292 if (treeElement === this.navigationSidebarTreeOutline.selectedTreeElement) 293 this._updateTreeElementWithCloseButton(treeElement); 294 }.bind(this)); 295 }, 296 297 _closeStatusButtonClicked: function(event) 298 { 299 this.navigationSidebarTreeOutline.selectedTreeElement.deselect(); 300 WebInspector.timelineSidebarPanel.showTimelineView(WebInspector.TimelineRecord.Type.Script); 301 } 302}; 303