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