1/* 2 * Copyright (C) 2012 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @extends {WebInspector.DataGrid} 34 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 35 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns 36 */ 37WebInspector.HeapSnapshotSortableDataGrid = function(dataDisplayDelegate, columns) 38{ 39 WebInspector.DataGrid.call(this, columns); 40 this._dataDisplayDelegate = dataDisplayDelegate; 41 42 /** 43 * @type {number} 44 */ 45 this._recursiveSortingDepth = 0; 46 /** 47 * @type {?WebInspector.HeapSnapshotGridNode} 48 */ 49 this._highlightedNode = null; 50 /** 51 * @type {boolean} 52 */ 53 this._populatedAndSorted = false; 54 /** 55 * @type {?WebInspector.StatusBarInput} 56 */ 57 this._nameFilter = null; 58 this.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this); 59 this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this); 60} 61 62WebInspector.HeapSnapshotSortableDataGrid.Events = { 63 ContentShown: "ContentShown", 64 SortingComplete: "SortingComplete" 65} 66 67WebInspector.HeapSnapshotSortableDataGrid.prototype = { 68 /** 69 * @param {!WebInspector.StatusBarInput} nameFilter 70 */ 71 setNameFilter: function(nameFilter) 72 { 73 this._nameFilter = nameFilter; 74 }, 75 76 /** 77 * @return {number} 78 */ 79 defaultPopulateCount: function() 80 { 81 return 100; 82 }, 83 84 _disposeAllNodes: function() 85 { 86 var children = this.topLevelNodes(); 87 for (var i = 0, l = children.length; i < l; ++i) 88 children[i].dispose(); 89 }, 90 91 /** 92 * @override 93 */ 94 wasShown: function() 95 { 96 if (this._nameFilter) { 97 this._nameFilter.addEventListener(WebInspector.StatusBarInput.Event.TextChanged, this._onNameFilterChanged, this); 98 this.updateVisibleNodes(true); 99 } 100 if (this._populatedAndSorted) 101 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); 102 }, 103 104 _sortingComplete: function() 105 { 106 this.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this); 107 this._populatedAndSorted = true; 108 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); 109 }, 110 111 /** 112 * @override 113 */ 114 willHide: function() 115 { 116 if (this._nameFilter) 117 this._nameFilter.removeEventListener(WebInspector.StatusBarInput.Event.TextChanged, this._onNameFilterChanged, this); 118 this._clearCurrentHighlight(); 119 }, 120 121 /** 122 * @param {!WebInspector.ContextMenu} contextMenu 123 * @param {?Event} event 124 */ 125 populateContextMenu: function(contextMenu, event) 126 { 127 var td = event.target.enclosingNodeOrSelfWithNodeName("td"); 128 if (!td) 129 return; 130 var node = td.heapSnapshotNode; 131 132 /** 133 * @this {WebInspector.HeapSnapshotSortableDataGrid} 134 */ 135 function revealInDominatorsView() 136 { 137 this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Dominators"); 138 } 139 140 /** 141 * @this {WebInspector.HeapSnapshotSortableDataGrid} 142 */ 143 function revealInSummaryView() 144 { 145 this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Summary"); 146 } 147 148 if (node instanceof WebInspector.HeapSnapshotRetainingObjectNode) { 149 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this)); 150 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this)); 151 } else if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) { 152 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this)); 153 } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) { 154 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this)); 155 } 156 }, 157 158 resetSortingCache: function() 159 { 160 delete this._lastSortColumnIdentifier; 161 delete this._lastSortAscending; 162 }, 163 164 /** 165 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 166 */ 167 topLevelNodes: function() 168 { 169 return this.rootNode().children; 170 }, 171 172 /** 173 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId 174 * @param {function(boolean)} callback 175 */ 176 highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId, callback) 177 { 178 }, 179 180 /** 181 * @param {!WebInspector.HeapSnapshotGridNode} node 182 */ 183 highlightNode: function(node) 184 { 185 var prevNode = this._highlightedNode; 186 this._clearCurrentHighlight(); 187 this._highlightedNode = node; 188 WebInspector.runCSSAnimationOnce(this._highlightedNode.element, "highlighted-row"); 189 }, 190 191 nodeWasDetached: function(node) 192 { 193 if (this._highlightedNode === node) 194 this._clearCurrentHighlight(); 195 }, 196 197 _clearCurrentHighlight: function() 198 { 199 if (!this._highlightedNode) 200 return 201 this._highlightedNode.element.classList.remove("highlighted-row"); 202 this._highlightedNode = null; 203 }, 204 205 resetNameFilter: function() 206 { 207 this._nameFilter.setValue(""); 208 this._onNameFilterChanged(); 209 }, 210 211 _onNameFilterChanged: function() 212 { 213 this.updateVisibleNodes(true); 214 }, 215 216 sortingChanged: function() 217 { 218 var sortAscending = this.isSortOrderAscending(); 219 var sortColumnIdentifier = this.sortColumnIdentifier(); 220 if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending) 221 return; 222 this._lastSortColumnIdentifier = sortColumnIdentifier; 223 this._lastSortAscending = sortAscending; 224 var sortFields = this._sortFields(sortColumnIdentifier, sortAscending); 225 226 function SortByTwoFields(nodeA, nodeB) 227 { 228 var field1 = nodeA[sortFields[0]]; 229 var field2 = nodeB[sortFields[0]]; 230 var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); 231 if (!sortFields[1]) 232 result = -result; 233 if (result !== 0) 234 return result; 235 field1 = nodeA[sortFields[2]]; 236 field2 = nodeB[sortFields[2]]; 237 result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); 238 if (!sortFields[3]) 239 result = -result; 240 return result; 241 } 242 this._performSorting(SortByTwoFields); 243 }, 244 245 _performSorting: function(sortFunction) 246 { 247 this.recursiveSortingEnter(); 248 var children = this.allChildren(this.rootNode()); 249 this.rootNode().removeChildren(); 250 children.sort(sortFunction); 251 for (var i = 0, l = children.length; i < l; ++i) { 252 var child = children[i]; 253 this.appendChildAfterSorting(child); 254 if (child.expanded) 255 child.sort(); 256 } 257 this.recursiveSortingLeave(); 258 }, 259 260 appendChildAfterSorting: function(child) 261 { 262 var revealed = child.revealed; 263 this.rootNode().appendChild(child); 264 child.revealed = revealed; 265 }, 266 267 recursiveSortingEnter: function() 268 { 269 ++this._recursiveSortingDepth; 270 }, 271 272 recursiveSortingLeave: function() 273 { 274 if (!this._recursiveSortingDepth) 275 return; 276 if (--this._recursiveSortingDepth) 277 return; 278 this.updateVisibleNodes(true); 279 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete); 280 }, 281 282 /** 283 * @param {boolean} force 284 */ 285 updateVisibleNodes: function(force) 286 { 287 }, 288 289 /** 290 * @param {!WebInspector.DataGridNode} parent 291 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 292 */ 293 allChildren: function(parent) 294 { 295 return parent.children; 296 }, 297 298 /** 299 * @param {!WebInspector.DataGridNode} parent 300 * @param {!WebInspector.DataGridNode} node 301 * @param {number} index 302 */ 303 insertChild: function(parent, node, index) 304 { 305 parent.insertChild(node, index); 306 }, 307 308 /** 309 * @param {!WebInspector.HeapSnapshotGridNode} parent 310 * @param {number} index 311 */ 312 removeChildByIndex: function(parent, index) 313 { 314 parent.removeChild(parent.children[index]); 315 }, 316 317 /** 318 * @param {!WebInspector.HeapSnapshotGridNode} parent 319 */ 320 removeAllChildren: function(parent) 321 { 322 parent.removeChildren(); 323 }, 324 325 __proto__: WebInspector.DataGrid.prototype 326} 327 328 329/** 330 * @constructor 331 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 332 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 333 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns 334 */ 335WebInspector.HeapSnapshotViewportDataGrid = function(dataDisplayDelegate, columns) 336{ 337 WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); 338 this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true); 339 /** 340 * @type {?WebInspector.HeapSnapshotGridNode} 341 */ 342 this._nodeToHighlightAfterScroll = null; 343 this._topPadding = new WebInspector.HeapSnapshotPaddingNode(); 344 this._topPaddingHeight = 0; 345 this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild); 346 this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode(); 347 this._bottomPaddingHeight = 0; 348 this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild); 349} 350 351WebInspector.HeapSnapshotViewportDataGrid.prototype = { 352 /** 353 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 354 */ 355 topLevelNodes: function() 356 { 357 return this.allChildren(this.rootNode()); 358 }, 359 360 appendChildAfterSorting: function(child) 361 { 362 // Do nothing here, it will be added in updateVisibleNodes. 363 }, 364 365 /** 366 * @override 367 * @param {boolean} force 368 * @param {!Array.<!WebInspector.HeapSnapshotGridNode>=} pathToReveal 369 */ 370 updateVisibleNodes: function(force, pathToReveal) 371 { 372 // Guard zone is used to ensure there are always some extra items 373 // above and below the viewport to support keyboard navigation. 374 var guardZoneHeight = 40; 375 var scrollHeight = this.scrollContainer.scrollHeight; 376 var scrollTop = this.scrollContainer.scrollTop; 377 var scrollBottom = scrollHeight - scrollTop - this.scrollContainer.offsetHeight; 378 scrollTop = Math.max(0, scrollTop - guardZoneHeight); 379 scrollBottom = Math.max(0, scrollBottom - guardZoneHeight); 380 var viewPortHeight = scrollHeight - scrollTop - scrollBottom; 381 if (!pathToReveal) { 382 // Do nothing if populated nodes still fit the viewport. 383 if (!force && scrollTop >= this._topPaddingHeight && scrollBottom >= this._bottomPaddingHeight) 384 return; 385 var hysteresisHeight = 500; 386 scrollTop -= hysteresisHeight; 387 viewPortHeight += 2 * hysteresisHeight; 388 } 389 var selectedNode = this.selectedNode; 390 this.rootNode().removeChildren(); 391 392 this._topPaddingHeight = 0; 393 this._bottomPaddingHeight = 0; 394 395 this._addVisibleNodes(this.rootNode(), scrollTop, scrollTop + viewPortHeight, pathToReveal || null); 396 397 this._topPadding.setHeight(this._topPaddingHeight); 398 this._bottomPadding.setHeight(this._bottomPaddingHeight); 399 400 if (selectedNode) { 401 // Keep selection even if the node is not in the current viewport. 402 if (selectedNode.parent) 403 selectedNode.select(true); 404 else 405 this.selectedNode = selectedNode; 406 } 407 }, 408 409 /** 410 * @param {!WebInspector.DataGridNode} parentNode 411 * @param {number} topBound 412 * @param {number} bottomBound 413 * @param {?Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal 414 * @return {number} 415 */ 416 _addVisibleNodes: function(parentNode, topBound, bottomBound, pathToReveal) 417 { 418 if (!parentNode.expanded) 419 return 0; 420 421 var nodeToReveal = pathToReveal ? pathToReveal[0] : null; 422 var restPathToReveal = pathToReveal && pathToReveal.length > 1 ? pathToReveal.slice(1) : null; 423 var children = this.allChildren(parentNode); 424 var topPadding = 0; 425 var nameFilterValue = this._nameFilter ? this._nameFilter.value().toLowerCase() : ""; 426 // Iterate over invisible nodes beyond the upper bound of viewport. 427 // Do not insert them into the grid, but count their total height. 428 for (var i = 0; i < children.length; ++i) { 429 var child = children[i]; 430 if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) 431 continue; 432 var newTop = topPadding + this._nodeHeight(child); 433 if (nodeToReveal === child || (!nodeToReveal && newTop > topBound)) 434 break; 435 topPadding = newTop; 436 } 437 438 // Put visible nodes into the data grid. 439 var position = topPadding; 440 for (; i < children.length && (nodeToReveal || position < bottomBound); ++i) { 441 var child = children[i]; 442 if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) 443 continue; 444 var hasChildren = child.hasChildren; 445 child.removeChildren(); 446 child.hasChildren = hasChildren; 447 child.revealed = true; 448 parentNode.appendChild(child); 449 position += child.nodeSelfHeight(); 450 position += this._addVisibleNodes(child, topBound - position, bottomBound - position, restPathToReveal); 451 if (nodeToReveal === child) 452 break; 453 } 454 455 // Count the invisible nodes beyond the bottom bound of the viewport. 456 var bottomPadding = 0; 457 for (; i < children.length; ++i) { 458 var child = children[i]; 459 if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) 460 continue; 461 bottomPadding += this._nodeHeight(child); 462 } 463 464 this._topPaddingHeight += topPadding; 465 this._bottomPaddingHeight += bottomPadding; 466 return position + bottomPadding; 467 }, 468 469 /** 470 * @param {!WebInspector.HeapSnapshotGridNode} node 471 * @return {number} 472 */ 473 _nodeHeight: function(node) 474 { 475 if (!node.revealed) 476 return 0; 477 var result = node.nodeSelfHeight(); 478 if (!node.expanded) 479 return result; 480 var children = this.allChildren(node); 481 for (var i = 0; i < children.length; i++) 482 result += this._nodeHeight(children[i]); 483 return result; 484 }, 485 486 /** 487 * @override 488 * @return {?Element} 489 */ 490 defaultAttachLocation: function() 491 { 492 return this._bottomPadding.element; 493 }, 494 495 /** 496 * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal 497 */ 498 revealTreeNode: function(pathToReveal) 499 { 500 this.updateVisibleNodes(true, pathToReveal); 501 }, 502 503 /** 504 * @param {!WebInspector.DataGridNode} parent 505 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 506 */ 507 allChildren: function(parent) 508 { 509 return parent._allChildren || (parent._allChildren = []); 510 }, 511 512 /** 513 * @param {!WebInspector.DataGridNode} parent 514 * @param {!WebInspector.DataGridNode} node 515 */ 516 appendNode: function(parent, node) 517 { 518 this.allChildren(parent).push(node); 519 }, 520 521 /** 522 * @param {!WebInspector.DataGridNode} parent 523 * @param {!WebInspector.DataGridNode} node 524 * @param {number} index 525 */ 526 insertChild: function(parent, node, index) 527 { 528 this.allChildren(parent).splice(index, 0, node); 529 }, 530 531 removeChildByIndex: function(parent, index) 532 { 533 this.allChildren(parent).splice(index, 1); 534 }, 535 536 removeAllChildren: function(parent) 537 { 538 parent._allChildren = []; 539 }, 540 541 removeTopLevelNodes: function() 542 { 543 this._disposeAllNodes(); 544 this.rootNode().removeChildren(); 545 this.rootNode()._allChildren = []; 546 }, 547 548 /** 549 * @override 550 * @param {!WebInspector.HeapSnapshotGridNode} node 551 */ 552 highlightNode: function(node) 553 { 554 if (this._isScrolledIntoView(node.element)) { 555 this.updateVisibleNodes(true); 556 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node); 557 } else { 558 node.element.scrollIntoViewIfNeeded(true); 559 this._nodeToHighlightAfterScroll = node; 560 } 561 }, 562 563 /** 564 * @param {!Element} element 565 * @return {boolean} 566 */ 567 _isScrolledIntoView: function(element) 568 { 569 var viewportTop = this.scrollContainer.scrollTop; 570 var viewportBottom = viewportTop + this.scrollContainer.clientHeight; 571 var elemTop = element.offsetTop 572 var elemBottom = elemTop + element.offsetHeight; 573 return elemBottom <= viewportBottom && elemTop >= viewportTop; 574 }, 575 576 onResize: function() 577 { 578 WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this); 579 this.updateVisibleNodes(false); 580 }, 581 582 _onScroll: function(event) 583 { 584 this.updateVisibleNodes(false); 585 586 if (this._nodeToHighlightAfterScroll) { 587 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll); 588 this._nodeToHighlightAfterScroll = null; 589 } 590 }, 591 592 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 593} 594 595/** 596 * @constructor 597 */ 598WebInspector.HeapSnapshotPaddingNode = function() 599{ 600 this.element = document.createElement("tr"); 601 this.element.classList.add("revealed"); 602 this.setHeight(0); 603} 604 605WebInspector.HeapSnapshotPaddingNode.prototype = { 606 setHeight: function(height) 607 { 608 this.element.style.height = height + "px"; 609 }, 610 removeFromTable: function() 611 { 612 var parent = this.element.parentNode; 613 if (parent) 614 parent.removeChild(this.element); 615 } 616} 617 618 619/** 620 * @constructor 621 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 622 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 623 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns 624 */ 625WebInspector.HeapSnapshotContainmentDataGrid = function(dataDisplayDelegate, columns) 626{ 627 columns = columns || [ 628 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 629 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true}, 630 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 631 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending} 632 ]; 633 WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); 634} 635 636WebInspector.HeapSnapshotContainmentDataGrid.prototype = { 637 /** 638 * @param {!WebInspector.HeapSnapshotProxy} snapshot 639 * @param {number} nodeIndex 640 */ 641 setDataSource: function(snapshot, nodeIndex) 642 { 643 this.snapshot = snapshot; 644 var node = { nodeIndex: nodeIndex || snapshot.rootNodeIndex }; 645 var fakeEdge = { node: node }; 646 this.setRootNode(this._createRootNode(snapshot, fakeEdge)); 647 this.rootNode().sort(); 648 }, 649 650 _createRootNode: function(snapshot, fakeEdge) 651 { 652 return new WebInspector.HeapSnapshotObjectNode(this, snapshot, fakeEdge, null); 653 }, 654 655 sortingChanged: function() 656 { 657 var rootNode = this.rootNode(); 658 if (rootNode.hasChildren) 659 rootNode.sort(); 660 }, 661 662 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 663} 664 665 666/** 667 * @constructor 668 * @extends {WebInspector.HeapSnapshotContainmentDataGrid} 669 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 670 */ 671WebInspector.HeapSnapshotRetainmentDataGrid = function(dataDisplayDelegate) 672{ 673 var columns = [ 674 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 675 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending}, 676 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 677 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true} 678 ]; 679 WebInspector.HeapSnapshotContainmentDataGrid.call(this, dataDisplayDelegate, columns); 680} 681 682WebInspector.HeapSnapshotRetainmentDataGrid.Events = { 683 ExpandRetainersComplete: "ExpandRetainersComplete" 684} 685 686WebInspector.HeapSnapshotRetainmentDataGrid.prototype = { 687 _createRootNode: function(snapshot, fakeEdge) 688 { 689 return new WebInspector.HeapSnapshotRetainingObjectNode(this, snapshot, fakeEdge, null); 690 }, 691 692 _sortFields: function(sortColumn, sortAscending) 693 { 694 return { 695 object: ["_name", sortAscending, "_count", false], 696 count: ["_count", sortAscending, "_name", true], 697 shallowSize: ["_shallowSize", sortAscending, "_name", true], 698 retainedSize: ["_retainedSize", sortAscending, "_name", true], 699 distance: ["_distance", sortAscending, "_name", true] 700 }[sortColumn]; 701 }, 702 703 reset: function() 704 { 705 this.rootNode().removeChildren(); 706 this.resetSortingCache(); 707 }, 708 709 /** 710 * @param {!WebInspector.HeapSnapshotProxy} snapshot 711 * @param {number} nodeIndex 712 */ 713 setDataSource: function(snapshot, nodeIndex) 714 { 715 WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex); 716 this.rootNode().expand(); 717 }, 718 719 __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype 720} 721 722/** 723 * @constructor 724 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 725 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 726 */ 727WebInspector.HeapSnapshotConstructorsDataGrid = function(dataDisplayDelegate) 728{ 729 var columns = [ 730 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 731 {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true}, 732 {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true}, 733 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 734 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 735 ]; 736 WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); 737 this._profileIndex = -1; 738 739 this._objectIdToSelect = null; 740} 741 742WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { 743 _sortFields: function(sortColumn, sortAscending) 744 { 745 return { 746 object: ["_name", sortAscending, "_count", false], 747 distance: ["_distance", sortAscending, "_retainedSize", true], 748 count: ["_count", sortAscending, "_name", true], 749 shallowSize: ["_shallowSize", sortAscending, "_name", true], 750 retainedSize: ["_retainedSize", sortAscending, "_name", true] 751 }[sortColumn]; 752 }, 753 754 /** 755 * @override 756 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id 757 * @param {function(boolean)} callback 758 */ 759 highlightObjectByHeapSnapshotId: function(id, callback) 760 { 761 if (!this.snapshot) { 762 this._objectIdToSelect = id; 763 return; 764 } 765 766 /** 767 * @param {?string} className 768 * @this {WebInspector.HeapSnapshotConstructorsDataGrid} 769 */ 770 function didGetClassName(className) 771 { 772 if (!className) { 773 callback(false); 774 return; 775 } 776 var constructorNodes = this.topLevelNodes(); 777 for (var i = 0; i < constructorNodes.length; i++) { 778 var parent = constructorNodes[i]; 779 if (parent._name === className) { 780 parent.revealNodeBySnapshotObjectId(parseInt(id, 10), callback); 781 return; 782 } 783 } 784 } 785 this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this)); 786 }, 787 788 clear: function() 789 { 790 this._nextRequestedFilter = null; 791 this._lastFilter = null; 792 this.removeTopLevelNodes(); 793 }, 794 795 setDataSource: function(snapshot) 796 { 797 this.snapshot = snapshot; 798 if (this._profileIndex === -1) 799 this._populateChildren(); 800 801 if (this._objectIdToSelect) { 802 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {}); 803 this._objectIdToSelect = null; 804 } 805 }, 806 807 /** 808 * @param {number} minNodeId 809 * @param {number} maxNodeId 810 */ 811 setSelectionRange: function(minNodeId, maxNodeId) 812 { 813 this._populateChildren(new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId)); 814 }, 815 816 /** 817 * @param {number} allocationNodeId 818 */ 819 setAllocationNodeId: function(allocationNodeId) 820 { 821 var filter = new WebInspector.HeapSnapshotCommon.NodeFilter(); 822 filter.allocationNodeId = allocationNodeId; 823 this._populateChildren(filter); 824 }, 825 826 /** 827 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 828 * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} aggregates 829 */ 830 _aggregatesReceived: function(nodeFilter, aggregates) 831 { 832 this._filterInProgress = null; 833 if (this._nextRequestedFilter) { 834 this.snapshot.aggregatesWithFilter(this._nextRequestedFilter, this._aggregatesReceived.bind(this, this._nextRequestedFilter)); 835 this._filterInProgress = this._nextRequestedFilter; 836 this._nextRequestedFilter = null; 837 } 838 this.removeTopLevelNodes(); 839 this.resetSortingCache(); 840 for (var constructor in aggregates) 841 this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], nodeFilter)); 842 this.sortingChanged(); 843 this._lastFilter = nodeFilter; 844 }, 845 846 /** 847 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter=} nodeFilter 848 */ 849 _populateChildren: function(nodeFilter) 850 { 851 nodeFilter = nodeFilter || new WebInspector.HeapSnapshotCommon.NodeFilter(); 852 853 if (this._filterInProgress) { 854 this._nextRequestedFilter = this._filterInProgress.equals(nodeFilter) ? null : nodeFilter; 855 return; 856 } 857 if (this._lastFilter && this._lastFilter.equals(nodeFilter)) 858 return; 859 this._filterInProgress = nodeFilter; 860 this.snapshot.aggregatesWithFilter(nodeFilter, this._aggregatesReceived.bind(this, nodeFilter)); 861 }, 862 863 filterSelectIndexChanged: function(profiles, profileIndex) 864 { 865 this._profileIndex = profileIndex; 866 867 var nodeFilter; 868 if (profileIndex !== -1) { 869 var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0; 870 var maxNodeId = profiles[profileIndex].maxJSObjectId; 871 nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId) 872 } 873 874 this._populateChildren(nodeFilter); 875 }, 876 877 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 878} 879 880 881/** 882 * @constructor 883 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 884 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 885 */ 886WebInspector.HeapSnapshotDiffDataGrid = function(dataDisplayDelegate) 887{ 888 var columns = [ 889 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 890 {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true}, 891 {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true}, 892 {id: "countDelta", title: WebInspector.UIString("# Delta"), width: "64px", sortable: true}, 893 {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, 894 {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true}, 895 {id: "sizeDelta", title: WebInspector.UIString("Size Delta"), width: "72px", sortable: true} 896 ]; 897 WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); 898} 899 900WebInspector.HeapSnapshotDiffDataGrid.prototype = { 901 /** 902 * @override 903 * @return {number} 904 */ 905 defaultPopulateCount: function() 906 { 907 return 50; 908 }, 909 910 _sortFields: function(sortColumn, sortAscending) 911 { 912 return { 913 object: ["_name", sortAscending, "_count", false], 914 addedCount: ["_addedCount", sortAscending, "_name", true], 915 removedCount: ["_removedCount", sortAscending, "_name", true], 916 countDelta: ["_countDelta", sortAscending, "_name", true], 917 addedSize: ["_addedSize", sortAscending, "_name", true], 918 removedSize: ["_removedSize", sortAscending, "_name", true], 919 sizeDelta: ["_sizeDelta", sortAscending, "_name", true] 920 }[sortColumn]; 921 }, 922 923 setDataSource: function(snapshot) 924 { 925 this.snapshot = snapshot; 926 }, 927 928 /** 929 * @param {!WebInspector.HeapSnapshotProxy} baseSnapshot 930 */ 931 setBaseDataSource: function(baseSnapshot) 932 { 933 this.baseSnapshot = baseSnapshot; 934 this.removeTopLevelNodes(); 935 this.resetSortingCache(); 936 if (this.baseSnapshot === this.snapshot) { 937 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete); 938 return; 939 } 940 this._populateChildren(); 941 }, 942 943 _populateChildren: function() 944 { 945 /** 946 * @this {WebInspector.HeapSnapshotDiffDataGrid} 947 */ 948 function aggregatesForDiffReceived(aggregatesForDiff) 949 { 950 this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this)); 951 952 /** 953 * @this {WebInspector.HeapSnapshotDiffDataGrid} 954 */ 955 function didCalculateSnapshotDiff(diffByClassName) 956 { 957 for (var className in diffByClassName) { 958 var diff = diffByClassName[className]; 959 this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotDiffNode(this, className, diff)); 960 } 961 this.sortingChanged(); 962 } 963 } 964 // Two snapshots live in different workers isolated from each other. That is why 965 // we first need to collect information about the nodes in the first snapshot and 966 // then pass it to the second snapshot to calclulate the diff. 967 this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this)); 968 }, 969 970 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 971} 972 973 974/** 975 * @constructor 976 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 977 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 978 */ 979WebInspector.HeapSnapshotDominatorsDataGrid = function(dataDisplayDelegate) 980{ 981 var columns = [ 982 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 983 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 984 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 985 ]; 986 WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); 987 this._objectIdToSelect = null; 988} 989 990WebInspector.HeapSnapshotDominatorsDataGrid.prototype = { 991 /** 992 * @override 993 * @return {number} 994 */ 995 defaultPopulateCount: function() 996 { 997 return 25; 998 }, 999 1000 setDataSource: function(snapshot) 1001 { 1002 this.snapshot = snapshot; 1003 1004 var fakeNode = new WebInspector.HeapSnapshotCommon.Node(-1, "", 0, this.snapshot.rootNodeIndex, 0, 0, ""); 1005 this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode)); 1006 this.rootNode().sort(); 1007 1008 if (this._objectIdToSelect) { 1009 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {}); 1010 this._objectIdToSelect = null; 1011 } 1012 }, 1013 1014 sortingChanged: function() 1015 { 1016 this.rootNode().sort(); 1017 }, 1018 1019 /** 1020 * @override 1021 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id 1022 * @param {function(boolean)} callback 1023 */ 1024 highlightObjectByHeapSnapshotId: function(id, callback) 1025 { 1026 if (!this.snapshot) { 1027 this._objectIdToSelect = id; 1028 callback(false); 1029 return; 1030 } 1031 1032 /** 1033 * @this {WebInspector.HeapSnapshotDominatorsDataGrid} 1034 */ 1035 function didGetDominators(dominatorIds) 1036 { 1037 if (!dominatorIds) { 1038 console.error("Cannot find corresponding heap snapshot node"); 1039 callback(false); 1040 return; 1041 } 1042 var dominatorNode = this.rootNode(); 1043 expandNextDominator.call(this, dominatorIds, dominatorNode); 1044 } 1045 1046 /** 1047 * @this {WebInspector.HeapSnapshotDominatorsDataGrid} 1048 */ 1049 function expandNextDominator(dominatorIds, dominatorNode) 1050 { 1051 if (!dominatorNode) { 1052 console.error("Cannot find dominator node"); 1053 callback(false); 1054 return; 1055 } 1056 if (!dominatorIds.length) { 1057 this.highlightNode(dominatorNode); 1058 dominatorNode.element.scrollIntoViewIfNeeded(true); 1059 callback(true); 1060 return; 1061 } 1062 var snapshotObjectId = dominatorIds.pop(); 1063 dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds)); 1064 } 1065 1066 this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this)); 1067 }, 1068 1069 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 1070} 1071 1072 1073/** 1074 * @constructor 1075 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 1076 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 1077 */ 1078WebInspector.AllocationDataGrid = function(dataDisplayDelegate) 1079{ 1080 var columns = [ 1081 {id: "liveCount", title: WebInspector.UIString("Live Count"), width: "72px", sortable: true}, 1082 {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true}, 1083 {id: "liveSize", title: WebInspector.UIString("Live Size"), width: "72px", sortable: true}, 1084 {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, 1085 {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true}, 1086 ]; 1087 WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); 1088 this._linkifier = new WebInspector.Linkifier(); 1089} 1090 1091WebInspector.AllocationDataGrid.prototype = { 1092 dispose: function() 1093 { 1094 this._linkifier.reset(); 1095 }, 1096 1097 setDataSource: function(snapshot) 1098 { 1099 this.snapshot = snapshot; 1100 this.snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this)); 1101 1102 /** 1103 * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} tops 1104 * @this {WebInspector.AllocationDataGrid} 1105 */ 1106 function didReceiveAllocationTracesTops(tops) 1107 { 1108 this._topNodes = tops; 1109 this._populateChildren(); 1110 } 1111 }, 1112 1113 _populateChildren: function() 1114 { 1115 this.removeTopLevelNodes(); 1116 var root = this.rootNode(); 1117 var tops = this._topNodes; 1118 for (var i = 0; i < tops.length; i++) 1119 this.appendNode(root, new WebInspector.AllocationGridNode(this, tops[i])); 1120 this.updateVisibleNodes(true); 1121 }, 1122 1123 sortingChanged: function() 1124 { 1125 this._topNodes.sort(this._createComparator()); 1126 this.rootNode().removeChildren(); 1127 this._populateChildren(); 1128 }, 1129 1130 1131 /** 1132 * @return {function(!Object, !Object):number} 1133 */ 1134 _createComparator: function() 1135 { 1136 var fieldName = this.sortColumnIdentifier(); 1137 var compareResult = (this.sortOrder() === WebInspector.DataGrid.Order.Ascending) ? +1 : -1; 1138 /** 1139 * @param {!Object} a 1140 * @param {!Object} b 1141 * @return {number} 1142 */ 1143 function compare(a, b) 1144 { 1145 if (a[fieldName] > b[fieldName]) 1146 return compareResult; 1147 if (a[fieldName] < b[fieldName]) 1148 return -compareResult; 1149 return 0; 1150 } 1151 return compare; 1152 }, 1153 1154 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 1155} 1156