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 */ 35 WebInspector.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 55 WebInspector.HeapSnapshotSortableDataGrid.Events = { 56 ContentShown: "ContentShown" 57 } 58 59 WebInspector.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 */ 266 WebInspector.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 279 WebInspector.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 var selectedNode = this.selectedNode; 313 314 this.rootNode().removeChildren(); 315 // The height of the view port + invisible top part. 316 var heightToFill = viewPortHeight + (scrollTop - topPadding); 317 var filledHeight = 0; 318 while (i < children.length && filledHeight < heightToFill) { 319 if (children[i].revealed) { 320 this.rootNode().appendChild(children[i]); 321 filledHeight += children[i].nodeHeight(); 322 } 323 ++i; 324 } 325 326 var bottomPadding = 0; 327 while (i < children.length) { 328 bottomPadding += children[i].nodeHeight(); 329 ++i; 330 } 331 332 this._addPaddingRows(topPadding, bottomPadding); 333 334 if (selectedNode) { 335 if (selectedNode.parent) { 336 selectedNode.select(true); 337 } else { 338 // Keep selection even if the node is not in the current viewport. 339 this.selectedNode = selectedNode; 340 } 341 } 342 }, 343 344 appendTopLevelNode: function(node) 345 { 346 this._topLevelNodes.push(node); 347 }, 348 349 removeTopLevelNodes: function() 350 { 351 this.rootNode().removeChildren(); 352 this._topLevelNodes = []; 353 }, 354 355 /** 356 * @override 357 * @param {WebInspector.HeapSnapshotGridNode} node 358 */ 359 highlightNode: function(node) 360 { 361 if (this._isScrolledIntoView(node.element)) 362 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node); 363 else { 364 node.element.scrollIntoViewIfNeeded(true); 365 this._nodeToHighlightAfterScroll = node; 366 } 367 }, 368 369 _isScrolledIntoView: function(element) 370 { 371 var viewportTop = this.scrollContainer.scrollTop; 372 var viewportBottom = viewportTop + this.scrollContainer.clientHeight; 373 var elemTop = element.offsetTop 374 var elemBottom = elemTop + element.offsetHeight; 375 return elemBottom <= viewportBottom && elemTop >= viewportTop; 376 }, 377 378 _addPaddingRows: function(top, bottom) 379 { 380 if (this._topPadding.element.parentNode !== this.dataTableBody) 381 this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild); 382 if (this._bottomPadding.element.parentNode !== this.dataTableBody) 383 this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild); 384 this._topPadding.setHeight(top); 385 this._bottomPadding.setHeight(bottom); 386 }, 387 388 _removePaddingRows: function() 389 { 390 this._bottomPadding.removeFromTable(); 391 this._topPadding.removeFromTable(); 392 }, 393 394 onResize: function() 395 { 396 WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this); 397 this.updateVisibleNodes(); 398 }, 399 400 _onScroll: function(event) 401 { 402 this.updateVisibleNodes(); 403 404 if (this._nodeToHighlightAfterScroll) { 405 WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll); 406 this._nodeToHighlightAfterScroll = null; 407 } 408 }, 409 410 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 411 } 412 413 /** 414 * @constructor 415 */ 416 WebInspector.HeapSnapshotPaddingNode = function() 417 { 418 this.element = document.createElement("tr"); 419 this.element.addStyleClass("revealed"); 420 } 421 422 WebInspector.HeapSnapshotPaddingNode.prototype = { 423 setHeight: function(height) 424 { 425 this.element.style.height = height + "px"; 426 }, 427 removeFromTable: function() 428 { 429 var parent = this.element.parentNode; 430 if (parent) 431 parent.removeChild(this.element); 432 } 433 } 434 435 436 /** 437 * @constructor 438 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 439 * @param {Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns 440 */ 441 WebInspector.HeapSnapshotContainmentDataGrid = function(columns) 442 { 443 columns = columns || [ 444 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 445 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true}, 446 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 447 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending} 448 ]; 449 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); 450 } 451 452 WebInspector.HeapSnapshotContainmentDataGrid.prototype = { 453 setDataSource: function(snapshot, nodeIndex) 454 { 455 this.snapshot = snapshot; 456 var node = new WebInspector.HeapSnapshotNode(snapshot, nodeIndex || snapshot.rootNodeIndex); 457 var fakeEdge = { node: node }; 458 this.setRootNode(new WebInspector.HeapSnapshotObjectNode(this, false, fakeEdge, null)); 459 this.rootNode().sort(); 460 }, 461 462 sortingChanged: function() 463 { 464 this.rootNode().sort(); 465 }, 466 467 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 468 } 469 470 471 /** 472 * @constructor 473 * @extends {WebInspector.HeapSnapshotContainmentDataGrid} 474 */ 475 WebInspector.HeapSnapshotRetainmentDataGrid = function() 476 { 477 this.showRetainingEdges = true; 478 var columns = [ 479 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 480 {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending}, 481 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 482 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true} 483 ]; 484 WebInspector.HeapSnapshotContainmentDataGrid.call(this, columns); 485 } 486 487 WebInspector.HeapSnapshotRetainmentDataGrid.Events = { 488 ExpandRetainersComplete: "ExpandRetainersComplete" 489 } 490 491 WebInspector.HeapSnapshotRetainmentDataGrid.prototype = { 492 _sortFields: function(sortColumn, sortAscending) 493 { 494 return { 495 object: ["_name", sortAscending, "_count", false], 496 count: ["_count", sortAscending, "_name", true], 497 shallowSize: ["_shallowSize", sortAscending, "_name", true], 498 retainedSize: ["_retainedSize", sortAscending, "_name", true], 499 distance: ["_distance", sortAscending, "_name", true] 500 }[sortColumn]; 501 }, 502 503 reset: function() 504 { 505 this.rootNode().removeChildren(); 506 this.resetSortingCache(); 507 }, 508 509 /** 510 * @param {!WebInspector.HeapSnapshotProxy} snapshot 511 * @param {number} nodeIndex 512 */ 513 setDataSource: function(snapshot, nodeIndex) 514 { 515 WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex); 516 517 var dataGrid = this; 518 var maxExpandLevels = 20; 519 /** 520 * @this {!WebInspector.HeapSnapshotObjectNode} 521 */ 522 function populateComplete() 523 { 524 this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); 525 this.expand(); 526 if (--maxExpandLevels > 0 && this.children.length > 0) { 527 var retainer = this.children[0]; 528 if (retainer._distance > 1) { 529 retainer.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, retainer); 530 retainer.populate(); 531 return; 532 } 533 } 534 dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete); 535 } 536 this.rootNode().addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this.rootNode()); 537 }, 538 539 __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype 540 } 541 542 /** 543 * @constructor 544 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 545 */ 546 WebInspector.HeapSnapshotConstructorsDataGrid = function() 547 { 548 var columns = [ 549 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 550 {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true}, 551 {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true}, 552 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 553 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 554 ]; 555 WebInspector.HeapSnapshotViewportDataGrid.call(this, columns); 556 this._profileIndex = -1; 557 this._topLevelNodes = []; 558 559 this._objectIdToSelect = null; 560 } 561 562 /** 563 * @constructor 564 * @param {number=} minNodeId 565 * @param {number=} maxNodeId 566 */ 567 WebInspector.HeapSnapshotConstructorsDataGrid.Request = function(minNodeId, maxNodeId) 568 { 569 if (typeof minNodeId === "number") { 570 this.key = minNodeId + ".." + maxNodeId; 571 this.filter = "function(node) { var id = node.id(); return id > " + minNodeId + " && id <= " + maxNodeId + "; }"; 572 } else { 573 this.key = "allObjects"; 574 this.filter = null; 575 } 576 } 577 578 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { 579 _sortFields: function(sortColumn, sortAscending) 580 { 581 return { 582 object: ["_name", sortAscending, "_count", false], 583 distance: ["_distance", sortAscending, "_retainedSize", true], 584 count: ["_count", sortAscending, "_name", true], 585 shallowSize: ["_shallowSize", sortAscending, "_name", true], 586 retainedSize: ["_retainedSize", sortAscending, "_name", true] 587 }[sortColumn]; 588 }, 589 590 /** 591 * @override 592 * @param {HeapProfilerAgent.HeapSnapshotObjectId} id 593 */ 594 highlightObjectByHeapSnapshotId: function(id) 595 { 596 if (!this.snapshot) { 597 this._objectIdToSelect = id; 598 return; 599 } 600 601 function didGetClassName(className) 602 { 603 var constructorNodes = this.topLevelNodes(); 604 for (var i = 0; i < constructorNodes.length; i++) { 605 var parent = constructorNodes[i]; 606 if (parent._name === className) { 607 parent.revealNodeBySnapshotObjectId(parseInt(id, 10)); 608 return; 609 } 610 } 611 } 612 this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this)); 613 }, 614 615 setDataSource: function(snapshot) 616 { 617 this.snapshot = snapshot; 618 if (this._profileIndex === -1) 619 this._populateChildren(); 620 621 if (this._objectIdToSelect) { 622 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect); 623 this._objectIdToSelect = null; 624 } 625 }, 626 627 /** 628 * @param {number} minNodeId 629 * @param {number} maxNodeId 630 */ 631 setSelectionRange: function(minNodeId, maxNodeId) 632 { 633 this._populateChildren(new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId)); 634 }, 635 636 _aggregatesReceived: function(key, aggregates) 637 { 638 this._requestInProgress = null; 639 if (this._nextRequest) { 640 this.snapshot.aggregates(false, this._nextRequest.key, this._nextRequest.filter, this._aggregatesReceived.bind(this, this._nextRequest.key)); 641 this._requestInProgress = this._nextRequest; 642 this._nextRequest = null; 643 } 644 this.dispose(); 645 this.removeTopLevelNodes(); 646 this.resetSortingCache(); 647 for (var constructor in aggregates) 648 this.appendTopLevelNode(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key)); 649 this.sortingChanged(); 650 this._lastKey = key; 651 }, 652 653 /** 654 * @param {WebInspector.HeapSnapshotConstructorsDataGrid.Request=} request 655 */ 656 _populateChildren: function(request) 657 { 658 request = request || new WebInspector.HeapSnapshotConstructorsDataGrid.Request(); 659 660 if (this._requestInProgress) { 661 this._nextRequest = this._requestInProgress.key === request.key ? null : request; 662 return; 663 } 664 if (this._lastKey === request.key) 665 return; 666 this._requestInProgress = request; 667 this.snapshot.aggregates(false, request.key, request.filter, this._aggregatesReceived.bind(this, request.key)); 668 }, 669 670 filterSelectIndexChanged: function(profiles, profileIndex) 671 { 672 this._profileIndex = profileIndex; 673 674 var request = null; 675 if (profileIndex !== -1) { 676 var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0; 677 var maxNodeId = profiles[profileIndex].maxJSObjectId; 678 request = new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId) 679 } 680 681 this._populateChildren(request); 682 }, 683 684 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 685 } 686 687 688 /** 689 * @constructor 690 * @extends {WebInspector.HeapSnapshotViewportDataGrid} 691 */ 692 WebInspector.HeapSnapshotDiffDataGrid = function() 693 { 694 var columns = [ 695 {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, 696 {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true}, 697 {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true}, 698 {id: "countDelta", title: "# Delta", width: "64px", sortable: true}, 699 {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, 700 {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true}, 701 {id: "sizeDelta", title: "Size Delta", width: "72px", sortable: true} 702 ]; 703 WebInspector.HeapSnapshotViewportDataGrid.call(this, columns); 704 } 705 706 WebInspector.HeapSnapshotDiffDataGrid.prototype = { 707 /** 708 * @override 709 * @return {number} 710 */ 711 defaultPopulateCount: function() 712 { 713 return 50; 714 }, 715 716 _sortFields: function(sortColumn, sortAscending) 717 { 718 return { 719 object: ["_name", sortAscending, "_count", false], 720 addedCount: ["_addedCount", sortAscending, "_name", true], 721 removedCount: ["_removedCount", sortAscending, "_name", true], 722 countDelta: ["_countDelta", sortAscending, "_name", true], 723 addedSize: ["_addedSize", sortAscending, "_name", true], 724 removedSize: ["_removedSize", sortAscending, "_name", true], 725 sizeDelta: ["_sizeDelta", sortAscending, "_name", true] 726 }[sortColumn]; 727 }, 728 729 setDataSource: function(snapshot) 730 { 731 this.snapshot = snapshot; 732 }, 733 734 /** 735 * @param {WebInspector.HeapSnapshotProxy} baseSnapshot 736 */ 737 setBaseDataSource: function(baseSnapshot) 738 { 739 this.baseSnapshot = baseSnapshot; 740 this.dispose(); 741 this.removeTopLevelNodes(); 742 this.resetSortingCache(); 743 if (this.baseSnapshot === this.snapshot) { 744 this.dispatchEventToListeners("sorting complete"); 745 return; 746 } 747 this._populateChildren(); 748 }, 749 750 _populateChildren: function() 751 { 752 function aggregatesForDiffReceived(aggregatesForDiff) 753 { 754 this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this)); 755 function didCalculateSnapshotDiff(diffByClassName) 756 { 757 for (var className in diffByClassName) { 758 var diff = diffByClassName[className]; 759 this.appendTopLevelNode(new WebInspector.HeapSnapshotDiffNode(this, className, diff)); 760 } 761 this.sortingChanged(); 762 } 763 } 764 // Two snapshots live in different workers isolated from each other. That is why 765 // we first need to collect information about the nodes in the first snapshot and 766 // then pass it to the second snapshot to calclulate the diff. 767 this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this)); 768 }, 769 770 __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype 771 } 772 773 774 /** 775 * @constructor 776 * @extends {WebInspector.HeapSnapshotSortableDataGrid} 777 */ 778 WebInspector.HeapSnapshotDominatorsDataGrid = function() 779 { 780 var columns = [ 781 {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, 782 {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, 783 {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} 784 ]; 785 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); 786 this._objectIdToSelect = null; 787 } 788 789 WebInspector.HeapSnapshotDominatorsDataGrid.prototype = { 790 /** 791 * @override 792 * @return {number} 793 */ 794 defaultPopulateCount: function() 795 { 796 return 25; 797 }, 798 799 setDataSource: function(snapshot) 800 { 801 this.snapshot = snapshot; 802 803 var fakeNode = { nodeIndex: this.snapshot.rootNodeIndex }; 804 this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode)); 805 this.rootNode().sort(); 806 807 if (this._objectIdToSelect) { 808 this.highlightObjectByHeapSnapshotId(this._objectIdToSelect); 809 this._objectIdToSelect = null; 810 } 811 }, 812 813 sortingChanged: function() 814 { 815 this.rootNode().sort(); 816 }, 817 818 /** 819 * @override 820 * @param {HeapProfilerAgent.HeapSnapshotObjectId} id 821 */ 822 highlightObjectByHeapSnapshotId: function(id) 823 { 824 if (!this.snapshot) { 825 this._objectIdToSelect = id; 826 return; 827 } 828 829 function didGetDominators(dominatorIds) 830 { 831 if (!dominatorIds) { 832 WebInspector.log(WebInspector.UIString("Cannot find corresponding heap snapshot node")); 833 return; 834 } 835 var dominatorNode = this.rootNode(); 836 expandNextDominator.call(this, dominatorIds, dominatorNode); 837 } 838 839 function expandNextDominator(dominatorIds, dominatorNode) 840 { 841 if (!dominatorNode) { 842 console.error("Cannot find dominator node"); 843 return; 844 } 845 if (!dominatorIds.length) { 846 this.highlightNode(dominatorNode); 847 dominatorNode.element.scrollIntoViewIfNeeded(true); 848 return; 849 } 850 var snapshotObjectId = dominatorIds.pop(); 851 dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds)); 852 } 853 854 this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this)); 855 }, 856 857 __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype 858 } 859 860