| 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 |