Update To 11.40.268.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 = createElement("td");
247         cell.className = "numeric-column";
248         if (this.dataGrid.snapshot.totalSize !== 0) {
249             var div = createElement("div");
250             var valueSpan = 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 = 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
407                     // Merge with the next range.
408                     if (nextRange && newEndOfRange === nextRange.from) {
409                         range.to = nextRange.to;
410                         // Remove "show next" button if there is one.
411                         this.removeChildByIndex(insertionIndex);
412                         this._retrievedChildrenRanges.splice(rangeIndex + 1, 1);
413                     } else {
414                         range.to = newEndOfRange;
415                         // Remove or update next button.
416                         if (newEndOfRange === itemsRange.totalLength)
417                             this.removeChildByIndex(insertionIndex);
418                         else
419                             this.allChildren()[insertionIndex].setStartPosition(itemsRange.endPosition);
420                     }
421                 }
422             }
423
424             // TODO: fix this.
425             this._instanceCount += items.length;
426             if (firstNotSerializedPosition < toPosition) {
427                 serializeNextChunk.call(this);
428                 return;
429             }
430
431             if (this.expanded)
432                 this._dataGrid.updateVisibleNodes(true);
433             if (afterPopulate)
434                 afterPopulate();
435             this.dispatchEventToListeners(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete);
436         }
437         serializeNextChunk.call(this);
438     },
439
440     _saveChildren: function()
441     {
442         this._savedChildren = null;
443         var children = this.allChildren();
444         for (var i = 0, l = children.length; i < l; ++i) {
445             var child = children[i];
446             if (!child.expanded)
447                 continue;
448             if (!this._savedChildren)
449                 this._savedChildren = {};
450             this._savedChildren[this._childHashForNode(child)] = child;
451         }
452     },
453
454     sort: function()
455     {
456         this._dataGrid.recursiveSortingEnter();
457
458         /**
459          * @this {WebInspector.HeapSnapshotGridNode}
460          */
461         function afterSort()
462         {
463             this._saveChildren();
464             this._dataGrid.removeAllChildren(this);
465             this._retrievedChildrenRanges = [];
466
467             /**
468              * @this {WebInspector.HeapSnapshotGridNode}
469              */
470             function afterPopulate()
471             {
472                 var children = this.allChildren();
473                 for (var i = 0, l = children.length; i < l; ++i) {
474                     var child = children[i];
475                     if (child.expanded)
476                         child.sort();
477                 }
478                 this._dataGrid.recursiveSortingLeave();
479             }
480             var instanceCount = this._instanceCount;
481             this._instanceCount = 0;
482             this._populateChildren(0, instanceCount, afterPopulate.bind(this));
483         }
484
485         this._provider().sortAndRewind(this.comparator(), afterSort.bind(this));
486     },
487
488     __proto__: WebInspector.DataGridNode.prototype
489 }
490
491
492 /**
493  * @constructor
494  * @extends {WebInspector.HeapSnapshotGridNode}
495  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
496  * @param {!WebInspector.HeapSnapshotCommon.Node} node
497  */
498 WebInspector.HeapSnapshotGenericObjectNode = function(dataGrid, node)
499 {
500     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, false);
501     // node is null for DataGrid root nodes.
502     if (!node)
503         return;
504     this._name = node.name;
505     this._type = node.type;
506     this._distance = node.distance;
507     this._shallowSize = node.selfSize;
508     this._retainedSize = node.retainedSize;
509     this.snapshotNodeId = node.id;
510     this.snapshotNodeIndex = node.nodeIndex;
511     if (this._type === "string")
512         this._reachableFromWindow = true;
513     else if (this._type === "object" && this._name.startsWith("Window")) {
514         this._name = this.shortenWindowURL(this._name, false);
515         this._reachableFromWindow = true;
516     } else if (node.canBeQueried)
517         this._reachableFromWindow = true;
518     if (node.detachedDOMTreeNode)
519         this.detachedDOMTreeNode = true;
520
521     var snapshot = dataGrid.snapshot;
522     var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
523     var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
524     this.data = {
525         "distance": this._toUIDistance(this._distance),
526         "shallowSize": Number.withThousandsSeparator(this._shallowSize),
527         "retainedSize": Number.withThousandsSeparator(this._retainedSize),
528         "shallowSize-percent": this._toPercentString(shallowSizePercent),
529         "retainedSize-percent": this._toPercentString(retainedSizePercent)
530     };
531 };
532
533 WebInspector.HeapSnapshotGenericObjectNode.prototype = {
534     /**
535      * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
536      */
537     retainersDataSource: function()
538     {
539         return {snapshot: this._dataGrid.snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
540     },
541
542     /**
543      * @param {string} columnIdentifier
544      * @return {!Element}
545      */
546     createCell: function(columnIdentifier)
547     {
548         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : this._createObjectCell();
549         if (this._searchMatched)
550             cell.classList.add("highlight");
551         return cell;
552     },
553
554     /**
555      * @return {!Element}
556      */
557     _createObjectCell: function()
558     {
559         var value = this._name;
560         var valueStyle = "object";
561         switch (this._type) {
562         case "concatenated string":
563         case "string":
564             value = "\"" + value + "\"";
565             valueStyle = "string";
566             break;
567         case "regexp":
568             value = "/" + value + "/";
569             valueStyle = "string";
570             break;
571         case "closure":
572             value = "function" + (value ? " " : "") + value + "()";
573             valueStyle = "function";
574             break;
575         case "number":
576             valueStyle = "number";
577             break;
578         case "hidden":
579             valueStyle = "null";
580             break;
581         case "array":
582             if (!value)
583                 value = "[]";
584             else
585                 value += "[]";
586             break;
587         };
588         if (this._reachableFromWindow)
589             valueStyle += " highlight";
590         if (value === "Object")
591             value = "";
592         if (this.detachedDOMTreeNode)
593             valueStyle += " detached-dom-tree-node";
594         return this._createObjectCellWithValue(valueStyle, value);
595     },
596
597     _createObjectCellWithValue: function(valueStyle, value)
598     {
599         var cell = createElement("td");
600         cell.className = "object-column";
601         var div = createElement("div");
602         div.className = "source-code event-properties";
603         div.style.overflow = "visible";
604
605         this._prefixObjectCell(div);
606
607         var valueSpan = createElement("span");
608         valueSpan.className = "value console-formatted-" + valueStyle;
609         valueSpan.textContent = value;
610         div.appendChild(valueSpan);
611
612         var idSpan = createElement("span");
613         idSpan.className = "console-formatted-id";
614         idSpan.textContent = " @" + this.snapshotNodeId;
615         div.appendChild(idSpan);
616
617         cell.appendChild(div);
618         cell.classList.add("disclosure");
619         if (this.depth)
620             cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
621         cell.heapSnapshotNode = this;
622         return cell;
623     },
624
625     _prefixObjectCell: function(div)
626     {
627     },
628
629     /**
630      * @param {!WebInspector.Target} target
631      * @param {!function(!WebInspector.RemoteObject)} callback
632      * @param {string} objectGroupName
633      */
634     queryObjectContent: function(target, callback, objectGroupName)
635     {
636         /**
637          * @param {?Protocol.Error} error
638          * @param {!RuntimeAgent.RemoteObject} object
639          */
640         function formatResult(error, object)
641         {
642             if (!error && object.type)
643                 callback(target.runtimeModel.createRemoteObject(object));
644             else
645                 callback(target.runtimeModel.createRemoteObjectFromPrimitiveValue(WebInspector.UIString("Preview is not available")));
646         }
647
648         if (this._type === "string")
649             callback(target.runtimeModel.createRemoteObjectFromPrimitiveValue(this._name));
650         else
651             target.heapProfilerAgent().getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult);
652     },
653
654     updateHasChildren: function()
655     {
656         /**
657          * @this {WebInspector.HeapSnapshotGenericObjectNode}
658          */
659         function isEmptyCallback(isEmpty)
660         {
661             this.hasChildren = !isEmpty;
662         }
663         this._provider().isEmpty(isEmptyCallback.bind(this));
664     },
665
666     /**
667      * @param {string} fullName
668      * @param {boolean} hasObjectId
669      * @return {string}
670      */
671     shortenWindowURL: function(fullName, hasObjectId)
672     {
673         var startPos = fullName.indexOf("/");
674         var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
675         if (startPos !== -1 && endPos !== -1) {
676             var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
677             var url = fullURL.trimURL();
678             if (url.length > 40)
679                 url = url.trimMiddle(40);
680             return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
681         } else
682             return fullName;
683     },
684
685     __proto__: WebInspector.HeapSnapshotGridNode.prototype
686 }
687
688 /**
689  * @constructor
690  * @extends {WebInspector.HeapSnapshotGenericObjectNode}
691  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
692  * @param {!WebInspector.HeapSnapshotProxy} snapshot
693  * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
694  * @param {?WebInspector.HeapSnapshotObjectNode} parentObjectNode
695  */
696 WebInspector.HeapSnapshotObjectNode = function(dataGrid, snapshot, edge, parentObjectNode)
697 {
698     WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, edge.node);
699     this._referenceName = edge.name;
700     this._referenceType = edge.type;
701     this._edgeIndex = edge.edgeIndex;
702     this._snapshot = snapshot;
703
704     this._parentObjectNode = parentObjectNode;
705     this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId();
706     if (!this._cycledWithAncestorGridNode)
707         this.updateHasChildren();
708
709     var data = this.data;
710     data["count"] = "";
711     data["addedCount"] = "";
712     data["removedCount"] = "";
713     data["countDelta"] = "";
714     data["addedSize"] = "";
715     data["removedSize"] = "";
716     data["sizeDelta"] = "";
717 }
718
719 WebInspector.HeapSnapshotObjectNode.prototype = {
720     /**
721      * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
722      */
723     retainersDataSource: function()
724     {
725         return {snapshot: this._snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
726     },
727
728     /**
729      * @return {!WebInspector.HeapSnapshotProviderProxy}
730      */
731     createProvider: function()
732     {
733         return this._snapshot.createEdgesProvider(this.snapshotNodeIndex);
734     },
735
736     _findAncestorWithSameSnapshotNodeId: function()
737     {
738         var ancestor = this._parentObjectNode;
739         while (ancestor) {
740             if (ancestor.snapshotNodeId === this.snapshotNodeId)
741                 return ancestor;
742             ancestor = ancestor._parentObjectNode;
743         }
744         return null;
745     },
746
747     /**
748      * @param {!WebInspector.HeapSnapshotCommon.Edge} item
749      * @return {!WebInspector.HeapSnapshotObjectNode}
750      */
751     _createChildNode: function(item)
752     {
753         return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._snapshot, item, this);
754     },
755
756     /**
757      * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
758      * @return {number}
759      */
760     _childHashForEntity: function(edge)
761     {
762         return edge.edgeIndex;
763     },
764
765     /**
766      * @param {!WebInspector.HeapSnapshotObjectNode} childNode
767      * @return {number}
768      */
769     _childHashForNode: function(childNode)
770     {
771         return childNode._edgeIndex;
772     },
773
774     /**
775      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
776      */
777     comparator: function()
778     {
779         var sortAscending = this._dataGrid.isSortOrderAscending();
780         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
781         var sortFields = {
782             object: ["!edgeName", sortAscending, "retainedSize", false],
783             count: ["!edgeName", true, "retainedSize", false],
784             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
785             retainedSize: ["retainedSize", sortAscending, "!edgeName", true],
786             distance: ["distance", sortAscending, "_name", true]
787         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
788         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
789     },
790
791     _prefixObjectCell: function(div)
792     {
793         var name = this._referenceName;
794         if (name === "") name = "(empty)";
795         var nameClass = "name";
796         switch (this._referenceType) {
797         case "context":
798             nameClass = "console-formatted-number";
799             break;
800         case "internal":
801         case "hidden":
802         case "weak":
803             nameClass = "console-formatted-null";
804             break;
805         case "element":
806             name = "[" + name + "]";
807             break;
808         }
809
810         if (this._cycledWithAncestorGridNode)
811             div.className += " cycled-ancessor-node";
812
813         var nameSpan = createElement("span");
814         nameSpan.className = nameClass;
815         nameSpan.textContent = name;
816         div.appendChild(nameSpan);
817
818         var separatorSpan = createElement("span");
819         separatorSpan.className = "grayed";
820         separatorSpan.textContent = this._edgeNodeSeparator();
821         div.appendChild(separatorSpan);
822     },
823
824     /**
825      * @return {string}
826      */
827     _edgeNodeSeparator: function()
828     {
829         return " :: ";
830     },
831
832     __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
833 }
834
835 /**
836  * @constructor
837  * @extends {WebInspector.HeapSnapshotObjectNode}
838  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
839  * @param {!WebInspector.HeapSnapshotProxy} snapshot
840  * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
841  * @param {?WebInspector.HeapSnapshotRetainingObjectNode} parentRetainingObjectNode
842  */
843 WebInspector.HeapSnapshotRetainingObjectNode = function(dataGrid, snapshot, edge, parentRetainingObjectNode)
844 {
845     WebInspector.HeapSnapshotObjectNode.call(this, dataGrid, snapshot, edge, parentRetainingObjectNode);
846 }
847
848 WebInspector.HeapSnapshotRetainingObjectNode.prototype = {
849     /**
850      * @return {!WebInspector.HeapSnapshotProviderProxy}
851      */
852     createProvider: function()
853     {
854         return this._snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex);
855     },
856
857     /**
858      * @param {!WebInspector.HeapSnapshotCommon.Edge} item
859      * @return {!WebInspector.HeapSnapshotRetainingObjectNode}
860      */
861     _createChildNode: function(item)
862     {
863         return new WebInspector.HeapSnapshotRetainingObjectNode(this._dataGrid, this._snapshot, item, this);
864     },
865
866     /**
867      * @return {string}
868      */
869     _edgeNodeSeparator: function()
870     {
871         return " in ";
872     },
873
874     expand: function()
875     {
876         this._expandRetainersChain(20);
877     },
878
879     /**
880      * @param {number} maxExpandLevels
881      */
882     _expandRetainersChain: function(maxExpandLevels)
883     {
884         /**
885          * @this {!WebInspector.HeapSnapshotRetainingObjectNode}
886          */
887         function populateComplete()
888         {
889             this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
890             this._expandRetainersChain(maxExpandLevels);
891         }
892
893         if (!this._populated) {
894             this.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
895             this.populate();
896             return;
897         }
898         WebInspector.HeapSnapshotGenericObjectNode.prototype.expand.call(this);
899         if (--maxExpandLevels > 0 && this.children.length > 0) {
900             var retainer = this.children[0];
901             if (retainer._distance > 1) {
902                 retainer._expandRetainersChain(maxExpandLevels);
903                 return;
904             }
905         }
906         this._dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete);
907     },
908
909     __proto__: WebInspector.HeapSnapshotObjectNode.prototype
910 }
911
912 /**
913  * @constructor
914  * @extends {WebInspector.HeapSnapshotGenericObjectNode}
915  * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
916  * @param {!WebInspector.HeapSnapshotProxy} snapshot
917  * @param {!WebInspector.HeapSnapshotCommon.Node} node
918  * @param {boolean} isDeletedNode
919  */
920 WebInspector.HeapSnapshotInstanceNode = function(dataGrid, snapshot, node, isDeletedNode)
921 {
922     WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
923     this._baseSnapshotOrSnapshot = snapshot;
924     this._isDeletedNode = isDeletedNode;
925     this.updateHasChildren();
926
927     var data = this.data;
928     data["count"] = "";
929     data["countDelta"] = "";
930     data["sizeDelta"] = "";
931     if (this._isDeletedNode) {
932         data["addedCount"] = "";
933         data["addedSize"] = "";
934         data["removedCount"] = "\u2022";
935         data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
936     } else {
937         data["addedCount"] = "\u2022";
938         data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
939         data["removedCount"] = "";
940         data["removedSize"] = "";
941     }
942 };
943
944 WebInspector.HeapSnapshotInstanceNode.prototype = {
945     /**
946      * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
947      */
948     retainersDataSource: function()
949     {
950         return {snapshot: this._baseSnapshotOrSnapshot, snapshotNodeIndex: this.snapshotNodeIndex};
951     },
952
953     /**
954      * @return {!WebInspector.HeapSnapshotProviderProxy}
955      */
956     createProvider: function()
957     {
958         return this._baseSnapshotOrSnapshot.createEdgesProvider(this.snapshotNodeIndex);
959     },
960
961     /**
962      * @param {!WebInspector.HeapSnapshotCommon.Edge} item
963      * @return {!WebInspector.HeapSnapshotObjectNode}
964      */
965     _createChildNode: function(item)
966     {
967         return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._baseSnapshotOrSnapshot, item, null);
968     },
969
970     /**
971      * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
972      * @return {number}
973      */
974     _childHashForEntity: function(edge)
975     {
976         return edge.edgeIndex;
977     },
978
979     /**
980      * @param {!WebInspector.HeapSnapshotObjectNode} childNode
981      * @return {number}
982      */
983     _childHashForNode: function(childNode)
984     {
985         return childNode._edgeIndex;
986     },
987
988     /**
989      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
990      */
991     comparator: function()
992     {
993         var sortAscending = this._dataGrid.isSortOrderAscending();
994         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
995         var sortFields = {
996             object: ["!edgeName", sortAscending, "retainedSize", false],
997             distance: ["distance", sortAscending, "retainedSize", false],
998             count: ["!edgeName", true, "retainedSize", false],
999             addedSize: ["selfSize", sortAscending, "!edgeName", true],
1000             removedSize: ["selfSize", sortAscending, "!edgeName", true],
1001             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
1002             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
1003         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
1004         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1005     },
1006
1007     __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
1008 }
1009
1010 /**
1011  * @constructor
1012  * @param {!WebInspector.HeapSnapshotConstructorsDataGrid} dataGrid
1013  * @param {string} className
1014  * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
1015  * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
1016  * @extends {WebInspector.HeapSnapshotGridNode}
1017  */
1018 WebInspector.HeapSnapshotConstructorNode = function(dataGrid, className, aggregate, nodeFilter)
1019 {
1020     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, aggregate.count > 0);
1021     this._name = className;
1022     this._nodeFilter = nodeFilter;
1023     this._distance = aggregate.distance;
1024     this._count = aggregate.count;
1025     this._shallowSize = aggregate.self;
1026     this._retainedSize = aggregate.maxRet;
1027
1028     var snapshot = dataGrid.snapshot;
1029     var countPercent = this._count / snapshot.nodeCount * 100.0;
1030     var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
1031     var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
1032
1033     this.data = {
1034         "object": className,
1035         "count": Number.withThousandsSeparator(this._count),
1036         "distance": this._toUIDistance(this._distance),
1037         "shallowSize": Number.withThousandsSeparator(this._shallowSize),
1038         "retainedSize": Number.withThousandsSeparator(this._retainedSize),
1039         "count-percent": this._toPercentString(countPercent),
1040         "shallowSize-percent": this._toPercentString(shallowSizePercent),
1041         "retainedSize-percent": this._toPercentString(retainedSizePercent)
1042     };
1043 }
1044
1045 WebInspector.HeapSnapshotConstructorNode.prototype = {
1046     /**
1047      * @override
1048      * @return {!WebInspector.HeapSnapshotProviderProxy}
1049      */
1050     createProvider: function()
1051     {
1052         return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._nodeFilter)
1053     },
1054
1055     /**
1056      * @param {number} snapshotObjectId
1057      * @param {function(boolean)} callback
1058      */
1059     revealNodeBySnapshotObjectId: function(snapshotObjectId, callback)
1060     {
1061         /**
1062          * @this {WebInspector.HeapSnapshotConstructorNode}
1063          */
1064         function didExpand()
1065         {
1066             this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
1067         }
1068
1069         /**
1070          * @this {WebInspector.HeapSnapshotConstructorNode}
1071          * @param {number} nodePosition
1072          */
1073         function didGetNodePosition(nodePosition)
1074         {
1075             if (nodePosition === -1) {
1076                 this.collapse();
1077                 callback(false);
1078             } else {
1079                 this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
1080             }
1081         }
1082
1083         /**
1084          * @this {WebInspector.HeapSnapshotConstructorNode}
1085          * @param {number} nodePosition
1086          */
1087         function didPopulateChildren(nodePosition)
1088         {
1089             var child = this.childForPosition(nodePosition);
1090             if (child) {
1091                 this._dataGrid.revealTreeNode([this, child]);
1092                 this._dataGrid.highlightNode(/** @type {!WebInspector.HeapSnapshotGridNode} */ (child));
1093             }
1094             callback(!!child);
1095         }
1096
1097         this._dataGrid.resetNameFilter();
1098         this.expandWithoutPopulate(didExpand.bind(this));
1099     },
1100
1101     /**
1102      * @param {string} filterValue
1103      * @return {boolean}
1104      */
1105     filteredOut: function(filterValue)
1106     {
1107         return this._name.toLowerCase().indexOf(filterValue) === -1;
1108     },
1109
1110     /**
1111      * @param {string} columnIdentifier
1112      * @return {!Element}
1113      */
1114     createCell: function(columnIdentifier)
1115     {
1116         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
1117         if (this._searchMatched)
1118             cell.classList.add("highlight");
1119         return cell;
1120     },
1121
1122     /**
1123      * @param {!WebInspector.HeapSnapshotCommon.Node} item
1124      * @return {!WebInspector.HeapSnapshotInstanceNode}
1125      */
1126     _createChildNode: function(item)
1127     {
1128         return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
1129     },
1130
1131     /**
1132      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1133      */
1134     comparator: function()
1135     {
1136         var sortAscending = this._dataGrid.isSortOrderAscending();
1137         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1138         var sortFields = {
1139             object: ["id", sortAscending, "retainedSize", false],
1140             distance: ["distance", sortAscending, "retainedSize", false],
1141             count: ["id", true, "retainedSize", false],
1142             shallowSize: ["selfSize", sortAscending, "id", true],
1143             retainedSize: ["retainedSize", sortAscending, "id", true]
1144         }[sortColumnIdentifier];
1145         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1146     },
1147
1148     /**
1149      * @param {!WebInspector.HeapSnapshotCommon.Node} node
1150      * @return {number}
1151      */
1152     _childHashForEntity: function(node)
1153     {
1154         return node.id;
1155     },
1156
1157     /**
1158      * @param {!WebInspector.HeapSnapshotInstanceNode} childNode
1159      * @return {number}
1160      */
1161     _childHashForNode: function(childNode)
1162     {
1163         return childNode.snapshotNodeId;
1164     },
1165
1166     __proto__: WebInspector.HeapSnapshotGridNode.prototype
1167 }
1168
1169
1170 /**
1171  * @constructor
1172  * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
1173  * @param {!WebInspector.HeapSnapshotProviderProxy} addedNodesProvider
1174  * @param {!WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider
1175  * @param {number} addedCount
1176  * @param {number} removedCount
1177  */
1178 WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount)
1179 {
1180     this._addedNodesProvider = addedNodesProvider;
1181     this._deletedNodesProvider = deletedNodesProvider;
1182     this._addedCount = addedCount;
1183     this._removedCount = removedCount;
1184 }
1185
1186 WebInspector.HeapSnapshotDiffNodesProvider.prototype = {
1187     dispose: function()
1188     {
1189         this._addedNodesProvider.dispose();
1190         this._deletedNodesProvider.dispose();
1191     },
1192
1193     /**
1194      * @override
1195      * @param {number} snapshotObjectId
1196      * @param {function(number)} callback
1197      */
1198     nodePosition: function(snapshotObjectId, callback)
1199     {
1200         throw new Error("Unreachable");
1201     },
1202
1203     /**
1204      * @param {function(boolean)} callback
1205      */
1206     isEmpty: function(callback)
1207     {
1208         callback(false);
1209     },
1210
1211     /**
1212      * @param {number} beginPosition
1213      * @param {number} endPosition
1214      * @param {!function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
1215      */
1216     serializeItemsRange: function(beginPosition, endPosition, callback)
1217     {
1218         /**
1219          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} items
1220          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1221          */
1222         function didReceiveAllItems(items)
1223         {
1224             items.totalLength = this._addedCount + this._removedCount;
1225             callback(items);
1226         }
1227
1228         /**
1229          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} addedItems
1230          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
1231          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1232          */
1233         function didReceiveDeletedItems(addedItems, itemsRange)
1234         {
1235             var items = itemsRange.items;
1236             if (!addedItems.items.length)
1237                 addedItems.startPosition = this._addedCount + itemsRange.startPosition;
1238             for (var i = 0; i < items.length; i++) {
1239                 items[i].isAddedNotRemoved = false;
1240                 addedItems.items.push(items[i]);
1241             }
1242             addedItems.endPosition = this._addedCount + itemsRange.endPosition;
1243             didReceiveAllItems.call(this, addedItems);
1244         }
1245
1246         /**
1247          * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
1248          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1249          */
1250         function didReceiveAddedItems(itemsRange)
1251         {
1252             var items = itemsRange.items;
1253             for (var i = 0; i < items.length; i++)
1254                 items[i].isAddedNotRemoved = true;
1255             if (itemsRange.endPosition < endPosition)
1256                 return this._deletedNodesProvider.serializeItemsRange(0, endPosition - itemsRange.endPosition, didReceiveDeletedItems.bind(this, itemsRange));
1257
1258             itemsRange.totalLength = this._addedCount + this._removedCount;
1259             didReceiveAllItems.call(this, itemsRange);
1260         }
1261
1262         if (beginPosition < this._addedCount) {
1263             this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this));
1264         } else {
1265             var emptyRange = new WebInspector.HeapSnapshotCommon.ItemsRange(0, 0, 0, []);
1266             this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, emptyRange));
1267         }
1268     },
1269
1270     /**
1271      * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
1272      * @param {function()} callback
1273      */
1274     sortAndRewind: function(comparator, callback)
1275     {
1276         /**
1277          * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1278          */
1279         function afterSort()
1280         {
1281             this._deletedNodesProvider.sortAndRewind(comparator, callback);
1282         }
1283         this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this));
1284     }
1285 };
1286
1287 /**
1288  * @constructor
1289  * @param {!WebInspector.HeapSnapshotDiffDataGrid} dataGrid
1290  * @param {string} className
1291  * @param {!WebInspector.HeapSnapshotCommon.DiffForClass} diffForClass
1292  * @extends {WebInspector.HeapSnapshotGridNode}
1293  */
1294 WebInspector.HeapSnapshotDiffNode = function(dataGrid, className, diffForClass)
1295 {
1296     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, true);
1297     this._name = className;
1298     this._addedCount = diffForClass.addedCount;
1299     this._removedCount = diffForClass.removedCount;
1300     this._countDelta = diffForClass.countDelta;
1301     this._addedSize = diffForClass.addedSize;
1302     this._removedSize = diffForClass.removedSize;
1303     this._sizeDelta = diffForClass.sizeDelta;
1304     this._deletedIndexes = diffForClass.deletedIndexes;
1305     this.data = {
1306         "object": className,
1307         "addedCount": Number.withThousandsSeparator(this._addedCount),
1308         "removedCount": Number.withThousandsSeparator(this._removedCount),
1309         "countDelta":  this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta)),
1310         "addedSize": Number.withThousandsSeparator(this._addedSize),
1311         "removedSize": Number.withThousandsSeparator(this._removedSize),
1312         "sizeDelta": this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta))
1313     };
1314 }
1315
1316 WebInspector.HeapSnapshotDiffNode.prototype = {
1317     /**
1318      * @override
1319      * @return {!WebInspector.HeapSnapshotDiffNodesProvider}
1320      */
1321     createProvider: function()
1322     {
1323         var tree = this._dataGrid;
1324         return new WebInspector.HeapSnapshotDiffNodesProvider(
1325             tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name),
1326             tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes),
1327             this._addedCount,
1328             this._removedCount);
1329     },
1330
1331     /**
1332      * @param {string} columnIdentifier
1333      * @return {!Element}
1334      */
1335     createCell: function(columnIdentifier)
1336     {
1337         var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
1338         if (columnIdentifier !== "object")
1339             cell.classList.add("numeric-column");
1340         return cell;
1341     },
1342
1343     /**
1344      * @param {!WebInspector.HeapSnapshotCommon.Node} item
1345      * @return {!WebInspector.HeapSnapshotInstanceNode}
1346      */
1347     _createChildNode: function(item)
1348     {
1349         if (item.isAddedNotRemoved)
1350             return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
1351         else
1352             return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, item, true);
1353     },
1354
1355     /**
1356      * @param {!WebInspector.HeapSnapshotCommon.Node} node
1357      * @return {number}
1358      */
1359     _childHashForEntity: function(node)
1360     {
1361         return node.id;
1362     },
1363
1364     /**
1365      * @param {!WebInspector.HeapSnapshotInstanceNode} childNode
1366      * @return {number}
1367      */
1368     _childHashForNode: function(childNode)
1369     {
1370         return childNode.snapshotNodeId;
1371     },
1372
1373     /**
1374      * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1375      */
1376     comparator: function()
1377     {
1378         var sortAscending = this._dataGrid.isSortOrderAscending();
1379         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1380         var sortFields = {
1381             object: ["id", sortAscending, "selfSize", false],
1382             addedCount: ["selfSize", sortAscending, "id", true],
1383             removedCount: ["selfSize", sortAscending, "id", true],
1384             countDelta: ["selfSize", sortAscending, "id", true],
1385             addedSize: ["selfSize", sortAscending, "id", true],
1386             removedSize: ["selfSize", sortAscending, "id", true],
1387             sizeDelta: ["selfSize", sortAscending, "id", true]
1388         }[sortColumnIdentifier];
1389         return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1390     },
1391
1392     /**
1393      * @param {string} filterValue
1394      * @return {boolean}
1395      */
1396     filteredOut: function(filterValue)
1397     {
1398         return this._name.toLowerCase().indexOf(filterValue) === -1;
1399     },
1400
1401     _signForDelta: function(delta)
1402     {
1403         if (delta === 0)
1404             return "";
1405         if (delta > 0)
1406             return "+";
1407         else
1408             return "\u2212";  // Math minus sign, same width as plus.
1409     },
1410
1411     __proto__: WebInspector.HeapSnapshotGridNode.prototype
1412 }
1413
1414
1415 /**
1416  * @constructor
1417  * @extends {WebInspector.HeapSnapshotGridNode}
1418  * @param {!WebInspector.AllocationDataGrid} dataGrid
1419  * @param {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode} data
1420  */
1421 WebInspector.AllocationGridNode = function(dataGrid, data)
1422 {
1423     WebInspector.HeapSnapshotGridNode.call(this, dataGrid, data.hasChildren);
1424     this._populated = false;
1425     this._allocationNode = data;
1426     this.data = {
1427         "liveCount": Number.withThousandsSeparator(data.liveCount),
1428         "count": Number.withThousandsSeparator(data.count),
1429         "liveSize": Number.withThousandsSeparator(data.liveSize),
1430         "size": Number.withThousandsSeparator(data.size),
1431         "name": data.name
1432     };
1433 }
1434
1435 WebInspector.AllocationGridNode.prototype = {
1436     populate: function()
1437     {
1438         if (this._populated)
1439             return;
1440         this._populated = true;
1441         this._dataGrid.snapshot.allocationNodeCallers(this._allocationNode.id, didReceiveCallers.bind(this));
1442
1443         /**
1444          * @param {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} callers
1445          * @this {WebInspector.AllocationGridNode}
1446          */
1447         function didReceiveCallers(callers)
1448         {
1449             var callersChain = callers.nodesWithSingleCaller;
1450             var parentNode = this;
1451             var dataGrid = /** @type {!WebInspector.AllocationDataGrid} */ (this._dataGrid);
1452             for (var i = 0; i < callersChain.length; i++) {
1453                 var child = new WebInspector.AllocationGridNode(dataGrid, callersChain[i]);
1454                 dataGrid.appendNode(parentNode, child);
1455                 parentNode = child;
1456                 parentNode._populated = true;
1457                 if (this.expanded)
1458                     parentNode.expand();
1459             }
1460
1461             var callersBranch = callers.branchingCallers;
1462             callersBranch.sort(this._dataGrid._createComparator());
1463             for (var i = 0; i < callersBranch.length; i++)
1464                 dataGrid.appendNode(parentNode, new WebInspector.AllocationGridNode(dataGrid, callersBranch[i]));
1465             dataGrid.updateVisibleNodes(true);
1466         }
1467     },
1468
1469     /**
1470      * @override
1471      */
1472     expand: function()
1473     {
1474         WebInspector.HeapSnapshotGridNode.prototype.expand.call(this);
1475         if (this.children.length === 1)
1476             this.children[0].expand();
1477     },
1478
1479     /**
1480      * @override
1481      * @param {string} columnIdentifier
1482      * @return {!Element}
1483      */
1484     createCell: function(columnIdentifier)
1485     {
1486         if (columnIdentifier !== "name")
1487             return this._createValueCell(columnIdentifier);
1488
1489         var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
1490         var allocationNode = this._allocationNode;
1491         var target = this._dataGrid.target();
1492         if (allocationNode.scriptId) {
1493             var linkifier = this._dataGrid._linkifier;
1494             var urlElement = linkifier.linkifyScriptLocation(target, String(allocationNode.scriptId), allocationNode.scriptName, allocationNode.line - 1, allocationNode.column - 1, "profile-node-file");
1495             urlElement.style.maxWidth = "75%";
1496             cell.insertBefore(urlElement, cell.firstChild);
1497         }
1498         return cell;
1499     },
1500
1501     /**
1502      * @return {number}
1503      */
1504     allocationNodeId: function()
1505     {
1506         return this._allocationNode.id;
1507     },
1508
1509     __proto__: WebInspector.HeapSnapshotGridNode.prototype
1510 }