Home | History | Annotate | Download | only in front-end
      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 WebInspector.HeapSnapshotGridNode = function(tree, hasChildren)
     32 {
     33     WebInspector.DataGridNode.call(this, null, hasChildren);
     34     this._defaultPopulateCount = tree._defaultPopulateCount;
     35     this._provider = null;
     36     this.addEventListener("populate", this._populate, this);
     37 }
     38 
     39 WebInspector.HeapSnapshotGridNode.prototype = {
     40     createCell: function(columnIdentifier)
     41     {
     42         var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
     43         if (this._searchMatched)
     44             cell.addStyleClass("highlight");
     45         return cell;
     46     },
     47 
     48     _populate: function(event)
     49     {
     50         WebInspector.PleaseWaitMessage.prototype.startAction(this.dataGrid.element, doPopulate.bind(this));
     51 
     52         function doPopulate()
     53         {
     54             this.removeEventListener("populate", this._populate, this);
     55             function sorted(ignored)
     56             {
     57                 this.populateChildren();
     58             }
     59             this._provider.sortAndRewind(this.comparator(), sorted.bind(this));
     60         }
     61     },
     62 
     63     populateChildren: function(provider, howMany, atIndex, afterPopulate, suppressNotifyAboutCompletion)
     64     {
     65         if (!howMany && provider) {
     66             howMany = provider.instanceCount;
     67             provider.instanceCount = 0;
     68         }
     69         provider = provider || this._provider;
     70         if (!("instanceCount" in provider))
     71             provider.instanceCount = 0;
     72         howMany = howMany || this._defaultPopulateCount;
     73         atIndex = atIndex || this.children.length;
     74         var haveSavedChildren = !!this._savedChildren;
     75         if (haveSavedChildren) {
     76             haveSavedChildren = false;
     77             for (var c in this._savedChildren) {
     78                 haveSavedChildren = true;
     79                 break;
     80             }
     81         }
     82 
     83         function childrenRetrieved(items, hasNext, length)
     84         {
     85             for (var i = 0, l = items.length; i < l; ++i) {
     86                 var item = items[i];
     87                 if (haveSavedChildren) {
     88                     var hash = this._childHashForEntity(item);
     89                     if (hash in this._savedChildren) {
     90                         this.insertChild(this._savedChildren[hash], atIndex++);
     91                         continue;
     92                     }
     93                 }
     94                 this.insertChild(this._createChildNode(item, provider), atIndex++);
     95             }
     96             provider.instanceCount += items.length;
     97 
     98             if (hasNext)
     99                 this.insertChild(new WebInspector.ShowMoreDataGridNode(this.populateChildren.bind(this, provider), this._defaultPopulateCount, length), atIndex++);
    100             if (afterPopulate)
    101                 afterPopulate();
    102             if (!suppressNotifyAboutCompletion) {
    103                 function notify()
    104                 {
    105                     this.dispatchEventToListeners("populate complete");
    106                 }
    107                 setTimeout(notify.bind(this), 0);
    108             }
    109         }
    110         provider.getNextItems(howMany, childrenRetrieved.bind(this));
    111     },
    112 
    113     _saveChildren: function()
    114     {
    115         this._savedChildren = {};
    116         for (var i = 0, childrenCount = this.children.length; i < childrenCount; ++i) {
    117             var child = this.children[i];
    118             if (child.expanded)
    119                 this._savedChildren[this._childHashForNode(child)] = child;
    120         }
    121     },
    122 
    123     sort: function()
    124     {
    125         function doSort()
    126         {
    127             function afterSort(sorted)
    128             {
    129                 if (!sorted)
    130                     return;
    131                 this._saveChildren();
    132                 this.removeChildren();
    133 
    134                 function afterPopulate()
    135                 {
    136                     for (var i = 0, l = this.children.length; i < l; ++i) {
    137                         var child = this.children[i];
    138                         if (child.expanded)
    139                             child.sort();
    140                     }
    141                     this.dataGrid.dispatchEventToListeners("sorting complete");
    142                 }
    143                 this.populateChildren(this._provider, null, null, afterPopulate.bind(this));
    144             }
    145             this._provider.sortAndRewind(this.comparator(), afterSort.bind(this));
    146         }
    147         this.dataGrid.dispatchEventToListeners("start sorting");
    148         WebInspector.PleaseWaitMessage.prototype.startAction(this.dataGrid.element, doSort.bind(this));
    149     }
    150 };
    151 
    152 WebInspector.HeapSnapshotGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
    153 
    154 WebInspector.HeapSnapshotGenericObjectNode = function(tree, node)
    155 {
    156     WebInspector.HeapSnapshotGridNode.call(this, tree, false);
    157     this._name = node.name;
    158     this._type = node.type;
    159     this._shallowSize = node.selfSize;
    160     this._retainedSize = node.retainedSize;
    161     this.snapshotNodeId = node.id;
    162     this.snapshotNodeIndex = node.nodeIndex;
    163 };
    164 
    165 WebInspector.HeapSnapshotGenericObjectNode.prototype = {
    166     createCell: function(columnIdentifier)
    167     {
    168         var cell = columnIdentifier !== "object" ? WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier) : this._createObjectCell();
    169         if (this._searchMatched)
    170             cell.addStyleClass("highlight");
    171         return cell;
    172     },
    173 
    174     _createObjectCell: function()
    175     {
    176         var cell = document.createElement("td");
    177         cell.className = "object-column";
    178         var div = document.createElement("div");
    179         div.className = "source-code event-properties";
    180         div.style.overflow = "hidden";
    181         var data = this.data["object"];
    182         if (this._prefixObjectCell)
    183             this._prefixObjectCell(div, data);
    184         var valueSpan = document.createElement("span");
    185         valueSpan.className = "value console-formatted-" + data.valueStyle;
    186         valueSpan.textContent = data.value;
    187         div.appendChild(valueSpan);
    188         cell.appendChild(div);
    189         cell.addStyleClass("disclosure");
    190         if (this.depth)
    191             cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
    192         return cell;
    193     },
    194 
    195     get _countPercent()
    196     {
    197         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
    198     },
    199 
    200     get data()
    201     {
    202         var data = this._emptyData();
    203 
    204         var value = this._name;
    205         var valueStyle = "object";
    206         switch (this._type) {
    207         case "string":
    208             value = "\"" + value + "\"";
    209             valueStyle = "string";
    210             break;
    211         case "regexp":
    212             value = "/" + value + "/";
    213             valueStyle = "string";
    214             break;
    215         case "closure":
    216             value = "function " + value + "()";
    217             valueStyle = "function";
    218             break;
    219         case "number":
    220             valueStyle = "number";
    221             break;
    222         case "hidden":
    223             valueStyle = "null";
    224             break;
    225         case "array":
    226             value += "[]";
    227             break;
    228         };
    229         data["object"] = { valueStyle: valueStyle, value: value + " @" + this.snapshotNodeId };
    230 
    231         var view = this.dataGrid.snapshotView;
    232         data["shallowSize"] = view.showShallowSizeAsPercent ? WebInspector.UIString("%.2f%%", this._shallowSizePercent) : Number.bytesToString(this._shallowSize);
    233         data["retainedSize"] = view.showRetainedSizeAsPercent ? WebInspector.UIString("%.2f%%", this._retainedSizePercent) : Number.bytesToString(this._retainedSize);
    234 
    235         return this._enhanceData ? this._enhanceData(data) : data;
    236     },
    237 
    238     get _retainedSizePercent()
    239     {
    240         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
    241     },
    242 
    243     get _shallowSizePercent()
    244     {
    245         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
    246     },
    247 
    248     _updateHasChildren: function()
    249     {
    250         function isEmptyCallback(isEmpty)
    251         {
    252             this.hasChildren = !isEmpty;
    253         }
    254         this._provider.isEmpty(isEmptyCallback.bind(this));
    255     }
    256 }
    257 
    258 WebInspector.HeapSnapshotGenericObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
    259 
    260 WebInspector.HeapSnapshotObjectNode = function(tree, edge)
    261 {
    262     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, edge.node);
    263     this._referenceName = edge.name;
    264     this._referenceType = edge.type;
    265     this._provider = this._createProvider(tree.snapshot, edge.nodeIndex);
    266     this._updateHasChildren();
    267 }
    268 
    269 WebInspector.HeapSnapshotObjectNode.prototype = {
    270     _createChildNode: function(item)
    271     {
    272         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, item);
    273     },
    274 
    275     _createProvider: function(snapshot, nodeIndex)
    276     {
    277         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
    278         return snapshot.createEdgesProvider(
    279             nodeIndex,
    280             function(edge) {
    281                 return !edge.isInvisible
    282                     && (showHiddenData || (!edge.isHidden && !edge.node.isHidden));
    283             });
    284     },
    285 
    286     _childHashForEntity: function(edge)
    287     {
    288         return edge.type + "#" + edge.name;
    289     },
    290 
    291     _childHashForNode: function(childNode)
    292     {
    293         return childNode._referenceType + "#" + childNode._referenceName;
    294     },
    295 
    296     comparator: function()
    297     {
    298         var sortAscending = this.dataGrid.sortOrder === "ascending";
    299         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
    300         var sortFields = {
    301             object: ["!edgeName", sortAscending, "retainedSize", false],
    302             count: ["!edgeName", true, "retainedSize", false],
    303             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
    304             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
    305         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
    306         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
    307     },
    308 
    309     _emptyData: function()
    310     {
    311         return {count:"", addedCount: "", removedCount: "", countDelta:"", addedSize: "", removedSize: "", sizeDelta: ""};
    312     },
    313 
    314     _enhanceData: function(data)
    315     {
    316         var name = this._referenceName;
    317         if (name === "") name = "(empty)";
    318         var nameClass = "name";
    319         switch (this._referenceType) {
    320         case "context":
    321             nameClass = "console-formatted-number";
    322             break;
    323         case "internal":
    324         case "hidden":
    325             nameClass = "console-formatted-null";
    326             break;
    327         }
    328         data["object"].nameClass = nameClass;
    329         data["object"].name = name;
    330         return data;
    331     },
    332 
    333     _prefixObjectCell: function(div, data)
    334     {
    335         var nameSpan = document.createElement("span");
    336         nameSpan.className = data.nameClass;
    337         nameSpan.textContent = data.name;
    338         var separatorSpan = document.createElement("span");
    339         separatorSpan.className = "separator";
    340         separatorSpan.textContent = ": ";
    341         div.appendChild(nameSpan);
    342         div.appendChild(separatorSpan);
    343     }
    344 }
    345 
    346 WebInspector.HeapSnapshotObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
    347 
    348 WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node)
    349 {
    350     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
    351     this._isDeletedNode = !!baseSnapshot;
    352     this._provider = this._createProvider(baseSnapshot || snapshot, node.nodeIndex);
    353     this._updateHasChildren();
    354 };
    355 
    356 WebInspector.HeapSnapshotInstanceNode.prototype = {
    357     _createChildNode: function(item)
    358     {
    359         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, item);
    360     },
    361 
    362     _createProvider: function(snapshot, nodeIndex)
    363     {
    364         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
    365         return snapshot.createEdgesProvider(
    366             nodeIndex,
    367             function(edge) {
    368                 return !edge.isInvisible
    369                     && (showHiddenData || (!edge.isHidden && !edge.node.isHidden));
    370             });
    371     },
    372 
    373     _childHashForEntity: function(edge)
    374     {
    375         return edge.type + "#" + edge.name;
    376     },
    377 
    378     _childHashForNode: function(childNode)
    379     {
    380         return childNode._referenceType + "#" + childNode._referenceName;
    381     },
    382 
    383     comparator: function()
    384     {
    385         var sortAscending = this.dataGrid.sortOrder === "ascending";
    386         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
    387         var sortFields = {
    388             object: ["!edgeName", sortAscending, "retainedSize", false],
    389             count: ["!edgeName", true, "retainedSize", false],
    390             addedSize: ["selfSize", sortAscending, "!edgeName", true],
    391             removedSize: ["selfSize", sortAscending, "!edgeName", true],
    392             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
    393             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
    394         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
    395         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
    396     },
    397 
    398     _emptyData: function()
    399     {
    400         return {count:"", countDelta:"", sizeDelta: ""};
    401     },
    402 
    403     _enhanceData: function(data)
    404     {
    405         if (this._isDeletedNode) {
    406             data["addedCount"] = "";
    407             data["addedSize"] = "";
    408             data["removedCount"] = "\u2022";
    409             data["removedSize"] = Number.bytesToString(this._shallowSize);
    410         } else {
    411             data["addedCount"] = "\u2022";
    412             data["addedSize"] = Number.bytesToString(this._shallowSize);
    413             data["removedCount"] = "";
    414             data["removedSize"] = "";
    415         }
    416         return data;
    417     }
    418 }
    419 
    420 WebInspector.HeapSnapshotInstanceNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
    421 
    422 WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate)
    423 {
    424     WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0);
    425     this._name = className;
    426     this._count = aggregate.count;
    427     this._shallowSize = aggregate.self;
    428     this._retainedSize = aggregate.maxRet;
    429     this._provider = this._createNodesProvider(tree.snapshot, aggregate.type, className);
    430 }
    431 
    432 WebInspector.HeapSnapshotConstructorNode.prototype = {
    433     _createChildNode: function(item)
    434     {
    435         return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, this.dataGrid.snapshot, item);
    436     },
    437 
    438     _createNodesProvider: function(snapshot, nodeType, nodeClassName)
    439     {
    440         return snapshot.createNodesProvider(
    441             function (node) {
    442                  return node.type === nodeType
    443                     && (nodeClassName === null || node.className === nodeClassName);
    444             });
    445     },
    446 
    447     comparator: function()
    448     {
    449         var sortAscending = this.dataGrid.sortOrder === "ascending";
    450         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
    451         var sortFields = {
    452             object: ["id", sortAscending, "retainedSize", false],
    453             count: ["id", true, "retainedSize", false],
    454             shallowSize: ["selfSize", sortAscending, "id", true],
    455             retainedSize: ["retainedSize", sortAscending, "id", true]
    456         }[sortColumnIdentifier];
    457         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
    458     },
    459 
    460     _childHashForEntity: function(node)
    461     {
    462         return node.id;
    463     },
    464 
    465     _childHashForNode: function(childNode)
    466     {
    467         return childNode.snapshotNodeId;
    468     },
    469 
    470     get data()
    471     {
    472         var data = {object: this._name, count: this._count};
    473         var view = this.dataGrid.snapshotView;
    474         data["count"] = view.showCountAsPercent ? WebInspector.UIString("%.2f%%", this._countPercent) : this._count;
    475         data["shallowSize"] = view.showShallowSizeAsPercent ? WebInspector.UIString("%.2f%%", this._shallowSizePercent) : Number.bytesToString(this._shallowSize);
    476         data["retainedSize"] = "> " + (view.showRetainedSizeAsPercent ? WebInspector.UIString("%.2f%%", this._retainedSizePercent) : Number.bytesToString(this._retainedSize));
    477         return data;
    478     },
    479 
    480     get _countPercent()
    481     {
    482         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
    483     },
    484 
    485     get _retainedSizePercent()
    486     {
    487         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
    488     },
    489 
    490     get _shallowSizePercent()
    491     {
    492         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
    493     }
    494 };
    495 
    496 WebInspector.HeapSnapshotConstructorNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
    497 
    498 WebInspector.HeapSnapshotIteratorsTuple = function(it1, it2)
    499 {
    500     this._it1 = it1;
    501     this._it2 = it2;
    502 }
    503 
    504 WebInspector.HeapSnapshotIteratorsTuple.prototype = {
    505     sortAndRewind: function(comparator, callback)
    506     {
    507         function afterSort(ignored)
    508         {
    509             this._it2.sortAndRewind(comparator, callback);
    510         }
    511         this._it1.sortAndRewind(comparator, afterSort.bind(this));
    512     }
    513 };
    514 
    515 WebInspector.HeapSnapshotDiffNode = function(tree, className, baseAggregate, aggregate)
    516 {
    517     WebInspector.HeapSnapshotGridNode.call(this, tree, true);
    518     this._name = className;
    519     this._baseIndexes = baseAggregate ? baseAggregate.idxs : [];
    520     this._indexes = aggregate ? aggregate.idxs : [];
    521     this._provider = this._createNodesProvider(tree.baseSnapshot, tree.snapshot, aggregate ? aggregate.type : baseAggregate.type, className);
    522 }
    523 
    524 WebInspector.HeapSnapshotDiffNode.prototype = {
    525     calculateDiff: function(dataGrid, callback)
    526     {
    527         var diff = dataGrid.snapshot.createDiff(this._name);
    528 
    529         function diffCalculated(diffResult)
    530         {
    531             this._diff = diffResult;
    532             this._baseIndexes = null;
    533             this._indexes = null;
    534             callback(this._diff.addedSize === 0 && this._diff.removedSize === 0);
    535         }
    536         function baseSelfSizesReceived(baseSelfSizes)
    537         {
    538             diff.pushBaseSelfSizes(baseSelfSizes);
    539             diff.calculate(diffCalculated.bind(this));
    540         }
    541         function baseIdsReceived(baseIds)
    542         {
    543             diff.pushBaseIds(dataGrid.baseSnapshot.uid, baseIds);
    544             dataGrid.snapshot.pushBaseIds(dataGrid.baseSnapshot.uid, this._name, baseIds);
    545             dataGrid.baseSnapshot.nodeFieldValuesByIndex("selfSize", this._baseIndexes, baseSelfSizesReceived.bind(this));
    546         }
    547         function idsReceived(ids)
    548         {
    549             dataGrid.baseSnapshot.pushBaseIds(dataGrid.snapshot.uid, this._name, ids);
    550         }
    551         dataGrid.baseSnapshot.nodeFieldValuesByIndex("id", this._baseIndexes, baseIdsReceived.bind(this));
    552         dataGrid.snapshot.nodeFieldValuesByIndex("id", this._indexes, idsReceived.bind(this));
    553     },
    554 
    555     _createChildNode: function(item, provider)
    556     {
    557         if (provider === this._provider._it1)
    558             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, provider.snapshot, item);
    559         else
    560             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, provider.snapshot, null, item);
    561     },
    562 
    563     _createNodesProvider: function(baseSnapshot, snapshot, nodeType, nodeClassName)
    564     {
    565         var className = this._name;
    566         return new WebInspector.HeapSnapshotIteratorsTuple(
    567             createProvider(snapshot, baseSnapshot), createProvider(baseSnapshot, snapshot));
    568 
    569         function createProvider(snapshot, otherSnapshot)
    570         {
    571             var otherSnapshotId = otherSnapshot.uid;
    572             var provider = snapshot.createNodesProvider(
    573                 function (node) {
    574                      return node.type === nodeType
    575                          && (nodeClassName === null || node.className === nodeClassName)
    576                          && !this.baseSnapshotHasNode(otherSnapshotId, className, node.id);
    577                 });
    578             provider.snapshot = snapshot;
    579             return provider;
    580         }
    581     },
    582 
    583     _childHashForEntity: function(node)
    584     {
    585         return node.id;
    586     },
    587 
    588     _childHashForNode: function(childNode)
    589     {
    590         return childNode.snapshotNodeId;
    591     },
    592 
    593     comparator: function()
    594     {
    595         var sortAscending = this.dataGrid.sortOrder === "ascending";
    596         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
    597         var sortFields = {
    598             object: ["id", sortAscending, "selfSize", false],
    599             addedCount: ["selfSize", sortAscending, "id", true],
    600             removedCount: ["selfSize", sortAscending, "id", true],
    601             countDelta: ["selfSize", sortAscending, "id", true],
    602             addedSize: ["selfSize", sortAscending, "id", true],
    603             removedSize: ["selfSize", sortAscending, "id", true],
    604             sizeDelta: ["selfSize", sortAscending, "id", true]
    605         }[sortColumnIdentifier];
    606         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
    607     },
    608 
    609     populateChildren: function(provider, howMany, atIndex, afterPopulate)
    610     {
    611         if (!provider && !howMany) {
    612             var firstProviderPopulated = function()
    613             {
    614                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, this._defaultPopulateCount, atIndex, afterPopulate);
    615             };
    616             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, this._defaultPopulateCount, atIndex, firstProviderPopulated.bind(this), true);
    617         } else if (!howMany) {
    618             var firstProviderPopulated = function()
    619             {
    620                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, null, atIndex, afterPopulate);
    621             };
    622             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, null, atIndex, firstProviderPopulated.bind(this), true);
    623         } else
    624             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, provider, howMany, atIndex, afterPopulate);
    625     },
    626 
    627     _signForDelta: function(delta)
    628     {
    629         if (delta === 0)
    630             return "";
    631         if (delta > 0)
    632             return "+";
    633         else
    634             return "\u2212";  // Math minus sign, same width as plus.
    635     },
    636 
    637     get data()
    638     {
    639         var data = {object: this._name};
    640 
    641         data["addedCount"] = this._diff.addedCount;
    642         data["removedCount"] = this._diff.removedCount;
    643         var countDelta = this._diff.countDelta;
    644         data["countDelta"] = WebInspector.UIString("%s%d", this._signForDelta(countDelta), Math.abs(countDelta));
    645         data["addedSize"] = Number.bytesToString(this._diff.addedSize);
    646         data["removedSize"] = Number.bytesToString(this._diff.removedSize);
    647         var sizeDelta = this._diff.sizeDelta;
    648         data["sizeDelta"] = WebInspector.UIString("%s%s", this._signForDelta(sizeDelta), Number.bytesToString(Math.abs(sizeDelta)));
    649 
    650         return data;
    651     }
    652 };
    653 
    654 WebInspector.HeapSnapshotDiffNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
    655 
    656 WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node)
    657 {
    658     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
    659     this._provider = this._createProvider(tree.snapshot, node.nodeIndex);
    660     this._updateHasChildren();
    661 };
    662 
    663 WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
    664     _createChildNode: function(item)
    665     {
    666         return new WebInspector.HeapSnapshotDominatorObjectNode(this.dataGrid, item);
    667     },
    668 
    669     _createProvider: function(snapshot, nodeIndex)
    670     {
    671         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
    672         return snapshot.createNodesProvider(
    673             function (node) {
    674                  var dominatorIndex = node.dominatorIndex;
    675                  return dominatorIndex === nodeIndex
    676                      && dominatorIndex !== node.nodeIndex
    677                      && (showHiddenData || !node.isHidden);
    678             });
    679     },
    680 
    681     _childHashForEntity: function(node)
    682     {
    683         return node.id;
    684     },
    685 
    686     _childHashForNode: function(childNode)
    687     {
    688         return childNode.snapshotNodeId;
    689     },
    690 
    691     comparator: function()
    692     {
    693         var sortAscending = this.dataGrid.sortOrder === "ascending";
    694         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
    695         var sortFields = {
    696             object: ["id", sortAscending, "retainedSize", false],
    697             shallowSize: ["selfSize", sortAscending, "id", true],
    698             retainedSize: ["retainedSize", sortAscending, "id", true]
    699         }[sortColumnIdentifier];
    700         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
    701     },
    702 
    703     _emptyData: function()
    704     {
    705         return {};
    706     }
    707 };
    708 
    709 WebInspector.HeapSnapshotDominatorObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
    710 
    711 function MixInSnapshotNodeFunctions(sourcePrototype, targetPrototype)
    712 {
    713     targetPrototype._childHashForEntity = sourcePrototype._childHashForEntity;
    714     targetPrototype._childHashForNode = sourcePrototype._childHashForNode;
    715     targetPrototype.comparator = sourcePrototype.comparator;
    716     targetPrototype._createChildNode = sourcePrototype._createChildNode;
    717     targetPrototype._createProvider = sourcePrototype._createProvider;
    718     targetPrototype.populateChildren = sourcePrototype.populateChildren;
    719     targetPrototype._saveChildren = sourcePrototype._saveChildren;
    720     targetPrototype.sort = sourcePrototype.sort;
    721 }
    722