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 */ 37 WebInspector.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 56 WebInspector.HeapSnapshotGridNode.Events = { 57 PopulateComplete: "PopulateComplete" 58 } 59 60 /** 61 * @param {!Array.<string>} fieldNames 62 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 63 */ 64 WebInspector.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 */ 73 WebInspector.HeapSnapshotGridNode.ChildrenProvider = function() { } 74 75 WebInspector.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 104 WebInspector.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 */ 497 WebInspector.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 532 WebInspector.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 /** 629 * @param {!WebInspector.Target} target 630 * @param {!function(!WebInspector.RemoteObject)} callback 631 * @param {string} objectGroupName 632 */ 633 queryObjectContent: function(target, callback, objectGroupName) 634 { 635 /** 636 * @param {?Protocol.Error} error 637 * @param {!RuntimeAgent.RemoteObject} object 638 */ 639 function formatResult(error, object) 640 { 641 if (!error && object.type) 642 callback(target.runtimeModel.createRemoteObject(object)); 643 else 644 callback(target.runtimeModel.createRemoteObjectFromPrimitiveValue(WebInspector.UIString("Preview is not available"))); 645 } 646 647 if (this._type === "string") 648 callback(target.runtimeModel.createRemoteObjectFromPrimitiveValue(this._name)); 649 else 650 target.heapProfilerAgent().getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult); 651 }, 652 653 updateHasChildren: function() 654 { 655 /** 656 * @this {WebInspector.HeapSnapshotGenericObjectNode} 657 */ 658 function isEmptyCallback(isEmpty) 659 { 660 this.hasChildren = !isEmpty; 661 } 662 this._provider().isEmpty(isEmptyCallback.bind(this)); 663 }, 664 665 /** 666 * @param {string} fullName 667 * @param {boolean} hasObjectId 668 * @return {string} 669 */ 670 shortenWindowURL: function(fullName, hasObjectId) 671 { 672 var startPos = fullName.indexOf("/"); 673 var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length; 674 if (startPos !== -1 && endPos !== -1) { 675 var fullURL = fullName.substring(startPos + 1, endPos).trimLeft(); 676 var url = fullURL.trimURL(); 677 if (url.length > 40) 678 url = url.trimMiddle(40); 679 return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos); 680 } else 681 return fullName; 682 }, 683 684 __proto__: WebInspector.HeapSnapshotGridNode.prototype 685 } 686 687 /** 688 * @constructor 689 * @extends {WebInspector.HeapSnapshotGenericObjectNode} 690 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 691 * @param {!WebInspector.HeapSnapshotProxy} snapshot 692 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 693 * @param {?WebInspector.HeapSnapshotObjectNode} parentObjectNode 694 */ 695 WebInspector.HeapSnapshotObjectNode = function(dataGrid, snapshot, edge, parentObjectNode) 696 { 697 WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, edge.node); 698 this._referenceName = edge.name; 699 this._referenceType = edge.type; 700 this._edgeIndex = edge.edgeIndex; 701 this._snapshot = snapshot; 702 703 this._parentObjectNode = parentObjectNode; 704 this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId(); 705 if (!this._cycledWithAncestorGridNode) 706 this.updateHasChildren(); 707 708 var data = this.data; 709 data["count"] = ""; 710 data["addedCount"] = ""; 711 data["removedCount"] = ""; 712 data["countDelta"] = ""; 713 data["addedSize"] = ""; 714 data["removedSize"] = ""; 715 data["sizeDelta"] = ""; 716 } 717 718 WebInspector.HeapSnapshotObjectNode.prototype = { 719 /** 720 * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}} 721 */ 722 retainersDataSource: function() 723 { 724 return {snapshot: this._snapshot, snapshotNodeIndex: this.snapshotNodeIndex}; 725 }, 726 727 /** 728 * @return {!WebInspector.HeapSnapshotProviderProxy} 729 */ 730 createProvider: function() 731 { 732 return this._snapshot.createEdgesProvider(this.snapshotNodeIndex); 733 }, 734 735 _findAncestorWithSameSnapshotNodeId: function() 736 { 737 var ancestor = this._parentObjectNode; 738 while (ancestor) { 739 if (ancestor.snapshotNodeId === this.snapshotNodeId) 740 return ancestor; 741 ancestor = ancestor._parentObjectNode; 742 } 743 return null; 744 }, 745 746 /** 747 * @param {!WebInspector.HeapSnapshotCommon.Edge} item 748 * @return {!WebInspector.HeapSnapshotObjectNode} 749 */ 750 _createChildNode: function(item) 751 { 752 return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._snapshot, item, this); 753 }, 754 755 /** 756 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 757 * @return {number} 758 */ 759 _childHashForEntity: function(edge) 760 { 761 return edge.edgeIndex; 762 }, 763 764 /** 765 * @param {!WebInspector.HeapSnapshotObjectNode} childNode 766 * @return {number} 767 */ 768 _childHashForNode: function(childNode) 769 { 770 return childNode._edgeIndex; 771 }, 772 773 /** 774 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 775 */ 776 comparator: function() 777 { 778 var sortAscending = this._dataGrid.isSortOrderAscending(); 779 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 780 var sortFields = { 781 object: ["!edgeName", sortAscending, "retainedSize", false], 782 count: ["!edgeName", true, "retainedSize", false], 783 shallowSize: ["selfSize", sortAscending, "!edgeName", true], 784 retainedSize: ["retainedSize", sortAscending, "!edgeName", true], 785 distance: ["distance", sortAscending, "_name", true] 786 }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false]; 787 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 788 }, 789 790 _prefixObjectCell: function(div) 791 { 792 var name = this._referenceName; 793 if (name === "") name = "(empty)"; 794 var nameClass = "name"; 795 switch (this._referenceType) { 796 case "context": 797 nameClass = "console-formatted-number"; 798 break; 799 case "internal": 800 case "hidden": 801 case "weak": 802 nameClass = "console-formatted-null"; 803 break; 804 case "element": 805 name = "[" + name + "]"; 806 break; 807 } 808 809 if (this._cycledWithAncestorGridNode) 810 div.className += " cycled-ancessor-node"; 811 812 var nameSpan = document.createElement("span"); 813 nameSpan.className = nameClass; 814 nameSpan.textContent = name; 815 div.appendChild(nameSpan); 816 817 var separatorSpan = document.createElement("span"); 818 separatorSpan.className = "grayed"; 819 separatorSpan.textContent = this._edgeNodeSeparator(); 820 div.appendChild(separatorSpan); 821 }, 822 823 /** 824 * @return {string} 825 */ 826 _edgeNodeSeparator: function() 827 { 828 return " :: "; 829 }, 830 831 __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype 832 } 833 834 /** 835 * @constructor 836 * @extends {WebInspector.HeapSnapshotObjectNode} 837 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 838 * @param {!WebInspector.HeapSnapshotProxy} snapshot 839 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 840 * @param {?WebInspector.HeapSnapshotRetainingObjectNode} parentRetainingObjectNode 841 */ 842 WebInspector.HeapSnapshotRetainingObjectNode = function(dataGrid, snapshot, edge, parentRetainingObjectNode) 843 { 844 WebInspector.HeapSnapshotObjectNode.call(this, dataGrid, snapshot, edge, parentRetainingObjectNode); 845 } 846 847 WebInspector.HeapSnapshotRetainingObjectNode.prototype = { 848 /** 849 * @return {!WebInspector.HeapSnapshotProviderProxy} 850 */ 851 createProvider: function() 852 { 853 return this._snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex); 854 }, 855 856 /** 857 * @param {!WebInspector.HeapSnapshotCommon.Edge} item 858 * @return {!WebInspector.HeapSnapshotRetainingObjectNode} 859 */ 860 _createChildNode: function(item) 861 { 862 return new WebInspector.HeapSnapshotRetainingObjectNode(this._dataGrid, this._snapshot, item, this); 863 }, 864 865 /** 866 * @return {string} 867 */ 868 _edgeNodeSeparator: function() 869 { 870 return " in "; 871 }, 872 873 expand: function() 874 { 875 this._expandRetainersChain(20); 876 }, 877 878 /** 879 * @param {number} maxExpandLevels 880 */ 881 _expandRetainersChain: function(maxExpandLevels) 882 { 883 /** 884 * @this {!WebInspector.HeapSnapshotRetainingObjectNode} 885 */ 886 function populateComplete() 887 { 888 this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); 889 this._expandRetainersChain(maxExpandLevels); 890 } 891 892 if (!this._populated) { 893 this.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); 894 this.populate(); 895 return; 896 } 897 WebInspector.HeapSnapshotGenericObjectNode.prototype.expand.call(this); 898 if (--maxExpandLevels > 0 && this.children.length > 0) { 899 var retainer = this.children[0]; 900 if (retainer._distance > 1) { 901 retainer._expandRetainersChain(maxExpandLevels); 902 return; 903 } 904 } 905 this._dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete); 906 }, 907 908 __proto__: WebInspector.HeapSnapshotObjectNode.prototype 909 } 910 911 /** 912 * @constructor 913 * @extends {WebInspector.HeapSnapshotGenericObjectNode} 914 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid 915 * @param {!WebInspector.HeapSnapshotProxy} snapshot 916 * @param {!WebInspector.HeapSnapshotCommon.Node} node 917 * @param {boolean} isDeletedNode 918 */ 919 WebInspector.HeapSnapshotInstanceNode = function(dataGrid, snapshot, node, isDeletedNode) 920 { 921 WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node); 922 this._baseSnapshotOrSnapshot = snapshot; 923 this._isDeletedNode = isDeletedNode; 924 this.updateHasChildren(); 925 926 var data = this.data; 927 data["count"] = ""; 928 data["countDelta"] = ""; 929 data["sizeDelta"] = ""; 930 if (this._isDeletedNode) { 931 data["addedCount"] = ""; 932 data["addedSize"] = ""; 933 data["removedCount"] = "\u2022"; 934 data["removedSize"] = Number.withThousandsSeparator(this._shallowSize); 935 } else { 936 data["addedCount"] = "\u2022"; 937 data["addedSize"] = Number.withThousandsSeparator(this._shallowSize); 938 data["removedCount"] = ""; 939 data["removedSize"] = ""; 940 } 941 }; 942 943 WebInspector.HeapSnapshotInstanceNode.prototype = { 944 /** 945 * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}} 946 */ 947 retainersDataSource: function() 948 { 949 return {snapshot: this._baseSnapshotOrSnapshot, snapshotNodeIndex: this.snapshotNodeIndex}; 950 }, 951 952 /** 953 * @return {!WebInspector.HeapSnapshotProviderProxy} 954 */ 955 createProvider: function() 956 { 957 return this._baseSnapshotOrSnapshot.createEdgesProvider(this.snapshotNodeIndex); 958 }, 959 960 /** 961 * @param {!WebInspector.HeapSnapshotCommon.Edge} item 962 * @return {!WebInspector.HeapSnapshotObjectNode} 963 */ 964 _createChildNode: function(item) 965 { 966 return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._baseSnapshotOrSnapshot, item, null); 967 }, 968 969 /** 970 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge 971 * @return {number} 972 */ 973 _childHashForEntity: function(edge) 974 { 975 return edge.edgeIndex; 976 }, 977 978 /** 979 * @param {!WebInspector.HeapSnapshotObjectNode} childNode 980 * @return {number} 981 */ 982 _childHashForNode: function(childNode) 983 { 984 return childNode._edgeIndex; 985 }, 986 987 /** 988 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 989 */ 990 comparator: function() 991 { 992 var sortAscending = this._dataGrid.isSortOrderAscending(); 993 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 994 var sortFields = { 995 object: ["!edgeName", sortAscending, "retainedSize", false], 996 distance: ["distance", sortAscending, "retainedSize", false], 997 count: ["!edgeName", true, "retainedSize", false], 998 addedSize: ["selfSize", sortAscending, "!edgeName", true], 999 removedSize: ["selfSize", sortAscending, "!edgeName", true], 1000 shallowSize: ["selfSize", sortAscending, "!edgeName", true], 1001 retainedSize: ["retainedSize", sortAscending, "!edgeName", true] 1002 }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false]; 1003 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 1004 }, 1005 1006 __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype 1007 } 1008 1009 /** 1010 * @constructor 1011 * @param {!WebInspector.HeapSnapshotConstructorsDataGrid} dataGrid 1012 * @param {string} className 1013 * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate 1014 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 1015 * @extends {WebInspector.HeapSnapshotGridNode} 1016 */ 1017 WebInspector.HeapSnapshotConstructorNode = function(dataGrid, className, aggregate, nodeFilter) 1018 { 1019 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, aggregate.count > 0); 1020 this._name = className; 1021 this._nodeFilter = nodeFilter; 1022 this._distance = aggregate.distance; 1023 this._count = aggregate.count; 1024 this._shallowSize = aggregate.self; 1025 this._retainedSize = aggregate.maxRet; 1026 1027 var snapshot = dataGrid.snapshot; 1028 var countPercent = this._count / snapshot.nodeCount * 100.0; 1029 var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0; 1030 var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0; 1031 1032 this.data = { 1033 "object": className, 1034 "count": Number.withThousandsSeparator(this._count), 1035 "distance": this._toUIDistance(this._distance), 1036 "shallowSize": Number.withThousandsSeparator(this._shallowSize), 1037 "retainedSize": Number.withThousandsSeparator(this._retainedSize), 1038 "count-percent": this._toPercentString(countPercent), 1039 "shallowSize-percent": this._toPercentString(shallowSizePercent), 1040 "retainedSize-percent": this._toPercentString(retainedSizePercent) 1041 }; 1042 } 1043 1044 WebInspector.HeapSnapshotConstructorNode.prototype = { 1045 /** 1046 * @override 1047 * @return {!WebInspector.HeapSnapshotProviderProxy} 1048 */ 1049 createProvider: function() 1050 { 1051 return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._nodeFilter) 1052 }, 1053 1054 /** 1055 * @param {number} snapshotObjectId 1056 * @param {function(boolean)} callback 1057 */ 1058 revealNodeBySnapshotObjectId: function(snapshotObjectId, callback) 1059 { 1060 /** 1061 * @this {WebInspector.HeapSnapshotConstructorNode} 1062 */ 1063 function didExpand() 1064 { 1065 this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this)); 1066 } 1067 1068 /** 1069 * @this {WebInspector.HeapSnapshotConstructorNode} 1070 * @param {number} nodePosition 1071 */ 1072 function didGetNodePosition(nodePosition) 1073 { 1074 if (nodePosition === -1) { 1075 this.collapse(); 1076 callback(false); 1077 } else { 1078 this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition)); 1079 } 1080 } 1081 1082 /** 1083 * @this {WebInspector.HeapSnapshotConstructorNode} 1084 * @param {number} nodePosition 1085 */ 1086 function didPopulateChildren(nodePosition) 1087 { 1088 var child = this.childForPosition(nodePosition); 1089 if (child) { 1090 this._dataGrid.revealTreeNode([this, child]); 1091 this._dataGrid.highlightNode(/** @type {!WebInspector.HeapSnapshotGridNode} */ (child)); 1092 } 1093 callback(!!child); 1094 } 1095 1096 this._dataGrid.resetNameFilter(); 1097 this.expandWithoutPopulate(didExpand.bind(this)); 1098 }, 1099 1100 /** 1101 * @param {string} filterValue 1102 * @return {boolean} 1103 */ 1104 filteredOut: function(filterValue) 1105 { 1106 return this._name.toLowerCase().indexOf(filterValue) === -1; 1107 }, 1108 1109 /** 1110 * @param {string} columnIdentifier 1111 * @return {!Element} 1112 */ 1113 createCell: function(columnIdentifier) 1114 { 1115 var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier); 1116 if (this._searchMatched) 1117 cell.classList.add("highlight"); 1118 return cell; 1119 }, 1120 1121 /** 1122 * @param {!WebInspector.HeapSnapshotCommon.Node} item 1123 * @return {!WebInspector.HeapSnapshotInstanceNode} 1124 */ 1125 _createChildNode: function(item) 1126 { 1127 return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false); 1128 }, 1129 1130 /** 1131 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 1132 */ 1133 comparator: function() 1134 { 1135 var sortAscending = this._dataGrid.isSortOrderAscending(); 1136 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 1137 var sortFields = { 1138 object: ["id", sortAscending, "retainedSize", false], 1139 distance: ["distance", sortAscending, "retainedSize", false], 1140 count: ["id", true, "retainedSize", false], 1141 shallowSize: ["selfSize", sortAscending, "id", true], 1142 retainedSize: ["retainedSize", sortAscending, "id", true] 1143 }[sortColumnIdentifier]; 1144 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 1145 }, 1146 1147 /** 1148 * @param {!WebInspector.HeapSnapshotCommon.Node} node 1149 * @return {number} 1150 */ 1151 _childHashForEntity: function(node) 1152 { 1153 return node.id; 1154 }, 1155 1156 /** 1157 * @param {!WebInspector.HeapSnapshotInstanceNode} childNode 1158 * @return {number} 1159 */ 1160 _childHashForNode: function(childNode) 1161 { 1162 return childNode.snapshotNodeId; 1163 }, 1164 1165 __proto__: WebInspector.HeapSnapshotGridNode.prototype 1166 } 1167 1168 1169 /** 1170 * @constructor 1171 * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider} 1172 * @param {!WebInspector.HeapSnapshotProviderProxy} addedNodesProvider 1173 * @param {!WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider 1174 * @param {number} addedCount 1175 * @param {number} removedCount 1176 */ 1177 WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount) 1178 { 1179 this._addedNodesProvider = addedNodesProvider; 1180 this._deletedNodesProvider = deletedNodesProvider; 1181 this._addedCount = addedCount; 1182 this._removedCount = removedCount; 1183 } 1184 1185 WebInspector.HeapSnapshotDiffNodesProvider.prototype = { 1186 dispose: function() 1187 { 1188 this._addedNodesProvider.dispose(); 1189 this._deletedNodesProvider.dispose(); 1190 }, 1191 1192 /** 1193 * @override 1194 * @param {number} snapshotObjectId 1195 * @param {function(number)} callback 1196 */ 1197 nodePosition: function(snapshotObjectId, callback) 1198 { 1199 throw new Error("Unreachable"); 1200 }, 1201 1202 /** 1203 * @param {function(boolean)} callback 1204 */ 1205 isEmpty: function(callback) 1206 { 1207 callback(false); 1208 }, 1209 1210 /** 1211 * @param {number} beginPosition 1212 * @param {number} endPosition 1213 * @param {!function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback 1214 */ 1215 serializeItemsRange: function(beginPosition, endPosition, callback) 1216 { 1217 /** 1218 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} items 1219 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1220 */ 1221 function didReceiveAllItems(items) 1222 { 1223 items.totalLength = this._addedCount + this._removedCount; 1224 callback(items); 1225 } 1226 1227 /** 1228 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} addedItems 1229 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange 1230 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1231 */ 1232 function didReceiveDeletedItems(addedItems, itemsRange) 1233 { 1234 var items = itemsRange.items; 1235 if (!addedItems.items.length) 1236 addedItems.startPosition = this._addedCount + itemsRange.startPosition; 1237 for (var i = 0; i < items.length; i++) { 1238 items[i].isAddedNotRemoved = false; 1239 addedItems.items.push(items[i]); 1240 } 1241 addedItems.endPosition = this._addedCount + itemsRange.endPosition; 1242 didReceiveAllItems.call(this, addedItems); 1243 } 1244 1245 /** 1246 * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange 1247 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1248 */ 1249 function didReceiveAddedItems(itemsRange) 1250 { 1251 var items = itemsRange.items; 1252 for (var i = 0; i < items.length; i++) 1253 items[i].isAddedNotRemoved = true; 1254 if (itemsRange.endPosition < endPosition) 1255 return this._deletedNodesProvider.serializeItemsRange(0, endPosition - itemsRange.endPosition, didReceiveDeletedItems.bind(this, itemsRange)); 1256 1257 itemsRange.totalLength = this._addedCount + this._removedCount; 1258 didReceiveAllItems.call(this, itemsRange); 1259 } 1260 1261 if (beginPosition < this._addedCount) { 1262 this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this)); 1263 } else { 1264 var emptyRange = new WebInspector.HeapSnapshotCommon.ItemsRange(0, 0, 0, []); 1265 this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, emptyRange)); 1266 } 1267 }, 1268 1269 /** 1270 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 1271 * @param {function()} callback 1272 */ 1273 sortAndRewind: function(comparator, callback) 1274 { 1275 /** 1276 * @this {WebInspector.HeapSnapshotDiffNodesProvider} 1277 */ 1278 function afterSort() 1279 { 1280 this._deletedNodesProvider.sortAndRewind(comparator, callback); 1281 } 1282 this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this)); 1283 } 1284 }; 1285 1286 /** 1287 * @constructor 1288 * @param {!WebInspector.HeapSnapshotDiffDataGrid} dataGrid 1289 * @param {string} className 1290 * @param {!WebInspector.HeapSnapshotCommon.DiffForClass} diffForClass 1291 * @extends {WebInspector.HeapSnapshotGridNode} 1292 */ 1293 WebInspector.HeapSnapshotDiffNode = function(dataGrid, className, diffForClass) 1294 { 1295 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, true); 1296 this._name = className; 1297 this._addedCount = diffForClass.addedCount; 1298 this._removedCount = diffForClass.removedCount; 1299 this._countDelta = diffForClass.countDelta; 1300 this._addedSize = diffForClass.addedSize; 1301 this._removedSize = diffForClass.removedSize; 1302 this._sizeDelta = diffForClass.sizeDelta; 1303 this._deletedIndexes = diffForClass.deletedIndexes; 1304 this.data = { 1305 "object": className, 1306 "addedCount": Number.withThousandsSeparator(this._addedCount), 1307 "removedCount": Number.withThousandsSeparator(this._removedCount), 1308 "countDelta": this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta)), 1309 "addedSize": Number.withThousandsSeparator(this._addedSize), 1310 "removedSize": Number.withThousandsSeparator(this._removedSize), 1311 "sizeDelta": this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta)) 1312 }; 1313 } 1314 1315 WebInspector.HeapSnapshotDiffNode.prototype = { 1316 /** 1317 * @override 1318 * @return {!WebInspector.HeapSnapshotDiffNodesProvider} 1319 */ 1320 createProvider: function() 1321 { 1322 var tree = this._dataGrid; 1323 return new WebInspector.HeapSnapshotDiffNodesProvider( 1324 tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name), 1325 tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes), 1326 this._addedCount, 1327 this._removedCount); 1328 }, 1329 1330 /** 1331 * @param {string} columnIdentifier 1332 * @return {!Element} 1333 */ 1334 createCell: function(columnIdentifier) 1335 { 1336 var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier); 1337 if (columnIdentifier !== "object") 1338 cell.classList.add("numeric-column"); 1339 return cell; 1340 }, 1341 1342 /** 1343 * @param {!WebInspector.HeapSnapshotCommon.Node} item 1344 * @return {!WebInspector.HeapSnapshotInstanceNode} 1345 */ 1346 _createChildNode: function(item) 1347 { 1348 if (item.isAddedNotRemoved) 1349 return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false); 1350 else 1351 return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, item, true); 1352 }, 1353 1354 /** 1355 * @param {!WebInspector.HeapSnapshotCommon.Node} node 1356 * @return {number} 1357 */ 1358 _childHashForEntity: function(node) 1359 { 1360 return node.id; 1361 }, 1362 1363 /** 1364 * @param {!WebInspector.HeapSnapshotInstanceNode} childNode 1365 * @return {number} 1366 */ 1367 _childHashForNode: function(childNode) 1368 { 1369 return childNode.snapshotNodeId; 1370 }, 1371 1372 /** 1373 * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig} 1374 */ 1375 comparator: function() 1376 { 1377 var sortAscending = this._dataGrid.isSortOrderAscending(); 1378 var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier(); 1379 var sortFields = { 1380 object: ["id", sortAscending, "selfSize", false], 1381 addedCount: ["selfSize", sortAscending, "id", true], 1382 removedCount: ["selfSize", sortAscending, "id", true], 1383 countDelta: ["selfSize", sortAscending, "id", true], 1384 addedSize: ["selfSize", sortAscending, "id", true], 1385 removedSize: ["selfSize", sortAscending, "id", true], 1386 sizeDelta: ["selfSize", sortAscending, "id", true] 1387 }[sortColumnIdentifier]; 1388 return WebInspector.HeapSnapshotGridNode.createComparator(sortFields); 1389 }, 1390 1391 /** 1392 * @param {string} filterValue 1393 * @return {boolean} 1394 */ 1395 filteredOut: function(filterValue) 1396 { 1397 return this._name.toLowerCase().indexOf(filterValue) === -1; 1398 }, 1399 1400 _signForDelta: function(delta) 1401 { 1402 if (delta === 0) 1403 return ""; 1404 if (delta > 0) 1405 return "+"; 1406 else 1407 return "\u2212"; // Math minus sign, same width as plus. 1408 }, 1409 1410 __proto__: WebInspector.HeapSnapshotGridNode.prototype 1411 } 1412 1413 1414 /** 1415 * @constructor 1416 * @extends {WebInspector.HeapSnapshotGridNode} 1417 * @param {!WebInspector.AllocationDataGrid} dataGrid 1418 * @param {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode} data 1419 */ 1420 WebInspector.AllocationGridNode = function(dataGrid, data) 1421 { 1422 WebInspector.HeapSnapshotGridNode.call(this, dataGrid, data.hasChildren); 1423 this._populated = false; 1424 this._allocationNode = data; 1425 this.data = { 1426 "liveCount": Number.withThousandsSeparator(data.liveCount), 1427 "count": Number.withThousandsSeparator(data.count), 1428 "liveSize": Number.withThousandsSeparator(data.liveSize), 1429 "size": Number.withThousandsSeparator(data.size), 1430 "name": data.name 1431 }; 1432 } 1433 1434 WebInspector.AllocationGridNode.prototype = { 1435 populate: function() 1436 { 1437 if (this._populated) 1438 return; 1439 this._populated = true; 1440 this._dataGrid.snapshot.allocationNodeCallers(this._allocationNode.id, didReceiveCallers.bind(this)); 1441 1442 /** 1443 * @param {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} callers 1444 * @this {WebInspector.AllocationGridNode} 1445 */ 1446 function didReceiveCallers(callers) 1447 { 1448 var callersChain = callers.nodesWithSingleCaller; 1449 var parentNode = this; 1450 var dataGrid = /** @type {!WebInspector.AllocationDataGrid} */ (this._dataGrid); 1451 for (var i = 0; i < callersChain.length; i++) { 1452 var child = new WebInspector.AllocationGridNode(dataGrid, callersChain[i]); 1453 dataGrid.appendNode(parentNode, child); 1454 parentNode = child; 1455 parentNode._populated = true; 1456 if (this.expanded) 1457 parentNode.expand(); 1458 } 1459 1460 var callersBranch = callers.branchingCallers; 1461 callersBranch.sort(this._dataGrid._createComparator()); 1462 for (var i = 0; i < callersBranch.length; i++) 1463 dataGrid.appendNode(parentNode, new WebInspector.AllocationGridNode(dataGrid, callersBranch[i])); 1464 dataGrid.updateVisibleNodes(true); 1465 } 1466 }, 1467 1468 /** 1469 * @override 1470 */ 1471 expand: function() 1472 { 1473 WebInspector.HeapSnapshotGridNode.prototype.expand.call(this); 1474 if (this.children.length === 1) 1475 this.children[0].expand(); 1476 }, 1477 1478 /** 1479 * @override 1480 * @param {string} columnIdentifier 1481 * @return {!Element} 1482 */ 1483 createCell: function(columnIdentifier) 1484 { 1485 if (columnIdentifier !== "name") 1486 return this._createValueCell(columnIdentifier); 1487 1488 var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier); 1489 var allocationNode = this._allocationNode; 1490 var target = this._dataGrid.target(); 1491 if (allocationNode.scriptId) { 1492 var linkifier = this._dataGrid._linkifier; 1493 var urlElement = linkifier.linkifyScriptLocation(target, String(allocationNode.scriptId), allocationNode.scriptName, allocationNode.line - 1, allocationNode.column - 1, "profile-node-file"); 1494 urlElement.style.maxWidth = "75%"; 1495 cell.insertBefore(urlElement, cell.firstChild); 1496 } 1497 return cell; 1498 }, 1499 1500 /** 1501 * @return {number} 1502 */ 1503 allocationNodeId: function() 1504 { 1505 return this._allocationNode.id; 1506 }, 1507 1508 __proto__: WebInspector.HeapSnapshotGridNode.prototype 1509 } 1510