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 */ 37 WebInspector.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 62 WebInspector.HeapSnapshotSortableDataGrid.Events = { 63 ContentShown: "ContentShown", 64 SortingComplete: "SortingComplete" 65 } 66 67 WebInspector.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 revealInSummaryView() 136 { 137 this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Summary"); 138 } 139 140 if (node instanceof WebInspector.HeapSnapshotRetainingObjectNode) 141 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this)); 142 }, 143 144 resetSortingCache: function() 145 { 146 delete this._lastSortColumnIdentifier; 147 delete this._lastSortAscending; 148 }, 149 150 /** 151 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 152 */ 153 topLevelNodes: function() 154 { 155 return this.rootNode().children; 156 }, 157 158 /** 159 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId 160 * @param {function(boolean)} callback 161 */ 162 highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId, callback) 163 { 164 }, 165 166 /** 167 * @param {!WebInspector.HeapSnapshotGridNode} node 168 */ 169 highlightNode: function(node) 170 { 171 var prevNode = this._highlightedNode; 172 this._clearCurrentHighlight(); 173 this._highlightedNode = node; 174 WebInspector.runCSSAnimationOnce(this._highlightedNode.element(), "highlighted-row"); 175 }, 176 177 nodeWasDetached: function(node) 178 { 179 if (this._highlightedNode === node) 180 this._clearCurrentHighlight(); 181 }, 182 183 _clearCurrentHighlight: function() 184 { 185 if (!this._highlightedNode) 186 return 187 this._highlightedNode.element().classList.remove("highlighted-row"); 188 this._highlightedNode = null; 189 }, 190 191 resetNameFilter: function() 192 { 193 this._nameFilter.setValue(""); 194 this._onNameFilterChanged(); 195 }, 196 197 _onNameFilterChanged: function() 198 { 199 this.updateVisibleNodes(true); 200 }, 201 202 sortingChanged: function() 203 { 204 var sortAscending = this.isSortOrderAscending(); 205 var sortColumnIdentifier = this.sortColumnIdentifier(); 206 if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending) 207 return; 208 this._lastSortColumnIdentifier = sortColumnIdentifier; 209 this._lastSortAscending = sortAscending; 210 var sortFields = this._sortFields(sortColumnIdentifier, sortAscending); 211 212 function SortByTwoFields(nodeA, nodeB) 213 { 214 var field1 = nodeA[sortFields[0]]; 215 var field2 = nodeB[sortFields[0]]; 216 var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); 217 if (!sortFields[1]) 218 result = -result; 219 if (result !== 0) 220 return result; 221 field1 = nodeA[sortFields[2]]; 222 field2 = nodeB[sortFields[2]]; 223 result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); 224 if (!sortFields[3]) 225 result = -result; 226 return result; 227 } 228 this._performSorting(SortByTwoFields); 229 }, 230 231 _performSorting: function(sortFunction) 232 { 233 this.recursiveSortingEnter(); 234 var children = this.allChildren(this.rootNode()); 235 this.rootNode().removeChildren(); 236 children.sort(sortFunction); 237 for (var i = 0, l = children.length; i < l; ++i) { 238 var child = children[i]; 239 this.appendChildAfterSorting(child); 240 if (child.expanded) 241 child.sort(); 242 } 243 this.recursiveSortingLeave(); 244 }, 245 246 appendChildAfterSorting: function(child) 247 { 248 var revealed = child.revealed; 249 this.rootNode().appendChild(child); 250 child.revealed = revealed; 251 }, 252 253 recursiveSortingEnter: function() 254 { 255 ++this._recursiveSortingDepth; 256 }, 257 258 recursiveSortingLeave: function() 259 { 260 if (!this._recursiveSortingDepth) 261 return; 262 if (--this._recursiveSortingDepth) 263 return; 264 this.updateVisibleNodes(true); 265 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete); 266 }, 267 268 /** 269 * @param {boolean} force 270 */ 271 updateVisibleNodes: function(force) 272 { 273 }, 274 275 /** 276 * @param {!WebInspector.DataGridNode} parent 277 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 278 */ 279 allChildren: function(parent) 280 { 281 return parent.children; 282 }, 283 284 /** 285 * @param {!WebInspector.DataGridNode} parent 286 * @param {!WebInspector.DataGridNode} node 287 * @param {number} index 288 */ 289 insertChild: function(parent, node, index) 290 { 291 parent.insertChild(node, index); 292 }, 293 294 /** 295 * @param {!WebInspector.HeapSnapshotGridNode} parent 296 * @param {number} index 297 */ 298 removeChildByIndex: function(parent, index) 299 { 300 parent.removeChild(parent.children[index]); 301 }, 302 303 /** 304 * @param {!WebInspector.HeapSnapshotGridNode} parent 305 */ 306 removeAllChildren: function(parent) 307 { 308 parent.removeChildren(); 309 }, 310 311 __proto__: WebInspector.DataGrid.prototype 312 } 313 314 315 /** 316 * @constructor 317 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 318 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 319 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns 320 */ 321 WebInspector.HeapSnapshotViewportDataGrid = function(dataDisplayDelegate, columns) 322 { 323 WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); 324 this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true); 325 /** 326 * @type {?WebInspector.HeapSnapshotGridNode} 327 */ 328 this._nodeToHighlightAfterScroll = null; 329 this._topPaddingHeight = 0; 330 this._bottomPaddingHeight = 0; 331 } 332 333 WebInspector.HeapSnapshotViewportDataGrid.prototype = { 334 /** 335 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 336 */ 337 topLevelNodes: function() 338 { 339 return this.allChildren(this.rootNode()); 340 }, 341 342 appendChildAfterSorting: function(child) 343 { 344 // Do nothing here, it will be added in updateVisibleNodes. 345 }, 346 347 /** 348 * @override 349 * @param {boolean} force 350 * @param {!Array.<!WebInspector.HeapSnapshotGridNode>=} pathToReveal 351 */ 352 updateVisibleNodes: function(force, pathToReveal) 353 { 354 // Guard zone is used to ensure there are always some extra items 355 // above and below the viewport to support keyboard navigation. 356 var guardZoneHeight = 40; 357 var scrollHeight = this.scrollContainer.scrollHeight; 358 var scrollTop = this.scrollContainer.scrollTop; 359 var scrollBottom = scrollHeight - scrollTop - this.scrollContainer.offsetHeight; 360 scrollTop = Math.max(0, scrollTop - guardZoneHeight); 361 scrollBottom = Math.max(0, scrollBottom - guardZoneHeight); 362 var viewPortHeight = scrollHeight - scrollTop - scrollBottom; 363 if (!pathToReveal) { 364 // Do nothing if populated nodes still fit the viewport. 365 if (!force && scrollTop >= this._topPaddingHeight && scrollBottom >= this._bottomPaddingHeight) 366 return; 367 var hysteresisHeight = 500; 368 scrollTop -= hysteresisHeight; 369 viewPortHeight += 2 * hysteresisHeight; 370 } 371 var selectedNode = this.selectedNode; 372 this.rootNode().removeChildren(); 373 374 this._topPaddingHeight = 0; 375 this._bottomPaddingHeight = 0; 376 377 this._addVisibleNodes(this.rootNode(), scrollTop, scrollTop + viewPortHeight, pathToReveal || null); 378 379 this.setVerticalPadding(this._topPaddingHeight, this._bottomPaddingHeight); 380 381 if (selectedNode) { 382 // Keep selection even if the node is not in the current viewport. 383 if (selectedNode.parent) 384 selectedNode.select(true); 385 else 386 this.selectedNode = selectedNode; 387 } 388 }, 389 390 /** 391 * @param {!WebInspector.DataGridNode} parentNode 392 * @param {number} topBound 393 * @param {number} bottomBound 394 * @param {?Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal 395 * @return {number} 396 */ 397 _addVisibleNodes: function(parentNode, topBound, bottomBound, pathToReveal) 398 { 399 if (!parentNode.expanded) 400 return 0; 401 402 var nodeToReveal = pathToReveal ? pathToReveal[0] : null; 403 var restPathToReveal = pathToReveal && pathToReveal.length > 1 ? pathToReveal.slice(1) : null; 404 var children = this.allChildren(parentNode); 405 var topPadding = 0; 406 var nameFilterValue = this._nameFilter ? this._nameFilter.value().toLowerCase() : ""; 407 // Iterate over invisible nodes beyond the upper bound of viewport. 408 // Do not insert them into the grid, but count their total height. 409 for (var i = 0; i < children.length; ++i) { 410 var child = children[i]; 411 if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) 412 continue; 413 var newTop = topPadding + this._nodeHeight(child); 414 if (nodeToReveal === child || (!nodeToReveal && newTop > topBound)) 415 break; 416 topPadding = newTop; 417 } 418 419 // Put visible nodes into the data grid. 420 var position = topPadding; 421 for (; i < children.length && (nodeToReveal || position < bottomBound); ++i) { 422 var child = children[i]; 423 if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) 424 continue; 425 var hasChildren = child.hasChildren; 426 child.removeChildren(); 427 child.hasChildren = hasChildren; 428 child.revealed = true; 429 parentNode.appendChild(child); 430 position += child.nodeSelfHeight(); 431 position += this._addVisibleNodes(child, topBound - position, bottomBound - position, restPathToReveal); 432 if (nodeToReveal === child) 433 break; 434 } 435 436 // Count the invisible nodes beyond the bottom bound of the viewport. 437 var bottomPadding = 0; 438 for (; i < children.length; ++i) { 439 var child = children[i]; 440 if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) 441 continue; 442 bottomPadding += this._nodeHeight(child); 443 } 444 445 this._topPaddingHeight += topPadding; 446 this._bottomPaddingHeight += bottomPadding; 447 return position + bottomPadding; 448 }, 449 450 /** 451 * @param {!WebInspector.HeapSnapshotGridNode} node 452 * @return {number} 453 */ 454 _nodeHeight: function(node) 455 { 456 if (!node.revealed) 457 return 0; 458 var result = node.nodeSelfHeight(); 459 if (!node.expanded) 460 return result; 461 var children = this.allChildren(node); 462 for (var i = 0; i < children.length; i++) 463 result += this._nodeHeight(children[i]); 464 return result; 465 }, 466 467 /** 468 * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal 469 */ 470 revealTreeNode: function(pathToReveal) 471 { 472 this.updateVisibleNodes(true, pathToReveal); 473 }, 474 475 /** 476 * @param {!WebInspector.DataGridNode} parent 477 * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} 478 */ 479 allChildren: function(parent) 480 { 481 return parent._allChildren || (parent._allChildren = []); 482 }, 483 484 /** 485 * @param {!WebInspector.DataGridNode} parent 486 * @param {!WebInspector.DataGridNode} node 487 */ 488 appendNode: function(parent, node) 489 { 490 this.allChildren(parent).push(node); 491 }, 492 493 /** 494 * @param {!WebInspector.DataGridNode} parent 495 * @param {!WebInspector.DataGridNode} node 496 * @param {number} index 497 */ 498 insertChild: function(parent, node, index) 499 { 500 this.allChildren(parent).splice(index, 0, node); 501 }, 502 503 removeChildByIndex: function(parent, index) 504 { 505 this.allChildren(parent).splice(index, 1); 506 }, 507 508 removeAllChildren: function(parent) 509 { 510 parent._allChildren = []; 511 }, 512 513 removeTopLevelNodes: function() 514 { 515 this._disposeAllNodes(); 516 this.rootNode().removeChildren(); 517 this.rootNode()._allChildren = []; 518 }, 519 520 /** 521 * @override 522 * @param {!WebInspector.HeapSnapshotGridNode} node 523 */ 524 highlightNode: function(node) 525 { 526 if (this._isScrolledIntoView(node.element())) { 527 this.updateVisibleNodes(true); 528 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node); 529 } else { 530 node.element().scrollIntoViewIfNeeded(true); 531 this._nodeToHighlightAfterScroll = node; 532 } 533 }, 534 535 /** 536 * @param {!Element} element 537 * @return {boolean} 538 */ 539 _isScrolledIntoView: function(element) 540 { 541 var viewportTop = this.scrollContainer.scrollTop; 542 var viewportBottom = viewportTop + this.scrollContainer.clientHeight; 543 var elemTop = element.offsetTop 544 var elemBottom = elemTop + element.offsetHeight; 545 return elemBottom <= viewportBottom && elemTop >= viewportTop; 546 }, 547 548 onResize: function() 549 { 550 WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this); 551 this.updateVisibleNodes(false); 552 }, 553 554 _onScroll: function(event) 555 { 556 this.updateVisibleNodes(false); 557 558 if (this._nodeToHighlightAfterScroll) { 559 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll); 560 this._nodeToHighlightAfterScroll = null; 561 } 562 }, 563 564 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 565 } 566 567 /** 568 * @constructor 569 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 570 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 571 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns 572 */ 573 WebInspector.HeapSnapshotContainmentDataGrid = function(dataDisplayDelegate, columns) 574 { 575 columns = columns || [ 576 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 577 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true}, 578 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 579 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending} 580 ]; 581 WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); 582 } 583 584 WebInspector.HeapSnapshotContainmentDataGrid.prototype = { 585 /** 586 * @param {!WebInspector.HeapSnapshotProxy} snapshot 587 * @param {number} nodeIndex 588 */ 589 setDataSource: function(snapshot, nodeIndex) 590 { 591 this.snapshot = snapshot; 592 var node = { nodeIndex: nodeIndex || snapshot.rootNodeIndex }; 593 var fakeEdge = { node: node }; 594 this.setRootNode(this._createRootNode(snapshot, fakeEdge)); 595 this.rootNode().sort(); 596 }, 597 598 _createRootNode: function(snapshot, fakeEdge) 599 { 600 return new WebInspector.HeapSnapshotObjectNode(this, snapshot, fakeEdge, null); 601 }, 602 603 sortingChanged: function() 604 { 605 var rootNode = this.rootNode(); 606 if (rootNode.hasChildren) 607 rootNode.sort(); 608 }, 609 610 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 611 } 612 613 614 /** 615 * @constructor 616 * @extends {WebInspector.HeapSnapshotContainmentDataGrid} 617 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 618 */ 619 WebInspector.HeapSnapshotRetainmentDataGrid = function(dataDisplayDelegate) 620 { 621 var columns = [ 622 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 623 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending}, 624 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 625 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true} 626 ]; 627 WebInspector.HeapSnapshotContainmentDataGrid.call(this, dataDisplayDelegate, columns); 628 } 629 630 WebInspector.HeapSnapshotRetainmentDataGrid.Events = { 631 ExpandRetainersComplete: "ExpandRetainersComplete" 632 } 633 634 WebInspector.HeapSnapshotRetainmentDataGrid.prototype = { 635 _createRootNode: function(snapshot, fakeEdge) 636 { 637 return new WebInspector.HeapSnapshotRetainingObjectNode(this, snapshot, fakeEdge, null); 638 }, 639 640 _sortFields: function(sortColumn, sortAscending) 641 { 642 return { 643 object: ["_name", sortAscending, "_count", false], 644 count: ["_count", sortAscending, "_name", true], 645 shallowSize: ["_shallowSize", sortAscending, "_name", true], 646 retainedSize: ["_retainedSize", sortAscending, "_name", true], 647 distance: ["_distance", sortAscending, "_name", true] 648 }[sortColumn]; 649 }, 650 651 reset: function() 652 { 653 this.rootNode().removeChildren(); 654 this.resetSortingCache(); 655 }, 656 657 /** 658 * @param {!WebInspector.HeapSnapshotProxy} snapshot 659 * @param {number} nodeIndex 660 */ 661 setDataSource: function(snapshot, nodeIndex) 662 { 663 WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex); 664 this.rootNode().expand(); 665 }, 666 667 __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype 668 } 669 670 /** 671 * @constructor 672 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 673 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 674 */ 675 WebInspector.HeapSnapshotConstructorsDataGrid = function(dataDisplayDelegate) 676 { 677 var columns = [ 678 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 679 {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true}, 680 {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true}, 681 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 682 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 683 ]; 684 WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); 685 this._profileIndex = -1; 686 687 this._objectIdToSelect = null; 688 } 689 690 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { 691 _sortFields: function(sortColumn, sortAscending) 692 { 693 return { 694 object: ["_name", sortAscending, "_count", false], 695 distance: ["_distance", sortAscending, "_retainedSize", true], 696 count: ["_count", sortAscending, "_name", true], 697 shallowSize: ["_shallowSize", sortAscending, "_name", true], 698 retainedSize: ["_retainedSize", sortAscending, "_name", true] 699 }[sortColumn]; 700 }, 701 702 /** 703 * @override 704 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id 705 * @param {function(boolean)} callback 706 */ 707 highlightObjectByHeapSnapshotId: function(id, callback) 708 { 709 if (!this.snapshot) { 710 this._objectIdToSelect = id; 711 return; 712 } 713 714 /** 715 * @param {?string} className 716 * @this {WebInspector.HeapSnapshotConstructorsDataGrid} 717 */ 718 function didGetClassName(className) 719 { 720 if (!className) { 721 callback(false); 722 return; 723 } 724 var constructorNodes = this.topLevelNodes(); 725 for (var i = 0; i < constructorNodes.length; i++) { 726 var parent = constructorNodes[i]; 727 if (parent._name === className) { 728 parent.revealNodeBySnapshotObjectId(parseInt(id, 10), callback); 729 return; 730 } 731 } 732 } 733 this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this)); 734 }, 735 736 clear: function() 737 { 738 this._nextRequestedFilter = null; 739 this._lastFilter = null; 740 this.removeTopLevelNodes(); 741 }, 742 743 setDataSource: function(snapshot) 744 { 745 this.snapshot = snapshot; 746 if (this._profileIndex === -1) 747 this._populateChildren(); 748 749 if (this._objectIdToSelect) { 750 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {}); 751 this._objectIdToSelect = null; 752 } 753 }, 754 755 /** 756 * @param {number} minNodeId 757 * @param {number} maxNodeId 758 */ 759 setSelectionRange: function(minNodeId, maxNodeId) 760 { 761 this._populateChildren(new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId)); 762 }, 763 764 /** 765 * @param {number} allocationNodeId 766 */ 767 setAllocationNodeId: function(allocationNodeId) 768 { 769 var filter = new WebInspector.HeapSnapshotCommon.NodeFilter(); 770 filter.allocationNodeId = allocationNodeId; 771 this._populateChildren(filter); 772 }, 773 774 /** 775 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 776 * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} aggregates 777 */ 778 _aggregatesReceived: function(nodeFilter, aggregates) 779 { 780 this._filterInProgress = null; 781 if (this._nextRequestedFilter) { 782 this.snapshot.aggregatesWithFilter(this._nextRequestedFilter, this._aggregatesReceived.bind(this, this._nextRequestedFilter)); 783 this._filterInProgress = this._nextRequestedFilter; 784 this._nextRequestedFilter = null; 785 } 786 this.removeTopLevelNodes(); 787 this.resetSortingCache(); 788 for (var constructor in aggregates) 789 this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], nodeFilter)); 790 this.sortingChanged(); 791 this._lastFilter = nodeFilter; 792 }, 793 794 /** 795 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter=} nodeFilter 796 */ 797 _populateChildren: function(nodeFilter) 798 { 799 nodeFilter = nodeFilter || new WebInspector.HeapSnapshotCommon.NodeFilter(); 800 801 if (this._filterInProgress) { 802 this._nextRequestedFilter = this._filterInProgress.equals(nodeFilter) ? null : nodeFilter; 803 return; 804 } 805 if (this._lastFilter && this._lastFilter.equals(nodeFilter)) 806 return; 807 this._filterInProgress = nodeFilter; 808 this.snapshot.aggregatesWithFilter(nodeFilter, this._aggregatesReceived.bind(this, nodeFilter)); 809 }, 810 811 filterSelectIndexChanged: function(profiles, profileIndex) 812 { 813 this._profileIndex = profileIndex; 814 815 var nodeFilter; 816 if (profileIndex !== -1) { 817 var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0; 818 var maxNodeId = profiles[profileIndex].maxJSObjectId; 819 nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId) 820 } 821 822 this._populateChildren(nodeFilter); 823 }, 824 825 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 826 } 827 828 829 /** 830 * @constructor 831 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 832 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 833 */ 834 WebInspector.HeapSnapshotDiffDataGrid = function(dataDisplayDelegate) 835 { 836 var columns = [ 837 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 838 {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true}, 839 {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true}, 840 {id: "countDelta", title: WebInspector.UIString("# Delta"), width: "64px", sortable: true}, 841 {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, 842 {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true}, 843 {id: "sizeDelta", title: WebInspector.UIString("Size Delta"), width: "72px", sortable: true} 844 ]; 845 WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); 846 } 847 848 WebInspector.HeapSnapshotDiffDataGrid.prototype = { 849 /** 850 * @override 851 * @return {number} 852 */ 853 defaultPopulateCount: function() 854 { 855 return 50; 856 }, 857 858 _sortFields: function(sortColumn, sortAscending) 859 { 860 return { 861 object: ["_name", sortAscending, "_count", false], 862 addedCount: ["_addedCount", sortAscending, "_name", true], 863 removedCount: ["_removedCount", sortAscending, "_name", true], 864 countDelta: ["_countDelta", sortAscending, "_name", true], 865 addedSize: ["_addedSize", sortAscending, "_name", true], 866 removedSize: ["_removedSize", sortAscending, "_name", true], 867 sizeDelta: ["_sizeDelta", sortAscending, "_name", true] 868 }[sortColumn]; 869 }, 870 871 setDataSource: function(snapshot) 872 { 873 this.snapshot = snapshot; 874 }, 875 876 /** 877 * @param {!WebInspector.HeapSnapshotProxy} baseSnapshot 878 */ 879 setBaseDataSource: function(baseSnapshot) 880 { 881 this.baseSnapshot = baseSnapshot; 882 this.removeTopLevelNodes(); 883 this.resetSortingCache(); 884 if (this.baseSnapshot === this.snapshot) { 885 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete); 886 return; 887 } 888 this._populateChildren(); 889 }, 890 891 _populateChildren: function() 892 { 893 /** 894 * @this {WebInspector.HeapSnapshotDiffDataGrid} 895 */ 896 function aggregatesForDiffReceived(aggregatesForDiff) 897 { 898 this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this)); 899 900 /** 901 * @this {WebInspector.HeapSnapshotDiffDataGrid} 902 */ 903 function didCalculateSnapshotDiff(diffByClassName) 904 { 905 for (var className in diffByClassName) { 906 var diff = diffByClassName[className]; 907 this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotDiffNode(this, className, diff)); 908 } 909 this.sortingChanged(); 910 } 911 } 912 // Two snapshots live in different workers isolated from each other. That is why 913 // we first need to collect information about the nodes in the first snapshot and 914 // then pass it to the second snapshot to calclulate the diff. 915 this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this)); 916 }, 917 918 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 919 } 920 921 922 /** 923 * @constructor 924 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 925 * @param {?WebInspector.Target} target 926 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 927 */ 928 WebInspector.AllocationDataGrid = function(target, dataDisplayDelegate) 929 { 930 var columns = [ 931 {id: "liveCount", title: WebInspector.UIString("Live Count"), width: "72px", sortable: true}, 932 {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true}, 933 {id: "liveSize", title: WebInspector.UIString("Live Size"), width: "72px", sortable: true}, 934 {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, 935 {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true}, 936 ]; 937 WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); 938 this._target = target; 939 this._linkifier = new WebInspector.Linkifier(); 940 } 941 942 WebInspector.AllocationDataGrid.prototype = { 943 944 /** 945 * @return {?WebInspector.Target} 946 */ 947 target: function() 948 { 949 return this._target; 950 }, 951 952 dispose: function() 953 { 954 this._linkifier.reset(); 955 }, 956 957 setDataSource: function(snapshot) 958 { 959 this.snapshot = snapshot; 960 this.snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this)); 961 962 /** 963 * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} tops 964 * @this {WebInspector.AllocationDataGrid} 965 */ 966 function didReceiveAllocationTracesTops(tops) 967 { 968 this._topNodes = tops; 969 this._populateChildren(); 970 } 971 }, 972 973 _populateChildren: function() 974 { 975 this.removeTopLevelNodes(); 976 var root = this.rootNode(); 977 var tops = this._topNodes; 978 for (var i = 0; i < tops.length; i++) 979 this.appendNode(root, new WebInspector.AllocationGridNode(this, tops[i])); 980 this.updateVisibleNodes(true); 981 }, 982 983 sortingChanged: function() 984 { 985 this._topNodes.sort(this._createComparator()); 986 this.rootNode().removeChildren(); 987 this._populateChildren(); 988 }, 989 990 991 /** 992 * @return {function(!Object, !Object):number} 993 */ 994 _createComparator: function() 995 { 996 var fieldName = this.sortColumnIdentifier(); 997 var compareResult = (this.sortOrder() === WebInspector.DataGrid.Order.Ascending) ? +1 : -1; 998 /** 999 * @param {!Object} a 1000 * @param {!Object} b 1001 * @return {number} 1002 */ 1003 function compare(a, b) 1004 { 1005 if (a[fieldName] > b[fieldName]) 1006 return compareResult; 1007 if (a[fieldName] < b[fieldName]) 1008 return -compareResult; 1009 return 0; 1010 } 1011 return compare; 1012 }, 1013 1014 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 1015 } 1016