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 */ 35WebInspector.HeapSnapshotSortableDataGrid = function(columns) 36{ 37 WebInspector.DataGrid.call(this, columns); 38 39 /** 40 * @type {number} 41 */ 42 this._recursiveSortingDepth = 0; 43 /** 44 * @type {WebInspector.HeapSnapshotGridNode} 45 */ 46 this._highlightedNode = null; 47 /** 48 * @type {boolean} 49 */ 50 this._populatedAndSorted = false; 51 this.addEventListener("sorting complete", this._sortingComplete, this); 52 this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this); 53} 54 55WebInspector.HeapSnapshotSortableDataGrid.Events = { 56 ContentShown: "ContentShown" 57} 58 59WebInspector.HeapSnapshotSortableDataGrid.prototype = { 60 /** 61 * @return {number} 62 */ 63 defaultPopulateCount: function() 64 { 65 return 100; 66 }, 67 68 dispose: function() 69 { 70 var children = this.topLevelNodes(); 71 for (var i = 0, l = children.length; i < l; ++i) 72 children[i].dispose(); 73 }, 74 75 /** 76 * @override 77 */ 78 wasShown: function() 79 { 80 if (this._populatedAndSorted) 81 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); 82 }, 83 84 _sortingComplete: function() 85 { 86 this.removeEventListener("sorting complete", this._sortingComplete, this); 87 this._populatedAndSorted = true; 88 this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); 89 }, 90 91 /** 92 * @override 93 */ 94 willHide: function() 95 { 96 this._clearCurrentHighlight(); 97 }, 98 99 /** 100 * @param {WebInspector.ProfilesPanel} profilesPanel 101 * @param {WebInspector.ContextMenu} contextMenu 102 * @param {Event} event 103 */ 104 populateContextMenu: function(profilesPanel, contextMenu, event) 105 { 106 var td = event.target.enclosingNodeOrSelfWithNodeName("td"); 107 if (!td) 108 return; 109 var node = td.heapSnapshotNode; 110 if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) { 111 function revealInDominatorsView() 112 { 113 profilesPanel.showObject(node.snapshotNodeId, "Dominators"); 114 } 115 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this)); 116 } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) { 117 function revealInSummaryView() 118 { 119 profilesPanel.showObject(node.snapshotNodeId, "Summary"); 120 } 121 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this)); 122 } 123 }, 124 125 resetSortingCache: function() 126 { 127 delete this._lastSortColumnIdentifier; 128 delete this._lastSortAscending; 129 }, 130 131 topLevelNodes: function() 132 { 133 return this.rootNode().children; 134 }, 135 136 /** 137 * @param {HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId 138 */ 139 highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId) 140 { 141 }, 142 143 /** 144 * @param {WebInspector.HeapSnapshotGridNode} node 145 */ 146 highlightNode: function(node) 147 { 148 var prevNode = this._highlightedNode; 149 this._clearCurrentHighlight(); 150 this._highlightedNode = node; 151 this._highlightedNode.element.addStyleClass("highlighted-row"); 152 // If highlighted node hasn't changed reinsert it to make the highlight animation restart. 153 if (node === prevNode) { 154 var element = node.element; 155 var parent = element.parentElement; 156 var nextSibling = element.nextSibling; 157 parent.removeChild(element); 158 parent.insertBefore(element, nextSibling); 159 } 160 }, 161 162 nodeWasDetached: function(node) 163 { 164 if (this._highlightedNode === node) 165 this._clearCurrentHighlight(); 166 }, 167 168 _clearCurrentHighlight: function() 169 { 170 if (!this._highlightedNode) 171 return 172 this._highlightedNode.element.removeStyleClass("highlighted-row"); 173 this._highlightedNode = null; 174 }, 175 176 changeNameFilter: function(filter) 177 { 178 filter = filter.toLowerCase(); 179 var children = this.topLevelNodes(); 180 for (var i = 0, l = children.length; i < l; ++i) { 181 var node = children[i]; 182 if (node.depth === 0) 183 node.revealed = node._name.toLowerCase().indexOf(filter) !== -1; 184 } 185 this.updateVisibleNodes(); 186 }, 187 188 sortingChanged: function() 189 { 190 var sortAscending = this.isSortOrderAscending(); 191 var sortColumnIdentifier = this.sortColumnIdentifier(); 192 if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending) 193 return; 194 this._lastSortColumnIdentifier = sortColumnIdentifier; 195 this._lastSortAscending = sortAscending; 196 var sortFields = this._sortFields(sortColumnIdentifier, sortAscending); 197 198 function SortByTwoFields(nodeA, nodeB) 199 { 200 var field1 = nodeA[sortFields[0]]; 201 var field2 = nodeB[sortFields[0]]; 202 var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); 203 if (!sortFields[1]) 204 result = -result; 205 if (result !== 0) 206 return result; 207 field1 = nodeA[sortFields[2]]; 208 field2 = nodeB[sortFields[2]]; 209 result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); 210 if (!sortFields[3]) 211 result = -result; 212 return result; 213 } 214 this._performSorting(SortByTwoFields); 215 }, 216 217 _performSorting: function(sortFunction) 218 { 219 this.recursiveSortingEnter(); 220 var children = this._topLevelNodes; 221 this.rootNode().removeChildren(); 222 children.sort(sortFunction); 223 for (var i = 0, l = children.length; i < l; ++i) { 224 var child = children[i]; 225 this.appendChildAfterSorting(child); 226 if (child.expanded) 227 child.sort(); 228 } 229 this.updateVisibleNodes(); 230 this.recursiveSortingLeave(); 231 }, 232 233 appendChildAfterSorting: function(child) 234 { 235 var revealed = child.revealed; 236 this.rootNode().appendChild(child); 237 child.revealed = revealed; 238 }, 239 240 updateVisibleNodes: function() 241 { 242 }, 243 244 recursiveSortingEnter: function() 245 { 246 ++this._recursiveSortingDepth; 247 }, 248 249 recursiveSortingLeave: function() 250 { 251 if (!this._recursiveSortingDepth) 252 return; 253 if (!--this._recursiveSortingDepth) 254 this.dispatchEventToListeners("sorting complete"); 255 }, 256 257 __proto__: WebInspector.DataGrid.prototype 258} 259 260 261 262/** 263 * @constructor 264 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 265 */ 266WebInspector.HeapSnapshotViewportDataGrid = function(columns) 267{ 268 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); 269 this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true); 270 this._topLevelNodes = []; 271 this._topPadding = new WebInspector.HeapSnapshotPaddingNode(); 272 this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode(); 273 /** 274 * @type {WebInspector.HeapSnapshotGridNode} 275 */ 276 this._nodeToHighlightAfterScroll = null; 277} 278 279WebInspector.HeapSnapshotViewportDataGrid.prototype = { 280 topLevelNodes: function() 281 { 282 return this._topLevelNodes; 283 }, 284 285 appendChildAfterSorting: function(child) 286 { 287 // Do nothing here, it will be added in updateVisibleNodes. 288 }, 289 290 updateVisibleNodes: function() 291 { 292 var scrollTop = this.scrollContainer.scrollTop; 293 294 var viewPortHeight = this.scrollContainer.offsetHeight; 295 296 this._removePaddingRows(); 297 298 var children = this._topLevelNodes; 299 300 var i = 0; 301 var topPadding = 0; 302 while (i < children.length) { 303 if (children[i].revealed) { 304 var newTop = topPadding + children[i].nodeHeight(); 305 if (newTop > scrollTop) 306 break; 307 topPadding = newTop; 308 } 309 ++i; 310 } 311 312 this.rootNode().removeChildren(); 313 // The height of the view port + invisible top part. 314 var heightToFill = viewPortHeight + (scrollTop - topPadding); 315 var filledHeight = 0; 316 while (i < children.length && filledHeight < heightToFill) { 317 if (children[i].revealed) { 318 this.rootNode().appendChild(children[i]); 319 filledHeight += children[i].nodeHeight(); 320 } 321 ++i; 322 } 323 324 var bottomPadding = 0; 325 while (i < children.length) { 326 bottomPadding += children[i].nodeHeight(); 327 ++i; 328 } 329 330 this._addPaddingRows(topPadding, bottomPadding); 331 }, 332 333 appendTopLevelNode: function(node) 334 { 335 this._topLevelNodes.push(node); 336 }, 337 338 removeTopLevelNodes: function() 339 { 340 this.rootNode().removeChildren(); 341 this._topLevelNodes = []; 342 }, 343 344 /** 345 * @override 346 * @param {WebInspector.HeapSnapshotGridNode} node 347 */ 348 highlightNode: function(node) 349 { 350 if (this._isScrolledIntoView(node.element)) 351 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node); 352 else { 353 node.element.scrollIntoViewIfNeeded(true); 354 this._nodeToHighlightAfterScroll = node; 355 } 356 }, 357 358 _isScrolledIntoView: function(element) 359 { 360 var viewportTop = this.scrollContainer.scrollTop; 361 var viewportBottom = viewportTop + this.scrollContainer.clientHeight; 362 var elemTop = element.offsetTop 363 var elemBottom = elemTop + element.offsetHeight; 364 return elemBottom <= viewportBottom && elemTop >= viewportTop; 365 }, 366 367 _addPaddingRows: function(top, bottom) 368 { 369 if (this._topPadding.element.parentNode !== this.dataTableBody) 370 this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild); 371 if (this._bottomPadding.element.parentNode !== this.dataTableBody) 372 this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild); 373 this._topPadding.setHeight(top); 374 this._bottomPadding.setHeight(bottom); 375 }, 376 377 _removePaddingRows: function() 378 { 379 this._bottomPadding.removeFromTable(); 380 this._topPadding.removeFromTable(); 381 }, 382 383 onResize: function() 384 { 385 WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this); 386 this.updateVisibleNodes(); 387 }, 388 389 _onScroll: function(event) 390 { 391 this.updateVisibleNodes(); 392 393 if (this._nodeToHighlightAfterScroll) { 394 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll); 395 this._nodeToHighlightAfterScroll = null; 396 } 397 }, 398 399 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 400} 401 402/** 403 * @constructor 404 */ 405WebInspector.HeapSnapshotPaddingNode = function() 406{ 407 this.element = document.createElement("tr"); 408 this.element.addStyleClass("revealed"); 409} 410 411WebInspector.HeapSnapshotPaddingNode.prototype = { 412 setHeight: function(height) 413 { 414 this.element.style.height = height + "px"; 415 }, 416 removeFromTable: function() 417 { 418 var parent = this.element.parentNode; 419 if (parent) 420 parent.removeChild(this.element); 421 } 422} 423 424 425/** 426 * @constructor 427 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 428 * @param {Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns 429 */ 430WebInspector.HeapSnapshotContainmentDataGrid = function(columns) 431{ 432 columns = columns || [ 433 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 434 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 435 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending} 436 ]; 437 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); 438} 439 440WebInspector.HeapSnapshotContainmentDataGrid.prototype = { 441 setDataSource: function(snapshot, nodeIndex) 442 { 443 this.snapshot = snapshot; 444 var node = new WebInspector.HeapSnapshotNode(snapshot, nodeIndex || snapshot.rootNodeIndex); 445 var fakeEdge = { node: node }; 446 this.setRootNode(new WebInspector.HeapSnapshotObjectNode(this, false, fakeEdge, null)); 447 this.rootNode().sort(); 448 }, 449 450 sortingChanged: function() 451 { 452 this.rootNode().sort(); 453 }, 454 455 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 456} 457 458 459/** 460 * @constructor 461 * @extends {WebInspector.HeapSnapshotContainmentDataGrid} 462 */ 463WebInspector.HeapSnapshotRetainmentDataGrid = function() 464{ 465 this.showRetainingEdges = true; 466 var columns = [ 467 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 468 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 469 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true}, 470 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending} 471 ]; 472 WebInspector.HeapSnapshotContainmentDataGrid.call(this, columns); 473} 474 475WebInspector.HeapSnapshotRetainmentDataGrid.Events = { 476 ExpandRetainersComplete: "ExpandRetainersComplete" 477} 478 479WebInspector.HeapSnapshotRetainmentDataGrid.prototype = { 480 _sortFields: function(sortColumn, sortAscending) 481 { 482 return { 483 object: ["_name", sortAscending, "_count", false], 484 count: ["_count", sortAscending, "_name", true], 485 shallowSize: ["_shallowSize", sortAscending, "_name", true], 486 retainedSize: ["_retainedSize", sortAscending, "_name", true], 487 distance: ["_distance", sortAscending, "_name", true] 488 }[sortColumn]; 489 }, 490 491 reset: function() 492 { 493 this.rootNode().removeChildren(); 494 this.resetSortingCache(); 495 }, 496 497 /** 498 * @param {!WebInspector.HeapSnapshotProxy} snapshot 499 * @param {number} nodeIndex 500 */ 501 setDataSource: function(snapshot, nodeIndex) 502 { 503 WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex); 504 505 var dataGrid = this; 506 var maxExpandLevels = 20; 507 /** 508 * @this {!WebInspector.HeapSnapshotObjectNode} 509 */ 510 function populateComplete() 511 { 512 this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); 513 this.expand(); 514 if (this.children.length === 1 && --maxExpandLevels > 0) { 515 var child = this.children[0]; 516 child.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, child); 517 child.populate(); 518 } else 519 dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete); 520 } 521 this.rootNode().addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this.rootNode()); 522 }, 523 524 __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype 525} 526 527 528/** 529 * @constructor 530 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 531 */ 532WebInspector.HeapSnapshotConstructorsDataGrid = function() 533{ 534 var columns = [ 535 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 536 {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true}, 537 {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true}, 538 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 539 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 540 ]; 541 WebInspector.HeapSnapshotViewportDataGrid.call(this, columns); 542 this._profileIndex = -1; 543 this._topLevelNodes = []; 544 545 this._objectIdToSelect = null; 546} 547 548WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { 549 _sortFields: function(sortColumn, sortAscending) 550 { 551 return { 552 object: ["_name", sortAscending, "_count", false], 553 distance: ["_distance", sortAscending, "_retainedSize", true], 554 count: ["_count", sortAscending, "_name", true], 555 shallowSize: ["_shallowSize", sortAscending, "_name", true], 556 retainedSize: ["_retainedSize", sortAscending, "_name", true] 557 }[sortColumn]; 558 }, 559 560 /** 561 * @override 562 * @param {HeapProfilerAgent.HeapSnapshotObjectId} id 563 */ 564 highlightObjectByHeapSnapshotId: function(id) 565 { 566 if (!this.snapshot) { 567 this._objectIdToSelect = id; 568 return; 569 } 570 571 function didGetClassName(className) 572 { 573 var constructorNodes = this.topLevelNodes(); 574 for (var i = 0; i < constructorNodes.length; i++) { 575 var parent = constructorNodes[i]; 576 if (parent._name === className) { 577 parent.revealNodeBySnapshotObjectId(parseInt(id, 10)); 578 return; 579 } 580 } 581 } 582 this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this)); 583 }, 584 585 setDataSource: function(snapshot) 586 { 587 this.snapshot = snapshot; 588 if (this._profileIndex === -1) 589 this._populateChildren(); 590 591 if (this._objectIdToSelect) { 592 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect); 593 this._objectIdToSelect = null; 594 } 595 }, 596 597 _aggregatesReceived: function(key, aggregates) 598 { 599 for (var constructor in aggregates) 600 this.appendTopLevelNode(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key)); 601 this.sortingChanged(); 602 }, 603 604 _populateChildren: function() 605 { 606 607 this.dispose(); 608 this.removeTopLevelNodes(); 609 this.resetSortingCache(); 610 611 var key = this._profileIndex === -1 ? "allObjects" : this._minNodeId + ".." + this._maxNodeId; 612 var filter = this._profileIndex === -1 ? null : "function(node) { var id = node.id(); return id > " + this._minNodeId + " && id <= " + this._maxNodeId + "; }"; 613 614 this.snapshot.aggregates(false, key, filter, this._aggregatesReceived.bind(this, key)); 615 }, 616 617 filterSelectIndexChanged: function(profiles, profileIndex) 618 { 619 this._profileIndex = profileIndex; 620 621 delete this._maxNodeId; 622 delete this._minNodeId; 623 624 if (this._profileIndex !== -1) { 625 this._minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0; 626 this._maxNodeId = profiles[profileIndex].maxJSObjectId; 627 } 628 629 this._populateChildren(); 630 }, 631 632 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 633} 634 635 636/** 637 * @constructor 638 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 639 */ 640WebInspector.HeapSnapshotDiffDataGrid = function() 641{ 642 var columns = [ 643 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 644 {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true}, 645 {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true}, 646 {id: "countDelta", title: "# Delta", width: "64px", sortable: true}, 647 {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, 648 {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true}, 649 {id: "sizeDelta", title: "Size Delta", width: "72px", sortable: true} 650 ]; 651 WebInspector.HeapSnapshotViewportDataGrid.call(this, columns); 652} 653 654WebInspector.HeapSnapshotDiffDataGrid.prototype = { 655 /** 656 * @override 657 * @return {number} 658 */ 659 defaultPopulateCount: function() 660 { 661 return 50; 662 }, 663 664 _sortFields: function(sortColumn, sortAscending) 665 { 666 return { 667 object: ["_name", sortAscending, "_count", false], 668 addedCount: ["_addedCount", sortAscending, "_name", true], 669 removedCount: ["_removedCount", sortAscending, "_name", true], 670 countDelta: ["_countDelta", sortAscending, "_name", true], 671 addedSize: ["_addedSize", sortAscending, "_name", true], 672 removedSize: ["_removedSize", sortAscending, "_name", true], 673 sizeDelta: ["_sizeDelta", sortAscending, "_name", true] 674 }[sortColumn]; 675 }, 676 677 setDataSource: function(snapshot) 678 { 679 this.snapshot = snapshot; 680 }, 681 682 /** 683 * @param {WebInspector.HeapSnapshotProxy} baseSnapshot 684 */ 685 setBaseDataSource: function(baseSnapshot) 686 { 687 this.baseSnapshot = baseSnapshot; 688 this.dispose(); 689 this.removeTopLevelNodes(); 690 this.resetSortingCache(); 691 if (this.baseSnapshot === this.snapshot) { 692 this.dispatchEventToListeners("sorting complete"); 693 return; 694 } 695 this._populateChildren(); 696 }, 697 698 _populateChildren: function() 699 { 700 function aggregatesForDiffReceived(aggregatesForDiff) 701 { 702 this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this)); 703 function didCalculateSnapshotDiff(diffByClassName) 704 { 705 for (var className in diffByClassName) { 706 var diff = diffByClassName[className]; 707 this.appendTopLevelNode(new WebInspector.HeapSnapshotDiffNode(this, className, diff)); 708 } 709 this.sortingChanged(); 710 } 711 } 712 // Two snapshots live in different workers isolated from each other. That is why 713 // we first need to collect information about the nodes in the first snapshot and 714 // then pass it to the second snapshot to calclulate the diff. 715 this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this)); 716 }, 717 718 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 719} 720 721 722/** 723 * @constructor 724 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 725 */ 726WebInspector.HeapSnapshotDominatorsDataGrid = function() 727{ 728 var columns = [ 729 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 730 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 731 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 732 ]; 733 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); 734 this._objectIdToSelect = null; 735} 736 737WebInspector.HeapSnapshotDominatorsDataGrid.prototype = { 738 /** 739 * @override 740 * @return {number} 741 */ 742 defaultPopulateCount: function() 743 { 744 return 25; 745 }, 746 747 setDataSource: function(snapshot) 748 { 749 this.snapshot = snapshot; 750 751 var fakeNode = { nodeIndex: this.snapshot.rootNodeIndex }; 752 this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode)); 753 this.rootNode().sort(); 754 755 if (this._objectIdToSelect) { 756 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect); 757 this._objectIdToSelect = null; 758 } 759 }, 760 761 sortingChanged: function() 762 { 763 this.rootNode().sort(); 764 }, 765 766 /** 767 * @override 768 * @param {HeapProfilerAgent.HeapSnapshotObjectId} id 769 */ 770 highlightObjectByHeapSnapshotId: function(id) 771 { 772 if (!this.snapshot) { 773 this._objectIdToSelect = id; 774 return; 775 } 776 777 function didGetDominators(dominatorIds) 778 { 779 if (!dominatorIds) { 780 WebInspector.log(WebInspector.UIString("Cannot find corresponding heap snapshot node")); 781 return; 782 } 783 var dominatorNode = this.rootNode(); 784 expandNextDominator.call(this, dominatorIds, dominatorNode); 785 } 786 787 function expandNextDominator(dominatorIds, dominatorNode) 788 { 789 if (!dominatorNode) { 790 console.error("Cannot find dominator node"); 791 return; 792 } 793 if (!dominatorIds.length) { 794 this.highlightNode(dominatorNode); 795 dominatorNode.element.scrollIntoViewIfNeeded(true); 796 return; 797 } 798 var snapshotObjectId = dominatorIds.pop(); 799 dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds)); 800 } 801 802 this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this)); 803 }, 804 805 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 806} 807 808