/* * Copyright (C) 2011 Google 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: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT * OWNER OR 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. */ /** * @constructor * @extends {WebInspector.DataGridNode} * @param {WebInspector.HeapSnapshotSortableDataGrid} tree * @param {boolean} hasChildren */ WebInspector.HeapSnapshotGridNode = function(tree, hasChildren) { WebInspector.DataGridNode.call(this, null, hasChildren); this._dataGrid = tree; this._instanceCount = 0; this._savedChildren = null; /** * List of position ranges for all visible nodes: [startPos1, endPos1),...,[startPosN, endPosN) * Position is an item position in the provider. */ this._retrievedChildrenRanges = []; } WebInspector.HeapSnapshotGridNode.Events = { PopulateComplete: "PopulateComplete" } WebInspector.HeapSnapshotGridNode.prototype = { /** * @return {WebInspector.HeapSnapshotProviderProxy} */ createProvider: function() { throw new Error("Needs implemented."); }, /** * @return {WebInspector.HeapSnapshotProviderProxy} */ _provider: function() { if (!this._providerObject) this._providerObject = this.createProvider(); return this._providerObject; }, createCell: function(columnIdentifier) { var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); if (this._searchMatched) cell.addStyleClass("highlight"); return cell; }, collapse: function() { WebInspector.DataGridNode.prototype.collapse.call(this); this._dataGrid.updateVisibleNodes(); }, dispose: function() { if (this._provider()) this._provider().dispose(); for (var node = this.children[0]; node; node = node.traverseNextNode(true, this, true)) if (node.dispose) node.dispose(); }, _reachableFromWindow: false, queryObjectContent: function(callback) { }, /** * @override */ wasDetached: function() { this._dataGrid.nodeWasDetached(this); }, _toPercentString: function(num) { return num.toFixed(0) + "\u2009%"; // \u2009 is a thin space. }, /** * @param {number} nodePosition */ childForPosition: function(nodePosition) { var indexOfFirsChildInRange = 0; for (var i = 0; i < this._retrievedChildrenRanges.length; i++) { var range = this._retrievedChildrenRanges[i]; if (range.from <= nodePosition && nodePosition < range.to) { var childIndex = indexOfFirsChildInRange + nodePosition - range.from; return this.children[childIndex]; } indexOfFirsChildInRange += range.to - range.from + 1; } return null; }, _createValueCell: function(columnIdentifier) { var cell = document.createElement("td"); cell.className = columnIdentifier + "-column"; if (this.dataGrid.snapshot.totalSize !== 0) { var div = document.createElement("div"); var valueSpan = document.createElement("span"); valueSpan.textContent = this.data[columnIdentifier]; div.appendChild(valueSpan); var percentColumn = columnIdentifier + "-percent"; if (percentColumn in this.data) { var percentSpan = document.createElement("span"); percentSpan.className = "percent-column"; percentSpan.textContent = this.data[percentColumn]; div.appendChild(percentSpan); div.addStyleClass("heap-snapshot-multiple-values"); } cell.appendChild(div); } return cell; }, populate: function(event) { if (this._populated) return; this._populated = true; function sorted() { this._populateChildren(); } this._provider().sortAndRewind(this.comparator(), sorted.bind(this)); }, expandWithoutPopulate: function(callback) { // Make sure default populate won't take action. this._populated = true; this.expand(); this._provider().sortAndRewind(this.comparator(), callback); }, /** * @param {?number} fromPosition * @param {?number} toPosition */ _populateChildren: function(fromPosition, toPosition, afterPopulate) { fromPosition = fromPosition || 0; toPosition = toPosition || fromPosition + this._dataGrid.defaultPopulateCount(); var firstNotSerializedPosition = fromPosition; function serializeNextChunk() { if (firstNotSerializedPosition >= toPosition) return; var end = Math.min(firstNotSerializedPosition + this._dataGrid.defaultPopulateCount(), toPosition); this._provider().serializeItemsRange(firstNotSerializedPosition, end, childrenRetrieved.bind(this)); firstNotSerializedPosition = end; } function insertRetrievedChild(item, insertionIndex) { if (this._savedChildren) { var hash = this._childHashForEntity(item); if (hash in this._savedChildren) { this.insertChild(this._savedChildren[hash], insertionIndex); return; } } this.insertChild(this._createChildNode(item), insertionIndex); } function insertShowMoreButton(from, to, insertionIndex) { var button = new WebInspector.ShowMoreDataGridNode(this._populateChildren.bind(this), from, to, this._dataGrid.defaultPopulateCount()); this.insertChild(button, insertionIndex); } function childrenRetrieved(items) { var itemIndex = 0; var itemPosition = items.startPosition; var insertionIndex = 0; if (!this._retrievedChildrenRanges.length) { if (items.startPosition > 0) { this._retrievedChildrenRanges.push({from: 0, to: 0}); insertShowMoreButton.call(this, 0, items.startPosition, insertionIndex++); } this._retrievedChildrenRanges.push({from: items.startPosition, to: items.endPosition}); for (var i = 0, l = items.length; i < l; ++i) insertRetrievedChild.call(this, items[i], insertionIndex++); if (items.endPosition < items.totalLength) insertShowMoreButton.call(this, items.endPosition, items.totalLength, insertionIndex++); } else { var rangeIndex = 0; var found = false; var range; while (rangeIndex < this._retrievedChildrenRanges.length) { range = this._retrievedChildrenRanges[rangeIndex]; if (range.to >= itemPosition) { found = true; break; } insertionIndex += range.to - range.from; // Skip the button if there is one. if (range.to < items.totalLength) insertionIndex += 1; ++rangeIndex; } if (!found || items.startPosition < range.from) { // Update previous button. this.children[insertionIndex - 1].setEndPosition(items.startPosition); insertShowMoreButton.call(this, items.startPosition, found ? range.from : items.totalLength, insertionIndex); range = {from: items.startPosition, to: items.startPosition}; if (!found) rangeIndex = this._retrievedChildrenRanges.length; this._retrievedChildrenRanges.splice(rangeIndex, 0, range); } else { insertionIndex += itemPosition - range.from; } // At this point insertionIndex is always an index before button or between nodes. // Also it is always true here that range.from <= itemPosition <= range.to // Stretch the range right bound to include all new items. while (range.to < items.endPosition) { // Skip already added nodes. var skipCount = range.to - itemPosition; insertionIndex += skipCount; itemIndex += skipCount; itemPosition = range.to; // We're at the position before button: ...x