Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / HeapSnapshotClassDataGridNode.js
1 /*
2 * Copyright (C) 2016 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 WI.HeapSnapshotClassDataGridNode = class HeapSnapshotClassDataGridNode extends WI.DataGridNode
27 {
28     constructor(data, tree)
29     {
30         super(data, true);
31
32         this._data = data;
33         this._tree = tree;
34
35         this._batched = false;
36         this._instances = null;
37
38         this.addEventListener("populate", this._populate, this);
39     }
40
41     // Protected
42
43     get data() { return this._data; }
44
45     createCellContent(columnIdentifier)
46     {
47         if (columnIdentifier === "retainedSize") {
48             let size = this._data.retainedSize;
49             let fragment = document.createDocumentFragment();
50             let sizeElement = fragment.appendChild(document.createElement("span"));
51             sizeElement.classList.add("size");
52             sizeElement.textContent = Number.bytesToString(size);
53             let percentElement = fragment.appendChild(document.createElement("span"));
54             percentElement.classList.add("percentage");
55             percentElement.textContent = emDash;
56             return fragment;
57         }
58
59         if (columnIdentifier === "size")
60             return Number.bytesToString(this._data.size);
61
62         if (columnIdentifier === "className") {
63             const internal = false;
64             let {className, isObjectSubcategory} = this._data;
65             let fragment = document.createDocumentFragment();
66             let iconElement = fragment.appendChild(document.createElement("img"));
67             iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal, isObjectSubcategory));
68             let nameElement = fragment.appendChild(document.createElement("span"));
69             nameElement.classList.add("class-name");
70             nameElement.textContent = className;
71             return fragment;
72         }
73
74         return super.createCellContent(columnIdentifier);
75     }
76
77     sort()
78     {
79         if (this._batched) {
80             this._removeFetchMoreDataGridNode();
81             this._sortInstances();
82             this._updateBatchedChildren();
83             this._appendFetchMoreDataGridNode();
84             return;
85         }
86
87         let children = this.children;
88         children.sort(this._tree._sortComparator);
89
90         for (let i = 0; i < children.length; ++i) {
91             children[i]._recalculateSiblings(i);
92             children[i].sort();
93         }
94     }
95
96     removeCollectedNodes(collectedNodes)
97     {
98         let nodesToRemove = [];
99
100         this.forEachImmediateChild((dataGridNode) => {
101             if (dataGridNode instanceof WI.HeapSnapshotInstanceDataGridNode) {
102                 let heapSnapshotNode = dataGridNode.node;
103                 if (heapSnapshotNode.id in collectedNodes)
104                     nodesToRemove.push(dataGridNode);
105             }
106         });
107
108         if (nodesToRemove.length) {
109             for (let dataGridNode of nodesToRemove)
110                 this.removeChild(dataGridNode);
111         }
112
113         if (this._instances) {
114             this._instances = this._instances.filter((instance) => !(instance.id in collectedNodes));
115             this._fetchBatch(nodesToRemove.length);
116         }
117     }
118
119     updateCount(count)
120     {
121         if (count === this._data.count)
122             return;
123
124         if (!count) {
125             this._tree.removeChild(this);
126             return;
127         }
128
129         this._data.count = count;
130         this.needsRefresh();
131     }
132
133     // Private
134
135     _populate()
136     {
137         this.removeEventListener("populate", this._populate, this);
138
139         this._tree.heapSnapshot.instancesWithClassName(this._data.className, (instances) => {
140             // FIXME: <https://webkit.org/b/157905> Web Inspector: Provide a way to toggle between showing only live objects and live+dead objects
141             this._instances = instances.filter((instance) => !instance.dead);
142             this._sortInstances();
143
144             // Batch.
145             if (instances.length > WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit) {
146                 this._batched = true;
147                 this._fetchBatch(WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit);
148                 return;
149             }
150
151             for (let instance of this._instances)
152                 this.appendChild(new WI.HeapSnapshotInstanceDataGridNode(instance, this._tree));
153         });
154     }
155
156     _fetchBatch(batchSize)
157     {
158         if (this._batched && this.children.length)
159             this._removeFetchMoreDataGridNode();
160
161         let oldCount = this.children.length;
162         let newCount = oldCount + batchSize;
163
164         if (newCount >= this._instances.length) {
165             newCount = this._instances.length - 1;
166             this._batched = false;
167         }
168
169         let count = newCount - oldCount;
170         if (count) {
171             for (let i = 0; i <= count; ++i) {
172                 let instance = this._instances[oldCount + i];
173                 this.appendChild(new WI.HeapSnapshotInstanceDataGridNode(instance, this._tree));
174             }
175         }
176
177         if (this._batched)
178             this._appendFetchMoreDataGridNode();
179     }
180
181     _sortInstances()
182     {
183         this._instances.sort((a, b) => {
184             let fakeDataGridNodeA = {data: a};
185             let fakeDataGridNodeB = {data: b};
186             return this._tree._sortComparator(fakeDataGridNodeA, fakeDataGridNodeB);
187         });
188     }
189
190     _updateBatchedChildren()
191     {
192         let count = this.children.length;
193
194         this.removeChildren();
195
196         for (let i = 0; i < count; ++i)
197             this.appendChild(new WI.HeapSnapshotInstanceDataGridNode(this._instances[i], this._tree));
198     }
199
200     _removeFetchMoreDataGridNode()
201     {
202         console.assert(this.children[this.children.length - 1] instanceof WI.HeapSnapshotInstanceFetchMoreDataGridNode);
203
204         this.removeChild(this.children[this.children.length - 1]);
205     }
206
207     _appendFetchMoreDataGridNode()
208     {
209         console.assert(!(this.children[this.children.length - 1] instanceof WI.HeapSnapshotInstanceFetchMoreDataGridNode));
210
211         let count = this.children.length;
212         let totalCount = this._instances.length;
213         let remainingCount = totalCount - count;
214         let batchSize = remainingCount >= WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit ? WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit : 0;
215
216         this.appendChild(new WI.HeapSnapshotInstanceFetchMoreDataGridNode(this._tree, batchSize, remainingCount, this._fetchBatch.bind(this)));
217     }
218 };
219
220 WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit = 100;