93333568e9b735008530ef1025f71ce82d333851
[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         if (this._postfixObjectCell)
243             this._postfixObjectCell(div, data);
244         cell.appendChild(div);
245         cell.addStyleClass("disclosure");
246         if (this.depth)
247             cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
248         return cell;
249     },
250
251     get _countPercent()
252     {
253         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
254     },
255
256     get data()
257     {
258         var data = this._emptyData();
259
260         var value = this._name;
261         var valueStyle = "object";
262         switch (this._type) {
263         case "string":
264             value = "\"" + value + "\"";
265             valueStyle = "string";
266             break;
267         case "regexp":
268             value = "/" + value + "/";
269             valueStyle = "string";
270             break;
271         case "closure":
272             value = "function " + value + "()";
273             valueStyle = "function";
274             break;
275         case "number":
276             valueStyle = "number";
277             break;
278         case "hidden":
279             valueStyle = "null";
280             break;
281         case "array":
282             if (!value)
283                 value = "[]";
284             else
285                 value += " []";
286             break;
287         };
288         if (this.hasHoverMessage)
289             valueStyle += " highlight";
290         if (this.detachedDOMTreeNode)
291             valueStyle += " detached-dom-tree-node";
292         data["object"] = { valueStyle: valueStyle, value: value + ": @" + this.snapshotNodeId };
293
294         var view = this.dataGrid.snapshotView;
295         data["shallowSize"] = Number.withThousandsSeparator(this._shallowSize);
296         data["retainedSize"] = Number.withThousandsSeparator(this._retainedSize);
297         if (view._showPercentage) {
298             data["shallowSize-percent"] = this._toPercentString(this._shallowSizePercent);
299             data["retainedSize-percent"] = this._toPercentString(this._retainedSizePercent);
300         }
301
302         return this._enhanceData ? this._enhanceData(data) : data;
303     },
304
305     queryObjectContent: function(callback)
306     {
307         if (this._type === "string")
308             callback(WebInspector.RemoteObject.fromPrimitiveValue(this._name));
309         else {
310             function formatResult(error, object)
311             {
312                 if (!error && object.type)
313                     callback(WebInspector.RemoteObject.fromPayload(object), !!error);
314                 else
315                     callback(WebInspector.RemoteObject.fromPrimitiveValue(WebInspector.UIString("Not available")));
316             }
317             ProfilerAgent.getObjectByHeapObjectId(this.snapshotNodeId, formatResult);
318         }
319     },
320
321     get _retainedSizePercent()
322     {
323         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
324     },
325
326     get _shallowSizePercent()
327     {
328         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
329     },
330
331     _updateHasChildren: function()
332     {
333         function isEmptyCallback(isEmpty)
334         {
335             this.hasChildren = !isEmpty;
336         }
337         this._provider.isEmpty(isEmptyCallback.bind(this));
338     },
339
340     isDOMWindow: function(fullName)
341     {
342         return fullName.substr(0, 9) === "DOMWindow";
343     },
344
345     shortenWindowURL: function(fullName, hasObjectId)
346     {
347         var startPos = fullName.indexOf("/");
348         var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
349         if (startPos !== -1 && endPos !== -1) {
350             var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
351             var url = fullURL.trimURL();
352             if (url.length > 40)
353                 url = url.trimMiddle(40);
354             return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
355         } else
356             return fullName;
357     }
358 }
359
360 WebInspector.HeapSnapshotGenericObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
361
362 WebInspector.HeapSnapshotObjectNode = function(tree, isFromBaseSnapshot, edge)
363 {
364     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, edge.node);
365     this._referenceName = edge.name;
366     this._referenceType = edge.type;
367     this._propertyAccessor = edge.propertyAccessor;
368     this._retainerNode = tree.showRetainingEdges;
369     this._isFromBaseSnapshot = isFromBaseSnapshot;
370     this._provider = this._createProvider(!isFromBaseSnapshot ? tree.snapshot : tree.baseSnapshot, edge.nodeIndex, tree);
371     this._updateHasChildren();
372 }
373
374 WebInspector.HeapSnapshotObjectNode.prototype = {
375     _createChildNode: function(item)
376     {
377         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, this._isFromBaseSnapshot, item);
378     },
379
380     _createProvider: function(snapshot, nodeIndex, tree)
381     {
382         var showHiddenData = WebInspector.settings.showHeapSnapshotObjectsHiddenProperties.get();
383         var filter = "function(edge) {" +
384             "    return !edge.isInvisible" +
385             "        && (" + showHiddenData + " || (!edge.isHidden && !edge.node.isHidden));" +
386             "}";
387         if (tree.showRetainingEdges)
388             return snapshot.createRetainingEdgesProvider(nodeIndex, filter);
389         else
390             return snapshot.createEdgesProvider(nodeIndex, filter);
391     },
392
393     _childHashForEntity: function(edge)
394     {
395         return edge.type + "#" + edge.name;
396     },
397
398     _childHashForNode: function(childNode)
399     {
400         return childNode._referenceType + "#" + childNode._referenceName;
401     },
402
403     comparator: function()
404     {
405         var sortAscending = this.dataGrid.sortOrder === "ascending";
406         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
407         var sortFields = {
408             object: ["!edgeName", sortAscending, "retainedSize", false],
409             count: ["!edgeName", true, "retainedSize", false],
410             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
411             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
412         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
413         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
414     },
415
416     _emptyData: function()
417     {
418         return {count:"", addedCount: "", removedCount: "", countDelta:"", addedSize: "", removedSize: "", sizeDelta: ""};
419     },
420
421     _enhanceData: function(data)
422     {
423         var name = this._referenceName;
424         if (name === "") name = "(empty)";
425         var nameClass = "name";
426         switch (this._referenceType) {
427         case "context":
428             nameClass = "console-formatted-number";
429             break;
430         case "internal":
431         case "hidden":
432             nameClass = "console-formatted-null";
433             break;
434         }
435         data["object"].nameClass = nameClass;
436         data["object"].name = name;
437         return data;
438     },
439
440     _prefixObjectCell: function(div, data)
441     {
442         if (this._retainerNode) {
443             var prefixSpan = document.createElement("span");
444             prefixSpan.textContent = WebInspector.UIString("retained by ");
445             div.appendChild(prefixSpan);
446             return;
447         }
448
449         var nameSpan = document.createElement("span");
450         nameSpan.className = data.nameClass;
451         nameSpan.textContent = data.name;
452         div.appendChild(nameSpan);
453
454         var separatorSpan = document.createElement("span");
455         separatorSpan.className = "separator";
456         separatorSpan.textContent = ": ";
457         div.appendChild(separatorSpan);
458     },
459
460     _postfixObjectCell: function(div, data)
461     {
462         if (this._retainerNode) {
463             var referenceTypeSpan = document.createElement("span");
464             referenceTypeSpan.className = "console-formatted-object";
465             referenceTypeSpan.textContent = this._propertyAccessor;
466             div.appendChild(referenceTypeSpan);
467         }
468     }
469 }
470
471 WebInspector.HeapSnapshotObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
472
473 WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node)
474 {
475     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
476     this._isDeletedNode = !!baseSnapshot;
477     this._provider = this._createProvider(baseSnapshot || snapshot, node.nodeIndex);
478     this._updateHasChildren();
479 };
480
481 WebInspector.HeapSnapshotInstanceNode.prototype = {
482     _createChildNode: function(item)
483     {
484         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, this._isDeletedNode, item);
485     },
486
487     _createProvider: function(snapshot, nodeIndex)
488     {
489         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
490         return snapshot.createEdgesProvider(
491             nodeIndex,
492             "function(edge) {" +
493             "    return !edge.isInvisible" +
494             "        && (" + showHiddenData + " || (!edge.isHidden && !edge.node.isHidden));" +
495             "}");
496     },
497
498     _childHashForEntity: function(edge)
499     {
500         return edge.type + "#" + edge.name;
501     },
502
503     _childHashForNode: function(childNode)
504     {
505         return childNode._referenceType + "#" + childNode._referenceName;
506     },
507
508     comparator: function()
509     {
510         var sortAscending = this.dataGrid.sortOrder === "ascending";
511         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
512         var sortFields = {
513             object: ["!edgeName", sortAscending, "retainedSize", false],
514             count: ["!edgeName", true, "retainedSize", false],
515             addedSize: ["selfSize", sortAscending, "!edgeName", true],
516             removedSize: ["selfSize", sortAscending, "!edgeName", true],
517             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
518             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
519         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
520         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
521     },
522
523     _emptyData: function()
524     {
525         return {count:"", countDelta:"", sizeDelta: ""};
526     },
527
528     _enhanceData: function(data)
529     {
530         if (this._isDeletedNode) {
531             data["addedCount"] = "";
532             data["addedSize"] = "";
533             data["removedCount"] = "\u2022";
534             data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
535         } else {
536             data["addedCount"] = "\u2022";
537             data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
538             data["removedCount"] = "";
539             data["removedSize"] = "";
540         }
541         return data;
542     },
543
544     get isDeletedNode()
545     {
546         return this._isDeletedNode;
547     }
548 }
549
550 WebInspector.HeapSnapshotInstanceNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
551
552 WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate, aggregatesKey)
553 {
554     WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0);
555     this._name = className;
556     this._count = aggregate.count;
557     this._shallowSize = aggregate.self;
558     this._retainedSize = aggregate.maxRet;
559     this._provider = this._createNodesProvider(tree.snapshot, className, aggregatesKey);
560 }
561
562 WebInspector.HeapSnapshotConstructorNode.prototype = {
563     createCell: function(columnIdentifier)
564     {
565         var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
566         if (this._searchMatched)
567             cell.addStyleClass("highlight");
568         return cell;
569     },
570
571     _createChildNode: function(item)
572     {
573         return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, this.dataGrid.snapshot, item);
574     },
575
576     _createNodesProvider: function(snapshot, className, aggregatesKey)
577     {
578         return snapshot.createNodesProviderForClass(className, aggregatesKey);
579     },
580
581     comparator: function()
582     {
583         var sortAscending = this.dataGrid.sortOrder === "ascending";
584         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
585         var sortFields = {
586             object: ["id", sortAscending, "retainedSize", false],
587             count: ["id", true, "retainedSize", false],
588             shallowSize: ["selfSize", sortAscending, "id", true],
589             retainedSize: ["retainedSize", sortAscending, "id", true]
590         }[sortColumnIdentifier];
591         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
592     },
593
594     _childHashForEntity: function(node)
595     {
596         return node.id;
597     },
598
599     _childHashForNode: function(childNode)
600     {
601         return childNode.snapshotNodeId;
602     },
603
604     get data()
605     {
606         var data = {object: this._name};
607         var view = this.dataGrid.snapshotView;
608         data["count"] =  Number.withThousandsSeparator(this._count);
609         data["shallowSize"] = Number.withThousandsSeparator(this._shallowSize);
610         data["retainedSize"] = Number.withThousandsSeparator(this._retainedSize);
611         if (view._showPercentage) {
612             data["shallowSize-percent"] = this._toPercentString(this._shallowSizePercent);
613             data["retainedSize-percent"] = this._toPercentString(this._retainedSizePercent);
614         }
615         return data;
616     },
617
618     get _countPercent()
619     {
620         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
621     },
622
623     get _retainedSizePercent()
624     {
625         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
626     },
627
628     get _shallowSizePercent()
629     {
630         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
631     }
632 };
633
634 WebInspector.HeapSnapshotConstructorNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
635
636 WebInspector.HeapSnapshotIteratorsTuple = function(it1, it2)
637 {
638     this._it1 = it1;
639     this._it2 = it2;
640 }
641
642 WebInspector.HeapSnapshotIteratorsTuple.prototype = {
643     dispose: function()
644     {
645         this._it1.dispose();
646         this._it2.dispose();
647     },
648
649     sortAndRewind: function(comparator, callback)
650     {
651         function afterSort(ignored)
652         {
653             this._it2.sortAndRewind(comparator, callback);
654         }
655         this._it1.sortAndRewind(comparator, afterSort.bind(this));
656     }
657 };
658
659 WebInspector.HeapSnapshotDiffNode = function(tree, className, baseAggregate, aggregate)
660 {
661     WebInspector.HeapSnapshotGridNode.call(this, tree, true);
662     this._name = className;
663     this._baseIndexes = baseAggregate ? baseAggregate.idxs : [];
664     this._indexes = aggregate ? aggregate.idxs : [];
665     this._provider = this._createNodesProvider(tree.baseSnapshot, tree.snapshot, aggregate ? aggregate.type : baseAggregate.type, className);
666 }
667
668 WebInspector.HeapSnapshotDiffNode.prototype = {
669     calculateDiff: function(dataGrid, callback)
670     {
671         var diff = dataGrid.snapshot.createDiff(this._name);
672
673         function diffCalculated(diffResult)
674         {
675             diff.dispose();
676             this._addedCount = diffResult.addedCount;
677             this._removedCount = diffResult.removedCount;
678             this._countDelta = diffResult.countDelta;
679             this._addedSize = diffResult.addedSize;
680             this._removedSize = diffResult.removedSize;
681             this._sizeDelta = diffResult.sizeDelta;
682             this._baseIndexes = null;
683             this._indexes = null;
684             callback(this._addedSize === 0 && this._removedSize === 0);
685         }
686         function baseSelfSizesReceived(baseSelfSizes)
687         {
688             diff.pushBaseSelfSizes(baseSelfSizes);
689             diff.calculate(diffCalculated.bind(this));
690         }
691         function baseIdsReceived(baseIds)
692         {
693             diff.pushBaseIds(baseIds);
694             dataGrid.snapshot.pushBaseIds(dataGrid.baseSnapshot.uid, this._name, baseIds);
695             dataGrid.baseSnapshot.nodeFieldValuesByIndex("selfSize", this._baseIndexes, baseSelfSizesReceived.bind(this));
696         }
697         function idsReceived(ids)
698         {
699             dataGrid.baseSnapshot.pushBaseIds(dataGrid.snapshot.uid, this._name, ids);
700         }
701         dataGrid.baseSnapshot.nodeFieldValuesByIndex("id", this._baseIndexes, baseIdsReceived.bind(this));
702         dataGrid.snapshot.nodeFieldValuesByIndex("id", this._indexes, idsReceived.bind(this));
703     },
704
705     _createChildNode: function(item, provider)
706     {
707         if (provider === this._provider._it1)
708             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, provider.snapshot, item);
709         else
710             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, provider.snapshot, null, item);
711     },
712
713     _createNodesProvider: function(baseSnapshot, snapshot, nodeType, nodeClassName)
714     {
715         var className = this._name;
716         return new WebInspector.HeapSnapshotIteratorsTuple(
717             createProvider(snapshot, baseSnapshot), createProvider(baseSnapshot, snapshot));
718
719         function createProvider(snapshot, otherSnapshot)
720         {
721             var otherSnapshotId = otherSnapshot.uid;
722             var provider = snapshot.createNodesProvider(
723                 "function (node) {" +
724                 "     return node.type === \"" + nodeType + "\" " +
725                 (nodeClassName !== null ? "&& node.className === \"" + nodeClassName + "\"" : "") +
726                 "         && !this.baseSnapshotHasNode(" + otherSnapshotId + ", \"" + className + "\", node.id);" +
727                 "}");
728             provider.snapshot = snapshot;
729             return provider;
730         }
731     },
732
733     _childHashForEntity: function(node)
734     {
735         return node.id;
736     },
737
738     _childHashForNode: function(childNode)
739     {
740         return childNode.snapshotNodeId;
741     },
742
743     comparator: function()
744     {
745         var sortAscending = this.dataGrid.sortOrder === "ascending";
746         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
747         var sortFields = {
748             object: ["id", sortAscending, "selfSize", false],
749             addedCount: ["selfSize", sortAscending, "id", true],
750             removedCount: ["selfSize", sortAscending, "id", true],
751             countDelta: ["selfSize", sortAscending, "id", true],
752             addedSize: ["selfSize", sortAscending, "id", true],
753             removedSize: ["selfSize", sortAscending, "id", true],
754             sizeDelta: ["selfSize", sortAscending, "id", true]
755         }[sortColumnIdentifier];
756         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
757     },
758
759     populateChildren: function(provider, howMany, atIndex, afterPopulate)
760     {
761         if (!provider && !howMany) {
762             var firstProviderPopulated = function()
763             {
764                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, this._defaultPopulateCount, atIndex, afterPopulate);
765             };
766             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, this._defaultPopulateCount, atIndex, firstProviderPopulated.bind(this), true);
767         } else if (!howMany) {
768             var firstProviderPopulated = function()
769             {
770                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, null, atIndex, afterPopulate);
771             };
772             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, null, atIndex, firstProviderPopulated.bind(this), true);
773         } else
774             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, provider, howMany, atIndex, afterPopulate);
775     },
776
777     _signForDelta: function(delta)
778     {
779         if (delta === 0)
780             return "";
781         if (delta > 0)
782             return "+";
783         else
784             return "\u2212";  // Math minus sign, same width as plus.
785     },
786
787     get data()
788     {
789         var data = {object: this._name};
790
791         data["addedCount"] = Number.withThousandsSeparator(this._addedCount);
792         data["removedCount"] = Number.withThousandsSeparator(this._removedCount);
793         data["countDelta"] = this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta));
794         data["addedSize"] = Number.withThousandsSeparator(this._addedSize);
795         data["removedSize"] = Number.withThousandsSeparator(this._removedSize);
796         data["sizeDelta"] = this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta));
797
798         return data;
799     }
800 };
801
802 WebInspector.HeapSnapshotDiffNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
803
804 WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node)
805 {
806     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
807     this._provider = this._createProvider(tree.snapshot, node.nodeIndex);
808     this._updateHasChildren();
809 };
810
811 WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
812     _createChildNode: function(item)
813     {
814         return new WebInspector.HeapSnapshotDominatorObjectNode(this.dataGrid, item);
815     },
816
817     _createProvider: function(snapshot, nodeIndex)
818     {
819         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
820         return snapshot.createNodesProviderForDominator(nodeIndex,
821             "function (node) {" +
822             "     return " + showHiddenData + " || !node.isHidden;" +
823             "}");
824     },
825
826     _childHashForEntity: function(node)
827     {
828         return node.id;
829     },
830
831     _childHashForNode: function(childNode)
832     {
833         return childNode.snapshotNodeId;
834     },
835
836     comparator: function()
837     {
838         var sortAscending = this.dataGrid.sortOrder === "ascending";
839         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
840         var sortFields = {
841             object: ["id", sortAscending, "retainedSize", false],
842             shallowSize: ["selfSize", sortAscending, "id", true],
843             retainedSize: ["retainedSize", sortAscending, "id", true]
844         }[sortColumnIdentifier];
845         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
846     },
847
848     _emptyData: function()
849     {
850         return {};
851     }
852 };
853
854 WebInspector.HeapSnapshotDominatorObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
855
856 function MixInSnapshotNodeFunctions(sourcePrototype, targetPrototype)
857 {
858     targetPrototype._childHashForEntity = sourcePrototype._childHashForEntity;
859     targetPrototype._childHashForNode = sourcePrototype._childHashForNode;
860     targetPrototype.comparator = sourcePrototype.comparator;
861     targetPrototype._createChildNode = sourcePrototype._createChildNode;
862     targetPrototype._createProvider = sourcePrototype._createProvider;
863     targetPrototype.dispose = sourcePrototype.dispose;
864     targetPrototype.populateChildren = sourcePrototype.populateChildren;
865     targetPrototype._saveChildren = sourcePrototype._saveChildren;
866     targetPrototype.sort = sourcePrototype.sort;
867 }