Home | History | Annotate | Download | only in profiler
      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