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