1/* 2 * Copyright (C) 2011 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.DataGridNode} 34 * @param {!WebInspector.HeapSnapshotSortableDataGrid} tree 35 * @param {boolean} hasChildren 36 */ 37WebInspector.HeapSnapshotGridNode = function(tree, hasChildren) 38{ 39 WebInspector.DataGridNode.call(this, null, hasChildren); 40 this._dataGrid = tree; 41 this._instanceCount = 0; 42 43 this._savedChildren = null; 44 /** 45 * List of position ranges for all visible nodes: [startPos1, endPos1),...,[startPosN, endPosN) 46 * Position is an item position in the provider. 47 */ 48 this._retrievedChildrenRanges = []; 49 50 /** 51 * @type {?WebInspector.HeapSnapshotGridNode.ChildrenProvider} 52 */ 53 this._providerObject = null; 54} 55 56WebInspector.HeapSnapshotGridNode.Events = { 57 PopulateComplete: "PopulateComplete" 58} 59 60/** 61 * @param {!Array.<string>} fieldNames 62 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 63 */ 64WebInspector.HeapSnapshotGridNode.createComparator = function(fieldNames) 65{ 66 return /** @type {!WebInspector.HeapSnapshotCommon.ComparatorConfig} */ ({fieldName1: fieldNames[0], ascending1: fieldNames[1], fieldName2: fieldNames[2], ascending2: fieldNames[3]}); 67} 68 69 70/** 71 * @interface 72 */ 73WebInspector.HeapSnapshotGridNode.ChildrenProvider = function() { } 74 75WebInspector.HeapSnapshotGridNode.ChildrenProvider.prototype = { 76 dispose: function() { }, 77 78 /** 79 * @param {number} snapshotObjectId 80 * @param {function(number)} callback 81 */ 82 nodePosition: function(snapshotObjectId, callback) { }, 83 84 /** 85 * @param {function(boolean)} callback 86 */ 87 isEmpty: function(callback) { }, 88 89 /** 90 * @param {number} startPosition 91 * @param {number} endPosition 92 * @param {function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback 93 */ 94 serializeItemsRange: function(startPosition, endPosition, callback) { }, 95 96 /** 97 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 98 * @param {function()} callback 99 */ 100 sortAndRewind: function(comparator, callback) { } 101} 102 103 104WebInspector.HeapSnapshotGridNode.prototype = { 105 /** 106 * @return {!WebInspector.HeapSnapshotGridNode.ChildrenProvider} 107 */ 108 createProvider: function() 109 { 110 throw new Error("Not implemented."); 111 }, 112 113 /** 114 * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}} 115 */ 116 retainersDataSource: function() 117 { 118 return null; 119 }, 120 121 /** 122 * @return {!WebInspector.HeapSnapshotGridNode.ChildrenProvider} 123 */ 124 _provider: function() 125 { 126 if (!this._providerObject) 127 this._providerObject = this.createProvider(); 128 return this._providerObject; 129 }, 130 131 /** 132 * @param {string} columnIdentifier 133 * @return {!Element} 134 */ 135 createCell: function(columnIdentifier) 136 { 137 var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); 138 if (this._searchMatched) 139 cell.classList.add("highlight"); 140 return cell; 141 }, 142 143 /** 144 * @override 145 */ 146 collapse: function() 147 { 148 WebInspector.DataGridNode.prototype.collapse.call(this); 149 this._dataGrid.updateVisibleNodes(true); 150 }, 151 152 /** 153 * @override 154 */ 155 expand: function() 156 { 157 WebInspector.DataGridNode.prototype.expand.call(this); 158 this._dataGrid.updateVisibleNodes(true); 159 }, 160 161 /** 162 * @override 163 */ 164 dispose: function() 165 { 166 if (this._providerObject) 167 this._providerObject.dispose(); 168 for (var node = this.children[0]; node; node = node.traverseNextNode(true, this, true)) 169 if (node.dispose) 170 node.dispose(); 171 }, 172 173 _reachableFromWindow: false, 174 175 queryObjectContent: function(callback) 176 { 177 }, 178 179 /** 180 * @override 181 */ 182 wasDetached: function() 183 { 184 this._dataGrid.nodeWasDetached(this); 185 }, 186 187 /** 188 * @param {number} num 189 * @return {string} 190 */ 191 _toPercentString: function(num) 192 { 193 return num.toFixed(0) + "\u2009%"; // \u2009 is a thin space. 194 }, 195 196 /** 197 * @param {number} distance 198 * @return {string} 199 */ 200 _toUIDistance: function(distance) 201 { 202 var baseSystemDistance = WebInspector.HeapSnapshotCommon.baseSystemDistance; 203 return distance >= 0 && distance < baseSystemDistance ? WebInspector.UIString("%d", distance) : WebInspector.UIString("\u2212"); 204 }, 205 206 /** 207 * @return {!Array.<!WebInspector.DataGridNode>} 208 */ 209 allChildren: function() 210 { 211 return this._dataGrid.allChildren(this); 212 }, 213 214 /** 215 * @param {number} index 216 */ 217 removeChildByIndex: function(index) 218 { 219 this._dataGrid.removeChildByIndex(this, index); 220 }, 221 222 /** 223 * @param {number} nodePosition 224 * @return {?WebInspector.DataGridNode} 225 */ 226 childForPosition: function(nodePosition) 227 { 228 var indexOfFirstChildInRange = 0; 229 for (var i = 0; i < this._retrievedChildrenRanges.length; i++) { 230 var range = this._retrievedChildrenRanges[i]; 231 if (range.from <= nodePosition && nodePosition < range.to) { 232 var childIndex = indexOfFirstChildInRange + nodePosition - range.from; 233 return this.allChildren()[childIndex]; 234 } 235 indexOfFirstChildInRange += range.to - range.from + 1; 236 } 237 return null; 238 }, 239 240 /** 241 * @param {string} columnIdentifier 242 * @return {!Element} 243 */ 244 _createValueCell: function(columnIdentifier) 245 { 246 var cell = document.createElement("td"); 247 cell.className = "numeric-column"; 248 if (this.dataGrid.snapshot.totalSize !== 0) { 249 var div = document.createElement("div"); 250 var valueSpan = document.createElement("span"); 251 valueSpan.textContent = this.data[columnIdentifier]; 252 div.appendChild(valueSpan); 253 var percentColumn = columnIdentifier + "-percent"; 254 if (percentColumn in this.data) { 255 var percentSpan = document.createElement("span"); 256 percentSpan.className = "percent-column"; 257 percentSpan.textContent = this.data[percentColumn]; 258 div.appendChild(percentSpan); 259 div.classList.add("profile-multiple-values"); 260 } 261 cell.appendChild(div); 262 } 263 return cell; 264 }, 265 266 populate: function(event) 267 { 268 if (this._populated) 269 return; 270 this._populated = true; 271 272 /** 273 * @this {WebInspector.HeapSnapshotGridNode} 274 */ 275 function sorted() 276 { 277 this._populateChildren(); 278 } 279 this._provider().sortAndRewind(this.comparator(), sorted.bind(this)); 280 }, 281 282 expandWithoutPopulate: function(callback) 283 { 284 // Make sure default populate won't take action. 285 this._populated = true; 286 this.expand(); 287 this._provider().sortAndRewind(this.comparator(), callback); 288 }, 289 290 /** 291 * @param {?number=} fromPosition 292 * @param {?number=} toPosition 293 * @param {function()=} afterPopulate 294 */ 295 _populateChildren: function(fromPosition, toPosition, afterPopulate) 296 { 297 fromPosition = fromPosition || 0; 298 toPosition = toPosition || fromPosition + this._dataGrid.defaultPopulateCount(); 299 var firstNotSerializedPosition = fromPosition; 300 301 /** 302 * @this {WebInspector.HeapSnapshotGridNode} 303 */ 304 function serializeNextChunk() 305 { 306 if (firstNotSerializedPosition >= toPosition) 307 return; 308 var end = Math.min(firstNotSerializedPosition + this._dataGrid.defaultPopulateCount(), toPosition); 309 this._provider().serializeItemsRange(firstNotSerializedPosition, end, childrenRetrieved.bind(this)); 310 firstNotSerializedPosition = end; 311 } 312 313 /** 314 * @this {WebInspector.HeapSnapshotGridNode} 315 */ 316 function insertRetrievedChild(item, insertionIndex) 317 { 318 if (this._savedChildren) { 319 var hash = this._childHashForEntity(item); 320 if (hash in this._savedChildren) { 321 this._dataGrid.insertChild(this, this._savedChildren[hash], insertionIndex); 322 return; 323 } 324 } 325 this._dataGrid.insertChild(this, this._createChildNode(item), insertionIndex); 326 } 327 328 /** 329 * @this {WebInspector.HeapSnapshotGridNode} 330 */ 331 function insertShowMoreButton(from, to, insertionIndex) 332 { 333 var button = new WebInspector.ShowMoreDataGridNode(this._populateChildren.bind(this), from, to, this._dataGrid.defaultPopulateCount()); 334 this._dataGrid.insertChild(this, button, insertionIndex); 335 } 336 337 /** 338 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange 339 * @this {WebInspector.HeapSnapshotGridNode} 340 */ 341 function childrenRetrieved(itemsRange) 342 { 343 var itemIndex = 0; 344 var itemPosition = itemsRange.startPosition; 345 var items = itemsRange.items; 346 var insertionIndex = 0; 347 348 if (!this._retrievedChildrenRanges.length) { 349 if (itemsRange.startPosition > 0) { 350 this._retrievedChildrenRanges.push({from: 0, to: 0}); 351 insertShowMoreButton.call(this, 0, itemsRange.startPosition, insertionIndex++); 352 } 353 this._retrievedChildrenRanges.push({from: itemsRange.startPosition, to: itemsRange.endPosition}); 354 for (var i = 0, l = items.length; i < l; ++i) 355 insertRetrievedChild.call(this, items[i], insertionIndex++); 356 if (itemsRange.endPosition < itemsRange.totalLength) 357 insertShowMoreButton.call(this, itemsRange.endPosition, itemsRange.totalLength, insertionIndex++); 358 } else { 359 var rangeIndex = 0; 360 var found = false; 361 var range; 362 while (rangeIndex < this._retrievedChildrenRanges.length) { 363 range = this._retrievedChildrenRanges[rangeIndex]; 364 if (range.to >= itemPosition) { 365 found = true; 366 break; 367 } 368 insertionIndex += range.to - range.from; 369 // Skip the button if there is one. 370 if (range.to < itemsRange.totalLength) 371 insertionIndex += 1; 372 ++rangeIndex; 373 } 374 375 if (!found || itemsRange.startPosition < range.from) { 376 // Update previous button. 377 this.allChildren()[insertionIndex - 1].setEndPosition(itemsRange.startPosition); 378 insertShowMoreButton.call(this, itemsRange.startPosition, found ? range.from : itemsRange.totalLength, insertionIndex); 379 range = {from: itemsRange.startPosition, to: itemsRange.startPosition}; 380 if (!found) 381 rangeIndex = this._retrievedChildrenRanges.length; 382 this._retrievedChildrenRanges.splice(rangeIndex, 0, range); 383 } else { 384 insertionIndex += itemPosition - range.from; 385 } 386 // At this point insertionIndex is always an index before button or between nodes. 387 // Also it is always true here that range.from <= itemPosition <= range.to 388 389 // Stretch the range right bound to include all new items. 390 while (range.to < itemsRange.endPosition) { 391 // Skip already added nodes. 392 var skipCount = range.to - itemPosition; 393 insertionIndex += skipCount; 394 itemIndex += skipCount; 395 itemPosition = range.to; 396 397 // We're at the position before button: ...<?node>x<button> 398 var nextRange = this._retrievedChildrenRanges[rangeIndex + 1]; 399 var newEndOfRange = nextRange ? nextRange.from : itemsRange.totalLength; 400 if (newEndOfRange > itemsRange.endPosition) 401 newEndOfRange = itemsRange.endPosition; 402 while (itemPosition < newEndOfRange) { 403 insertRetrievedChild.call(this, items[itemIndex++], insertionIndex++); 404 ++itemPosition; 405 } 406 // Merge with the next range. 407 if (nextRange && newEndOfRange === nextRange.from) { 408 range.to = nextRange.to; 409 // Remove "show next" button if there is one. 410 this.removeChildByIndex(insertionIndex); 411 this._retrievedChildrenRanges.splice(rangeIndex + 1, 1); 412 } else { 413 range.to = newEndOfRange; 414 // Remove or update next button. 415 if (newEndOfRange === itemsRange.totalLength) 416 this.removeChildByIndex(insertionIndex); 417 else 418 this.allChildren()[insertionIndex].setStartPosition(itemsRange.endPosition); 419 } 420 } 421 } 422 423 // TODO: fix this. 424 this._instanceCount += items.length; 425 if (firstNotSerializedPosition < toPosition) { 426 serializeNextChunk.call(this); 427 return; 428 } 429 430 if (this.expanded) 431 this._dataGrid.updateVisibleNodes(true); 432 if (afterPopulate) 433 afterPopulate(); 434 this.dispatchEventToListeners(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete); 435 } 436 serializeNextChunk.call(this); 437 }, 438 439 _saveChildren: function() 440 { 441 this._savedChildren = null; 442 var children = this.allChildren(); 443 for (var i = 0, l = children.length; i < l; ++i) { 444 var child = children[i]; 445 if (!child.expanded) 446 continue; 447 if (!this._savedChildren) 448 this._savedChildren = {}; 449 this._savedChildren[this._childHashForNode(child)] = child; 450 } 451 }, 452 453 sort: function() 454 { 455 this._dataGrid.recursiveSortingEnter(); 456 457 /** 458 * @this {WebInspector.HeapSnapshotGridNode} 459 */ 460 function afterSort() 461 { 462 this._saveChildren(); 463 this._dataGrid.removeAllChildren(this); 464 this._retrievedChildrenRanges = []; 465 466 /** 467 * @this {WebInspector.HeapSnapshotGridNode} 468 */ 469 function afterPopulate() 470 { 471 var children = this.allChildren(); 472 for (var i = 0, l = children.length; i < l; ++i) { 473 var child = children[i]; 474 if (child.expanded) 475 child.sort(); 476 } 477 this._dataGrid.recursiveSortingLeave(); 478 } 479 var instanceCount = this._instanceCount; 480 this._instanceCount = 0; 481 this._populateChildren(0, instanceCount, afterPopulate.bind(this)); 482 } 483 484 this._provider().sortAndRewind(this.comparator(), afterSort.bind(this)); 485 }, 486 487 __proto__: WebInspector.DataGridNode.prototype 488} 489 490 491/** 492 * @constructor 493 * @extends {WebInspector.HeapSnapshotGridNode} 494 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 495 * @param {!WebInspector.HeapSnapshotCommon.Node} node 496 */ 497WebInspector.HeapSnapshotGenericObjectNode = function(dataGrid, node) 498{ 499 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, false); 500 // node is null for DataGrid root nodes. 501 if (!node) 502 return; 503 this._name = node.name; 504 this._type = node.type; 505 this._distance = node.distance; 506 this._shallowSize = node.selfSize; 507 this._retainedSize = node.retainedSize; 508 this.snapshotNodeId = node.id; 509 this.snapshotNodeIndex = node.nodeIndex; 510 if (this._type === "string") 511 this._reachableFromWindow = true; 512 else if (this._type === "object" && this._name.startsWith("Window")) { 513 this._name = this.shortenWindowURL(this._name, false); 514 this._reachableFromWindow = true; 515 } else if (node.canBeQueried) 516 this._reachableFromWindow = true; 517 if (node.detachedDOMTreeNode) 518 this.detachedDOMTreeNode = true; 519 520 var snapshot = dataGrid.snapshot; 521 var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0; 522 var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0; 523 this.data = { 524 "distance": this._toUIDistance(this._distance), 525 "shallowSize": Number.withThousandsSeparator(this._shallowSize), 526 "retainedSize": Number.withThousandsSeparator(this._retainedSize), 527 "shallowSize-percent": this._toPercentString(shallowSizePercent), 528 "retainedSize-percent": this._toPercentString(retainedSizePercent) 529 }; 530}; 531 532WebInspector.HeapSnapshotGenericObjectNode.prototype = { 533 /** 534 * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}} 535 */ 536 retainersDataSource: function() 537 { 538 return {snapshot: this._dataGrid.snapshot, snapshotNodeIndex: this.snapshotNodeIndex}; 539 }, 540 541 /** 542 * @param {string} columnIdentifier 543 * @return {!Element} 544 */ 545 createCell: function(columnIdentifier) 546 { 547 var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : this._createObjectCell(); 548 if (this._searchMatched) 549 cell.classList.add("highlight"); 550 return cell; 551 }, 552 553 /** 554 * @return {!Element} 555 */ 556 _createObjectCell: function() 557 { 558 var value = this._name; 559 var valueStyle = "object"; 560 switch (this._type) { 561 case "concatenated string": 562 case "string": 563 value = "\"" + value + "\""; 564 valueStyle = "string"; 565 break; 566 case "regexp": 567 value = "/" + value + "/"; 568 valueStyle = "string"; 569 break; 570 case "closure": 571 value = "function" + (value ? " " : "") + value + "()"; 572 valueStyle = "function"; 573 break; 574 case "number": 575 valueStyle = "number"; 576 break; 577 case "hidden": 578 valueStyle = "null"; 579 break; 580 case "array": 581 if (!value) 582 value = "[]"; 583 else 584 value += "[]"; 585 break; 586 }; 587 if (this._reachableFromWindow) 588 valueStyle += " highlight"; 589 if (value === "Object") 590 value = ""; 591 if (this.detachedDOMTreeNode) 592 valueStyle += " detached-dom-tree-node"; 593 return this._createObjectCellWithValue(valueStyle, value); 594 }, 595 596 _createObjectCellWithValue: function(valueStyle, value) 597 { 598 var cell = document.createElement("td"); 599 cell.className = "object-column"; 600 var div = document.createElement("div"); 601 div.className = "source-code event-properties"; 602 div.style.overflow = "visible"; 603 604 this._prefixObjectCell(div); 605 606 var valueSpan = document.createElement("span"); 607 valueSpan.className = "value console-formatted-" + valueStyle; 608 valueSpan.textContent = value; 609 div.appendChild(valueSpan); 610 611 var idSpan = document.createElement("span"); 612 idSpan.className = "console-formatted-id"; 613 idSpan.textContent = " @" + this.snapshotNodeId; 614 div.appendChild(idSpan); 615 616 cell.appendChild(div); 617 cell.classList.add("disclosure"); 618 if (this.depth) 619 cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px"); 620 cell.heapSnapshotNode = this; 621 return cell; 622 }, 623 624 _prefixObjectCell: function(div) 625 { 626 }, 627 628 queryObjectContent: function(callback, objectGroupName) 629 { 630 /** 631 * @param {?Protocol.Error} error 632 * @param {!RuntimeAgent.RemoteObject} object 633 */ 634 function formatResult(error, object) 635 { 636 if (!error && object.type) 637 callback(WebInspector.runtimeModel.createRemoteObject(object), !!error); 638 else 639 callback(WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(WebInspector.UIString("Preview is not available"))); 640 } 641 642 if (this._type === "string") 643 callback(WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(this._name)); 644 else 645 HeapProfilerAgent.getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult); 646 }, 647 648 updateHasChildren: function() 649 { 650 /** 651 * @this {WebInspector.HeapSnapshotGenericObjectNode} 652 */ 653 function isEmptyCallback(isEmpty) 654 { 655 this.hasChildren = !isEmpty; 656 } 657 this._provider().isEmpty(isEmptyCallback.bind(this)); 658 }, 659 660 /** 661 * @param {string} fullName 662 * @param {boolean} hasObjectId 663 * @return {string} 664 */ 665 shortenWindowURL: function(fullName, hasObjectId) 666 { 667 var startPos = fullName.indexOf("/"); 668 var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length; 669 if (startPos !== -1 && endPos !== -1) { 670 var fullURL = fullName.substring(startPos + 1, endPos).trimLeft(); 671 var url = fullURL.trimURL(); 672 if (url.length > 40) 673 url = url.trimMiddle(40); 674 return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos); 675 } else 676 return fullName; 677 }, 678 679 __proto__: WebInspector.HeapSnapshotGridNode.prototype 680} 681 682/** 683 * @constructor 684 * @extends {WebInspector.HeapSnapshotGenericObjectNode} 685 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 686 * @param {!WebInspector.HeapSnapshotProxy} snapshot 687 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 688 * @param {?WebInspector.HeapSnapshotObjectNode} parentObjectNode 689 */ 690WebInspector.HeapSnapshotObjectNode = function(dataGrid, snapshot, edge, parentObjectNode) 691{ 692 WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, edge.node); 693 this._referenceName = edge.name; 694 this._referenceType = edge.type; 695 this._edgeIndex = edge.edgeIndex; 696 this._snapshot = snapshot; 697 698 this._parentObjectNode = parentObjectNode; 699 this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId(); 700 if (!this._cycledWithAncestorGridNode) 701 this.updateHasChildren(); 702 703 var data = this.data; 704 data["count"] = ""; 705 data["addedCount"] = ""; 706 data["removedCount"] = ""; 707 data["countDelta"] = ""; 708 data["addedSize"] = ""; 709 data["removedSize"] = ""; 710 data["sizeDelta"] = ""; 711} 712 713WebInspector.HeapSnapshotObjectNode.prototype = { 714 /** 715 * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}} 716 */ 717 retainersDataSource: function() 718 { 719 return {snapshot: this._snapshot, snapshotNodeIndex: this.snapshotNodeIndex}; 720 }, 721 722 /** 723 * @return {!WebInspector.HeapSnapshotProviderProxy} 724 */ 725 createProvider: function() 726 { 727 return this._snapshot.createEdgesProvider(this.snapshotNodeIndex); 728 }, 729 730 _findAncestorWithSameSnapshotNodeId: function() 731 { 732 var ancestor = this._parentObjectNode; 733 while (ancestor) { 734 if (ancestor.snapshotNodeId === this.snapshotNodeId) 735 return ancestor; 736 ancestor = ancestor._parentObjectNode; 737 } 738 return null; 739 }, 740 741 /** 742 * @param {!WebInspector.HeapSnapshotCommon.Edge} item 743 * @return {!WebInspector.HeapSnapshotObjectNode} 744 */ 745 _createChildNode: function(item) 746 { 747 return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._snapshot, item, this); 748 }, 749 750 /** 751 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 752 * @return {number} 753 */ 754 _childHashForEntity: function(edge) 755 { 756 return edge.edgeIndex; 757 }, 758 759 /** 760 * @param {!WebInspector.HeapSnapshotObjectNode} childNode 761 * @return {number} 762 */ 763 _childHashForNode: function(childNode) 764 { 765 return childNode._edgeIndex; 766 }, 767 768 /** 769 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 770 */ 771 comparator: function() 772 { 773 var sortAscending = this._dataGrid.isSortOrderAscending(); 774 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 775 var sortFields = { 776 object: ["!edgeName", sortAscending, "retainedSize", false], 777 count: ["!edgeName", true, "retainedSize", false], 778 shallowSize: ["selfSize", sortAscending, "!edgeName", true], 779 retainedSize: ["retainedSize", sortAscending, "!edgeName", true], 780 distance: ["distance", sortAscending, "_name", true] 781 }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false]; 782 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 783 }, 784 785 _prefixObjectCell: function(div) 786 { 787 var name = this._referenceName; 788 if (name === "") name = "(empty)"; 789 var nameClass = "name"; 790 switch (this._referenceType) { 791 case "context": 792 nameClass = "console-formatted-number"; 793 break; 794 case "internal": 795 case "hidden": 796 case "weak": 797 nameClass = "console-formatted-null"; 798 break; 799 case "element": 800 name = "[" + name + "]"; 801 break; 802 } 803 804 if (this._cycledWithAncestorGridNode) 805 div.className += " cycled-ancessor-node"; 806 807 var nameSpan = document.createElement("span"); 808 nameSpan.className = nameClass; 809 nameSpan.textContent = name; 810 div.appendChild(nameSpan); 811 812 var separatorSpan = document.createElement("span"); 813 separatorSpan.className = "grayed"; 814 separatorSpan.textContent = this._edgeNodeSeparator(); 815 div.appendChild(separatorSpan); 816 }, 817 818 /** 819 * @return {string} 820 */ 821 _edgeNodeSeparator: function() 822 { 823 return " :: "; 824 }, 825 826 __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype 827} 828 829/** 830 * @constructor 831 * @extends {WebInspector.HeapSnapshotObjectNode} 832 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 833 * @param {!WebInspector.HeapSnapshotProxy} snapshot 834 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 835 * @param {?WebInspector.HeapSnapshotRetainingObjectNode} parentRetainingObjectNode 836 */ 837WebInspector.HeapSnapshotRetainingObjectNode = function(dataGrid, snapshot, edge, parentRetainingObjectNode) 838{ 839 WebInspector.HeapSnapshotObjectNode.call(this, dataGrid, snapshot, edge, parentRetainingObjectNode); 840} 841 842WebInspector.HeapSnapshotRetainingObjectNode.prototype = { 843 /** 844 * @return {!WebInspector.HeapSnapshotProviderProxy} 845 */ 846 createProvider: function() 847 { 848 return this._snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex); 849 }, 850 851 /** 852 * @param {!WebInspector.HeapSnapshotCommon.Edge} item 853 * @return {!WebInspector.HeapSnapshotRetainingObjectNode} 854 */ 855 _createChildNode: function(item) 856 { 857 return new WebInspector.HeapSnapshotRetainingObjectNode(this._dataGrid, this._snapshot, item, this); 858 }, 859 860 /** 861 * @return {string} 862 */ 863 _edgeNodeSeparator: function() 864 { 865 return " in "; 866 }, 867 868 expand: function() 869 { 870 this._expandRetainersChain(20); 871 }, 872 873 /** 874 * @param {number} maxExpandLevels 875 */ 876 _expandRetainersChain: function(maxExpandLevels) 877 { 878 /** 879 * @this {!WebInspector.HeapSnapshotRetainingObjectNode} 880 */ 881 function populateComplete() 882 { 883 this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); 884 this._expandRetainersChain(maxExpandLevels); 885 } 886 887 if (!this._populated) { 888 this.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); 889 this.populate(); 890 return; 891 } 892 WebInspector.HeapSnapshotGenericObjectNode.prototype.expand.call(this); 893 if (--maxExpandLevels > 0 && this.children.length > 0) { 894 var retainer = this.children[0]; 895 if (retainer._distance > 1) { 896 retainer._expandRetainersChain(maxExpandLevels); 897 return; 898 } 899 } 900 this._dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete); 901 }, 902 903 __proto__: WebInspector.HeapSnapshotObjectNode.prototype 904} 905 906/** 907 * @constructor 908 * @extends {WebInspector.HeapSnapshotGenericObjectNode} 909 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 910 * @param {!WebInspector.HeapSnapshotProxy} snapshot 911 * @param {!WebInspector.HeapSnapshotCommon.Node} node 912 * @param {boolean} isDeletedNode 913 */ 914WebInspector.HeapSnapshotInstanceNode = function(dataGrid, snapshot, node, isDeletedNode) 915{ 916 WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node); 917 this._baseSnapshotOrSnapshot = snapshot; 918 this._isDeletedNode = isDeletedNode; 919 this.updateHasChildren(); 920 921 var data = this.data; 922 data["count"] = ""; 923 data["countDelta"] = ""; 924 data["sizeDelta"] = ""; 925 if (this._isDeletedNode) { 926 data["addedCount"] = ""; 927 data["addedSize"] = ""; 928 data["removedCount"] = "\u2022"; 929 data["removedSize"] = Number.withThousandsSeparator(this._shallowSize); 930 } else { 931 data["addedCount"] = "\u2022"; 932 data["addedSize"] = Number.withThousandsSeparator(this._shallowSize); 933 data["removedCount"] = ""; 934 data["removedSize"] = ""; 935 } 936}; 937 938WebInspector.HeapSnapshotInstanceNode.prototype = { 939 /** 940 * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}} 941 */ 942 retainersDataSource: function() 943 { 944 return {snapshot: this._baseSnapshotOrSnapshot, snapshotNodeIndex: this.snapshotNodeIndex}; 945 }, 946 947 /** 948 * @return {!WebInspector.HeapSnapshotProviderProxy} 949 */ 950 createProvider: function() 951 { 952 return this._baseSnapshotOrSnapshot.createEdgesProvider(this.snapshotNodeIndex); 953 }, 954 955 /** 956 * @param {!WebInspector.HeapSnapshotCommon.Edge} item 957 * @return {!WebInspector.HeapSnapshotObjectNode} 958 */ 959 _createChildNode: function(item) 960 { 961 return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._baseSnapshotOrSnapshot, item, null); 962 }, 963 964 /** 965 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 966 * @return {number} 967 */ 968 _childHashForEntity: function(edge) 969 { 970 return edge.edgeIndex; 971 }, 972 973 /** 974 * @param {!WebInspector.HeapSnapshotObjectNode} childNode 975 * @return {number} 976 */ 977 _childHashForNode: function(childNode) 978 { 979 return childNode._edgeIndex; 980 }, 981 982 /** 983 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 984 */ 985 comparator: function() 986 { 987 var sortAscending = this._dataGrid.isSortOrderAscending(); 988 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 989 var sortFields = { 990 object: ["!edgeName", sortAscending, "retainedSize", false], 991 distance: ["distance", sortAscending, "retainedSize", false], 992 count: ["!edgeName", true, "retainedSize", false], 993 addedSize: ["selfSize", sortAscending, "!edgeName", true], 994 removedSize: ["selfSize", sortAscending, "!edgeName", true], 995 shallowSize: ["selfSize", sortAscending, "!edgeName", true], 996 retainedSize: ["retainedSize", sortAscending, "!edgeName", true] 997 }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false]; 998 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 999 }, 1000 1001 __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype 1002} 1003 1004/** 1005 * @constructor 1006 * @param {!WebInspector.HeapSnapshotConstructorsDataGrid} dataGrid 1007 * @param {string} className 1008 * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate 1009 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 1010 * @extends {WebInspector.HeapSnapshotGridNode} 1011 */ 1012WebInspector.HeapSnapshotConstructorNode = function(dataGrid, className, aggregate, nodeFilter) 1013{ 1014 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, aggregate.count > 0); 1015 this._name = className; 1016 this._nodeFilter = nodeFilter; 1017 this._distance = aggregate.distance; 1018 this._count = aggregate.count; 1019 this._shallowSize = aggregate.self; 1020 this._retainedSize = aggregate.maxRet; 1021 1022 var snapshot = dataGrid.snapshot; 1023 var countPercent = this._count / snapshot.nodeCount * 100.0; 1024 var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0; 1025 var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0; 1026 1027 this.data = { 1028 "object": className, 1029 "count": Number.withThousandsSeparator(this._count), 1030 "distance": this._toUIDistance(this._distance), 1031 "shallowSize": Number.withThousandsSeparator(this._shallowSize), 1032 "retainedSize": Number.withThousandsSeparator(this._retainedSize), 1033 "count-percent": this._toPercentString(countPercent), 1034 "shallowSize-percent": this._toPercentString(shallowSizePercent), 1035 "retainedSize-percent": this._toPercentString(retainedSizePercent) 1036 }; 1037} 1038 1039WebInspector.HeapSnapshotConstructorNode.prototype = { 1040 /** 1041 * @override 1042 * @return {!WebInspector.HeapSnapshotProviderProxy} 1043 */ 1044 createProvider: function() 1045 { 1046 return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._nodeFilter) 1047 }, 1048 1049 /** 1050 * @param {number} snapshotObjectId 1051 * @param {function(boolean)} callback 1052 */ 1053 revealNodeBySnapshotObjectId: function(snapshotObjectId, callback) 1054 { 1055 /** 1056 * @this {WebInspector.HeapSnapshotConstructorNode} 1057 */ 1058 function didExpand() 1059 { 1060 this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this)); 1061 } 1062 1063 /** 1064 * @this {WebInspector.HeapSnapshotConstructorNode} 1065 * @param {number} nodePosition 1066 */ 1067 function didGetNodePosition(nodePosition) 1068 { 1069 if (nodePosition === -1) { 1070 this.collapse(); 1071 callback(false); 1072 } else { 1073 this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition)); 1074 } 1075 } 1076 1077 /** 1078 * @this {WebInspector.HeapSnapshotConstructorNode} 1079 * @param {number} nodePosition 1080 */ 1081 function didPopulateChildren(nodePosition) 1082 { 1083 var child = this.childForPosition(nodePosition); 1084 if (child) { 1085 this._dataGrid.revealTreeNode([this, child]); 1086 this._dataGrid.highlightNode(/** @type {!WebInspector.HeapSnapshotGridNode} */ (child)); 1087 } 1088 callback(!!child); 1089 } 1090 1091 this._dataGrid.resetNameFilter(); 1092 this.expandWithoutPopulate(didExpand.bind(this)); 1093 }, 1094 1095 /** 1096 * @param {string} filterValue 1097 * @return {boolean} 1098 */ 1099 filteredOut: function(filterValue) 1100 { 1101 return this._name.toLowerCase().indexOf(filterValue) === -1; 1102 }, 1103 1104 /** 1105 * @param {string} columnIdentifier 1106 * @return {!Element} 1107 */ 1108 createCell: function(columnIdentifier) 1109 { 1110 var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier); 1111 if (this._searchMatched) 1112 cell.classList.add("highlight"); 1113 return cell; 1114 }, 1115 1116 /** 1117 * @param {!WebInspector.HeapSnapshotCommon.Node} item 1118 * @return {!WebInspector.HeapSnapshotInstanceNode} 1119 */ 1120 _createChildNode: function(item) 1121 { 1122 return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false); 1123 }, 1124 1125 /** 1126 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 1127 */ 1128 comparator: function() 1129 { 1130 var sortAscending = this._dataGrid.isSortOrderAscending(); 1131 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 1132 var sortFields = { 1133 object: ["id", sortAscending, "retainedSize", false], 1134 distance: ["distance", sortAscending, "retainedSize", false], 1135 count: ["id", true, "retainedSize", false], 1136 shallowSize: ["selfSize", sortAscending, "id", true], 1137 retainedSize: ["retainedSize", sortAscending, "id", true] 1138 }[sortColumnIdentifier]; 1139 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 1140 }, 1141 1142 /** 1143 * @param {!WebInspector.HeapSnapshotCommon.Node} node 1144 * @return {number} 1145 */ 1146 _childHashForEntity: function(node) 1147 { 1148 return node.id; 1149 }, 1150 1151 /** 1152 * @param {!WebInspector.HeapSnapshotInstanceNode} childNode 1153 * @return {number} 1154 */ 1155 _childHashForNode: function(childNode) 1156 { 1157 return childNode.snapshotNodeId; 1158 }, 1159 1160 __proto__: WebInspector.HeapSnapshotGridNode.prototype 1161} 1162 1163 1164/** 1165 * @constructor 1166 * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider} 1167 * @param {!WebInspector.HeapSnapshotProviderProxy} addedNodesProvider 1168 * @param {!WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider 1169 * @param {number} addedCount 1170 * @param {number} removedCount 1171 */ 1172WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount) 1173{ 1174 this._addedNodesProvider = addedNodesProvider; 1175 this._deletedNodesProvider = deletedNodesProvider; 1176 this._addedCount = addedCount; 1177 this._removedCount = removedCount; 1178} 1179 1180WebInspector.HeapSnapshotDiffNodesProvider.prototype = { 1181 dispose: function() 1182 { 1183 this._addedNodesProvider.dispose(); 1184 this._deletedNodesProvider.dispose(); 1185 }, 1186 1187 /** 1188 * @override 1189 * @param {number} snapshotObjectId 1190 * @param {function(number)} callback 1191 */ 1192 nodePosition: function(snapshotObjectId, callback) 1193 { 1194 throw new Error("Unreachable"); 1195 }, 1196 1197 /** 1198 * @param {function(boolean)} callback 1199 */ 1200 isEmpty: function(callback) 1201 { 1202 callback(false); 1203 }, 1204 1205 /** 1206 * @param {number} beginPosition 1207 * @param {number} endPosition 1208 * @param {!function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback 1209 */ 1210 serializeItemsRange: function(beginPosition, endPosition, callback) 1211 { 1212 /** 1213 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} items 1214 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1215 */ 1216 function didReceiveAllItems(items) 1217 { 1218 items.totalLength = this._addedCount + this._removedCount; 1219 callback(items); 1220 } 1221 1222 /** 1223 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} addedItems 1224 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange 1225 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1226 */ 1227 function didReceiveDeletedItems(addedItems, itemsRange) 1228 { 1229 var items = itemsRange.items; 1230 if (!addedItems.items.length) 1231 addedItems.startPosition = this._addedCount + itemsRange.startPosition; 1232 for (var i = 0; i < items.length; i++) { 1233 items[i].isAddedNotRemoved = false; 1234 addedItems.items.push(items[i]); 1235 } 1236 addedItems.endPosition = this._addedCount + itemsRange.endPosition; 1237 didReceiveAllItems.call(this, addedItems); 1238 } 1239 1240 /** 1241 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange 1242 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1243 */ 1244 function didReceiveAddedItems(itemsRange) 1245 { 1246 var items = itemsRange.items; 1247 for (var i = 0; i < items.length; i++) 1248 items[i].isAddedNotRemoved = true; 1249 if (itemsRange.endPosition < endPosition) 1250 return this._deletedNodesProvider.serializeItemsRange(0, endPosition - itemsRange.endPosition, didReceiveDeletedItems.bind(this, itemsRange)); 1251 1252 itemsRange.totalLength = this._addedCount + this._removedCount; 1253 didReceiveAllItems.call(this, itemsRange); 1254 } 1255 1256 if (beginPosition < this._addedCount) { 1257 this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this)); 1258 } else { 1259 var emptyRange = new WebInspector.HeapSnapshotCommon.ItemsRange(0, 0, 0, []); 1260 this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, emptyRange)); 1261 } 1262 }, 1263 1264 /** 1265 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 1266 * @param {function()} callback 1267 */ 1268 sortAndRewind: function(comparator, callback) 1269 { 1270 /** 1271 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1272 */ 1273 function afterSort() 1274 { 1275 this._deletedNodesProvider.sortAndRewind(comparator, callback); 1276 } 1277 this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this)); 1278 } 1279}; 1280 1281/** 1282 * @constructor 1283 * @param {!WebInspector.HeapSnapshotDiffDataGrid} dataGrid 1284 * @param {string} className 1285 * @param {!WebInspector.HeapSnapshotCommon.DiffForClass} diffForClass 1286 * @extends {WebInspector.HeapSnapshotGridNode} 1287 */ 1288WebInspector.HeapSnapshotDiffNode = function(dataGrid, className, diffForClass) 1289{ 1290 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, true); 1291 this._name = className; 1292 this._addedCount = diffForClass.addedCount; 1293 this._removedCount = diffForClass.removedCount; 1294 this._countDelta = diffForClass.countDelta; 1295 this._addedSize = diffForClass.addedSize; 1296 this._removedSize = diffForClass.removedSize; 1297 this._sizeDelta = diffForClass.sizeDelta; 1298 this._deletedIndexes = diffForClass.deletedIndexes; 1299 this.data = { 1300 "object": className, 1301 "addedCount": Number.withThousandsSeparator(this._addedCount), 1302 "removedCount": Number.withThousandsSeparator(this._removedCount), 1303 "countDelta": this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta)), 1304 "addedSize": Number.withThousandsSeparator(this._addedSize), 1305 "removedSize": Number.withThousandsSeparator(this._removedSize), 1306 "sizeDelta": this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta)) 1307 }; 1308} 1309 1310WebInspector.HeapSnapshotDiffNode.prototype = { 1311 /** 1312 * @override 1313 * @return {!WebInspector.HeapSnapshotDiffNodesProvider} 1314 */ 1315 createProvider: function() 1316 { 1317 var tree = this._dataGrid; 1318 return new WebInspector.HeapSnapshotDiffNodesProvider( 1319 tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name), 1320 tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes), 1321 this._addedCount, 1322 this._removedCount); 1323 }, 1324 1325 /** 1326 * @param {string} columnIdentifier 1327 * @return {!Element} 1328 */ 1329 createCell: function(columnIdentifier) 1330 { 1331 var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier); 1332 if (columnIdentifier !== "object") 1333 cell.classList.add("numeric-column"); 1334 return cell; 1335 }, 1336 1337 /** 1338 * @param {!WebInspector.HeapSnapshotCommon.Node} item 1339 * @return {!WebInspector.HeapSnapshotInstanceNode} 1340 */ 1341 _createChildNode: function(item) 1342 { 1343 if (item.isAddedNotRemoved) 1344 return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false); 1345 else 1346 return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, item, true); 1347 }, 1348 1349 /** 1350 * @param {!WebInspector.HeapSnapshotCommon.Node} node 1351 * @return {number} 1352 */ 1353 _childHashForEntity: function(node) 1354 { 1355 return node.id; 1356 }, 1357 1358 /** 1359 * @param {!WebInspector.HeapSnapshotInstanceNode} childNode 1360 * @return {number} 1361 */ 1362 _childHashForNode: function(childNode) 1363 { 1364 return childNode.snapshotNodeId; 1365 }, 1366 1367 /** 1368 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 1369 */ 1370 comparator: function() 1371 { 1372 var sortAscending = this._dataGrid.isSortOrderAscending(); 1373 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 1374 var sortFields = { 1375 object: ["id", sortAscending, "selfSize", false], 1376 addedCount: ["selfSize", sortAscending, "id", true], 1377 removedCount: ["selfSize", sortAscending, "id", true], 1378 countDelta: ["selfSize", sortAscending, "id", true], 1379 addedSize: ["selfSize", sortAscending, "id", true], 1380 removedSize: ["selfSize", sortAscending, "id", true], 1381 sizeDelta: ["selfSize", sortAscending, "id", true] 1382 }[sortColumnIdentifier]; 1383 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 1384 }, 1385 1386 /** 1387 * @param {string} filterValue 1388 * @return {boolean} 1389 */ 1390 filteredOut: function(filterValue) 1391 { 1392 return this._name.toLowerCase().indexOf(filterValue) === -1; 1393 }, 1394 1395 _signForDelta: function(delta) 1396 { 1397 if (delta === 0) 1398 return ""; 1399 if (delta > 0) 1400 return "+"; 1401 else 1402 return "\u2212"; // Math minus sign, same width as plus. 1403 }, 1404 1405 __proto__: WebInspector.HeapSnapshotGridNode.prototype 1406} 1407 1408 1409/** 1410 * @constructor 1411 * @extends {WebInspector.HeapSnapshotGenericObjectNode} 1412 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 1413 * @param {!WebInspector.HeapSnapshotCommon.Node} node 1414 */ 1415WebInspector.HeapSnapshotDominatorObjectNode = function(dataGrid, node) 1416{ 1417 WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node); 1418 this.updateHasChildren(); 1419}; 1420 1421WebInspector.HeapSnapshotDominatorObjectNode.prototype = { 1422 /** 1423 * @override 1424 * @return {!WebInspector.HeapSnapshotProviderProxy} 1425 */ 1426 createProvider: function() 1427 { 1428 return this._dataGrid.snapshot.createNodesProviderForDominator(this.snapshotNodeIndex); 1429 }, 1430 1431 /** 1432 * @param {number} snapshotObjectId 1433 * @param {function(?WebInspector.DataGridNode)} callback 1434 */ 1435 retrieveChildBySnapshotObjectId: function(snapshotObjectId, callback) 1436 { 1437 /** 1438 * @this {WebInspector.HeapSnapshotDominatorObjectNode} 1439 */ 1440 function didExpand() 1441 { 1442 this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this)); 1443 } 1444 1445 /** 1446 * @this {WebInspector.HeapSnapshotDominatorObjectNode} 1447 */ 1448 function didGetNodePosition(nodePosition) 1449 { 1450 if (nodePosition === -1) { 1451 this.collapse(); 1452 callback(null); 1453 } else 1454 this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition)); 1455 } 1456 1457 /** 1458 * @this {WebInspector.HeapSnapshotDominatorObjectNode} 1459 */ 1460 function didPopulateChildren(nodePosition) 1461 { 1462 var child = this.childForPosition(nodePosition); 1463 callback(child); 1464 } 1465 1466 // Make sure hasChildren flag is updated before expanding this node as updateHasChildren response 1467 // may not have been received yet. 1468 this.hasChildren = true; 1469 this.expandWithoutPopulate(didExpand.bind(this)); 1470 }, 1471 1472 _createChildNode: function(item) 1473 { 1474 return new WebInspector.HeapSnapshotDominatorObjectNode(this._dataGrid, item); 1475 }, 1476 1477 /** 1478 * @param {!WebInspector.HeapSnapshotCommon.Node} node 1479 * @return {number} 1480 */ 1481 _childHashForEntity: function(node) 1482 { 1483 return node.id; 1484 }, 1485 1486 /** 1487 * @param {!WebInspector.HeapSnapshotDominatorObjectNode} childNode 1488 * @return {number} 1489 */ 1490 _childHashForNode: function(childNode) 1491 { 1492 return childNode.snapshotNodeId; 1493 }, 1494 1495 /** 1496 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 1497 */ 1498 comparator: function() 1499 { 1500 var sortAscending = this._dataGrid.isSortOrderAscending(); 1501 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 1502 var sortFields = { 1503 object: ["id", sortAscending, "retainedSize", false], 1504 shallowSize: ["selfSize", sortAscending, "id", true], 1505 retainedSize: ["retainedSize", sortAscending, "id", true] 1506 }[sortColumnIdentifier]; 1507 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 1508 }, 1509 1510 __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype 1511} 1512 1513 1514/** 1515 * @constructor 1516 * @extends {WebInspector.HeapSnapshotGridNode} 1517 * @param {!WebInspector.AllocationDataGrid} dataGrid 1518 * @param {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode} data 1519 */ 1520WebInspector.AllocationGridNode = function(dataGrid, data) 1521{ 1522 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, data.hasChildren); 1523 this._populated = false; 1524 this._allocationNode = data; 1525 this.data = { 1526 "liveCount": Number.withThousandsSeparator(data.liveCount), 1527 "count": Number.withThousandsSeparator(data.count), 1528 "liveSize": Number.withThousandsSeparator(data.liveSize), 1529 "size": Number.withThousandsSeparator(data.size), 1530 "name": data.name 1531 }; 1532} 1533 1534WebInspector.AllocationGridNode.prototype = { 1535 populate: function() 1536 { 1537 if (this._populated) 1538 return; 1539 this._populated = true; 1540 this._dataGrid.snapshot.allocationNodeCallers(this._allocationNode.id, didReceiveCallers.bind(this)); 1541 1542 /** 1543 * @param {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} callers 1544 * @this {WebInspector.AllocationGridNode} 1545 */ 1546 function didReceiveCallers(callers) 1547 { 1548 var callersChain = callers.nodesWithSingleCaller; 1549 var parentNode = this; 1550 var dataGrid = /** @type {!WebInspector.AllocationDataGrid} */ (this._dataGrid); 1551 for (var i = 0; i < callersChain.length; i++) { 1552 var child = new WebInspector.AllocationGridNode(dataGrid, callersChain[i]); 1553 dataGrid.appendNode(parentNode, child); 1554 parentNode = child; 1555 parentNode._populated = true; 1556 if (this.expanded) 1557 parentNode.expand(); 1558 } 1559 1560 var callersBranch = callers.branchingCallers; 1561 callersBranch.sort(this._dataGrid._createComparator()); 1562 for (var i = 0; i < callersBranch.length; i++) 1563 dataGrid.appendNode(parentNode, new WebInspector.AllocationGridNode(dataGrid, callersBranch[i])); 1564 dataGrid.updateVisibleNodes(true); 1565 } 1566 }, 1567 1568 /** 1569 * @override 1570 */ 1571 expand: function() 1572 { 1573 WebInspector.HeapSnapshotGridNode.prototype.expand.call(this); 1574 if (this.children.length === 1) 1575 this.children[0].expand(); 1576 }, 1577 1578 /** 1579 * @override 1580 * @param {string} columnIdentifier 1581 * @return {!Element} 1582 */ 1583 createCell: function(columnIdentifier) 1584 { 1585 if (columnIdentifier !== "name") 1586 return this._createValueCell(columnIdentifier); 1587 1588 var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier); 1589 var allocationNode = this._allocationNode; 1590 if (allocationNode.scriptId) { 1591 var urlElement; 1592 var linkifier = this._dataGrid._linkifier; 1593 var script = WebInspector.debuggerModel.scriptForId(String(allocationNode.scriptId)); 1594 if (script) { 1595 var rawLocation = WebInspector.debuggerModel.createRawLocation(script, allocationNode.line - 1, allocationNode.column - 1); 1596 urlElement = linkifier.linkifyRawLocation(rawLocation, "profile-node-file"); 1597 } else { 1598 var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget()); 1599 urlElement = linkifier.linkifyLocation(target, allocationNode.scriptName, allocationNode.line - 1, allocationNode.column - 1, "profile-node-file"); 1600 } 1601 urlElement.style.maxWidth = "75%"; 1602 cell.insertBefore(urlElement, cell.firstChild); 1603 } 1604 return cell; 1605 }, 1606 1607 /** 1608 * @return {number} 1609 */ 1610 allocationNodeId: function() 1611 { 1612 return this._allocationNode.id; 1613 }, 1614 1615 __proto__: WebInspector.HeapSnapshotGridNode.prototype 1616} 1617