36e51b076d82179eb6702298f448cd17cb1442e3
[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     _populate: function(event)
64     {
65         this.removeEventListener("populate", this._populate, this);
66         function sorted(ignored)
67         {
68             this.populateChildren();
69         }
70         this._provider.sortAndRewind(this.comparator(), sorted.bind(this));
71     },
72
73     populateChildren: function(provider, howMany, atIndex, afterPopulate, suppressNotifyAboutCompletion)
74     {
75         if (!howMany && provider) {
76             howMany = provider.instanceCount;
77             provider.instanceCount = 0;
78         }
79         provider = provider || this._provider;
80         if (!("instanceCount" in provider))
81             provider.instanceCount = 0;
82         howMany = howMany || this._defaultPopulateCount;
83         atIndex = atIndex || this.children.length;
84         var haveSavedChildren = !!this._savedChildren;
85         if (haveSavedChildren) {
86             haveSavedChildren = false;
87             for (var c in this._savedChildren) {
88                 haveSavedChildren = true;
89                 break;
90             }
91         }
92
93         var part = 0;
94         function callSerialize()
95         {
96             if (part >= howMany)
97                 return;
98             part += this._defaultPopulateCount;
99             provider.serializeNextItems(this._defaultPopulateCount, childrenRetrieved.bind(this));
100         }
101         function childrenRetrieved(items)
102         {
103             var length = items.totalLength;
104             for (var i = 0, l = items.length; i < l; ++i) {
105                 var item = items[i];
106                 if (haveSavedChildren) {
107                     var hash = this._childHashForEntity(item);
108                     if (hash in this._savedChildren) {
109                         this.insertChild(this._savedChildren[hash], atIndex++);
110                         continue;
111                     }
112                 }
113                 this.insertChild(this._createChildNode(item, provider, this), atIndex++);
114             }
115             provider.instanceCount += items.length;
116             if (part < howMany) {
117                 setTimeout(callSerialize.bind(this), 0);
118                 return;
119             }
120
121             if (items.hasNext)
122                 this.insertChild(new WebInspector.ShowMoreDataGridNode(this.populateChildren.bind(this, provider), this._defaultPopulateCount, length), atIndex++);
123             if (afterPopulate)
124                 afterPopulate();
125             if (!suppressNotifyAboutCompletion) {
126                 function notify()
127                 {
128                     this.dispatchEventToListeners("populate complete");
129                 }
130                 setTimeout(notify.bind(this), 0);
131             }
132         }
133         setTimeout(callSerialize.bind(this), 0);
134     },
135
136     _saveChildren: function()
137     {
138         this._savedChildren = {};
139         for (var i = 0, childrenCount = this.children.length; i < childrenCount; ++i) {
140             var child = this.children[i];
141             if (child.expanded)
142                 this._savedChildren[this._childHashForNode(child)] = child;
143         }
144     },
145
146     sort: function()
147     {
148         this.dataGrid.recursiveSortingEnter();
149         function afterSort(sorted)
150         {
151             if (!sorted) {
152                 this.dataGrid.recursiveSortingLeave();
153                 return;
154             }
155             this._saveChildren();
156             this.removeChildren();
157
158             function afterPopulate()
159             {
160                 for (var i = 0, l = this.children.length; i < l; ++i) {
161                     var child = this.children[i];
162                     if (child.expanded)
163                         child.sort();
164                 }
165                 this.dataGrid.recursiveSortingLeave();
166             }
167             this.populateChildren(this._provider, null, null, afterPopulate.bind(this));
168         }
169         this._provider.sortAndRewind(this.comparator(), afterSort.bind(this));
170     }
171 };
172
173 WebInspector.HeapSnapshotGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
174
175 WebInspector.HeapSnapshotGenericObjectNode = function(tree, node)
176 {
177     WebInspector.HeapSnapshotGridNode.call(this, tree, false);
178     this._name = node.name;
179     this._type = node.type;
180     this._shallowSize = node.selfSize;
181     this._retainedSize = node.retainedSize;
182     this.snapshotNodeId = node.id;
183     this.snapshotNodeIndex = node.nodeIndex;
184     if (this._type === "string")
185         this.hasHoverMessage = true;
186     else if (this._type === "object" && this.isDOMWindow(this._name)) {
187         this._name = this.shortenWindowURL(this._name, false);
188         this.hasHoverMessage = true;
189     } else if (node.flags & tree.snapshot.nodeFlags.canBeQueried)
190         this.hasHoverMessage = true;
191     if (node.flags & tree.snapshot.nodeFlags.detachedDOMTreeNode)
192         this.detachedDOMTreeNode = true;
193 };
194
195 WebInspector.HeapSnapshotGenericObjectNode.prototype = {
196     createCell: function(columnIdentifier)
197     {
198         var cell = columnIdentifier !== "object" ? WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier) : this._createObjectCell();
199         if (this._searchMatched)
200             cell.addStyleClass("highlight");
201         return cell;
202     },
203
204     _createObjectCell: function()
205     {
206         var cell = document.createElement("td");
207         cell.className = "object-column";
208         var div = document.createElement("div");
209         div.className = "source-code event-properties";
210         div.style.overflow = "visible";
211         var data = this.data["object"];
212         if (this._prefixObjectCell)
213             this._prefixObjectCell(div, data);
214         var valueSpan = document.createElement("span");
215         valueSpan.className = "value console-formatted-" + data.valueStyle;
216         valueSpan.textContent = data.value;
217         div.appendChild(valueSpan);
218         if (this._postfixObjectCell)
219             this._postfixObjectCell(div, data);
220         cell.appendChild(div);
221         cell.addStyleClass("disclosure");
222         if (this.depth)
223             cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
224         return cell;
225     },
226
227     get _countPercent()
228     {
229         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
230     },
231
232     get data()
233     {
234         var data = this._emptyData();
235
236         var value = this._name;
237         var valueStyle = "object";
238         switch (this._type) {
239         case "string":
240             value = "\"" + value + "\"";
241             valueStyle = "string";
242             break;
243         case "regexp":
244             value = "/" + value + "/";
245             valueStyle = "string";
246             break;
247         case "closure":
248             value = "function " + value + "()";
249             valueStyle = "function";
250             break;
251         case "number":
252             valueStyle = "number";
253             break;
254         case "hidden":
255             valueStyle = "null";
256             break;
257         case "array":
258             if (!value)
259                 value = "[]";
260             else
261                 value += " []";
262             break;
263         };
264         if (this.hasHoverMessage)
265             valueStyle += " highlight";
266         if (this.detachedDOMTreeNode)
267             valueStyle += " detached-dom-tree-node";
268         data["object"] = { valueStyle: valueStyle, value: value + ": @" + this.snapshotNodeId };
269
270         var view = this.dataGrid.snapshotView;
271         data["shallowSize"] = view.showShallowSizeAsPercent ? WebInspector.UIString("%.2f%%", this._shallowSizePercent) : Number.bytesToString(this._shallowSize);
272         data["retainedSize"] = view.showRetainedSizeAsPercent ? WebInspector.UIString("%.2f%%", this._retainedSizePercent) : Number.bytesToString(this._retainedSize);
273
274         return this._enhanceData ? this._enhanceData(data) : data;
275     },
276
277     queryObjectContent: function(callback)
278     {
279         if (this._type === "string")
280             callback(WebInspector.RemoteObject.fromPrimitiveValue(this._name));
281         else {
282             function formatResult(error, object)
283             {
284                 if (!error && object.type)
285                     callback(WebInspector.RemoteObject.fromPayload(object), !!error);
286                 else
287                     callback(WebInspector.RemoteObject.fromPrimitiveValue(WebInspector.UIString("Not available")));
288             }
289             ProfilerAgent.getObjectByHeapObjectId(this.snapshotNodeId, formatResult);
290         }
291     },
292
293     get _retainedSizePercent()
294     {
295         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
296     },
297
298     get _shallowSizePercent()
299     {
300         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
301     },
302
303     _updateHasChildren: function()
304     {
305         function isEmptyCallback(isEmpty)
306         {
307             this.hasChildren = !isEmpty;
308         }
309         this._provider.isEmpty(isEmptyCallback.bind(this));
310     },
311
312     isDOMWindow: function(fullName)
313     {
314         return fullName.substr(0, 9) === "DOMWindow";
315     },
316
317     shortenWindowURL: function(fullName, hasObjectId)
318     {
319         var startPos = fullName.indexOf("/");
320         var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
321         if (startPos !== -1 && endPos !== -1) {
322             var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
323             var url = fullURL.trimURL();
324             if (url.length > 40)
325                 url = url.trimMiddle(40);
326             return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
327         } else
328             return fullName;
329     }
330 }
331
332 WebInspector.HeapSnapshotGenericObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
333
334 WebInspector.HeapSnapshotObjectNode = function(tree, isFromBaseSnapshot, edge)
335 {
336     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, edge.node);
337     this._referenceName = edge.name;
338     this._referenceType = edge.type;
339     this._propertyAccessor = edge.propertyAccessor;
340     this._retainerNode = tree.showRetainingEdges;
341     this._isFromBaseSnapshot = isFromBaseSnapshot;
342     this._provider = this._createProvider(!isFromBaseSnapshot ? tree.snapshot : tree.baseSnapshot, edge.nodeIndex, tree);
343     this._updateHasChildren();
344 }
345
346 WebInspector.HeapSnapshotObjectNode.prototype = {
347     _createChildNode: function(item)
348     {
349         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, this._isFromBaseSnapshot, item);
350     },
351
352     _createProvider: function(snapshot, nodeIndex, tree)
353     {
354         var showHiddenData = WebInspector.settings.showHeapSnapshotObjectsHiddenProperties.get();
355         var filter = "function(edge) {" +
356             "    return !edge.isInvisible" +
357             "        && (" + showHiddenData + " || (!edge.isHidden && !edge.node.isHidden));" +
358             "}";
359         if (tree.showRetainingEdges)
360             return snapshot.createRetainingEdgesProvider(nodeIndex, filter);
361         else
362             return snapshot.createEdgesProvider(nodeIndex, filter);
363     },
364
365     _childHashForEntity: function(edge)
366     {
367         return edge.type + "#" + edge.name;
368     },
369
370     _childHashForNode: function(childNode)
371     {
372         return childNode._referenceType + "#" + childNode._referenceName;
373     },
374
375     comparator: function()
376     {
377         var sortAscending = this.dataGrid.sortOrder === "ascending";
378         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
379         var sortFields = {
380             object: ["!edgeName", sortAscending, "retainedSize", false],
381             count: ["!edgeName", true, "retainedSize", false],
382             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
383             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
384         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
385         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
386     },
387
388     _emptyData: function()
389     {
390         return {count:"", addedCount: "", removedCount: "", countDelta:"", addedSize: "", removedSize: "", sizeDelta: ""};
391     },
392
393     _enhanceData: function(data)
394     {
395         var name = this._referenceName;
396         if (name === "") name = "(empty)";
397         var nameClass = "name";
398         switch (this._referenceType) {
399         case "context":
400             nameClass = "console-formatted-number";
401             break;
402         case "internal":
403         case "hidden":
404             nameClass = "console-formatted-null";
405             break;
406         }
407         data["object"].nameClass = nameClass;
408         data["object"].name = name;
409         return data;
410     },
411
412     _prefixObjectCell: function(div, data)
413     {
414         if (this._retainerNode) {
415             var prefixSpan = document.createElement("span");
416             prefixSpan.textContent = WebInspector.UIString("retained by ");
417             div.appendChild(prefixSpan);
418             return;
419         }
420
421         var nameSpan = document.createElement("span");
422         nameSpan.className = data.nameClass;
423         nameSpan.textContent = data.name;
424         div.appendChild(nameSpan);
425
426         var separatorSpan = document.createElement("span");
427         separatorSpan.className = "separator";
428         separatorSpan.textContent = ": ";
429         div.appendChild(separatorSpan);
430     },
431
432     _postfixObjectCell: function(div, data)
433     {
434         if (this._retainerNode) {
435             var referenceTypeSpan = document.createElement("span");
436             referenceTypeSpan.className = "console-formatted-object";
437             referenceTypeSpan.textContent = this._propertyAccessor;
438             div.appendChild(referenceTypeSpan);
439         }
440     }
441 }
442
443 WebInspector.HeapSnapshotObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
444
445 WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node)
446 {
447     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
448     this._isDeletedNode = !!baseSnapshot;
449     this._provider = this._createProvider(baseSnapshot || snapshot, node.nodeIndex);
450     this._updateHasChildren();
451 };
452
453 WebInspector.HeapSnapshotInstanceNode.prototype = {
454     _createChildNode: function(item)
455     {
456         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, this._isDeletedNode, item);
457     },
458
459     _createProvider: function(snapshot, nodeIndex)
460     {
461         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
462         return snapshot.createEdgesProvider(
463             nodeIndex,
464             "function(edge) {" +
465             "    return !edge.isInvisible" +
466             "        && (" + showHiddenData + " || (!edge.isHidden && !edge.node.isHidden));" +
467             "}");
468     },
469
470     _childHashForEntity: function(edge)
471     {
472         return edge.type + "#" + edge.name;
473     },
474
475     _childHashForNode: function(childNode)
476     {
477         return childNode._referenceType + "#" + childNode._referenceName;
478     },
479
480     comparator: function()
481     {
482         var sortAscending = this.dataGrid.sortOrder === "ascending";
483         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
484         var sortFields = {
485             object: ["!edgeName", sortAscending, "retainedSize", false],
486             count: ["!edgeName", true, "retainedSize", false],
487             addedSize: ["selfSize", sortAscending, "!edgeName", true],
488             removedSize: ["selfSize", sortAscending, "!edgeName", true],
489             shallowSize: ["selfSize", sortAscending, "!edgeName", true],
490             retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
491         }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
492         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
493     },
494
495     _emptyData: function()
496     {
497         return {count:"", countDelta:"", sizeDelta: ""};
498     },
499
500     _enhanceData: function(data)
501     {
502         if (this._isDeletedNode) {
503             data["addedCount"] = "";
504             data["addedSize"] = "";
505             data["removedCount"] = "\u2022";
506             data["removedSize"] = Number.bytesToString(this._shallowSize);
507         } else {
508             data["addedCount"] = "\u2022";
509             data["addedSize"] = Number.bytesToString(this._shallowSize);
510             data["removedCount"] = "";
511             data["removedSize"] = "";
512         }
513         return data;
514     },
515
516     get isDeletedNode()
517     {
518         return this._isDeletedNode;
519     }
520 }
521
522 WebInspector.HeapSnapshotInstanceNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
523
524 WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate, aggregatesKey)
525 {
526     WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0);
527     this._name = className;
528     this._count = aggregate.count;
529     this._shallowSize = aggregate.self;
530     this._retainedSize = aggregate.maxRet;
531     this._provider = this._createNodesProvider(tree.snapshot, className, aggregatesKey);
532 }
533
534 WebInspector.HeapSnapshotConstructorNode.prototype = {
535     _createChildNode: function(item)
536     {
537         return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, this.dataGrid.snapshot, item);
538     },
539
540     _createNodesProvider: function(snapshot, className, aggregatesKey)
541     {
542         return snapshot.createNodesProviderForClass(className, aggregatesKey);
543     },
544
545     comparator: function()
546     {
547         var sortAscending = this.dataGrid.sortOrder === "ascending";
548         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
549         var sortFields = {
550             object: ["id", sortAscending, "retainedSize", false],
551             count: ["id", true, "retainedSize", false],
552             shallowSize: ["selfSize", sortAscending, "id", true],
553             retainedSize: ["retainedSize", sortAscending, "id", true]
554         }[sortColumnIdentifier];
555         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
556     },
557
558     _childHashForEntity: function(node)
559     {
560         return node.id;
561     },
562
563     _childHashForNode: function(childNode)
564     {
565         return childNode.snapshotNodeId;
566     },
567
568     get data()
569     {
570         var data = {object: this._name, count: this._count};
571         var view = this.dataGrid.snapshotView;
572         data["count"] = view.showCountAsPercent ? WebInspector.UIString("%.2f%%", this._countPercent) : this._count;
573         data["shallowSize"] = view.showShallowSizeAsPercent ? WebInspector.UIString("%.2f%%", this._shallowSizePercent) : Number.bytesToString(this._shallowSize);
574         data["retainedSize"] = "> " + (view.showRetainedSizeAsPercent ? WebInspector.UIString("%.2f%%", this._retainedSizePercent) : Number.bytesToString(this._retainedSize));
575         return data;
576     },
577
578     get _countPercent()
579     {
580         return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
581     },
582
583     get _retainedSizePercent()
584     {
585         return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
586     },
587
588     get _shallowSizePercent()
589     {
590         return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
591     }
592 };
593
594 WebInspector.HeapSnapshotConstructorNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
595
596 WebInspector.HeapSnapshotIteratorsTuple = function(it1, it2)
597 {
598     this._it1 = it1;
599     this._it2 = it2;
600 }
601
602 WebInspector.HeapSnapshotIteratorsTuple.prototype = {
603     dispose: function()
604     {
605         this._it1.dispose();
606         this._it2.dispose();
607     },
608
609     sortAndRewind: function(comparator, callback)
610     {
611         function afterSort(ignored)
612         {
613             this._it2.sortAndRewind(comparator, callback);
614         }
615         this._it1.sortAndRewind(comparator, afterSort.bind(this));
616     }
617 };
618
619 WebInspector.HeapSnapshotDiffNode = function(tree, className, baseAggregate, aggregate)
620 {
621     WebInspector.HeapSnapshotGridNode.call(this, tree, true);
622     this._name = className;
623     this._baseIndexes = baseAggregate ? baseAggregate.idxs : [];
624     this._indexes = aggregate ? aggregate.idxs : [];
625     this._provider = this._createNodesProvider(tree.baseSnapshot, tree.snapshot, aggregate ? aggregate.type : baseAggregate.type, className);
626 }
627
628 WebInspector.HeapSnapshotDiffNode.prototype = {
629     calculateDiff: function(dataGrid, callback)
630     {
631         var diff = dataGrid.snapshot.createDiff(this._name);
632
633         function diffCalculated(diffResult)
634         {
635             diff.dispose();
636             this._addedCount = diffResult.addedCount;
637             this._removedCount = diffResult.removedCount;
638             this._countDelta = diffResult.countDelta;
639             this._addedSize = diffResult.addedSize;
640             this._removedSize = diffResult.removedSize;
641             this._sizeDelta = diffResult.sizeDelta;
642             this._baseIndexes = null;
643             this._indexes = null;
644             callback(this._addedSize === 0 && this._removedSize === 0);
645         }
646         function baseSelfSizesReceived(baseSelfSizes)
647         {
648             diff.pushBaseSelfSizes(baseSelfSizes);
649             diff.calculate(diffCalculated.bind(this));
650         }
651         function baseIdsReceived(baseIds)
652         {
653             diff.pushBaseIds(baseIds);
654             dataGrid.snapshot.pushBaseIds(dataGrid.baseSnapshot.uid, this._name, baseIds);
655             dataGrid.baseSnapshot.nodeFieldValuesByIndex("selfSize", this._baseIndexes, baseSelfSizesReceived.bind(this));
656         }
657         function idsReceived(ids)
658         {
659             dataGrid.baseSnapshot.pushBaseIds(dataGrid.snapshot.uid, this._name, ids);
660         }
661         dataGrid.baseSnapshot.nodeFieldValuesByIndex("id", this._baseIndexes, baseIdsReceived.bind(this));
662         dataGrid.snapshot.nodeFieldValuesByIndex("id", this._indexes, idsReceived.bind(this));
663     },
664
665     _createChildNode: function(item, provider)
666     {
667         if (provider === this._provider._it1)
668             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, provider.snapshot, item);
669         else
670             return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, provider.snapshot, null, item);
671     },
672
673     _createNodesProvider: function(baseSnapshot, snapshot, nodeType, nodeClassName)
674     {
675         var className = this._name;
676         return new WebInspector.HeapSnapshotIteratorsTuple(
677             createProvider(snapshot, baseSnapshot), createProvider(baseSnapshot, snapshot));
678
679         function createProvider(snapshot, otherSnapshot)
680         {
681             var otherSnapshotId = otherSnapshot.uid;
682             var provider = snapshot.createNodesProvider(
683                 "function (node) {" +
684                 "     return node.type === \"" + nodeType + "\" " +
685                 (nodeClassName !== null ? "&& node.className === \"" + nodeClassName + "\"" : "") +
686                 "         && !this.baseSnapshotHasNode(" + otherSnapshotId + ", \"" + className + "\", node.id);" +
687                 "}");
688             provider.snapshot = snapshot;
689             return provider;
690         }
691     },
692
693     _childHashForEntity: function(node)
694     {
695         return node.id;
696     },
697
698     _childHashForNode: function(childNode)
699     {
700         return childNode.snapshotNodeId;
701     },
702
703     comparator: function()
704     {
705         var sortAscending = this.dataGrid.sortOrder === "ascending";
706         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
707         var sortFields = {
708             object: ["id", sortAscending, "selfSize", false],
709             addedCount: ["selfSize", sortAscending, "id", true],
710             removedCount: ["selfSize", sortAscending, "id", true],
711             countDelta: ["selfSize", sortAscending, "id", true],
712             addedSize: ["selfSize", sortAscending, "id", true],
713             removedSize: ["selfSize", sortAscending, "id", true],
714             sizeDelta: ["selfSize", sortAscending, "id", true]
715         }[sortColumnIdentifier];
716         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
717     },
718
719     populateChildren: function(provider, howMany, atIndex, afterPopulate)
720     {
721         if (!provider && !howMany) {
722             var firstProviderPopulated = function()
723             {
724                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, this._defaultPopulateCount, atIndex, afterPopulate);
725             };
726             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, this._defaultPopulateCount, atIndex, firstProviderPopulated.bind(this), true);
727         } else if (!howMany) {
728             var firstProviderPopulated = function()
729             {
730                 WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, null, atIndex, afterPopulate);
731             };
732             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, null, atIndex, firstProviderPopulated.bind(this), true);
733         } else
734             WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, provider, howMany, atIndex, afterPopulate);
735     },
736
737     _signForDelta: function(delta)
738     {
739         if (delta === 0)
740             return "";
741         if (delta > 0)
742             return "+";
743         else
744             return "\u2212";  // Math minus sign, same width as plus.
745     },
746
747     get data()
748     {
749         var data = {object: this._name};
750
751         data["addedCount"] = this._addedCount;
752         data["removedCount"] = this._removedCount;
753         data["countDelta"] = WebInspector.UIString("%s%d", this._signForDelta(this._countDelta), Math.abs(this._countDelta));
754         data["addedSize"] = Number.bytesToString(this._addedSize);
755         data["removedSize"] = Number.bytesToString(this._removedSize);
756         data["sizeDelta"] = WebInspector.UIString("%s%s", this._signForDelta(this._sizeDelta), Number.bytesToString(Math.abs(this._sizeDelta)));
757
758         return data;
759     }
760 };
761
762 WebInspector.HeapSnapshotDiffNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype;
763
764 WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node)
765 {
766     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
767     this._provider = this._createProvider(tree.snapshot, node.nodeIndex);
768     this._updateHasChildren();
769 };
770
771 WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
772     _createChildNode: function(item)
773     {
774         return new WebInspector.HeapSnapshotDominatorObjectNode(this.dataGrid, item);
775     },
776
777     _createProvider: function(snapshot, nodeIndex)
778     {
779         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
780         return snapshot.createNodesProviderForDominator(nodeIndex,
781             "function (node) {" +
782             "     return " + showHiddenData + " || !node.isHidden;" +
783             "}");
784     },
785
786     _childHashForEntity: function(node)
787     {
788         return node.id;
789     },
790
791     _childHashForNode: function(childNode)
792     {
793         return childNode.snapshotNodeId;
794     },
795
796     comparator: function()
797     {
798         var sortAscending = this.dataGrid.sortOrder === "ascending";
799         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
800         var sortFields = {
801             object: ["id", sortAscending, "retainedSize", false],
802             shallowSize: ["selfSize", sortAscending, "id", true],
803             retainedSize: ["retainedSize", sortAscending, "id", true]
804         }[sortColumnIdentifier];
805         return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
806     },
807
808     _emptyData: function()
809     {
810         return {};
811     }
812 };
813
814 WebInspector.HeapSnapshotDominatorObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
815
816 function MixInSnapshotNodeFunctions(sourcePrototype, targetPrototype)
817 {
818     targetPrototype._childHashForEntity = sourcePrototype._childHashForEntity;
819     targetPrototype._childHashForNode = sourcePrototype._childHashForNode;
820     targetPrototype.comparator = sourcePrototype.comparator;
821     targetPrototype._createChildNode = sourcePrototype._createChildNode;
822     targetPrototype._createProvider = sourcePrototype._createProvider;
823     targetPrototype.dispose = sourcePrototype.dispose;
824     targetPrototype.populateChildren = sourcePrototype.populateChildren;
825     targetPrototype._saveChildren = sourcePrototype._saveChildren;
826     targetPrototype.sort = sourcePrototype.sort;
827 }