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