Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / 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     _toPercentString: function(num)
188     {
189         return num.toFixed(0) + "\u2009%"; // \u2009 is a thin space.
190     },
191
192     /**
193      * @return {!Array.<!WebInspector.DataGridNode>}
194      */
195     allChildren: function()
196     {
197         return this._dataGrid.allChildren(this);
198     },
199
200     /**
201      * @param {number} index
202      */
203     removeChildByIndex: function(index)
204     {
205         this._dataGrid.removeChildByIndex(this, index);
206     },
207
208     /**
209      * @param {number} nodePosition
210      * @return {?WebInspector.DataGridNode}
211      */
212     childForPosition: function(nodePosition)
213     {
214         var indexOfFirstChildInRange = 0;
215         for (var i = 0; i < this._retrievedChildrenRanges.length; i++) {
216            var range = this._retrievedChildrenRanges[i];
217            if (range.from <= nodePosition && nodePosition < range.to) {
218                var childIndex = indexOfFirstChildInRange + nodePosition - range.from;
219                return this.allChildren()[childIndex];
220            }
221            indexOfFirstChildInRange += range.to - range.from + 1;
222         }
223         return null;
224     },
225
226     /**
227      * @param {string} columnIdentifier
228      * @return {!Element}
229      */
230     _createValueCell: function(columnIdentifier)
231     {
232         var cell = document.createElement("td");
233         cell.className = columnIdentifier + "-column";
234         if (this.dataGrid.snapshot.totalSize !== 0) {
235             var div = document.createElement("div");
236             var valueSpan = document.createElement("span");
237             valueSpan.textContent = this.data[columnIdentifier];
238             div.appendChild(valueSpan);
239             var percentColumn = columnIdentifier + "-percent";
240             if (percentColumn in this.data) {
241                 var percentSpan = document.createElement("span");
242                 percentSpan.className = "percent-column";
243                 percentSpan.textContent = this.data[percentColumn];
244                 div.appendChild(percentSpan);
245                 div.classList.add("heap-snapshot-multiple-values");
246             }
247             cell.appendChild(div);
248         }
249         return cell;
250     },
251
252     populate: function(event)
253     {
254         if (this._populated)
255             return;
256         this._populated = true;
257
258         /**
259          * @this {WebInspector.HeapSnapshotGridNode}
260          */
261         function sorted()
262         {
263             this._populateChildren();
264         }
265         this._provider().sortAndRewind(this.comparator(), sorted.bind(this));
266     },
267
268     expandWithoutPopulate: function(callback)
269     {
270         // Make sure default populate won't take action.
271         this._populated = true;
272         this.expand();
273         this._provider().sortAndRewind(this.comparator(), callback);
274     },
275
276     /**
277      * @param {?number=} fromPosition
278      * @param {?number=} toPosition
279      * @param {function()=} afterPopulate
280      */
281     _populateChildren: function(fromPosition, toPosition, afterPopulate)
282     {
283         fromPosition = fromPosition || 0;
284         toPosition = toPosition || fromPosition + this._dataGrid.defaultPopulateCount();
285         var firstNotSerializedPosition = fromPosition;
286
287         /**
288          * @this {WebInspector.HeapSnapshotGridNode}
289          */
290         function serializeNextChunk()
291         {
292             if (firstNotSerializedPosition >= toPosition)
293                 return;
294             var end = Math.min(firstNotSerializedPosition + this._dataGrid.defaultPopulateCount(), toPosition);
295             this._provider().serializeItemsRange(firstNotSerializedPosition, end, childrenRetrieved.bind(this));
296             firstNotSerializedPosition = end;
297         }
298
299         /**
300          * @this {WebInspector.HeapSnapshotGridNode}
301          */
302         function insertRetrievedChild(item, insertionIndex)
303         {
304             if (this._savedChildren) {
305                 var hash = this._childHashForEntity(item);
306                 if (hash in this._savedChildren) {
307                     this._dataGrid.insertChild(this, this._savedChildren[hash], insertionIndex);
308                     return;
309                 }
310             }
311             this._dataGrid.insertChild(this, this._createChildNode(item), insertionIndex);
312         }
313
314         /**
315          * @this {WebInspector.HeapSnapshotGridNode}
316          */
317         function insertShowMoreButton(from, to, insertionIndex)
318         {
319             var button = new WebInspector.ShowMoreDataGridNode(this._populateChildren.bind(this), from, to, this._dataGrid.defaultPopulateCount());
320             this._dataGrid.insertChild(this, button, insertionIndex);
321         }
322
323         /**
324          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
325          * @this {WebInspector.HeapSnapshotGridNode}
326          */
327         function childrenRetrieved(itemsRange)
328         {
329             var itemIndex = 0;
330             var itemPosition = itemsRange.startPosition;
331             var items = itemsRange.items;
332             var insertionIndex = 0;
333
334             if (!this._retrievedChildrenRanges.length) {
335                 if (itemsRange.startPosition > 0) {
336                     this._retrievedChildrenRanges.push({from: 0, to: 0});
337                     insertShowMoreButton.call(this, 0, itemsRange.startPosition, insertionIndex++);
338                 }
339                 this._retrievedChildrenRanges.push({from: itemsRange.startPosition, to: itemsRange.endPosition});
340                 for (var i = 0, l = items.length; i < l; ++i)
341                     insertRetrievedChild.call(this, items[i], insertionIndex++);
342                 if (itemsRange.endPosition < itemsRange.totalLength)
343                     insertShowMoreButton.call(this, itemsRange.endPosition, itemsRange.totalLength, insertionIndex++);
344             } else {
345                 var rangeIndex = 0;
346                 var found = false;
347                 var range;
348                 while (rangeIndex < this._retrievedChildrenRanges.length) {
349                     range = this._retrievedChildrenRanges[rangeIndex];
350                     if (range.to >= itemPosition) {
351                         found = true;
352                         break;
353                     }
354                     insertionIndex += range.to - range.from;
355                     // Skip the button if there is one.
356                     if (range.to < itemsRange.totalLength)
357                         insertionIndex += 1;
358                     ++rangeIndex;
359                 }
360
361                 if (!found || itemsRange.startPosition < range.from) {
362                     // Update previous button.
363                     this.allChildren()[insertionIndex - 1].setEndPosition(itemsRange.startPosition);
364                     insertShowMoreButton.call(this, itemsRange.startPosition, found ? range.from : itemsRange.totalLength, insertionIndex);
365                     range = {from: itemsRange.startPosition, to: itemsRange.startPosition};
366                     if (!found)
367                         rangeIndex = this._retrievedChildrenRanges.length;
368                     this._retrievedChildrenRanges.splice(rangeIndex, 0, range);
369                 } else {
370                     insertionIndex += itemPosition - range.from;
371                 }
372                 // At this point insertionIndex is always an index before button or between nodes.
373                 // Also it is always true here that range.from <= itemPosition <= range.to
374
375                 // Stretch the range right bound to include all new items.
376                 while (range.to < itemsRange.endPosition) {
377                     // Skip already added nodes.
378                     var skipCount = range.to - itemPosition;
379                     insertionIndex += skipCount;
380                     itemIndex += skipCount;
381                     itemPosition = range.to;
382
383                     // We're at the position before button: ...<?node>x<button>
384                     var nextRange = this._retrievedChildrenRanges[rangeIndex + 1];
385                     var newEndOfRange = nextRange ? nextRange.from : itemsRange.totalLength;
386                     if (newEndOfRange > itemsRange.endPosition)
387                         newEndOfRange = itemsRange.endPosition;
388                     while (itemPosition < newEndOfRange) {
389                         insertRetrievedChild.call(this, items[itemIndex++], insertionIndex++);
390                         ++itemPosition;
391                     }
392                     // Merge with the next range.
393                     if (nextRange && newEndOfRange === nextRange.from) {
394                         range.to = nextRange.to;
395                         // Remove "show next" button if there is one.
396                         this.removeChildByIndex(insertionIndex);
397                         this._retrievedChildrenRanges.splice(rangeIndex + 1, 1);
398                     } else {
399                         range.to = newEndOfRange;
400                         // Remove or update next button.
401                         if (newEndOfRange === itemsRange.totalLength)
402                             this.removeChildByIndex(insertionIndex);
403                         else
404                             this.allChildren()[insertionIndex].setStartPosition(itemsRange.endPosition);
405                     }
406                 }
407             }
408
409             // TODO: fix this.
410             this._instanceCount += items.length;
411             if (firstNotSerializedPosition < toPosition) {
412                 serializeNextChunk.call(this);
413                 return;
414             }
415
416             if (this.expanded)
417                 this._dataGrid.updateVisibleNodes(true);
418             if (afterPopulate)
419                 afterPopulate();
420             this.dispatchEventToListeners(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete);
421         }
422         serializeNextChunk.call(this);
423     },
424
425     _saveChildren: function()
426     {
427         this._savedChildren = null;
428         var children = this.allChildren();
429         for (var i = 0, l = children.length; i < l; ++i) {
430             var child = children[i];
431             if (!child.expanded)
432                 continue;
433             if (!this._savedChildren)
434                 this._savedChildren = {};
435             this._savedChildren[this._childHashForNode(child)] = child;
436         }
437     },
438
439     sort: function()
440     {
441         this._dataGrid.recursiveSortingEnter();
442
443         /**
444          * @this {WebInspector.HeapSnapshotGridNode}
445          */
446         function afterSort()
447         {
448             this._saveChildren();
449             this._dataGrid.removeAllChildren(this);
450             this._retrievedChildrenRanges = [];
451
452             /**
453              * @this {WebInspector.HeapSnapshotGridNode}
454              */
455             function afterPopulate()
456             {
457                 var children = this.allChildren();
458                 for (var i = 0, l = children.length; i < l; ++i) {
459                     var child = children[i];
460                     if (child.expanded)
461                         child.sort();
462                 }
463                 this._dataGrid.recursiveSortingLeave();
464             }
465             var instanceCount = this._instanceCount;
466             this._instanceCount = 0;
467             this._populateChildren(0, instanceCount, afterPopulate.bind(this));
468         }
469
470         this._provider().sortAndRewind(this.comparator(), afterSort.bind(this));
471     },
472
473     __proto__: WebInspector.DataGridNode.prototype
474 }
475
476
477 /**
478  * @constructor
479  * @extends {WebInspector.HeapSnapshotGridNode}
480  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
481  */
482 WebInspector.HeapSnapshotGenericObjectNode = function(dataGrid, node)
483 {
484     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, false);
485     // node is null for DataGrid root nodes.
486     if (!node)
487         return;
488     this._name = node.name;
489     this._type = node.type;
490     this._distance = node.distance;
491     this._shallowSize = node.selfSize;
492     this._retainedSize = node.retainedSize;
493     this.snapshotNodeId = node.id;
494     this.snapshotNodeIndex = node.nodeIndex;
495     if (this._type === "string")
496         this._reachableFromWindow = true;
497     else if (this._type === "object" && this._name.startsWith("Window")) {
498         this._name = this.shortenWindowURL(this._name, false);
499         this._reachableFromWindow = true;
500     } else if (node.canBeQueried)
501         this._reachableFromWindow = true;
502     if (node.detachedDOMTreeNode)
503         this.detachedDOMTreeNode = true;
504
505     var snapshot = dataGrid.snapshot;
506     var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
507     var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
508     this.data = {
509         "distance": this._distance,
510         "shallowSize": Number.withThousandsSeparator(this._shallowSize),
511         "retainedSize": Number.withThousandsSeparator(this._retainedSize),
512         "shallowSize-percent": this._toPercentString(shallowSizePercent),
513         "retainedSize-percent": this._toPercentString(retainedSizePercent)
514     };
515 };
516
517 WebInspector.HeapSnapshotGenericObjectNode.prototype = {
518     /**
519      * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
520      */
521     retainersDataSource: function()
522     {
523         return {snapshot: this._dataGrid.snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
524     },
525
526     /**
527      * @param {string} columnIdentifier
528      * @return {!Element}
529      */
530     createCell: function(columnIdentifier)
531     {
532         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : this._createObjectCell();
533         if (this._searchMatched)
534             cell.classList.add("highlight");
535         return cell;
536     },
537
538     /**
539      * @return {!Element}
540      */
541     _createObjectCell: function()
542     {
543         var value = this._name;
544         var valueStyle = "object";
545         switch (this._type) {
546         case "concatenated string":
547         case "string":
548             value = "\"" + value + "\"";
549             valueStyle = "string";
550             break;
551         case "regexp":
552             value = "/" + value + "/";
553             valueStyle = "string";
554             break;
555         case "closure":
556             value = "function" + (value ? " " : "") + value + "()";
557             valueStyle = "function";
558             break;
559         case "number":
560             valueStyle = "number";
561             break;
562         case "hidden":
563             valueStyle = "null";
564             break;
565         case "array":
566             if (!value)
567                 value = "[]";
568             else
569                 value += "[]";
570             break;
571         };
572         if (this._reachableFromWindow)
573             valueStyle += " highlight";
574         if (value === "Object")
575             value = "";
576         if (this.detachedDOMTreeNode)
577             valueStyle += " detached-dom-tree-node";
578         return this._createObjectCellWithValue(valueStyle, value);
579     },
580
581     _createObjectCellWithValue: function(valueStyle, value)
582     {
583         var cell = document.createElement("td");
584         cell.className = "object-column";
585         var div = document.createElement("div");
586         div.className = "source-code event-properties";
587         div.style.overflow = "visible";
588
589         this._prefixObjectCell(div);
590
591         var valueSpan = document.createElement("span");
592         valueSpan.className = "value console-formatted-" + valueStyle;
593         valueSpan.textContent = value;
594         div.appendChild(valueSpan);
595
596         var idSpan = document.createElement("span");
597         idSpan.className = "console-formatted-id";
598         idSpan.textContent = " @" + this.snapshotNodeId;
599         div.appendChild(idSpan);
600
601         cell.appendChild(div);
602         cell.classList.add("disclosure");
603         if (this.depth)
604             cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
605         cell.heapSnapshotNode = this;
606         return cell;
607     },
608
609     _prefixObjectCell: function(div)
610     {
611     },
612
613     queryObjectContent: function(callback, objectGroupName)
614     {
615         /**
616          * @param {?Protocol.Error} error
617          * @param {!RuntimeAgent.RemoteObject} object
618          */
619         function formatResult(error, object)
620         {
621             if (!error && object.type)
622                 callback(WebInspector.RemoteObject.fromPayload(object), !!error);
623             else
624                 callback(WebInspector.RemoteObject.fromPrimitiveValue(WebInspector.UIString("Preview is not available")));
625         }
626
627         if (this._type === "string")
628             callback(WebInspector.RemoteObject.fromPrimitiveValue(this._name));
629         else
630             HeapProfilerAgent.getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult);
631     },
632
633     updateHasChildren: function()
634     {
635         /**
636          * @this {WebInspector.HeapSnapshotGenericObjectNode}
637          */
638         function isEmptyCallback(isEmpty)
639         {
640             this.hasChildren = !isEmpty;
641         }
642         this._provider().isEmpty(isEmptyCallback.bind(this));
643     },
644
645     /**
646      * @param {string} fullName
647      * @param {boolean} hasObjectId
648      * @return {string}
649      */
650     shortenWindowURL: function(fullName, hasObjectId)
651     {
652         var startPos = fullName.indexOf("/");
653         var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
654         if (startPos !== -1 && endPos !== -1) {
655             var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
656             var url = fullURL.trimURL();
657             if (url.length > 40)
658                 url = url.trimMiddle(40);
659             return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
660         } else
661             return fullName;
662     },
663
664     __proto__: WebInspector.HeapSnapshotGridNode.prototype
665 }
666
667 /**
668  * @constructor
669  * @extends {WebInspector.HeapSnapshotGenericObjectNode}
670  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
671  * @param {!WebInspector.HeapSnapshotProxy} snapshot
672  */
673 WebInspector.HeapSnapshotObjectNode = function(dataGrid, snapshot, edge, parentGridNode)
674 {
675     WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, edge.node);
676     this._referenceName = edge.name;
677     this._referenceType = edge.type;
678     this.showRetainingEdges = dataGrid.showRetainingEdges;
679     this._snapshot = snapshot;
680
681     this._parentGridNode = parentGridNode;
682     this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId();
683     if (!this._cycledWithAncestorGridNode)
684         this.updateHasChildren();
685
686     var data = this.data;
687     data["count"] = "";
688     data["addedCount"] = "";
689     data["removedCount"] = "";
690     data["countDelta"] = "";
691     data["addedSize"] = "";
692     data["removedSize"] = "";
693     data["sizeDelta"] = "";
694 }
695
696 WebInspector.HeapSnapshotObjectNode.prototype = {
697     /**
698      * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
699      */
700     retainersDataSource: function()
701     {
702         return {snapshot: this._snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
703     },
704
705     /**
706      * @return {!WebInspector.HeapSnapshotProviderProxy}
707      */
708     createProvider: function()
709     {
710         var tree = this._dataGrid;
711         if (this.showRetainingEdges)
712             return this._snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex);
713         else
714             return this._snapshot.createEdgesProvider(this.snapshotNodeIndex);
715     },
716
717     _findAncestorWithSameSnapshotNodeId: function()
718     {
719         var ancestor = this._parentGridNode;
720         while (ancestor) {
721             if (ancestor.snapshotNodeId === this.snapshotNodeId)
722                 return ancestor;
723             ancestor = ancestor._parentGridNode;
724         }
725         return null;
726     },
727
728     _createChildNode: function(item)
729     {
730         return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._snapshot, item, this);
731     },
732
733     _childHashForEntity: function(edge)
734     {
735         var prefix = this.showRetainingEdges ? edge.node.id + "#" : "";
736         return prefix + edge.type + "#" + edge.name;
737     },
738
739     _childHashForNode: function(childNode)
740     {
741         var prefix = this.showRetainingEdges ? childNode.snapshotNodeId + "#" : "";
742         return prefix + childNode._referenceType + "#" + childNode._referenceName;
743     },
744
745     /**
746      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
747      */
748     comparator: function()
749     {
750         var sortAscending = this._dataGrid.isSortOrderAscending();
751         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
752         var sortFields = {
753             object: ["!edgeName", sortAscending, "retainedSize", false],
754             count: ["!edgeName", true, "retainedSize", false],
755             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
756             retainedSize: ["retainedSize", sortAscending, "!edgeName", true],
757             distance: ["distance", sortAscending, "_name", true]
758         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
759         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
760     },
761
762     _prefixObjectCell: function(div)
763     {
764         var name = this._referenceName;
765         if (name === "") name = "(empty)";
766         var nameClass = "name";
767         switch (this._referenceType) {
768         case "context":
769             nameClass = "console-formatted-number";
770             break;
771         case "internal":
772         case "hidden":
773         case "weak":
774             nameClass = "console-formatted-null";
775             break;
776         case "element":
777             name = "[" + name + "]";
778             break;
779         }
780
781         if (this._cycledWithAncestorGridNode)
782             div.className += " cycled-ancessor-node";
783
784         var nameSpan = document.createElement("span");
785         nameSpan.className = nameClass;
786         nameSpan.textContent = name;
787         div.appendChild(nameSpan);
788
789         var separatorSpan = document.createElement("span");
790         separatorSpan.className = "grayed";
791         separatorSpan.textContent = this.showRetainingEdges ? " in " : " :: ";
792         div.appendChild(separatorSpan);
793     },
794
795     __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
796 }
797
798 /**
799  * @constructor
800  * @extends {WebInspector.HeapSnapshotGenericObjectNode}
801  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
802  * @param {!WebInspector.HeapSnapshotProxy} snapshot
803  * @param {boolean} isDeletedNode
804  */
805 WebInspector.HeapSnapshotInstanceNode = function(dataGrid, snapshot, node, isDeletedNode)
806 {
807     WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
808     this._baseSnapshotOrSnapshot = snapshot;
809     this._isDeletedNode = isDeletedNode;
810     this.updateHasChildren();
811
812     var data = this.data;
813     data["count"] = "";
814     data["countDelta"] = "";
815     data["sizeDelta"] = "";
816     if (this._isDeletedNode) {
817         data["addedCount"] = "";
818         data["addedSize"] = "";
819         data["removedCount"] = "\u2022";
820         data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
821     } else {
822         data["addedCount"] = "\u2022";
823         data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
824         data["removedCount"] = "";
825         data["removedSize"] = "";
826     }
827 };
828
829 WebInspector.HeapSnapshotInstanceNode.prototype = {
830     /**
831      * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
832      */
833     retainersDataSource: function()
834     {
835         return {snapshot: this._baseSnapshotOrSnapshot, snapshotNodeIndex: this.snapshotNodeIndex};
836     },
837
838     /**
839      * @return {!WebInspector.HeapSnapshotProviderProxy}
840      */
841     createProvider: function()
842     {
843         return this._baseSnapshotOrSnapshot.createEdgesProvider(this.snapshotNodeIndex);
844     },
845
846     _createChildNode: function(item)
847     {
848         return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._baseSnapshotOrSnapshot, item, null);
849     },
850
851     _childHashForEntity: function(edge)
852     {
853         return edge.type + "#" + edge.name;
854     },
855
856     _childHashForNode: function(childNode)
857     {
858         return childNode._referenceType + "#" + childNode._referenceName;
859     },
860
861     /**
862      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
863      */
864     comparator: function()
865     {
866         var sortAscending = this._dataGrid.isSortOrderAscending();
867         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
868         var sortFields = {
869             object: ["!edgeName", sortAscending, "retainedSize", false],
870             distance: ["distance", sortAscending, "retainedSize", false],
871             count: ["!edgeName", true, "retainedSize", false],
872             addedSize: ["selfSize", sortAscending, "!edgeName", true],
873             removedSize: ["selfSize", sortAscending, "!edgeName", true],
874             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
875             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
876         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
877         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
878     },
879
880     __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
881 }
882
883 /**
884  * @constructor
885  * @param {!WebInspector.HeapSnapshotConstructorsDataGrid} dataGrid
886  * @param {string} className
887  * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
888  * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
889  * @extends {WebInspector.HeapSnapshotGridNode}
890  */
891 WebInspector.HeapSnapshotConstructorNode = function(dataGrid, className, aggregate, nodeFilter)
892 {
893     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, aggregate.count > 0);
894     this._name = className;
895     this._nodeFilter = nodeFilter;
896     this._distance = aggregate.distance;
897     this._count = aggregate.count;
898     this._shallowSize = aggregate.self;
899     this._retainedSize = aggregate.maxRet;
900
901     var snapshot = dataGrid.snapshot;
902     var countPercent = this._count / snapshot.nodeCount * 100.0;
903     var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
904     var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
905
906     this.data = {
907         "object": className,
908         "count":  Number.withThousandsSeparator(this._count),
909         "distance":  this._distance,
910         "shallowSize": Number.withThousandsSeparator(this._shallowSize),
911         "retainedSize": Number.withThousandsSeparator(this._retainedSize),
912         "count-percent":  this._toPercentString(countPercent),
913         "shallowSize-percent": this._toPercentString(shallowSizePercent),
914         "retainedSize-percent": this._toPercentString(retainedSizePercent)
915     };
916 }
917
918 WebInspector.HeapSnapshotConstructorNode.prototype = {
919     /**
920      * @override
921      * @return {!WebInspector.HeapSnapshotProviderProxy}
922      */
923     createProvider: function()
924     {
925         return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._nodeFilter)
926     },
927
928     /**
929      * @param {number} snapshotObjectId
930      * @param {function(boolean)} callback
931      */
932     revealNodeBySnapshotObjectId: function(snapshotObjectId, callback)
933     {
934         /**
935          * @this {WebInspector.HeapSnapshotConstructorNode}
936          */
937         function didExpand()
938         {
939             this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
940         }
941
942         /**
943          * @this {WebInspector.HeapSnapshotConstructorNode}
944          * @param {number} nodePosition
945          */
946         function didGetNodePosition(nodePosition)
947         {
948             if (nodePosition === -1) {
949                 this.collapse();
950                 callback(false);
951             } else {
952                 this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
953             }
954         }
955
956         /**
957          * @this {WebInspector.HeapSnapshotConstructorNode}
958          * @param {number} nodePosition
959          */
960         function didPopulateChildren(nodePosition)
961         {
962             var child = this.childForPosition(nodePosition);
963             if (child) {
964                 this._dataGrid.revealTreeNode([this, child]);
965                 this._dataGrid.highlightNode(/** @type {!WebInspector.HeapSnapshotGridNode} */ (child));
966             }
967             callback(!!child);
968         }
969
970         this._dataGrid.resetNameFilter(this.expandWithoutPopulate.bind(this, didExpand.bind(this)));
971     },
972
973     /**
974      * @return {boolean}
975      */
976     filteredOut: function()
977     {
978         return this._name.toLowerCase().indexOf(this._dataGrid._nameFilter) === -1;
979     },
980
981     /**
982      * @param {string} columnIdentifier
983      * @return {!Element}
984      */
985     createCell: function(columnIdentifier)
986     {
987         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
988         if (this._searchMatched)
989             cell.classList.add("highlight");
990         return cell;
991     },
992
993     _createChildNode: function(item)
994     {
995         return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
996     },
997
998     /**
999      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1000      */
1001     comparator: function()
1002     {
1003         var sortAscending = this._dataGrid.isSortOrderAscending();
1004         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1005         var sortFields = {
1006             object: ["id", sortAscending, "retainedSize", false],
1007             distance: ["distance", sortAscending, "retainedSize", false],
1008             count: ["id", true, "retainedSize", false],
1009             shallowSize: ["selfSize", sortAscending, "id", true],
1010             retainedSize: ["retainedSize", sortAscending, "id", true]
1011         }[sortColumnIdentifier];
1012         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1013     },
1014
1015     _childHashForEntity: function(node)
1016     {
1017         return node.id;
1018     },
1019
1020     _childHashForNode: function(childNode)
1021     {
1022         return childNode.snapshotNodeId;
1023     },
1024
1025     __proto__: WebInspector.HeapSnapshotGridNode.prototype
1026 }
1027
1028
1029 /**
1030  * @constructor
1031  * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
1032  * @param {!WebInspector.HeapSnapshotProviderProxy} addedNodesProvider
1033  * @param {!WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider
1034  */
1035 WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount)
1036 {
1037     this._addedNodesProvider = addedNodesProvider;
1038     this._deletedNodesProvider = deletedNodesProvider;
1039     this._addedCount = addedCount;
1040     this._removedCount = removedCount;
1041 }
1042
1043 WebInspector.HeapSnapshotDiffNodesProvider.prototype = {
1044     dispose: function()
1045     {
1046         this._addedNodesProvider.dispose();
1047         this._deletedNodesProvider.dispose();
1048     },
1049
1050     /**
1051      * @override
1052      * @param {number} snapshotObjectId
1053      * @param {function(number)} callback
1054      */
1055     nodePosition: function(snapshotObjectId, callback)
1056     {
1057         throw new Error("Unreachable");
1058     },
1059
1060     /**
1061      * @param {function(boolean)} callback
1062      */
1063     isEmpty: function(callback)
1064     {
1065         callback(false);
1066     },
1067
1068     /**
1069      * @param {number} beginPosition
1070      * @param {number} endPosition
1071      * @param {!function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
1072      */
1073     serializeItemsRange: function(beginPosition, endPosition, callback)
1074     {
1075         /**
1076          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} items
1077          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1078          */
1079         function didReceiveAllItems(items)
1080         {
1081             items.totalLength = this._addedCount + this._removedCount;
1082             callback(items);
1083         }
1084
1085         /**
1086          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} addedItems
1087          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
1088          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1089          */
1090         function didReceiveDeletedItems(addedItems, itemsRange)
1091         {
1092             var items = itemsRange.items;
1093             if (!addedItems.items.length)
1094                 addedItems.startPosition = this._addedCount + itemsRange.startPosition;
1095             for (var i = 0; i < items.length; i++) {
1096                 items[i].isAddedNotRemoved = false;
1097                 addedItems.items.push(items[i]);
1098             }
1099             addedItems.endPosition = this._addedCount + itemsRange.endPosition;
1100             didReceiveAllItems.call(this, addedItems);
1101         }
1102
1103         /**
1104          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
1105          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1106          */
1107         function didReceiveAddedItems(itemsRange)
1108         {
1109             var items = itemsRange.items;
1110             for (var i = 0; i < items.length; i++)
1111                 items[i].isAddedNotRemoved = true;
1112             if (itemsRange.endPosition < endPosition)
1113                 return this._deletedNodesProvider.serializeItemsRange(0, endPosition - itemsRange.endPosition, didReceiveDeletedItems.bind(this, itemsRange));
1114
1115             itemsRange.totalLength = this._addedCount + this._removedCount;
1116             didReceiveAllItems.call(this, itemsRange);
1117         }
1118
1119         if (beginPosition < this._addedCount) {
1120             this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this));
1121         } else {
1122             var emptyRange = new WebInspector.HeapSnapshotCommon.ItemsRange(0, 0, 0, []);
1123             this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, emptyRange));
1124         }
1125     },
1126
1127     /**
1128      * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
1129      * @param {function()} callback
1130      */
1131     sortAndRewind: function(comparator, callback)
1132     {
1133         /**
1134          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1135          */
1136         function afterSort()
1137         {
1138             this._deletedNodesProvider.sortAndRewind(comparator, callback);
1139         }
1140         this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this));
1141     }
1142 };
1143
1144 /**
1145  * @constructor
1146  * @param {!WebInspector.HeapSnapshotDiffDataGrid} dataGrid
1147  * @param {string} className
1148  * @param {!WebInspector.HeapSnapshotCommon.DiffForClass} diffForClass
1149  * @extends {WebInspector.HeapSnapshotGridNode}
1150  */
1151 WebInspector.HeapSnapshotDiffNode = function(dataGrid, className, diffForClass)
1152 {
1153     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, true);
1154     this._name = className;
1155     this._addedCount = diffForClass.addedCount;
1156     this._removedCount = diffForClass.removedCount;
1157     this._countDelta = diffForClass.countDelta;
1158     this._addedSize = diffForClass.addedSize;
1159     this._removedSize = diffForClass.removedSize;
1160     this._sizeDelta = diffForClass.sizeDelta;
1161     this._deletedIndexes = diffForClass.deletedIndexes;
1162     this.data = {
1163         "object": className,
1164         "addedCount": Number.withThousandsSeparator(this._addedCount),
1165         "removedCount": Number.withThousandsSeparator(this._removedCount),
1166         "countDelta":  this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta)),
1167         "addedSize": Number.withThousandsSeparator(this._addedSize),
1168         "removedSize": Number.withThousandsSeparator(this._removedSize),
1169         "sizeDelta": this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta))
1170     };
1171 }
1172
1173 WebInspector.HeapSnapshotDiffNode.prototype = {
1174     /**
1175      * @override
1176      * @return {!WebInspector.HeapSnapshotDiffNodesProvider}
1177      */
1178     createProvider: function()
1179     {
1180         var tree = this._dataGrid;
1181         return new WebInspector.HeapSnapshotDiffNodesProvider(
1182             tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name),
1183             tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes),
1184             this._addedCount,
1185             this._removedCount);
1186     },
1187
1188     _createChildNode: function(item)
1189     {
1190         if (item.isAddedNotRemoved)
1191             return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
1192         else
1193             return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, item, true);
1194     },
1195
1196     _childHashForEntity: function(node)
1197     {
1198         return node.id;
1199     },
1200
1201     _childHashForNode: function(childNode)
1202     {
1203         return childNode.snapshotNodeId;
1204     },
1205
1206     /**
1207      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1208      */
1209     comparator: function()
1210     {
1211         var sortAscending = this._dataGrid.isSortOrderAscending();
1212         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1213         var sortFields = {
1214             object: ["id", sortAscending, "selfSize", false],
1215             addedCount: ["selfSize", sortAscending, "id", true],
1216             removedCount: ["selfSize", sortAscending, "id", true],
1217             countDelta: ["selfSize", sortAscending, "id", true],
1218             addedSize: ["selfSize", sortAscending, "id", true],
1219             removedSize: ["selfSize", sortAscending, "id", true],
1220             sizeDelta: ["selfSize", sortAscending, "id", true]
1221         }[sortColumnIdentifier];
1222         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1223     },
1224
1225     /**
1226      * @return {boolean}
1227      */
1228     filteredOut: function()
1229     {
1230         return this._name.toLowerCase().indexOf(this._dataGrid._nameFilter) === -1;
1231     },
1232
1233     _signForDelta: function(delta)
1234     {
1235         if (delta === 0)
1236             return "";
1237         if (delta > 0)
1238             return "+";
1239         else
1240             return "\u2212";  // Math minus sign, same width as plus.
1241     },
1242
1243     __proto__: WebInspector.HeapSnapshotGridNode.prototype
1244 }
1245
1246
1247 /**
1248  * @constructor
1249  * @extends {WebInspector.HeapSnapshotGenericObjectNode}
1250  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
1251  */
1252 WebInspector.HeapSnapshotDominatorObjectNode = function(dataGrid, node)
1253 {
1254     WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
1255     this.updateHasChildren();
1256 };
1257
1258 WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
1259     /**
1260      * @override
1261      * @return {!WebInspector.HeapSnapshotProviderProxy}
1262      */
1263     createProvider: function()
1264     {
1265         return this._dataGrid.snapshot.createNodesProviderForDominator(this.snapshotNodeIndex);
1266     },
1267
1268     /**
1269      * @param {number} snapshotObjectId
1270      * @param {function(?WebInspector.DataGridNode)} callback
1271      */
1272     retrieveChildBySnapshotObjectId: function(snapshotObjectId, callback)
1273     {
1274         /**
1275          * @this {WebInspector.HeapSnapshotDominatorObjectNode}
1276          */
1277         function didExpand()
1278         {
1279             this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
1280         }
1281
1282         /**
1283          * @this {WebInspector.HeapSnapshotDominatorObjectNode}
1284          */
1285         function didGetNodePosition(nodePosition)
1286         {
1287             if (nodePosition === -1) {
1288                 this.collapse();
1289                 callback(null);
1290             } else
1291                 this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
1292         }
1293
1294         /**
1295          * @this {WebInspector.HeapSnapshotDominatorObjectNode}
1296          */
1297         function didPopulateChildren(nodePosition)
1298         {
1299             var child = this.childForPosition(nodePosition);
1300             callback(child);
1301         }
1302
1303         // Make sure hasChildren flag is updated before expanding this node as updateHasChildren response
1304         // may not have been received yet.
1305         this.hasChildren = true;
1306         this.expandWithoutPopulate(didExpand.bind(this));
1307     },
1308
1309     _createChildNode: function(item)
1310     {
1311         return new WebInspector.HeapSnapshotDominatorObjectNode(this._dataGrid, item);
1312     },
1313
1314     _childHashForEntity: function(node)
1315     {
1316         return node.id;
1317     },
1318
1319     _childHashForNode: function(childNode)
1320     {
1321         return childNode.snapshotNodeId;
1322     },
1323
1324     /**
1325      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1326      */
1327     comparator: function()
1328     {
1329         var sortAscending = this._dataGrid.isSortOrderAscending();
1330         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1331         var sortFields = {
1332             object: ["id", sortAscending, "retainedSize", false],
1333             shallowSize: ["selfSize", sortAscending, "id", true],
1334             retainedSize: ["retainedSize", sortAscending, "id", true]
1335         }[sortColumnIdentifier];
1336         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1337     },
1338
1339     __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
1340 }
1341
1342
1343 /**
1344  * @constructor
1345  * @extends {WebInspector.DataGridNode}
1346  * @param {!WebInspector.AllocationDataGrid} dataGrid
1347  * @param {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode} data
1348  */
1349 WebInspector.AllocationGridNode = function(dataGrid, data)
1350 {
1351     WebInspector.DataGridNode.call(this, data, data.hasChildren);
1352     this._dataGrid = dataGrid;
1353     this._populated = false;
1354 }
1355
1356 WebInspector.AllocationGridNode.prototype = {
1357     populate: function()
1358     {
1359         if (this._populated)
1360             return;
1361         this._populated = true;
1362         this._dataGrid.snapshot.allocationNodeCallers(this.data.id, didReceiveCallers.bind(this));
1363
1364         /**
1365          * @param {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} callers
1366          * @this {WebInspector.AllocationGridNode}
1367          */
1368         function didReceiveCallers(callers)
1369         {
1370             var callersChain = callers.nodesWithSingleCaller;
1371             var parentNode = this;
1372             for (var i = 0; i < callersChain.length; i++) {
1373                 var child = new WebInspector.AllocationGridNode(this._dataGrid, callersChain[i]);
1374                 parentNode.appendChild(child);
1375                 parentNode = child;
1376                 parentNode._populated = true;
1377                 if (this.expanded)
1378                     parentNode.expand();
1379             }
1380
1381             var callersBranch = callers.branchingCallers;
1382             callersBranch.sort(this._dataGrid._createComparator());
1383             for (var i = 0; i < callersBranch.length; i++)
1384                 parentNode.appendChild(new WebInspector.AllocationGridNode(this._dataGrid, callersBranch[i]));
1385         }
1386     },
1387
1388     /**
1389      * @override
1390      */
1391     expand: function()
1392     {
1393         WebInspector.DataGridNode.prototype.expand.call(this);
1394         if (this.children.length === 1)
1395             this.children[0].expand();
1396     },
1397
1398     /**
1399      * @override
1400      * @param {string} columnIdentifier
1401      * @return {!Element}
1402      */
1403     createCell: function(columnIdentifier)
1404     {
1405         var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
1406
1407         if (columnIdentifier !== "name")
1408             return cell;
1409
1410         var functionInfo = this.data;
1411         if (functionInfo.scriptName) {
1412             var urlElement = this._dataGrid._linkifier.linkifyLocation(functionInfo.scriptName, functionInfo.line - 1, functionInfo.column - 1, "profile-node-file");
1413             urlElement.style.maxWidth = "75%";
1414             cell.insertBefore(urlElement, cell.firstChild);
1415         }
1416
1417         return cell;
1418     },
1419
1420     /**
1421      * @return {number}
1422      */
1423     allocationNodeId: function()
1424     {
1425         return this.data.id;
1426     },
1427
1428     __proto__: WebInspector.DataGridNode.prototype
1429 }