OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | |
5 /** | 4 /** |
6 * @constructor | |
7 * @implements {WebInspector.Searchable} | 5 * @implements {WebInspector.Searchable} |
8 * @extends {WebInspector.SimpleView} | 6 * @unrestricted |
9 */ | 7 */ |
10 WebInspector.ProfileView = function() | 8 WebInspector.ProfileView = class extends WebInspector.SimpleView { |
11 { | 9 constructor() { |
12 WebInspector.SimpleView.call(this, WebInspector.UIString("Profile")); | 10 super(WebInspector.UIString('Profile')); |
13 | 11 |
14 this._searchableView = new WebInspector.SearchableView(this); | 12 this._searchableView = new WebInspector.SearchableView(this); |
15 this._searchableView.setPlaceholder(WebInspector.UIString("Find by cost (>50
ms), name or file")); | 13 this._searchableView.setPlaceholder(WebInspector.UIString('Find by cost (>50
ms), name or file')); |
16 this._searchableView.show(this.element); | 14 this._searchableView.show(this.element); |
17 | 15 |
18 var columns = /** @type {!Array<!WebInspector.DataGrid.ColumnDescriptor>} */
([]); | 16 var columns = /** @type {!Array<!WebInspector.DataGrid.ColumnDescriptor>} */
([]); |
19 columns.push({id: "self", title: this.columnHeader("self"), width: "120px",
fixedWidth: true, sortable: true, sort: WebInspector.DataGrid.Order.Descending})
; | 17 columns.push({ |
20 columns.push({id: "total", title: this.columnHeader("total"), width: "120px"
, fixedWidth: true, sortable: true}); | 18 id: 'self', |
21 columns.push({id: "function", title: WebInspector.UIString("Function"), disc
losure: true, sortable: true}); | 19 title: this.columnHeader('self'), |
| 20 width: '120px', |
| 21 fixedWidth: true, |
| 22 sortable: true, |
| 23 sort: WebInspector.DataGrid.Order.Descending |
| 24 }); |
| 25 columns.push({id: 'total', title: this.columnHeader('total'), width: '120px'
, fixedWidth: true, sortable: true}); |
| 26 columns.push({id: 'function', title: WebInspector.UIString('Function'), disc
losure: true, sortable: true}); |
22 | 27 |
23 this.dataGrid = new WebInspector.DataGrid(columns); | 28 this.dataGrid = new WebInspector.DataGrid(columns); |
24 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged,
this._sortProfile, this); | 29 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged,
this._sortProfile, this); |
25 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, th
is._nodeSelected.bind(this, true)); | 30 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, th
is._nodeSelected.bind(this, true)); |
26 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.DeselectedNode,
this._nodeSelected.bind(this, false)); | 31 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.DeselectedNode,
this._nodeSelected.bind(this, false)); |
27 | 32 |
28 this.viewSelectComboBox = new WebInspector.ToolbarComboBox(this._changeView.
bind(this)); | 33 this.viewSelectComboBox = new WebInspector.ToolbarComboBox(this._changeView.
bind(this)); |
29 | 34 |
30 this.focusButton = new WebInspector.ToolbarButton(WebInspector.UIString("Foc
us selected function"), "visibility-toolbar-item"); | 35 this.focusButton = |
| 36 new WebInspector.ToolbarButton(WebInspector.UIString('Focus selected fun
ction'), 'visibility-toolbar-item'); |
31 this.focusButton.setEnabled(false); | 37 this.focusButton.setEnabled(false); |
32 this.focusButton.addEventListener("click", this._focusClicked, this); | 38 this.focusButton.addEventListener('click', this._focusClicked, this); |
33 | 39 |
34 this.excludeButton = new WebInspector.ToolbarButton(WebInspector.UIString("E
xclude selected function"), "delete-toolbar-item"); | 40 this.excludeButton = |
| 41 new WebInspector.ToolbarButton(WebInspector.UIString('Exclude selected f
unction'), 'delete-toolbar-item'); |
35 this.excludeButton.setEnabled(false); | 42 this.excludeButton.setEnabled(false); |
36 this.excludeButton.addEventListener("click", this._excludeClicked, this); | 43 this.excludeButton.addEventListener('click', this._excludeClicked, this); |
37 | 44 |
38 this.resetButton = new WebInspector.ToolbarButton(WebInspector.UIString("Res
tore all functions"), "refresh-toolbar-item"); | 45 this.resetButton = |
| 46 new WebInspector.ToolbarButton(WebInspector.UIString('Restore all functi
ons'), 'refresh-toolbar-item'); |
39 this.resetButton.setEnabled(false); | 47 this.resetButton.setEnabled(false); |
40 this.resetButton.addEventListener("click", this._resetClicked, this); | 48 this.resetButton.addEventListener('click', this._resetClicked, this); |
41 | 49 |
42 this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.Defa
ultFormatter(30)); | 50 this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.Defa
ultFormatter(30)); |
| 51 } |
| 52 |
| 53 /** |
| 54 * @param {!Array<!{title: string, value: string}>} entryInfo |
| 55 * @return {!Element} |
| 56 */ |
| 57 static buildPopoverTable(entryInfo) { |
| 58 var table = createElement('table'); |
| 59 for (var entry of entryInfo) { |
| 60 var row = table.createChild('tr'); |
| 61 row.createChild('td').textContent = entry.title; |
| 62 row.createChild('td').textContent = entry.value; |
| 63 } |
| 64 return table; |
| 65 } |
| 66 |
| 67 /** |
| 68 * @param {!WebInspector.ProfileDataGridNode.Formatter} nodeFormatter |
| 69 * @param {!Array<string>=} viewTypes |
| 70 * @protected |
| 71 */ |
| 72 initialize(nodeFormatter, viewTypes) { |
| 73 this._nodeFormatter = nodeFormatter; |
| 74 |
| 75 this._viewType = WebInspector.settings.createSetting('profileView', WebInspe
ctor.ProfileView.ViewTypes.Heavy); |
| 76 viewTypes = viewTypes || [ |
| 77 WebInspector.ProfileView.ViewTypes.Flame, WebInspector.ProfileView.ViewTyp
es.Heavy, |
| 78 WebInspector.ProfileView.ViewTypes.Tree |
| 79 ]; |
| 80 |
| 81 var optionNames = new Map([ |
| 82 [WebInspector.ProfileView.ViewTypes.Flame, WebInspector.UIString('Chart')]
, |
| 83 [WebInspector.ProfileView.ViewTypes.Heavy, WebInspector.UIString('Heavy (B
ottom Up)')], |
| 84 [WebInspector.ProfileView.ViewTypes.Tree, WebInspector.UIString('Tree (Top
Down)')], |
| 85 ]); |
| 86 |
| 87 var options = |
| 88 new Map(viewTypes.map(type => [type, this.viewSelectComboBox.createOptio
n(optionNames.get(type), '', type)])); |
| 89 var optionName = this._viewType.get() || viewTypes[0]; |
| 90 var option = options.get(optionName) || options.get(viewTypes[0]); |
| 91 this.viewSelectComboBox.select(option); |
| 92 |
| 93 this._changeView(); |
| 94 if (this._flameChart) |
| 95 this._flameChart.update(); |
| 96 } |
| 97 |
| 98 /** |
| 99 * @override |
| 100 */ |
| 101 focus() { |
| 102 if (this._flameChart) |
| 103 this._flameChart.focus(); |
| 104 else |
| 105 super.focus(); |
| 106 } |
| 107 |
| 108 /** |
| 109 * @param {string} columnId |
| 110 * @return {string} |
| 111 */ |
| 112 columnHeader(columnId) { |
| 113 throw 'Not implemented'; |
| 114 } |
| 115 |
| 116 /** |
| 117 * @return {?WebInspector.Target} |
| 118 */ |
| 119 target() { |
| 120 return this._profileHeader.target(); |
| 121 } |
| 122 |
| 123 /** |
| 124 * @param {number} timeLeft |
| 125 * @param {number} timeRight |
| 126 */ |
| 127 selectRange(timeLeft, timeRight) { |
| 128 if (!this._flameChart) |
| 129 return; |
| 130 this._flameChart.selectRange(timeLeft, timeRight); |
| 131 } |
| 132 |
| 133 /** |
| 134 * @override |
| 135 * @return {!Array.<!WebInspector.ToolbarItem>} |
| 136 */ |
| 137 syncToolbarItems() { |
| 138 return [this.viewSelectComboBox, this.focusButton, this.excludeButton, this.
resetButton]; |
| 139 } |
| 140 |
| 141 /** |
| 142 * @return {!WebInspector.ProfileDataGridTree} |
| 143 */ |
| 144 _getBottomUpProfileDataGridTree() { |
| 145 if (!this._bottomUpProfileDataGridTree) |
| 146 this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfileDataGr
idTree( |
| 147 this._nodeFormatter, this._searchableView, this.profile.root, this.adj
ustedTotal); |
| 148 return this._bottomUpProfileDataGridTree; |
| 149 } |
| 150 |
| 151 /** |
| 152 * @return {!WebInspector.ProfileDataGridTree} |
| 153 */ |
| 154 _getTopDownProfileDataGridTree() { |
| 155 if (!this._topDownProfileDataGridTree) |
| 156 this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDataGrid
Tree( |
| 157 this._nodeFormatter, this._searchableView, this.profile.root, this.adj
ustedTotal); |
| 158 return this._topDownProfileDataGridTree; |
| 159 } |
| 160 |
| 161 /** |
| 162 * @override |
| 163 */ |
| 164 willHide() { |
| 165 this._currentSearchResultIndex = -1; |
| 166 } |
| 167 |
| 168 refresh() { |
| 169 var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selecte
dNode.profileNode : null; |
| 170 |
| 171 this.dataGrid.rootNode().removeChildren(); |
| 172 |
| 173 var children = this.profileDataGridTree.children; |
| 174 var count = children.length; |
| 175 |
| 176 for (var index = 0; index < count; ++index) |
| 177 this.dataGrid.rootNode().appendChild(children[index]); |
| 178 |
| 179 if (selectedProfileNode) |
| 180 selectedProfileNode.selected = true; |
| 181 } |
| 182 |
| 183 refreshVisibleData() { |
| 184 var child = this.dataGrid.rootNode().children[0]; |
| 185 while (child) { |
| 186 child.refresh(); |
| 187 child = child.traverseNextNode(false, null, true); |
| 188 } |
| 189 } |
| 190 |
| 191 /** |
| 192 * @return {!WebInspector.SearchableView} |
| 193 */ |
| 194 searchableView() { |
| 195 return this._searchableView; |
| 196 } |
| 197 |
| 198 /** |
| 199 * @override |
| 200 * @return {boolean} |
| 201 */ |
| 202 supportsCaseSensitiveSearch() { |
| 203 return true; |
| 204 } |
| 205 |
| 206 /** |
| 207 * @override |
| 208 * @return {boolean} |
| 209 */ |
| 210 supportsRegexSearch() { |
| 211 return false; |
| 212 } |
| 213 |
| 214 /** |
| 215 * @override |
| 216 */ |
| 217 searchCanceled() { |
| 218 this._searchableElement.searchCanceled(); |
| 219 } |
| 220 |
| 221 /** |
| 222 * @override |
| 223 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig |
| 224 * @param {boolean} shouldJump |
| 225 * @param {boolean=} jumpBackwards |
| 226 */ |
| 227 performSearch(searchConfig, shouldJump, jumpBackwards) { |
| 228 this._searchableElement.performSearch(searchConfig, shouldJump, jumpBackward
s); |
| 229 } |
| 230 |
| 231 /** |
| 232 * @override |
| 233 */ |
| 234 jumpToNextSearchResult() { |
| 235 this._searchableElement.jumpToNextSearchResult(); |
| 236 } |
| 237 |
| 238 /** |
| 239 * @override |
| 240 */ |
| 241 jumpToPreviousSearchResult() { |
| 242 this._searchableElement.jumpToPreviousSearchResult(); |
| 243 } |
| 244 |
| 245 /** |
| 246 * @return {!WebInspector.Linkifier} |
| 247 */ |
| 248 linkifier() { |
| 249 return this._linkifier; |
| 250 } |
| 251 |
| 252 /** |
| 253 * @return {!WebInspector.FlameChartDataProvider} |
| 254 */ |
| 255 createFlameChartDataProvider() { |
| 256 throw 'Not implemented'; |
| 257 } |
| 258 |
| 259 _ensureFlameChartCreated() { |
| 260 if (this._flameChart) |
| 261 return; |
| 262 this._dataProvider = this.createFlameChartDataProvider(); |
| 263 this._flameChart = new WebInspector.CPUProfileFlameChart(this._searchableVie
w, this._dataProvider); |
| 264 this._flameChart.addEventListener(WebInspector.FlameChart.Events.EntrySelect
ed, this._onEntrySelected.bind(this)); |
| 265 } |
| 266 |
| 267 /** |
| 268 * @param {!WebInspector.Event} event |
| 269 */ |
| 270 _onEntrySelected(event) { |
| 271 var entryIndex = event.data; |
| 272 var node = this._dataProvider._entryNodes[entryIndex]; |
| 273 var debuggerModel = this._profileHeader._debuggerModel; |
| 274 if (!node || !node.scriptId || !debuggerModel) |
| 275 return; |
| 276 var script = debuggerModel.scriptForId(node.scriptId); |
| 277 if (!script) |
| 278 return; |
| 279 var location = /** @type {!WebInspector.DebuggerModel.Location} */ ( |
| 280 debuggerModel.createRawLocation(script, node.lineNumber, node.columnNumb
er)); |
| 281 WebInspector.Revealer.reveal(WebInspector.debuggerWorkspaceBinding.rawLocati
onToUILocation(location)); |
| 282 } |
| 283 |
| 284 _changeView() { |
| 285 if (!this.profile) |
| 286 return; |
| 287 |
| 288 this._searchableView.closeSearch(); |
| 289 |
| 290 if (this._visibleView) |
| 291 this._visibleView.detach(); |
| 292 |
| 293 this._viewType.set(this.viewSelectComboBox.selectedOption().value); |
| 294 switch (this._viewType.get()) { |
| 295 case WebInspector.ProfileView.ViewTypes.Flame: |
| 296 this._ensureFlameChartCreated(); |
| 297 this._visibleView = this._flameChart; |
| 298 this._searchableElement = this._flameChart; |
| 299 break; |
| 300 case WebInspector.ProfileView.ViewTypes.Tree: |
| 301 this.profileDataGridTree = this._getTopDownProfileDataGridTree(); |
| 302 this._sortProfile(); |
| 303 this._visibleView = this.dataGrid.asWidget(); |
| 304 this._searchableElement = this.profileDataGridTree; |
| 305 break; |
| 306 case WebInspector.ProfileView.ViewTypes.Heavy: |
| 307 this.profileDataGridTree = this._getBottomUpProfileDataGridTree(); |
| 308 this._sortProfile(); |
| 309 this._visibleView = this.dataGrid.asWidget(); |
| 310 this._searchableElement = this.profileDataGridTree; |
| 311 break; |
| 312 } |
| 313 |
| 314 var isFlame = this._viewType.get() === WebInspector.ProfileView.ViewTypes.Fl
ame; |
| 315 this.focusButton.setVisible(!isFlame); |
| 316 this.excludeButton.setVisible(!isFlame); |
| 317 this.resetButton.setVisible(!isFlame); |
| 318 |
| 319 this._visibleView.show(this._searchableView.element); |
| 320 } |
| 321 |
| 322 /** |
| 323 * @param {boolean} selected |
| 324 */ |
| 325 _nodeSelected(selected) { |
| 326 this.focusButton.setEnabled(selected); |
| 327 this.excludeButton.setEnabled(selected); |
| 328 } |
| 329 |
| 330 _focusClicked(event) { |
| 331 if (!this.dataGrid.selectedNode) |
| 332 return; |
| 333 |
| 334 this.resetButton.setEnabled(true); |
| 335 this.profileDataGridTree.focus(this.dataGrid.selectedNode); |
| 336 this.refresh(); |
| 337 this.refreshVisibleData(); |
| 338 } |
| 339 |
| 340 _excludeClicked(event) { |
| 341 var selectedNode = this.dataGrid.selectedNode; |
| 342 |
| 343 if (!selectedNode) |
| 344 return; |
| 345 |
| 346 selectedNode.deselect(); |
| 347 |
| 348 this.resetButton.setEnabled(true); |
| 349 this.profileDataGridTree.exclude(selectedNode); |
| 350 this.refresh(); |
| 351 this.refreshVisibleData(); |
| 352 } |
| 353 |
| 354 _resetClicked(event) { |
| 355 this.resetButton.setEnabled(false); |
| 356 this.profileDataGridTree.restore(); |
| 357 this._linkifier.reset(); |
| 358 this.refresh(); |
| 359 this.refreshVisibleData(); |
| 360 } |
| 361 |
| 362 _sortProfile() { |
| 363 var sortAscending = this.dataGrid.isSortOrderAscending(); |
| 364 var sortColumnId = this.dataGrid.sortColumnId(); |
| 365 var sortProperty = sortColumnId === 'function' ? 'functionName' : sortColumn
Id || ''; |
| 366 this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyCompa
rator(sortProperty, sortAscending)); |
| 367 |
| 368 this.refresh(); |
| 369 } |
43 }; | 370 }; |
44 | 371 |
45 /** @enum {string} */ | 372 /** @enum {string} */ |
46 WebInspector.ProfileView.ViewTypes = { | 373 WebInspector.ProfileView.ViewTypes = { |
47 Flame: "Flame", | 374 Flame: 'Flame', |
48 Tree: "Tree", | 375 Tree: 'Tree', |
49 Heavy: "Heavy" | 376 Heavy: 'Heavy' |
50 }; | 377 }; |
51 | 378 |
| 379 |
52 /** | 380 /** |
53 * @param {!Array<!{title: string, value: string}>} entryInfo | |
54 * @return {!Element} | |
55 */ | |
56 WebInspector.ProfileView.buildPopoverTable = function(entryInfo) | |
57 { | |
58 var table = createElement("table"); | |
59 for (var entry of entryInfo) { | |
60 var row = table.createChild("tr"); | |
61 row.createChild("td").textContent = entry.title; | |
62 row.createChild("td").textContent = entry.value; | |
63 } | |
64 return table; | |
65 }; | |
66 | |
67 WebInspector.ProfileView.prototype = { | |
68 /** | |
69 * @param {!WebInspector.ProfileDataGridNode.Formatter} nodeFormatter | |
70 * @param {!Array<string>=} viewTypes | |
71 * @protected | |
72 */ | |
73 initialize: function(nodeFormatter, viewTypes) | |
74 { | |
75 this._nodeFormatter = nodeFormatter; | |
76 | |
77 this._viewType = WebInspector.settings.createSetting("profileView", WebI
nspector.ProfileView.ViewTypes.Heavy); | |
78 viewTypes = viewTypes || [ | |
79 WebInspector.ProfileView.ViewTypes.Flame, | |
80 WebInspector.ProfileView.ViewTypes.Heavy, | |
81 WebInspector.ProfileView.ViewTypes.Tree | |
82 ]; | |
83 | |
84 var optionNames = new Map([ | |
85 [WebInspector.ProfileView.ViewTypes.Flame, WebInspector.UIString("Ch
art")], | |
86 [WebInspector.ProfileView.ViewTypes.Heavy, WebInspector.UIString("He
avy (Bottom Up)")], | |
87 [WebInspector.ProfileView.ViewTypes.Tree, WebInspector.UIString("Tre
e (Top Down)")], | |
88 ]); | |
89 | |
90 var options = new Map(viewTypes.map(type => [type, this.viewSelectComboB
ox.createOption(optionNames.get(type), "", type)])); | |
91 var optionName = this._viewType.get() || viewTypes[0]; | |
92 var option = options.get(optionName) || options.get(viewTypes[0]); | |
93 this.viewSelectComboBox.select(option); | |
94 | |
95 this._changeView(); | |
96 if (this._flameChart) | |
97 this._flameChart.update(); | |
98 }, | |
99 | |
100 focus: function() | |
101 { | |
102 if (this._flameChart) | |
103 this._flameChart.focus(); | |
104 else | |
105 WebInspector.Widget.prototype.focus.call(this); | |
106 }, | |
107 | |
108 /** | |
109 * @param {string} columnId | |
110 * @return {string} | |
111 */ | |
112 columnHeader: function(columnId) | |
113 { | |
114 throw "Not implemented"; | |
115 }, | |
116 | |
117 /** | |
118 * @return {?WebInspector.Target} | |
119 */ | |
120 target: function() | |
121 { | |
122 return this._profileHeader.target(); | |
123 }, | |
124 | |
125 /** | |
126 * @param {number} timeLeft | |
127 * @param {number} timeRight | |
128 */ | |
129 selectRange: function(timeLeft, timeRight) | |
130 { | |
131 if (!this._flameChart) | |
132 return; | |
133 this._flameChart.selectRange(timeLeft, timeRight); | |
134 }, | |
135 | |
136 /** | |
137 * @override | |
138 * @return {!Array.<!WebInspector.ToolbarItem>} | |
139 */ | |
140 syncToolbarItems: function() | |
141 { | |
142 return [this.viewSelectComboBox, this.focusButton, this.excludeButton, t
his.resetButton]; | |
143 }, | |
144 | |
145 /** | |
146 * @return {!WebInspector.ProfileDataGridTree} | |
147 */ | |
148 _getBottomUpProfileDataGridTree: function() | |
149 { | |
150 if (!this._bottomUpProfileDataGridTree) | |
151 this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfile
DataGridTree(this._nodeFormatter, this._searchableView, this.profile.root, this.
adjustedTotal); | |
152 return this._bottomUpProfileDataGridTree; | |
153 }, | |
154 | |
155 /** | |
156 * @return {!WebInspector.ProfileDataGridTree} | |
157 */ | |
158 _getTopDownProfileDataGridTree: function() | |
159 { | |
160 if (!this._topDownProfileDataGridTree) | |
161 this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDa
taGridTree(this._nodeFormatter, this._searchableView, this.profile.root, this.ad
justedTotal); | |
162 return this._topDownProfileDataGridTree; | |
163 }, | |
164 | |
165 /** | |
166 * @override | |
167 */ | |
168 willHide: function() | |
169 { | |
170 this._currentSearchResultIndex = -1; | |
171 }, | |
172 | |
173 refresh: function() | |
174 { | |
175 var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.sel
ectedNode.profileNode : null; | |
176 | |
177 this.dataGrid.rootNode().removeChildren(); | |
178 | |
179 var children = this.profileDataGridTree.children; | |
180 var count = children.length; | |
181 | |
182 for (var index = 0; index < count; ++index) | |
183 this.dataGrid.rootNode().appendChild(children[index]); | |
184 | |
185 if (selectedProfileNode) | |
186 selectedProfileNode.selected = true; | |
187 }, | |
188 | |
189 refreshVisibleData: function() | |
190 { | |
191 var child = this.dataGrid.rootNode().children[0]; | |
192 while (child) { | |
193 child.refresh(); | |
194 child = child.traverseNextNode(false, null, true); | |
195 } | |
196 }, | |
197 | |
198 /** | |
199 * @return {!WebInspector.SearchableView} | |
200 */ | |
201 searchableView: function() | |
202 { | |
203 return this._searchableView; | |
204 }, | |
205 | |
206 /** | |
207 * @override | |
208 * @return {boolean} | |
209 */ | |
210 supportsCaseSensitiveSearch: function() | |
211 { | |
212 return true; | |
213 }, | |
214 | |
215 /** | |
216 * @override | |
217 * @return {boolean} | |
218 */ | |
219 supportsRegexSearch: function() | |
220 { | |
221 return false; | |
222 }, | |
223 | |
224 /** | |
225 * @override | |
226 */ | |
227 searchCanceled: function() | |
228 { | |
229 this._searchableElement.searchCanceled(); | |
230 }, | |
231 | |
232 /** | |
233 * @override | |
234 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig | |
235 * @param {boolean} shouldJump | |
236 * @param {boolean=} jumpBackwards | |
237 */ | |
238 performSearch: function(searchConfig, shouldJump, jumpBackwards) | |
239 { | |
240 this._searchableElement.performSearch(searchConfig, shouldJump, jumpBack
wards); | |
241 }, | |
242 | |
243 /** | |
244 * @override | |
245 */ | |
246 jumpToNextSearchResult: function() | |
247 { | |
248 this._searchableElement.jumpToNextSearchResult(); | |
249 }, | |
250 | |
251 /** | |
252 * @override | |
253 */ | |
254 jumpToPreviousSearchResult: function() | |
255 { | |
256 this._searchableElement.jumpToPreviousSearchResult(); | |
257 }, | |
258 | |
259 /** | |
260 * @return {!WebInspector.Linkifier} | |
261 */ | |
262 linkifier: function() | |
263 { | |
264 return this._linkifier; | |
265 }, | |
266 | |
267 /** | |
268 * @return {!WebInspector.FlameChartDataProvider} | |
269 */ | |
270 createFlameChartDataProvider: function() | |
271 { | |
272 throw "Not implemented"; | |
273 }, | |
274 | |
275 _ensureFlameChartCreated: function() | |
276 { | |
277 if (this._flameChart) | |
278 return; | |
279 this._dataProvider = this.createFlameChartDataProvider(); | |
280 this._flameChart = new WebInspector.CPUProfileFlameChart(this._searchabl
eView, this._dataProvider); | |
281 this._flameChart.addEventListener(WebInspector.FlameChart.Events.EntrySe
lected, this._onEntrySelected.bind(this)); | |
282 }, | |
283 | |
284 /** | |
285 * @param {!WebInspector.Event} event | |
286 */ | |
287 _onEntrySelected: function(event) | |
288 { | |
289 var entryIndex = event.data; | |
290 var node = this._dataProvider._entryNodes[entryIndex]; | |
291 var debuggerModel = this._profileHeader._debuggerModel; | |
292 if (!node || !node.scriptId || !debuggerModel) | |
293 return; | |
294 var script = debuggerModel.scriptForId(node.scriptId); | |
295 if (!script) | |
296 return; | |
297 var location = /** @type {!WebInspector.DebuggerModel.Location} */ (debu
ggerModel.createRawLocation(script, node.lineNumber, node.columnNumber)); | |
298 WebInspector.Revealer.reveal(WebInspector.debuggerWorkspaceBinding.rawLo
cationToUILocation(location)); | |
299 }, | |
300 | |
301 _changeView: function() | |
302 { | |
303 if (!this.profile) | |
304 return; | |
305 | |
306 this._searchableView.closeSearch(); | |
307 | |
308 if (this._visibleView) | |
309 this._visibleView.detach(); | |
310 | |
311 this._viewType.set(this.viewSelectComboBox.selectedOption().value); | |
312 switch (this._viewType.get()) { | |
313 case WebInspector.ProfileView.ViewTypes.Flame: | |
314 this._ensureFlameChartCreated(); | |
315 this._visibleView = this._flameChart; | |
316 this._searchableElement = this._flameChart; | |
317 break; | |
318 case WebInspector.ProfileView.ViewTypes.Tree: | |
319 this.profileDataGridTree = this._getTopDownProfileDataGridTree(); | |
320 this._sortProfile(); | |
321 this._visibleView = this.dataGrid.asWidget(); | |
322 this._searchableElement = this.profileDataGridTree; | |
323 break; | |
324 case WebInspector.ProfileView.ViewTypes.Heavy: | |
325 this.profileDataGridTree = this._getBottomUpProfileDataGridTree(); | |
326 this._sortProfile(); | |
327 this._visibleView = this.dataGrid.asWidget(); | |
328 this._searchableElement = this.profileDataGridTree; | |
329 break; | |
330 } | |
331 | |
332 var isFlame = this._viewType.get() === WebInspector.ProfileView.ViewType
s.Flame; | |
333 this.focusButton.setVisible(!isFlame); | |
334 this.excludeButton.setVisible(!isFlame); | |
335 this.resetButton.setVisible(!isFlame); | |
336 | |
337 this._visibleView.show(this._searchableView.element); | |
338 }, | |
339 | |
340 /** | |
341 * @param {boolean} selected | |
342 */ | |
343 _nodeSelected: function(selected) | |
344 { | |
345 this.focusButton.setEnabled(selected); | |
346 this.excludeButton.setEnabled(selected); | |
347 }, | |
348 | |
349 _focusClicked: function(event) | |
350 { | |
351 if (!this.dataGrid.selectedNode) | |
352 return; | |
353 | |
354 this.resetButton.setEnabled(true); | |
355 this.profileDataGridTree.focus(this.dataGrid.selectedNode); | |
356 this.refresh(); | |
357 this.refreshVisibleData(); | |
358 }, | |
359 | |
360 _excludeClicked: function(event) | |
361 { | |
362 var selectedNode = this.dataGrid.selectedNode; | |
363 | |
364 if (!selectedNode) | |
365 return; | |
366 | |
367 selectedNode.deselect(); | |
368 | |
369 this.resetButton.setEnabled(true); | |
370 this.profileDataGridTree.exclude(selectedNode); | |
371 this.refresh(); | |
372 this.refreshVisibleData(); | |
373 }, | |
374 | |
375 _resetClicked: function(event) | |
376 { | |
377 this.resetButton.setEnabled(false); | |
378 this.profileDataGridTree.restore(); | |
379 this._linkifier.reset(); | |
380 this.refresh(); | |
381 this.refreshVisibleData(); | |
382 }, | |
383 | |
384 _sortProfile: function() | |
385 { | |
386 var sortAscending = this.dataGrid.isSortOrderAscending(); | |
387 var sortColumnId = this.dataGrid.sortColumnId(); | |
388 var sortProperty = sortColumnId === "function" ? "functionName" : sortCo
lumnId || ""; | |
389 this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyC
omparator(sortProperty, sortAscending)); | |
390 | |
391 this.refresh(); | |
392 }, | |
393 | |
394 __proto__: WebInspector.SimpleView.prototype | |
395 }; | |
396 | |
397 /** | |
398 * @constructor | |
399 * @extends {WebInspector.ProfileHeader} | |
400 * @implements {WebInspector.OutputStream} | 381 * @implements {WebInspector.OutputStream} |
401 * @implements {WebInspector.OutputStreamDelegate} | 382 * @implements {WebInspector.OutputStreamDelegate} |
402 * @param {?WebInspector.Target} target | 383 * @unrestricted |
403 * @param {!WebInspector.ProfileType} type | |
404 * @param {string=} title | |
405 */ | 384 */ |
406 WebInspector.WritableProfileHeader = function(target, type, title) | 385 WebInspector.WritableProfileHeader = class extends WebInspector.ProfileHeader { |
407 { | 386 /** |
408 WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UI
String("Profile %d", type.nextProfileUid())); | 387 * @param {?WebInspector.Target} target |
| 388 * @param {!WebInspector.ProfileType} type |
| 389 * @param {string=} title |
| 390 */ |
| 391 constructor(target, type, title) { |
| 392 super(target, type, title || WebInspector.UIString('Profile %d', type.nextPr
ofileUid())); |
409 this._debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | 393 this._debuggerModel = WebInspector.DebuggerModel.fromTarget(target); |
410 this._tempFile = null; | 394 this._tempFile = null; |
| 395 } |
| 396 |
| 397 /** |
| 398 * @override |
| 399 */ |
| 400 onTransferStarted() { |
| 401 this._jsonifiedProfile = ''; |
| 402 this.updateStatus( |
| 403 WebInspector.UIString('Loading\u2026 %s', Number.bytesToString(this._jso
nifiedProfile.length)), true); |
| 404 } |
| 405 |
| 406 /** |
| 407 * @override |
| 408 * @param {!WebInspector.ChunkedReader} reader |
| 409 */ |
| 410 onChunkTransferred(reader) { |
| 411 this.updateStatus(WebInspector.UIString('Loading\u2026 %d%%', Number.bytesTo
String(this._jsonifiedProfile.length))); |
| 412 } |
| 413 |
| 414 /** |
| 415 * @override |
| 416 */ |
| 417 onTransferFinished() { |
| 418 this.updateStatus(WebInspector.UIString('Parsing\u2026'), true); |
| 419 this._profile = JSON.parse(this._jsonifiedProfile); |
| 420 this._jsonifiedProfile = null; |
| 421 this.updateStatus(WebInspector.UIString('Loaded'), false); |
| 422 |
| 423 if (this._profileType.profileBeingRecorded() === this) |
| 424 this._profileType.setProfileBeingRecorded(null); |
| 425 } |
| 426 |
| 427 /** |
| 428 * @override |
| 429 * @param {!WebInspector.ChunkedReader} reader |
| 430 * @param {!Event} e |
| 431 */ |
| 432 onError(reader, e) { |
| 433 var subtitle; |
| 434 switch (e.target.error.code) { |
| 435 case e.target.error.NOT_FOUND_ERR: |
| 436 subtitle = WebInspector.UIString('\'%s\' not found.', reader.fileName())
; |
| 437 break; |
| 438 case e.target.error.NOT_READABLE_ERR: |
| 439 subtitle = WebInspector.UIString('\'%s\' is not readable', reader.fileNa
me()); |
| 440 break; |
| 441 case e.target.error.ABORT_ERR: |
| 442 return; |
| 443 default: |
| 444 subtitle = WebInspector.UIString('\'%s\' error %d', reader.fileName(), e
.target.error.code); |
| 445 } |
| 446 this.updateStatus(subtitle); |
| 447 } |
| 448 |
| 449 /** |
| 450 * @override |
| 451 * @param {string} text |
| 452 */ |
| 453 write(text) { |
| 454 this._jsonifiedProfile += text; |
| 455 } |
| 456 |
| 457 /** |
| 458 * @override |
| 459 */ |
| 460 close() { |
| 461 } |
| 462 |
| 463 /** |
| 464 * @override |
| 465 */ |
| 466 dispose() { |
| 467 this.removeTempFile(); |
| 468 } |
| 469 |
| 470 /** |
| 471 * @override |
| 472 * @param {!WebInspector.ProfileType.DataDisplayDelegate} panel |
| 473 * @return {!WebInspector.ProfileSidebarTreeElement} |
| 474 */ |
| 475 createSidebarTreeElement(panel) { |
| 476 return new WebInspector.ProfileSidebarTreeElement(panel, this, 'profile-side
bar-tree-item'); |
| 477 } |
| 478 |
| 479 /** |
| 480 * @override |
| 481 * @return {boolean} |
| 482 */ |
| 483 canSaveToFile() { |
| 484 return !this.fromFile() && this._protocolProfile; |
| 485 } |
| 486 |
| 487 /** |
| 488 * @override |
| 489 */ |
| 490 saveToFile() { |
| 491 var fileOutputStream = new WebInspector.FileOutputStream(); |
| 492 |
| 493 /** |
| 494 * @param {boolean} accepted |
| 495 * @this {WebInspector.WritableProfileHeader} |
| 496 */ |
| 497 function onOpenForSave(accepted) { |
| 498 if (!accepted) |
| 499 return; |
| 500 function didRead(data) { |
| 501 if (data) |
| 502 fileOutputStream.write(data, fileOutputStream.close.bind(fileOutputStr
eam)); |
| 503 else |
| 504 fileOutputStream.close(); |
| 505 } |
| 506 if (this._failedToCreateTempFile) { |
| 507 WebInspector.console.error('Failed to open temp file with heap snapshot'
); |
| 508 fileOutputStream.close(); |
| 509 } else if (this._tempFile) { |
| 510 this._tempFile.read(didRead); |
| 511 } else { |
| 512 this._onTempFileReady = onOpenForSave.bind(this, accepted); |
| 513 } |
| 514 } |
| 515 this._fileName = this._fileName || |
| 516 `${this._profileType.typeName()}-${new Date().toISO8601Compact()}${this.
_profileType.fileExtension()}`; |
| 517 fileOutputStream.open(this._fileName, onOpenForSave.bind(this)); |
| 518 } |
| 519 |
| 520 /** |
| 521 * @override |
| 522 * @param {!File} file |
| 523 */ |
| 524 loadFromFile(file) { |
| 525 this.updateStatus(WebInspector.UIString('Loading\u2026'), true); |
| 526 var fileReader = new WebInspector.ChunkedFileReader(file, 10000000, this); |
| 527 fileReader.start(this); |
| 528 } |
| 529 |
| 530 /** |
| 531 * @param {*} profile |
| 532 */ |
| 533 setProtocolProfile(profile) { |
| 534 this._protocolProfile = profile; |
| 535 this._saveProfileDataToTempFile(profile); |
| 536 if (this.canSaveToFile()) |
| 537 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileRec
eived); |
| 538 } |
| 539 |
| 540 /** |
| 541 * @param {*} data |
| 542 */ |
| 543 _saveProfileDataToTempFile(data) { |
| 544 var serializedData = JSON.stringify(data); |
| 545 |
| 546 /** |
| 547 * @this {WebInspector.WritableProfileHeader} |
| 548 */ |
| 549 function didCreateTempFile(tempFile) { |
| 550 this._writeToTempFile(tempFile, serializedData); |
| 551 } |
| 552 WebInspector.TempFile.create('cpu-profiler', String(this.uid)).then(didCreat
eTempFile.bind(this)); |
| 553 } |
| 554 |
| 555 /** |
| 556 * @param {?WebInspector.TempFile} tempFile |
| 557 * @param {string} serializedData |
| 558 */ |
| 559 _writeToTempFile(tempFile, serializedData) { |
| 560 this._tempFile = tempFile; |
| 561 if (!tempFile) { |
| 562 this._failedToCreateTempFile = true; |
| 563 this._notifyTempFileReady(); |
| 564 return; |
| 565 } |
| 566 /** |
| 567 * @param {number} fileSize |
| 568 * @this {WebInspector.WritableProfileHeader} |
| 569 */ |
| 570 function didWriteToTempFile(fileSize) { |
| 571 if (!fileSize) |
| 572 this._failedToCreateTempFile = true; |
| 573 tempFile.finishWriting(); |
| 574 this._notifyTempFileReady(); |
| 575 } |
| 576 tempFile.write([serializedData], didWriteToTempFile.bind(this)); |
| 577 } |
| 578 |
| 579 _notifyTempFileReady() { |
| 580 if (this._onTempFileReady) { |
| 581 this._onTempFileReady(); |
| 582 this._onTempFileReady = null; |
| 583 } |
| 584 } |
411 }; | 585 }; |
412 | |
413 WebInspector.WritableProfileHeader.prototype = { | |
414 /** | |
415 * @override | |
416 */ | |
417 onTransferStarted: function() | |
418 { | |
419 this._jsonifiedProfile = ""; | |
420 this.updateStatus(WebInspector.UIString("Loading\u2026 %s", Number.bytes
ToString(this._jsonifiedProfile.length)), true); | |
421 }, | |
422 | |
423 /** | |
424 * @override | |
425 * @param {!WebInspector.ChunkedReader} reader | |
426 */ | |
427 onChunkTransferred: function(reader) | |
428 { | |
429 this.updateStatus(WebInspector.UIString("Loading\u2026 %d%%", Number.byt
esToString(this._jsonifiedProfile.length))); | |
430 }, | |
431 | |
432 /** | |
433 * @override | |
434 */ | |
435 onTransferFinished: function() | |
436 { | |
437 this.updateStatus(WebInspector.UIString("Parsing\u2026"), true); | |
438 this._profile = JSON.parse(this._jsonifiedProfile); | |
439 this._jsonifiedProfile = null; | |
440 this.updateStatus(WebInspector.UIString("Loaded"), false); | |
441 | |
442 if (this._profileType.profileBeingRecorded() === this) | |
443 this._profileType.setProfileBeingRecorded(null); | |
444 }, | |
445 | |
446 /** | |
447 * @override | |
448 * @param {!WebInspector.ChunkedReader} reader | |
449 * @param {!Event} e | |
450 */ | |
451 onError: function(reader, e) | |
452 { | |
453 var subtitle; | |
454 switch (e.target.error.code) { | |
455 case e.target.error.NOT_FOUND_ERR: | |
456 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName(
)); | |
457 break; | |
458 case e.target.error.NOT_READABLE_ERR: | |
459 subtitle = WebInspector.UIString("'%s' is not readable", reader.file
Name()); | |
460 break; | |
461 case e.target.error.ABORT_ERR: | |
462 return; | |
463 default: | |
464 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(),
e.target.error.code); | |
465 } | |
466 this.updateStatus(subtitle); | |
467 }, | |
468 | |
469 /** | |
470 * @override | |
471 * @param {string} text | |
472 */ | |
473 write: function(text) | |
474 { | |
475 this._jsonifiedProfile += text; | |
476 }, | |
477 | |
478 /** | |
479 * @override | |
480 */ | |
481 close: function() { }, | |
482 | |
483 /** | |
484 * @override | |
485 */ | |
486 dispose: function() | |
487 { | |
488 this.removeTempFile(); | |
489 }, | |
490 | |
491 /** | |
492 * @override | |
493 * @param {!WebInspector.ProfileType.DataDisplayDelegate} panel | |
494 * @return {!WebInspector.ProfileSidebarTreeElement} | |
495 */ | |
496 createSidebarTreeElement: function(panel) | |
497 { | |
498 return new WebInspector.ProfileSidebarTreeElement(panel, this, "profile-
sidebar-tree-item"); | |
499 }, | |
500 | |
501 /** | |
502 * @override | |
503 * @return {boolean} | |
504 */ | |
505 canSaveToFile: function() | |
506 { | |
507 return !this.fromFile() && this._protocolProfile; | |
508 }, | |
509 | |
510 saveToFile: function() | |
511 { | |
512 var fileOutputStream = new WebInspector.FileOutputStream(); | |
513 | |
514 /** | |
515 * @param {boolean} accepted | |
516 * @this {WebInspector.WritableProfileHeader} | |
517 */ | |
518 function onOpenForSave(accepted) | |
519 { | |
520 if (!accepted) | |
521 return; | |
522 function didRead(data) | |
523 { | |
524 if (data) | |
525 fileOutputStream.write(data, fileOutputStream.close.bind(fil
eOutputStream)); | |
526 else | |
527 fileOutputStream.close(); | |
528 } | |
529 if (this._failedToCreateTempFile) { | |
530 WebInspector.console.error("Failed to open temp file with heap s
napshot"); | |
531 fileOutputStream.close(); | |
532 } else if (this._tempFile) { | |
533 this._tempFile.read(didRead); | |
534 } else { | |
535 this._onTempFileReady = onOpenForSave.bind(this, accepted); | |
536 } | |
537 } | |
538 this._fileName = this._fileName || `${this._profileType.typeName()}-${ne
w Date().toISO8601Compact()}${this._profileType.fileExtension()}`; | |
539 fileOutputStream.open(this._fileName, onOpenForSave.bind(this)); | |
540 }, | |
541 | |
542 /** | |
543 * @override | |
544 * @param {!File} file | |
545 */ | |
546 loadFromFile: function(file) | |
547 { | |
548 this.updateStatus(WebInspector.UIString("Loading\u2026"), true); | |
549 var fileReader = new WebInspector.ChunkedFileReader(file, 10000000, this
); | |
550 fileReader.start(this); | |
551 }, | |
552 | |
553 /** | |
554 * @param {*} profile | |
555 */ | |
556 setProtocolProfile: function(profile) | |
557 { | |
558 this._protocolProfile = profile; | |
559 this._saveProfileDataToTempFile(profile); | |
560 if (this.canSaveToFile()) | |
561 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.Prof
ileReceived); | |
562 }, | |
563 | |
564 /** | |
565 * @param {*} data | |
566 */ | |
567 _saveProfileDataToTempFile: function(data) | |
568 { | |
569 var serializedData = JSON.stringify(data); | |
570 | |
571 /** | |
572 * @this {WebInspector.WritableProfileHeader} | |
573 */ | |
574 function didCreateTempFile(tempFile) | |
575 { | |
576 this._writeToTempFile(tempFile, serializedData); | |
577 } | |
578 WebInspector.TempFile.create("cpu-profiler", String(this.uid)) | |
579 .then(didCreateTempFile.bind(this)); | |
580 }, | |
581 | |
582 /** | |
583 * @param {?WebInspector.TempFile} tempFile | |
584 * @param {string} serializedData | |
585 */ | |
586 _writeToTempFile: function(tempFile, serializedData) | |
587 { | |
588 this._tempFile = tempFile; | |
589 if (!tempFile) { | |
590 this._failedToCreateTempFile = true; | |
591 this._notifyTempFileReady(); | |
592 return; | |
593 } | |
594 /** | |
595 * @param {number} fileSize | |
596 * @this {WebInspector.WritableProfileHeader} | |
597 */ | |
598 function didWriteToTempFile(fileSize) | |
599 { | |
600 if (!fileSize) | |
601 this._failedToCreateTempFile = true; | |
602 tempFile.finishWriting(); | |
603 this._notifyTempFileReady(); | |
604 } | |
605 tempFile.write([serializedData], didWriteToTempFile.bind(this)); | |
606 }, | |
607 | |
608 _notifyTempFileReady: function() | |
609 { | |
610 if (this._onTempFileReady) { | |
611 this._onTempFileReady(); | |
612 this._onTempFileReady = null; | |
613 } | |
614 }, | |
615 | |
616 __proto__: WebInspector.ProfileHeader.prototype | |
617 }; | |
OLD | NEW |