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