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