• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    queryObjectContent: function(callback, objectGroupName)
629    {
630        /**
631         * @param {?Protocol.Error} error
632         * @param {!RuntimeAgent.RemoteObject} object
633         */
634        function formatResult(error, object)
635        {
636            if (!error && object.type)
637                callback(WebInspector.runtimeModel.createRemoteObject(object), !!error);
638            else
639                callback(WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(WebInspector.UIString("Preview is not available")));
640        }
641
642        if (this._type === "string")
643            callback(WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(this._name));
644        else
645            HeapProfilerAgent.getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult);
646    },
647
648    updateHasChildren: function()
649    {
650        /**
651         * @this {WebInspector.HeapSnapshotGenericObjectNode}
652         */
653        function isEmptyCallback(isEmpty)
654        {
655            this.hasChildren = !isEmpty;
656        }
657        this._provider().isEmpty(isEmptyCallback.bind(this));
658    },
659
660    /**
661     * @param {string} fullName
662     * @param {boolean} hasObjectId
663     * @return {string}
664     */
665    shortenWindowURL: function(fullName, hasObjectId)
666    {
667        var startPos = fullName.indexOf("/");
668        var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
669        if (startPos !== -1 && endPos !== -1) {
670            var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
671            var url = fullURL.trimURL();
672            if (url.length > 40)
673                url = url.trimMiddle(40);
674            return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
675        } else
676            return fullName;
677    },
678
679    __proto__: WebInspector.HeapSnapshotGridNode.prototype
680}
681
682/**
683 * @constructor
684 * @extends {WebInspector.HeapSnapshotGenericObjectNode}
685 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
686 * @param {!WebInspector.HeapSnapshotProxy} snapshot
687 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
688 * @param {?WebInspector.HeapSnapshotObjectNode} parentObjectNode
689 */
690WebInspector.HeapSnapshotObjectNode = function(dataGrid, snapshot, edge, parentObjectNode)
691{
692    WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, edge.node);
693    this._referenceName = edge.name;
694    this._referenceType = edge.type;
695    this._edgeIndex = edge.edgeIndex;
696    this._snapshot = snapshot;
697
698    this._parentObjectNode = parentObjectNode;
699    this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId();
700    if (!this._cycledWithAncestorGridNode)
701        this.updateHasChildren();
702
703    var data = this.data;
704    data["count"] = "";
705    data["addedCount"] = "";
706    data["removedCount"] = "";
707    data["countDelta"] = "";
708    data["addedSize"] = "";
709    data["removedSize"] = "";
710    data["sizeDelta"] = "";
711}
712
713WebInspector.HeapSnapshotObjectNode.prototype = {
714    /**
715     * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
716     */
717    retainersDataSource: function()
718    {
719        return {snapshot: this._snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
720    },
721
722    /**
723     * @return {!WebInspector.HeapSnapshotProviderProxy}
724     */
725    createProvider: function()
726    {
727        return this._snapshot.createEdgesProvider(this.snapshotNodeIndex);
728    },
729
730    _findAncestorWithSameSnapshotNodeId: function()
731    {
732        var ancestor = this._parentObjectNode;
733        while (ancestor) {
734            if (ancestor.snapshotNodeId === this.snapshotNodeId)
735                return ancestor;
736            ancestor = ancestor._parentObjectNode;
737        }
738        return null;
739    },
740
741    /**
742     * @param {!WebInspector.HeapSnapshotCommon.Edge} item
743     * @return {!WebInspector.HeapSnapshotObjectNode}
744     */
745    _createChildNode: function(item)
746    {
747        return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._snapshot, item, this);
748    },
749
750    /**
751     * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
752     * @return {number}
753     */
754    _childHashForEntity: function(edge)
755    {
756        return edge.edgeIndex;
757    },
758
759    /**
760     * @param {!WebInspector.HeapSnapshotObjectNode} childNode
761     * @return {number}
762     */
763    _childHashForNode: function(childNode)
764    {
765        return childNode._edgeIndex;
766    },
767
768    /**
769     * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
770     */
771    comparator: function()
772    {
773        var sortAscending = this._dataGrid.isSortOrderAscending();
774        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
775        var sortFields = {
776            object: ["!edgeName", sortAscending, "retainedSize", false],
777            count: ["!edgeName", true, "retainedSize", false],
778            shallowSize: ["selfSize", sortAscending, "!edgeName", true],
779            retainedSize: ["retainedSize", sortAscending, "!edgeName", true],
780            distance: ["distance", sortAscending, "_name", true]
781        }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
782        return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
783    },
784
785    _prefixObjectCell: function(div)
786    {
787        var name = this._referenceName;
788        if (name === "") name = "(empty)";
789        var nameClass = "name";
790        switch (this._referenceType) {
791        case "context":
792            nameClass = "console-formatted-number";
793            break;
794        case "internal":
795        case "hidden":
796        case "weak":
797            nameClass = "console-formatted-null";
798            break;
799        case "element":
800            name = "[" + name + "]";
801            break;
802        }
803
804        if (this._cycledWithAncestorGridNode)
805            div.className += " cycled-ancessor-node";
806
807        var nameSpan = document.createElement("span");
808        nameSpan.className = nameClass;
809        nameSpan.textContent = name;
810        div.appendChild(nameSpan);
811
812        var separatorSpan = document.createElement("span");
813        separatorSpan.className = "grayed";
814        separatorSpan.textContent = this._edgeNodeSeparator();
815        div.appendChild(separatorSpan);
816    },
817
818    /**
819     * @return {string}
820     */
821    _edgeNodeSeparator: function()
822    {
823        return " :: ";
824    },
825
826    __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
827}
828
829/**
830 * @constructor
831 * @extends {WebInspector.HeapSnapshotObjectNode}
832 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
833 * @param {!WebInspector.HeapSnapshotProxy} snapshot
834 * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
835 * @param {?WebInspector.HeapSnapshotRetainingObjectNode} parentRetainingObjectNode
836 */
837WebInspector.HeapSnapshotRetainingObjectNode = function(dataGrid, snapshot, edge, parentRetainingObjectNode)
838{
839    WebInspector.HeapSnapshotObjectNode.call(this, dataGrid, snapshot, edge, parentRetainingObjectNode);
840}
841
842WebInspector.HeapSnapshotRetainingObjectNode.prototype = {
843    /**
844     * @return {!WebInspector.HeapSnapshotProviderProxy}
845     */
846    createProvider: function()
847    {
848        return this._snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex);
849    },
850
851    /**
852     * @param {!WebInspector.HeapSnapshotCommon.Edge} item
853     * @return {!WebInspector.HeapSnapshotRetainingObjectNode}
854     */
855    _createChildNode: function(item)
856    {
857        return new WebInspector.HeapSnapshotRetainingObjectNode(this._dataGrid, this._snapshot, item, this);
858    },
859
860    /**
861     * @return {string}
862     */
863    _edgeNodeSeparator: function()
864    {
865        return " in ";
866    },
867
868    expand: function()
869    {
870        this._expandRetainersChain(20);
871    },
872
873    /**
874     * @param {number} maxExpandLevels
875     */
876    _expandRetainersChain: function(maxExpandLevels)
877    {
878        /**
879         * @this {!WebInspector.HeapSnapshotRetainingObjectNode}
880         */
881        function populateComplete()
882        {
883            this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
884            this._expandRetainersChain(maxExpandLevels);
885        }
886
887        if (!this._populated) {
888            this.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
889            this.populate();
890            return;
891        }
892        WebInspector.HeapSnapshotGenericObjectNode.prototype.expand.call(this);
893        if (--maxExpandLevels > 0 && this.children.length > 0) {
894            var retainer = this.children[0];
895            if (retainer._distance > 1) {
896                retainer._expandRetainersChain(maxExpandLevels);
897                return;
898            }
899        }
900        this._dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete);
901    },
902
903    __proto__: WebInspector.HeapSnapshotObjectNode.prototype
904}
905
906/**
907 * @constructor
908 * @extends {WebInspector.HeapSnapshotGenericObjectNode}
909 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
910 * @param {!WebInspector.HeapSnapshotProxy} snapshot
911 * @param {!WebInspector.HeapSnapshotCommon.Node} node
912 * @param {boolean} isDeletedNode
913 */
914WebInspector.HeapSnapshotInstanceNode = function(dataGrid, snapshot, node, isDeletedNode)
915{
916    WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
917    this._baseSnapshotOrSnapshot = snapshot;
918    this._isDeletedNode = isDeletedNode;
919    this.updateHasChildren();
920
921    var data = this.data;
922    data["count"] = "";
923    data["countDelta"] = "";
924    data["sizeDelta"] = "";
925    if (this._isDeletedNode) {
926        data["addedCount"] = "";
927        data["addedSize"] = "";
928        data["removedCount"] = "\u2022";
929        data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
930    } else {
931        data["addedCount"] = "\u2022";
932        data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
933        data["removedCount"] = "";
934        data["removedSize"] = "";
935    }
936};
937
938WebInspector.HeapSnapshotInstanceNode.prototype = {
939    /**
940     * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
941     */
942    retainersDataSource: function()
943    {
944        return {snapshot: this._baseSnapshotOrSnapshot, snapshotNodeIndex: this.snapshotNodeIndex};
945    },
946
947    /**
948     * @return {!WebInspector.HeapSnapshotProviderProxy}
949     */
950    createProvider: function()
951    {
952        return this._baseSnapshotOrSnapshot.createEdgesProvider(this.snapshotNodeIndex);
953    },
954
955    /**
956     * @param {!WebInspector.HeapSnapshotCommon.Edge} item
957     * @return {!WebInspector.HeapSnapshotObjectNode}
958     */
959    _createChildNode: function(item)
960    {
961        return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._baseSnapshotOrSnapshot, item, null);
962    },
963
964    /**
965     * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
966     * @return {number}
967     */
968    _childHashForEntity: function(edge)
969    {
970        return edge.edgeIndex;
971    },
972
973    /**
974     * @param {!WebInspector.HeapSnapshotObjectNode} childNode
975     * @return {number}
976     */
977    _childHashForNode: function(childNode)
978    {
979        return childNode._edgeIndex;
980    },
981
982    /**
983     * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
984     */
985    comparator: function()
986    {
987        var sortAscending = this._dataGrid.isSortOrderAscending();
988        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
989        var sortFields = {
990            object: ["!edgeName", sortAscending, "retainedSize", false],
991            distance: ["distance", sortAscending, "retainedSize", false],
992            count: ["!edgeName", true, "retainedSize", false],
993            addedSize: ["selfSize", sortAscending, "!edgeName", true],
994            removedSize: ["selfSize", sortAscending, "!edgeName", true],
995            shallowSize: ["selfSize", sortAscending, "!edgeName", true],
996            retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
997        }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
998        return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
999    },
1000
1001    __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
1002}
1003
1004/**
1005 * @constructor
1006 * @param {!WebInspector.HeapSnapshotConstructorsDataGrid} dataGrid
1007 * @param {string} className
1008 * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
1009 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
1010 * @extends {WebInspector.HeapSnapshotGridNode}
1011 */
1012WebInspector.HeapSnapshotConstructorNode = function(dataGrid, className, aggregate, nodeFilter)
1013{
1014    WebInspector.HeapSnapshotGridNode.call(this, dataGrid, aggregate.count > 0);
1015    this._name = className;
1016    this._nodeFilter = nodeFilter;
1017    this._distance = aggregate.distance;
1018    this._count = aggregate.count;
1019    this._shallowSize = aggregate.self;
1020    this._retainedSize = aggregate.maxRet;
1021
1022    var snapshot = dataGrid.snapshot;
1023    var countPercent = this._count / snapshot.nodeCount * 100.0;
1024    var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
1025    var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
1026
1027    this.data = {
1028        "object": className,
1029        "count": Number.withThousandsSeparator(this._count),
1030        "distance": this._toUIDistance(this._distance),
1031        "shallowSize": Number.withThousandsSeparator(this._shallowSize),
1032        "retainedSize": Number.withThousandsSeparator(this._retainedSize),
1033        "count-percent": this._toPercentString(countPercent),
1034        "shallowSize-percent": this._toPercentString(shallowSizePercent),
1035        "retainedSize-percent": this._toPercentString(retainedSizePercent)
1036    };
1037}
1038
1039WebInspector.HeapSnapshotConstructorNode.prototype = {
1040    /**
1041     * @override
1042     * @return {!WebInspector.HeapSnapshotProviderProxy}
1043     */
1044    createProvider: function()
1045    {
1046        return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._nodeFilter)
1047    },
1048
1049    /**
1050     * @param {number} snapshotObjectId
1051     * @param {function(boolean)} callback
1052     */
1053    revealNodeBySnapshotObjectId: function(snapshotObjectId, callback)
1054    {
1055        /**
1056         * @this {WebInspector.HeapSnapshotConstructorNode}
1057         */
1058        function didExpand()
1059        {
1060            this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
1061        }
1062
1063        /**
1064         * @this {WebInspector.HeapSnapshotConstructorNode}
1065         * @param {number} nodePosition
1066         */
1067        function didGetNodePosition(nodePosition)
1068        {
1069            if (nodePosition === -1) {
1070                this.collapse();
1071                callback(false);
1072            } else {
1073                this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
1074            }
1075        }
1076
1077        /**
1078         * @this {WebInspector.HeapSnapshotConstructorNode}
1079         * @param {number} nodePosition
1080         */
1081        function didPopulateChildren(nodePosition)
1082        {
1083            var child = this.childForPosition(nodePosition);
1084            if (child) {
1085                this._dataGrid.revealTreeNode([this, child]);
1086                this._dataGrid.highlightNode(/** @type {!WebInspector.HeapSnapshotGridNode} */ (child));
1087            }
1088            callback(!!child);
1089        }
1090
1091        this._dataGrid.resetNameFilter();
1092        this.expandWithoutPopulate(didExpand.bind(this));
1093    },
1094
1095    /**
1096     * @param {string} filterValue
1097     * @return {boolean}
1098     */
1099    filteredOut: function(filterValue)
1100    {
1101        return this._name.toLowerCase().indexOf(filterValue) === -1;
1102    },
1103
1104    /**
1105     * @param {string} columnIdentifier
1106     * @return {!Element}
1107     */
1108    createCell: function(columnIdentifier)
1109    {
1110        var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
1111        if (this._searchMatched)
1112            cell.classList.add("highlight");
1113        return cell;
1114    },
1115
1116    /**
1117     * @param {!WebInspector.HeapSnapshotCommon.Node} item
1118     * @return {!WebInspector.HeapSnapshotInstanceNode}
1119     */
1120    _createChildNode: function(item)
1121    {
1122        return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
1123    },
1124
1125    /**
1126     * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1127     */
1128    comparator: function()
1129    {
1130        var sortAscending = this._dataGrid.isSortOrderAscending();
1131        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1132        var sortFields = {
1133            object: ["id", sortAscending, "retainedSize", false],
1134            distance: ["distance", sortAscending, "retainedSize", false],
1135            count: ["id", true, "retainedSize", false],
1136            shallowSize: ["selfSize", sortAscending, "id", true],
1137            retainedSize: ["retainedSize", sortAscending, "id", true]
1138        }[sortColumnIdentifier];
1139        return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1140    },
1141
1142    /**
1143     * @param {!WebInspector.HeapSnapshotCommon.Node} node
1144     * @return {number}
1145     */
1146    _childHashForEntity: function(node)
1147    {
1148        return node.id;
1149    },
1150
1151    /**
1152     * @param {!WebInspector.HeapSnapshotInstanceNode} childNode
1153     * @return {number}
1154     */
1155    _childHashForNode: function(childNode)
1156    {
1157        return childNode.snapshotNodeId;
1158    },
1159
1160    __proto__: WebInspector.HeapSnapshotGridNode.prototype
1161}
1162
1163
1164/**
1165 * @constructor
1166 * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
1167 * @param {!WebInspector.HeapSnapshotProviderProxy} addedNodesProvider
1168 * @param {!WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider
1169 * @param {number} addedCount
1170 * @param {number} removedCount
1171 */
1172WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount)
1173{
1174    this._addedNodesProvider = addedNodesProvider;
1175    this._deletedNodesProvider = deletedNodesProvider;
1176    this._addedCount = addedCount;
1177    this._removedCount = removedCount;
1178}
1179
1180WebInspector.HeapSnapshotDiffNodesProvider.prototype = {
1181    dispose: function()
1182    {
1183        this._addedNodesProvider.dispose();
1184        this._deletedNodesProvider.dispose();
1185    },
1186
1187    /**
1188     * @override
1189     * @param {number} snapshotObjectId
1190     * @param {function(number)} callback
1191     */
1192    nodePosition: function(snapshotObjectId, callback)
1193    {
1194        throw new Error("Unreachable");
1195    },
1196
1197    /**
1198     * @param {function(boolean)} callback
1199     */
1200    isEmpty: function(callback)
1201    {
1202        callback(false);
1203    },
1204
1205    /**
1206     * @param {number} beginPosition
1207     * @param {number} endPosition
1208     * @param {!function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
1209     */
1210    serializeItemsRange: function(beginPosition, endPosition, callback)
1211    {
1212        /**
1213         * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} items
1214         * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1215         */
1216        function didReceiveAllItems(items)
1217        {
1218            items.totalLength = this._addedCount + this._removedCount;
1219            callback(items);
1220        }
1221
1222        /**
1223         * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} addedItems
1224         * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
1225         * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1226         */
1227        function didReceiveDeletedItems(addedItems, itemsRange)
1228        {
1229            var items = itemsRange.items;
1230            if (!addedItems.items.length)
1231                addedItems.startPosition = this._addedCount + itemsRange.startPosition;
1232            for (var i = 0; i < items.length; i++) {
1233                items[i].isAddedNotRemoved = false;
1234                addedItems.items.push(items[i]);
1235            }
1236            addedItems.endPosition = this._addedCount + itemsRange.endPosition;
1237            didReceiveAllItems.call(this, addedItems);
1238        }
1239
1240        /**
1241         * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
1242         * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1243         */
1244        function didReceiveAddedItems(itemsRange)
1245        {
1246            var items = itemsRange.items;
1247            for (var i = 0; i < items.length; i++)
1248                items[i].isAddedNotRemoved = true;
1249            if (itemsRange.endPosition < endPosition)
1250                return this._deletedNodesProvider.serializeItemsRange(0, endPosition - itemsRange.endPosition, didReceiveDeletedItems.bind(this, itemsRange));
1251
1252            itemsRange.totalLength = this._addedCount + this._removedCount;
1253            didReceiveAllItems.call(this, itemsRange);
1254        }
1255
1256        if (beginPosition < this._addedCount) {
1257            this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this));
1258        } else {
1259            var emptyRange = new WebInspector.HeapSnapshotCommon.ItemsRange(0, 0, 0, []);
1260            this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, emptyRange));
1261        }
1262    },
1263
1264    /**
1265     * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
1266     * @param {function()} callback
1267     */
1268    sortAndRewind: function(comparator, callback)
1269    {
1270        /**
1271         * @this {WebInspector.HeapSnapshotDiffNodesProvider}
1272         */
1273        function afterSort()
1274        {
1275            this._deletedNodesProvider.sortAndRewind(comparator, callback);
1276        }
1277        this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this));
1278    }
1279};
1280
1281/**
1282 * @constructor
1283 * @param {!WebInspector.HeapSnapshotDiffDataGrid} dataGrid
1284 * @param {string} className
1285 * @param {!WebInspector.HeapSnapshotCommon.DiffForClass} diffForClass
1286 * @extends {WebInspector.HeapSnapshotGridNode}
1287 */
1288WebInspector.HeapSnapshotDiffNode = function(dataGrid, className, diffForClass)
1289{
1290    WebInspector.HeapSnapshotGridNode.call(this, dataGrid, true);
1291    this._name = className;
1292    this._addedCount = diffForClass.addedCount;
1293    this._removedCount = diffForClass.removedCount;
1294    this._countDelta = diffForClass.countDelta;
1295    this._addedSize = diffForClass.addedSize;
1296    this._removedSize = diffForClass.removedSize;
1297    this._sizeDelta = diffForClass.sizeDelta;
1298    this._deletedIndexes = diffForClass.deletedIndexes;
1299    this.data = {
1300        "object": className,
1301        "addedCount": Number.withThousandsSeparator(this._addedCount),
1302        "removedCount": Number.withThousandsSeparator(this._removedCount),
1303        "countDelta":  this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta)),
1304        "addedSize": Number.withThousandsSeparator(this._addedSize),
1305        "removedSize": Number.withThousandsSeparator(this._removedSize),
1306        "sizeDelta": this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta))
1307    };
1308}
1309
1310WebInspector.HeapSnapshotDiffNode.prototype = {
1311    /**
1312     * @override
1313     * @return {!WebInspector.HeapSnapshotDiffNodesProvider}
1314     */
1315    createProvider: function()
1316    {
1317        var tree = this._dataGrid;
1318        return new WebInspector.HeapSnapshotDiffNodesProvider(
1319            tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name),
1320            tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes),
1321            this._addedCount,
1322            this._removedCount);
1323    },
1324
1325    /**
1326     * @param {string} columnIdentifier
1327     * @return {!Element}
1328     */
1329    createCell: function(columnIdentifier)
1330    {
1331        var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
1332        if (columnIdentifier !== "object")
1333            cell.classList.add("numeric-column");
1334        return cell;
1335    },
1336
1337    /**
1338     * @param {!WebInspector.HeapSnapshotCommon.Node} item
1339     * @return {!WebInspector.HeapSnapshotInstanceNode}
1340     */
1341    _createChildNode: function(item)
1342    {
1343        if (item.isAddedNotRemoved)
1344            return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
1345        else
1346            return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, item, true);
1347    },
1348
1349    /**
1350     * @param {!WebInspector.HeapSnapshotCommon.Node} node
1351     * @return {number}
1352     */
1353    _childHashForEntity: function(node)
1354    {
1355        return node.id;
1356    },
1357
1358    /**
1359     * @param {!WebInspector.HeapSnapshotInstanceNode} childNode
1360     * @return {number}
1361     */
1362    _childHashForNode: function(childNode)
1363    {
1364        return childNode.snapshotNodeId;
1365    },
1366
1367    /**
1368     * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1369     */
1370    comparator: function()
1371    {
1372        var sortAscending = this._dataGrid.isSortOrderAscending();
1373        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1374        var sortFields = {
1375            object: ["id", sortAscending, "selfSize", false],
1376            addedCount: ["selfSize", sortAscending, "id", true],
1377            removedCount: ["selfSize", sortAscending, "id", true],
1378            countDelta: ["selfSize", sortAscending, "id", true],
1379            addedSize: ["selfSize", sortAscending, "id", true],
1380            removedSize: ["selfSize", sortAscending, "id", true],
1381            sizeDelta: ["selfSize", sortAscending, "id", true]
1382        }[sortColumnIdentifier];
1383        return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1384    },
1385
1386    /**
1387     * @param {string} filterValue
1388     * @return {boolean}
1389     */
1390    filteredOut: function(filterValue)
1391    {
1392        return this._name.toLowerCase().indexOf(filterValue) === -1;
1393    },
1394
1395    _signForDelta: function(delta)
1396    {
1397        if (delta === 0)
1398            return "";
1399        if (delta > 0)
1400            return "+";
1401        else
1402            return "\u2212";  // Math minus sign, same width as plus.
1403    },
1404
1405    __proto__: WebInspector.HeapSnapshotGridNode.prototype
1406}
1407
1408
1409/**
1410 * @constructor
1411 * @extends {WebInspector.HeapSnapshotGenericObjectNode}
1412 * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
1413 * @param {!WebInspector.HeapSnapshotCommon.Node} node
1414 */
1415WebInspector.HeapSnapshotDominatorObjectNode = function(dataGrid, node)
1416{
1417    WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
1418    this.updateHasChildren();
1419};
1420
1421WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
1422    /**
1423     * @override
1424     * @return {!WebInspector.HeapSnapshotProviderProxy}
1425     */
1426    createProvider: function()
1427    {
1428        return this._dataGrid.snapshot.createNodesProviderForDominator(this.snapshotNodeIndex);
1429    },
1430
1431    /**
1432     * @param {number} snapshotObjectId
1433     * @param {function(?WebInspector.DataGridNode)} callback
1434     */
1435    retrieveChildBySnapshotObjectId: function(snapshotObjectId, callback)
1436    {
1437        /**
1438         * @this {WebInspector.HeapSnapshotDominatorObjectNode}
1439         */
1440        function didExpand()
1441        {
1442            this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
1443        }
1444
1445        /**
1446         * @this {WebInspector.HeapSnapshotDominatorObjectNode}
1447         */
1448        function didGetNodePosition(nodePosition)
1449        {
1450            if (nodePosition === -1) {
1451                this.collapse();
1452                callback(null);
1453            } else
1454                this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
1455        }
1456
1457        /**
1458         * @this {WebInspector.HeapSnapshotDominatorObjectNode}
1459         */
1460        function didPopulateChildren(nodePosition)
1461        {
1462            var child = this.childForPosition(nodePosition);
1463            callback(child);
1464        }
1465
1466        // Make sure hasChildren flag is updated before expanding this node as updateHasChildren response
1467        // may not have been received yet.
1468        this.hasChildren = true;
1469        this.expandWithoutPopulate(didExpand.bind(this));
1470    },
1471
1472    _createChildNode: function(item)
1473    {
1474        return new WebInspector.HeapSnapshotDominatorObjectNode(this._dataGrid, item);
1475    },
1476
1477    /**
1478     * @param {!WebInspector.HeapSnapshotCommon.Node} node
1479     * @return {number}
1480     */
1481    _childHashForEntity: function(node)
1482    {
1483        return node.id;
1484    },
1485
1486    /**
1487     * @param {!WebInspector.HeapSnapshotDominatorObjectNode} childNode
1488     * @return {number}
1489     */
1490    _childHashForNode: function(childNode)
1491    {
1492        return childNode.snapshotNodeId;
1493    },
1494
1495    /**
1496     * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
1497     */
1498    comparator: function()
1499    {
1500        var sortAscending = this._dataGrid.isSortOrderAscending();
1501        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
1502        var sortFields = {
1503            object: ["id", sortAscending, "retainedSize", false],
1504            shallowSize: ["selfSize", sortAscending, "id", true],
1505            retainedSize: ["retainedSize", sortAscending, "id", true]
1506        }[sortColumnIdentifier];
1507        return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
1508    },
1509
1510    __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
1511}
1512
1513
1514/**
1515 * @constructor
1516 * @extends {WebInspector.HeapSnapshotGridNode}
1517 * @param {!WebInspector.AllocationDataGrid} dataGrid
1518 * @param {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode} data
1519 */
1520WebInspector.AllocationGridNode = function(dataGrid, data)
1521{
1522    WebInspector.HeapSnapshotGridNode.call(this, dataGrid, data.hasChildren);
1523    this._populated = false;
1524    this._allocationNode = data;
1525    this.data = {
1526        "liveCount": Number.withThousandsSeparator(data.liveCount),
1527        "count": Number.withThousandsSeparator(data.count),
1528        "liveSize": Number.withThousandsSeparator(data.liveSize),
1529        "size": Number.withThousandsSeparator(data.size),
1530        "name": data.name
1531    };
1532}
1533
1534WebInspector.AllocationGridNode.prototype = {
1535    populate: function()
1536    {
1537        if (this._populated)
1538            return;
1539        this._populated = true;
1540        this._dataGrid.snapshot.allocationNodeCallers(this._allocationNode.id, didReceiveCallers.bind(this));
1541
1542        /**
1543         * @param {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} callers
1544         * @this {WebInspector.AllocationGridNode}
1545         */
1546        function didReceiveCallers(callers)
1547        {
1548            var callersChain = callers.nodesWithSingleCaller;
1549            var parentNode = this;
1550            var dataGrid = /** @type {!WebInspector.AllocationDataGrid} */ (this._dataGrid);
1551            for (var i = 0; i < callersChain.length; i++) {
1552                var child = new WebInspector.AllocationGridNode(dataGrid, callersChain[i]);
1553                dataGrid.appendNode(parentNode, child);
1554                parentNode = child;
1555                parentNode._populated = true;
1556                if (this.expanded)
1557                    parentNode.expand();
1558            }
1559
1560            var callersBranch = callers.branchingCallers;
1561            callersBranch.sort(this._dataGrid._createComparator());
1562            for (var i = 0; i < callersBranch.length; i++)
1563                dataGrid.appendNode(parentNode, new WebInspector.AllocationGridNode(dataGrid, callersBranch[i]));
1564            dataGrid.updateVisibleNodes(true);
1565        }
1566    },
1567
1568    /**
1569     * @override
1570     */
1571    expand: function()
1572    {
1573        WebInspector.HeapSnapshotGridNode.prototype.expand.call(this);
1574        if (this.children.length === 1)
1575            this.children[0].expand();
1576    },
1577
1578    /**
1579     * @override
1580     * @param {string} columnIdentifier
1581     * @return {!Element}
1582     */
1583    createCell: function(columnIdentifier)
1584    {
1585        if (columnIdentifier !== "name")
1586            return this._createValueCell(columnIdentifier);
1587
1588        var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
1589        var allocationNode = this._allocationNode;
1590        if (allocationNode.scriptId) {
1591            var urlElement;
1592            var linkifier = this._dataGrid._linkifier;
1593            var script = WebInspector.debuggerModel.scriptForId(String(allocationNode.scriptId));
1594            if (script) {
1595                var rawLocation = WebInspector.debuggerModel.createRawLocation(script, allocationNode.line - 1, allocationNode.column - 1);
1596                urlElement = linkifier.linkifyRawLocation(rawLocation, "profile-node-file");
1597            } else {
1598                var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
1599                urlElement = linkifier.linkifyLocation(target, allocationNode.scriptName, allocationNode.line - 1, allocationNode.column - 1, "profile-node-file");
1600            }
1601            urlElement.style.maxWidth = "75%";
1602            cell.insertBefore(urlElement, cell.firstChild);
1603        }
1604        return cell;
1605    },
1606
1607    /**
1608     * @return {number}
1609     */
1610    allocationNodeId: function()
1611    {
1612        return this._allocationNode.id;
1613    },
1614
1615    __proto__: WebInspector.HeapSnapshotGridNode.prototype
1616}
1617