/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ WI.HeapSnapshotClassDataGridNode = class HeapSnapshotClassDataGridNode extends WI.DataGridNode { constructor(data, tree) { super(data, true); this._data = data; this._tree = tree; this._batched = false; this._instances = null; this.addEventListener("populate", this._populate, this); } // Protected get data() { return this._data; } createCellContent(columnIdentifier) { if (columnIdentifier === "retainedSize") { let size = this._data.retainedSize; let fragment = document.createDocumentFragment(); let sizeElement = fragment.appendChild(document.createElement("span")); sizeElement.classList.add("size"); sizeElement.textContent = Number.bytesToString(size); let percentElement = fragment.appendChild(document.createElement("span")); percentElement.classList.add("percentage"); percentElement.textContent = emDash; return fragment; } if (columnIdentifier === "size") return Number.bytesToString(this._data.size); if (columnIdentifier === "className") { let {className} = this._data; let fragment = document.createDocumentFragment(); let iconElement = fragment.appendChild(document.createElement("img")); iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className)); let nameElement = fragment.appendChild(document.createElement("span")); nameElement.classList.add("class-name"); nameElement.textContent = className; return fragment; } return super.createCellContent(columnIdentifier); } sort() { if (this._batched) { this._removeFetchMoreDataGridNode(); this._sortInstances(); this._updateBatchedChildren(); this._appendFetchMoreDataGridNode(); return; } let children = this.children; children.sort(this._tree._sortComparator); for (let i = 0; i < children.length; ++i) { children[i]._recalculateSiblings(i); children[i].sort(); } } removeCollectedNodes(collectedNodes) { let nodesToRemove = []; this.forEachImmediateChild((dataGridNode) => { if (dataGridNode instanceof WI.HeapSnapshotInstanceDataGridNode) { let heapSnapshotNode = dataGridNode.node; if (heapSnapshotNode.id in collectedNodes) nodesToRemove.push(dataGridNode); } }); if (nodesToRemove.length) { for (let dataGridNode of nodesToRemove) this.removeChild(dataGridNode); } if (this._instances) { this._instances = this._instances.filter((instance) => !(instance.id in collectedNodes)); this._fetchBatch(nodesToRemove.length); } } updateCount(count) { if (count === this._data.count) return; if (!count) { this._tree.removeChild(this); return; } this._data.count = count; this.needsRefresh(); } // Private _populate() { this.removeEventListener("populate", this._populate, this); this._tree.heapSnapshot.instancesWithClassName(this._data.className, (instances) => { // FIXME: Web Inspector: Provide a way to toggle between showing only live objects and live+dead objects this._instances = instances.filter((instance) => !instance.dead); this._sortInstances(); // Batch. if (instances.length > WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit) { this._batched = true; this._fetchBatch(WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit); return; } for (let instance of this._instances) this.appendChild(new WI.HeapSnapshotInstanceDataGridNode(instance, this._tree)); }); } _fetchBatch(batchSize) { if (this._batched && this.children.length) this._removeFetchMoreDataGridNode(); let oldCount = this.children.length; let newCount = oldCount + batchSize; if (newCount >= this._instances.length) { newCount = this._instances.length - 1; this._batched = false; } let count = newCount - oldCount; if (count) { for (let i = 0; i <= count; ++i) { let instance = this._instances[oldCount + i]; this.appendChild(new WI.HeapSnapshotInstanceDataGridNode(instance, this._tree)); } } if (this._batched) this._appendFetchMoreDataGridNode(); } _sortInstances() { this._instances.sort((a, b) => { let fakeDataGridNodeA = {data: a}; let fakeDataGridNodeB = {data: b}; return this._tree._sortComparator(fakeDataGridNodeA, fakeDataGridNodeB); }); } _updateBatchedChildren() { let count = this.children.length; this.removeChildren(); for (let i = 0; i < count; ++i) this.appendChild(new WI.HeapSnapshotInstanceDataGridNode(this._instances[i], this._tree)); } _removeFetchMoreDataGridNode() { console.assert(this.children[this.children.length - 1] instanceof WI.HeapSnapshotInstanceFetchMoreDataGridNode); this.removeChild(this.children[this.children.length - 1]); } _appendFetchMoreDataGridNode() { console.assert(!(this.children[this.children.length - 1] instanceof WI.HeapSnapshotInstanceFetchMoreDataGridNode)); let count = this.children.length; let totalCount = this._instances.length; let remainingCount = totalCount - count; let batchSize = remainingCount >= WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit ? WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit : 0; this.appendChild(new WI.HeapSnapshotInstanceFetchMoreDataGridNode(this._tree, batchSize, remainingCount, this._fetchBatch.bind(this))); } }; WI.HeapSnapshotClassDataGridNode.ChildrenBatchLimit = 100;