Web Inspector: get rid of artificial heap snapshot nodes from the retaining tree.
[WebKit-https.git] / Source / WebCore / inspector / front-end / DetailedHeapshotGridNodes.js
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 WebInspector.HeapSnapshotGridNode = function(tree, hasChildren)
32 {
33     WebInspector.DataGridNode.call(this, null, hasChildren);
34     this._defaultPopulateCount = tree._defaultPopulateCount;
35     this._provider = null;
36     this.addEventListener("populate", this._populate, this);
37 }
38
39 WebInspector.HeapSnapshotGridNode.prototype = {
40     createCell: function(columnIdentifier)
41     {
42         var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
43         if (this._searchMatched)
44             cell.addStyleClass("highlight");
45         return cell;
46     },
47
48     dispose: function()
49     {
50         if (this._provider)
51             this._provider.dispose();
52         for (var node = this.children[0]; node; node = node.traverseNextNode(true, this, true))
53             if (node.dispose)
54                 node.dispose();
55     },
56
57     hasHoverMessage: false,
58
59     queryObjectContent: function(callback)
60     {
61     },
62
63     _toPercentString: function(num)
64     {
65         return num.toFixed(2) + "%";
66     },
67
68     _createValueCell: function(columnIdentifier)
69     {
70         var cell = document.createElement("td");
71         cell.className = columnIdentifier + "-column";
72         var div = document.createElement("div");
73         var valueSpan = document.createElement("span");
74         valueSpan.textContent = this.data[columnIdentifier];
75         var percentColumn = columnIdentifier + "-percent";
76         if (percentColumn in this.data) {
77             var percentSpan = document.createElement("span");
78             percentSpan.className = "percent-column";
79             percentSpan.textContent = this.data[percentColumn];
80             div.appendChild(percentSpan);
81         }
82         div.appendChild(valueSpan);
83         cell.appendChild(div);
84         return cell;
85     },
86
87     _populate: function(event)
88     {
89         this.removeEventListener("populate", this._populate, this);
90         function sorted(ignored)
91         {
92             this.populateChildren();
93         }
94         this._provider.sortAndRewind(this.comparator(), sorted.bind(this));
95     },
96
97     populateChildren: function(provider, howMany, atIndex, afterPopulate, suppressNotifyAboutCompletion)
98     {
99         if (!howMany && provider) {
100             howMany = provider.instanceCount;
101             provider.instanceCount = 0;
102         }
103         provider = provider || this._provider;
104         if (!("instanceCount" in provider))
105             provider.instanceCount = 0;
106         howMany = howMany || this._defaultPopulateCount;
107         atIndex = atIndex || this.children.length;
108         var haveSavedChildren = !!this._savedChildren;
109         if (haveSavedChildren) {
110             haveSavedChildren = false;
111             for (var c in this._savedChildren) {
112                 haveSavedChildren = true;
113                 break;
114             }
115         }
116
117         var part = 0;
118         function callSerialize()
119         {
120             if (part >= howMany)
121                 return;
122             part += this._defaultPopulateCount;
123             provider.serializeNextItems(this._defaultPopulateCount, childrenRetrieved.bind(this));
124         }
125         function childrenRetrieved(items)
126         {
127             var length = items.totalLength;
128             for (var i = 0, l = items.length; i < l; ++i) {
129                 var item = items[i];
130                 if (haveSavedChildren) {
131                     var hash = this._childHashForEntity(item);
132                     if (hash in this._savedChildren) {
133                         this.insertChild(this._savedChildren[hash], atIndex++);
134                         continue;
135                     }
136                 }
137                 this.insertChild(this._createChildNode(item, provider, this), atIndex++);
138             }
139             provider.instanceCount += items.length;
140             if (part < howMany) {
141                 setTimeout(callSerialize.bind(this), 0);
142                 return;
143             }
144
145             if (items.hasNext)
146                 this.insertChild(new WebInspector.ShowMoreDataGridNode(this.populateChildren.bind(this, provider), this._defaultPopulateCount, length), atIndex++);
147             if (afterPopulate)
148                 afterPopulate();
149             if (!suppressNotifyAboutCompletion) {
150                 function notify()
151                 {
152                     this.dispatchEventToListeners("populate complete");
153                 }
154                 setTimeout(notify.bind(this), 0);
155             }
156         }
157         setTimeout(callSerialize.bind(this), 0);
158     },
159
160     _saveChildren: function()
161     {
162         this._savedChildren = {};
163         for (var i = 0, childrenCount = this.children.length; i < childrenCount; ++i) {
164             var child = this.children[i];
165             if (child.expanded)
166                 this._savedChildren[this._childHashForNode(child)] = child;
167         }
168     },
169
170     sort: function()
171     {
172         this.dataGrid.recursiveSortingEnter();
173         function afterSort(sorted)
174         {
175             if (!sorted) {
176                 this.dataGrid.recursiveSortingLeave();
177                 return;
178             }
179             this._saveChildren();
180             this.removeChildren();
181
182             function afterPopulate()
183             {
184                 for (var i = 0, l = this.children.length; i < l; ++i) {
185                     var child = this.children[i];
186                     if (child.expanded)
187                         child.sort();
188                 }
189                 this.dataGrid.recursiveSortingLeave();
190             }
191             this.populateChildren(this._provider, null, null, afterPopulate.bind(this));
192         }
193         this._provider.sortAndRewind(this.comparator(), afterSort.bind(this));
194     }
195 };
196
197 WebInspector.HeapSnapshotGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
198
199 WebInspector.HeapSnapshotGenericObjectNode = function(tree, node)
200 {
201     WebInspector.HeapSnapshotGridNode.call(this, tree, false);
202     this._name = node.name;
203     this._type = node.type;
204     this._shallowSize = node.selfSize;
205     this._retainedSize = node.retainedSize;
206     this.snapshotNodeId = node.id;
207     this.snapshotNodeIndex = node.nodeIndex;
208     if (this._type === "string")
209         this.hasHoverMessage = true;
210     else if (this._type === "object" && this.isDOMWindow(this._name)) {
211         this._name = this.shortenWindowURL(this._name, false);
212         this.hasHoverMessage = true;
213     } else if (node.flags & tree.snapshot.nodeFlags.canBeQueried)
214         this.hasHoverMessage = true;
215     if (node.flags & tree.snapshot.nodeFlags.detachedDOMTreeNode)
216         this.detachedDOMTreeNode = true;
217 };
218
219 WebInspector.HeapSnapshotGenericObjectNode.prototype = {
220     createCell: function(columnIdentifier)
221     {
222         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : this._createObjectCell();
223         if (this._searchMatched)
224             cell.addStyleClass("highlight");
225         return cell;
226     },
227
228     _createObjectCell: function()
229     {
230         var cell = document.createElement("td");
231         cell.className = "object-column";
232         var div = document.createElement("div");
233         div.className = "source-code event-properties";
234         div.style.overflow = "visible";
235         var data = this.data["object"];
236         if (this._prefixObjectCell)
237             this._prefixObjectCell(div, data);
238         var valueSpan = document.createElement("span");
239         valueSpan.className = "value console-formatted-" + data.valueStyle;
240         valueSpan.textContent = data.value;
241         div.appendChild(valueSpan);
242         cell.appendChild(div);
243         cell.addStyleClass("disclosure");
244         if (this.depth)
245             cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
246         return cell;
247     },
248
249     get _countPercent()
250     {
251         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
252     },
253
254     get data()
255     {
256         var data = this._emptyData();
257
258         var value = this._name;
259         var valueStyle = "object";
260         switch (this._type) {
261         case "string":
262             value = "\"" + value + "\"";
263             valueStyle = "string";
264             break;
265         case "regexp":
266             value = "/" + value + "/";
267             valueStyle = "string";
268             break;
269         case "closure":
270             value = "function " + value + "()";
271             valueStyle = "function";
272             break;
273         case "number":
274             valueStyle = "number";
275             break;
276         case "hidden":
277             valueStyle = "null";
278             break;
279         case "array":
280             if (!value)
281                 value = "[]";
282             else
283                 value += " []";
284             break;
285         };
286         if (this.hasHoverMessage)
287             valueStyle += " highlight";
288         if (this.detachedDOMTreeNode)
289             valueStyle += " detached-dom-tree-node";
290         data["object"] = { valueStyle: valueStyle, value: value + ": @" + this.snapshotNodeId };
291
292         var view = this.dataGrid.snapshotView;
293         data["shallowSize"] = Number.withThousandsSeparator(this._shallowSize);
294         data["retainedSize"] = Number.withThousandsSeparator(this._retainedSize);
295         if (view._showPercentage) {
296             data["shallowSize-percent"] = this._toPercentString(this._shallowSizePercent);
297             data["retainedSize-percent"] = this._toPercentString(this._retainedSizePercent);
298         }
299
300         return this._enhanceData ? this._enhanceData(data) : data;
301     },
302
303     queryObjectContent: function(callback)
304     {
305         if (this._type === "string")
306             callback(WebInspector.RemoteObject.fromPrimitiveValue(this._name));
307         else {
308             function formatResult(error, object)
309             {
310                 if (!error && object.type)
311                     callback(WebInspector.RemoteObject.fromPayload(object), !!error);
312                 else
313                     callback(WebInspector.RemoteObject.fromPrimitiveValue(WebInspector.UIString("Not available")));
314             }
315             ProfilerAgent.getObjectByHeapObjectId(this.snapshotNodeId, formatResult);
316         }
317     },
318
319     get _retainedSizePercent()
320     {
321         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
322     },
323
324     get _shallowSizePercent()
325     {
326         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
327     },
328
329     updateHasChildren: function()
330     {
331         function isEmptyCallback(isEmpty)
332         {
333             this.hasChildren = !isEmpty;
334         }
335         this._provider.isEmpty(isEmptyCallback.bind(this));
336     },
337
338     isDOMWindow: function(fullName)
339     {
340         return fullName.substr(0, 9) === "DOMWindow";
341     },
342
343     shortenWindowURL: function(fullName, hasObjectId)
344     {
345         var startPos = fullName.indexOf("/");
346         var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
347         if (startPos !== -1 && endPos !== -1) {
348             var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
349             var url = fullURL.trimURL();
350             if (url.length > 40)
351                 url = url.trimMiddle(40);
352             return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
353         } else
354             return fullName;
355     }
356 }
357
358 WebInspector.HeapSnapshotGenericObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
359
360 WebInspector.HeapSnapshotObjectNode = function(tree, isFromBaseSnapshot, edge, parentGridNode)
361 {
362     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, edge.node);
363     this._referenceName = edge.name;
364     this._referenceType = edge.type;
365     this._propertyAccessor = edge.propertyAccessor;
366     this.showRetainingEdges = tree.showRetainingEdges;
367     this._isFromBaseSnapshot = isFromBaseSnapshot;
368     this._provider = this._createProvider(!isFromBaseSnapshot ? tree.snapshot : tree.baseSnapshot, edge.nodeIndex, tree);
369     this.updateHasChildren(parentGridNode);
370 }
371
372 WebInspector.HeapSnapshotObjectNode.prototype = {
373     updateHasChildren: function(parentGridNode)
374     {
375         if (this.showRetainingEdges) {
376             this._parentGridNode = parentGridNode;
377             var ancestor = parentGridNode;
378             while (ancestor) {
379                 if (ancestor.snapshotNodeId === this.snapshotNodeId) {
380                     this._cycledWithAncestorGridNode = ancestor;
381                     return;
382                 }
383                 ancestor = ancestor._parentGridNode;
384             }
385         }
386         WebInspector.HeapSnapshotGenericObjectNode.prototype.updateHasChildren.call(this);
387     },
388
389     _createChildNode: function(item)
390     {
391         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, this._isFromBaseSnapshot, item, this);
392     },
393
394     _createProvider: function(snapshot, nodeIndex, tree)
395     {
396         var showHiddenData = WebInspector.settings.showHeapSnapshotObjectsHiddenProperties.get();
397         var filter = "function(edge) {\n" +
398             "    return !edge.isInvisible\n" +
399             "        && (" + !this.showRetainingEdges + " || (edge.node.id !== 1 && !edge.node.isArtificial))\n" +
400             "        && (" + showHiddenData + " || (!edge.isHidden && !edge.node.isHidden));\n" +
401             "}\n";
402         if (tree.showRetainingEdges)
403             return snapshot.createRetainingEdgesProvider(nodeIndex, filter);
404         else
405             return snapshot.createEdgesProvider(nodeIndex, filter);
406     },
407
408     _childHashForEntity: function(edge)
409     {
410         return edge.type + "#" + edge.name;
411     },
412
413     _childHashForNode: function(childNode)
414     {
415         return childNode._referenceType + "#" + childNode._referenceName;
416     },
417
418     comparator: function()
419     {
420         var sortAscending = this.dataGrid.sortOrder === "ascending";
421         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
422         var sortFields = {
423             object: ["!edgeName", sortAscending, "retainedSize", false],
424             count: ["!edgeName", true, "retainedSize", false],
425             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
426             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
427         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
428         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
429     },
430
431     _emptyData: function()
432     {
433         return {count:"", addedCount: "", removedCount: "", countDelta:"", addedSize: "", removedSize: "", sizeDelta: ""};
434     },
435
436     _enhanceData: function(data)
437     {
438         var name = this._referenceName;
439         if (name === "") name = "(empty)";
440         var nameClass = "name";
441         switch (this._referenceType) {
442         case "context":
443             nameClass = "console-formatted-number";
444             break;
445         case "internal":
446         case "hidden":
447             nameClass = "console-formatted-null";
448             break;
449         }
450         data["object"].nameClass = nameClass;
451         data["object"].name = name;
452         return data;
453     },
454
455     _prefixObjectCell: function(div, data)
456     {
457         if (this.showRetainingEdges) {
458             if (this._cycledWithAncestorGridNode)
459                 div.className += " cycled-ancessor-node";
460             var referenceNameSpan = document.createElement("span");
461             referenceNameSpan.className = "name";
462             referenceNameSpan.textContent = this._referenceName + " ";
463             div.appendChild(referenceNameSpan);
464
465             var separatorSpan = document.createElement("span");
466             separatorSpan.className = "separator";
467             separatorSpan.textContent = " of ";
468             div.appendChild(separatorSpan);
469
470             return;
471         }
472
473         var nameSpan = document.createElement("span");
474         nameSpan.className = data.nameClass;
475         nameSpan.textContent = data.name;
476         div.appendChild(nameSpan);
477
478         var separatorSpan = document.createElement("span");
479         separatorSpan.className = "separator";
480         separatorSpan.textContent = ": ";
481         div.appendChild(separatorSpan);
482     }
483 }
484
485 WebInspector.HeapSnapshotObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
486
487 WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node)
488 {
489     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
490     this._isDeletedNode = !!baseSnapshot;
491     this._provider = this._createProvider(baseSnapshot || snapshot, node.nodeIndex);
492     this.updateHasChildren();
493 };
494
495 WebInspector.HeapSnapshotInstanceNode.prototype = {
496     _createChildNode: function(item)
497     {
498         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, this._isDeletedNode, item);
499     },
500
501     _createProvider: function(snapshot, nodeIndex)
502     {
503         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
504         return snapshot.createEdgesProvider(
505             nodeIndex,
506             "function(edge) {" +
507             "    return !edge.isInvisible" +
508             "        && (" + showHiddenData + " || (!edge.isHidden && !edge.node.isHidden));" +
509             "}");
510     },
511
512     _childHashForEntity: function(edge)
513     {
514         return edge.type + "#" + edge.name;
515     },
516
517     _childHashForNode: function(childNode)
518     {
519         return childNode._referenceType + "#" + childNode._referenceName;
520     },
521
522     comparator: function()
523     {
524         var sortAscending = this.dataGrid.sortOrder === "ascending";
525         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
526         var sortFields = {
527             object: ["!edgeName", sortAscending, "retainedSize", false],
528             count: ["!edgeName", true, "retainedSize", false],
529             addedSize: ["selfSize", sortAscending, "!edgeName", true],
530             removedSize: ["selfSize", sortAscending, "!edgeName", true],
531             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
532             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
533         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
534         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
535     },
536
537     _emptyData: function()
538     {
539         return {count:"", countDelta:"", sizeDelta: ""};
540     },
541
542     _enhanceData: function(data)
543     {
544         if (this._isDeletedNode) {
545             data["addedCount"] = "";
546             data["addedSize"] = "";
547             data["removedCount"] = "\u2022";
548             data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
549         } else {
550             data["addedCount"] = "\u2022";
551             data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
552             data["removedCount"] = "";
553             data["removedSize"] = "";
554         }
555         return data;
556     },
557
558     get isDeletedNode()
559     {
560         return this._isDeletedNode;
561     }
562 }
563
564 WebInspector.HeapSnapshotInstanceNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
565
566 WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate, aggregatesKey)
567 {
568     WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0);
569     this._name = className;
570     this._count = aggregate.count;
571     this._shallowSize = aggregate.self;
572     this._retainedSize = aggregate.maxRet;
573     this._provider = this._createNodesProvider(tree.snapshot, className, aggregatesKey);
574 }
575
576 WebInspector.HeapSnapshotConstructorNode.prototype = {
577     createCell: function(columnIdentifier)
578     {
579         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
580         if (this._searchMatched)
581             cell.addStyleClass("highlight");
582         return cell;
583     },
584
585     _createChildNode: function(item)
586     {
587         return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, this.dataGrid.snapshot, item);
588     },
589
590     _createNodesProvider: function(snapshot, className, aggregatesKey)
591     {
592         return snapshot.createNodesProviderForClass(className, aggregatesKey);
593     },
594
595     comparator: function()
596     {
597         var sortAscending = this.dataGrid.sortOrder === "ascending";
598         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
599         var sortFields = {
600             object: ["id", sortAscending, "retainedSize", false],
601             count: ["id", true, "retainedSize", false],
602             shallowSize: ["selfSize", sortAscending, "id", true],
603             retainedSize: ["retainedSize", sortAscending, "id", true]
604         }[sortColumnIdentifier];
605         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
606     },
607
608     _childHashForEntity: function(node)
609     {
610         return node.id;
611     },
612
613     _childHashForNode: function(childNode)
614     {
615         return childNode.snapshotNodeId;
616     },
617
618     get data()
619     {
620         var data = {object: this._name};
621         var view = this.dataGrid.snapshotView;
622         data["count"] =  Number.withThousandsSeparator(this._count);
623         data["shallowSize"] = Number.withThousandsSeparator(this._shallowSize);
624         data["retainedSize"] = Number.withThousandsSeparator(this._retainedSize);
625         if (view._showPercentage) {
626             data["shallowSize-percent"] = this._toPercentString(this._shallowSizePercent);
627             data["retainedSize-percent"] = this._toPercentString(this._retainedSizePercent);
628         }
629         return data;
630     },
631
632     get _countPercent()
633     {
634         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
635     },
636
637     get _retainedSizePercent()
638     {
639         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
640     },
641
642     get _shallowSizePercent()
643     {
644         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
645     }
646 };
647
648 WebInspector.HeapSnapshotConstructorNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
649
650 WebInspector.HeapSnapshotIteratorsTuple = function(it1, it2)
651 {
652     this._it1 = it1;
653     this._it2 = it2;
654 }
655
656 WebInspector.HeapSnapshotIteratorsTuple.prototype = {
657     dispose: function()
658     {
659         this._it1.dispose();
660         this._it2.dispose();
661     },
662
663     sortAndRewind: function(comparator, callback)
664     {
665         function afterSort(ignored)
666         {
667             this._it2.sortAndRewind(comparator, callback);
668         }
669         this._it1.sortAndRewind(comparator, afterSort.bind(this));
670     }
671 };
672
673 WebInspector.HeapSnapshotDiffNode = function(tree, className, baseAggregate, aggregate)
674 {
675     WebInspector.HeapSnapshotGridNode.call(this, tree, true);
676     this._name = className;
677     this._baseIndexes = baseAggregate ? baseAggregate.idxs : [];
678     this._indexes = aggregate ? aggregate.idxs : [];
679     this._provider = this._createNodesProvider(tree.baseSnapshot, tree.snapshot, aggregate ? aggregate.type : baseAggregate.type, className);
680 }
681
682 WebInspector.HeapSnapshotDiffNode.prototype = {
683     calculateDiff: function(dataGrid, callback)
684     {
685         var diff = dataGrid.snapshot.createDiff(this._name);
686
687         function diffCalculated(diffResult)
688         {
689             diff.dispose();
690             this._addedCount = diffResult.addedCount;
691             this._removedCount = diffResult.removedCount;
692             this._countDelta = diffResult.countDelta;
693             this._addedSize = diffResult.addedSize;
694             this._removedSize = diffResult.removedSize;
695             this._sizeDelta = diffResult.sizeDelta;
696             this._baseIndexes = null;
697             this._indexes = null;
698             callback(this._addedSize === 0 && this._removedSize === 0);
699         }
700         function baseSelfSizesReceived(baseSelfSizes)
701         {
702             diff.pushBaseSelfSizes(baseSelfSizes);
703             diff.calculate(diffCalculated.bind(this));
704         }
705         function baseIdsReceived(baseIds)
706         {
707             diff.pushBaseIds(baseIds);
708             dataGrid.snapshot.pushBaseIds(dataGrid.baseSnapshot.uid, this._name, baseIds);
709             dataGrid.baseSnapshot.nodeFieldValuesByIndex("selfSize", this._baseIndexes, baseSelfSizesReceived.bind(this));
710         }
711         function idsReceived(ids)
712         {
713             dataGrid.baseSnapshot.pushBaseIds(dataGrid.snapshot.uid, this._name, ids);
714         }
715         dataGrid.baseSnapshot.nodeFieldValuesByIndex("id", this._baseIndexes, baseIdsReceived.bind(this));
716         dataGrid.snapshot.nodeFieldValuesByIndex("id", this._indexes, idsReceived.bind(this));
717     },
718
719     _createChildNode: function(item, provider)
720     {
721         if (provider === this._provider._it1)
722             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, provider.snapshot, item);
723         else
724             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, provider.snapshot, null, item);
725     },
726
727     _createNodesProvider: function(baseSnapshot, snapshot, nodeType, nodeClassName)
728     {
729         var className = this._name;
730         return new WebInspector.HeapSnapshotIteratorsTuple(
731             createProvider(snapshot, baseSnapshot), createProvider(baseSnapshot, snapshot));
732
733         function createProvider(snapshot, otherSnapshot)
734         {
735             var otherSnapshotId = otherSnapshot.uid;
736             var provider = snapshot.createNodesProvider(
737                 "function (node) {" +
738                 "     return node.type === \"" + nodeType + "\" " +
739                 (nodeClassName !== null ? "&& node.className === \"" + nodeClassName + "\"" : "") +
740                 "         && !this.baseSnapshotHasNode(" + otherSnapshotId + ", \"" + className + "\", node.id);" +
741                 "}");
742             provider.snapshot = snapshot;
743             return provider;
744         }
745     },
746
747     _childHashForEntity: function(node)
748     {
749         return node.id;
750     },
751
752     _childHashForNode: function(childNode)
753     {
754         return childNode.snapshotNodeId;
755     },
756
757     comparator: function()
758     {
759         var sortAscending = this.dataGrid.sortOrder === "ascending";
760         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
761         var sortFields = {
762             object: ["id", sortAscending, "selfSize", false],
763             addedCount: ["selfSize", sortAscending, "id", true],
764             removedCount: ["selfSize", sortAscending, "id", true],
765             countDelta: ["selfSize", sortAscending, "id", true],
766             addedSize: ["selfSize", sortAscending, "id", true],
767             removedSize: ["selfSize", sortAscending, "id", true],
768             sizeDelta: ["selfSize", sortAscending, "id", true]
769         }[sortColumnIdentifier];
770         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
771     },
772
773     populateChildren: function(provider, howMany, atIndex, afterPopulate)
774     {
775         if (!provider && !howMany) {
776             var firstProviderPopulated = function()
777             {
778                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, this._defaultPopulateCount, atIndex, afterPopulate);
779             };
780             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, this._defaultPopulateCount, atIndex, firstProviderPopulated.bind(this), true);
781         } else if (!howMany) {
782             var firstProviderPopulated = function()
783             {
784                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, null, atIndex, afterPopulate);
785             };
786             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, null, atIndex, firstProviderPopulated.bind(this), true);
787         } else
788             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, provider, howMany, atIndex, afterPopulate);
789     },
790
791     _signForDelta: function(delta)
792     {
793         if (delta === 0)
794             return "";
795         if (delta > 0)
796             return "+";
797         else
798             return "\u2212";  // Math minus sign, same width as plus.
799     },
800
801     get data()
802     {
803         var data = {object: this._name};
804
805         data["addedCount"] = Number.withThousandsSeparator(this._addedCount);
806         data["removedCount"] = Number.withThousandsSeparator(this._removedCount);
807         data["countDelta"] = this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta));
808         data["addedSize"] = Number.withThousandsSeparator(this._addedSize);
809         data["removedSize"] = Number.withThousandsSeparator(this._removedSize);
810         data["sizeDelta"] = this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta));
811
812         return data;
813     }
814 };
815
816 WebInspector.HeapSnapshotDiffNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
817
818 WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node)
819 {
820     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
821     this._provider = this._createProvider(tree.snapshot, node.nodeIndex);
822     this.updateHasChildren();
823 };
824
825 WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
826     _createChildNode: function(item)
827     {
828         return new WebInspector.HeapSnapshotDominatorObjectNode(this.dataGrid, item);
829     },
830
831     _createProvider: function(snapshot, nodeIndex)
832     {
833         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
834         return snapshot.createNodesProviderForDominator(nodeIndex,
835             "function (node) {" +
836             "     return " + showHiddenData + " || !node.isHidden;" +
837             "}");
838     },
839
840     _childHashForEntity: function(node)
841     {
842         return node.id;
843     },
844
845     _childHashForNode: function(childNode)
846     {
847         return childNode.snapshotNodeId;
848     },
849
850     comparator: function()
851     {
852         var sortAscending = this.dataGrid.sortOrder === "ascending";
853         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
854         var sortFields = {
855             object: ["id", sortAscending, "retainedSize", false],
856             shallowSize: ["selfSize", sortAscending, "id", true],
857             retainedSize: ["retainedSize", sortAscending, "id", true]
858         }[sortColumnIdentifier];
859         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
860     },
861
862     _emptyData: function()
863     {
864         return {};
865     }
866 };
867
868 WebInspector.HeapSnapshotDominatorObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
869
870 function MixInSnapshotNodeFunctions(sourcePrototype, targetPrototype)
871 {
872     targetPrototype._childHashForEntity = sourcePrototype._childHashForEntity;
873     targetPrototype._childHashForNode = sourcePrototype._childHashForNode;
874     targetPrototype.comparator = sourcePrototype.comparator;
875     targetPrototype._createChildNode = sourcePrototype._createChildNode;
876     targetPrototype._createProvider = sourcePrototype._createProvider;
877     targetPrototype.dispose = sourcePrototype.dispose;
878     targetPrototype.populateChildren = sourcePrototype.populateChildren;
879     targetPrototype._saveChildren = sourcePrototype._saveChildren;
880     targetPrototype.sort = sourcePrototype.sort;
881 }