summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/profiler
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/third_party/WebKit/Source/devtools/front_end/profiler
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/profiler')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileBottomUpDataGrid.js296
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileDataGrid.js450
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js650
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileTopDownDataGrid.js144
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js897
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasProfileView.js1276
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasReplayStateView.js529
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotCommon.js349
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js1155
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js1616
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotProxy.js550
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js2285
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js232
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js1350
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/AllocationProfile.js422
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshot.js2293
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotLoader.js269
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorker.js49
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.js111
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/JSHeapSnapshot.js824
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/profiler/module.json31
21 files changed, 15778 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileBottomUpDataGrid.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileBottomUpDataGrid.js
new file mode 100644
index 00000000000..5994476145a
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileBottomUpDataGrid.js
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 280 North 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. ``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
+ * 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.
+ */
+
+// Bottom Up Profiling shows the entire callstack backwards:
+// The root node is a representation of each individual function called, and each child of that node represents
+// a reverse-callstack showing how many of those calls came from it. So, unlike top-down, the statistics in
+// each child still represent the root node. We have to be particularly careful of recursion with this mode
+// because a root node can represent itself AND an ancestor.
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridNode}
+ * @param {!ProfilerAgent.CPUProfileNode} profileNode
+ * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
+ */
+WebInspector.BottomUpProfileDataGridNode = function(profileNode, owningTree)
+{
+ WebInspector.ProfileDataGridNode.call(this, profileNode, owningTree, this._willHaveChildren(profileNode));
+
+ this._remainingNodeInfos = [];
+}
+
+WebInspector.BottomUpProfileDataGridNode.prototype = {
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ */
+ _takePropertiesFromProfileDataGridNode: function(profileDataGridNode)
+ {
+ this._save();
+
+ this.selfTime = profileDataGridNode.selfTime;
+ this.totalTime = profileDataGridNode.totalTime;
+ },
+
+ /**
+ * When focusing, we keep just the members of the callstack.
+ * @param {!WebInspector.ProfileDataGridNode} child
+ */
+ _keepOnlyChild: function(child)
+ {
+ this._save();
+
+ this.removeChildren();
+ this.appendChild(child);
+ },
+
+ _exclude: function(aCallUID)
+ {
+ if (this._remainingNodeInfos)
+ this.populate();
+
+ this._save();
+
+ var children = this.children;
+ var index = this.children.length;
+
+ while (index--)
+ children[index]._exclude(aCallUID);
+
+ var child = this.childrenByCallUID[aCallUID];
+
+ if (child)
+ this._merge(child, true);
+ },
+
+ _restore: function()
+ {
+ WebInspector.ProfileDataGridNode.prototype._restore();
+
+ if (!this.children.length)
+ this.hasChildren = this._willHaveChildren(this.profileNode);
+ },
+
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} child
+ * @param {boolean} shouldAbsorb
+ */
+ _merge: function(child, shouldAbsorb)
+ {
+ this.selfTime -= child.selfTime;
+
+ WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shouldAbsorb);
+ },
+
+ _sharedPopulate: function()
+ {
+ var remainingNodeInfos = this._remainingNodeInfos;
+ var count = remainingNodeInfos.length;
+
+ for (var index = 0; index < count; ++index) {
+ var nodeInfo = remainingNodeInfos[index];
+ var ancestor = nodeInfo.ancestor;
+ var focusNode = nodeInfo.focusNode;
+ var child = this.findChild(ancestor);
+
+ // If we already have this child, then merge the data together.
+ if (child) {
+ var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor;
+
+ child.selfTime += focusNode.selfTime;
+
+ if (!totalTimeAccountedFor)
+ child.totalTime += focusNode.totalTime;
+ } else {
+ // If not, add it as a true ancestor.
+ // In heavy mode, we take our visual identity from ancestor node...
+ child = new WebInspector.BottomUpProfileDataGridNode(ancestor, this.tree);
+
+ if (ancestor !== focusNode) {
+ // but the actual statistics from the "root" node (bottom of the callstack).
+ child.selfTime = focusNode.selfTime;
+ child.totalTime = focusNode.totalTime;
+ }
+
+ this.appendChild(child);
+ }
+
+ var parent = ancestor.parent;
+ if (parent && parent.parent) {
+ nodeInfo.ancestor = parent;
+ child._remainingNodeInfos.push(nodeInfo);
+ }
+ }
+
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i].buildData();
+
+ delete this._remainingNodeInfos;
+ },
+
+ _willHaveChildren: function(profileNode)
+ {
+ // In bottom up mode, our parents are our children since we display an inverted tree.
+ // However, we don't want to show the very top parent since it is redundant.
+ return !!(profileNode.parent && profileNode.parent.parent);
+ },
+
+ __proto__: WebInspector.ProfileDataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridTree}
+ * @param {!WebInspector.CPUProfileView} profileView
+ * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode
+ */
+WebInspector.BottomUpProfileDataGridTree = function(profileView, rootProfileNode)
+{
+ WebInspector.ProfileDataGridTree.call(this, profileView, rootProfileNode);
+
+ // Iterate each node in pre-order.
+ var profileNodeUIDs = 0;
+ var profileNodeGroups = [[], [rootProfileNode]];
+ var visitedProfileNodesForCallUID = {};
+
+ this._remainingNodeInfos = [];
+
+ for (var profileNodeGroupIndex = 0; profileNodeGroupIndex < profileNodeGroups.length; ++profileNodeGroupIndex) {
+ var parentProfileNodes = profileNodeGroups[profileNodeGroupIndex];
+ var profileNodes = profileNodeGroups[++profileNodeGroupIndex];
+ var count = profileNodes.length;
+
+ for (var index = 0; index < count; ++index) {
+ var profileNode = profileNodes[index];
+
+ if (!profileNode.UID)
+ profileNode.UID = ++profileNodeUIDs;
+
+ if (profileNode.parent) {
+ // The total time of this ancestor is accounted for if we're in any form of recursive cycle.
+ var visitedNodes = visitedProfileNodesForCallUID[profileNode.callUID];
+ var totalTimeAccountedFor = false;
+
+ if (!visitedNodes) {
+ visitedNodes = {}
+ visitedProfileNodesForCallUID[profileNode.callUID] = visitedNodes;
+ } else {
+ // The total time for this node has already been accounted for iff one of it's parents has already been visited.
+ // We can do this check in this style because we are traversing the tree in pre-order.
+ var parentCount = parentProfileNodes.length;
+ for (var parentIndex = 0; parentIndex < parentCount; ++parentIndex) {
+ if (visitedNodes[parentProfileNodes[parentIndex].UID]) {
+ totalTimeAccountedFor = true;
+ break;
+ }
+ }
+ }
+
+ visitedNodes[profileNode.UID] = true;
+
+ this._remainingNodeInfos.push({ ancestor:profileNode, focusNode:profileNode, totalTimeAccountedFor:totalTimeAccountedFor });
+ }
+
+ var children = profileNode.children;
+ if (children.length) {
+ profileNodeGroups.push(parentProfileNodes.concat([profileNode]))
+ profileNodeGroups.push(children);
+ }
+ }
+ }
+
+ // Populate the top level nodes.
+ var any = /** @type {*} */(this);
+ var node = /** @type {!WebInspector.ProfileDataGridNode} */(any);
+ WebInspector.BottomUpProfileDataGridNode.prototype.populate.call(node);
+
+ return this;
+}
+
+WebInspector.BottomUpProfileDataGridTree.prototype = {
+ /**
+ * When focusing, we keep the entire callstack up to this ancestor.
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ */
+ focus: function(profileDataGridNode)
+ {
+ if (!profileDataGridNode)
+ return;
+
+ this._save();
+
+ var currentNode = profileDataGridNode;
+ var focusNode = profileDataGridNode;
+
+ while (currentNode.parent && (currentNode instanceof WebInspector.ProfileDataGridNode)) {
+ currentNode._takePropertiesFromProfileDataGridNode(profileDataGridNode);
+
+ focusNode = currentNode;
+ currentNode = currentNode.parent;
+
+ if (currentNode instanceof WebInspector.ProfileDataGridNode)
+ currentNode._keepOnlyChild(focusNode);
+ }
+
+ this.children = [focusNode];
+ this.totalTime = profileDataGridNode.totalTime;
+ },
+
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ */
+ exclude: function(profileDataGridNode)
+ {
+ if (!profileDataGridNode)
+ return;
+
+ this._save();
+
+ var excludedCallUID = profileDataGridNode.callUID;
+ var excludedTopLevelChild = this.childrenByCallUID[excludedCallUID];
+
+ // If we have a top level node that is excluded, get rid of it completely (not keeping children),
+ // since bottom up data relies entirely on the root node.
+ if (excludedTopLevelChild)
+ this.children.remove(excludedTopLevelChild);
+
+ var children = this.children;
+ var count = children.length;
+
+ for (var index = 0; index < count; ++index)
+ children[index]._exclude(excludedCallUID);
+
+ if (this.lastComparator)
+ this.sort(this.lastComparator, true);
+ },
+
+ buildData: function()
+ {
+ },
+
+ _sharedPopulate: WebInspector.BottomUpProfileDataGridNode.prototype._sharedPopulate,
+
+ __proto__: WebInspector.ProfileDataGridTree.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileDataGrid.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileDataGrid.js
new file mode 100644
index 00000000000..9fd11dc9135
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileDataGrid.js
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2009 280 North 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. ``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
+ * 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 {!ProfilerAgent.CPUProfileNode} profileNode
+ * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
+ * @param {boolean} hasChildren
+ */
+WebInspector.ProfileDataGridNode = function(profileNode, owningTree, hasChildren)
+{
+ this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ this.profileNode = profileNode;
+
+ WebInspector.DataGridNode.call(this, null, hasChildren);
+
+ this.tree = owningTree;
+
+ this.childrenByCallUID = {};
+ this.lastComparator = null;
+
+ this.callUID = profileNode.callUID;
+ this.selfTime = profileNode.selfTime;
+ this.totalTime = profileNode.totalTime;
+ this.functionName = profileNode.functionName;
+ this._deoptReason = (!profileNode.deoptReason || profileNode.deoptReason === "no reason") ? "" : profileNode.deoptReason;
+ this.url = profileNode.url;
+}
+
+WebInspector.ProfileDataGridNode.prototype = {
+ /**
+ * @override
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = this._createValueCell(columnIdentifier) || WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+
+ if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
+ cell.classList.add("highlight");
+ else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
+ cell.classList.add("highlight");
+
+ if (columnIdentifier !== "function")
+ return cell;
+
+ if (this._deoptReason)
+ cell.classList.add("not-optimized");
+
+ if (this.profileNode._searchMatchedFunctionColumn)
+ cell.classList.add("highlight");
+
+ if (this.profileNode.scriptId !== "0") {
+ var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
+ var columnNumber = this.profileNode.columnNumber ? this.profileNode.columnNumber - 1 : 0;
+ var location = new WebInspector.DebuggerModel.Location(/** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget()), this.profileNode.scriptId, lineNumber, columnNumber);
+ var urlElement = this.tree.profileView._linkifier.linkifyRawLocation(location, "profile-node-file");
+ if (!urlElement)
+ urlElement = this.tree.profileView._linkifier.linkifyLocation(this._target, this.profileNode.url, lineNumber, columnNumber, "profile-node-file");
+ urlElement.style.maxWidth = "75%";
+ cell.insertBefore(urlElement, cell.firstChild);
+ }
+
+ return cell;
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {?Element}
+ */
+ _createValueCell: function(columnIdentifier)
+ {
+ if (columnIdentifier !== "self" && columnIdentifier !== "total")
+ return null;
+
+ var cell = document.createElement("td");
+ cell.className = "numeric-column";
+ 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.classList.add("profile-multiple-values");
+ }
+ cell.appendChild(div);
+ return cell;
+ },
+
+ buildData: function()
+ {
+ function formatMilliseconds(time)
+ {
+ return WebInspector.UIString("%.1f\u2009ms", time);
+ }
+ function formatPercent(value)
+ {
+ return WebInspector.UIString("%.2f\u2009%", value);
+ }
+
+ var functionName;
+ if (this._deoptReason) {
+ var content = document.createDocumentFragment();
+ var marker = content.createChild("span", "profile-warn-marker");
+ marker.title = WebInspector.UIString("Not optimized: %s", this._deoptReason);
+ content.createTextChild(this.functionName);
+ functionName = content;
+ } else {
+ functionName = this.functionName;
+ }
+
+ this.data = {
+ "function": functionName,
+ "self-percent": formatPercent(this.selfPercent),
+ "self": formatMilliseconds(this.selfTime),
+ "total-percent": formatPercent(this.totalPercent),
+ "total": formatMilliseconds(this.totalTime),
+ };
+ },
+
+ select: function(supressSelectedEvent)
+ {
+ WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
+ this.tree.profileView._dataGridNodeSelected(this);
+ },
+
+ deselect: function(supressDeselectedEvent)
+ {
+ WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
+ this.tree.profileView._dataGridNodeDeselected(this);
+ },
+
+ /**
+ * @param {function(!T, !T)} comparator
+ * @param {boolean} force
+ * @template T
+ */
+ sort: function(comparator, force)
+ {
+ var gridNodeGroups = [[this]];
+
+ for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
+ var gridNodes = gridNodeGroups[gridNodeGroupIndex];
+ var count = gridNodes.length;
+
+ for (var index = 0; index < count; ++index) {
+ var gridNode = gridNodes[index];
+
+ // If the grid node is collapsed, then don't sort children (save operation for later).
+ // If the grid node has the same sorting as previously, then there is no point in sorting it again.
+ if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
+ if (gridNode.children.length)
+ gridNode.shouldRefreshChildren = true;
+ continue;
+ }
+
+ gridNode.lastComparator = comparator;
+
+ var children = gridNode.children;
+ var childCount = children.length;
+
+ if (childCount) {
+ children.sort(comparator);
+
+ for (var childIndex = 0; childIndex < childCount; ++childIndex)
+ children[childIndex]._recalculateSiblings(childIndex);
+
+ gridNodeGroups.push(children);
+ }
+ }
+ }
+ },
+
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ * @param {number} index
+ */
+ insertChild: function(profileDataGridNode, index)
+ {
+ WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
+
+ this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
+ },
+
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ */
+ removeChild: function(profileDataGridNode)
+ {
+ WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
+
+ delete this.childrenByCallUID[profileDataGridNode.callUID];
+ },
+
+ removeChildren: function()
+ {
+ WebInspector.DataGridNode.prototype.removeChildren.call(this);
+
+ this.childrenByCallUID = {};
+ },
+
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} node
+ * @return {?WebInspector.ProfileDataGridNode}
+ */
+ findChild: function(node)
+ {
+ if (!node)
+ return null;
+ return this.childrenByCallUID[node.callUID];
+ },
+
+ get selfPercent()
+ {
+ return this.selfTime / this.tree.totalTime * 100.0;
+ },
+
+ get totalPercent()
+ {
+ return this.totalTime / this.tree.totalTime * 100.0;
+ },
+
+ get _parent()
+ {
+ return this.parent !== this.dataGrid ? this.parent : this.tree;
+ },
+
+ populate: function()
+ {
+ if (this._populated)
+ return;
+ this._populated = true;
+
+ this._sharedPopulate();
+
+ var currentComparator = this.tree.lastComparator;
+
+ if (currentComparator)
+ this.sort(currentComparator, true);
+ },
+
+ // When focusing and collapsing we modify lots of nodes in the tree.
+ // This allows us to restore them all to their original state when we revert.
+ _save: function()
+ {
+ if (this._savedChildren)
+ return;
+
+ this._savedSelfTime = this.selfTime;
+ this._savedTotalTime = this.totalTime;
+
+ this._savedChildren = this.children.slice();
+ },
+
+ // When focusing and collapsing we modify lots of nodes in the tree.
+ // This allows us to restore them all to their original state when we revert.
+ _restore: function()
+ {
+ if (!this._savedChildren)
+ return;
+
+ this.selfTime = this._savedSelfTime;
+ this.totalTime = this._savedTotalTime;
+
+ this.removeChildren();
+
+ var children = this._savedChildren;
+ var count = children.length;
+
+ for (var index = 0; index < count; ++index) {
+ children[index]._restore();
+ this.appendChild(children[index]);
+ }
+ },
+
+ _merge: function(child, shouldAbsorb)
+ {
+ this.selfTime += child.selfTime;
+
+ if (!shouldAbsorb)
+ this.totalTime += child.totalTime;
+
+ var children = this.children.slice();
+
+ this.removeChildren();
+
+ var count = children.length;
+
+ for (var index = 0; index < count; ++index) {
+ if (!shouldAbsorb || children[index] !== child)
+ this.appendChild(children[index]);
+ }
+
+ children = child.children.slice();
+ count = children.length;
+
+ for (var index = 0; index < count; ++index) {
+ var orphanedChild = children[index],
+ existingChild = this.childrenByCallUID[orphanedChild.callUID];
+
+ if (existingChild)
+ existingChild._merge(orphanedChild, false);
+ else
+ this.appendChild(orphanedChild);
+ }
+ },
+
+ __proto__: WebInspector.DataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.CPUProfileView} profileView
+ * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode
+ */
+WebInspector.ProfileDataGridTree = function(profileView, rootProfileNode)
+{
+ this.tree = this;
+ this.children = [];
+
+ this.profileView = profileView;
+
+ this.totalTime = rootProfileNode.totalTime;
+ this.lastComparator = null;
+
+ this.childrenByCallUID = {};
+}
+
+WebInspector.ProfileDataGridTree.prototype = {
+ get expanded()
+ {
+ return true;
+ },
+
+ appendChild: function(child)
+ {
+ this.insertChild(child, this.children.length);
+ },
+
+ insertChild: function(child, index)
+ {
+ this.children.splice(index, 0, child);
+ this.childrenByCallUID[child.callUID] = child;
+ },
+
+ removeChildren: function()
+ {
+ this.children = [];
+ this.childrenByCallUID = {};
+ },
+
+ findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
+ sort: WebInspector.ProfileDataGridNode.prototype.sort,
+
+ _save: function()
+ {
+ if (this._savedChildren)
+ return;
+
+ this._savedTotalTime = this.totalTime;
+ this._savedChildren = this.children.slice();
+ },
+
+ restore: function()
+ {
+ if (!this._savedChildren)
+ return;
+
+ this.children = this._savedChildren;
+ this.totalTime = this._savedTotalTime;
+
+ var children = this.children;
+ var count = children.length;
+
+ for (var index = 0; index < count; ++index)
+ children[index]._restore();
+
+ this._savedChildren = null;
+ }
+}
+
+WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
+
+/**
+ * @param {string} property
+ * @param {boolean} isAscending
+ * @return {function(!Object.<string, *>, !Object.<string, *>)}
+ */
+WebInspector.ProfileDataGridTree.propertyComparator = function(property, isAscending)
+{
+ var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property];
+
+ if (!comparator) {
+ if (isAscending) {
+ comparator = function(lhs, rhs)
+ {
+ if (lhs[property] < rhs[property])
+ return -1;
+
+ if (lhs[property] > rhs[property])
+ return 1;
+
+ return 0;
+ }
+ } else {
+ comparator = function(lhs, rhs)
+ {
+ if (lhs[property] > rhs[property])
+ return -1;
+
+ if (lhs[property] < rhs[property])
+ return 1;
+
+ return 0;
+ }
+ }
+
+ WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;
+ }
+
+ return comparator;
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js
new file mode 100644
index 00000000000..7ad221b89ae
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js
@@ -0,0 +1,650 @@
+/**
+ * Copyright (C) 2014 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
+ * @implements {WebInspector.FlameChartDataProvider}
+ * @param {!WebInspector.CPUProfileDataModel} cpuProfile
+ * @param {!WebInspector.Target} target
+ */
+WebInspector.CPUFlameChartDataProvider = function(cpuProfile, target)
+{
+ WebInspector.FlameChartDataProvider.call(this);
+ this._cpuProfile = cpuProfile;
+ this._target = target;
+ this._colorGenerator = WebInspector.CPUFlameChartDataProvider.colorGenerator();
+}
+
+WebInspector.CPUFlameChartDataProvider.prototype = {
+ /**
+ * @return {number}
+ */
+ barHeight: function()
+ {
+ return 15;
+ },
+
+ /**
+ * @return {number}
+ */
+ textBaseline: function()
+ {
+ return 4;
+ },
+
+ /**
+ * @return {number}
+ */
+ textPadding: function()
+ {
+ return 2;
+ },
+
+ /**
+ * @param {number} startTime
+ * @param {number} endTime
+ * @return {?Array.<number>}
+ */
+ dividerOffsets: function(startTime, endTime)
+ {
+ return null;
+ },
+
+ /**
+ * @return {number}
+ */
+ minimumBoundary: function()
+ {
+ return this._cpuProfile.profileStartTime;
+ },
+
+ /**
+ * @return {number}
+ */
+ totalTime: function()
+ {
+ return this._cpuProfile.profileHead.totalTime;
+ },
+
+ /**
+ * @return {number}
+ */
+ maxStackDepth: function()
+ {
+ return this._maxStackDepth;
+ },
+
+ /**
+ * @return {?WebInspector.FlameChart.TimelineData}
+ */
+ timelineData: function()
+ {
+ return this._timelineData || this._calculateTimelineData();
+ },
+
+ /**
+ * @return {?WebInspector.FlameChart.TimelineData}
+ */
+ _calculateTimelineData: function()
+ {
+ /**
+ * @constructor
+ * @param {number} depth
+ * @param {number} duration
+ * @param {number} startTime
+ * @param {number} selfTime
+ * @param {!ProfilerAgent.CPUProfileNode} node
+ */
+ function ChartEntry(depth, duration, startTime, selfTime, node)
+ {
+ this.depth = depth;
+ this.duration = duration;
+ this.startTime = startTime;
+ this.selfTime = selfTime;
+ this.node = node;
+ }
+
+ /** @type {!Array.<?ChartEntry>} */
+ var entries = [];
+ /** @type {!Array.<number>} */
+ var stack = [];
+ var maxDepth = 5;
+
+ function onOpenFrame()
+ {
+ stack.push(entries.length);
+ // Reserve space for the entry, as they have to be ordered by startTime.
+ // The entry itself will be put there in onCloseFrame.
+ entries.push(null);
+ }
+ function onCloseFrame(depth, node, startTime, totalTime, selfTime)
+ {
+ var index = stack.pop();
+ entries[index] = new ChartEntry(depth, totalTime, startTime, selfTime, node);
+ maxDepth = Math.max(maxDepth, depth);
+ }
+ this._cpuProfile.forEachFrame(onOpenFrame, onCloseFrame);
+
+ /** @type {!Array.<!ProfilerAgent.CPUProfileNode>} */
+ var entryNodes = new Array(entries.length);
+ var entryLevels = new Uint8Array(entries.length);
+ var entryTotalTimes = new Float32Array(entries.length);
+ var entrySelfTimes = new Float32Array(entries.length);
+ var entryStartTimes = new Float64Array(entries.length);
+ var minimumBoundary = this.minimumBoundary();
+
+ for (var i = 0; i < entries.length; ++i) {
+ var entry = entries[i];
+ entryNodes[i] = entry.node;
+ entryLevels[i] = entry.depth;
+ entryTotalTimes[i] = entry.duration;
+ entryStartTimes[i] = entry.startTime;
+ entrySelfTimes[i] = entry.selfTime;
+ }
+
+ this._maxStackDepth = maxDepth;
+
+ /** @type {!WebInspector.FlameChart.TimelineData} */
+ this._timelineData = {
+ entryLevels: entryLevels,
+ entryTotalTimes: entryTotalTimes,
+ entryStartTimes: entryStartTimes,
+ };
+
+ /** @type {!Array.<!ProfilerAgent.CPUProfileNode>} */
+ this._entryNodes = entryNodes;
+ this._entrySelfTimes = entrySelfTimes;
+
+ return this._timelineData;
+ },
+
+ /**
+ * @param {number} ms
+ * @return {string}
+ */
+ _millisecondsToString: function(ms)
+ {
+ if (ms === 0)
+ return "0";
+ if (ms < 1000)
+ return WebInspector.UIString("%.1f\u2009ms", ms);
+ return Number.secondsToString(ms / 1000, true);
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {?Array.<!{title: string, text: string}>}
+ */
+ prepareHighlightedEntryInfo: function(entryIndex)
+ {
+ var timelineData = this._timelineData;
+ var node = this._entryNodes[entryIndex];
+ if (!node)
+ return null;
+
+ var entryInfo = [];
+ function pushEntryInfoRow(title, text)
+ {
+ var row = {};
+ row.title = title;
+ row.text = text;
+ entryInfo.push(row);
+ }
+
+ pushEntryInfoRow(WebInspector.UIString("Name"), node.functionName);
+ var selfTime = this._millisecondsToString(this._entrySelfTimes[entryIndex]);
+ var totalTime = this._millisecondsToString(timelineData.entryTotalTimes[entryIndex]);
+ pushEntryInfoRow(WebInspector.UIString("Self time"), selfTime);
+ pushEntryInfoRow(WebInspector.UIString("Total time"), totalTime);
+ var target = this._target;
+ var text = WebInspector.Linkifier.liveLocationText(target, node.scriptId, node.lineNumber, node.columnNumber);
+ pushEntryInfoRow(WebInspector.UIString("URL"), text);
+ pushEntryInfoRow(WebInspector.UIString("Aggregated self time"), Number.secondsToString(node.selfTime / 1000, true));
+ pushEntryInfoRow(WebInspector.UIString("Aggregated total time"), Number.secondsToString(node.totalTime / 1000, true));
+ if (node.deoptReason && node.deoptReason !== "no reason")
+ pushEntryInfoRow(WebInspector.UIString("Not optimized"), node.deoptReason);
+
+ return entryInfo;
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {boolean}
+ */
+ canJumpToEntry: function(entryIndex)
+ {
+ return this._entryNodes[entryIndex].scriptId !== "0";
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {?string}
+ */
+ entryTitle: function(entryIndex)
+ {
+ var node = this._entryNodes[entryIndex];
+ return node.functionName;
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {?string}
+ */
+ entryFont: function(entryIndex)
+ {
+ if (!this._font) {
+ this._font = (this.barHeight() - 4) + "px " + WebInspector.fontFamily();
+ this._boldFont = "bold " + this._font;
+ }
+ var node = this._entryNodes[entryIndex];
+ var reason = node.deoptReason;
+ return (reason && reason !== "no reason") ? this._boldFont : this._font;
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {string}
+ */
+ entryColor: function(entryIndex)
+ {
+ var node = this._entryNodes[entryIndex];
+ return this._colorGenerator.colorForID(node.functionName + ":" + node.url);
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @param {!CanvasRenderingContext2D} context
+ * @param {?string} text
+ * @param {number} barX
+ * @param {number} barY
+ * @param {number} barWidth
+ * @param {number} barHeight
+ * @param {function(number):number} timeToPosition
+ * @return {boolean}
+ */
+ decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, barHeight, timeToPosition)
+ {
+ return false;
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {boolean}
+ */
+ forceDecoration: function(entryIndex)
+ {
+ return false;
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {!{startTime: number, endTime: number}}
+ */
+ highlightTimeRange: function(entryIndex)
+ {
+ var startTime = this._timelineData.entryStartTimes[entryIndex];
+ return {
+ startTime: startTime,
+ endTime: startTime + this._timelineData.entryTotalTimes[entryIndex]
+ };
+ },
+
+ /**
+ * @return {number}
+ */
+ paddingLeft: function()
+ {
+ return 15;
+ },
+
+ /**
+ * @param {number} entryIndex
+ * @return {string}
+ */
+ textColor: function(entryIndex)
+ {
+ return "#333";
+ }
+}
+
+
+/**
+ * @return {!WebInspector.FlameChart.ColorGenerator}
+ */
+WebInspector.CPUFlameChartDataProvider.colorGenerator = function()
+{
+ if (!WebInspector.CPUFlameChartDataProvider._colorGenerator) {
+ var colorGenerator = new WebInspector.FlameChart.ColorGenerator(
+ { min: 180, max: 310, count: 7 },
+ { min: 50, max: 80, count: 5 },
+ { min: 80, max: 90, count: 3 });
+ colorGenerator.setColorForID("(idle):", "hsl(0, 0%, 94%)");
+ colorGenerator.setColorForID("(program):", "hsl(0, 0%, 80%)");
+ colorGenerator.setColorForID("(garbage collector):", "hsl(0, 0%, 80%)");
+ WebInspector.CPUFlameChartDataProvider._colorGenerator = colorGenerator;
+ }
+ return WebInspector.CPUFlameChartDataProvider._colorGenerator;
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @param {!WebInspector.FlameChartDataProvider} dataProvider
+ */
+WebInspector.CPUProfileFlameChart = function(dataProvider)
+{
+ WebInspector.VBox.call(this);
+ this.registerRequiredCSS("flameChart.css");
+ this.element.id = "cpu-flame-chart";
+
+ this._overviewPane = new WebInspector.CPUProfileFlameChart.OverviewPane(dataProvider);
+ this._overviewPane.show(this.element);
+
+ this._mainPane = new WebInspector.FlameChart(dataProvider, this._overviewPane, true);
+ this._mainPane.show(this.element);
+ this._mainPane.addEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected, this);
+ this._overviewPane.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
+}
+
+WebInspector.CPUProfileFlameChart.prototype = {
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onWindowChanged: function(event)
+ {
+ var windowLeft = event.data.windowTimeLeft;
+ var windowRight = event.data.windowTimeRight;
+ this._mainPane.setWindowTimes(windowLeft, windowRight);
+ },
+
+ /**
+ * @param {!number} timeLeft
+ * @param {!number} timeRight
+ */
+ selectRange: function(timeLeft, timeRight)
+ {
+ this._overviewPane._selectRange(timeLeft, timeRight);
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onEntrySelected: function(event)
+ {
+ this.dispatchEventToListeners(WebInspector.FlameChart.Events.EntrySelected, event.data);
+ },
+
+ update: function()
+ {
+ this._overviewPane.update();
+ this._mainPane.update();
+ },
+
+ __proto__: WebInspector.VBox.prototype
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.CPUProfileFlameChart.OverviewCalculator = function()
+{
+}
+
+WebInspector.CPUProfileFlameChart.OverviewCalculator.prototype = {
+ /**
+ * @return {number}
+ */
+ paddingLeft: function()
+ {
+ return 0;
+ },
+
+ /**
+ * @param {!WebInspector.CPUProfileFlameChart.OverviewPane} overviewPane
+ */
+ _updateBoundaries: function(overviewPane)
+ {
+ this._minimumBoundaries = overviewPane._dataProvider.minimumBoundary();
+ var totalTime = overviewPane._dataProvider.totalTime();
+ this._maximumBoundaries = this._minimumBoundaries + totalTime;
+ this._xScaleFactor = overviewPane._overviewContainer.clientWidth / totalTime;
+ },
+
+ /**
+ * @param {number} time
+ * @return {number}
+ */
+ computePosition: function(time)
+ {
+ return (time - this._minimumBoundaries) * this._xScaleFactor;
+ },
+
+ /**
+ * @param {number} value
+ * @param {number=} precision
+ * @return {string}
+ */
+ formatTime: function(value, precision)
+ {
+ return Number.secondsToString((value - this._minimumBoundaries) / 1000);
+ },
+
+ /**
+ * @return {number}
+ */
+ maximumBoundary: function()
+ {
+ return this._maximumBoundaries;
+ },
+
+ /**
+ * @return {number}
+ */
+ minimumBoundary: function()
+ {
+ return this._minimumBoundaries;
+ },
+
+ /**
+ * @return {number}
+ */
+ zeroTime: function()
+ {
+ return this._minimumBoundaries;
+ },
+
+ /**
+ * @return {number}
+ */
+ boundarySpan: function()
+ {
+ return this._maximumBoundaries - this._minimumBoundaries;
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @implements {WebInspector.FlameChartDelegate}
+ * @param {!WebInspector.FlameChartDataProvider} dataProvider
+ */
+WebInspector.CPUProfileFlameChart.OverviewPane = function(dataProvider)
+{
+ WebInspector.VBox.call(this);
+ this.element.classList.add("flame-chart-overview-pane");
+ this._overviewContainer = this.element.createChild("div", "overview-container");
+ this._overviewGrid = new WebInspector.OverviewGrid("flame-chart");
+ this._overviewGrid.element.classList.add("fill");
+ this._overviewCanvas = this._overviewContainer.createChild("canvas", "flame-chart-overview-canvas");
+ this._overviewContainer.appendChild(this._overviewGrid.element);
+ this._overviewCalculator = new WebInspector.CPUProfileFlameChart.OverviewCalculator();
+ this._dataProvider = dataProvider;
+ this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
+}
+
+WebInspector.CPUProfileFlameChart.OverviewPane.prototype = {
+ /**
+ * @param {number} windowStartTime
+ * @param {number} windowEndTime
+ */
+ requestWindowTimes: function(windowStartTime, windowEndTime)
+ {
+ this._selectRange(windowStartTime, windowEndTime);
+ },
+
+ /**
+ * @param {!number} timeLeft
+ * @param {!number} timeRight
+ */
+ _selectRange: function(timeLeft, timeRight)
+ {
+ var startTime = this._dataProvider.minimumBoundary();
+ var totalTime = this._dataProvider.totalTime();
+ this._overviewGrid.setWindow((timeLeft - startTime) / totalTime, (timeRight - startTime) / totalTime);
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onWindowChanged: function(event)
+ {
+ var startTime = this._dataProvider.minimumBoundary();
+ var totalTime = this._dataProvider.totalTime();
+ var data = {
+ windowTimeLeft: startTime + this._overviewGrid.windowLeft() * totalTime,
+ windowTimeRight: startTime + this._overviewGrid.windowRight() * totalTime
+ };
+ this.dispatchEventToListeners(WebInspector.OverviewGrid.Events.WindowChanged, data);
+ },
+
+ /**
+ * @return {?WebInspector.FlameChart.TimelineData}
+ */
+ _timelineData: function()
+ {
+ return this._dataProvider.timelineData();
+ },
+
+ onResize: function()
+ {
+ this._scheduleUpdate();
+ },
+
+ _scheduleUpdate: function()
+ {
+ if (this._updateTimerId)
+ return;
+ this._updateTimerId = requestAnimationFrame(this.update.bind(this));
+ },
+
+ update: function()
+ {
+ this._updateTimerId = 0;
+ var timelineData = this._timelineData();
+ if (!timelineData)
+ return;
+ this._resetCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - WebInspector.FlameChart.DividersBarHeight);
+ this._overviewCalculator._updateBoundaries(this);
+ this._overviewGrid.updateDividers(this._overviewCalculator);
+ this._drawOverviewCanvas();
+ },
+
+ _drawOverviewCanvas: function()
+ {
+ var canvasWidth = this._overviewCanvas.width;
+ var canvasHeight = this._overviewCanvas.height;
+ var drawData = this._calculateDrawData(canvasWidth);
+ var context = this._overviewCanvas.getContext("2d");
+ var ratio = window.devicePixelRatio;
+ var offsetFromBottom = ratio;
+ var lineWidth = 1;
+ var yScaleFactor = canvasHeight / (this._dataProvider.maxStackDepth() * 1.1);
+ context.lineWidth = lineWidth;
+ context.translate(0.5, 0.5);
+ context.strokeStyle = "rgba(20,0,0,0.4)";
+ context.fillStyle = "rgba(214,225,254,0.8)";
+ context.moveTo(-lineWidth, canvasHeight + lineWidth);
+ context.lineTo(-lineWidth, Math.round(canvasHeight - drawData[0] * yScaleFactor - offsetFromBottom));
+ var value;
+ for (var x = 0; x < canvasWidth; ++x) {
+ value = Math.round(canvasHeight - drawData[x] * yScaleFactor - offsetFromBottom);
+ context.lineTo(x, value);
+ }
+ context.lineTo(canvasWidth + lineWidth, value);
+ context.lineTo(canvasWidth + lineWidth, canvasHeight + lineWidth);
+ context.fill();
+ context.stroke();
+ context.closePath();
+ },
+
+ /**
+ * @param {number} width
+ * @return {!Uint8Array}
+ */
+ _calculateDrawData: function(width)
+ {
+ var dataProvider = this._dataProvider;
+ var timelineData = this._timelineData();
+ var entryStartTimes = timelineData.entryStartTimes;
+ var entryTotalTimes = timelineData.entryTotalTimes;
+ var entryLevels = timelineData.entryLevels;
+ var length = entryStartTimes.length;
+ var minimumBoundary = this._dataProvider.minimumBoundary();
+
+ var drawData = new Uint8Array(width);
+ var scaleFactor = width / dataProvider.totalTime();
+
+ for (var entryIndex = 0; entryIndex < length; ++entryIndex) {
+ var start = Math.floor((entryStartTimes[entryIndex] - minimumBoundary) * scaleFactor);
+ var finish = Math.floor((entryStartTimes[entryIndex] - minimumBoundary + entryTotalTimes[entryIndex]) * scaleFactor);
+ for (var x = start; x <= finish; ++x)
+ drawData[x] = Math.max(drawData[x], entryLevels[entryIndex] + 1);
+ }
+ return drawData;
+ },
+
+ /**
+ * @param {!number} width
+ * @param {!number} height
+ */
+ _resetCanvas: function(width, height)
+ {
+ var ratio = window.devicePixelRatio;
+ this._overviewCanvas.width = width * ratio;
+ this._overviewCanvas.height = height * ratio;
+ this._overviewCanvas.style.width = width + "px";
+ this._overviewCanvas.style.height = height + "px";
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileTopDownDataGrid.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileTopDownDataGrid.js
new file mode 100644
index 00000000000..f1f7a7a5fe7
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileTopDownDataGrid.js
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2009 280 North 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. ``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
+ * 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.ProfileDataGridNode}
+ * @param {!ProfilerAgent.CPUProfileNode} profileNode
+ * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
+ */
+WebInspector.TopDownProfileDataGridNode = function(profileNode, owningTree)
+{
+ var hasChildren = !!(profileNode.children && profileNode.children.length);
+
+ WebInspector.ProfileDataGridNode.call(this, profileNode, owningTree, hasChildren);
+
+ this._remainingChildren = profileNode.children;
+ this.buildData();
+}
+
+WebInspector.TopDownProfileDataGridNode.prototype = {
+ _sharedPopulate: function()
+ {
+ var children = this._remainingChildren;
+ var childrenLength = children.length;
+
+ for (var i = 0; i < childrenLength; ++i)
+ this.appendChild(new WebInspector.TopDownProfileDataGridNode(children[i], this.tree));
+
+ this._remainingChildren = null;
+ },
+
+ _exclude: function(aCallUID)
+ {
+ if (this._remainingChildren)
+ this.populate();
+
+ this._save();
+
+ var children = this.children;
+ var index = this.children.length;
+
+ while (index--)
+ children[index]._exclude(aCallUID);
+
+ var child = this.childrenByCallUID[aCallUID];
+
+ if (child)
+ this._merge(child, true);
+ },
+
+ __proto__: WebInspector.ProfileDataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridTree}
+ * @param {!WebInspector.CPUProfileView} profileView
+ * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode
+ */
+WebInspector.TopDownProfileDataGridTree = function(profileView, rootProfileNode)
+{
+ WebInspector.ProfileDataGridTree.call(this, profileView, rootProfileNode);
+
+ this._remainingChildren = rootProfileNode.children;
+
+ var any = /** @type {*} */(this);
+ var node = /** @type {!WebInspector.ProfileDataGridNode} */(any);
+ WebInspector.TopDownProfileDataGridNode.prototype.populate.call(node);
+}
+
+WebInspector.TopDownProfileDataGridTree.prototype = {
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ */
+ focus: function(profileDataGridNode)
+ {
+ if (!profileDataGridNode)
+ return;
+
+ this._save();
+ profileDataGridNode.savePosition();
+
+ this.children = [profileDataGridNode];
+ this.totalTime = profileDataGridNode.totalTime;
+ },
+
+ /**
+ * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+ */
+ exclude: function(profileDataGridNode)
+ {
+ if (!profileDataGridNode)
+ return;
+
+ this._save();
+
+ var excludedCallUID = profileDataGridNode.callUID;
+
+ var any = /** @type {*} */(this);
+ var node = /** @type {!WebInspector.TopDownProfileDataGridNode} */(any);
+ WebInspector.TopDownProfileDataGridNode.prototype._exclude.call(node, excludedCallUID);
+
+ if (this.lastComparator)
+ this.sort(this.lastComparator, true);
+ },
+
+ restore: function()
+ {
+ if (!this._savedChildren)
+ return;
+
+ this.children[0].restorePosition();
+
+ WebInspector.ProfileDataGridTree.prototype.restore.call(this);
+ },
+
+ _merge: WebInspector.TopDownProfileDataGridNode.prototype._merge,
+
+ _sharedPopulate: WebInspector.TopDownProfileDataGridNode.prototype._sharedPopulate,
+
+ __proto__: WebInspector.ProfileDataGridTree.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js
new file mode 100644
index 00000000000..c448cc921b1
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.VBox}
+ * @param {!WebInspector.CPUProfileHeader} profileHeader
+ */
+WebInspector.CPUProfileView = function(profileHeader)
+{
+ WebInspector.VBox.call(this);
+ this.element.classList.add("cpu-profile-view");
+
+ this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebInspector.CPUProfileView._TypeHeavy);
+
+ var columns = [];
+ columns.push({id: "self", title: WebInspector.UIString("Self"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true});
+ columns.push({id: "total", title: WebInspector.UIString("Total"), width: "120px", sortable: true});
+ columns.push({id: "function", title: WebInspector.UIString("Function"), disclosure: true, sortable: true});
+
+ this.dataGrid = new WebInspector.DataGrid(columns);
+ this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortProfile, this);
+ this.dataGrid.show(this.element);
+
+ this.viewSelectComboBox = new WebInspector.StatusBarComboBox(this._changeView.bind(this));
+
+ var options = {};
+ options[WebInspector.CPUProfileView._TypeFlame] = this.viewSelectComboBox.createOption(WebInspector.UIString("Chart"), "", WebInspector.CPUProfileView._TypeFlame);
+ options[WebInspector.CPUProfileView._TypeHeavy] = this.viewSelectComboBox.createOption(WebInspector.UIString("Heavy (Bottom Up)"), "", WebInspector.CPUProfileView._TypeHeavy);
+ options[WebInspector.CPUProfileView._TypeTree] = this.viewSelectComboBox.createOption(WebInspector.UIString("Tree (Top Down)"), "", WebInspector.CPUProfileView._TypeTree);
+
+ var optionName = this._viewType.get() || WebInspector.CPUProfileView._TypeFlame;
+ var option = options[optionName] || options[WebInspector.CPUProfileView._TypeFlame];
+ this.viewSelectComboBox.select(option);
+
+ this._statusBarButtonsElement = document.createElement("span");
+
+ this.focusButton = new WebInspector.StatusBarButton(WebInspector.UIString("Focus selected function."), "focus-profile-node-status-bar-item");
+ this.focusButton.setEnabled(false);
+ this.focusButton.addEventListener("click", this._focusClicked, this);
+ this._statusBarButtonsElement.appendChild(this.focusButton.element);
+
+ this.excludeButton = new WebInspector.StatusBarButton(WebInspector.UIString("Exclude selected function."), "exclude-profile-node-status-bar-item");
+ this.excludeButton.setEnabled(false);
+ this.excludeButton.addEventListener("click", this._excludeClicked, this);
+ this._statusBarButtonsElement.appendChild(this.excludeButton.element);
+
+ this.resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Restore all functions."), "reset-profile-status-bar-item");
+ this.resetButton.visible = false;
+ this.resetButton.addEventListener("click", this._resetClicked, this);
+ this._statusBarButtonsElement.appendChild(this.resetButton.element);
+
+ this._profileHeader = profileHeader;
+ this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultFormatter(30));
+
+ this.profile = new WebInspector.CPUProfileDataModel(profileHeader._profile || profileHeader.protocolProfile());
+
+ this._changeView();
+ if (this._flameChart)
+ this._flameChart.update();
+}
+
+WebInspector.CPUProfileView._TypeFlame = "Flame";
+WebInspector.CPUProfileView._TypeTree = "Tree";
+WebInspector.CPUProfileView._TypeHeavy = "Heavy";
+
+WebInspector.CPUProfileView.prototype = {
+ /**
+ * @param {!number} timeLeft
+ * @param {!number} timeRight
+ */
+ selectRange: function(timeLeft, timeRight)
+ {
+ if (!this._flameChart)
+ return;
+ this._flameChart.selectRange(timeLeft, timeRight);
+ },
+
+ get statusBarItems()
+ {
+ return [this.viewSelectComboBox.element, this._statusBarButtonsElement];
+ },
+
+ /**
+ * @return {!WebInspector.ProfileDataGridTree}
+ */
+ _getBottomUpProfileDataGridTree: function()
+ {
+ if (!this._bottomUpProfileDataGridTree)
+ this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfileDataGridTree(this, /** @type {!ProfilerAgent.CPUProfileNode} */ (this.profile.profileHead));
+ return this._bottomUpProfileDataGridTree;
+ },
+
+ /**
+ * @return {!WebInspector.ProfileDataGridTree}
+ */
+ _getTopDownProfileDataGridTree: function()
+ {
+ if (!this._topDownProfileDataGridTree)
+ this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDataGridTree(this, /** @type {!ProfilerAgent.CPUProfileNode} */ (this.profile.profileHead));
+ return this._topDownProfileDataGridTree;
+ },
+
+ willHide: function()
+ {
+ this._currentSearchResultIndex = -1;
+ },
+
+ refresh: function()
+ {
+ var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null;
+
+ this.dataGrid.rootNode().removeChildren();
+
+ var children = this.profileDataGridTree.children;
+ var count = children.length;
+
+ for (var index = 0; index < count; ++index)
+ this.dataGrid.rootNode().appendChild(children[index]);
+
+ if (selectedProfileNode)
+ selectedProfileNode.selected = true;
+ },
+
+ refreshVisibleData: function()
+ {
+ var child = this.dataGrid.rootNode().children[0];
+ while (child) {
+ child.refresh();
+ child = child.traverseNextNode(false, null, true);
+ }
+ },
+
+ searchCanceled: function()
+ {
+ if (this._searchResults) {
+ for (var i = 0; i < this._searchResults.length; ++i) {
+ var profileNode = this._searchResults[i].profileNode;
+
+ delete profileNode._searchMatchedSelfColumn;
+ delete profileNode._searchMatchedTotalColumn;
+ delete profileNode._searchMatchedFunctionColumn;
+
+ profileNode.refresh();
+ }
+ }
+
+ delete this._searchFinishedCallback;
+ this._currentSearchResultIndex = -1;
+ this._searchResults = [];
+ },
+
+ performSearch: function(query, finishedCallback)
+ {
+ // Call searchCanceled since it will reset everything we need before doing a new search.
+ this.searchCanceled();
+
+ query = query.trim();
+
+ if (!query.length)
+ return;
+
+ this._searchFinishedCallback = finishedCallback;
+
+ var greaterThan = (query.startsWith(">"));
+ var lessThan = (query.startsWith("<"));
+ var equalTo = (query.startsWith("=") || ((greaterThan || lessThan) && query.indexOf("=") === 1));
+ var percentUnits = (query.lastIndexOf("%") === (query.length - 1));
+ var millisecondsUnits = (query.length > 2 && query.lastIndexOf("ms") === (query.length - 2));
+ var secondsUnits = (!millisecondsUnits && query.lastIndexOf("s") === (query.length - 1));
+
+ var queryNumber = parseFloat(query);
+ if (greaterThan || lessThan || equalTo) {
+ if (equalTo && (greaterThan || lessThan))
+ queryNumber = parseFloat(query.substring(2));
+ else
+ queryNumber = parseFloat(query.substring(1));
+ }
+
+ var queryNumberMilliseconds = (secondsUnits ? (queryNumber * 1000) : queryNumber);
+
+ // Make equalTo implicitly true if it wasn't specified there is no other operator.
+ if (!isNaN(queryNumber) && !(greaterThan || lessThan))
+ equalTo = true;
+
+ var matcher = createPlainTextSearchRegex(query, "i");
+
+ function matchesQuery(/*ProfileDataGridNode*/ profileDataGridNode)
+ {
+ delete profileDataGridNode._searchMatchedSelfColumn;
+ delete profileDataGridNode._searchMatchedTotalColumn;
+ delete profileDataGridNode._searchMatchedFunctionColumn;
+
+ if (percentUnits) {
+ if (lessThan) {
+ if (profileDataGridNode.selfPercent < queryNumber)
+ profileDataGridNode._searchMatchedSelfColumn = true;
+ if (profileDataGridNode.totalPercent < queryNumber)
+ profileDataGridNode._searchMatchedTotalColumn = true;
+ } else if (greaterThan) {
+ if (profileDataGridNode.selfPercent > queryNumber)
+ profileDataGridNode._searchMatchedSelfColumn = true;
+ if (profileDataGridNode.totalPercent > queryNumber)
+ profileDataGridNode._searchMatchedTotalColumn = true;
+ }
+
+ if (equalTo) {
+ if (profileDataGridNode.selfPercent == queryNumber)
+ profileDataGridNode._searchMatchedSelfColumn = true;
+ if (profileDataGridNode.totalPercent == queryNumber)
+ profileDataGridNode._searchMatchedTotalColumn = true;
+ }
+ } else if (millisecondsUnits || secondsUnits) {
+ if (lessThan) {
+ if (profileDataGridNode.selfTime < queryNumberMilliseconds)
+ profileDataGridNode._searchMatchedSelfColumn = true;
+ if (profileDataGridNode.totalTime < queryNumberMilliseconds)
+ profileDataGridNode._searchMatchedTotalColumn = true;
+ } else if (greaterThan) {
+ if (profileDataGridNode.selfTime > queryNumberMilliseconds)
+ profileDataGridNode._searchMatchedSelfColumn = true;
+ if (profileDataGridNode.totalTime > queryNumberMilliseconds)
+ profileDataGridNode._searchMatchedTotalColumn = true;
+ }
+
+ if (equalTo) {
+ if (profileDataGridNode.selfTime == queryNumberMilliseconds)
+ profileDataGridNode._searchMatchedSelfColumn = true;
+ if (profileDataGridNode.totalTime == queryNumberMilliseconds)
+ profileDataGridNode._searchMatchedTotalColumn = true;
+ }
+ }
+
+ if (profileDataGridNode.functionName.match(matcher) || (profileDataGridNode.url && profileDataGridNode.url.match(matcher)))
+ profileDataGridNode._searchMatchedFunctionColumn = true;
+
+ if (profileDataGridNode._searchMatchedSelfColumn ||
+ profileDataGridNode._searchMatchedTotalColumn ||
+ profileDataGridNode._searchMatchedFunctionColumn)
+ {
+ profileDataGridNode.refresh();
+ return true;
+ }
+
+ return false;
+ }
+
+ var current = this.profileDataGridTree.children[0];
+
+ while (current) {
+ if (matchesQuery(current)) {
+ this._searchResults.push({ profileNode: current });
+ }
+
+ current = current.traverseNextNode(false, null, false);
+ }
+
+ finishedCallback(this, this._searchResults.length);
+ },
+
+ jumpToFirstSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ this._currentSearchResultIndex = 0;
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ jumpToLastSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ this._currentSearchResultIndex = (this._searchResults.length - 1);
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ jumpToNextSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ if (++this._currentSearchResultIndex >= this._searchResults.length)
+ this._currentSearchResultIndex = 0;
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ jumpToPreviousSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ if (--this._currentSearchResultIndex < 0)
+ this._currentSearchResultIndex = (this._searchResults.length - 1);
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ showingFirstSearchResult: function()
+ {
+ return (this._currentSearchResultIndex === 0);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ showingLastSearchResult: function()
+ {
+ return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
+ },
+
+ /**
+ * @return {number}
+ */
+ currentSearchResultIndex: function() {
+ return this._currentSearchResultIndex;
+ },
+
+ _jumpToSearchResult: function(index)
+ {
+ var searchResult = this._searchResults[index];
+ if (!searchResult)
+ return;
+
+ var profileNode = searchResult.profileNode;
+ profileNode.revealAndSelect();
+ },
+
+ _ensureFlameChartCreated: function()
+ {
+ if (this._flameChart)
+ return;
+ this._dataProvider = new WebInspector.CPUFlameChartDataProvider(this.profile, this._profileHeader.target());
+ this._flameChart = new WebInspector.CPUProfileFlameChart(this._dataProvider);
+ this._flameChart.addEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected.bind(this));
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onEntrySelected: function(event)
+ {
+ var entryIndex = event.data;
+ var node = this._dataProvider._entryNodes[entryIndex];
+ if (!node || !node.scriptId)
+ return;
+ var script = WebInspector.debuggerModel.scriptForId(node.scriptId)
+ if (!script)
+ return;
+ WebInspector.Revealer.reveal(script.rawLocationToUILocation(node.lineNumber));
+ },
+
+ _changeView: function()
+ {
+ if (!this.profile)
+ return;
+
+ switch (this.viewSelectComboBox.selectedOption().value) {
+ case WebInspector.CPUProfileView._TypeFlame:
+ this._ensureFlameChartCreated();
+ this.dataGrid.detach();
+ this._flameChart.show(this.element);
+ this._viewType.set(WebInspector.CPUProfileView._TypeFlame);
+ this._statusBarButtonsElement.classList.toggle("hidden", true);
+ return;
+ case WebInspector.CPUProfileView._TypeTree:
+ this.profileDataGridTree = this._getTopDownProfileDataGridTree();
+ this._sortProfile();
+ this._viewType.set(WebInspector.CPUProfileView._TypeTree);
+ break;
+ case WebInspector.CPUProfileView._TypeHeavy:
+ this.profileDataGridTree = this._getBottomUpProfileDataGridTree();
+ this._sortProfile();
+ this._viewType.set(WebInspector.CPUProfileView._TypeHeavy);
+ break;
+ }
+
+ this._statusBarButtonsElement.classList.toggle("hidden", false);
+
+ if (this._flameChart)
+ this._flameChart.detach();
+ this.dataGrid.show(this.element);
+
+ if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+ return;
+
+ // The current search needs to be performed again. First negate out previous match
+ // count by calling the search finished callback with a negative number of matches.
+ // Then perform the search again the with same query and callback.
+ this._searchFinishedCallback(this, -this._searchResults.length);
+ this.performSearch(this.currentQuery, this._searchFinishedCallback);
+ },
+
+ _focusClicked: function(event)
+ {
+ if (!this.dataGrid.selectedNode)
+ return;
+
+ this.resetButton.visible = true;
+ this.profileDataGridTree.focus(this.dataGrid.selectedNode);
+ this.refresh();
+ this.refreshVisibleData();
+ },
+
+ _excludeClicked: function(event)
+ {
+ var selectedNode = this.dataGrid.selectedNode
+
+ if (!selectedNode)
+ return;
+
+ selectedNode.deselect();
+
+ this.resetButton.visible = true;
+ this.profileDataGridTree.exclude(selectedNode);
+ this.refresh();
+ this.refreshVisibleData();
+ },
+
+ _resetClicked: function(event)
+ {
+ this.resetButton.visible = false;
+ this.profileDataGridTree.restore();
+ this._linkifier.reset();
+ this.refresh();
+ this.refreshVisibleData();
+ },
+
+ _dataGridNodeSelected: function(node)
+ {
+ this.focusButton.setEnabled(true);
+ this.excludeButton.setEnabled(true);
+ },
+
+ _dataGridNodeDeselected: function(node)
+ {
+ this.focusButton.setEnabled(false);
+ this.excludeButton.setEnabled(false);
+ },
+
+ _sortProfile: function()
+ {
+ var sortAscending = this.dataGrid.isSortOrderAscending();
+ var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier();
+ var sortProperty = {
+ "self": "selfTime",
+ "total": "totalTime",
+ "function": "functionName"
+ }[sortColumnIdentifier];
+
+ this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator(sortProperty, sortAscending));
+
+ this.refresh();
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ * @implements {WebInspector.CPUProfilerModel.Delegate}
+ */
+WebInspector.CPUProfileType = function()
+{
+ WebInspector.ProfileType.call(this, WebInspector.CPUProfileType.TypeId, WebInspector.UIString("Collect JavaScript CPU Profile"));
+ this._recording = false;
+
+ this._nextAnonymousConsoleProfileNumber = 1;
+ this._anonymousConsoleProfileIdToTitle = {};
+
+ WebInspector.CPUProfileType.instance = this;
+ WebInspector.cpuProfilerModel.setDelegate(this);
+}
+
+WebInspector.CPUProfileType.TypeId = "CPU";
+
+WebInspector.CPUProfileType.prototype = {
+ /**
+ * @override
+ * @return {string}
+ */
+ fileExtension: function()
+ {
+ return ".cpuprofile";
+ },
+
+ get buttonTooltip()
+ {
+ return this._recording ? WebInspector.UIString("Stop CPU profiling.") : WebInspector.UIString("Start CPU profiling.");
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ buttonClicked: function()
+ {
+ if (this._recording) {
+ this.stopRecordingProfile();
+ return false;
+ } else {
+ this.startRecordingProfile();
+ return true;
+ }
+ },
+
+ get treeItemTitle()
+ {
+ return WebInspector.UIString("CPU PROFILES");
+ },
+
+ get description()
+ {
+ return WebInspector.UIString("CPU profiles show where the execution time is spent in your page's JavaScript functions.");
+ },
+
+ /**
+ * @param {string} id
+ * @param {!WebInspector.DebuggerModel.Location} scriptLocation
+ * @param {string=} title
+ */
+ consoleProfileStarted: function(id, scriptLocation, title)
+ {
+ var resolvedTitle = title;
+ if (!resolvedTitle) {
+ resolvedTitle = WebInspector.UIString("Profile %s", this._nextAnonymousConsoleProfileNumber++);
+ this._anonymousConsoleProfileIdToTitle[id] = resolvedTitle;
+ }
+ this._addMessageToConsole(WebInspector.ConsoleMessage.MessageType.Profile, scriptLocation, WebInspector.UIString("Profile '%s' started.", resolvedTitle));
+ },
+
+ /**
+ * @param {string} protocolId
+ * @param {!WebInspector.DebuggerModel.Location} scriptLocation
+ * @param {!ProfilerAgent.CPUProfile} cpuProfile
+ * @param {string=} title
+ */
+ consoleProfileFinished: function(protocolId, scriptLocation, cpuProfile, title)
+ {
+ var resolvedTitle = title;
+ if (typeof title === "undefined") {
+ resolvedTitle = this._anonymousConsoleProfileIdToTitle[protocolId];
+ delete this._anonymousConsoleProfileIdToTitle[protocolId];
+ }
+
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ var profile = new WebInspector.CPUProfileHeader(target, this, resolvedTitle);
+ profile.setProtocolProfile(cpuProfile);
+ this.addProfile(profile);
+ this._addMessageToConsole(WebInspector.ConsoleMessage.MessageType.ProfileEnd, scriptLocation, WebInspector.UIString("Profile '%s' finished.", resolvedTitle));
+ },
+
+ /**
+ * @param {string} type
+ * @param {!WebInspector.DebuggerModel.Location} scriptLocation
+ * @param {string} messageText
+ */
+ _addMessageToConsole: function(type, scriptLocation, messageText)
+ {
+ var script = scriptLocation.script();
+ var message = new WebInspector.ConsoleMessage(
+ WebInspector.console.target(),
+ WebInspector.ConsoleMessage.MessageSource.ConsoleAPI,
+ WebInspector.ConsoleMessage.MessageLevel.Debug,
+ messageText,
+ type,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ [{
+ functionName: "",
+ scriptId: scriptLocation.scriptId,
+ url: script ? script.contentURL() : "",
+ lineNumber: scriptLocation.lineNumber,
+ columnNumber: scriptLocation.columnNumber || 0
+ }]);
+
+ WebInspector.console.addMessage(message);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isRecordingProfile: function()
+ {
+ return this._recording;
+ },
+
+ startRecordingProfile: function()
+ {
+ if (this._profileBeingRecorded)
+ return;
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ var profile = new WebInspector.CPUProfileHeader(target, this);
+ this.setProfileBeingRecorded(profile);
+ this.addProfile(profile);
+ profile.updateStatus(WebInspector.UIString("Recording\u2026"));
+ this._recording = true;
+ WebInspector.cpuProfilerModel.setRecording(true);
+ WebInspector.userMetrics.ProfilesCPUProfileTaken.record();
+ ProfilerAgent.start();
+ },
+
+ stopRecordingProfile: function()
+ {
+ this._recording = false;
+ WebInspector.cpuProfilerModel.setRecording(false);
+
+ /**
+ * @param {?string} error
+ * @param {?ProfilerAgent.CPUProfile} profile
+ * @this {WebInspector.CPUProfileType}
+ */
+ function didStopProfiling(error, profile)
+ {
+ if (!this._profileBeingRecorded)
+ return;
+ this._profileBeingRecorded.setProtocolProfile(profile);
+ this._profileBeingRecorded.updateStatus("");
+ var recordedProfile = this._profileBeingRecorded;
+ this.setProfileBeingRecorded(null);
+ this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, recordedProfile);
+ }
+ ProfilerAgent.stop(didStopProfiling.bind(this));
+ },
+
+ /**
+ * @override
+ * @param {string} title
+ * @return {!WebInspector.ProfileHeader}
+ */
+ createProfileLoadedFromFile: function(title)
+ {
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ return new WebInspector.CPUProfileHeader(target, this, title);
+ },
+
+ /**
+ * @override
+ */
+ profileBeingRecordedRemoved: function()
+ {
+ this.stopRecordingProfile();
+ },
+
+ __proto__: WebInspector.ProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @implements {WebInspector.OutputStream}
+ * @implements {WebInspector.OutputStreamDelegate}
+ * @param {!WebInspector.Target} target
+ * @param {!WebInspector.CPUProfileType} type
+ * @param {string=} title
+ */
+WebInspector.CPUProfileHeader = function(target, type, title)
+{
+ WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Profile %d", type._nextProfileUid));
+ this._tempFile = null;
+}
+
+WebInspector.CPUProfileHeader.prototype = {
+ onTransferStarted: function()
+ {
+ this._jsonifiedProfile = "";
+ this.updateStatus(WebInspector.UIString("Loading\u2026 %s", Number.bytesToString(this._jsonifiedProfile.length)), true);
+ },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ */
+ onChunkTransferred: function(reader)
+ {
+ this.updateStatus(WebInspector.UIString("Loading\u2026 %d\%", Number.bytesToString(this._jsonifiedProfile.length)));
+ },
+
+ onTransferFinished: function()
+ {
+ this.updateStatus(WebInspector.UIString("Parsing\u2026"), true);
+ this._profile = JSON.parse(this._jsonifiedProfile);
+ this._jsonifiedProfile = null;
+ this.updateStatus(WebInspector.UIString("Loaded"), false);
+
+ if (this._profileType.profileBeingRecorded() === this)
+ this._profileType.setProfileBeingRecorded(null);
+ },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ * @param {?Event} e
+ */
+ onError: function(reader, e)
+ {
+ var subtitle;
+ switch(e.target.error.code) {
+ case e.target.error.NOT_FOUND_ERR:
+ subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
+ break;
+ case e.target.error.NOT_READABLE_ERR:
+ subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
+ break;
+ case e.target.error.ABORT_ERR:
+ return;
+ default:
+ subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
+ }
+ this.updateStatus(subtitle);
+ },
+
+ /**
+ * @param {string} text
+ */
+ write: function(text)
+ {
+ this._jsonifiedProfile += text;
+ },
+
+ close: function() { },
+
+ /**
+ * @override
+ */
+ dispose: function()
+ {
+ this.removeTempFile();
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.ProfilesPanel} panel
+ * @return {!WebInspector.ProfileSidebarTreeElement}
+ */
+ createSidebarTreeElement: function(panel)
+ {
+ return new WebInspector.ProfileSidebarTreeElement(panel, this, "profile-sidebar-tree-item");
+ },
+
+ /**
+ * @override
+ * @return {!WebInspector.CPUProfileView}
+ */
+ createView: function()
+ {
+ return new WebInspector.CPUProfileView(this);
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ canSaveToFile: function()
+ {
+ return !this.fromFile() && this._protocolProfile;
+ },
+
+ saveToFile: function()
+ {
+ var fileOutputStream = new WebInspector.FileOutputStream();
+
+ /**
+ * @param {boolean} accepted
+ * @this {WebInspector.CPUProfileHeader}
+ */
+ function onOpenForSave(accepted)
+ {
+ if (!accepted)
+ return;
+ function didRead(data)
+ {
+ if (data)
+ fileOutputStream.write(data, fileOutputStream.close.bind(fileOutputStream));
+ else
+ fileOutputStream.close();
+ }
+ if (this._failedToCreateTempFile) {
+ WebInspector.messageSink.addErrorMessage("Failed to open temp file with heap snapshot");
+ fileOutputStream.close();
+ } else if (this._tempFile) {
+ this._tempFile.read(didRead);
+ } else {
+ this._onTempFileReady = onOpenForSave.bind(this, accepted);
+ }
+ }
+ this._fileName = this._fileName || "CPU-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
+ fileOutputStream.open(this._fileName, onOpenForSave.bind(this));
+ },
+
+ /**
+ * @param {!File} file
+ */
+ loadFromFile: function(file)
+ {
+ this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
+ var fileReader = new WebInspector.ChunkedFileReader(file, 10000000, this);
+ fileReader.start(this);
+ },
+
+
+ /**
+ * @return {?ProfilerAgent.CPUProfile}
+ */
+ protocolProfile: function()
+ {
+ return this._protocolProfile;
+ },
+
+ /**
+ * @param {!ProfilerAgent.CPUProfile} cpuProfile
+ */
+ setProtocolProfile: function(cpuProfile)
+ {
+ this._protocolProfile = cpuProfile;
+ this._saveProfileDataToTempFile(cpuProfile);
+ if (this.canSaveToFile())
+ this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
+ },
+
+ /**
+ * @param {!ProfilerAgent.CPUProfile} data
+ */
+ _saveProfileDataToTempFile: function(data)
+ {
+ var serializedData = JSON.stringify(data);
+
+ /**
+ * @this {WebInspector.CPUProfileHeader}
+ */
+ function didCreateTempFile(tempFile)
+ {
+ this._writeToTempFile(tempFile, serializedData);
+ }
+ new WebInspector.TempFile("cpu-profiler", this.uid, didCreateTempFile.bind(this));
+ },
+
+ /**
+ * @param {?WebInspector.TempFile} tempFile
+ * @param {string} serializedData
+ */
+ _writeToTempFile: function(tempFile, serializedData)
+ {
+ this._tempFile = tempFile;
+ if (!tempFile) {
+ this._failedToCreateTempFile = true;
+ this._notifyTempFileReady();
+ return;
+ }
+ /**
+ * @param {boolean} success
+ * @this {WebInspector.CPUProfileHeader}
+ */
+ function didWriteToTempFile(success)
+ {
+ if (!success)
+ this._failedToCreateTempFile = true;
+ tempFile.finishWriting();
+ this._notifyTempFileReady();
+ }
+ tempFile.write(serializedData, didWriteToTempFile.bind(this));
+ },
+
+ _notifyTempFileReady: function()
+ {
+ if (this._onTempFileReady) {
+ this._onTempFileReady();
+ this._onTempFileReady = null;
+ }
+ },
+
+ __proto__: WebInspector.ProfileHeader.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasProfileView.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasProfileView.js
new file mode 100644
index 00000000000..edaeedb3cde
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasProfileView.js
@@ -0,0 +1,1276 @@
+/*
+ * Copyright (C) 2013 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.VBox}
+ * @param {!WebInspector.CanvasProfileHeader} profile
+ */
+WebInspector.CanvasProfileView = function(profile)
+{
+ WebInspector.VBox.call(this);
+ this.registerRequiredCSS("canvasProfiler.css");
+ this.element.classList.add("canvas-profile-view");
+
+ this._profile = profile;
+ this._traceLogId = profile.traceLogId();
+ this._traceLogPlayer = /** @type {!WebInspector.CanvasTraceLogPlayerProxy} */ (profile.traceLogPlayer());
+ this._linkifier = new WebInspector.Linkifier();
+
+ this._replayInfoSplitView = new WebInspector.SplitView(true, true, "canvasProfileViewReplaySplitViewState", 0.34);
+ this._replayInfoSplitView.show(this.element);
+
+ this._imageSplitView = new WebInspector.SplitView(false, true, "canvasProfileViewSplitViewState", 300);
+ this._imageSplitView.show(this._replayInfoSplitView.mainElement());
+
+ var replayImageContainerView = new WebInspector.VBox();
+ replayImageContainerView.setMinimumSize(50, 28);
+ replayImageContainerView.show(this._imageSplitView.mainElement());
+
+ // NOTE: The replayImageContainer can NOT be a flex div (e.g. VBox or SplitView elements)!
+ var replayImageContainer = replayImageContainerView.element.createChild("div");
+ replayImageContainer.id = "canvas-replay-image-container";
+ this._replayImageElement = replayImageContainer.createChild("img", "canvas-replay-image");
+ this._debugInfoElement = replayImageContainer.createChild("div", "canvas-debug-info hidden");
+ this._spinnerIcon = replayImageContainer.createChild("div", "spinner-icon small hidden");
+
+ var replayLogContainerView = new WebInspector.VBox();
+ replayLogContainerView.setMinimumSize(22, 22);
+ replayLogContainerView.show(this._imageSplitView.sidebarElement());
+
+ var replayLogContainer = replayLogContainerView.element;
+ var controlsContainer = replayLogContainer.createChild("div", "status-bar");
+ var logGridContainer = replayLogContainer.createChild("div", "canvas-replay-log");
+
+ this._createControlButton(controlsContainer, "canvas-replay-first-step", WebInspector.UIString("First call."), this._onReplayFirstStepClick.bind(this));
+ this._createControlButton(controlsContainer, "canvas-replay-prev-step", WebInspector.UIString("Previous call."), this._onReplayStepClick.bind(this, false));
+ this._createControlButton(controlsContainer, "canvas-replay-next-step", WebInspector.UIString("Next call."), this._onReplayStepClick.bind(this, true));
+ this._createControlButton(controlsContainer, "canvas-replay-prev-draw", WebInspector.UIString("Previous drawing call."), this._onReplayDrawingCallClick.bind(this, false));
+ this._createControlButton(controlsContainer, "canvas-replay-next-draw", WebInspector.UIString("Next drawing call."), this._onReplayDrawingCallClick.bind(this, true));
+ this._createControlButton(controlsContainer, "canvas-replay-last-step", WebInspector.UIString("Last call."), this._onReplayLastStepClick.bind(this));
+
+ this._replayContextSelector = new WebInspector.StatusBarComboBox(this._onReplayContextChanged.bind(this));
+ this._replayContextSelector.createOption(WebInspector.UIString("<screenshot auto>"), WebInspector.UIString("Show screenshot of the last replayed resource."), "");
+ controlsContainer.appendChild(this._replayContextSelector.element);
+
+ this._installReplayInfoSidebarWidgets(controlsContainer);
+
+ this._replayStateView = new WebInspector.CanvasReplayStateView(this._traceLogPlayer);
+ this._replayStateView.show(this._replayInfoSplitView.sidebarElement());
+
+ /** @type {!Object.<string, boolean>} */
+ this._replayContexts = {};
+
+ var columns = [
+ {title: "#", sortable: false, width: "5%"},
+ {title: WebInspector.UIString("Call"), sortable: false, width: "75%", disclosure: true},
+ {title: WebInspector.UIString("Location"), sortable: false, width: "20%"}
+ ];
+
+ this._logGrid = new WebInspector.DataGrid(columns);
+ this._logGrid.element.classList.add("fill");
+ this._logGrid.show(logGridContainer);
+ this._logGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._replayTraceLog, this);
+
+ this.element.addEventListener("mousedown", this._onMouseClick.bind(this), true);
+
+ this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._popoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onHidePopover.bind(this), true);
+ this._popoverHelper.setRemoteObjectFormatter(this._hexNumbersFormatter.bind(this));
+
+ this._requestTraceLog(0);
+}
+
+/**
+ * @const
+ * @type {number}
+ */
+WebInspector.CanvasProfileView.TraceLogPollingInterval = 500;
+
+WebInspector.CanvasProfileView.prototype = {
+ dispose: function()
+ {
+ this._linkifier.reset();
+ },
+
+ get statusBarItems()
+ {
+ return [];
+ },
+
+ get profile()
+ {
+ return this._profile;
+ },
+
+ /**
+ * @override
+ * @return {!Array.<!Element>}
+ */
+ elementsToRestoreScrollPositionsFor: function()
+ {
+ return [this._logGrid.scrollContainer];
+ },
+
+ /**
+ * @param {!Element} controlsContainer
+ */
+ _installReplayInfoSidebarWidgets: function(controlsContainer)
+ {
+ this._replayInfoResizeWidgetElement = controlsContainer.createChild("div", "resizer-widget");
+ this._replayInfoSplitView.addEventListener(WebInspector.SplitView.Events.ShowModeChanged, this._updateReplayInfoResizeWidget, this);
+ this._updateReplayInfoResizeWidget();
+ this._replayInfoSplitView.installResizer(this._replayInfoResizeWidgetElement);
+
+ this._toggleReplayStateSidebarButton = this._replayInfoSplitView.createShowHideSidebarButton("sidebar", "canvas-sidebar-show-hide-button");
+
+ controlsContainer.appendChild(this._toggleReplayStateSidebarButton.element);
+ this._replayInfoSplitView.hideSidebar();
+ },
+
+ _updateReplayInfoResizeWidget: function()
+ {
+ this._replayInfoResizeWidgetElement.classList.toggle("hidden", this._replayInfoSplitView.showMode() !== WebInspector.SplitView.ShowMode.Both);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onMouseClick: function(event)
+ {
+ var resourceLinkElement = event.target.enclosingNodeOrSelfWithClass("canvas-formatted-resource");
+ if (resourceLinkElement) {
+ this._replayInfoSplitView.showBoth();
+ this._replayStateView.selectResource(resourceLinkElement.__resourceId);
+ event.consume(true);
+ return;
+ }
+ if (event.target.enclosingNodeOrSelfWithClass("webkit-html-resource-link"))
+ event.consume(false);
+ },
+
+ /**
+ * @param {!Element} parent
+ * @param {string} className
+ * @param {string} title
+ * @param {function(this:WebInspector.CanvasProfileView)} clickCallback
+ */
+ _createControlButton: function(parent, className, title, clickCallback)
+ {
+ var button = new WebInspector.StatusBarButton(title, className + " canvas-replay-button");
+ parent.appendChild(button.element);
+
+ button.makeLongClickEnabled();
+ button.addEventListener("click", clickCallback, this);
+ button.addEventListener("longClickDown", clickCallback, this);
+ button.addEventListener("longClickPress", clickCallback, this);
+ },
+
+ _onReplayContextChanged: function()
+ {
+ var selectedContextId = this._replayContextSelector.selectedOption().value;
+
+ /**
+ * @param {?CanvasAgent.ResourceState} resourceState
+ * @this {WebInspector.CanvasProfileView}
+ */
+ function didReceiveResourceState(resourceState)
+ {
+ this._enableWaitIcon(false);
+ if (selectedContextId !== this._replayContextSelector.selectedOption().value)
+ return;
+ var imageURL = (resourceState && resourceState.imageURL) || "";
+ this._replayImageElement.src = imageURL;
+ this._replayImageElement.style.visibility = imageURL ? "" : "hidden";
+ }
+
+ this._enableWaitIcon(true);
+ this._traceLogPlayer.getResourceState(selectedContextId, didReceiveResourceState.bind(this));
+ },
+
+ /**
+ * @param {boolean} forward
+ */
+ _onReplayStepClick: function(forward)
+ {
+ var selectedNode = this._logGrid.selectedNode;
+ if (!selectedNode)
+ return;
+ var nextNode = selectedNode;
+ do {
+ nextNode = forward ? nextNode.traverseNextNode(false) : nextNode.traversePreviousNode(false);
+ } while (nextNode && typeof nextNode.index !== "number");
+ (nextNode || selectedNode).revealAndSelect();
+ },
+
+ /**
+ * @param {boolean} forward
+ */
+ _onReplayDrawingCallClick: function(forward)
+ {
+ var selectedNode = this._logGrid.selectedNode;
+ if (!selectedNode)
+ return;
+ var nextNode = selectedNode;
+ while (nextNode) {
+ var sibling = forward ? nextNode.nextSibling : nextNode.previousSibling;
+ if (sibling) {
+ nextNode = sibling;
+ if (nextNode.hasChildren || nextNode.call.isDrawingCall)
+ break;
+ } else {
+ nextNode = nextNode.parent;
+ if (!forward)
+ break;
+ }
+ }
+ if (!nextNode && forward)
+ this._onReplayLastStepClick();
+ else
+ (nextNode || selectedNode).revealAndSelect();
+ },
+
+ _onReplayFirstStepClick: function()
+ {
+ var firstNode = this._logGrid.rootNode().children[0];
+ if (firstNode)
+ firstNode.revealAndSelect();
+ },
+
+ _onReplayLastStepClick: function()
+ {
+ var lastNode = this._logGrid.rootNode().children.peekLast();
+ if (!lastNode)
+ return;
+ while (lastNode.expanded) {
+ var lastChild = lastNode.children.peekLast();
+ if (!lastChild)
+ break;
+ lastNode = lastChild;
+ }
+ lastNode.revealAndSelect();
+ },
+
+ /**
+ * @param {boolean} enable
+ */
+ _enableWaitIcon: function(enable)
+ {
+ this._spinnerIcon.classList.toggle("hidden", !enable);
+ this._debugInfoElement.classList.toggle("hidden", enable);
+ },
+
+ _replayTraceLog: function()
+ {
+ if (this._pendingReplayTraceLogEvent)
+ return;
+ var index = this._selectedCallIndex();
+ if (index === -1 || index === this._lastReplayCallIndex)
+ return;
+ this._lastReplayCallIndex = index;
+ this._pendingReplayTraceLogEvent = true;
+
+ /**
+ * @param {?CanvasAgent.ResourceState} resourceState
+ * @param {number} replayTime
+ * @this {WebInspector.CanvasProfileView}
+ */
+ function didReplayTraceLog(resourceState, replayTime)
+ {
+ delete this._pendingReplayTraceLogEvent;
+ this._enableWaitIcon(false);
+
+ this._debugInfoElement.textContent = WebInspector.UIString("Replay time: %s", Number.secondsToString(replayTime / 1000, true));
+ this._onReplayContextChanged();
+
+ if (index !== this._selectedCallIndex())
+ this._replayTraceLog();
+ }
+ this._enableWaitIcon(true);
+ this._traceLogPlayer.replayTraceLog(index, didReplayTraceLog.bind(this));
+ },
+
+ /**
+ * @param {number} offset
+ */
+ _requestTraceLog: function(offset)
+ {
+ /**
+ * @param {?CanvasAgent.TraceLog} traceLog
+ * @this {WebInspector.CanvasProfileView}
+ */
+ function didReceiveTraceLog(traceLog)
+ {
+ this._enableWaitIcon(false);
+ if (!traceLog)
+ return;
+ var callNodes = [];
+ var calls = traceLog.calls;
+ var index = traceLog.startOffset;
+ for (var i = 0, n = calls.length; i < n; ++i)
+ callNodes.push(this._createCallNode(index++, calls[i]));
+ var contexts = traceLog.contexts;
+ for (var i = 0, n = contexts.length; i < n; ++i) {
+ var contextId = contexts[i].resourceId || "";
+ var description = contexts[i].description || "";
+ if (this._replayContexts[contextId])
+ continue;
+ this._replayContexts[contextId] = true;
+ this._replayContextSelector.createOption(description, WebInspector.UIString("Show screenshot of this context's canvas."), contextId);
+ }
+ this._appendCallNodes(callNodes);
+ if (traceLog.alive)
+ setTimeout(this._requestTraceLog.bind(this, index), WebInspector.CanvasProfileView.TraceLogPollingInterval);
+ else
+ this._flattenSingleFrameNode();
+ this._profile._updateCapturingStatus(traceLog);
+ this._onReplayLastStepClick(); // Automatically replay the last step.
+ }
+ this._enableWaitIcon(true);
+ this._traceLogPlayer.getTraceLog(offset, undefined, didReceiveTraceLog.bind(this));
+ },
+
+ /**
+ * @return {number}
+ */
+ _selectedCallIndex: function()
+ {
+ var node = this._logGrid.selectedNode;
+ return node ? this._peekLastRecursively(node).index : -1;
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} node
+ * @return {!WebInspector.DataGridNode}
+ */
+ _peekLastRecursively: function(node)
+ {
+ var lastChild;
+ while ((lastChild = node.children.peekLast()))
+ node = lastChild;
+ return node;
+ },
+
+ /**
+ * @param {!Array.<!WebInspector.DataGridNode>} callNodes
+ */
+ _appendCallNodes: function(callNodes)
+ {
+ var rootNode = this._logGrid.rootNode();
+ var frameNode = rootNode.children.peekLast();
+ if (frameNode && this._peekLastRecursively(frameNode).call.isFrameEndCall)
+ frameNode = null;
+ for (var i = 0, n = callNodes.length; i < n; ++i) {
+ if (!frameNode) {
+ var index = rootNode.children.length;
+ var data = {};
+ data[0] = "";
+ data[1] = WebInspector.UIString("Frame #%d", index + 1);
+ data[2] = "";
+ frameNode = new WebInspector.DataGridNode(data);
+ frameNode.selectable = true;
+ rootNode.appendChild(frameNode);
+ }
+ var nextFrameCallIndex = i + 1;
+ while (nextFrameCallIndex < n && !callNodes[nextFrameCallIndex - 1].call.isFrameEndCall)
+ ++nextFrameCallIndex;
+ this._appendCallNodesToFrameNode(frameNode, callNodes, i, nextFrameCallIndex);
+ i = nextFrameCallIndex - 1;
+ frameNode = null;
+ }
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} frameNode
+ * @param {!Array.<!WebInspector.DataGridNode>} callNodes
+ * @param {number} fromIndex
+ * @param {number} toIndex not inclusive
+ */
+ _appendCallNodesToFrameNode: function(frameNode, callNodes, fromIndex, toIndex)
+ {
+ var self = this;
+ function appendDrawCallGroup()
+ {
+ var index = self._drawCallGroupsCount || 0;
+ var data = {};
+ data[0] = "";
+ data[1] = WebInspector.UIString("Draw call group #%d", index + 1);
+ data[2] = "";
+ var node = new WebInspector.DataGridNode(data);
+ node.selectable = true;
+ self._drawCallGroupsCount = index + 1;
+ frameNode.appendChild(node);
+ return node;
+ }
+
+ function splitDrawCallGroup(drawCallGroup)
+ {
+ var splitIndex = 0;
+ var splitNode;
+ while ((splitNode = drawCallGroup.children[splitIndex])) {
+ if (splitNode.call.isDrawingCall)
+ break;
+ ++splitIndex;
+ }
+ var newDrawCallGroup = appendDrawCallGroup();
+ var lastNode;
+ while ((lastNode = drawCallGroup.children[splitIndex + 1]))
+ newDrawCallGroup.appendChild(lastNode);
+ return newDrawCallGroup;
+ }
+
+ var drawCallGroup = frameNode.children.peekLast();
+ var groupHasDrawCall = false;
+ if (drawCallGroup) {
+ for (var i = 0, n = drawCallGroup.children.length; i < n; ++i) {
+ if (drawCallGroup.children[i].call.isDrawingCall) {
+ groupHasDrawCall = true;
+ break;
+ }
+ }
+ } else
+ drawCallGroup = appendDrawCallGroup();
+
+ for (var i = fromIndex; i < toIndex; ++i) {
+ var node = callNodes[i];
+ drawCallGroup.appendChild(node);
+ if (node.call.isDrawingCall) {
+ if (groupHasDrawCall)
+ drawCallGroup = splitDrawCallGroup(drawCallGroup);
+ else
+ groupHasDrawCall = true;
+ }
+ }
+ },
+
+ /**
+ * @param {number} index
+ * @param {!CanvasAgent.Call} call
+ * @return {!WebInspector.DataGridNode}
+ */
+ _createCallNode: function(index, call)
+ {
+ var callViewElement = document.createElement("div");
+
+ var data = {};
+ data[0] = index + 1;
+ data[1] = callViewElement;
+ data[2] = "";
+ if (call.sourceURL) {
+ // FIXME(62725): stack trace line/column numbers are one-based.
+ var lineNumber = Math.max(0, call.lineNumber - 1) || 0;
+ var columnNumber = Math.max(0, call.columnNumber - 1) || 0;
+ data[2] = this._linkifier.linkifyLocation(this.profile.target(), call.sourceURL, lineNumber, columnNumber);
+ }
+
+ callViewElement.createChild("span", "canvas-function-name").textContent = call.functionName || "context." + call.property;
+
+ if (call.arguments) {
+ callViewElement.createTextChild("(");
+ for (var i = 0, n = call.arguments.length; i < n; ++i) {
+ var argument = /** @type {!CanvasAgent.CallArgument} */ (call.arguments[i]);
+ if (i)
+ callViewElement.createTextChild(", ");
+ var element = WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(argument);
+ element.__argumentIndex = i;
+ callViewElement.appendChild(element);
+ }
+ callViewElement.createTextChild(")");
+ } else if (call.value) {
+ callViewElement.createTextChild(" = ");
+ callViewElement.appendChild(WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(call.value));
+ }
+
+ if (call.result) {
+ callViewElement.createTextChild(" => ");
+ callViewElement.appendChild(WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(call.result));
+ }
+
+ var node = new WebInspector.DataGridNode(data);
+ node.index = index;
+ node.selectable = true;
+ node.call = call;
+ return node;
+ },
+
+ _popoverAnchor: function(element, event)
+ {
+ var argumentElement = element.enclosingNodeOrSelfWithClass("canvas-call-argument");
+ if (!argumentElement || argumentElement.__suppressPopover)
+ return null;
+ return argumentElement;
+ },
+
+ _resolveObjectForPopover: function(argumentElement, showCallback, objectGroupName)
+ {
+ /**
+ * @param {?Protocol.Error} error
+ * @param {!RuntimeAgent.RemoteObject=} result
+ * @param {!CanvasAgent.ResourceState=} resourceState
+ * @this {WebInspector.CanvasProfileView}
+ */
+ function showObjectPopover(error, result, resourceState)
+ {
+ if (error)
+ return;
+
+ // FIXME: handle resourceState also
+ if (!result)
+ return;
+
+ this._popoverAnchorElement = argumentElement.cloneNode(true);
+ this._popoverAnchorElement.classList.add("canvas-popover-anchor");
+ this._popoverAnchorElement.classList.add("source-frame-eval-expression");
+ argumentElement.parentElement.appendChild(this._popoverAnchorElement);
+
+ var diffLeft = this._popoverAnchorElement.boxInWindow().x - argumentElement.boxInWindow().x;
+ this._popoverAnchorElement.style.left = this._popoverAnchorElement.offsetLeft - diffLeft + "px";
+
+ showCallback(WebInspector.runtimeModel.createRemoteObject(result), false, this._popoverAnchorElement);
+ }
+
+ var evalResult = argumentElement.__evalResult;
+ if (evalResult)
+ showObjectPopover.call(this, null, evalResult);
+ else {
+ var dataGridNode = this._logGrid.dataGridNodeFromNode(argumentElement);
+ if (!dataGridNode || typeof dataGridNode.index !== "number") {
+ this._popoverHelper.hidePopover();
+ return;
+ }
+ var callIndex = dataGridNode.index;
+ var argumentIndex = argumentElement.__argumentIndex;
+ if (typeof argumentIndex !== "number")
+ argumentIndex = -1;
+ CanvasAgent.evaluateTraceLogCallArgument(this._traceLogId, callIndex, argumentIndex, objectGroupName, showObjectPopover.bind(this));
+ }
+ },
+
+ /**
+ * @param {!WebInspector.RemoteObject} object
+ * @return {string}
+ */
+ _hexNumbersFormatter: function(object)
+ {
+ if (object.type === "number") {
+ // Show enum values in hex with min length of 4 (e.g. 0x0012).
+ var str = "0000" + Number(object.description).toString(16).toUpperCase();
+ str = str.replace(/^0+(.{4,})$/, "$1");
+ return "0x" + str;
+ }
+ return object.description || "";
+ },
+
+ _onHidePopover: function()
+ {
+ if (this._popoverAnchorElement) {
+ this._popoverAnchorElement.remove()
+ delete this._popoverAnchorElement;
+ }
+ },
+
+ _flattenSingleFrameNode: function()
+ {
+ var rootNode = this._logGrid.rootNode();
+ if (rootNode.children.length !== 1)
+ return;
+ var frameNode = rootNode.children[0];
+ while (frameNode.children[0])
+ rootNode.appendChild(frameNode.children[0]);
+ rootNode.removeChild(frameNode);
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ */
+WebInspector.CanvasProfileType = function()
+{
+ WebInspector.ProfileType.call(this, WebInspector.CanvasProfileType.TypeId, WebInspector.UIString("Capture Canvas Frame"));
+ this._recording = false;
+ this._lastProfileHeader = null;
+
+ this._capturingModeSelector = new WebInspector.StatusBarComboBox(this._dispatchViewUpdatedEvent.bind(this));
+ this._capturingModeSelector.element.title = WebInspector.UIString("Canvas capture mode.");
+ this._capturingModeSelector.createOption(WebInspector.UIString("Single Frame"), WebInspector.UIString("Capture a single canvas frame."), "");
+ this._capturingModeSelector.createOption(WebInspector.UIString("Consecutive Frames"), WebInspector.UIString("Capture consecutive canvas frames."), "1");
+
+ /** @type {!Object.<string, !Element>} */
+ this._frameOptions = {};
+
+ /** @type {!Object.<string, boolean>} */
+ this._framesWithCanvases = {};
+
+ this._frameSelector = new WebInspector.StatusBarComboBox(this._dispatchViewUpdatedEvent.bind(this));
+ this._frameSelector.element.title = WebInspector.UIString("Frame containing the canvases to capture.");
+ this._frameSelector.element.classList.add("hidden");
+
+ this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ this._target.resourceTreeModel.frames().forEach(this._addFrame, this);
+ this._target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, this._frameAdded, this);
+ this._target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameRemoved, this);
+
+ this._dispatcher = new WebInspector.CanvasDispatcher(this);
+ this._canvasAgentEnabled = false;
+
+ this._decorationElement = document.createElement("div");
+ this._decorationElement.className = "profile-canvas-decoration";
+ this._updateDecorationElement();
+}
+
+WebInspector.CanvasProfileType.TypeId = "CANVAS_PROFILE";
+
+WebInspector.CanvasProfileType.prototype = {
+ get statusBarItems()
+ {
+ return [this._capturingModeSelector.element, this._frameSelector.element];
+ },
+
+ get buttonTooltip()
+ {
+ if (this._isSingleFrameMode())
+ return WebInspector.UIString("Capture next canvas frame.");
+ else
+ return this._recording ? WebInspector.UIString("Stop capturing canvas frames.") : WebInspector.UIString("Start capturing canvas frames.");
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ buttonClicked: function()
+ {
+ if (!this._canvasAgentEnabled)
+ return false;
+ if (this._recording) {
+ this._recording = false;
+ this._stopFrameCapturing();
+ } else if (this._isSingleFrameMode()) {
+ this._recording = false;
+ this._runSingleFrameCapturing();
+ } else {
+ this._recording = true;
+ this._startFrameCapturing();
+ }
+ return this._recording;
+ },
+
+ _runSingleFrameCapturing: function()
+ {
+ var frameId = this._selectedFrameId();
+ this._target.profilingLock.acquire();
+ CanvasAgent.captureFrame(frameId, this._didStartCapturingFrame.bind(this, frameId));
+ this._target.profilingLock.release();
+ },
+
+ _startFrameCapturing: function()
+ {
+ var frameId = this._selectedFrameId();
+ this._target.profilingLock.acquire();
+ CanvasAgent.startCapturing(frameId, this._didStartCapturingFrame.bind(this, frameId));
+ },
+
+ _stopFrameCapturing: function()
+ {
+ if (!this._lastProfileHeader) {
+ this._target.profilingLock.release();
+ return;
+ }
+ var profileHeader = this._lastProfileHeader;
+ var traceLogId = profileHeader.traceLogId();
+ this._lastProfileHeader = null;
+ function didStopCapturing()
+ {
+ profileHeader._updateCapturingStatus();
+ }
+ CanvasAgent.stopCapturing(traceLogId, didStopCapturing);
+ this._target.profilingLock.release();
+ },
+
+ /**
+ * @param {string|undefined} frameId
+ * @param {?Protocol.Error} error
+ * @param {!CanvasAgent.TraceLogId} traceLogId
+ */
+ _didStartCapturingFrame: function(frameId, error, traceLogId)
+ {
+ if (error || this._lastProfileHeader && this._lastProfileHeader.traceLogId() === traceLogId)
+ return;
+ var profileHeader = new WebInspector.CanvasProfileHeader(this._target, this, traceLogId, frameId);
+ this._lastProfileHeader = profileHeader;
+ this.addProfile(profileHeader);
+ profileHeader._updateCapturingStatus();
+ },
+
+ get treeItemTitle()
+ {
+ return WebInspector.UIString("CANVAS PROFILE");
+ },
+
+ get description()
+ {
+ return WebInspector.UIString("Canvas calls instrumentation");
+ },
+
+ /**
+ * @override
+ * @return {!Element}
+ */
+ decorationElement: function()
+ {
+ return this._decorationElement;
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ removeProfile: function(profile)
+ {
+ WebInspector.ProfileType.prototype.removeProfile.call(this, profile);
+ if (this._recording && profile === this._lastProfileHeader)
+ this._recording = false;
+ },
+
+ /**
+ * @param {boolean=} forcePageReload
+ */
+ _updateDecorationElement: function(forcePageReload)
+ {
+ this._decorationElement.removeChildren();
+ this._decorationElement.createChild("div", "warning-icon-small");
+ this._decorationElement.appendChild(document.createTextNode(this._canvasAgentEnabled ? WebInspector.UIString("Canvas Profiler is enabled.") : WebInspector.UIString("Canvas Profiler is disabled.")));
+ var button = this._decorationElement.createChild("button");
+ button.type = "button";
+ button.textContent = this._canvasAgentEnabled ? WebInspector.UIString("Disable") : WebInspector.UIString("Enable");
+ button.addEventListener("click", this._onProfilerEnableButtonClick.bind(this, !this._canvasAgentEnabled), false);
+
+ var target = this._target;
+ /**
+ * @param {?Protocol.Error} error
+ * @param {boolean} result
+ */
+ function hasUninstrumentedCanvasesCallback(error, result)
+ {
+ if (error || result)
+ target.resourceTreeModel.reloadPage();
+ }
+
+ if (forcePageReload) {
+ if (this._canvasAgentEnabled) {
+ CanvasAgent.hasUninstrumentedCanvases(hasUninstrumentedCanvasesCallback);
+ } else {
+ for (var frameId in this._framesWithCanvases) {
+ if (this._framesWithCanvases.hasOwnProperty(frameId)) {
+ target.resourceTreeModel.reloadPage();
+ break;
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * @param {boolean} enable
+ */
+ _onProfilerEnableButtonClick: function(enable)
+ {
+ if (this._canvasAgentEnabled === enable)
+ return;
+
+ /**
+ * @param {?Protocol.Error} error
+ * @this {WebInspector.CanvasProfileType}
+ */
+ function callback(error)
+ {
+ if (error)
+ return;
+ this._canvasAgentEnabled = enable;
+ this._updateDecorationElement(true);
+ this._dispatchViewUpdatedEvent();
+ }
+ if (enable)
+ CanvasAgent.enable(callback.bind(this));
+ else
+ CanvasAgent.disable(callback.bind(this));
+ },
+
+ /**
+ * @return {boolean}
+ */
+ _isSingleFrameMode: function()
+ {
+ return !this._capturingModeSelector.selectedOption().value;
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _frameAdded: function(event)
+ {
+ var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
+ this._addFrame(frame);
+ },
+
+ /**
+ * @param {!WebInspector.ResourceTreeFrame} frame
+ */
+ _addFrame: function(frame)
+ {
+ var frameId = frame.id;
+ var option = document.createElement("option");
+ option.text = frame.displayName();
+ option.title = frame.url;
+ option.value = frameId;
+
+ this._frameOptions[frameId] = option;
+
+ if (this._framesWithCanvases[frameId]) {
+ this._frameSelector.addOption(option);
+ this._dispatchViewUpdatedEvent();
+ }
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _frameRemoved: function(event)
+ {
+ var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
+ var frameId = frame.id;
+ var option = this._frameOptions[frameId];
+ if (option && this._framesWithCanvases[frameId]) {
+ this._frameSelector.removeOption(option);
+ this._dispatchViewUpdatedEvent();
+ }
+ delete this._frameOptions[frameId];
+ delete this._framesWithCanvases[frameId];
+ },
+
+ /**
+ * @param {string} frameId
+ */
+ _contextCreated: function(frameId)
+ {
+ if (this._framesWithCanvases[frameId])
+ return;
+ this._framesWithCanvases[frameId] = true;
+ var option = this._frameOptions[frameId];
+ if (option) {
+ this._frameSelector.addOption(option);
+ this._dispatchViewUpdatedEvent();
+ }
+ },
+
+ /**
+ * @param {!PageAgent.FrameId=} frameId
+ * @param {!CanvasAgent.TraceLogId=} traceLogId
+ */
+ _traceLogsRemoved: function(frameId, traceLogId)
+ {
+ var sidebarElementsToDelete = [];
+ var sidebarElements = /** @type {!Array.<!WebInspector.ProfileSidebarTreeElement>} */ ((this.treeElement && this.treeElement.children) || []);
+ for (var i = 0, n = sidebarElements.length; i < n; ++i) {
+ var header = /** @type {!WebInspector.CanvasProfileHeader} */ (sidebarElements[i].profile);
+ if (!header)
+ continue;
+ if (frameId && frameId !== header.frameId())
+ continue;
+ if (traceLogId && traceLogId !== header.traceLogId())
+ continue;
+ sidebarElementsToDelete.push(sidebarElements[i]);
+ }
+ for (var i = 0, n = sidebarElementsToDelete.length; i < n; ++i)
+ sidebarElementsToDelete[i].ondelete();
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ _selectedFrameId: function()
+ {
+ var option = this._frameSelector.selectedOption();
+ return option ? option.value : undefined;
+ },
+
+ _dispatchViewUpdatedEvent: function()
+ {
+ this._frameSelector.element.classList.toggle("hidden", this._frameSelector.size() <= 1);
+ this.dispatchEventToListeners(WebInspector.ProfileType.Events.ViewUpdated);
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ isInstantProfile: function()
+ {
+ return this._isSingleFrameMode();
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ isEnabled: function()
+ {
+ return this._canvasAgentEnabled;
+ },
+
+ __proto__: WebInspector.ProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @implements {CanvasAgent.Dispatcher}
+ * @param {!WebInspector.CanvasProfileType} profileType
+ */
+WebInspector.CanvasDispatcher = function(profileType)
+{
+ this._profileType = profileType;
+ InspectorBackend.registerCanvasDispatcher(this);
+}
+
+WebInspector.CanvasDispatcher.prototype = {
+ /**
+ * @param {string} frameId
+ */
+ contextCreated: function(frameId)
+ {
+ this._profileType._contextCreated(frameId);
+ },
+
+ /**
+ * @param {!PageAgent.FrameId=} frameId
+ * @param {!CanvasAgent.TraceLogId=} traceLogId
+ */
+ traceLogsRemoved: function(frameId, traceLogId)
+ {
+ this._profileType._traceLogsRemoved(frameId, traceLogId);
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @param {!WebInspector.Target} target
+ * @param {!WebInspector.CanvasProfileType} type
+ * @param {!CanvasAgent.TraceLogId=} traceLogId
+ * @param {!PageAgent.FrameId=} frameId
+ */
+WebInspector.CanvasProfileHeader = function(target, type, traceLogId, frameId)
+{
+ WebInspector.ProfileHeader.call(this, target, type, WebInspector.UIString("Trace Log %d", type._nextProfileUid));
+ /** @type {!CanvasAgent.TraceLogId} */
+ this._traceLogId = traceLogId || "";
+ this._frameId = frameId;
+ this._alive = true;
+ this._traceLogSize = 0;
+ this._traceLogPlayer = traceLogId ? new WebInspector.CanvasTraceLogPlayerProxy(traceLogId) : null;
+}
+
+WebInspector.CanvasProfileHeader.prototype = {
+ /**
+ * @return {!CanvasAgent.TraceLogId}
+ */
+ traceLogId: function()
+ {
+ return this._traceLogId;
+ },
+
+ /**
+ * @return {?WebInspector.CanvasTraceLogPlayerProxy}
+ */
+ traceLogPlayer: function()
+ {
+ return this._traceLogPlayer;
+ },
+
+ /**
+ * @return {!PageAgent.FrameId|undefined}
+ */
+ frameId: function()
+ {
+ return this._frameId;
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.ProfilesPanel} panel
+ * @return {!WebInspector.ProfileSidebarTreeElement}
+ */
+ createSidebarTreeElement: function(panel)
+ {
+ return new WebInspector.ProfileSidebarTreeElement(panel, this, "profile-sidebar-tree-item");
+ },
+
+ /**
+ * @override
+ * @return {!WebInspector.CanvasProfileView}
+ */
+ createView: function()
+ {
+ return new WebInspector.CanvasProfileView(this);
+ },
+
+ /**
+ * @override
+ */
+ dispose: function()
+ {
+ if (this._traceLogPlayer)
+ this._traceLogPlayer.dispose();
+ clearTimeout(this._requestStatusTimer);
+ this._alive = false;
+ },
+
+ /**
+ * @param {!CanvasAgent.TraceLog=} traceLog
+ */
+ _updateCapturingStatus: function(traceLog)
+ {
+ if (!this._traceLogId)
+ return;
+
+ if (traceLog) {
+ this._alive = traceLog.alive;
+ this._traceLogSize = traceLog.totalAvailableCalls;
+ }
+
+ var subtitle = this._alive ? WebInspector.UIString("Capturing\u2026 %d calls", this._traceLogSize) : WebInspector.UIString("Captured %d calls", this._traceLogSize);
+ this.updateStatus(subtitle, this._alive);
+
+ if (this._alive) {
+ clearTimeout(this._requestStatusTimer);
+ this._requestStatusTimer = setTimeout(this._requestCapturingStatus.bind(this), WebInspector.CanvasProfileView.TraceLogPollingInterval);
+ }
+ },
+
+ _requestCapturingStatus: function()
+ {
+ /**
+ * @param {?CanvasAgent.TraceLog} traceLog
+ * @this {WebInspector.CanvasProfileHeader}
+ */
+ function didReceiveTraceLog(traceLog)
+ {
+ if (!traceLog)
+ return;
+ this._alive = traceLog.alive;
+ this._traceLogSize = traceLog.totalAvailableCalls;
+ this._updateCapturingStatus();
+ }
+ this._traceLogPlayer.getTraceLog(0, 0, didReceiveTraceLog.bind(this));
+ },
+
+ __proto__: WebInspector.ProfileHeader.prototype
+}
+
+WebInspector.CanvasProfileDataGridHelper = {
+ /**
+ * @param {!CanvasAgent.CallArgument} callArgument
+ * @return {!Element}
+ */
+ createCallArgumentElement: function(callArgument)
+ {
+ if (callArgument.enumName)
+ return WebInspector.CanvasProfileDataGridHelper.createEnumValueElement(callArgument.enumName, +callArgument.description);
+ var element = document.createElement("span");
+ element.className = "canvas-call-argument";
+ var description = callArgument.description;
+ if (callArgument.type === "string") {
+ const maxStringLength = 150;
+ element.createTextChild("\"");
+ element.createChild("span", "canvas-formatted-string").textContent = description.trimMiddle(maxStringLength);
+ element.createTextChild("\"");
+ element.__suppressPopover = (description.length <= maxStringLength && !/[\r\n]/.test(description));
+ if (!element.__suppressPopover)
+ element.__evalResult = WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(description);
+ } else {
+ var type = callArgument.subtype || callArgument.type;
+ if (type) {
+ element.classList.add("canvas-formatted-" + type);
+ if (["null", "undefined", "boolean", "number"].indexOf(type) >= 0)
+ element.__suppressPopover = true;
+ }
+ element.textContent = description;
+ if (callArgument.remoteObject)
+ element.__evalResult = WebInspector.runtimeModel.createRemoteObject(callArgument.remoteObject);
+ }
+ if (callArgument.resourceId) {
+ element.classList.add("canvas-formatted-resource");
+ element.__resourceId = callArgument.resourceId;
+ }
+ return element;
+ },
+
+ /**
+ * @param {string} enumName
+ * @param {number} enumValue
+ * @return {!Element}
+ */
+ createEnumValueElement: function(enumName, enumValue)
+ {
+ var element = document.createElement("span");
+ element.className = "canvas-call-argument canvas-formatted-number";
+ element.textContent = enumName;
+ element.__evalResult = WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(enumValue);
+ return element;
+ }
+}
+
+/**
+ * @extends {WebInspector.Object}
+ * @constructor
+ * @param {!CanvasAgent.TraceLogId} traceLogId
+ */
+WebInspector.CanvasTraceLogPlayerProxy = function(traceLogId)
+{
+ this._traceLogId = traceLogId;
+ /** @type {!Object.<string, !CanvasAgent.ResourceState>} */
+ this._currentResourceStates = {};
+ /** @type {?CanvasAgent.ResourceId} */
+ this._defaultResourceId = null;
+}
+
+/** @enum {string} */
+WebInspector.CanvasTraceLogPlayerProxy.Events = {
+ CanvasTraceLogReceived: "CanvasTraceLogReceived",
+ CanvasReplayStateChanged: "CanvasReplayStateChanged",
+ CanvasResourceStateReceived: "CanvasResourceStateReceived",
+}
+
+WebInspector.CanvasTraceLogPlayerProxy.prototype = {
+ /**
+ * @param {number|undefined} startOffset
+ * @param {number|undefined} maxLength
+ * @param {function(?CanvasAgent.TraceLog):void} userCallback
+ */
+ getTraceLog: function(startOffset, maxLength, userCallback)
+ {
+ /**
+ * @param {?Protocol.Error} error
+ * @param {!CanvasAgent.TraceLog} traceLog
+ * @this {WebInspector.CanvasTraceLogPlayerProxy}
+ */
+ function callback(error, traceLog)
+ {
+ if (error || !traceLog) {
+ userCallback(null);
+ return;
+ }
+ userCallback(traceLog);
+ this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasTraceLogReceived, traceLog);
+ }
+ CanvasAgent.getTraceLog(this._traceLogId, startOffset, maxLength, callback.bind(this));
+ },
+
+ dispose: function()
+ {
+ this._currentResourceStates = {};
+ CanvasAgent.dropTraceLog(this._traceLogId);
+ this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged);
+ },
+
+ /**
+ * @param {?CanvasAgent.ResourceId} resourceId
+ * @param {function(?CanvasAgent.ResourceState):void} userCallback
+ */
+ getResourceState: function(resourceId, userCallback)
+ {
+ resourceId = resourceId || this._defaultResourceId;
+ if (!resourceId) {
+ userCallback(null); // Has not been replayed yet.
+ return;
+ }
+ var effectiveResourceId = /** @type {!CanvasAgent.ResourceId} */ (resourceId);
+ if (this._currentResourceStates[effectiveResourceId]) {
+ userCallback(this._currentResourceStates[effectiveResourceId]);
+ return;
+ }
+
+ /**
+ * @param {?Protocol.Error} error
+ * @param {!CanvasAgent.ResourceState} resourceState
+ * @this {WebInspector.CanvasTraceLogPlayerProxy}
+ */
+ function callback(error, resourceState)
+ {
+ if (error || !resourceState) {
+ userCallback(null);
+ return;
+ }
+ this._currentResourceStates[effectiveResourceId] = resourceState;
+ userCallback(resourceState);
+ this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasResourceStateReceived, resourceState);
+ }
+ CanvasAgent.getResourceState(this._traceLogId, effectiveResourceId, callback.bind(this));
+ },
+
+ /**
+ * @param {number} index
+ * @param {function(?CanvasAgent.ResourceState, number):void} userCallback
+ */
+ replayTraceLog: function(index, userCallback)
+ {
+ /**
+ * @param {?Protocol.Error} error
+ * @param {!CanvasAgent.ResourceState} resourceState
+ * @param {number} replayTime
+ * @this {WebInspector.CanvasTraceLogPlayerProxy}
+ */
+ function callback(error, resourceState, replayTime)
+ {
+ this._currentResourceStates = {};
+ if (error) {
+ userCallback(null, replayTime);
+ } else {
+ this._defaultResourceId = resourceState.id;
+ this._currentResourceStates[resourceState.id] = resourceState;
+ userCallback(resourceState, replayTime);
+ }
+ this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged);
+ if (!error)
+ this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasResourceStateReceived, resourceState);
+ }
+ CanvasAgent.replayTraceLog(this._traceLogId, index, callback.bind(this));
+ },
+
+ clearResourceStates: function()
+ {
+ this._currentResourceStates = {};
+ this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged);
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasReplayStateView.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasReplayStateView.js
new file mode 100644
index 00000000000..dedb14c2b4b
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/CanvasReplayStateView.js
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2013 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.VBox}
+ * @param {!WebInspector.CanvasTraceLogPlayerProxy} traceLogPlayer
+ */
+WebInspector.CanvasReplayStateView = function(traceLogPlayer)
+{
+ WebInspector.VBox.call(this);
+ this.registerRequiredCSS("canvasProfiler.css");
+ this.element.classList.add("canvas-replay-state-view");
+ this._traceLogPlayer = traceLogPlayer;
+
+ var controlsContainer = this.element.createChild("div", "status-bar");
+ this._prevButton = this._createControlButton(controlsContainer, "canvas-replay-state-prev", WebInspector.UIString("Previous resource."), this._onResourceNavigationClick.bind(this, false));
+ this._nextButton = this._createControlButton(controlsContainer, "canvas-replay-state-next", WebInspector.UIString("Next resource."), this._onResourceNavigationClick.bind(this, true));
+ this._createControlButton(controlsContainer, "canvas-replay-state-refresh", WebInspector.UIString("Refresh."), this._onStateRefreshClick.bind(this));
+
+ this._resourceSelector = new WebInspector.StatusBarComboBox(this._onReplayResourceChanged.bind(this));
+ this._currentOption = this._resourceSelector.createOption(WebInspector.UIString("<auto>"), WebInspector.UIString("Show state of the last replayed resource."), "");
+ controlsContainer.appendChild(this._resourceSelector.element);
+
+ /** @type {!Object.<string, string>} */
+ this._resourceIdToDescription = {};
+
+ /** @type {!Object.<string, !Object.<string, boolean>>} */
+ this._gridNodesExpandedState = {};
+ /** @type {!Object.<string, !{scrollTop: number, scrollLeft: number}>} */
+ this._gridScrollPositions = {};
+
+ /** @type {?CanvasAgent.ResourceId} */
+ this._currentResourceId = null;
+ /** @type {!Array.<!Element>} */
+ this._prevOptionsStack = [];
+ /** @type {!Array.<!Element>} */
+ this._nextOptionsStack = [];
+
+ /** @type {!Array.<!WebInspector.DataGridNode>} */
+ this._highlightedGridNodes = [];
+
+ var columns = [
+ {title: WebInspector.UIString("Name"), sortable: false, width: "50%", disclosure: true},
+ {title: WebInspector.UIString("Value"), sortable: false, width: "50%"}
+ ];
+
+ this._stateGrid = new WebInspector.DataGrid(columns);
+ this._stateGrid.element.classList.add("fill");
+ this._stateGrid.show(this.element);
+
+ this._traceLogPlayer.addEventListener(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged, this._onReplayResourceChanged, this);
+ this._traceLogPlayer.addEventListener(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasTraceLogReceived, this._onCanvasTraceLogReceived, this);
+ this._traceLogPlayer.addEventListener(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasResourceStateReceived, this._onCanvasResourceStateReceived, this);
+
+ this._updateButtonsEnabledState();
+}
+
+WebInspector.CanvasReplayStateView.prototype = {
+ /**
+ * @param {string} resourceId
+ */
+ selectResource: function(resourceId)
+ {
+ if (resourceId === this._resourceSelector.selectedOption().value)
+ return;
+ var option = this._resourceSelector.selectElement().firstChild;
+ for (var index = 0; option; ++index, option = option.nextSibling) {
+ if (resourceId === option.value) {
+ this._resourceSelector.setSelectedIndex(index);
+ this._onReplayResourceChanged();
+ break;
+ }
+ }
+ },
+
+ /**
+ * @param {!Element} parent
+ * @param {string} className
+ * @param {string} title
+ * @param {function(this:WebInspector.CanvasProfileView)} clickCallback
+ * @return {!WebInspector.StatusBarButton}
+ */
+ _createControlButton: function(parent, className, title, clickCallback)
+ {
+ var button = new WebInspector.StatusBarButton(title, className + " canvas-replay-button");
+ parent.appendChild(button.element);
+
+ button.makeLongClickEnabled();
+ button.addEventListener("click", clickCallback, this);
+ button.addEventListener("longClickDown", clickCallback, this);
+ button.addEventListener("longClickPress", clickCallback, this);
+ return button;
+ },
+
+ /**
+ * @param {boolean} forward
+ */
+ _onResourceNavigationClick: function(forward)
+ {
+ var newOption = forward ? this._nextOptionsStack.pop() : this._prevOptionsStack.pop();
+ if (!newOption)
+ return;
+ (forward ? this._prevOptionsStack : this._nextOptionsStack).push(this._currentOption);
+ this._isNavigationButton = true;
+ this.selectResource(newOption.value);
+ delete this._isNavigationButton;
+ this._updateButtonsEnabledState();
+ },
+
+ _onStateRefreshClick: function()
+ {
+ this._traceLogPlayer.clearResourceStates();
+ },
+
+ _updateButtonsEnabledState: function()
+ {
+ this._prevButton.setEnabled(this._prevOptionsStack.length > 0);
+ this._nextButton.setEnabled(this._nextOptionsStack.length > 0);
+ },
+
+ _updateCurrentOption: function()
+ {
+ const maxStackSize = 256;
+ var selectedOption = this._resourceSelector.selectedOption();
+ if (this._currentOption === selectedOption)
+ return;
+ if (!this._isNavigationButton) {
+ this._prevOptionsStack.push(this._currentOption);
+ this._nextOptionsStack = [];
+ if (this._prevOptionsStack.length > maxStackSize)
+ this._prevOptionsStack.shift();
+ this._updateButtonsEnabledState();
+ }
+ this._currentOption = selectedOption;
+ },
+
+ /**
+ * @param {!CanvasAgent.TraceLog} traceLog
+ */
+ _collectResourcesFromTraceLog: function(traceLog)
+ {
+ /** @type {!Array.<!CanvasAgent.CallArgument>} */
+ var collectedResources = [];
+ var calls = traceLog.calls;
+ for (var i = 0, n = calls.length; i < n; ++i) {
+ var call = calls[i];
+ var args = call.arguments || [];
+ for (var j = 0; j < args.length; ++j)
+ this._collectResourceFromCallArgument(args[j], collectedResources);
+ this._collectResourceFromCallArgument(call.result, collectedResources);
+ this._collectResourceFromCallArgument(call.value, collectedResources);
+ }
+ var contexts = traceLog.contexts;
+ for (var i = 0, n = contexts.length; i < n; ++i)
+ this._collectResourceFromCallArgument(contexts[i], collectedResources);
+ this._addCollectedResourcesToSelector(collectedResources);
+ },
+
+ /**
+ * @param {!CanvasAgent.ResourceState} resourceState
+ */
+ _collectResourcesFromResourceState: function(resourceState)
+ {
+ /** @type {!Array.<!CanvasAgent.CallArgument>} */
+ var collectedResources = [];
+ this._collectResourceFromResourceStateDescriptors(resourceState.descriptors, collectedResources);
+ this._addCollectedResourcesToSelector(collectedResources);
+ },
+
+ /**
+ * @param {!Array.<!CanvasAgent.ResourceStateDescriptor>|undefined} descriptors
+ * @param {!Array.<!CanvasAgent.CallArgument>} output
+ */
+ _collectResourceFromResourceStateDescriptors: function(descriptors, output)
+ {
+ if (!descriptors)
+ return;
+ for (var i = 0, n = descriptors.length; i < n; ++i) {
+ var descriptor = descriptors[i];
+ this._collectResourceFromCallArgument(descriptor.value, output);
+ this._collectResourceFromResourceStateDescriptors(descriptor.values, output);
+ }
+ },
+
+ /**
+ * @param {!CanvasAgent.CallArgument|undefined} argument
+ * @param {!Array.<!CanvasAgent.CallArgument>} output
+ */
+ _collectResourceFromCallArgument: function(argument, output)
+ {
+ if (!argument)
+ return;
+ var resourceId = argument.resourceId;
+ if (!resourceId || this._resourceIdToDescription[resourceId])
+ return;
+ this._resourceIdToDescription[resourceId] = argument.description;
+ output.push(argument);
+ },
+
+ /**
+ * @param {!Array.<!CanvasAgent.CallArgument>} collectedResources
+ */
+ _addCollectedResourcesToSelector: function(collectedResources)
+ {
+ if (!collectedResources.length)
+ return;
+ /**
+ * @param {!CanvasAgent.CallArgument} arg1
+ * @param {!CanvasAgent.CallArgument} arg2
+ * @return {number}
+ */
+ function comparator(arg1, arg2)
+ {
+ var a = arg1.description;
+ var b = arg2.description;
+ return String.naturalOrderComparator(a, b);
+ }
+ collectedResources.sort(comparator);
+
+ var selectElement = this._resourceSelector.selectElement();
+ var currentOption = selectElement.firstChild;
+ currentOption = currentOption.nextSibling; // Skip the "<auto>" option.
+ for (var i = 0, n = collectedResources.length; i < n; ++i) {
+ var argument = collectedResources[i];
+ while (currentOption && String.naturalOrderComparator(currentOption.text, argument.description) < 0)
+ currentOption = currentOption.nextSibling;
+ var option = this._resourceSelector.createOption(argument.description, WebInspector.UIString("Show state of this resource."), argument.resourceId);
+ if (currentOption)
+ selectElement.insertBefore(option, currentOption);
+ }
+ },
+
+ _onReplayResourceChanged: function()
+ {
+ this._updateCurrentOption();
+ var selectedResourceId = this._resourceSelector.selectedOption().value;
+
+ /**
+ * @param {?CanvasAgent.ResourceState} resourceState
+ * @this {WebInspector.CanvasReplayStateView}
+ */
+ function didReceiveResourceState(resourceState)
+ {
+ if (selectedResourceId !== this._resourceSelector.selectedOption().value)
+ return;
+ this._showResourceState(resourceState);
+ }
+ this._traceLogPlayer.getResourceState(selectedResourceId, didReceiveResourceState.bind(this));
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onCanvasTraceLogReceived: function(event)
+ {
+ var traceLog = /** @type {!CanvasAgent.TraceLog} */ (event.data);
+ console.assert(traceLog);
+ this._collectResourcesFromTraceLog(traceLog);
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onCanvasResourceStateReceived: function(event)
+ {
+ var resourceState = /** @type {!CanvasAgent.ResourceState} */ (event.data);
+ console.assert(resourceState);
+ this._collectResourcesFromResourceState(resourceState);
+ },
+
+ /**
+ * @param {?CanvasAgent.ResourceState} resourceState
+ */
+ _showResourceState: function(resourceState)
+ {
+ this._saveExpandedState();
+ this._saveScrollState();
+
+ var rootNode = this._stateGrid.rootNode();
+ if (!resourceState) {
+ this._currentResourceId = null;
+ this._updateDataGridHighlights([]);
+ rootNode.removeChildren();
+ return;
+ }
+
+ var nodesToHighlight = [];
+ var nameToOldGridNodes = {};
+
+ /**
+ * @param {!Object} map
+ * @param {!WebInspector.DataGridNode=} node
+ */
+ function populateNameToNodesMap(map, node)
+ {
+ if (!node)
+ return;
+ for (var i = 0, child; child = node.children[i]; ++i) {
+ var item = {
+ node: child,
+ children: {}
+ };
+ map[child.name] = item;
+ populateNameToNodesMap(item.children, child);
+ }
+ }
+ populateNameToNodesMap(nameToOldGridNodes, rootNode);
+ rootNode.removeChildren();
+
+ /**
+ * @param {!CanvasAgent.ResourceStateDescriptor} d1
+ * @param {!CanvasAgent.ResourceStateDescriptor} d2
+ * @return {number}
+ */
+ function comparator(d1, d2)
+ {
+ var hasChildren1 = !!d1.values;
+ var hasChildren2 = !!d2.values;
+ if (hasChildren1 !== hasChildren2)
+ return hasChildren1 ? 1 : -1;
+ return String.naturalOrderComparator(d1.name, d2.name);
+ }
+ /**
+ * @param {!Array.<!CanvasAgent.ResourceStateDescriptor>|undefined} descriptors
+ * @param {!WebInspector.DataGridNode} parent
+ * @param {!Object=} nameToOldChildren
+ * @this {WebInspector.CanvasReplayStateView}
+ */
+ function appendResourceStateDescriptors(descriptors, parent, nameToOldChildren)
+ {
+ descriptors = descriptors || [];
+ descriptors.sort(comparator);
+ var oldChildren = nameToOldChildren || {};
+ for (var i = 0, n = descriptors.length; i < n; ++i) {
+ var descriptor = descriptors[i];
+ var childNode = this._createDataGridNode(descriptor);
+ parent.appendChild(childNode);
+ var oldChildrenItem = oldChildren[childNode.name] || {};
+ var oldChildNode = oldChildrenItem.node;
+ if (!oldChildNode || oldChildNode.element.textContent !== childNode.element.textContent)
+ nodesToHighlight.push(childNode);
+ appendResourceStateDescriptors.call(this, descriptor.values, childNode, oldChildrenItem.children);
+ }
+ }
+ appendResourceStateDescriptors.call(this, resourceState.descriptors, rootNode, nameToOldGridNodes);
+
+ var shouldHighlightChanges = (this._resourceKindId(this._currentResourceId) === this._resourceKindId(resourceState.id));
+ this._currentResourceId = resourceState.id;
+ this._restoreExpandedState();
+ this._updateDataGridHighlights(shouldHighlightChanges ? nodesToHighlight : []);
+ this._restoreScrollState();
+ },
+
+ /**
+ * @param {!Array.<!WebInspector.DataGridNode>} nodes
+ */
+ _updateDataGridHighlights: function(nodes)
+ {
+ for (var i = 0, n = this._highlightedGridNodes.length; i < n; ++i)
+ this._highlightedGridNodes[i].element.classList.remove("canvas-grid-node-highlighted");
+
+ this._highlightedGridNodes = nodes;
+
+ for (var i = 0, n = this._highlightedGridNodes.length; i < n; ++i) {
+ var node = this._highlightedGridNodes[i];
+ WebInspector.runCSSAnimationOnce(node.element, "canvas-grid-node-highlighted");
+ node.reveal();
+ }
+ },
+
+ /**
+ * @param {?CanvasAgent.ResourceId} resourceId
+ * @return {string}
+ */
+ _resourceKindId: function(resourceId)
+ {
+ var description = (resourceId && this._resourceIdToDescription[resourceId]) || "";
+ return description.replace(/\d+/g, "");
+ },
+
+ /**
+ * @param {function(!WebInspector.DataGridNode, string):void} callback
+ */
+ _forEachGridNode: function(callback)
+ {
+ /**
+ * @param {!WebInspector.DataGridNode} node
+ * @param {string} key
+ */
+ function processRecursively(node, key)
+ {
+ for (var i = 0, child; child = node.children[i]; ++i) {
+ var childKey = key + "#" + child.name;
+ callback(child, childKey);
+ processRecursively(child, childKey);
+ }
+ }
+ processRecursively(this._stateGrid.rootNode(), "");
+ },
+
+ _saveExpandedState: function()
+ {
+ if (!this._currentResourceId)
+ return;
+ var expandedState = {};
+ var key = this._resourceKindId(this._currentResourceId);
+ this._gridNodesExpandedState[key] = expandedState;
+ /**
+ * @param {!WebInspector.DataGridNode} node
+ * @param {string} key
+ */
+ function callback(node, key)
+ {
+ if (node.expanded)
+ expandedState[key] = true;
+ }
+ this._forEachGridNode(callback);
+ },
+
+ _restoreExpandedState: function()
+ {
+ if (!this._currentResourceId)
+ return;
+ var key = this._resourceKindId(this._currentResourceId);
+ var expandedState = this._gridNodesExpandedState[key];
+ if (!expandedState)
+ return;
+ /**
+ * @param {!WebInspector.DataGridNode} node
+ * @param {string} key
+ */
+ function callback(node, key)
+ {
+ if (expandedState[key])
+ node.expand();
+ }
+ this._forEachGridNode(callback);
+ },
+
+ _saveScrollState: function()
+ {
+ if (!this._currentResourceId)
+ return;
+ var key = this._resourceKindId(this._currentResourceId);
+ this._gridScrollPositions[key] = {
+ scrollTop: this._stateGrid.scrollContainer.scrollTop,
+ scrollLeft: this._stateGrid.scrollContainer.scrollLeft
+ };
+ },
+
+ _restoreScrollState: function()
+ {
+ if (!this._currentResourceId)
+ return;
+ var key = this._resourceKindId(this._currentResourceId);
+ var scrollState = this._gridScrollPositions[key];
+ if (!scrollState)
+ return;
+ this._stateGrid.scrollContainer.scrollTop = scrollState.scrollTop;
+ this._stateGrid.scrollContainer.scrollLeft = scrollState.scrollLeft;
+ },
+
+ /**
+ * @param {!CanvasAgent.ResourceStateDescriptor} descriptor
+ * @return {!WebInspector.DataGridNode}
+ */
+ _createDataGridNode: function(descriptor)
+ {
+ var name = descriptor.name;
+ var callArgument = descriptor.value;
+
+ /** @type {!Element|string} */
+ var valueElement = callArgument ? WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(callArgument) : "";
+
+ /** @type {!Element|string} */
+ var nameElement = name;
+ if (typeof descriptor.enumValueForName !== "undefined")
+ nameElement = WebInspector.CanvasProfileDataGridHelper.createEnumValueElement(name, +descriptor.enumValueForName);
+
+ if (descriptor.isArray && descriptor.values) {
+ if (typeof nameElement === "string")
+ nameElement += "[" + descriptor.values.length + "]";
+ else {
+ var element = document.createElement("span");
+ element.appendChild(nameElement);
+ element.createTextChild("[" + descriptor.values.length + "]");
+ nameElement = element;
+ }
+ }
+
+ var data = {};
+ data[0] = nameElement;
+ data[1] = valueElement;
+ var node = new WebInspector.DataGridNode(data);
+ node.selectable = false;
+ node.name = name;
+ return node;
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotCommon.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotCommon.js
new file mode 100644
index 00000000000..1effb8aa78b
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotCommon.js
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+WebInspector.HeapSnapshotProgressEvent = {
+ Update: "ProgressUpdate"
+};
+
+WebInspector.HeapSnapshotCommon = {
+}
+
+WebInspector.HeapSnapshotCommon.baseSystemDistance = 100000000;
+
+/**
+ * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} nodesWithSingleCaller
+ * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} branchingCallers
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.AllocationNodeCallers = function(nodesWithSingleCaller, branchingCallers)
+{
+ /** @type {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} */
+ this.nodesWithSingleCaller = nodesWithSingleCaller;
+ /** @type {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} */
+ this.branchingCallers = branchingCallers;
+}
+
+/**
+ * @param {number} nodeId
+ * @param {string} functionName
+ * @param {string} scriptName
+ * @param {number} scriptId
+ * @param {number} line
+ * @param {number} column
+ * @param {number} count
+ * @param {number} size
+ * @param {number} liveCount
+ * @param {number} liveSize
+ * @param {boolean} hasChildren
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.SerializedAllocationNode = function(nodeId, functionName, scriptName, scriptId, line, column, count, size, liveCount, liveSize, hasChildren)
+{
+ /** @type {number} */
+ this.id = nodeId;
+ /** @type {string} */
+ this.name = functionName;
+ /** @type {string} */
+ this.scriptName = scriptName;
+ /** @type {number} */
+ this.scriptId = scriptId;
+ /** @type {number} */
+ this.line = line;
+ /** @type {number} */
+ this.column = column;
+ /** @type {number} */
+ this.count = count;
+ /** @type {number} */
+ this.size = size;
+ /** @type {number} */
+ this.liveCount = liveCount;
+ /** @type {number} */
+ this.liveSize = liveSize;
+ /** @type {boolean} */
+ this.hasChildren = hasChildren;
+}
+
+/**
+ * @param {string} functionName
+ * @param {string} scriptName
+ * @param {number} scriptId
+ * @param {number} line
+ * @param {number} column
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.AllocationStackFrame = function(functionName, scriptName, scriptId, line, column)
+{
+ /** @type {string} */
+ this.functionName = functionName;
+ /** @type {string} */
+ this.scriptName = scriptName;
+ /** @type {number} */
+ this.scriptId = scriptId;
+ /** @type {number} */
+ this.line = line;
+ /** @type {number} */
+ this.column = column;
+}
+
+/**
+ * @constructor
+ * @param {number} id
+ * @param {string} name
+ * @param {number} distance
+ * @param {number} nodeIndex
+ * @param {number} retainedSize
+ * @param {number} selfSize
+ * @param {string} type
+ */
+WebInspector.HeapSnapshotCommon.Node = function(id, name, distance, nodeIndex, retainedSize, selfSize, type)
+{
+ this.id = id;
+ this.name = name;
+ this.distance = distance;
+ this.nodeIndex = nodeIndex;
+ this.retainedSize = retainedSize;
+ this.selfSize = selfSize;
+ this.type = type;
+
+ this.canBeQueried = false;
+ this.detachedDOMTreeNode = false;
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ * @param {string} type
+ * @param {number} edgeIndex
+ */
+WebInspector.HeapSnapshotCommon.Edge = function(name, node, type, edgeIndex)
+{
+ this.name = name;
+ this.node = node;
+ this.type = type;
+ this.edgeIndex = edgeIndex;
+};
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.Aggregate = function()
+{
+ /** @type {number} */
+ this.count;
+ /** @type {number} */
+ this.distance;
+ /** @type {number} */
+ this.self;
+ /** @type {number} */
+ this.maxRet;
+ /** @type {number} */
+ this.type;
+ /** @type {string} */
+ this.name;
+ /** @type {!Array.<number>} */
+ this.idxs;
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.AggregateForDiff = function() {
+ /** @type {!Array.<number>} */
+ this.indexes = [];
+ /** @type {!Array.<string>} */
+ this.ids = [];
+ /** @type {!Array.<number>} */
+ this.selfSizes = [];
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.Diff = function()
+{
+ /** @type {number} */
+ this.addedCount = 0;
+ /** @type {number} */
+ this.removedCount = 0;
+ /** @type {number} */
+ this.addedSize = 0;
+ /** @type {number} */
+ this.removedSize = 0;
+ /** @type {!Array.<number>} */
+ this.deletedIndexes = [];
+ /** @type {!Array.<number>} */
+ this.addedIndexes = [];
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.DiffForClass = function()
+{
+ /** @type {number} */
+ this.addedCount;
+ /** @type {number} */
+ this.removedCount;
+ /** @type {number} */
+ this.addedSize;
+ /** @type {number} */
+ this.removedSize;
+ /** @type {!Array.<number>} */
+ this.deletedIndexes;
+ /** @type {!Array.<number>} */
+ this.addedIndexes;
+
+ /** @type {number} */
+ this.countDelta;
+ /** @type {number} */
+ this.sizeDelta;
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.ComparatorConfig = function()
+{
+ /** @type {string} */
+ this.fieldName1;
+ /** @type {boolean} */
+ this.ascending1;
+ /** @type {string} */
+ this.fieldName2;
+ /** @type {boolean} */
+ this.ascending2;
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.WorkerCommand = function()
+{
+ /** @type {number} */
+ this.callId;
+ /** @type {string} */
+ this.disposition;
+ /** @type {number} */
+ this.objectId;
+ /** @type {number} */
+ this.newObjectId;
+ /** @type {string} */
+ this.methodName;
+ /** @type {!Array.<*>} */
+ this.methodArguments;
+ /** @type {string} */
+ this.source;
+}
+
+/**
+ * @constructor
+ * @param {number} startPosition
+ * @param {number} endPosition
+ * @param {number} totalLength
+ * @param {!Array.<*>} items
+ */
+WebInspector.HeapSnapshotCommon.ItemsRange = function(startPosition, endPosition, totalLength, items)
+{
+ /** @type {number} */
+ this.startPosition = startPosition;
+ /** @type {number} */
+ this.endPosition = endPosition;
+ /** @type {number} */
+ this.totalLength = totalLength;
+ /** @type {!Array.<*>} */
+ this.items = items;
+}
+
+/**
+ * @param {number} nodeCount
+ * @param {number} rootNodeIndex
+ * @param {number} totalSize
+ * @param {number} maxJSObjectId
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.StaticData = function(nodeCount, rootNodeIndex, totalSize, maxJSObjectId)
+{
+ /** @type {number} */
+ this.nodeCount = nodeCount;
+ /** @type {number} */
+ this.rootNodeIndex = rootNodeIndex;
+ /** @type {number} */
+ this.totalSize = totalSize;
+ /** @type {number} */
+ this.maxJSObjectId = maxJSObjectId;
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.Statistics = function()
+{
+ /** @type {number} */
+ this.total;
+ /** @type {number} */
+ this.v8heap;
+ /** @type {number} */
+ this.native;
+ /** @type {number} */
+ this.code;
+ /** @type {number} */
+ this.jsArrays;
+ /** @type {number} */
+ this.strings;
+}
+
+
+/**
+ * @param {number=} minNodeId
+ * @param {number=} maxNodeId
+ * @constructor
+ */
+WebInspector.HeapSnapshotCommon.NodeFilter = function(minNodeId, maxNodeId)
+{
+ /** @type {number|undefined} */
+ this.minNodeId = minNodeId;
+ /** @type {number|undefined} */
+ this.maxNodeId = maxNodeId;
+ /** @type {number|undefined} */
+ this.allocationNodeId;
+}
+
+WebInspector.HeapSnapshotCommon.NodeFilter.prototype =
+{
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} o
+ * @return {boolean}
+ */
+ equals: function(o)
+ {
+ return this.minNodeId === o.minNodeId && this.maxNodeId === o.maxNodeId && this.allocationNodeId === o.allocationNodeId;
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js
new file mode 100644
index 00000000000..6b7ee735104
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotDataGrids.js
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (C) 2012 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.DataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns
+ */
+WebInspector.HeapSnapshotSortableDataGrid = function(dataDisplayDelegate, columns)
+{
+ WebInspector.DataGrid.call(this, columns);
+ this._dataDisplayDelegate = dataDisplayDelegate;
+
+ /**
+ * @type {number}
+ */
+ this._recursiveSortingDepth = 0;
+ /**
+ * @type {?WebInspector.HeapSnapshotGridNode}
+ */
+ this._highlightedNode = null;
+ /**
+ * @type {boolean}
+ */
+ this._populatedAndSorted = false;
+ /**
+ * @type {?WebInspector.StatusBarInput}
+ */
+ this._nameFilter = null;
+ this.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this);
+ this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this);
+}
+
+WebInspector.HeapSnapshotSortableDataGrid.Events = {
+ ContentShown: "ContentShown",
+ SortingComplete: "SortingComplete"
+}
+
+WebInspector.HeapSnapshotSortableDataGrid.prototype = {
+ /**
+ * @param {!WebInspector.StatusBarInput} nameFilter
+ */
+ setNameFilter: function(nameFilter)
+ {
+ this._nameFilter = nameFilter;
+ },
+
+ /**
+ * @return {number}
+ */
+ defaultPopulateCount: function()
+ {
+ return 100;
+ },
+
+ _disposeAllNodes: function()
+ {
+ var children = this.topLevelNodes();
+ for (var i = 0, l = children.length; i < l; ++i)
+ children[i].dispose();
+ },
+
+ /**
+ * @override
+ */
+ wasShown: function()
+ {
+ if (this._nameFilter) {
+ this._nameFilter.addEventListener(WebInspector.StatusBarInput.Event.TextChanged, this._onNameFilterChanged, this);
+ this.updateVisibleNodes(true);
+ }
+ if (this._populatedAndSorted)
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
+ },
+
+ _sortingComplete: function()
+ {
+ this.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this);
+ this._populatedAndSorted = true;
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
+ },
+
+ /**
+ * @override
+ */
+ willHide: function()
+ {
+ if (this._nameFilter)
+ this._nameFilter.removeEventListener(WebInspector.StatusBarInput.Event.TextChanged, this._onNameFilterChanged, this);
+ this._clearCurrentHighlight();
+ },
+
+ /**
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {?Event} event
+ */
+ populateContextMenu: function(contextMenu, event)
+ {
+ var td = event.target.enclosingNodeOrSelfWithNodeName("td");
+ if (!td)
+ return;
+ var node = td.heapSnapshotNode;
+
+ /**
+ * @this {WebInspector.HeapSnapshotSortableDataGrid}
+ */
+ function revealInDominatorsView()
+ {
+ this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Dominators");
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotSortableDataGrid}
+ */
+ function revealInSummaryView()
+ {
+ this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Summary");
+ }
+
+ if (node instanceof WebInspector.HeapSnapshotRetainingObjectNode) {
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
+ } else if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) {
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
+ } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) {
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
+ }
+ },
+
+ resetSortingCache: function()
+ {
+ delete this._lastSortColumnIdentifier;
+ delete this._lastSortAscending;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
+ */
+ topLevelNodes: function()
+ {
+ return this.rootNode().children;
+ },
+
+ /**
+ * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId
+ * @param {function(boolean)} callback
+ */
+ highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId, callback)
+ {
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotGridNode} node
+ */
+ highlightNode: function(node)
+ {
+ var prevNode = this._highlightedNode;
+ this._clearCurrentHighlight();
+ this._highlightedNode = node;
+ WebInspector.runCSSAnimationOnce(this._highlightedNode.element, "highlighted-row");
+ },
+
+ nodeWasDetached: function(node)
+ {
+ if (this._highlightedNode === node)
+ this._clearCurrentHighlight();
+ },
+
+ _clearCurrentHighlight: function()
+ {
+ if (!this._highlightedNode)
+ return
+ this._highlightedNode.element.classList.remove("highlighted-row");
+ this._highlightedNode = null;
+ },
+
+ resetNameFilter: function()
+ {
+ this._nameFilter.setValue("");
+ this._onNameFilterChanged();
+ },
+
+ _onNameFilterChanged: function()
+ {
+ this.updateVisibleNodes(true);
+ },
+
+ sortingChanged: function()
+ {
+ var sortAscending = this.isSortOrderAscending();
+ var sortColumnIdentifier = this.sortColumnIdentifier();
+ if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
+ return;
+ this._lastSortColumnIdentifier = sortColumnIdentifier;
+ this._lastSortAscending = sortAscending;
+ var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
+
+ function SortByTwoFields(nodeA, nodeB)
+ {
+ var field1 = nodeA[sortFields[0]];
+ var field2 = nodeB[sortFields[0]];
+ var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
+ if (!sortFields[1])
+ result = -result;
+ if (result !== 0)
+ return result;
+ field1 = nodeA[sortFields[2]];
+ field2 = nodeB[sortFields[2]];
+ result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
+ if (!sortFields[3])
+ result = -result;
+ return result;
+ }
+ this._performSorting(SortByTwoFields);
+ },
+
+ _performSorting: function(sortFunction)
+ {
+ this.recursiveSortingEnter();
+ var children = this.allChildren(this.rootNode());
+ this.rootNode().removeChildren();
+ children.sort(sortFunction);
+ for (var i = 0, l = children.length; i < l; ++i) {
+ var child = children[i];
+ this.appendChildAfterSorting(child);
+ if (child.expanded)
+ child.sort();
+ }
+ this.recursiveSortingLeave();
+ },
+
+ appendChildAfterSorting: function(child)
+ {
+ var revealed = child.revealed;
+ this.rootNode().appendChild(child);
+ child.revealed = revealed;
+ },
+
+ recursiveSortingEnter: function()
+ {
+ ++this._recursiveSortingDepth;
+ },
+
+ recursiveSortingLeave: function()
+ {
+ if (!this._recursiveSortingDepth)
+ return;
+ if (--this._recursiveSortingDepth)
+ return;
+ this.updateVisibleNodes(true);
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete);
+ },
+
+ /**
+ * @param {boolean} force
+ */
+ updateVisibleNodes: function(force)
+ {
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} parent
+ * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
+ */
+ allChildren: function(parent)
+ {
+ return parent.children;
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} parent
+ * @param {!WebInspector.DataGridNode} node
+ * @param {number} index
+ */
+ insertChild: function(parent, node, index)
+ {
+ parent.insertChild(node, index);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotGridNode} parent
+ * @param {number} index
+ */
+ removeChildByIndex: function(parent, index)
+ {
+ parent.removeChild(parent.children[index]);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotGridNode} parent
+ */
+ removeAllChildren: function(parent)
+ {
+ parent.removeChildren();
+ },
+
+ __proto__: WebInspector.DataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotSortableDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns
+ */
+WebInspector.HeapSnapshotViewportDataGrid = function(dataDisplayDelegate, columns)
+{
+ WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
+ this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true);
+ /**
+ * @type {?WebInspector.HeapSnapshotGridNode}
+ */
+ this._nodeToHighlightAfterScroll = null;
+ this._topPadding = new WebInspector.HeapSnapshotPaddingNode();
+ this._topPaddingHeight = 0;
+ this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild);
+ this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode();
+ this._bottomPaddingHeight = 0;
+ this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild);
+}
+
+WebInspector.HeapSnapshotViewportDataGrid.prototype = {
+ /**
+ * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
+ */
+ topLevelNodes: function()
+ {
+ return this.allChildren(this.rootNode());
+ },
+
+ appendChildAfterSorting: function(child)
+ {
+ // Do nothing here, it will be added in updateVisibleNodes.
+ },
+
+ /**
+ * @override
+ * @param {boolean} force
+ * @param {!Array.<!WebInspector.HeapSnapshotGridNode>=} pathToReveal
+ */
+ updateVisibleNodes: function(force, pathToReveal)
+ {
+ // Guard zone is used to ensure there are always some extra items
+ // above and below the viewport to support keyboard navigation.
+ var guardZoneHeight = 40;
+ var scrollHeight = this.scrollContainer.scrollHeight;
+ var scrollTop = this.scrollContainer.scrollTop;
+ var scrollBottom = scrollHeight - scrollTop - this.scrollContainer.offsetHeight;
+ scrollTop = Math.max(0, scrollTop - guardZoneHeight);
+ scrollBottom = Math.max(0, scrollBottom - guardZoneHeight);
+ var viewPortHeight = scrollHeight - scrollTop - scrollBottom;
+ if (!pathToReveal) {
+ // Do nothing if populated nodes still fit the viewport.
+ if (!force && scrollTop >= this._topPaddingHeight && scrollBottom >= this._bottomPaddingHeight)
+ return;
+ var hysteresisHeight = 500;
+ scrollTop -= hysteresisHeight;
+ viewPortHeight += 2 * hysteresisHeight;
+ }
+ var selectedNode = this.selectedNode;
+ this.rootNode().removeChildren();
+
+ this._topPaddingHeight = 0;
+ this._bottomPaddingHeight = 0;
+
+ this._addVisibleNodes(this.rootNode(), scrollTop, scrollTop + viewPortHeight, pathToReveal || null);
+
+ this._topPadding.setHeight(this._topPaddingHeight);
+ this._bottomPadding.setHeight(this._bottomPaddingHeight);
+
+ if (selectedNode) {
+ // Keep selection even if the node is not in the current viewport.
+ if (selectedNode.parent)
+ selectedNode.select(true);
+ else
+ this.selectedNode = selectedNode;
+ }
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} parentNode
+ * @param {number} topBound
+ * @param {number} bottomBound
+ * @param {?Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal
+ * @return {number}
+ */
+ _addVisibleNodes: function(parentNode, topBound, bottomBound, pathToReveal)
+ {
+ if (!parentNode.expanded)
+ return 0;
+
+ var nodeToReveal = pathToReveal ? pathToReveal[0] : null;
+ var restPathToReveal = pathToReveal && pathToReveal.length > 1 ? pathToReveal.slice(1) : null;
+ var children = this.allChildren(parentNode);
+ var topPadding = 0;
+ var nameFilterValue = this._nameFilter ? this._nameFilter.value().toLowerCase() : "";
+ // Iterate over invisible nodes beyond the upper bound of viewport.
+ // Do not insert them into the grid, but count their total height.
+ for (var i = 0; i < children.length; ++i) {
+ var child = children[i];
+ if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
+ continue;
+ var newTop = topPadding + this._nodeHeight(child);
+ if (nodeToReveal === child || (!nodeToReveal && newTop > topBound))
+ break;
+ topPadding = newTop;
+ }
+
+ // Put visible nodes into the data grid.
+ var position = topPadding;
+ for (; i < children.length && (nodeToReveal || position < bottomBound); ++i) {
+ var child = children[i];
+ if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
+ continue;
+ var hasChildren = child.hasChildren;
+ child.removeChildren();
+ child.hasChildren = hasChildren;
+ child.revealed = true;
+ parentNode.appendChild(child);
+ position += child.nodeSelfHeight();
+ position += this._addVisibleNodes(child, topBound - position, bottomBound - position, restPathToReveal);
+ if (nodeToReveal === child)
+ break;
+ }
+
+ // Count the invisible nodes beyond the bottom bound of the viewport.
+ var bottomPadding = 0;
+ for (; i < children.length; ++i) {
+ var child = children[i];
+ if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
+ continue;
+ bottomPadding += this._nodeHeight(child);
+ }
+
+ this._topPaddingHeight += topPadding;
+ this._bottomPaddingHeight += bottomPadding;
+ return position + bottomPadding;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotGridNode} node
+ * @return {number}
+ */
+ _nodeHeight: function(node)
+ {
+ if (!node.revealed)
+ return 0;
+ var result = node.nodeSelfHeight();
+ if (!node.expanded)
+ return result;
+ var children = this.allChildren(node);
+ for (var i = 0; i < children.length; i++)
+ result += this._nodeHeight(children[i]);
+ return result;
+ },
+
+ /**
+ * @override
+ * @return {?Element}
+ */
+ defaultAttachLocation: function()
+ {
+ return this._bottomPadding.element;
+ },
+
+ /**
+ * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal
+ */
+ revealTreeNode: function(pathToReveal)
+ {
+ this.updateVisibleNodes(true, pathToReveal);
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} parent
+ * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
+ */
+ allChildren: function(parent)
+ {
+ return parent._allChildren || (parent._allChildren = []);
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} parent
+ * @param {!WebInspector.DataGridNode} node
+ */
+ appendNode: function(parent, node)
+ {
+ this.allChildren(parent).push(node);
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} parent
+ * @param {!WebInspector.DataGridNode} node
+ * @param {number} index
+ */
+ insertChild: function(parent, node, index)
+ {
+ this.allChildren(parent).splice(index, 0, node);
+ },
+
+ removeChildByIndex: function(parent, index)
+ {
+ this.allChildren(parent).splice(index, 1);
+ },
+
+ removeAllChildren: function(parent)
+ {
+ parent._allChildren = [];
+ },
+
+ removeTopLevelNodes: function()
+ {
+ this._disposeAllNodes();
+ this.rootNode().removeChildren();
+ this.rootNode()._allChildren = [];
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotGridNode} node
+ */
+ highlightNode: function(node)
+ {
+ if (this._isScrolledIntoView(node.element)) {
+ this.updateVisibleNodes(true);
+ WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node);
+ } else {
+ node.element.scrollIntoViewIfNeeded(true);
+ this._nodeToHighlightAfterScroll = node;
+ }
+ },
+
+ /**
+ * @param {!Element} element
+ * @return {boolean}
+ */
+ _isScrolledIntoView: function(element)
+ {
+ var viewportTop = this.scrollContainer.scrollTop;
+ var viewportBottom = viewportTop + this.scrollContainer.clientHeight;
+ var elemTop = element.offsetTop
+ var elemBottom = elemTop + element.offsetHeight;
+ return elemBottom <= viewportBottom && elemTop >= viewportTop;
+ },
+
+ onResize: function()
+ {
+ WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this);
+ this.updateVisibleNodes(false);
+ },
+
+ _onScroll: function(event)
+ {
+ this.updateVisibleNodes(false);
+
+ if (this._nodeToHighlightAfterScroll) {
+ WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll);
+ this._nodeToHighlightAfterScroll = null;
+ }
+ },
+
+ __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotPaddingNode = function()
+{
+ this.element = document.createElement("tr");
+ this.element.classList.add("revealed");
+ this.setHeight(0);
+}
+
+WebInspector.HeapSnapshotPaddingNode.prototype = {
+ setHeight: function(height)
+ {
+ this.element.style.height = height + "px";
+ },
+ removeFromTable: function()
+ {
+ var parent = this.element.parentNode;
+ if (parent)
+ parent.removeChild(this.element);
+ }
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotSortableDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns
+ */
+WebInspector.HeapSnapshotContainmentDataGrid = function(dataDisplayDelegate, columns)
+{
+ columns = columns || [
+ {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
+ {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true},
+ {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+ {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}
+ ];
+ WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
+}
+
+WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
+ /**
+ * @param {!WebInspector.HeapSnapshotProxy} snapshot
+ * @param {number} nodeIndex
+ */
+ setDataSource: function(snapshot, nodeIndex)
+ {
+ this.snapshot = snapshot;
+ var node = { nodeIndex: nodeIndex || snapshot.rootNodeIndex };
+ var fakeEdge = { node: node };
+ this.setRootNode(this._createRootNode(snapshot, fakeEdge));
+ this.rootNode().sort();
+ },
+
+ _createRootNode: function(snapshot, fakeEdge)
+ {
+ return new WebInspector.HeapSnapshotObjectNode(this, snapshot, fakeEdge, null);
+ },
+
+ sortingChanged: function()
+ {
+ var rootNode = this.rootNode();
+ if (rootNode.hasChildren)
+ rootNode.sort();
+ },
+
+ __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotContainmentDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ */
+WebInspector.HeapSnapshotRetainmentDataGrid = function(dataDisplayDelegate)
+{
+ var columns = [
+ {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
+ {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending},
+ {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+ {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true}
+ ];
+ WebInspector.HeapSnapshotContainmentDataGrid.call(this, dataDisplayDelegate, columns);
+}
+
+WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
+ ExpandRetainersComplete: "ExpandRetainersComplete"
+}
+
+WebInspector.HeapSnapshotRetainmentDataGrid.prototype = {
+ _createRootNode: function(snapshot, fakeEdge)
+ {
+ return new WebInspector.HeapSnapshotRetainingObjectNode(this, snapshot, fakeEdge, null);
+ },
+
+ _sortFields: function(sortColumn, sortAscending)
+ {
+ return {
+ object: ["_name", sortAscending, "_count", false],
+ count: ["_count", sortAscending, "_name", true],
+ shallowSize: ["_shallowSize", sortAscending, "_name", true],
+ retainedSize: ["_retainedSize", sortAscending, "_name", true],
+ distance: ["_distance", sortAscending, "_name", true]
+ }[sortColumn];
+ },
+
+ reset: function()
+ {
+ this.rootNode().removeChildren();
+ this.resetSortingCache();
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotProxy} snapshot
+ * @param {number} nodeIndex
+ */
+ setDataSource: function(snapshot, nodeIndex)
+ {
+ WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex);
+ this.rootNode().expand();
+ },
+
+ __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotViewportDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ */
+WebInspector.HeapSnapshotConstructorsDataGrid = function(dataDisplayDelegate)
+{
+ var columns = [
+ {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
+ {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true},
+ {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true},
+ {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+ {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
+ ];
+ WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
+ this._profileIndex = -1;
+
+ this._objectIdToSelect = null;
+}
+
+WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
+ _sortFields: function(sortColumn, sortAscending)
+ {
+ return {
+ object: ["_name", sortAscending, "_count", false],
+ distance: ["_distance", sortAscending, "_retainedSize", true],
+ count: ["_count", sortAscending, "_name", true],
+ shallowSize: ["_shallowSize", sortAscending, "_name", true],
+ retainedSize: ["_retainedSize", sortAscending, "_name", true]
+ }[sortColumn];
+ },
+
+ /**
+ * @override
+ * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
+ * @param {function(boolean)} callback
+ */
+ highlightObjectByHeapSnapshotId: function(id, callback)
+ {
+ if (!this.snapshot) {
+ this._objectIdToSelect = id;
+ return;
+ }
+
+ /**
+ * @param {?string} className
+ * @this {WebInspector.HeapSnapshotConstructorsDataGrid}
+ */
+ function didGetClassName(className)
+ {
+ if (!className) {
+ callback(false);
+ return;
+ }
+ var constructorNodes = this.topLevelNodes();
+ for (var i = 0; i < constructorNodes.length; i++) {
+ var parent = constructorNodes[i];
+ if (parent._name === className) {
+ parent.revealNodeBySnapshotObjectId(parseInt(id, 10), callback);
+ return;
+ }
+ }
+ }
+ this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this));
+ },
+
+ clear: function()
+ {
+ this._nextRequestedFilter = null;
+ this._lastFilter = null;
+ this.removeTopLevelNodes();
+ },
+
+ setDataSource: function(snapshot)
+ {
+ this.snapshot = snapshot;
+ if (this._profileIndex === -1)
+ this._populateChildren();
+
+ if (this._objectIdToSelect) {
+ this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
+ this._objectIdToSelect = null;
+ }
+ },
+
+ /**
+ * @param {number} minNodeId
+ * @param {number} maxNodeId
+ */
+ setSelectionRange: function(minNodeId, maxNodeId)
+ {
+ this._populateChildren(new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId));
+ },
+
+ /**
+ * @param {number} allocationNodeId
+ */
+ setAllocationNodeId: function(allocationNodeId)
+ {
+ var filter = new WebInspector.HeapSnapshotCommon.NodeFilter();
+ filter.allocationNodeId = allocationNodeId;
+ this._populateChildren(filter);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
+ * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} aggregates
+ */
+ _aggregatesReceived: function(nodeFilter, aggregates)
+ {
+ this._filterInProgress = null;
+ if (this._nextRequestedFilter) {
+ this.snapshot.aggregatesWithFilter(this._nextRequestedFilter, this._aggregatesReceived.bind(this, this._nextRequestedFilter));
+ this._filterInProgress = this._nextRequestedFilter;
+ this._nextRequestedFilter = null;
+ }
+ this.removeTopLevelNodes();
+ this.resetSortingCache();
+ for (var constructor in aggregates)
+ this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], nodeFilter));
+ this.sortingChanged();
+ this._lastFilter = nodeFilter;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter=} nodeFilter
+ */
+ _populateChildren: function(nodeFilter)
+ {
+ nodeFilter = nodeFilter || new WebInspector.HeapSnapshotCommon.NodeFilter();
+
+ if (this._filterInProgress) {
+ this._nextRequestedFilter = this._filterInProgress.equals(nodeFilter) ? null : nodeFilter;
+ return;
+ }
+ if (this._lastFilter && this._lastFilter.equals(nodeFilter))
+ return;
+ this._filterInProgress = nodeFilter;
+ this.snapshot.aggregatesWithFilter(nodeFilter, this._aggregatesReceived.bind(this, nodeFilter));
+ },
+
+ filterSelectIndexChanged: function(profiles, profileIndex)
+ {
+ this._profileIndex = profileIndex;
+
+ var nodeFilter;
+ if (profileIndex !== -1) {
+ var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0;
+ var maxNodeId = profiles[profileIndex].maxJSObjectId;
+ nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId)
+ }
+
+ this._populateChildren(nodeFilter);
+ },
+
+ __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotViewportDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ */
+WebInspector.HeapSnapshotDiffDataGrid = function(dataDisplayDelegate)
+{
+ var columns = [
+ {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
+ {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true},
+ {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true},
+ {id: "countDelta", title: WebInspector.UIString("# Delta"), width: "64px", sortable: true},
+ {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
+ {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true},
+ {id: "sizeDelta", title: WebInspector.UIString("Size Delta"), width: "72px", sortable: true}
+ ];
+ WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
+}
+
+WebInspector.HeapSnapshotDiffDataGrid.prototype = {
+ /**
+ * @override
+ * @return {number}
+ */
+ defaultPopulateCount: function()
+ {
+ return 50;
+ },
+
+ _sortFields: function(sortColumn, sortAscending)
+ {
+ return {
+ object: ["_name", sortAscending, "_count", false],
+ addedCount: ["_addedCount", sortAscending, "_name", true],
+ removedCount: ["_removedCount", sortAscending, "_name", true],
+ countDelta: ["_countDelta", sortAscending, "_name", true],
+ addedSize: ["_addedSize", sortAscending, "_name", true],
+ removedSize: ["_removedSize", sortAscending, "_name", true],
+ sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
+ }[sortColumn];
+ },
+
+ setDataSource: function(snapshot)
+ {
+ this.snapshot = snapshot;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotProxy} baseSnapshot
+ */
+ setBaseDataSource: function(baseSnapshot)
+ {
+ this.baseSnapshot = baseSnapshot;
+ this.removeTopLevelNodes();
+ this.resetSortingCache();
+ if (this.baseSnapshot === this.snapshot) {
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete);
+ return;
+ }
+ this._populateChildren();
+ },
+
+ _populateChildren: function()
+ {
+ /**
+ * @this {WebInspector.HeapSnapshotDiffDataGrid}
+ */
+ function aggregatesForDiffReceived(aggregatesForDiff)
+ {
+ this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this));
+
+ /**
+ * @this {WebInspector.HeapSnapshotDiffDataGrid}
+ */
+ function didCalculateSnapshotDiff(diffByClassName)
+ {
+ for (var className in diffByClassName) {
+ var diff = diffByClassName[className];
+ this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotDiffNode(this, className, diff));
+ }
+ this.sortingChanged();
+ }
+ }
+ // Two snapshots live in different workers isolated from each other. That is why
+ // we first need to collect information about the nodes in the first snapshot and
+ // then pass it to the second snapshot to calclulate the diff.
+ this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this));
+ },
+
+ __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotSortableDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ */
+WebInspector.HeapSnapshotDominatorsDataGrid = function(dataDisplayDelegate)
+{
+ var columns = [
+ {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
+ {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+ {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
+ ];
+ WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
+ this._objectIdToSelect = null;
+}
+
+WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
+ /**
+ * @override
+ * @return {number}
+ */
+ defaultPopulateCount: function()
+ {
+ return 25;
+ },
+
+ setDataSource: function(snapshot)
+ {
+ this.snapshot = snapshot;
+
+ var fakeNode = new WebInspector.HeapSnapshotCommon.Node(-1, "", 0, this.snapshot.rootNodeIndex, 0, 0, "");
+ this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode));
+ this.rootNode().sort();
+
+ if (this._objectIdToSelect) {
+ this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
+ this._objectIdToSelect = null;
+ }
+ },
+
+ sortingChanged: function()
+ {
+ this.rootNode().sort();
+ },
+
+ /**
+ * @override
+ * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
+ * @param {function(boolean)} callback
+ */
+ highlightObjectByHeapSnapshotId: function(id, callback)
+ {
+ if (!this.snapshot) {
+ this._objectIdToSelect = id;
+ callback(false);
+ return;
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotDominatorsDataGrid}
+ */
+ function didGetDominators(dominatorIds)
+ {
+ if (!dominatorIds) {
+ console.error("Cannot find corresponding heap snapshot node");
+ callback(false);
+ return;
+ }
+ var dominatorNode = this.rootNode();
+ expandNextDominator.call(this, dominatorIds, dominatorNode);
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotDominatorsDataGrid}
+ */
+ function expandNextDominator(dominatorIds, dominatorNode)
+ {
+ if (!dominatorNode) {
+ console.error("Cannot find dominator node");
+ callback(false);
+ return;
+ }
+ if (!dominatorIds.length) {
+ this.highlightNode(dominatorNode);
+ dominatorNode.element.scrollIntoViewIfNeeded(true);
+ callback(true);
+ return;
+ }
+ var snapshotObjectId = dominatorIds.pop();
+ dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds));
+ }
+
+ this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this));
+ },
+
+ __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotViewportDataGrid}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ */
+WebInspector.AllocationDataGrid = function(dataDisplayDelegate)
+{
+ var columns = [
+ {id: "liveCount", title: WebInspector.UIString("Live Count"), width: "72px", sortable: true},
+ {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true},
+ {id: "liveSize", title: WebInspector.UIString("Live Size"), width: "72px", sortable: true},
+ {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
+ {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true},
+ ];
+ WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
+ this._linkifier = new WebInspector.Linkifier();
+}
+
+WebInspector.AllocationDataGrid.prototype = {
+ dispose: function()
+ {
+ this._linkifier.reset();
+ },
+
+ setDataSource: function(snapshot)
+ {
+ this.snapshot = snapshot;
+ this.snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this));
+
+ /**
+ * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} tops
+ * @this {WebInspector.AllocationDataGrid}
+ */
+ function didReceiveAllocationTracesTops(tops)
+ {
+ this._topNodes = tops;
+ this._populateChildren();
+ }
+ },
+
+ _populateChildren: function()
+ {
+ this.removeTopLevelNodes();
+ var root = this.rootNode();
+ var tops = this._topNodes;
+ for (var i = 0; i < tops.length; i++)
+ this.appendNode(root, new WebInspector.AllocationGridNode(this, tops[i]));
+ this.updateVisibleNodes(true);
+ },
+
+ sortingChanged: function()
+ {
+ this._topNodes.sort(this._createComparator());
+ this.rootNode().removeChildren();
+ this._populateChildren();
+ },
+
+
+ /**
+ * @return {function(!Object, !Object):number}
+ */
+ _createComparator: function()
+ {
+ var fieldName = this.sortColumnIdentifier();
+ var compareResult = (this.sortOrder() === WebInspector.DataGrid.Order.Ascending) ? +1 : -1;
+ /**
+ * @param {!Object} a
+ * @param {!Object} b
+ * @return {number}
+ */
+ function compare(a, b)
+ {
+ if (a[fieldName] > b[fieldName])
+ return compareResult;
+ if (a[fieldName] < b[fieldName])
+ return -compareResult;
+ return 0;
+ }
+ return compare;
+ },
+
+ __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js
new file mode 100644
index 00000000000..3a2223f1fbd
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js
@@ -0,0 +1,1616 @@
+/*
+ * 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 = [];
+
+ /**
+ * @type {?WebInspector.HeapSnapshotGridNode.ChildrenProvider}
+ */
+ this._providerObject = null;
+}
+
+WebInspector.HeapSnapshotGridNode.Events = {
+ PopulateComplete: "PopulateComplete"
+}
+
+/**
+ * @param {!Array.<string>} fieldNames
+ * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
+ */
+WebInspector.HeapSnapshotGridNode.createComparator = function(fieldNames)
+{
+ return /** @type {!WebInspector.HeapSnapshotCommon.ComparatorConfig} */ ({fieldName1: fieldNames[0], ascending1: fieldNames[1], fieldName2: fieldNames[2], ascending2: fieldNames[3]});
+}
+
+
+/**
+ * @interface
+ */
+WebInspector.HeapSnapshotGridNode.ChildrenProvider = function() { }
+
+WebInspector.HeapSnapshotGridNode.ChildrenProvider.prototype = {
+ dispose: function() { },
+
+ /**
+ * @param {number} snapshotObjectId
+ * @param {function(number)} callback
+ */
+ nodePosition: function(snapshotObjectId, callback) { },
+
+ /**
+ * @param {function(boolean)} callback
+ */
+ isEmpty: function(callback) { },
+
+ /**
+ * @param {number} startPosition
+ * @param {number} endPosition
+ * @param {function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
+ */
+ serializeItemsRange: function(startPosition, endPosition, callback) { },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
+ * @param {function()} callback
+ */
+ sortAndRewind: function(comparator, callback) { }
+}
+
+
+WebInspector.HeapSnapshotGridNode.prototype = {
+ /**
+ * @return {!WebInspector.HeapSnapshotGridNode.ChildrenProvider}
+ */
+ createProvider: function()
+ {
+ throw new Error("Not implemented.");
+ },
+
+ /**
+ * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
+ */
+ retainersDataSource: function()
+ {
+ return null;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotGridNode.ChildrenProvider}
+ */
+ _provider: function()
+ {
+ if (!this._providerObject)
+ this._providerObject = this.createProvider();
+ return this._providerObject;
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+ if (this._searchMatched)
+ cell.classList.add("highlight");
+ return cell;
+ },
+
+ /**
+ * @override
+ */
+ collapse: function()
+ {
+ WebInspector.DataGridNode.prototype.collapse.call(this);
+ this._dataGrid.updateVisibleNodes(true);
+ },
+
+ /**
+ * @override
+ */
+ expand: function()
+ {
+ WebInspector.DataGridNode.prototype.expand.call(this);
+ this._dataGrid.updateVisibleNodes(true);
+ },
+
+ /**
+ * @override
+ */
+ dispose: function()
+ {
+ if (this._providerObject)
+ this._providerObject.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);
+ },
+
+ /**
+ * @param {number} num
+ * @return {string}
+ */
+ _toPercentString: function(num)
+ {
+ return num.toFixed(0) + "\u2009%"; // \u2009 is a thin space.
+ },
+
+ /**
+ * @param {number} distance
+ * @return {string}
+ */
+ _toUIDistance: function(distance)
+ {
+ var baseSystemDistance = WebInspector.HeapSnapshotCommon.baseSystemDistance;
+ return distance >= 0 && distance < baseSystemDistance ? WebInspector.UIString("%d", distance) : WebInspector.UIString("\u2212");
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.DataGridNode>}
+ */
+ allChildren: function()
+ {
+ return this._dataGrid.allChildren(this);
+ },
+
+ /**
+ * @param {number} index
+ */
+ removeChildByIndex: function(index)
+ {
+ this._dataGrid.removeChildByIndex(this, index);
+ },
+
+ /**
+ * @param {number} nodePosition
+ * @return {?WebInspector.DataGridNode}
+ */
+ childForPosition: function(nodePosition)
+ {
+ var indexOfFirstChildInRange = 0;
+ for (var i = 0; i < this._retrievedChildrenRanges.length; i++) {
+ var range = this._retrievedChildrenRanges[i];
+ if (range.from <= nodePosition && nodePosition < range.to) {
+ var childIndex = indexOfFirstChildInRange + nodePosition - range.from;
+ return this.allChildren()[childIndex];
+ }
+ indexOfFirstChildInRange += range.to - range.from + 1;
+ }
+ return null;
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ _createValueCell: function(columnIdentifier)
+ {
+ var cell = document.createElement("td");
+ cell.className = "numeric-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.classList.add("profile-multiple-values");
+ }
+ cell.appendChild(div);
+ }
+ return cell;
+ },
+
+ populate: function(event)
+ {
+ if (this._populated)
+ return;
+ this._populated = true;
+
+ /**
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ 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
+ * @param {function()=} afterPopulate
+ */
+ _populateChildren: function(fromPosition, toPosition, afterPopulate)
+ {
+ fromPosition = fromPosition || 0;
+ toPosition = toPosition || fromPosition + this._dataGrid.defaultPopulateCount();
+ var firstNotSerializedPosition = fromPosition;
+
+ /**
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ 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;
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ function insertRetrievedChild(item, insertionIndex)
+ {
+ if (this._savedChildren) {
+ var hash = this._childHashForEntity(item);
+ if (hash in this._savedChildren) {
+ this._dataGrid.insertChild(this, this._savedChildren[hash], insertionIndex);
+ return;
+ }
+ }
+ this._dataGrid.insertChild(this, this._createChildNode(item), insertionIndex);
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ function insertShowMoreButton(from, to, insertionIndex)
+ {
+ var button = new WebInspector.ShowMoreDataGridNode(this._populateChildren.bind(this), from, to, this._dataGrid.defaultPopulateCount());
+ this._dataGrid.insertChild(this, button, insertionIndex);
+ }
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ function childrenRetrieved(itemsRange)
+ {
+ var itemIndex = 0;
+ var itemPosition = itemsRange.startPosition;
+ var items = itemsRange.items;
+ var insertionIndex = 0;
+
+ if (!this._retrievedChildrenRanges.length) {
+ if (itemsRange.startPosition > 0) {
+ this._retrievedChildrenRanges.push({from: 0, to: 0});
+ insertShowMoreButton.call(this, 0, itemsRange.startPosition, insertionIndex++);
+ }
+ this._retrievedChildrenRanges.push({from: itemsRange.startPosition, to: itemsRange.endPosition});
+ for (var i = 0, l = items.length; i < l; ++i)
+ insertRetrievedChild.call(this, items[i], insertionIndex++);
+ if (itemsRange.endPosition < itemsRange.totalLength)
+ insertShowMoreButton.call(this, itemsRange.endPosition, itemsRange.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 < itemsRange.totalLength)
+ insertionIndex += 1;
+ ++rangeIndex;
+ }
+
+ if (!found || itemsRange.startPosition < range.from) {
+ // Update previous button.
+ this.allChildren()[insertionIndex - 1].setEndPosition(itemsRange.startPosition);
+ insertShowMoreButton.call(this, itemsRange.startPosition, found ? range.from : itemsRange.totalLength, insertionIndex);
+ range = {from: itemsRange.startPosition, to: itemsRange.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 < itemsRange.endPosition) {
+ // Skip already added nodes.
+ var skipCount = range.to - itemPosition;
+ insertionIndex += skipCount;
+ itemIndex += skipCount;
+ itemPosition = range.to;
+
+ // We're at the position before button: ...<?node>x<button>
+ var nextRange = this._retrievedChildrenRanges[rangeIndex + 1];
+ var newEndOfRange = nextRange ? nextRange.from : itemsRange.totalLength;
+ if (newEndOfRange > itemsRange.endPosition)
+ newEndOfRange = itemsRange.endPosition;
+ while (itemPosition < newEndOfRange) {
+ insertRetrievedChild.call(this, items[itemIndex++], insertionIndex++);
+ ++itemPosition;
+ }
+ // Merge with the next range.
+ if (nextRange && newEndOfRange === nextRange.from) {
+ range.to = nextRange.to;
+ // Remove "show next" button if there is one.
+ this.removeChildByIndex(insertionIndex);
+ this._retrievedChildrenRanges.splice(rangeIndex + 1, 1);
+ } else {
+ range.to = newEndOfRange;
+ // Remove or update next button.
+ if (newEndOfRange === itemsRange.totalLength)
+ this.removeChildByIndex(insertionIndex);
+ else
+ this.allChildren()[insertionIndex].setStartPosition(itemsRange.endPosition);
+ }
+ }
+ }
+
+ // TODO: fix this.
+ this._instanceCount += items.length;
+ if (firstNotSerializedPosition < toPosition) {
+ serializeNextChunk.call(this);
+ return;
+ }
+
+ if (this.expanded)
+ this._dataGrid.updateVisibleNodes(true);
+ if (afterPopulate)
+ afterPopulate();
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete);
+ }
+ serializeNextChunk.call(this);
+ },
+
+ _saveChildren: function()
+ {
+ this._savedChildren = null;
+ var children = this.allChildren();
+ for (var i = 0, l = children.length; i < l; ++i) {
+ var child = children[i];
+ if (!child.expanded)
+ continue;
+ if (!this._savedChildren)
+ this._savedChildren = {};
+ this._savedChildren[this._childHashForNode(child)] = child;
+ }
+ },
+
+ sort: function()
+ {
+ this._dataGrid.recursiveSortingEnter();
+
+ /**
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ function afterSort()
+ {
+ this._saveChildren();
+ this._dataGrid.removeAllChildren(this);
+ this._retrievedChildrenRanges = [];
+
+ /**
+ * @this {WebInspector.HeapSnapshotGridNode}
+ */
+ function afterPopulate()
+ {
+ var children = this.allChildren();
+ for (var i = 0, l = children.length; i < l; ++i) {
+ var child = children[i];
+ if (child.expanded)
+ child.sort();
+ }
+ this._dataGrid.recursiveSortingLeave();
+ }
+ var instanceCount = this._instanceCount;
+ this._instanceCount = 0;
+ this._populateChildren(0, instanceCount, afterPopulate.bind(this));
+ }
+
+ this._provider().sortAndRewind(this.comparator(), afterSort.bind(this));
+ },
+
+ __proto__: WebInspector.DataGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ */
+WebInspector.HeapSnapshotGenericObjectNode = function(dataGrid, node)
+{
+ WebInspector.HeapSnapshotGridNode.call(this, dataGrid, false);
+ // node is null for DataGrid root nodes.
+ if (!node)
+ return;
+ this._name = node.name;
+ this._type = node.type;
+ this._distance = node.distance;
+ this._shallowSize = node.selfSize;
+ this._retainedSize = node.retainedSize;
+ this.snapshotNodeId = node.id;
+ this.snapshotNodeIndex = node.nodeIndex;
+ if (this._type === "string")
+ this._reachableFromWindow = true;
+ else if (this._type === "object" && this._name.startsWith("Window")) {
+ this._name = this.shortenWindowURL(this._name, false);
+ this._reachableFromWindow = true;
+ } else if (node.canBeQueried)
+ this._reachableFromWindow = true;
+ if (node.detachedDOMTreeNode)
+ this.detachedDOMTreeNode = true;
+
+ var snapshot = dataGrid.snapshot;
+ var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
+ var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
+ this.data = {
+ "distance": this._toUIDistance(this._distance),
+ "shallowSize": Number.withThousandsSeparator(this._shallowSize),
+ "retainedSize": Number.withThousandsSeparator(this._retainedSize),
+ "shallowSize-percent": this._toPercentString(shallowSizePercent),
+ "retainedSize-percent": this._toPercentString(retainedSizePercent)
+ };
+};
+
+WebInspector.HeapSnapshotGenericObjectNode.prototype = {
+ /**
+ * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
+ */
+ retainersDataSource: function()
+ {
+ return {snapshot: this._dataGrid.snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : this._createObjectCell();
+ if (this._searchMatched)
+ cell.classList.add("highlight");
+ return cell;
+ },
+
+ /**
+ * @return {!Element}
+ */
+ _createObjectCell: function()
+ {
+ var value = this._name;
+ var valueStyle = "object";
+ switch (this._type) {
+ case "concatenated string":
+ case "string":
+ value = "\"" + value + "\"";
+ valueStyle = "string";
+ break;
+ case "regexp":
+ value = "/" + value + "/";
+ valueStyle = "string";
+ break;
+ case "closure":
+ value = "function" + (value ? " " : "") + value + "()";
+ valueStyle = "function";
+ break;
+ case "number":
+ valueStyle = "number";
+ break;
+ case "hidden":
+ valueStyle = "null";
+ break;
+ case "array":
+ if (!value)
+ value = "[]";
+ else
+ value += "[]";
+ break;
+ };
+ if (this._reachableFromWindow)
+ valueStyle += " highlight";
+ if (value === "Object")
+ value = "";
+ if (this.detachedDOMTreeNode)
+ valueStyle += " detached-dom-tree-node";
+ return this._createObjectCellWithValue(valueStyle, value);
+ },
+
+ _createObjectCellWithValue: function(valueStyle, value)
+ {
+ var cell = document.createElement("td");
+ cell.className = "object-column";
+ var div = document.createElement("div");
+ div.className = "source-code event-properties";
+ div.style.overflow = "visible";
+
+ this._prefixObjectCell(div);
+
+ var valueSpan = document.createElement("span");
+ valueSpan.className = "value console-formatted-" + valueStyle;
+ valueSpan.textContent = value;
+ div.appendChild(valueSpan);
+
+ var idSpan = document.createElement("span");
+ idSpan.className = "console-formatted-id";
+ idSpan.textContent = " @" + this.snapshotNodeId;
+ div.appendChild(idSpan);
+
+ cell.appendChild(div);
+ cell.classList.add("disclosure");
+ if (this.depth)
+ cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
+ cell.heapSnapshotNode = this;
+ return cell;
+ },
+
+ _prefixObjectCell: function(div)
+ {
+ },
+
+ queryObjectContent: function(callback, objectGroupName)
+ {
+ /**
+ * @param {?Protocol.Error} error
+ * @param {!RuntimeAgent.RemoteObject} object
+ */
+ function formatResult(error, object)
+ {
+ if (!error && object.type)
+ callback(WebInspector.runtimeModel.createRemoteObject(object), !!error);
+ else
+ callback(WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(WebInspector.UIString("Preview is not available")));
+ }
+
+ if (this._type === "string")
+ callback(WebInspector.runtimeModel.createRemoteObjectFromPrimitiveValue(this._name));
+ else
+ HeapProfilerAgent.getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult);
+ },
+
+ updateHasChildren: function()
+ {
+ /**
+ * @this {WebInspector.HeapSnapshotGenericObjectNode}
+ */
+ function isEmptyCallback(isEmpty)
+ {
+ this.hasChildren = !isEmpty;
+ }
+ this._provider().isEmpty(isEmptyCallback.bind(this));
+ },
+
+ /**
+ * @param {string} fullName
+ * @param {boolean} hasObjectId
+ * @return {string}
+ */
+ shortenWindowURL: function(fullName, hasObjectId)
+ {
+ var startPos = fullName.indexOf("/");
+ var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
+ if (startPos !== -1 && endPos !== -1) {
+ var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
+ var url = fullURL.trimURL();
+ if (url.length > 40)
+ url = url.trimMiddle(40);
+ return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
+ } else
+ return fullName;
+ },
+
+ __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGenericObjectNode}
+ * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
+ * @param {!WebInspector.HeapSnapshotProxy} snapshot
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
+ * @param {?WebInspector.HeapSnapshotObjectNode} parentObjectNode
+ */
+WebInspector.HeapSnapshotObjectNode = function(dataGrid, snapshot, edge, parentObjectNode)
+{
+ WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, edge.node);
+ this._referenceName = edge.name;
+ this._referenceType = edge.type;
+ this._edgeIndex = edge.edgeIndex;
+ this._snapshot = snapshot;
+
+ this._parentObjectNode = parentObjectNode;
+ this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId();
+ if (!this._cycledWithAncestorGridNode)
+ this.updateHasChildren();
+
+ var data = this.data;
+ data["count"] = "";
+ data["addedCount"] = "";
+ data["removedCount"] = "";
+ data["countDelta"] = "";
+ data["addedSize"] = "";
+ data["removedSize"] = "";
+ data["sizeDelta"] = "";
+}
+
+WebInspector.HeapSnapshotObjectNode.prototype = {
+ /**
+ * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
+ */
+ retainersDataSource: function()
+ {
+ return {snapshot: this._snapshot, snapshotNodeIndex: this.snapshotNodeIndex};
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createProvider: function()
+ {
+ return this._snapshot.createEdgesProvider(this.snapshotNodeIndex);
+ },
+
+ _findAncestorWithSameSnapshotNodeId: function()
+ {
+ var ancestor = this._parentObjectNode;
+ while (ancestor) {
+ if (ancestor.snapshotNodeId === this.snapshotNodeId)
+ return ancestor;
+ ancestor = ancestor._parentObjectNode;
+ }
+ return null;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} item
+ * @return {!WebInspector.HeapSnapshotObjectNode}
+ */
+ _createChildNode: function(item)
+ {
+ return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._snapshot, item, this);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
+ * @return {number}
+ */
+ _childHashForEntity: function(edge)
+ {
+ return edge.edgeIndex;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotObjectNode} childNode
+ * @return {number}
+ */
+ _childHashForNode: function(childNode)
+ {
+ return childNode._edgeIndex;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
+ */
+ comparator: function()
+ {
+ var sortAscending = this._dataGrid.isSortOrderAscending();
+ var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+ var sortFields = {
+ object: ["!edgeName", sortAscending, "retainedSize", false],
+ count: ["!edgeName", true, "retainedSize", false],
+ shallowSize: ["selfSize", sortAscending, "!edgeName", true],
+ retainedSize: ["retainedSize", sortAscending, "!edgeName", true],
+ distance: ["distance", sortAscending, "_name", true]
+ }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
+ return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
+ },
+
+ _prefixObjectCell: function(div)
+ {
+ var name = this._referenceName;
+ if (name === "") name = "(empty)";
+ var nameClass = "name";
+ switch (this._referenceType) {
+ case "context":
+ nameClass = "console-formatted-number";
+ break;
+ case "internal":
+ case "hidden":
+ case "weak":
+ nameClass = "console-formatted-null";
+ break;
+ case "element":
+ name = "[" + name + "]";
+ break;
+ }
+
+ if (this._cycledWithAncestorGridNode)
+ div.className += " cycled-ancessor-node";
+
+ var nameSpan = document.createElement("span");
+ nameSpan.className = nameClass;
+ nameSpan.textContent = name;
+ div.appendChild(nameSpan);
+
+ var separatorSpan = document.createElement("span");
+ separatorSpan.className = "grayed";
+ separatorSpan.textContent = this._edgeNodeSeparator();
+ div.appendChild(separatorSpan);
+ },
+
+ /**
+ * @return {string}
+ */
+ _edgeNodeSeparator: function()
+ {
+ return " :: ";
+ },
+
+ __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotObjectNode}
+ * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
+ * @param {!WebInspector.HeapSnapshotProxy} snapshot
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
+ * @param {?WebInspector.HeapSnapshotRetainingObjectNode} parentRetainingObjectNode
+ */
+WebInspector.HeapSnapshotRetainingObjectNode = function(dataGrid, snapshot, edge, parentRetainingObjectNode)
+{
+ WebInspector.HeapSnapshotObjectNode.call(this, dataGrid, snapshot, edge, parentRetainingObjectNode);
+}
+
+WebInspector.HeapSnapshotRetainingObjectNode.prototype = {
+ /**
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createProvider: function()
+ {
+ return this._snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} item
+ * @return {!WebInspector.HeapSnapshotRetainingObjectNode}
+ */
+ _createChildNode: function(item)
+ {
+ return new WebInspector.HeapSnapshotRetainingObjectNode(this._dataGrid, this._snapshot, item, this);
+ },
+
+ /**
+ * @return {string}
+ */
+ _edgeNodeSeparator: function()
+ {
+ return " in ";
+ },
+
+ expand: function()
+ {
+ this._expandRetainersChain(20);
+ },
+
+ /**
+ * @param {number} maxExpandLevels
+ */
+ _expandRetainersChain: function(maxExpandLevels)
+ {
+ /**
+ * @this {!WebInspector.HeapSnapshotRetainingObjectNode}
+ */
+ function populateComplete()
+ {
+ this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
+ this._expandRetainersChain(maxExpandLevels);
+ }
+
+ if (!this._populated) {
+ this.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
+ this.populate();
+ return;
+ }
+ WebInspector.HeapSnapshotGenericObjectNode.prototype.expand.call(this);
+ if (--maxExpandLevels > 0 && this.children.length > 0) {
+ var retainer = this.children[0];
+ if (retainer._distance > 1) {
+ retainer._expandRetainersChain(maxExpandLevels);
+ return;
+ }
+ }
+ this._dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete);
+ },
+
+ __proto__: WebInspector.HeapSnapshotObjectNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGenericObjectNode}
+ * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
+ * @param {!WebInspector.HeapSnapshotProxy} snapshot
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ * @param {boolean} isDeletedNode
+ */
+WebInspector.HeapSnapshotInstanceNode = function(dataGrid, snapshot, node, isDeletedNode)
+{
+ WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
+ this._baseSnapshotOrSnapshot = snapshot;
+ this._isDeletedNode = isDeletedNode;
+ this.updateHasChildren();
+
+ var data = this.data;
+ data["count"] = "";
+ data["countDelta"] = "";
+ data["sizeDelta"] = "";
+ if (this._isDeletedNode) {
+ data["addedCount"] = "";
+ data["addedSize"] = "";
+ data["removedCount"] = "\u2022";
+ data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
+ } else {
+ data["addedCount"] = "\u2022";
+ data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
+ data["removedCount"] = "";
+ data["removedSize"] = "";
+ }
+};
+
+WebInspector.HeapSnapshotInstanceNode.prototype = {
+ /**
+ * @return {?{snapshot:!WebInspector.HeapSnapshotProxy, snapshotNodeIndex:number}}
+ */
+ retainersDataSource: function()
+ {
+ return {snapshot: this._baseSnapshotOrSnapshot, snapshotNodeIndex: this.snapshotNodeIndex};
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createProvider: function()
+ {
+ return this._baseSnapshotOrSnapshot.createEdgesProvider(this.snapshotNodeIndex);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} item
+ * @return {!WebInspector.HeapSnapshotObjectNode}
+ */
+ _createChildNode: function(item)
+ {
+ return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._baseSnapshotOrSnapshot, item, null);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Edge} edge
+ * @return {number}
+ */
+ _childHashForEntity: function(edge)
+ {
+ return edge.edgeIndex;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotObjectNode} childNode
+ * @return {number}
+ */
+ _childHashForNode: function(childNode)
+ {
+ return childNode._edgeIndex;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
+ */
+ comparator: function()
+ {
+ var sortAscending = this._dataGrid.isSortOrderAscending();
+ var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+ var sortFields = {
+ object: ["!edgeName", sortAscending, "retainedSize", false],
+ distance: ["distance", sortAscending, "retainedSize", false],
+ count: ["!edgeName", true, "retainedSize", false],
+ addedSize: ["selfSize", sortAscending, "!edgeName", true],
+ removedSize: ["selfSize", sortAscending, "!edgeName", true],
+ shallowSize: ["selfSize", sortAscending, "!edgeName", true],
+ retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
+ }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
+ return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
+ },
+
+ __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.HeapSnapshotConstructorsDataGrid} dataGrid
+ * @param {string} className
+ * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ */
+WebInspector.HeapSnapshotConstructorNode = function(dataGrid, className, aggregate, nodeFilter)
+{
+ WebInspector.HeapSnapshotGridNode.call(this, dataGrid, aggregate.count > 0);
+ this._name = className;
+ this._nodeFilter = nodeFilter;
+ this._distance = aggregate.distance;
+ this._count = aggregate.count;
+ this._shallowSize = aggregate.self;
+ this._retainedSize = aggregate.maxRet;
+
+ var snapshot = dataGrid.snapshot;
+ var countPercent = this._count / snapshot.nodeCount * 100.0;
+ var retainedSizePercent = this._retainedSize / snapshot.totalSize * 100.0;
+ var shallowSizePercent = this._shallowSize / snapshot.totalSize * 100.0;
+
+ this.data = {
+ "object": className,
+ "count": Number.withThousandsSeparator(this._count),
+ "distance": this._toUIDistance(this._distance),
+ "shallowSize": Number.withThousandsSeparator(this._shallowSize),
+ "retainedSize": Number.withThousandsSeparator(this._retainedSize),
+ "count-percent": this._toPercentString(countPercent),
+ "shallowSize-percent": this._toPercentString(shallowSizePercent),
+ "retainedSize-percent": this._toPercentString(retainedSizePercent)
+ };
+}
+
+WebInspector.HeapSnapshotConstructorNode.prototype = {
+ /**
+ * @override
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createProvider: function()
+ {
+ return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._nodeFilter)
+ },
+
+ /**
+ * @param {number} snapshotObjectId
+ * @param {function(boolean)} callback
+ */
+ revealNodeBySnapshotObjectId: function(snapshotObjectId, callback)
+ {
+ /**
+ * @this {WebInspector.HeapSnapshotConstructorNode}
+ */
+ function didExpand()
+ {
+ this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotConstructorNode}
+ * @param {number} nodePosition
+ */
+ function didGetNodePosition(nodePosition)
+ {
+ if (nodePosition === -1) {
+ this.collapse();
+ callback(false);
+ } else {
+ this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
+ }
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotConstructorNode}
+ * @param {number} nodePosition
+ */
+ function didPopulateChildren(nodePosition)
+ {
+ var child = this.childForPosition(nodePosition);
+ if (child) {
+ this._dataGrid.revealTreeNode([this, child]);
+ this._dataGrid.highlightNode(/** @type {!WebInspector.HeapSnapshotGridNode} */ (child));
+ }
+ callback(!!child);
+ }
+
+ this._dataGrid.resetNameFilter();
+ this.expandWithoutPopulate(didExpand.bind(this));
+ },
+
+ /**
+ * @param {string} filterValue
+ * @return {boolean}
+ */
+ filteredOut: function(filterValue)
+ {
+ return this._name.toLowerCase().indexOf(filterValue) === -1;
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
+ if (this._searchMatched)
+ cell.classList.add("highlight");
+ return cell;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Node} item
+ * @return {!WebInspector.HeapSnapshotInstanceNode}
+ */
+ _createChildNode: function(item)
+ {
+ return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
+ */
+ comparator: function()
+ {
+ var sortAscending = this._dataGrid.isSortOrderAscending();
+ var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+ var sortFields = {
+ object: ["id", sortAscending, "retainedSize", false],
+ distance: ["distance", sortAscending, "retainedSize", false],
+ count: ["id", true, "retainedSize", false],
+ shallowSize: ["selfSize", sortAscending, "id", true],
+ retainedSize: ["retainedSize", sortAscending, "id", true]
+ }[sortColumnIdentifier];
+ return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ * @return {number}
+ */
+ _childHashForEntity: function(node)
+ {
+ return node.id;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotInstanceNode} childNode
+ * @return {number}
+ */
+ _childHashForNode: function(childNode)
+ {
+ return childNode.snapshotNodeId;
+ },
+
+ __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
+ * @param {!WebInspector.HeapSnapshotProviderProxy} addedNodesProvider
+ * @param {!WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider
+ * @param {number} addedCount
+ * @param {number} removedCount
+ */
+WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount)
+{
+ this._addedNodesProvider = addedNodesProvider;
+ this._deletedNodesProvider = deletedNodesProvider;
+ this._addedCount = addedCount;
+ this._removedCount = removedCount;
+}
+
+WebInspector.HeapSnapshotDiffNodesProvider.prototype = {
+ dispose: function()
+ {
+ this._addedNodesProvider.dispose();
+ this._deletedNodesProvider.dispose();
+ },
+
+ /**
+ * @override
+ * @param {number} snapshotObjectId
+ * @param {function(number)} callback
+ */
+ nodePosition: function(snapshotObjectId, callback)
+ {
+ throw new Error("Unreachable");
+ },
+
+ /**
+ * @param {function(boolean)} callback
+ */
+ isEmpty: function(callback)
+ {
+ callback(false);
+ },
+
+ /**
+ * @param {number} beginPosition
+ * @param {number} endPosition
+ * @param {!function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
+ */
+ serializeItemsRange: function(beginPosition, endPosition, callback)
+ {
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} items
+ * @this {WebInspector.HeapSnapshotDiffNodesProvider}
+ */
+ function didReceiveAllItems(items)
+ {
+ items.totalLength = this._addedCount + this._removedCount;
+ callback(items);
+ }
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} addedItems
+ * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
+ * @this {WebInspector.HeapSnapshotDiffNodesProvider}
+ */
+ function didReceiveDeletedItems(addedItems, itemsRange)
+ {
+ var items = itemsRange.items;
+ if (!addedItems.items.length)
+ addedItems.startPosition = this._addedCount + itemsRange.startPosition;
+ for (var i = 0; i < items.length; i++) {
+ items[i].isAddedNotRemoved = false;
+ addedItems.items.push(items[i]);
+ }
+ addedItems.endPosition = this._addedCount + itemsRange.endPosition;
+ didReceiveAllItems.call(this, addedItems);
+ }
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ItemsRange} itemsRange
+ * @this {WebInspector.HeapSnapshotDiffNodesProvider}
+ */
+ function didReceiveAddedItems(itemsRange)
+ {
+ var items = itemsRange.items;
+ for (var i = 0; i < items.length; i++)
+ items[i].isAddedNotRemoved = true;
+ if (itemsRange.endPosition < endPosition)
+ return this._deletedNodesProvider.serializeItemsRange(0, endPosition - itemsRange.endPosition, didReceiveDeletedItems.bind(this, itemsRange));
+
+ itemsRange.totalLength = this._addedCount + this._removedCount;
+ didReceiveAllItems.call(this, itemsRange);
+ }
+
+ if (beginPosition < this._addedCount) {
+ this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this));
+ } else {
+ var emptyRange = new WebInspector.HeapSnapshotCommon.ItemsRange(0, 0, 0, []);
+ this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, emptyRange));
+ }
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
+ * @param {function()} callback
+ */
+ sortAndRewind: function(comparator, callback)
+ {
+ /**
+ * @this {WebInspector.HeapSnapshotDiffNodesProvider}
+ */
+ function afterSort()
+ {
+ this._deletedNodesProvider.sortAndRewind(comparator, callback);
+ }
+ this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this));
+ }
+};
+
+/**
+ * @constructor
+ * @param {!WebInspector.HeapSnapshotDiffDataGrid} dataGrid
+ * @param {string} className
+ * @param {!WebInspector.HeapSnapshotCommon.DiffForClass} diffForClass
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ */
+WebInspector.HeapSnapshotDiffNode = function(dataGrid, className, diffForClass)
+{
+ WebInspector.HeapSnapshotGridNode.call(this, dataGrid, true);
+ this._name = className;
+ this._addedCount = diffForClass.addedCount;
+ this._removedCount = diffForClass.removedCount;
+ this._countDelta = diffForClass.countDelta;
+ this._addedSize = diffForClass.addedSize;
+ this._removedSize = diffForClass.removedSize;
+ this._sizeDelta = diffForClass.sizeDelta;
+ this._deletedIndexes = diffForClass.deletedIndexes;
+ this.data = {
+ "object": className,
+ "addedCount": Number.withThousandsSeparator(this._addedCount),
+ "removedCount": Number.withThousandsSeparator(this._removedCount),
+ "countDelta": this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta)),
+ "addedSize": Number.withThousandsSeparator(this._addedSize),
+ "removedSize": Number.withThousandsSeparator(this._removedSize),
+ "sizeDelta": this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta))
+ };
+}
+
+WebInspector.HeapSnapshotDiffNode.prototype = {
+ /**
+ * @override
+ * @return {!WebInspector.HeapSnapshotDiffNodesProvider}
+ */
+ createProvider: function()
+ {
+ var tree = this._dataGrid;
+ return new WebInspector.HeapSnapshotDiffNodesProvider(
+ tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name),
+ tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes),
+ this._addedCount,
+ this._removedCount);
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
+ if (columnIdentifier !== "object")
+ cell.classList.add("numeric-column");
+ return cell;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Node} item
+ * @return {!WebInspector.HeapSnapshotInstanceNode}
+ */
+ _createChildNode: function(item)
+ {
+ if (item.isAddedNotRemoved)
+ return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.snapshot, item, false);
+ else
+ return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, item, true);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ * @return {number}
+ */
+ _childHashForEntity: function(node)
+ {
+ return node.id;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotInstanceNode} childNode
+ * @return {number}
+ */
+ _childHashForNode: function(childNode)
+ {
+ return childNode.snapshotNodeId;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
+ */
+ comparator: function()
+ {
+ var sortAscending = this._dataGrid.isSortOrderAscending();
+ var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+ var sortFields = {
+ object: ["id", sortAscending, "selfSize", false],
+ addedCount: ["selfSize", sortAscending, "id", true],
+ removedCount: ["selfSize", sortAscending, "id", true],
+ countDelta: ["selfSize", sortAscending, "id", true],
+ addedSize: ["selfSize", sortAscending, "id", true],
+ removedSize: ["selfSize", sortAscending, "id", true],
+ sizeDelta: ["selfSize", sortAscending, "id", true]
+ }[sortColumnIdentifier];
+ return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
+ },
+
+ /**
+ * @param {string} filterValue
+ * @return {boolean}
+ */
+ filteredOut: function(filterValue)
+ {
+ return this._name.toLowerCase().indexOf(filterValue) === -1;
+ },
+
+ _signForDelta: function(delta)
+ {
+ if (delta === 0)
+ return "";
+ if (delta > 0)
+ return "+";
+ else
+ return "\u2212"; // Math minus sign, same width as plus.
+ },
+
+ __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGenericObjectNode}
+ * @param {!WebInspector.HeapSnapshotSortableDataGrid} dataGrid
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ */
+WebInspector.HeapSnapshotDominatorObjectNode = function(dataGrid, node)
+{
+ WebInspector.HeapSnapshotGenericObjectNode.call(this, dataGrid, node);
+ this.updateHasChildren();
+};
+
+WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
+ /**
+ * @override
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createProvider: function()
+ {
+ return this._dataGrid.snapshot.createNodesProviderForDominator(this.snapshotNodeIndex);
+ },
+
+ /**
+ * @param {number} snapshotObjectId
+ * @param {function(?WebInspector.DataGridNode)} callback
+ */
+ retrieveChildBySnapshotObjectId: function(snapshotObjectId, callback)
+ {
+ /**
+ * @this {WebInspector.HeapSnapshotDominatorObjectNode}
+ */
+ function didExpand()
+ {
+ this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotDominatorObjectNode}
+ */
+ function didGetNodePosition(nodePosition)
+ {
+ if (nodePosition === -1) {
+ this.collapse();
+ callback(null);
+ } else
+ this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotDominatorObjectNode}
+ */
+ function didPopulateChildren(nodePosition)
+ {
+ var child = this.childForPosition(nodePosition);
+ callback(child);
+ }
+
+ // Make sure hasChildren flag is updated before expanding this node as updateHasChildren response
+ // may not have been received yet.
+ this.hasChildren = true;
+ this.expandWithoutPopulate(didExpand.bind(this));
+ },
+
+ _createChildNode: function(item)
+ {
+ return new WebInspector.HeapSnapshotDominatorObjectNode(this._dataGrid, item);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Node} node
+ * @return {number}
+ */
+ _childHashForEntity: function(node)
+ {
+ return node.id;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotDominatorObjectNode} childNode
+ * @return {number}
+ */
+ _childHashForNode: function(childNode)
+ {
+ return childNode.snapshotNodeId;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.ComparatorConfig}
+ */
+ comparator: function()
+ {
+ var sortAscending = this._dataGrid.isSortOrderAscending();
+ var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+ var sortFields = {
+ object: ["id", sortAscending, "retainedSize", false],
+ shallowSize: ["selfSize", sortAscending, "id", true],
+ retainedSize: ["retainedSize", sortAscending, "id", true]
+ }[sortColumnIdentifier];
+ return WebInspector.HeapSnapshotGridNode.createComparator(sortFields);
+ },
+
+ __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ * @param {!WebInspector.AllocationDataGrid} dataGrid
+ * @param {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode} data
+ */
+WebInspector.AllocationGridNode = function(dataGrid, data)
+{
+ WebInspector.HeapSnapshotGridNode.call(this, dataGrid, data.hasChildren);
+ this._populated = false;
+ this._allocationNode = data;
+ this.data = {
+ "liveCount": Number.withThousandsSeparator(data.liveCount),
+ "count": Number.withThousandsSeparator(data.count),
+ "liveSize": Number.withThousandsSeparator(data.liveSize),
+ "size": Number.withThousandsSeparator(data.size),
+ "name": data.name
+ };
+}
+
+WebInspector.AllocationGridNode.prototype = {
+ populate: function()
+ {
+ if (this._populated)
+ return;
+ this._populated = true;
+ this._dataGrid.snapshot.allocationNodeCallers(this._allocationNode.id, didReceiveCallers.bind(this));
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} callers
+ * @this {WebInspector.AllocationGridNode}
+ */
+ function didReceiveCallers(callers)
+ {
+ var callersChain = callers.nodesWithSingleCaller;
+ var parentNode = this;
+ var dataGrid = /** @type {!WebInspector.AllocationDataGrid} */ (this._dataGrid);
+ for (var i = 0; i < callersChain.length; i++) {
+ var child = new WebInspector.AllocationGridNode(dataGrid, callersChain[i]);
+ dataGrid.appendNode(parentNode, child);
+ parentNode = child;
+ parentNode._populated = true;
+ if (this.expanded)
+ parentNode.expand();
+ }
+
+ var callersBranch = callers.branchingCallers;
+ callersBranch.sort(this._dataGrid._createComparator());
+ for (var i = 0; i < callersBranch.length; i++)
+ dataGrid.appendNode(parentNode, new WebInspector.AllocationGridNode(dataGrid, callersBranch[i]));
+ dataGrid.updateVisibleNodes(true);
+ }
+ },
+
+ /**
+ * @override
+ */
+ expand: function()
+ {
+ WebInspector.HeapSnapshotGridNode.prototype.expand.call(this);
+ if (this.children.length === 1)
+ this.children[0].expand();
+ },
+
+ /**
+ * @override
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ if (columnIdentifier !== "name")
+ return this._createValueCell(columnIdentifier);
+
+ var cell = WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
+ var allocationNode = this._allocationNode;
+ if (allocationNode.scriptId) {
+ var urlElement;
+ var linkifier = this._dataGrid._linkifier;
+ var script = WebInspector.debuggerModel.scriptForId(String(allocationNode.scriptId));
+ if (script) {
+ var rawLocation = WebInspector.debuggerModel.createRawLocation(script, allocationNode.line - 1, allocationNode.column - 1);
+ urlElement = linkifier.linkifyRawLocation(rawLocation, "profile-node-file");
+ } else {
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ urlElement = linkifier.linkifyLocation(target, allocationNode.scriptName, allocationNode.line - 1, allocationNode.column - 1, "profile-node-file");
+ }
+ urlElement.style.maxWidth = "75%";
+ cell.insertBefore(urlElement, cell.firstChild);
+ }
+ return cell;
+ },
+
+ /**
+ * @return {number}
+ */
+ allocationNodeId: function()
+ {
+ return this._allocationNode.id;
+ },
+
+ __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotProxy.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotProxy.js
new file mode 100644
index 00000000000..109398a15a0
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotProxy.js
@@ -0,0 +1,550 @@
+/*
+ * 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 copyrightdd
+ * 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
+ * @param {function(string, *)} eventHandler
+ * @extends {WebInspector.Object}
+ */
+WebInspector.HeapSnapshotWorkerProxy = function(eventHandler)
+{
+ this._eventHandler = eventHandler;
+ this._nextObjectId = 1;
+ this._nextCallId = 1;
+ this._callbacks = [];
+ this._previousCallbacks = [];
+ this._worker = new Worker("profiler/heap_snapshot_worker/HeapSnapshotWorker.js");
+ this._worker.onmessage = this._messageReceived.bind(this);
+}
+
+WebInspector.HeapSnapshotWorkerProxy.prototype = {
+ /**
+ * @param {number} profileUid
+ * @param {function(!WebInspector.HeapSnapshotProxy)} snapshotReceivedCallback
+ * @return {!WebInspector.HeapSnapshotLoaderProxy}
+ */
+ createLoader: function(profileUid, snapshotReceivedCallback)
+ {
+ var objectId = this._nextObjectId++;
+ var proxy = new WebInspector.HeapSnapshotLoaderProxy(this, objectId, profileUid, snapshotReceivedCallback);
+ this._postMessage({callId: this._nextCallId++, disposition: "create", objectId: objectId, methodName: "WebInspector.HeapSnapshotLoader"});
+ return proxy;
+ },
+
+ dispose: function()
+ {
+ this._worker.terminate();
+ if (this._interval)
+ clearInterval(this._interval);
+ },
+
+ disposeObject: function(objectId)
+ {
+ this._postMessage({callId: this._nextCallId++, disposition: "dispose", objectId: objectId});
+ },
+
+ evaluateForTest: function(script, callback)
+ {
+ var callId = this._nextCallId++;
+ this._callbacks[callId] = callback;
+ this._postMessage({callId: callId, disposition: "evaluateForTest", source: script});
+ },
+
+ /**
+ * @param {?function(...[?])} callback
+ * @param {string} objectId
+ * @param {string} methodName
+ * @param {function(new:T, ...[?])} proxyConstructor
+ * @return {?Object}
+ * @template T
+ */
+ callFactoryMethod: function(callback, objectId, methodName, proxyConstructor)
+ {
+ var callId = this._nextCallId++;
+ var methodArguments = Array.prototype.slice.call(arguments, 4);
+ var newObjectId = this._nextObjectId++;
+
+ /**
+ * @this {WebInspector.HeapSnapshotWorkerProxy}
+ */
+ function wrapCallback(remoteResult)
+ {
+ callback(remoteResult ? new proxyConstructor(this, newObjectId) : null);
+ }
+
+ if (callback) {
+ this._callbacks[callId] = wrapCallback.bind(this);
+ this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
+ return null;
+ } else {
+ this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
+ return new proxyConstructor(this, newObjectId);
+ }
+ },
+
+ /**
+ * @param {function(*)} callback
+ * @param {string} objectId
+ * @param {string} methodName
+ */
+ callMethod: function(callback, objectId, methodName)
+ {
+ var callId = this._nextCallId++;
+ var methodArguments = Array.prototype.slice.call(arguments, 3);
+ if (callback)
+ this._callbacks[callId] = callback;
+ this._postMessage({callId: callId, disposition: "method", objectId: objectId, methodName: methodName, methodArguments: methodArguments});
+ },
+
+ startCheckingForLongRunningCalls: function()
+ {
+ if (this._interval)
+ return;
+ this._checkLongRunningCalls();
+ this._interval = setInterval(this._checkLongRunningCalls.bind(this), 300);
+ },
+
+ _checkLongRunningCalls: function()
+ {
+ for (var callId in this._previousCallbacks)
+ if (!(callId in this._callbacks))
+ delete this._previousCallbacks[callId];
+ var hasLongRunningCalls = false;
+ for (callId in this._previousCallbacks) {
+ hasLongRunningCalls = true;
+ break;
+ }
+ this.dispatchEventToListeners("wait", hasLongRunningCalls);
+ for (callId in this._callbacks)
+ this._previousCallbacks[callId] = true;
+ },
+
+ /**
+ * @param {!MessageEvent} event
+ */
+ _messageReceived: function(event)
+ {
+ var data = event.data;
+ if (data.eventName) {
+ if (this._eventHandler)
+ this._eventHandler(data.eventName, data.data);
+ return;
+ }
+ if (data.error) {
+ if (data.errorMethodName)
+ WebInspector.messageSink.addMessage(WebInspector.UIString("An error occurred when a call to method '%s' was requested", data.errorMethodName));
+ WebInspector.messageSink.addMessage(data["errorCallStack"]);
+ delete this._callbacks[data.callId];
+ return;
+ }
+ if (!this._callbacks[data.callId])
+ return;
+ var callback = this._callbacks[data.callId];
+ delete this._callbacks[data.callId];
+ callback(data.result);
+ },
+
+ _postMessage: function(message)
+ {
+ this._worker.postMessage(message);
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+
+/**
+ * @constructor
+ * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
+ * @param {number} objectId
+ */
+WebInspector.HeapSnapshotProxyObject = function(worker, objectId)
+{
+ this._worker = worker;
+ this._objectId = objectId;
+}
+
+WebInspector.HeapSnapshotProxyObject.prototype = {
+ /**
+ * @param {string} workerMethodName
+ * @param {!Array.<*>} args
+ */
+ _callWorker: function(workerMethodName, args)
+ {
+ args.splice(1, 0, this._objectId);
+ return this._worker[workerMethodName].apply(this._worker, args);
+ },
+
+ dispose: function()
+ {
+ this._worker.disposeObject(this._objectId);
+ },
+
+ disposeWorker: function()
+ {
+ this._worker.dispose();
+ },
+
+ /**
+ * @param {?function(...[?])} callback
+ * @param {string} methodName
+ * @param {function (new:T, ...[?])} proxyConstructor
+ * @param {...*} var_args
+ * @return {!T}
+ * @template T
+ */
+ callFactoryMethod: function(callback, methodName, proxyConstructor, var_args)
+ {
+ return this._callWorker("callFactoryMethod", Array.prototype.slice.call(arguments, 0));
+ },
+
+ /**
+ * @param {function(T)|undefined} callback
+ * @param {string} methodName
+ * @param {...*} var_args
+ * @return {*}
+ * @template T
+ */
+ callMethod: function(callback, methodName, var_args)
+ {
+ return this._callWorker("callMethod", Array.prototype.slice.call(arguments, 0));
+ }
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxyObject}
+ * @implements {WebInspector.OutputStream}
+ * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
+ * @param {number} objectId
+ * @param {number} profileUid
+ * @param {function(!WebInspector.HeapSnapshotProxy)} snapshotReceivedCallback
+ */
+WebInspector.HeapSnapshotLoaderProxy = function(worker, objectId, profileUid, snapshotReceivedCallback)
+{
+ WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
+ this._profileUid = profileUid;
+ this._snapshotReceivedCallback = snapshotReceivedCallback;
+}
+
+WebInspector.HeapSnapshotLoaderProxy.prototype = {
+ /**
+ * @param {string} chunk
+ * @param {function(!WebInspector.OutputStream)=} callback
+ */
+ write: function(chunk, callback)
+ {
+ this.callMethod(callback, "write", chunk);
+ },
+
+ /**
+ * @param {function()=} callback
+ */
+ close: function(callback)
+ {
+ /**
+ * @this {WebInspector.HeapSnapshotLoaderProxy}
+ */
+ function buildSnapshot()
+ {
+ if (callback)
+ callback();
+ var showHiddenData = WebInspector.settings.showAdvancedHeapSnapshotProperties.get();
+ this.callFactoryMethod(updateStaticData.bind(this), "buildSnapshot", WebInspector.HeapSnapshotProxy, showHiddenData);
+ }
+
+ /**
+ * @param {!WebInspector.HeapSnapshotProxy} snapshotProxy
+ * @this {WebInspector.HeapSnapshotLoaderProxy}
+ */
+ function updateStaticData(snapshotProxy)
+ {
+ this.dispose();
+ snapshotProxy.setProfileUid(this._profileUid);
+ snapshotProxy.updateStaticData(this._snapshotReceivedCallback.bind(this));
+ }
+
+ this.callMethod(buildSnapshot.bind(this), "close");
+ },
+
+ __proto__: WebInspector.HeapSnapshotProxyObject.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxyObject}
+ * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
+ * @param {number} objectId
+ */
+WebInspector.HeapSnapshotProxy = function(worker, objectId)
+{
+ WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
+ /** @type {?WebInspector.HeapSnapshotCommon.StaticData} */
+ this._staticData = null;
+}
+
+WebInspector.HeapSnapshotProxy.prototype = {
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} filter
+ * @param {function(!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>)} callback
+ */
+ aggregatesWithFilter: function(filter, callback)
+ {
+ this.callMethod(callback, "aggregatesWithFilter", filter);
+ },
+
+ aggregatesForDiff: function(callback)
+ {
+ this.callMethod(callback, "aggregatesForDiff");
+ },
+
+ calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates, callback)
+ {
+ this.callMethod(callback, "calculateSnapshotDiff", baseSnapshotId, baseSnapshotAggregates);
+ },
+
+ nodeClassName: function(snapshotObjectId, callback)
+ {
+ this.callMethod(callback, "nodeClassName", snapshotObjectId);
+ },
+
+ dominatorIdsForNode: function(nodeIndex, callback)
+ {
+ this.callMethod(callback, "dominatorIdsForNode", nodeIndex);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createEdgesProvider: function(nodeIndex)
+ {
+ return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {!WebInspector.HeapSnapshotProviderProxy}
+ */
+ createRetainingEdgesProvider: function(nodeIndex)
+ {
+ return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
+ },
+
+ /**
+ * @param {string} baseSnapshotId
+ * @param {string} className
+ * @return {?WebInspector.HeapSnapshotProviderProxy}
+ */
+ createAddedNodesProvider: function(baseSnapshotId, className)
+ {
+ return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className);
+ },
+
+ /**
+ * @param {!Array.<number>} nodeIndexes
+ * @return {?WebInspector.HeapSnapshotProviderProxy}
+ */
+ createDeletedNodesProvider: function(nodeIndexes)
+ {
+ return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes);
+ },
+
+ /**
+ * @param {function(*):boolean} filter
+ * @return {?WebInspector.HeapSnapshotProviderProxy}
+ */
+ createNodesProvider: function(filter)
+ {
+ return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter);
+ },
+
+ /**
+ * @param {string} className
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
+ * @return {?WebInspector.HeapSnapshotProviderProxy}
+ */
+ createNodesProviderForClass: function(className, nodeFilter)
+ {
+ return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, nodeFilter);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {?WebInspector.HeapSnapshotProviderProxy}
+ */
+ createNodesProviderForDominator: function(nodeIndex)
+ {
+ return this.callFactoryMethod(null, "createNodesProviderForDominator", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
+ },
+
+ allocationTracesTops: function(callback)
+ {
+ this.callMethod(callback, "allocationTracesTops");
+ },
+
+ /**
+ * @param {number} nodeId
+ * @param {function(!WebInspector.HeapSnapshotCommon.AllocationNodeCallers)} callback
+ */
+ allocationNodeCallers: function(nodeId, callback)
+ {
+ this.callMethod(callback, "allocationNodeCallers", nodeId);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @param {function(?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>)} callback
+ */
+ allocationStack: function(nodeIndex, callback)
+ {
+ this.callMethod(callback, "allocationStack", nodeIndex);
+ },
+
+ dispose: function()
+ {
+ throw new Error("Should never be called");
+ },
+
+ get nodeCount()
+ {
+ return this._staticData.nodeCount;
+ },
+
+ get rootNodeIndex()
+ {
+ return this._staticData.rootNodeIndex;
+ },
+
+ updateStaticData: function(callback)
+ {
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.StaticData} staticData
+ * @this {WebInspector.HeapSnapshotProxy}
+ */
+ function dataReceived(staticData)
+ {
+ this._staticData = staticData;
+ callback(this);
+ }
+ this.callMethod(dataReceived.bind(this), "updateStaticData");
+ },
+
+ /**
+ * @param {!function(!WebInspector.HeapSnapshotCommon.Statistics):void} callback
+ */
+ getStatistics: function(callback)
+ {
+ this.callMethod(callback, "getStatistics");
+ },
+
+ get totalSize()
+ {
+ return this._staticData.totalSize;
+ },
+
+ get uid()
+ {
+ return this._profileUid;
+ },
+
+ setProfileUid: function(profileUid)
+ {
+ this._profileUid = profileUid;
+ },
+
+ /**
+ * @return {number}
+ */
+ maxJSObjectId: function()
+ {
+ return this._staticData.maxJSObjectId;
+ },
+
+ __proto__: WebInspector.HeapSnapshotProxyObject.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxyObject}
+ * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
+ * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
+ * @param {number} objectId
+ */
+WebInspector.HeapSnapshotProviderProxy = function(worker, objectId)
+{
+ WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
+}
+
+WebInspector.HeapSnapshotProviderProxy.prototype = {
+ /**
+ * @override
+ * @param {number} snapshotObjectId
+ * @param {function(number)} callback
+ */
+ nodePosition: function(snapshotObjectId, callback)
+ {
+ this.callMethod(callback, "nodePosition", snapshotObjectId);
+ },
+
+ /**
+ * @override
+ * @param {function(boolean)} callback
+ */
+ isEmpty: function(callback)
+ {
+ this.callMethod(callback, "isEmpty");
+ },
+
+ /**
+ * @override
+ * @param {number} startPosition
+ * @param {number} endPosition
+ * @param {function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
+ */
+ serializeItemsRange: function(startPosition, endPosition, callback)
+ {
+ this.callMethod(callback, "serializeItemsRange", startPosition, endPosition);
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
+ * @param {function()} callback
+ */
+ sortAndRewind: function(comparator, callback)
+ {
+ this.callMethod(callback, "sortAndRewind", comparator);
+ },
+
+ __proto__: WebInspector.HeapSnapshotProxyObject.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js
new file mode 100644
index 00000000000..33148fcd4b1
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js
@@ -0,0 +1,2285 @@
+/*
+ * 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.
+ */
+
+/**
+ * FIXME: ES5 strict mode check is suppressed due to multiple uses of arguments.callee.
+ * @fileoverview
+ * @suppress {es5Strict}
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {!WebInspector.HeapProfileHeader} profile
+ */
+WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile)
+{
+ WebInspector.VBox.call(this);
+
+ this.element.classList.add("heap-snapshot-view");
+
+ profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
+ profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
+
+ if (profile._profileType.id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
+ this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
+ this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
+ }
+
+ this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200);
+ this._splitView.show(this.element);
+
+ this._containmentView = new WebInspector.VBox();
+ this._containmentView.setMinimumSize(50, 25);
+ this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(dataDisplayDelegate);
+ this._containmentDataGrid.show(this._containmentView.element);
+ this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+ this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
+
+ this._constructorsView = new WebInspector.VBox();
+ this._constructorsView.setMinimumSize(50, 25);
+
+ this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(dataDisplayDelegate);
+ this._constructorsDataGrid.show(this._constructorsView.element);
+ this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+ this._diffView = new WebInspector.VBox();
+ this._diffView.setMinimumSize(50, 25);
+
+ this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(dataDisplayDelegate);
+ this._diffDataGrid.show(this._diffView.element);
+ this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+ this._dominatorView = new WebInspector.VBox();
+ this._dominatorView.setMinimumSize(50, 25);
+ this._dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid(dataDisplayDelegate);
+ this._dominatorDataGrid.show(this._dominatorView.element);
+ this._dominatorDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+ if (WebInspector.experimentsSettings.heapAllocationProfiler.isEnabled() && profile.profileType() === WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType) {
+ this._allocationView = new WebInspector.VBox();
+ this._allocationView.setMinimumSize(50, 25);
+ this._allocationDataGrid = new WebInspector.AllocationDataGrid(dataDisplayDelegate);
+ this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
+ this._allocationDataGrid.show(this._allocationView.element);
+
+ this._allocationStackView = new WebInspector.HeapAllocationStackView();
+ this._allocationStackView.setMinimumSize(50, 25);
+
+ this._tabbedPane = new WebInspector.TabbedPane();
+ this._tabbedPane.closeableTabs = false;
+ this._tabbedPane.headerElement().classList.add("heap-object-details-header");
+ }
+
+ this._retainmentView = new WebInspector.VBox();
+ this._retainmentView.setMinimumSize(50, 21);
+ this._retainmentView.element.classList.add("retaining-paths-view");
+
+ var splitViewResizer;
+ if (this._allocationStackView) {
+ this._tabbedPane = new WebInspector.TabbedPane();
+ this._tabbedPane.closeableTabs = false;
+ this._tabbedPane.headerElement().classList.add("heap-object-details-header");
+
+ this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView);
+ this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
+
+ splitViewResizer = this._tabbedPane.headerElement();
+ this._objectDetailsView = this._tabbedPane;
+ } else {
+ var retainmentViewHeader = document.createElementWithClass("div", "heap-snapshot-view-resizer");
+ var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
+ var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
+ retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
+ this._retainmentView.element.appendChild(retainmentViewHeader);
+
+ splitViewResizer = retainmentViewHeader;
+ this._objectDetailsView = this._retainmentView;
+ }
+ this._splitView.hideDefaultResizer();
+ this._splitView.installResizer(splitViewResizer);
+
+ this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(dataDisplayDelegate);
+ this._retainmentDataGrid.show(this._retainmentView.element);
+ this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
+ this._retainmentDataGrid.reset();
+
+ this._perspectives = [];
+ this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
+ if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
+ this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
+ this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
+ if (WebInspector.settings.showAdvancedHeapSnapshotProperties.get())
+ this._perspectives.push(new WebInspector.HeapSnapshotView.DominatorPerspective());
+ if (this._allocationView)
+ this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
+ if (WebInspector.experimentsSettings.heapSnapshotStatistics.isEnabled())
+ this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
+
+ this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
+ for (var i = 0; i < this._perspectives.length; ++i)
+ this._perspectiveSelect.createOption(this._perspectives[i].title());
+
+ this._profile = profile;
+
+ this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
+ this._baseSelect.visible = false;
+ this._updateBaseOptions();
+
+ this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
+ this._filterSelect.visible = false;
+ this._updateFilterOptions();
+
+ this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
+ this._classNameFilter.visible = false;
+ this._constructorsDataGrid.setNameFilter(this._classNameFilter);
+ this._diffDataGrid.setNameFilter(this._classNameFilter);
+
+ this._selectedSizeText = new WebInspector.StatusBarText("");
+
+ this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
+
+ this._currentPerspectiveIndex = 0;
+ this._currentPerspective = this._perspectives[0];
+ this._currentPerspective.activate(this);
+ this._dataGrid = this._currentPerspective.masterGrid(this);
+
+ this._refreshView();
+}
+
+/**
+ * @constructor
+ * @param {string} title
+ */
+WebInspector.HeapSnapshotView.Perspective = function(title)
+{
+ this._title = title;
+}
+
+WebInspector.HeapSnapshotView.Perspective.prototype = {
+ /**
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView) { },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ deactivate: function(heapSnapshotView)
+ {
+ heapSnapshotView._baseSelect.visible = false;
+ heapSnapshotView._filterSelect.visible = false;
+ heapSnapshotView._classNameFilter.visible = false;
+ if (heapSnapshotView._trackingOverviewGrid)
+ heapSnapshotView._trackingOverviewGrid.detach();
+ if (heapSnapshotView._allocationView)
+ heapSnapshotView._allocationView.detach();
+ if (heapSnapshotView._statisticsView)
+ heapSnapshotView._statisticsView.detach();
+
+ heapSnapshotView._splitView.detach();
+ heapSnapshotView._splitView.detachChildViews();
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return null;
+ },
+
+ /**
+ * @return {string}
+ */
+ title: function()
+ {
+ return this._title;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ supportsSearch: function()
+ {
+ return false;
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView.Perspective}
+ */
+WebInspector.HeapSnapshotView.SummaryPerspective = function()
+{
+ WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
+}
+
+WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView)
+ {
+ heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
+ heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
+ heapSnapshotView._splitView.show(heapSnapshotView.element);
+ heapSnapshotView._filterSelect.visible = true;
+ heapSnapshotView._classNameFilter.visible = true;
+ if (heapSnapshotView._trackingOverviewGrid) {
+ heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element);
+ heapSnapshotView._trackingOverviewGrid.update();
+ heapSnapshotView._trackingOverviewGrid._updateGrid();
+ }
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return heapSnapshotView._constructorsDataGrid;
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ supportsSearch: function()
+ {
+ return true;
+ },
+
+ __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView.Perspective}
+ */
+WebInspector.HeapSnapshotView.ComparisonPerspective = function()
+{
+ WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
+}
+
+WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView)
+ {
+ heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement());
+ heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
+ heapSnapshotView._splitView.show(heapSnapshotView.element);
+ heapSnapshotView._baseSelect.visible = true;
+ heapSnapshotView._classNameFilter.visible = true;
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return heapSnapshotView._diffDataGrid;
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ supportsSearch: function()
+ {
+ return true;
+ },
+
+ __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView.Perspective}
+ */
+WebInspector.HeapSnapshotView.ContainmentPerspective = function()
+{
+ WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
+}
+
+WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView)
+ {
+ heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement());
+ heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
+ heapSnapshotView._splitView.show(heapSnapshotView.element);
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return heapSnapshotView._containmentDataGrid;
+ },
+ __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView.Perspective}
+ */
+WebInspector.HeapSnapshotView.DominatorPerspective = function()
+{
+ WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Dominators"));
+}
+
+WebInspector.HeapSnapshotView.DominatorPerspective.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView)
+ {
+ heapSnapshotView._dominatorView.show(heapSnapshotView._splitView.mainElement());
+ heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
+ heapSnapshotView._splitView.show(heapSnapshotView.element);
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return heapSnapshotView._dominatorDataGrid;
+ },
+
+ __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView.Perspective}
+ */
+WebInspector.HeapSnapshotView.AllocationPerspective = function()
+{
+ WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
+ this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
+
+ var resizer = document.createElementWithClass("div", "heap-snapshot-view-resizer");
+ var title = resizer.createChild("div", "title").createChild("span");
+ title.textContent = WebInspector.UIString("Live objects");
+ this._allocationSplitView.hideDefaultResizer();
+ this._allocationSplitView.installResizer(resizer);
+
+ this._allocationSplitView.sidebarElement().appendChild(resizer);
+}
+
+WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView)
+ {
+ heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement());
+ heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
+ heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
+ heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement());
+ this._allocationSplitView.show(heapSnapshotView.element);
+
+ heapSnapshotView._constructorsDataGrid.clear();
+ var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
+ if (selectedNode)
+ heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ deactivate: function(heapSnapshotView)
+ {
+ this._allocationSplitView.detach();
+ WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return heapSnapshotView._allocationDataGrid;
+ },
+
+ __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView.Perspective}
+ */
+WebInspector.HeapSnapshotView.StatisticsPerspective = function()
+{
+ WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
+}
+
+WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ */
+ activate: function(heapSnapshotView)
+ {
+ heapSnapshotView._statisticsView.show(heapSnapshotView.element);
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
+ * @return {?WebInspector.DataGrid}
+ */
+ masterGrid: function(heapSnapshotView)
+ {
+ return null;
+ },
+
+ __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
+}
+
+
+WebInspector.HeapSnapshotView.prototype = {
+ _refreshView: function()
+ {
+ this._profile.load(profileCallback.bind(this));
+
+ /**
+ * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function profileCallback(heapSnapshotProxy)
+ {
+ heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this));
+ var list = this._profiles();
+ var profileIndex = list.indexOf(this._profile);
+ this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
+ this._dataGrid.setDataSource(heapSnapshotProxy);
+ if (this._trackingOverviewGrid)
+ this._trackingOverviewGrid._updateGrid();
+ }
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
+ */
+ _gotStatistics: function(statistics) {
+ this._statisticsView.setTotal(statistics.total);
+ this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
+ this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
+ this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
+ this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
+ this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
+ },
+
+ _onIdsRangeChanged: function(event)
+ {
+ var minId = event.data.minId;
+ var maxId = event.data.maxId;
+ this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
+ if (this._constructorsDataGrid.snapshot)
+ this._constructorsDataGrid.setSelectionRange(minId, maxId);
+ },
+
+ get statusBarItems()
+ {
+ var result = [this._perspectiveSelect.element, this._classNameFilter.element];
+ if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
+ result.push(this._baseSelect.element, this._filterSelect.element);
+ result.push(this._selectedSizeText.element);
+ return result;
+ },
+
+ wasShown: function()
+ {
+ // FIXME: load base and current snapshots in parallel
+ this._profile.load(profileCallback.bind(this));
+
+ /**
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function profileCallback() {
+ this._profile._wasShown();
+ if (this._baseProfile)
+ this._baseProfile.load(function() { });
+ }
+ },
+
+ willHide: function()
+ {
+ this._currentSearchResultIndex = -1;
+ this._popoverHelper.hidePopover();
+ if (this.helpPopover && this.helpPopover.isShowing())
+ this.helpPopover.hide();
+ },
+
+ searchCanceled: function()
+ {
+ if (this._searchResults) {
+ for (var i = 0; i < this._searchResults.length; ++i) {
+ var node = this._searchResults[i].node;
+ delete node._searchMatched;
+ node.refresh();
+ }
+ }
+
+ delete this._searchFinishedCallback;
+ this._currentSearchResultIndex = -1;
+ this._searchResults = [];
+ },
+
+ /**
+ * @param {string} query
+ * @param {function(!WebInspector.View, number)} finishedCallback
+ */
+ performSearch: function(query, finishedCallback)
+ {
+ // Call searchCanceled since it will reset everything we need before doing a new search.
+ this.searchCanceled();
+
+ query = query.trim();
+
+ if (!query)
+ return;
+ if (!this._currentPerspective.supportsSearch())
+ return;
+
+ /**
+ * @param {boolean} found
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function didHighlight(found)
+ {
+ finishedCallback(this, found ? 1 : 0);
+ }
+
+ if (query.charAt(0) === "@") {
+ var snapshotNodeId = parseInt(query.substring(1), 10);
+ if (!isNaN(snapshotNodeId))
+ this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
+ else
+ finishedCallback(this, 0);
+ return;
+ }
+
+ this._searchFinishedCallback = finishedCallback;
+ var nameRegExp = createPlainTextSearchRegex(query, "i");
+
+ function matchesByName(gridNode) {
+ return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
+ }
+
+ function matchesQuery(gridNode)
+ {
+ delete gridNode._searchMatched;
+ if (matchesByName(gridNode)) {
+ gridNode._searchMatched = true;
+ gridNode.refresh();
+ return true;
+ }
+ return false;
+ }
+
+ var current = this._dataGrid.rootNode().children[0];
+ var depth = 0;
+ var info = {};
+
+ // Restrict to type nodes and instances.
+ const maxDepth = 1;
+
+ while (current) {
+ if (matchesQuery(current))
+ this._searchResults.push({ node: current });
+ current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
+ depth += info.depthChange;
+ }
+
+ finishedCallback(this, this._searchResults.length);
+ },
+
+ jumpToFirstSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ this._currentSearchResultIndex = 0;
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ jumpToLastSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ this._currentSearchResultIndex = (this._searchResults.length - 1);
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ jumpToNextSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ if (++this._currentSearchResultIndex >= this._searchResults.length)
+ this._currentSearchResultIndex = 0;
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ jumpToPreviousSearchResult: function()
+ {
+ if (!this._searchResults || !this._searchResults.length)
+ return;
+ if (--this._currentSearchResultIndex < 0)
+ this._currentSearchResultIndex = (this._searchResults.length - 1);
+ this._jumpToSearchResult(this._currentSearchResultIndex);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ showingFirstSearchResult: function()
+ {
+ return (this._currentSearchResultIndex === 0);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ showingLastSearchResult: function()
+ {
+ return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
+ },
+
+ /**
+ * @return {number}
+ */
+ currentSearchResultIndex: function() {
+ return this._currentSearchResultIndex;
+ },
+
+ _jumpToSearchResult: function(index)
+ {
+ var searchResult = this._searchResults[index];
+ if (!searchResult)
+ return;
+
+ var node = searchResult.node;
+ node.revealAndSelect();
+ },
+
+ refreshVisibleData: function()
+ {
+ if (!this._dataGrid)
+ return;
+ var child = this._dataGrid.rootNode().children[0];
+ while (child) {
+ child.refresh();
+ child = child.traverseNextNode(false, null, true);
+ }
+ },
+
+ _changeBase: function()
+ {
+ if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
+ return;
+
+ this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
+ var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
+ // Change set base data source only if main data source is already set.
+ if (dataGrid.snapshot)
+ this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
+
+ if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+ return;
+
+ // The current search needs to be performed again. First negate out previous match
+ // count by calling the search finished callback with a negative number of matches.
+ // Then perform the search again with the same query and callback.
+ this._searchFinishedCallback(this, -this._searchResults.length);
+ this.performSearch(this.currentQuery, this._searchFinishedCallback);
+ },
+
+ _changeFilter: function()
+ {
+ var profileIndex = this._filterSelect.selectedIndex() - 1;
+ this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
+
+ WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+ action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
+ label: this._filterSelect.selectedOption().label
+ });
+
+ if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+ return;
+
+ // The current search needs to be performed again. First negate out previous match
+ // count by calling the search finished callback with a negative number of matches.
+ // Then perform the search again with the same query and callback.
+ this._searchFinishedCallback(this, -this._searchResults.length);
+ this.performSearch(this.currentQuery, this._searchFinishedCallback);
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.ProfileHeader>}
+ */
+ _profiles: function()
+ {
+ return this._profile.profileType().getProfiles();
+ },
+
+ /**
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {?Event} event
+ */
+ populateContextMenu: function(contextMenu, event)
+ {
+ if (this._dataGrid)
+ this._dataGrid.populateContextMenu(contextMenu, event);
+ },
+
+ _selectionChanged: function(event)
+ {
+ var selectedNode = event.target.selectedNode;
+ this._setSelectedNodeForDetailsView(selectedNode);
+ this._inspectedObjectChanged(event);
+ },
+
+ _onSelectAllocationNode: function(event)
+ {
+ var selectedNode = event.target.selectedNode;
+ this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
+ this._setSelectedNodeForDetailsView(null);
+ },
+
+ _inspectedObjectChanged: function(event)
+ {
+ var selectedNode = event.target.selectedNode;
+ if (!this._profile.fromFile() && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
+ ConsoleAgent.addInspectedHeapObject(selectedNode.snapshotNodeId);
+ },
+
+ /**
+ * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
+ */
+ _setSelectedNodeForDetailsView: function(nodeItem)
+ {
+ var dataSource = nodeItem && nodeItem.retainersDataSource();
+ if (dataSource) {
+ this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
+ if (this._allocationStackView)
+ this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex)
+ } else {
+ if (this._allocationStackView)
+ this._allocationStackView.clear();
+ this._retainmentDataGrid.reset();
+ }
+ },
+
+ /**
+ * @param {string} perspectiveTitle
+ * @param {function()} callback
+ */
+ _changePerspectiveAndWait: function(perspectiveTitle, callback)
+ {
+ var perspectiveIndex = null;
+ for (var i = 0; i < this._perspectives.length; ++i) {
+ if (this._perspectives[i].title() === perspectiveTitle) {
+ perspectiveIndex = i;
+ break;
+ }
+ }
+ if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
+ setTimeout(callback, 0);
+ return;
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function dataGridContentShown(event)
+ {
+ var dataGrid = event.data;
+ dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
+ if (dataGrid === this._dataGrid)
+ callback();
+ }
+ this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
+
+ this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
+ this._changePerspective(perspectiveIndex);
+ },
+
+ _updateDataSourceAndView: function()
+ {
+ var dataGrid = this._dataGrid;
+ if (!dataGrid || dataGrid.snapshot)
+ return;
+
+ this._profile.load(didLoadSnapshot.bind(this));
+
+ /**
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function didLoadSnapshot(snapshotProxy)
+ {
+ if (this._dataGrid !== dataGrid)
+ return;
+ if (dataGrid.snapshot !== snapshotProxy)
+ dataGrid.setDataSource(snapshotProxy);
+ if (dataGrid === this._diffDataGrid) {
+ if (!this._baseProfile)
+ this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
+ this._baseProfile.load(didLoadBaseSnaphot.bind(this));
+ }
+ }
+
+ /**
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function didLoadBaseSnaphot(baseSnapshotProxy)
+ {
+ if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
+ this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
+ }
+ },
+
+ _onSelectedPerspectiveChanged: function(event)
+ {
+ this._changePerspective(event.target.selectedIndex);
+ // FIXME: This is needed by CodeSchool extension.
+ this._onSelectedViewChanged(event);
+ },
+
+ _onSelectedViewChanged: function(event)
+ {
+ },
+
+ _changePerspective: function(selectedIndex)
+ {
+ if (selectedIndex === this._currentPerspectiveIndex)
+ return;
+
+ this._currentPerspectiveIndex = selectedIndex;
+
+ this._currentPerspective.deactivate(this);
+ var perspective = this._perspectives[selectedIndex];
+ this._currentPerspective = perspective;
+ this._dataGrid = perspective.masterGrid(this);
+ perspective.activate(this);
+
+ this.refreshVisibleData();
+ if (this._dataGrid)
+ this._dataGrid.updateWidths();
+
+ this._updateDataSourceAndView();
+
+ if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+ return;
+
+ // The current search needs to be performed again. First negate out previous match
+ // count by calling the search finished callback with a negative number of matches.
+ // Then perform the search again the with same query and callback.
+ this._searchFinishedCallback(this, -this._searchResults.length);
+ this.performSearch(this.currentQuery, this._searchFinishedCallback);
+ },
+
+ /**
+ * @param {string} perspectiveName
+ * @param {number} snapshotObjectId
+ */
+ highlightLiveObject: function(perspectiveName, snapshotObjectId)
+ {
+ this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
+
+ /**
+ * @this {WebInspector.HeapSnapshotView}
+ */
+ function didChangePerspective()
+ {
+ this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject);
+ }
+
+ function didHighlightObject(found)
+ {
+ if (!found)
+ WebInspector.messageSink.addErrorMessage("Cannot find corresponding heap snapshot node", true);
+ }
+ },
+
+ _getHoverAnchor: function(target)
+ {
+ var span = target.enclosingNodeOrSelfWithNodeName("span");
+ if (!span)
+ return;
+ var row = target.enclosingNodeOrSelfWithNodeName("tr");
+ if (!row)
+ return;
+ span.node = row._dataGridNode;
+ return span;
+ },
+
+ _resolveObjectForPopover: function(element, showCallback, objectGroupName)
+ {
+ if (this._profile.fromFile())
+ return;
+ element.node.queryObjectContent(showCallback, objectGroupName);
+ },
+
+ _updateBaseOptions: function()
+ {
+ var list = this._profiles();
+ // We're assuming that snapshots can only be added.
+ if (this._baseSelect.size() === list.length)
+ return;
+
+ for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
+ var title = list[i].title;
+ this._baseSelect.createOption(title);
+ }
+ },
+
+ _updateFilterOptions: function()
+ {
+ var list = this._profiles();
+ // We're assuming that snapshots can only be added.
+ if (this._filterSelect.size() - 1 === list.length)
+ return;
+
+ if (!this._filterSelect.size())
+ this._filterSelect.createOption(WebInspector.UIString("All objects"));
+
+ for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
+ var title = list[i].title;
+ if (!i)
+ title = WebInspector.UIString("Objects allocated before %s", title);
+ else
+ title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
+ this._filterSelect.createOption(title);
+ }
+ },
+
+ _updateControls: function()
+ {
+ this._updateBaseOptions();
+ this._updateFilterOptions();
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onReceiveSnapshot: function(event)
+ {
+ this._updateControls();
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onProfileHeaderRemoved: function(event)
+ {
+ var profile = event.data;
+ if (this._profile === profile) {
+ this.detach();
+ this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
+ this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
+ this.dispose();
+ } else {
+ this._updateControls();
+ }
+ },
+
+ dispose: function()
+ {
+ if (this._allocationStackView) {
+ this._allocationStackView.clear();
+ this._allocationDataGrid.dispose();
+ }
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @constructor
+ * @implements {HeapProfilerAgent.Dispatcher}
+ */
+WebInspector.HeapProfilerDispatcher = function()
+{
+ this._dispatchers = [];
+ InspectorBackend.registerHeapProfilerDispatcher(this);
+}
+
+WebInspector.HeapProfilerDispatcher.prototype = {
+ /**
+ * @param {!HeapProfilerAgent.Dispatcher} dispatcher
+ */
+ register: function(dispatcher)
+ {
+ this._dispatchers.push(dispatcher);
+ },
+
+ _genericCaller: function(eventName)
+ {
+ var args = Array.prototype.slice.call(arguments.callee.caller.arguments);
+ for (var i = 0; i < this._dispatchers.length; ++i)
+ this._dispatchers[i][eventName].apply(this._dispatchers[i], args);
+ },
+
+ /**
+ * @override
+ * @param {!Array.<number>} samples
+ */
+ heapStatsUpdate: function(samples)
+ {
+ this._genericCaller("heapStatsUpdate");
+ },
+
+ /**
+ * @override
+ * @param {number} lastSeenObjectId
+ * @param {number} timestamp
+ */
+ lastSeenObjectId: function(lastSeenObjectId, timestamp)
+ {
+ this._genericCaller("lastSeenObjectId");
+ },
+
+ /**
+ * @override
+ * @param {string} chunk
+ */
+ addHeapSnapshotChunk: function(chunk)
+ {
+ this._genericCaller("addHeapSnapshotChunk");
+ },
+
+ /**
+ * @override
+ * @param {number} done
+ * @param {number} total
+ * @param {boolean=} finished
+ */
+ reportHeapSnapshotProgress: function(done, total, finished)
+ {
+ this._genericCaller("reportHeapSnapshotProgress");
+ },
+
+ /**
+ * @override
+ */
+ resetProfiles: function()
+ {
+ this._genericCaller("resetProfiles");
+ }
+}
+
+WebInspector.HeapProfilerDispatcher._dispatcher = new WebInspector.HeapProfilerDispatcher();
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ * @implements {HeapProfilerAgent.Dispatcher}
+ * @param {string=} id
+ * @param {string=} title
+ */
+WebInspector.HeapSnapshotProfileType = function(id, title)
+{
+ WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
+ WebInspector.HeapProfilerDispatcher._dispatcher.register(this);
+}
+
+WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
+WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
+
+WebInspector.HeapSnapshotProfileType.prototype = {
+ /**
+ * @override
+ * @return {string}
+ */
+ fileExtension: function()
+ {
+ return ".heapsnapshot";
+ },
+
+ get buttonTooltip()
+ {
+ return WebInspector.UIString("Take heap snapshot.");
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ isInstantProfile: function()
+ {
+ return true;
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ buttonClicked: function()
+ {
+ this._takeHeapSnapshot(function() {});
+ WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
+ return false;
+ },
+
+ /**
+ * @override
+ * @param {!Array.<number>} samples
+ */
+ heapStatsUpdate: function(samples)
+ {
+ },
+
+ /**
+ * @override
+ * @param {number} lastSeenObjectId
+ * @param {number} timestamp
+ */
+ lastSeenObjectId: function(lastSeenObjectId, timestamp)
+ {
+ },
+
+ get treeItemTitle()
+ {
+ return WebInspector.UIString("HEAP SNAPSHOTS");
+ },
+
+ get description()
+ {
+ return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
+ },
+
+ /**
+ * @override
+ * @param {string} title
+ * @return {!WebInspector.ProfileHeader}
+ */
+ createProfileLoadedFromFile: function(title)
+ {
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ return new WebInspector.HeapProfileHeader(target, this, title);
+ },
+
+ _takeHeapSnapshot: function(callback)
+ {
+ if (this.profileBeingRecorded())
+ return;
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ var profile = new WebInspector.HeapProfileHeader(target, this);
+ this.setProfileBeingRecorded(profile);
+ this.addProfile(profile);
+ profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
+
+ /**
+ * @param {?string} error
+ * @this {WebInspector.HeapSnapshotProfileType}
+ */
+ function didTakeHeapSnapshot(error)
+ {
+ var profile = this._profileBeingRecorded;
+ profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
+ profile._finishLoad();
+ this.setProfileBeingRecorded(null);
+ this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
+ callback();
+ }
+ HeapProfilerAgent.takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
+ },
+
+ /**
+ * @override
+ * @param {string} chunk
+ */
+ addHeapSnapshotChunk: function(chunk)
+ {
+ if (!this.profileBeingRecorded())
+ return;
+ this.profileBeingRecorded().transferChunk(chunk);
+ },
+
+ /**
+ * @override
+ * @param {number} done
+ * @param {number} total
+ * @param {boolean=} finished
+ */
+ reportHeapSnapshotProgress: function(done, total, finished)
+ {
+ var profile = this.profileBeingRecorded();
+ if (!profile)
+ return;
+ profile.updateStatus(WebInspector.UIString("%.0f%", (done / total) * 100), true);
+ if (finished)
+ profile._prepareToLoad();
+ },
+
+ /**
+ * @override
+ */
+ resetProfiles: function()
+ {
+ this._reset();
+ },
+
+ _snapshotReceived: function(profile)
+ {
+ if (this._profileBeingRecorded === profile)
+ this.setProfileBeingRecorded(null);
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
+ },
+
+ __proto__: WebInspector.ProfileType.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProfileType}
+ */
+WebInspector.TrackingHeapSnapshotProfileType = function()
+{
+ WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
+}
+
+WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
+
+WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
+WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
+WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
+
+WebInspector.TrackingHeapSnapshotProfileType.prototype = {
+
+ /**
+ * @override
+ * @param {!Array.<number>} samples
+ */
+ heapStatsUpdate: function(samples)
+ {
+ if (!this._profileSamples)
+ return;
+ var index;
+ for (var i = 0; i < samples.length; i += 3) {
+ index = samples[i];
+ var count = samples[i+1];
+ var size = samples[i+2];
+ this._profileSamples.sizes[index] = size;
+ if (!this._profileSamples.max[index])
+ this._profileSamples.max[index] = size;
+ }
+ },
+
+ /**
+ * @override
+ * @param {number} lastSeenObjectId
+ * @param {number} timestamp
+ */
+ lastSeenObjectId: function(lastSeenObjectId, timestamp)
+ {
+ var profileSamples = this._profileSamples;
+ if (!profileSamples)
+ return;
+ var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
+ profileSamples.ids[currentIndex] = lastSeenObjectId;
+ if (!profileSamples.max[currentIndex]) {
+ profileSamples.max[currentIndex] = 0;
+ profileSamples.sizes[currentIndex] = 0;
+ }
+ profileSamples.timestamps[currentIndex] = timestamp;
+ if (profileSamples.totalTime < timestamp - profileSamples.timestamps[0])
+ profileSamples.totalTime *= 2;
+ this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
+ this._profileBeingRecorded.updateStatus(null, true);
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ hasTemporaryView: function()
+ {
+ return true;
+ },
+
+ get buttonTooltip()
+ {
+ return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ isInstantProfile: function()
+ {
+ return false;
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ buttonClicked: function()
+ {
+ return this._toggleRecording();
+ },
+
+ _startRecordingProfile: function()
+ {
+ if (this.profileBeingRecorded())
+ return;
+ this._addNewProfile();
+ HeapProfilerAgent.startTrackingHeapObjects(WebInspector.experimentsSettings.heapAllocationProfiler.isEnabled());
+ },
+
+ _addNewProfile: function()
+ {
+ var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this));
+ this._lastSeenIndex = -1;
+ this._profileSamples = {
+ 'sizes': [],
+ 'ids': [],
+ 'timestamps': [],
+ 'max': [],
+ 'totalTime': 30000
+ };
+ this._profileBeingRecorded._profileSamples = this._profileSamples;
+ this._recording = true;
+ this.addProfile(this._profileBeingRecorded);
+ this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
+ this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
+ },
+
+ _stopRecordingProfile: function()
+ {
+ this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
+ /**
+ * @param {?string} error
+ * @this {WebInspector.HeapSnapshotProfileType}
+ */
+ function didTakeHeapSnapshot(error)
+ {
+ var profile = this._profileBeingRecorded;
+ if (!profile)
+ return;
+ profile._finishLoad();
+ this._profileSamples = null;
+ this.setProfileBeingRecorded(null);
+ this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
+ }
+
+ HeapProfilerAgent.stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
+ this._recording = false;
+ this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
+ },
+
+ _toggleRecording: function()
+ {
+ if (this._recording)
+ this._stopRecordingProfile();
+ else
+ this._startRecordingProfile();
+ return this._recording;
+ },
+
+ get treeItemTitle()
+ {
+ return WebInspector.UIString("HEAP TIMELINES");
+ },
+
+ get description()
+ {
+ return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
+ },
+
+ /**
+ * @override
+ */
+ resetProfiles: function()
+ {
+ var wasRecording = this._recording;
+ // Clear current profile to avoid stopping backend.
+ this.setProfileBeingRecorded(null);
+ WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this);
+ this._profileSamples = null;
+ this._lastSeenIndex = -1;
+ if (wasRecording)
+ this._addNewProfile();
+ },
+
+ /**
+ * @override
+ */
+ profileBeingRecordedRemoved: function()
+ {
+ this._stopRecordingProfile();
+ this._profileSamples = null;
+ },
+
+ __proto__: WebInspector.HeapSnapshotProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @param {!WebInspector.Target} target
+ * @param {!WebInspector.HeapSnapshotProfileType} type
+ * @param {string=} title
+ */
+WebInspector.HeapProfileHeader = function(target, type, title)
+{
+ WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type._nextProfileUid));
+ this.maxJSObjectId = -1;
+ /**
+ * @type {?WebInspector.HeapSnapshotWorkerProxy}
+ */
+ this._workerProxy = null;
+ /**
+ * @type {?WebInspector.OutputStream}
+ */
+ this._receiver = null;
+ /**
+ * @type {?WebInspector.HeapSnapshotProxy}
+ */
+ this._snapshotProxy = null;
+ /**
+ * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
+ */
+ this._loadCallbacks = [];
+ this._totalNumberOfChunks = 0;
+ this._bufferedWriter = null;
+}
+
+WebInspector.HeapProfileHeader.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @return {!WebInspector.ProfileSidebarTreeElement}
+ */
+ createSidebarTreeElement: function(dataDisplayDelegate)
+ {
+ return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
+ },
+
+ /**
+ * @override
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @return {!WebInspector.HeapSnapshotView}
+ */
+ createView: function(dataDisplayDelegate)
+ {
+ return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this);
+ },
+
+ /**
+ * @override
+ * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
+ */
+ load: function(callback)
+ {
+ if (this.uid === -1)
+ return;
+ if (this._snapshotProxy) {
+ callback(this._snapshotProxy);
+ return;
+ }
+ this._loadCallbacks.push(callback);
+ },
+
+ _prepareToLoad: function()
+ {
+ console.assert(!this._receiver, "Already loading");
+ this._setupWorker();
+ this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
+ },
+
+ _finishLoad: function()
+ {
+ if (!this._wasDisposed)
+ this._receiver.close(function() {});
+ if (this._bufferedWriter) {
+ this._bufferedWriter.close(this._didWriteToTempFile.bind(this));
+ this._bufferedWriter = null;
+ }
+ },
+
+ _didWriteToTempFile: function(tempFile)
+ {
+ if (this._wasDisposed) {
+ if (tempFile)
+ tempFile.remove();
+ return;
+ }
+ this._tempFile = tempFile;
+ if (!tempFile)
+ this._failedToCreateTempFile = true;
+ if (this._onTempFileReady) {
+ this._onTempFileReady();
+ this._onTempFileReady = null;
+ }
+ },
+
+ _setupWorker: function()
+ {
+ /**
+ * @this {WebInspector.HeapProfileHeader}
+ */
+ function setProfileWait(event)
+ {
+ this.updateStatus(null, event.data);
+ }
+ console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
+ this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
+ this._workerProxy.addEventListener("wait", setProfileWait, this);
+ this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
+ },
+
+ /**
+ * @param {string} eventName
+ * @param {*} data
+ */
+ _handleWorkerEvent: function(eventName, data)
+ {
+ if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
+ return;
+ var subtitle = /** @type {string} */ (data);
+ this.updateStatus(subtitle);
+ },
+
+ /**
+ * @override
+ */
+ dispose: function()
+ {
+ if (this._workerProxy)
+ this._workerProxy.dispose();
+ this.removeTempFile();
+ this._wasDisposed = true;
+ },
+
+ _didCompleteSnapshotTransfer: function()
+ {
+ if (!this._snapshotProxy)
+ return;
+ this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
+ },
+
+ /**
+ * @param {string} chunk
+ */
+ transferChunk: function(chunk)
+ {
+ if (!this._bufferedWriter)
+ this._bufferedWriter = new WebInspector.BufferedTempFileWriter("heap-profiler", this.uid);
+ this._bufferedWriter.write(chunk);
+
+ ++this._totalNumberOfChunks;
+ this._receiver.write(chunk, function() {});
+ },
+
+ _snapshotReceived: function(snapshotProxy)
+ {
+ if (this._wasDisposed)
+ return;
+ this._receiver = null;
+ this._snapshotProxy = snapshotProxy;
+ this.maxJSObjectId = snapshotProxy.maxJSObjectId();
+ this._didCompleteSnapshotTransfer();
+ this._workerProxy.startCheckingForLongRunningCalls();
+ this.notifySnapshotReceived();
+ },
+
+ notifySnapshotReceived: function()
+ {
+ for (var i = 0; i < this._loadCallbacks.length; i++)
+ this._loadCallbacks[i](this._snapshotProxy);
+ this._loadCallbacks = null;
+ this._profileType._snapshotReceived(this);
+ if (this.canSaveToFile())
+ this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
+ },
+
+ // Hook point for tests.
+ _wasShown: function()
+ {
+ },
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ canSaveToFile: function()
+ {
+ return !this.fromFile() && this._snapshotProxy;
+ },
+
+ /**
+ * @override
+ */
+ saveToFile: function()
+ {
+ var fileOutputStream = new WebInspector.FileOutputStream();
+
+ /**
+ * @param {boolean} accepted
+ * @this {WebInspector.HeapProfileHeader}
+ */
+ function onOpen(accepted)
+ {
+ if (!accepted)
+ return;
+ if (this._failedToCreateTempFile) {
+ WebInspector.messageSink.addErrorMessage("Failed to open temp file with heap snapshot");
+ fileOutputStream.close();
+ } else if (this._tempFile) {
+ var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
+ this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
+ } else {
+ this._onTempFileReady = onOpen.bind(this, accepted);
+ this._updateSaveProgress(0, 1);
+ }
+ }
+ this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
+ fileOutputStream.open(this._fileName, onOpen.bind(this));
+ },
+
+ _updateSaveProgress: function(value, total)
+ {
+ var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
+ this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
+ },
+
+ /**
+ * @override
+ * @param {!File} file
+ */
+ loadFromFile: function(file)
+ {
+ this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
+ this._setupWorker();
+ var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
+ var fileReader = this._createFileReader(file, delegate);
+ fileReader.start(this._receiver);
+ },
+
+ _createFileReader: function(file, delegate)
+ {
+ return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
+ },
+
+ __proto__: WebInspector.ProfileHeader.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStreamDelegate}
+ */
+WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
+{
+ this._snapshotHeader = snapshotHeader;
+}
+
+WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
+ onTransferStarted: function()
+ {
+ },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ */
+ onChunkTransferred: function(reader)
+ {
+ },
+
+ onTransferFinished: function()
+ {
+ },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ * @param {?Event} e
+ */
+ onError: function (reader, e)
+ {
+ var subtitle;
+ switch(e.target.error.code) {
+ case e.target.error.NOT_FOUND_ERR:
+ subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
+ break;
+ case e.target.error.NOT_READABLE_ERR:
+ subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
+ break;
+ case e.target.error.ABORT_ERR:
+ return;
+ default:
+ subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
+ }
+ this._snapshotHeader.updateStatus(subtitle);
+ }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStreamDelegate}
+ * @param {!WebInspector.HeapProfileHeader} profileHeader
+ */
+WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
+{
+ this._profileHeader = profileHeader;
+}
+
+WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
+ onTransferStarted: function()
+ {
+ this._profileHeader._updateSaveProgress(0, 1);
+ },
+
+ onTransferFinished: function()
+ {
+ this._profileHeader._didCompleteSnapshotTransfer();
+ },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ */
+ onChunkTransferred: function(reader)
+ {
+ this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
+ },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ * @param {?Event} event
+ */
+ onError: function(reader, event)
+ {
+ WebInspector.messageSink.addErrorMessage("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event).message);
+ this.onTransferFinished();
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
+ */
+WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
+{
+ WebInspector.VBox.call(this);
+ this.registerRequiredCSS("flameChart.css");
+ this.element.id = "heap-recording-view";
+ this.element.classList.add("heap-tracking-overview");
+
+ this._overviewContainer = this.element.createChild("div", "overview-container");
+ this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
+ this._overviewGrid.element.classList.add("fill");
+
+ this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
+ this._overviewContainer.appendChild(this._overviewGrid.element);
+ this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
+ this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
+
+ this._profileSamples = heapProfileHeader._profileSamples;
+ if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
+ this._profileType = heapProfileHeader._profileType;
+ this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
+ this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
+ }
+ var timestamps = this._profileSamples.timestamps;
+ var totalTime = this._profileSamples.totalTime;
+ this._windowLeft = 0.0;
+ this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
+ this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
+ this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
+ this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
+}
+
+WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
+
+WebInspector.HeapTrackingOverviewGrid.prototype = {
+ _onStopTracking: function(event)
+ {
+ this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
+ this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
+ },
+
+ _onHeapStatsUpdate: function(event)
+ {
+ this._profileSamples = event.data;
+ this._scheduleUpdate();
+ },
+
+ /**
+ * @param {number} width
+ * @param {number} height
+ */
+ _drawOverviewCanvas: function(width, height)
+ {
+ if (!this._profileSamples)
+ return;
+ var profileSamples = this._profileSamples;
+ var sizes = profileSamples.sizes;
+ var topSizes = profileSamples.max;
+ var timestamps = profileSamples.timestamps;
+ var startTime = timestamps[0];
+ var endTime = timestamps[timestamps.length - 1];
+
+ var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
+ var maxSize = 0;
+ /**
+ * @param {!Array.<number>} sizes
+ * @param {function(number, number):void} callback
+ */
+ function aggregateAndCall(sizes, callback)
+ {
+ var size = 0;
+ var currentX = 0;
+ for (var i = 1; i < timestamps.length; ++i) {
+ var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
+ if (x !== currentX) {
+ if (size)
+ callback(currentX, size);
+ size = 0;
+ currentX = x;
+ }
+ size += sizes[i];
+ }
+ callback(currentX, size);
+ }
+
+ /**
+ * @param {number} x
+ * @param {number} size
+ */
+ function maxSizeCallback(x, size)
+ {
+ maxSize = Math.max(maxSize, size);
+ }
+
+ aggregateAndCall(sizes, maxSizeCallback);
+
+ var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
+
+ this._overviewCanvas.width = width * window.devicePixelRatio;
+ this._overviewCanvas.height = height * window.devicePixelRatio;
+ this._overviewCanvas.style.width = width + "px";
+ this._overviewCanvas.style.height = height + "px";
+
+ var context = this._overviewCanvas.getContext("2d");
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
+
+ context.beginPath();
+ context.lineWidth = 2;
+ context.strokeStyle = "rgba(192, 192, 192, 0.6)";
+ var currentX = (endTime - startTime) * scaleFactor;
+ context.moveTo(currentX, height - 1);
+ context.lineTo(currentX, 0);
+ context.stroke();
+ context.closePath();
+
+ var gridY;
+ var gridValue;
+ var gridLabelHeight = 14;
+ if (yScaleFactor) {
+ const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
+ // The round value calculation is a bit tricky, because
+ // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
+ // e.g. a round value 10KB is 10240 bytes.
+ gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
+ gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
+ if (gridValue * 5 <= maxGridValue)
+ gridValue *= 5;
+ gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
+ context.beginPath();
+ context.lineWidth = 1;
+ context.strokeStyle = "rgba(0, 0, 0, 0.2)";
+ context.moveTo(0, gridY);
+ context.lineTo(width, gridY);
+ context.stroke();
+ context.closePath();
+ }
+
+ /**
+ * @param {number} x
+ * @param {number} size
+ */
+ function drawBarCallback(x, size)
+ {
+ context.moveTo(x, height - 1);
+ context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
+ }
+
+ context.beginPath();
+ context.lineWidth = 2;
+ context.strokeStyle = "rgba(192, 192, 192, 0.6)";
+ aggregateAndCall(topSizes, drawBarCallback);
+ context.stroke();
+ context.closePath();
+
+ context.beginPath();
+ context.lineWidth = 2;
+ context.strokeStyle = "rgba(0, 0, 192, 0.8)";
+ aggregateAndCall(sizes, drawBarCallback);
+ context.stroke();
+ context.closePath();
+
+ if (gridValue) {
+ var label = Number.bytesToString(gridValue);
+ var labelPadding = 4;
+ var labelX = 0;
+ var labelY = gridY - 0.5;
+ var labelWidth = 2 * labelPadding + context.measureText(label).width;
+ context.beginPath();
+ context.textBaseline = "bottom";
+ context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
+ context.fillStyle = "rgba(255, 255, 255, 0.75)";
+ context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
+ context.fillStyle = "rgb(64, 64, 64)";
+ context.fillText(label, labelX + labelPadding, labelY);
+ context.fill();
+ context.closePath();
+ }
+ },
+
+ onResize: function()
+ {
+ this._updateOverviewCanvas = true;
+ this._scheduleUpdate();
+ },
+
+ _onWindowChanged: function()
+ {
+ if (!this._updateGridTimerId)
+ this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
+ },
+
+ _scheduleUpdate: function()
+ {
+ if (this._updateTimerId)
+ return;
+ this._updateTimerId = setTimeout(this.update.bind(this), 10);
+ },
+
+ _updateBoundaries: function()
+ {
+ this._windowLeft = this._overviewGrid.windowLeft();
+ this._windowRight = this._overviewGrid.windowRight();
+ this._windowWidth = this._windowRight - this._windowLeft;
+ },
+
+ update: function()
+ {
+ this._updateTimerId = null;
+ if (!this.isShowing())
+ return;
+ this._updateBoundaries();
+ this._overviewCalculator._updateBoundaries(this);
+ this._overviewGrid.updateDividers(this._overviewCalculator);
+ this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
+ },
+
+ _updateGrid: function()
+ {
+ this._updateGridTimerId = 0;
+ this._updateBoundaries();
+ var ids = this._profileSamples.ids;
+ var timestamps = this._profileSamples.timestamps;
+ var sizes = this._profileSamples.sizes;
+ var startTime = timestamps[0];
+ var totalTime = this._profileSamples.totalTime;
+ var timeLeft = startTime + totalTime * this._windowLeft;
+ var timeRight = startTime + totalTime * this._windowRight;
+ var minId = 0;
+ var maxId = ids[ids.length - 1] + 1;
+ var size = 0;
+ for (var i = 0; i < timestamps.length; ++i) {
+ if (!timestamps[i])
+ continue;
+ if (timestamps[i] > timeRight)
+ break;
+ maxId = ids[i];
+ if (timestamps[i] < timeLeft) {
+ minId = ids[i];
+ continue;
+ }
+ size += sizes[i];
+ }
+
+ this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+
+/**
+ * @constructor
+ */
+WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
+{
+ this._lastUpdate = 0;
+ this._currentScale = 0.0;
+}
+
+WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
+ /**
+ * @param {number} target
+ * @return {number}
+ */
+ nextScale: function(target) {
+ target = target || this._currentScale;
+ if (this._currentScale) {
+ var now = Date.now();
+ var timeDeltaMs = now - this._lastUpdate;
+ this._lastUpdate = now;
+ var maxChangePerSec = 20;
+ var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
+ var scaleChange = target / this._currentScale;
+ this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
+ } else
+ this._currentScale = target;
+ return this._currentScale;
+ }
+}
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
+{
+}
+
+WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
+ /**
+ * @return {number}
+ */
+ paddingLeft: function()
+ {
+ return 0;
+ },
+
+ /**
+ * @param {!WebInspector.HeapTrackingOverviewGrid} chart
+ */
+ _updateBoundaries: function(chart)
+ {
+ this._minimumBoundaries = 0;
+ this._maximumBoundaries = chart._profileSamples.totalTime;
+ this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
+ },
+
+ /**
+ * @param {number} time
+ * @return {number}
+ */
+ computePosition: function(time)
+ {
+ return (time - this._minimumBoundaries) * this._xScaleFactor;
+ },
+
+ /**
+ * @param {number} value
+ * @param {number=} precision
+ * @return {string}
+ */
+ formatTime: function(value, precision)
+ {
+ return Number.secondsToString(value / 1000, !!precision);
+ },
+
+ /**
+ * @return {number}
+ */
+ maximumBoundary: function()
+ {
+ return this._maximumBoundaries;
+ },
+
+ /**
+ * @return {number}
+ */
+ minimumBoundary: function()
+ {
+ return this._minimumBoundaries;
+ },
+
+ /**
+ * @return {number}
+ */
+ zeroTime: function()
+ {
+ return this._minimumBoundaries;
+ },
+
+ /**
+ * @return {number}
+ */
+ boundarySpan: function()
+ {
+ return this._maximumBoundaries - this._minimumBoundaries;
+ }
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ */
+WebInspector.HeapSnapshotStatisticsView = function()
+{
+ WebInspector.VBox.call(this);
+ this.setMinimumSize(50, 25);
+ this._pieChart = new WebInspector.PieChart();
+ this._pieChart.setSize(150);
+ this.element.appendChild(this._pieChart.element);
+ this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
+}
+
+WebInspector.HeapSnapshotStatisticsView.prototype = {
+ /**
+ * @param {number} value
+ */
+ setTotal: function(value)
+ {
+ this._pieChart.setTotal(value);
+ },
+
+ /**
+ * @param {number} value
+ * @param {string} name
+ * @param {string=} color
+ */
+ addRecord: function(value, name, color)
+ {
+ if (color)
+ this._pieChart.addSlice(value, color);
+
+ var node = this._labels.createChild("div");
+ var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
+ var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
+ var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
+ if (color)
+ swatchDiv.style.backgroundColor = color;
+ else
+ swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
+ nameDiv.textContent = name;
+ sizeDiv.textContent = WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.HeapAllocationStackView = function()
+{
+ WebInspector.View.call(this);
+ this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ this._linkifier = new WebInspector.Linkifier();
+}
+
+WebInspector.HeapAllocationStackView.prototype = {
+ /**
+ * @param {!WebInspector.HeapSnapshotProxy} snapshot
+ * @param {number} snapshotNodeIndex
+ */
+ setAllocatedObject: function(snapshot, snapshotNodeIndex)
+ {
+ this.clear();
+ snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this));
+ },
+
+ clear: function()
+ {
+ this.element.removeChildren();
+ this._linkifier.reset();
+ },
+
+ /**
+ * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
+ */
+ _didReceiveAllocationStack: function(frames)
+ {
+ if (!frames) {
+ var stackDiv = this.element.createChild("div", "no-heap-allocation-stack");
+ stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
+ return;
+ }
+
+ var stackDiv = this.element.createChild("div", "heap-allocation-stack");
+ for (var i = 0; i < frames.length; i++) {
+ var frame = frames[i];
+ var frameDiv = stackDiv.createChild("div", "stack-frame");
+ var name = frameDiv.createChild("div");
+ name.textContent = frame.functionName;
+ if (frame.scriptId) {
+ var urlElement;
+ var rawLocation = new WebInspector.DebuggerModel.Location(this._target, String(frame.scriptId), frame.line - 1, frame.column - 1);
+ if (rawLocation)
+ urlElement = this._linkifier.linkifyRawLocation(rawLocation);
+ if (!urlElement)
+ urlElement = this._linkifier.linkifyLocation(this._target, frame.scriptName, frame.line - 1, frame.column - 1);
+ frameDiv.appendChild(urlElement);
+ }
+ }
+ },
+
+ __proto__: WebInspector.View.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
new file mode 100644
index 00000000000..a36592873db
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
@@ -0,0 +1,232 @@
+/*
+ * 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.VBox}
+ * @param {!WebInspector.ProfilesPanel} profilesPanel
+ */
+WebInspector.ProfileLauncherView = function(profilesPanel)
+{
+ WebInspector.VBox.call(this);
+
+ this._panel = profilesPanel;
+
+ this.element.classList.add("profile-launcher-view");
+ this.element.classList.add("panel-enabler-view");
+
+ this._contentElement = this.element.createChild("div", "profile-launcher-view-content");
+ this._innerContentElement = this._contentElement.createChild("div");
+
+ this._controlButton = this._contentElement.createChild("button", "control-profiling");
+ this._controlButton.addEventListener("click", this._controlButtonClicked.bind(this), false);
+ this._recordButtonEnabled = true;
+
+ this._loadButton = this._contentElement.createChild("button", "load-profile");
+ this._loadButton.textContent = WebInspector.UIString("Load");
+ this._loadButton.addEventListener("click", this._loadButtonClicked.bind(this), false);
+}
+
+WebInspector.ProfileLauncherView.prototype = {
+ /**
+ * @param {!WebInspector.ProfileType} profileType
+ */
+ addProfileType: function(profileType)
+ {
+ var descriptionElement = this._innerContentElement.createChild("h1");
+ descriptionElement.textContent = profileType.description;
+ var decorationElement = profileType.decorationElement();
+ if (decorationElement)
+ this._innerContentElement.appendChild(decorationElement);
+ this._isInstantProfile = profileType.isInstantProfile();
+ this._isEnabled = profileType.isEnabled();
+ this._profileTypeId = profileType.id;
+ },
+
+ _controlButtonClicked: function()
+ {
+ this._panel.toggleRecordButton();
+ },
+
+ _loadButtonClicked: function()
+ {
+ this._panel.showLoadFromFileDialog();
+ },
+
+ _updateControls: function()
+ {
+ if (this._isEnabled && this._recordButtonEnabled)
+ this._controlButton.removeAttribute("disabled");
+ else
+ this._controlButton.setAttribute("disabled", "");
+ this._controlButton.title = this._recordButtonEnabled ? "" : WebInspector.UIString("Another profiler is already active");
+ if (this._isInstantProfile) {
+ this._controlButton.classList.remove("running");
+ this._controlButton.textContent = WebInspector.UIString("Take Snapshot");
+ } else if (this._isProfiling) {
+ this._controlButton.classList.add("running");
+ this._controlButton.textContent = WebInspector.UIString("Stop");
+ } else {
+ this._controlButton.classList.remove("running");
+ this._controlButton.textContent = WebInspector.UIString("Start");
+ }
+ },
+
+ profileStarted: function()
+ {
+ this._isProfiling = true;
+ this._updateControls();
+ },
+
+ profileFinished: function()
+ {
+ this._isProfiling = false;
+ this._updateControls();
+ },
+
+ /**
+ * @param {!WebInspector.ProfileType} profileType
+ * @param {boolean} recordButtonEnabled
+ */
+ updateProfileType: function(profileType, recordButtonEnabled)
+ {
+ this._isInstantProfile = profileType.isInstantProfile();
+ this._recordButtonEnabled = recordButtonEnabled;
+ this._isEnabled = profileType.isEnabled();
+ this._profileTypeId = profileType.id;
+ this._updateControls();
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileLauncherView}
+ * @param {!WebInspector.ProfilesPanel} profilesPanel
+ */
+WebInspector.MultiProfileLauncherView = function(profilesPanel)
+{
+ WebInspector.ProfileLauncherView.call(this, profilesPanel);
+
+ WebInspector.settings.selectedProfileType = WebInspector.settings.createSetting("selectedProfileType", "CPU");
+
+ var header = this._innerContentElement.createChild("h1");
+ header.textContent = WebInspector.UIString("Select profiling type");
+
+ this._profileTypeSelectorForm = this._innerContentElement.createChild("form");
+
+ this._innerContentElement.createChild("div", "flexible-space");
+
+ this._typeIdToOptionElement = {};
+}
+
+WebInspector.MultiProfileLauncherView.EventTypes = {
+ ProfileTypeSelected: "profile-type-selected"
+}
+
+WebInspector.MultiProfileLauncherView.prototype = {
+ /**
+ * @override
+ * @param {!WebInspector.ProfileType} profileType
+ */
+ addProfileType: function(profileType)
+ {
+ var labelElement = this._profileTypeSelectorForm.createChild("label");
+ labelElement.textContent = profileType.name;
+ var optionElement = document.createElement("input");
+ labelElement.insertBefore(optionElement, labelElement.firstChild);
+ this._typeIdToOptionElement[profileType.id] = optionElement;
+ optionElement._profileType = profileType;
+ optionElement.type = "radio";
+ optionElement.name = "profile-type";
+ optionElement.style.hidden = true;
+ optionElement.addEventListener("change", this._profileTypeChanged.bind(this, profileType), false);
+ var descriptionElement = labelElement.createChild("p");
+ descriptionElement.textContent = profileType.description;
+ var decorationElement = profileType.decorationElement();
+ if (decorationElement)
+ labelElement.appendChild(decorationElement);
+ },
+
+ restoreSelectedProfileType: function()
+ {
+ var typeId = WebInspector.settings.selectedProfileType.get();
+ if (!(typeId in this._typeIdToOptionElement))
+ typeId = Object.keys(this._typeIdToOptionElement)[0];
+ this._typeIdToOptionElement[typeId].checked = true;
+ var type = this._typeIdToOptionElement[typeId]._profileType;
+ this.dispatchEventToListeners(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, type);
+ },
+
+ _controlButtonClicked: function()
+ {
+ this._panel.toggleRecordButton();
+ },
+
+ _updateControls: function()
+ {
+ WebInspector.ProfileLauncherView.prototype._updateControls.call(this);
+ var items = this._profileTypeSelectorForm.elements;
+ for (var i = 0; i < items.length; ++i) {
+ if (items[i].type === "radio")
+ items[i].disabled = this._isProfiling;
+ }
+ },
+
+ /**
+ * @param {!WebInspector.ProfileType} profileType
+ */
+ _profileTypeChanged: function(profileType)
+ {
+ this.dispatchEventToListeners(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, profileType);
+ this._isInstantProfile = profileType.isInstantProfile();
+ this._isEnabled = profileType.isEnabled();
+ this._profileTypeId = profileType.id;
+ this._updateControls();
+ WebInspector.settings.selectedProfileType.set(profileType.id);
+ },
+
+ profileStarted: function()
+ {
+ this._isProfiling = true;
+ this._updateControls();
+ },
+
+ profileFinished: function()
+ {
+ this._isProfiling = false;
+ this._updateControls();
+ },
+
+ __proto__: WebInspector.ProfileLauncherView.prototype
+}
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js
new file mode 100644
index 00000000000..4e00a2ca50b
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js
@@ -0,0 +1,1350 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.Object}
+ * @param {string} id
+ * @param {string} name
+ */
+WebInspector.ProfileType = function(id, name)
+{
+ WebInspector.Object.call(this);
+ this._id = id;
+ this._name = name;
+ /** @type {!Array.<!WebInspector.ProfileHeader>} */
+ this._profiles = [];
+ /** @type {?WebInspector.ProfileHeader} */
+ this._profileBeingRecorded = null;
+ this._nextProfileUid = 1;
+
+ window.addEventListener("unload", this._clearTempStorage.bind(this), false);
+}
+
+/**
+ * @enum {string}
+ */
+WebInspector.ProfileType.Events = {
+ AddProfileHeader: "add-profile-header",
+ ProfileComplete: "profile-complete",
+ RemoveProfileHeader: "remove-profile-header",
+ ViewUpdated: "view-updated"
+}
+
+WebInspector.ProfileType.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasTemporaryView: function()
+ {
+ return false;
+ },
+
+ /**
+ * @return {?string}
+ */
+ fileExtension: function()
+ {
+ return null;
+ },
+
+ get statusBarItems()
+ {
+ return [];
+ },
+
+ get buttonTooltip()
+ {
+ return "";
+ },
+
+ get id()
+ {
+ return this._id;
+ },
+
+ get treeItemTitle()
+ {
+ return this._name;
+ },
+
+ get name()
+ {
+ return this._name;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ buttonClicked: function()
+ {
+ return false;
+ },
+
+ get description()
+ {
+ return "";
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isInstantProfile: function()
+ {
+ return false;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isEnabled: function()
+ {
+ return true;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.ProfileHeader>}
+ */
+ getProfiles: function()
+ {
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ * @return {boolean}
+ * @this {WebInspector.ProfileType}
+ */
+ function isFinished(profile)
+ {
+ return this._profileBeingRecorded !== profile;
+ }
+ return this._profiles.filter(isFinished.bind(this));
+ },
+
+ /**
+ * @return {?Element}
+ */
+ decorationElement: function()
+ {
+ return null;
+ },
+
+ /**
+ * @nosideeffects
+ * @param {number} uid
+ * @return {?WebInspector.ProfileHeader}
+ */
+ getProfile: function(uid)
+ {
+
+ for (var i = 0; i < this._profiles.length; ++i) {
+ if (this._profiles[i].uid === uid)
+ return this._profiles[i];
+ }
+ return null;
+ },
+
+ /**
+ * @param {!File} file
+ */
+ loadFromFile: function(file)
+ {
+ var name = file.name;
+ if (name.endsWith(this.fileExtension()))
+ name = name.substr(0, name.length - this.fileExtension().length);
+ var profile = this.createProfileLoadedFromFile(name);
+ profile.setFromFile();
+ this.setProfileBeingRecorded(profile);
+ this.addProfile(profile);
+ profile.loadFromFile(file);
+ },
+
+ /**
+ * @param {string} title
+ * @return {!WebInspector.ProfileHeader}
+ */
+ createProfileLoadedFromFile: function(title)
+ {
+ throw new Error("Needs implemented.");
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ addProfile: function(profile)
+ {
+ this._profiles.push(profile);
+ this.dispatchEventToListeners(WebInspector.ProfileType.Events.AddProfileHeader, profile);
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ removeProfile: function(profile)
+ {
+ var index = this._profiles.indexOf(profile);
+ if (index === -1)
+ return;
+ this._profiles.splice(index, 1);
+ this._disposeProfile(profile);
+ },
+
+ _clearTempStorage: function()
+ {
+ for (var i = 0; i < this._profiles.length; ++i)
+ this._profiles[i].removeTempFile();
+ },
+
+ /**
+ * @nosideeffects
+ * @return {?WebInspector.ProfileHeader}
+ */
+ profileBeingRecorded: function()
+ {
+ return this._profileBeingRecorded;
+ },
+
+ /**
+ * @param {?WebInspector.ProfileHeader} profile
+ */
+ setProfileBeingRecorded: function(profile)
+ {
+ if (this._profileBeingRecorded)
+ this._profileBeingRecorded.target().profilingLock.release();
+ if (profile)
+ profile.target().profilingLock.acquire();
+ this._profileBeingRecorded = profile;
+ },
+
+ profileBeingRecordedRemoved: function()
+ {
+ },
+
+ _reset: function()
+ {
+ var profiles = this._profiles.slice(0);
+ for (var i = 0; i < profiles.length; ++i)
+ this._disposeProfile(profiles[i]);
+ this._profiles = [];
+
+ this._nextProfileUid = 1;
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ _disposeProfile: function(profile)
+ {
+ this.dispatchEventToListeners(WebInspector.ProfileType.Events.RemoveProfileHeader, profile);
+ profile.dispose();
+ if (this._profileBeingRecorded === profile) {
+ this.profileBeingRecordedRemoved();
+ this.setProfileBeingRecorded(null);
+ }
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.ProfileType.DataDisplayDelegate = function()
+{
+}
+
+WebInspector.ProfileType.DataDisplayDelegate.prototype = {
+ /**
+ * @param {?WebInspector.ProfileHeader} profile
+ * @return {?WebInspector.View}
+ */
+ showProfile: function(profile) { },
+
+ /**
+ * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
+ * @param {string} perspectiveName
+ */
+ showObject: function(snapshotObjectId, perspectiveName) { }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TargetAwareObject}
+ * @param {!WebInspector.Target} target
+ * @param {!WebInspector.ProfileType} profileType
+ * @param {string} title
+ */
+WebInspector.ProfileHeader = function(target, profileType, title)
+{
+ WebInspector.TargetAwareObject.call(this, target);
+ this._profileType = profileType;
+ this.title = title;
+ this.uid = profileType._nextProfileUid++;
+ this._fromFile = false;
+}
+
+/**
+ * @constructor
+ * @param {?string} subtitle
+ * @param {boolean|undefined} wait
+ */
+WebInspector.ProfileHeader.StatusUpdate = function(subtitle, wait)
+{
+ /** @type {?string} */
+ this.subtitle = subtitle;
+ /** @type {boolean|undefined} */
+ this.wait = wait;
+}
+
+WebInspector.ProfileHeader.Events = {
+ UpdateStatus: "UpdateStatus",
+ ProfileReceived: "ProfileReceived"
+}
+
+WebInspector.ProfileHeader.prototype = {
+ /**
+ * @return {!WebInspector.ProfileType}
+ */
+ profileType: function()
+ {
+ return this._profileType;
+ },
+
+ /**
+ * @param {?string} subtitle
+ * @param {boolean=} wait
+ */
+ updateStatus: function(subtitle, wait)
+ {
+ this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.UpdateStatus, new WebInspector.ProfileHeader.StatusUpdate(subtitle, wait));
+ },
+
+ /**
+ * Must be implemented by subclasses.
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @return {!WebInspector.ProfileSidebarTreeElement}
+ */
+ createSidebarTreeElement: function(dataDisplayDelegate)
+ {
+ throw new Error("Needs implemented.");
+ },
+
+ /**
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @return {!WebInspector.View}
+ */
+ createView: function(dataDisplayDelegate)
+ {
+ throw new Error("Not implemented.");
+ },
+
+ removeTempFile: function()
+ {
+ if (this._tempFile)
+ this._tempFile.remove();
+ },
+
+ dispose: function()
+ {
+ },
+
+ /**
+ * @param {!Function} callback
+ */
+ load: function(callback)
+ {
+ },
+
+ /**
+ * @return {boolean}
+ */
+ canSaveToFile: function()
+ {
+ return false;
+ },
+
+ saveToFile: function()
+ {
+ throw new Error("Needs implemented");
+ },
+
+ /**
+ * @param {!File} file
+ */
+ loadFromFile: function(file)
+ {
+ throw new Error("Needs implemented");
+ },
+
+ /**
+ * @return {boolean}
+ */
+ fromFile: function()
+ {
+ return this._fromFile;
+ },
+
+ setFromFile: function()
+ {
+ this._fromFile = true;
+ },
+
+ __proto__: WebInspector.TargetAwareObject.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.Searchable}
+ * @implements {WebInspector.ProfileType.DataDisplayDelegate}
+ * @extends {WebInspector.PanelWithSidebarTree}
+ */
+WebInspector.ProfilesPanel = function()
+{
+ WebInspector.PanelWithSidebarTree.call(this, "profiles");
+ this.registerRequiredCSS("panelEnablerView.css");
+ this.registerRequiredCSS("heapProfiler.css");
+ this.registerRequiredCSS("profilesPanel.css");
+
+ this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
+ this._target.profilingLock.addEventListener(WebInspector.Lock.Events.StateChanged, this._onProfilingStateChanged, this);
+
+ this._searchableView = new WebInspector.SearchableView(this);
+
+ var mainView = new WebInspector.VBox();
+ this._searchableView.show(mainView.element);
+ mainView.show(this.mainElement());
+
+ this.profilesItemTreeElement = new WebInspector.ProfilesSidebarTreeElement(this);
+ this.sidebarTree.appendChild(this.profilesItemTreeElement);
+
+ this.profileViews = document.createElement("div");
+ this.profileViews.id = "profile-views";
+ this.profileViews.classList.add("vbox");
+ this._searchableView.element.appendChild(this.profileViews);
+
+ var statusBarContainer = document.createElementWithClass("div", "profiles-status-bar");
+ mainView.element.insertBefore(statusBarContainer, mainView.element.firstChild);
+ this._statusBarElement = statusBarContainer.createChild("div", "status-bar");
+
+ this.sidebarElement().classList.add("profiles-sidebar-tree-box");
+ var statusBarContainerLeft = document.createElementWithClass("div", "profiles-status-bar");
+ this.sidebarElement().insertBefore(statusBarContainerLeft, this.sidebarElement().firstChild);
+ this._statusBarButtons = statusBarContainerLeft.createChild("div", "status-bar");
+
+ this.recordButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
+ this.recordButton.addEventListener("click", this.toggleRecordButton, this);
+ this._statusBarButtons.appendChild(this.recordButton.element);
+
+ this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item");
+ this.clearResultsButton.addEventListener("click", this._reset, this);
+ this._statusBarButtons.appendChild(this.clearResultsButton.element);
+
+ this._profileTypeStatusBarItemsContainer = this._statusBarElement.createChild("div");
+ this._profileViewStatusBarItemsContainer = this._statusBarElement.createChild("div");
+
+ this._profileGroups = {};
+ this._launcherView = new WebInspector.MultiProfileLauncherView(this);
+ this._launcherView.addEventListener(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, this._onProfileTypeSelected, this);
+
+ this._profileToView = [];
+ this._typeIdToSidebarSection = {};
+ var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
+ for (var i = 0; i < types.length; i++)
+ this._registerProfileType(types[i]);
+ this._launcherView.restoreSelectedProfileType();
+ this.profilesItemTreeElement.select();
+ this._showLauncherView();
+
+ this._createFileSelectorElement();
+ this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+ this._registerShortcuts();
+
+ this._configureCpuProfilerSamplingInterval();
+ WebInspector.settings.highResolutionCpuProfiling.addChangeListener(this._configureCpuProfilerSamplingInterval, this);
+}
+
+
+/**
+ * @constructor
+ */
+WebInspector.ProfileTypeRegistry = function() {
+ this._profileTypes = [];
+
+ this.cpuProfileType = new WebInspector.CPUProfileType();
+ this._addProfileType(this.cpuProfileType);
+ this.heapSnapshotProfileType = new WebInspector.HeapSnapshotProfileType();
+ this._addProfileType(this.heapSnapshotProfileType);
+ this.trackingHeapSnapshotProfileType = new WebInspector.TrackingHeapSnapshotProfileType();
+ this._addProfileType(this.trackingHeapSnapshotProfileType);
+ HeapProfilerAgent.enable();
+
+ if (Capabilities.isMainFrontend && WebInspector.experimentsSettings.canvasInspection.isEnabled()) {
+ this.canvasProfileType = new WebInspector.CanvasProfileType();
+ this._addProfileType(this.canvasProfileType);
+ }
+}
+
+WebInspector.ProfileTypeRegistry.prototype = {
+ /**
+ * @param {!WebInspector.ProfileType} profileType
+ */
+ _addProfileType: function(profileType)
+ {
+ this._profileTypes.push(profileType);
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.ProfileType>}
+ */
+ profileTypes: function()
+ {
+ return this._profileTypes;
+ }
+}
+
+
+
+WebInspector.ProfilesPanel.prototype = {
+ /**
+ * @return {!WebInspector.SearchableView}
+ */
+ searchableView: function()
+ {
+ return this._searchableView;
+ },
+
+ _createFileSelectorElement: function()
+ {
+ if (this._fileSelectorElement)
+ this.element.removeChild(this._fileSelectorElement);
+ this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
+ this.element.appendChild(this._fileSelectorElement);
+ },
+
+ _findProfileTypeByExtension: function(fileName)
+ {
+ var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
+ for (var i = 0; i < types.length; i++) {
+ var type = types[i];
+ var extension = type.fileExtension();
+ if (!extension)
+ continue;
+ if (fileName.endsWith(type.fileExtension()))
+ return type;
+ }
+ return null;
+ },
+
+ _registerShortcuts: function()
+ {
+ this.registerShortcuts(WebInspector.ShortcutsScreen.ProfilesPanelShortcuts.StartStopRecording, this.toggleRecordButton.bind(this));
+ },
+
+ _configureCpuProfilerSamplingInterval: function()
+ {
+ var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get() ? 100 : 1000;
+ ProfilerAgent.setSamplingInterval(intervalUs, didChangeInterval);
+ function didChangeInterval(error)
+ {
+ if (error)
+ WebInspector.messageSink.addErrorMessage(error, true);
+ }
+ },
+
+ /**
+ * @param {!File} file
+ */
+ _loadFromFile: function(file)
+ {
+ this._createFileSelectorElement();
+
+ var profileType = this._findProfileTypeByExtension(file.name);
+ if (!profileType) {
+ var extensions = [];
+ var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
+ for (var i = 0; i < types.length; i++) {
+ var extension = types[i].fileExtension();
+ if (!extension || extensions.indexOf(extension) !== -1)
+ continue;
+ extensions.push(extension);
+ }
+ WebInspector.messageSink.addMessage(WebInspector.UIString("Can't load file. Only files with extensions '%s' can be loaded.", extensions.join("', '")));
+ return;
+ }
+
+ if (!!profileType.profileBeingRecorded()) {
+ WebInspector.messageSink.addMessage(WebInspector.UIString("Can't load profile while another profile is recording."));
+ return;
+ }
+
+ profileType.loadFromFile(file);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ toggleRecordButton: function()
+ {
+ if (!this.recordButton.enabled())
+ return true;
+ var type = this._selectedProfileType;
+ var isProfiling = type.buttonClicked();
+ this._updateRecordButton(isProfiling);
+ if (isProfiling) {
+ this._launcherView.profileStarted();
+ if (type.hasTemporaryView())
+ this.showProfile(type.profileBeingRecorded());
+ } else {
+ this._launcherView.profileFinished();
+ }
+ return true;
+ },
+
+ _onProfilingStateChanged: function()
+ {
+ this._updateRecordButton(this.recordButton.toggled);
+ },
+
+ /**
+ * @param {boolean} toggled
+ */
+ _updateRecordButton: function(toggled)
+ {
+ var enable = toggled || !this._target.profilingLock.isAcquired();
+ this.recordButton.setEnabled(enable);
+ this.recordButton.toggled = toggled;
+ if (enable)
+ this.recordButton.title = this._selectedProfileType ? this._selectedProfileType.buttonTooltip : "";
+ else
+ this.recordButton.title = WebInspector.UIString("Another profiler is already active");
+ if (this._selectedProfileType)
+ this._launcherView.updateProfileType(this._selectedProfileType, enable);
+ },
+
+ _profileBeingRecordedRemoved: function()
+ {
+ this._updateRecordButton(false);
+ this._launcherView.profileFinished();
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onProfileTypeSelected: function(event)
+ {
+ this._selectedProfileType = /** @type {!WebInspector.ProfileType} */ (event.data);
+ this._updateProfileTypeSpecificUI();
+ },
+
+ _updateProfileTypeSpecificUI: function()
+ {
+ this._updateRecordButton(this.recordButton.toggled);
+ this._profileTypeStatusBarItemsContainer.removeChildren();
+ var statusBarItems = this._selectedProfileType.statusBarItems;
+ if (statusBarItems) {
+ for (var i = 0; i < statusBarItems.length; ++i)
+ this._profileTypeStatusBarItemsContainer.appendChild(statusBarItems[i]);
+ }
+ },
+
+ _reset: function()
+ {
+ WebInspector.Panel.prototype.reset.call(this);
+
+ var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
+ for (var i = 0; i < types.length; i++)
+ types[i]._reset();
+
+ delete this.visibleView;
+ delete this.currentQuery;
+ this.searchCanceled();
+
+ this._profileGroups = {};
+ this._updateRecordButton(false);
+ this._launcherView.profileFinished();
+
+ this.sidebarTree.element.classList.remove("some-expandable");
+
+ this._launcherView.detach();
+ this.profileViews.removeChildren();
+ this._profileViewStatusBarItemsContainer.removeChildren();
+
+ this.removeAllListeners();
+
+ this.recordButton.visible = true;
+ this._profileViewStatusBarItemsContainer.classList.remove("hidden");
+ this.clearResultsButton.element.classList.remove("hidden");
+ this.profilesItemTreeElement.select();
+ this._showLauncherView();
+ },
+
+ _showLauncherView: function()
+ {
+ this.closeVisibleView();
+ this._profileViewStatusBarItemsContainer.removeChildren();
+ this._launcherView.show(this.profileViews);
+ this.visibleView = this._launcherView;
+ },
+
+ _garbageCollectButtonClicked: function()
+ {
+ HeapProfilerAgent.collectGarbage();
+ },
+
+ /**
+ * @param {!WebInspector.ProfileType} profileType
+ */
+ _registerProfileType: function(profileType)
+ {
+ this._launcherView.addProfileType(profileType);
+ var profileTypeSection = new WebInspector.ProfileTypeSidebarSection(this, profileType);
+ this._typeIdToSidebarSection[profileType.id] = profileTypeSection
+ this.sidebarTree.appendChild(profileTypeSection);
+ profileTypeSection.childrenListElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+
+ /**
+ * @param {!WebInspector.Event} event
+ * @this {WebInspector.ProfilesPanel}
+ */
+ function onAddProfileHeader(event)
+ {
+ this._addProfileHeader(/** @type {!WebInspector.ProfileHeader} */ (event.data));
+ }
+
+ /**
+ * @param {!WebInspector.Event} event
+ * @this {WebInspector.ProfilesPanel}
+ */
+ function onRemoveProfileHeader(event)
+ {
+ this._removeProfileHeader(/** @type {!WebInspector.ProfileHeader} */ (event.data));
+ }
+
+ /**
+ * @param {!WebInspector.Event} event
+ * @this {WebInspector.ProfilesPanel}
+ */
+ function profileComplete(event)
+ {
+ this.showProfile(/** @type {!WebInspector.ProfileHeader} */ (event.data));
+ }
+
+ profileType.addEventListener(WebInspector.ProfileType.Events.ViewUpdated, this._updateProfileTypeSpecificUI, this);
+ profileType.addEventListener(WebInspector.ProfileType.Events.AddProfileHeader, onAddProfileHeader, this);
+ profileType.addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, onRemoveProfileHeader, this);
+ profileType.addEventListener(WebInspector.ProfileType.Events.ProfileComplete, profileComplete, this);
+
+ var profiles = profileType.getProfiles();
+ for (var i = 0; i < profiles.length; i++)
+ this._addProfileHeader(profiles[i]);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _handleContextMenuEvent: function(event)
+ {
+ var element = event.srcElement;
+ while (element && !element.treeElement && element !== this.element)
+ element = element.parentElement;
+ if (!element)
+ return;
+ if (element.treeElement && element.treeElement.handleContextMenuEvent) {
+ element.treeElement.handleContextMenuEvent(event, this);
+ return;
+ }
+
+ var contextMenu = new WebInspector.ContextMenu(event);
+ if (this.visibleView instanceof WebInspector.HeapSnapshotView) {
+ this.visibleView.populateContextMenu(contextMenu, event);
+ }
+ if (element !== this.element || event.srcElement === this.sidebarElement()) {
+ contextMenu.appendItem(WebInspector.UIString("Load\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));
+ }
+ contextMenu.show();
+ },
+
+ showLoadFromFileDialog: function()
+ {
+ this._fileSelectorElement.click();
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ _addProfileHeader: function(profile)
+ {
+ var profileType = profile.profileType();
+ var typeId = profileType.id;
+ this._typeIdToSidebarSection[typeId].addProfileHeader(profile);
+ if (!this.visibleView || this.visibleView === this._launcherView)
+ this.showProfile(profile);
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ _removeProfileHeader: function(profile)
+ {
+ if (profile.profileType()._profileBeingRecorded === profile)
+ this._profileBeingRecordedRemoved();
+
+ var i = this._indexOfViewForProfile(profile);
+ if (i !== -1)
+ this._profileToView.splice(i, 1);
+
+ var profileType = profile.profileType();
+ var typeId = profileType.id;
+ var sectionIsEmpty = this._typeIdToSidebarSection[typeId].removeProfileHeader(profile);
+
+ // No other item will be selected if there aren't any other profiles, so
+ // make sure that view gets cleared when the last profile is removed.
+ if (sectionIsEmpty) {
+ this.profilesItemTreeElement.select();
+ this._showLauncherView();
+ }
+ },
+
+ /**
+ * @param {?WebInspector.ProfileHeader} profile
+ * @return {?WebInspector.View}
+ */
+ showProfile: function(profile)
+ {
+ if (!profile || (profile.profileType().profileBeingRecorded() === profile) && !profile.profileType().hasTemporaryView())
+ return null;
+
+ var view = this._viewForProfile(profile);
+ if (view === this.visibleView)
+ return view;
+
+ this.closeVisibleView();
+
+ view.show(this.profileViews);
+
+ this.visibleView = view;
+
+ var profileTypeSection = this._typeIdToSidebarSection[profile.profileType().id];
+ var sidebarElement = profileTypeSection.sidebarElementForProfile(profile);
+ sidebarElement.revealAndSelect();
+
+ this._profileViewStatusBarItemsContainer.removeChildren();
+
+ var statusBarItems = view.statusBarItems;
+ if (statusBarItems)
+ for (var i = 0; i < statusBarItems.length; ++i)
+ this._profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
+
+ return view;
+ },
+
+ /**
+ * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
+ * @param {string} perspectiveName
+ */
+ showObject: function(snapshotObjectId, perspectiveName)
+ {
+ var heapProfiles = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType.getProfiles();
+ for (var i = 0; i < heapProfiles.length; i++) {
+ var profile = heapProfiles[i];
+ // FIXME: allow to choose snapshot if there are several options.
+ if (profile.maxJSObjectId >= snapshotObjectId) {
+ this.showProfile(profile);
+ var view = this._viewForProfile(profile);
+ view.highlightLiveObject(perspectiveName, snapshotObjectId);
+ break;
+ }
+ }
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ * @return {!WebInspector.View}
+ */
+ _viewForProfile: function(profile)
+ {
+ var index = this._indexOfViewForProfile(profile);
+ if (index !== -1)
+ return this._profileToView[index].view;
+ var view = profile.createView(this);
+ view.element.classList.add("profile-view");
+ this._profileToView.push({ profile: profile, view: view});
+ return view;
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ * @return {number}
+ */
+ _indexOfViewForProfile: function(profile)
+ {
+ for (var i = 0; i < this._profileToView.length; i++) {
+ if (this._profileToView[i].profile === profile)
+ return i;
+ }
+ return -1;
+ },
+
+ closeVisibleView: function()
+ {
+ if (this.visibleView)
+ this.visibleView.detach();
+ delete this.visibleView;
+ },
+
+ /**
+ * @param {string} query
+ * @param {boolean} shouldJump
+ * @param {boolean=} jumpBackwards
+ */
+ performSearch: function(query, shouldJump, jumpBackwards)
+ {
+ this.searchCanceled();
+
+ var visibleView = this.visibleView;
+ if (!visibleView)
+ return;
+
+ /**
+ * @this {WebInspector.ProfilesPanel}
+ */
+ function finishedCallback(view, searchMatches)
+ {
+ if (!searchMatches)
+ return;
+ this._searchableView.updateSearchMatchesCount(searchMatches);
+ this._searchResultsView = view;
+ if (shouldJump) {
+ if (jumpBackwards)
+ view.jumpToLastSearchResult();
+ else
+ view.jumpToFirstSearchResult();
+ this._searchableView.updateCurrentMatchIndex(view.currentSearchResultIndex());
+ }
+ }
+
+ visibleView.currentQuery = query;
+ visibleView.performSearch(query, finishedCallback.bind(this));
+ },
+
+ jumpToNextSearchResult: function()
+ {
+ if (!this._searchResultsView)
+ return;
+ if (this._searchResultsView !== this.visibleView)
+ return;
+ this._searchResultsView.jumpToNextSearchResult();
+ this._searchableView.updateCurrentMatchIndex(this._searchResultsView.currentSearchResultIndex());
+ },
+
+ jumpToPreviousSearchResult: function()
+ {
+ if (!this._searchResultsView)
+ return;
+ if (this._searchResultsView !== this.visibleView)
+ return;
+ this._searchResultsView.jumpToPreviousSearchResult();
+ this._searchableView.updateCurrentMatchIndex(this._searchResultsView.currentSearchResultIndex());
+ },
+
+ searchCanceled: function()
+ {
+ if (this._searchResultsView) {
+ if (this._searchResultsView.searchCanceled)
+ this._searchResultsView.searchCanceled();
+ this._searchResultsView.currentQuery = null;
+ this._searchResultsView = null;
+ }
+ this._searchableView.updateSearchMatchesCount(0);
+ },
+
+ /**
+ * @param {!Event} event
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {!Object} target
+ */
+ appendApplicableItems: function(event, contextMenu, target)
+ {
+ if (!(target instanceof WebInspector.RemoteObject))
+ return;
+
+ if (WebInspector.inspectorView.currentPanel() !== this)
+ return;
+
+ var object = /** @type {!WebInspector.RemoteObject} */ (target);
+ var objectId = object.objectId;
+ if (!objectId)
+ return;
+
+ var heapProfiles = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType.getProfiles();
+ if (!heapProfiles.length)
+ return;
+
+ /**
+ * @this {WebInspector.ProfilesPanel}
+ */
+ function revealInView(viewName)
+ {
+ HeapProfilerAgent.getHeapObjectId(objectId, didReceiveHeapObjectId.bind(this, viewName));
+ }
+
+ /**
+ * @this {WebInspector.ProfilesPanel}
+ */
+ function didReceiveHeapObjectId(viewName, error, result)
+ {
+ if (WebInspector.inspectorView.currentPanel() !== this)
+ return;
+ if (!error)
+ this.showObject(result, viewName);
+ }
+
+ if (WebInspector.settings.showAdvancedHeapSnapshotProperties.get())
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInView.bind(this, "Dominators"));
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInView.bind(this, "Summary"));
+ },
+
+ __proto__: WebInspector.PanelWithSidebarTree.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarSectionTreeElement}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {!WebInspector.ProfileType} profileType
+ */
+WebInspector.ProfileTypeSidebarSection = function(dataDisplayDelegate, profileType)
+{
+ WebInspector.SidebarSectionTreeElement.call(this, profileType.treeItemTitle, null, true);
+ this._dataDisplayDelegate = dataDisplayDelegate;
+ this._profileTreeElements = [];
+ this._profileGroups = {};
+ this.hidden = true;
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ProfileTypeSidebarSection.ProfileGroup = function()
+{
+ this.profileSidebarTreeElements = [];
+ this.sidebarTreeElement = null;
+}
+
+WebInspector.ProfileTypeSidebarSection.prototype = {
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ */
+ addProfileHeader: function(profile)
+ {
+ this.hidden = false;
+ var profileType = profile.profileType();
+ var sidebarParent = this;
+ var profileTreeElement = profile.createSidebarTreeElement(this._dataDisplayDelegate);
+ this._profileTreeElements.push(profileTreeElement);
+
+ if (!profile.fromFile() && profileType.profileBeingRecorded() !== profile) {
+ var profileTitle = profile.title;
+ var group = this._profileGroups[profileTitle];
+ if (!group) {
+ group = new WebInspector.ProfileTypeSidebarSection.ProfileGroup();
+ this._profileGroups[profileTitle] = group;
+ }
+ group.profileSidebarTreeElements.push(profileTreeElement);
+
+ var groupSize = group.profileSidebarTreeElements.length;
+ if (groupSize === 2) {
+ // Make a group TreeElement now that there are 2 profiles.
+ group.sidebarTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(this._dataDisplayDelegate, profile.title);
+
+ var firstProfileTreeElement = group.profileSidebarTreeElements[0];
+ // Insert at the same index for the first profile of the group.
+ var index = this.children.indexOf(firstProfileTreeElement);
+ this.insertChild(group.sidebarTreeElement, index);
+
+ // Move the first profile to the group.
+ var selected = firstProfileTreeElement.selected;
+ this.removeChild(firstProfileTreeElement);
+ group.sidebarTreeElement.appendChild(firstProfileTreeElement);
+ if (selected)
+ firstProfileTreeElement.revealAndSelect();
+
+ firstProfileTreeElement.small = true;
+ firstProfileTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
+
+ this.treeOutline.element.classList.add("some-expandable");
+ }
+
+ if (groupSize >= 2) {
+ sidebarParent = group.sidebarTreeElement;
+ profileTreeElement.small = true;
+ profileTreeElement.mainTitle = WebInspector.UIString("Run %d", groupSize);
+ }
+ }
+
+ sidebarParent.appendChild(profileTreeElement);
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ * @return {boolean}
+ */
+ removeProfileHeader: function(profile)
+ {
+ var index = this._sidebarElementIndex(profile);
+ if (index === -1)
+ return false;
+ var profileTreeElement = this._profileTreeElements[index];
+ this._profileTreeElements.splice(index, 1);
+
+ var sidebarParent = this;
+ var group = this._profileGroups[profile.title];
+ if (group) {
+ var groupElements = group.profileSidebarTreeElements;
+ groupElements.splice(groupElements.indexOf(profileTreeElement), 1);
+ if (groupElements.length === 1) {
+ // Move the last profile out of its group and remove the group.
+ var pos = sidebarParent.children.indexOf(group.sidebarTreeElement);
+ this.insertChild(groupElements[0], pos);
+ groupElements[0].small = false;
+ groupElements[0].mainTitle = group.sidebarTreeElement.title;
+ this.removeChild(group.sidebarTreeElement);
+ }
+ if (groupElements.length !== 0)
+ sidebarParent = group.sidebarTreeElement;
+ }
+ sidebarParent.removeChild(profileTreeElement);
+ profileTreeElement.dispose();
+
+ if (this.children.length)
+ return false;
+ this.hidden = true;
+ return true;
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ * @return {?WebInspector.ProfileSidebarTreeElement}
+ */
+ sidebarElementForProfile: function(profile)
+ {
+ var index = this._sidebarElementIndex(profile);
+ return index === -1 ? null : this._profileTreeElements[index];
+ },
+
+ /**
+ * @param {!WebInspector.ProfileHeader} profile
+ * @return {number}
+ */
+ _sidebarElementIndex: function(profile)
+ {
+ var elements = this._profileTreeElements;
+ for (var i = 0; i < elements.length; i++) {
+ if (elements[i].profile === profile)
+ return i;
+ }
+ return -1;
+ },
+
+ __proto__: WebInspector.SidebarSectionTreeElement.prototype
+}
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.ContextMenu.Provider}
+ */
+WebInspector.ProfilesPanel.ContextMenuProvider = function()
+{
+}
+
+WebInspector.ProfilesPanel.ContextMenuProvider.prototype = {
+ /**
+ * @param {!Event} event
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {!Object} target
+ */
+ appendApplicableItems: function(event, contextMenu, target)
+ {
+ WebInspector.inspectorView.panel("profiles").appendApplicableItems(event, contextMenu, target);
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {!WebInspector.ProfileHeader} profile
+ * @param {string} className
+ */
+WebInspector.ProfileSidebarTreeElement = function(dataDisplayDelegate, profile, className)
+{
+ this._dataDisplayDelegate = dataDisplayDelegate;
+ this.profile = profile;
+ WebInspector.SidebarTreeElement.call(this, className, profile.title, "", profile, false);
+ this.refreshTitles();
+ profile.addEventListener(WebInspector.ProfileHeader.Events.UpdateStatus, this._updateStatus, this);
+ if (profile.canSaveToFile())
+ this._createSaveLink();
+ else
+ profile.addEventListener(WebInspector.ProfileHeader.Events.ProfileReceived, this._onProfileReceived, this);
+}
+
+WebInspector.ProfileSidebarTreeElement.prototype = {
+ _createSaveLink: function()
+ {
+ this._saveLinkElement = this.titleContainer.createChild("span", "save-link");
+ this._saveLinkElement.textContent = WebInspector.UIString("Save");
+ this._saveLinkElement.addEventListener("click", this._saveProfile.bind(this), false);
+ },
+
+ _onProfileReceived: function(event)
+ {
+ this._createSaveLink();
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _updateStatus: function(event)
+ {
+ var statusUpdate = event.data;
+ if (statusUpdate.subtitle !== null)
+ this.subtitle = statusUpdate.subtitle;
+ if (typeof statusUpdate.wait === "boolean")
+ this.wait = statusUpdate.wait;
+ this.refreshTitles();
+ },
+
+ dispose: function()
+ {
+ this.profile.removeEventListener(WebInspector.ProfileHeader.Events.UpdateStatus, this._updateStatus, this);
+ this.profile.removeEventListener(WebInspector.ProfileHeader.Events.ProfileReceived, this._onProfileReceived, this);
+ },
+
+ onselect: function()
+ {
+ this._dataDisplayDelegate.showProfile(this.profile);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ ondelete: function()
+ {
+ this.profile.profileType().removeProfile(this.profile);
+ return true;
+ },
+
+ /**
+ * @param {!Event} event
+ * @param {!WebInspector.ProfilesPanel} panel
+ */
+ handleContextMenuEvent: function(event, panel)
+ {
+ var profile = this.profile;
+ var contextMenu = new WebInspector.ContextMenu(event);
+ // FIXME: use context menu provider
+ contextMenu.appendItem(WebInspector.UIString("Load\u2026"), panel._fileSelectorElement.click.bind(panel._fileSelectorElement));
+ if (profile.canSaveToFile())
+ contextMenu.appendItem(WebInspector.UIString("Save\u2026"), profile.saveToFile.bind(profile));
+ contextMenu.appendItem(WebInspector.UIString("Delete"), this.ondelete.bind(this));
+ contextMenu.show();
+ },
+
+ _saveProfile: function(event)
+ {
+ this.profile.saveToFile();
+ },
+
+ __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
+ * @param {string} title
+ * @param {string=} subtitle
+ */
+WebInspector.ProfileGroupSidebarTreeElement = function(dataDisplayDelegate, title, subtitle)
+{
+ WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
+ this._dataDisplayDelegate = dataDisplayDelegate;
+}
+
+WebInspector.ProfileGroupSidebarTreeElement.prototype = {
+ onselect: function()
+ {
+ if (this.children.length > 0)
+ this._dataDisplayDelegate.showProfile(this.children[this.children.length - 1].profile);
+ },
+
+ __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {!WebInspector.ProfilesPanel} panel
+ */
+WebInspector.ProfilesSidebarTreeElement = function(panel)
+{
+ this._panel = panel;
+ this.small = false;
+
+ WebInspector.SidebarTreeElement.call(this, "profile-launcher-view-tree-item", WebInspector.UIString("Profiles"), "", null, false);
+}
+
+WebInspector.ProfilesSidebarTreeElement.prototype = {
+ onselect: function()
+ {
+ this._panel._showLauncherView();
+ },
+
+ get selectable()
+ {
+ return true;
+ },
+
+ __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+
+importScript("../sdk/CPUProfileModel.js");
+importScript("CPUProfileDataGrid.js");
+importScript("CPUProfileBottomUpDataGrid.js");
+importScript("CPUProfileTopDownDataGrid.js");
+importScript("CPUProfileFlameChart.js");
+importScript("CPUProfileView.js");
+importScript("HeapSnapshotCommon.js");
+importScript("HeapSnapshotProxy.js");
+importScript("HeapSnapshotDataGrids.js");
+importScript("HeapSnapshotGridNodes.js");
+importScript("HeapSnapshotView.js");
+importScript("ProfileLauncherView.js");
+importScript("CanvasProfileView.js");
+importScript("CanvasReplayStateView.js");
+
+WebInspector.ProfileTypeRegistry.instance = new WebInspector.ProfileTypeRegistry();
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/AllocationProfile.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/AllocationProfile.js
new file mode 100644
index 00000000000..c6eb7c7ac4a
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/AllocationProfile.js
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2013 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
+ */
+WebInspector.AllocationProfile = function(profile, liveObjectStats)
+{
+ this._strings = profile.strings;
+ this._liveObjectStats = liveObjectStats;
+
+ this._nextNodeId = 1;
+ this._functionInfos = []
+ this._idToNode = {};
+ this._idToTopDownNode = {};
+ this._collapsedTopNodeIdToFunctionInfo = {};
+
+ this._traceTops = null;
+
+ this._buildFunctionAllocationInfos(profile);
+ this._traceTree = this._buildAllocationTree(profile, liveObjectStats);
+}
+
+WebInspector.AllocationProfile.prototype = {
+ _buildFunctionAllocationInfos: function(profile)
+ {
+ var strings = this._strings;
+
+ var functionInfoFields = profile.snapshot.meta.trace_function_info_fields;
+ var functionIdOffset = functionInfoFields.indexOf("function_id");
+ var functionNameOffset = functionInfoFields.indexOf("name");
+ var scriptNameOffset = functionInfoFields.indexOf("script_name");
+ var scriptIdOffset = functionInfoFields.indexOf("script_id");
+ var lineOffset = functionInfoFields.indexOf("line");
+ var columnOffset = functionInfoFields.indexOf("column");
+ var functionInfoFieldCount = functionInfoFields.length;
+
+ var rawInfos = profile.trace_function_infos;
+ var infoLength = rawInfos.length;
+ var functionInfos = this._functionInfos = new Array(infoLength / functionInfoFieldCount);
+ var index = 0;
+ for (var i = 0; i < infoLength; i += functionInfoFieldCount) {
+ functionInfos[index++] = new WebInspector.FunctionAllocationInfo(
+ strings[rawInfos[i + functionNameOffset]],
+ strings[rawInfos[i + scriptNameOffset]],
+ rawInfos[i + scriptIdOffset],
+ rawInfos[i + lineOffset],
+ rawInfos[i + columnOffset]);
+ }
+ },
+
+ _buildAllocationTree: function(profile, liveObjectStats)
+ {
+ var traceTreeRaw = profile.trace_tree;
+ var functionInfos = this._functionInfos;
+ var idToTopDownNode = this._idToTopDownNode;
+
+ var traceNodeFields = profile.snapshot.meta.trace_node_fields;
+ var nodeIdOffset = traceNodeFields.indexOf("id");
+ var functionInfoIndexOffset = traceNodeFields.indexOf("function_info_index");
+ var allocationCountOffset = traceNodeFields.indexOf("count");
+ var allocationSizeOffset = traceNodeFields.indexOf("size");
+ var childrenOffset = traceNodeFields.indexOf("children");
+ var nodeFieldCount = traceNodeFields.length;
+
+ function traverseNode(rawNodeArray, nodeOffset, parent)
+ {
+ var functionInfo = functionInfos[rawNodeArray[nodeOffset + functionInfoIndexOffset]];
+ var id = rawNodeArray[nodeOffset + nodeIdOffset];
+ var stats = liveObjectStats[id];
+ var liveCount = stats ? stats.count : 0;
+ var liveSize = stats ? stats.size : 0;
+ var result = new WebInspector.TopDownAllocationNode(
+ id,
+ functionInfo,
+ rawNodeArray[nodeOffset + allocationCountOffset],
+ rawNodeArray[nodeOffset + allocationSizeOffset],
+ liveCount,
+ liveSize,
+ parent);
+ idToTopDownNode[id] = result;
+ functionInfo.addTraceTopNode(result);
+
+ var rawChildren = rawNodeArray[nodeOffset + childrenOffset];
+ for (var i = 0; i < rawChildren.length; i += nodeFieldCount) {
+ result.children.push(traverseNode(rawChildren, i, result));
+ }
+ return result;
+ }
+
+ return traverseNode(traceTreeRaw, 0, null);
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>}
+ */
+ serializeTraceTops: function()
+ {
+ if (this._traceTops)
+ return this._traceTops;
+ var result = this._traceTops = [];
+ var functionInfos = this._functionInfos;
+ for (var i = 0; i < functionInfos.length; i++) {
+ var info = functionInfos[i];
+ if (info.totalCount === 0)
+ continue;
+ var nodeId = this._nextNodeId++;
+ var isRoot = i == 0;
+ result.push(this._serializeNode(
+ nodeId,
+ info,
+ info.totalCount,
+ info.totalSize,
+ info.totalLiveCount,
+ info.totalLiveSize,
+ !isRoot));
+ this._collapsedTopNodeIdToFunctionInfo[nodeId] = info;
+ }
+ result.sort(function(a, b) {
+ return b.size - a.size;
+ });
+ return result;
+ },
+
+ /**
+ * @param {number} nodeId
+ * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers}
+ */
+ serializeCallers: function(nodeId)
+ {
+ var node = this._ensureBottomUpNode(nodeId);
+ var nodesWithSingleCaller = [];
+ while (node.callers().length === 1) {
+ node = node.callers()[0];
+ nodesWithSingleCaller.push(this._serializeCaller(node));
+ }
+
+ var branchingCallers = [];
+ var callers = node.callers();
+ for (var i = 0; i < callers.length; i++) {
+ branchingCallers.push(this._serializeCaller(callers[i]));
+ }
+ return new WebInspector.HeapSnapshotCommon.AllocationNodeCallers(nodesWithSingleCaller, branchingCallers);
+ },
+
+ /**
+ * @param {number} traceNodeId
+ * @return {!Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>}
+ */
+ serializeAllocationStack: function(traceNodeId)
+ {
+ var node = this._idToTopDownNode[traceNodeId];
+ var result = [];
+ while (node) {
+ var functionInfo = node.functionInfo;
+ result.push(new WebInspector.HeapSnapshotCommon.AllocationStackFrame(
+ functionInfo.functionName,
+ functionInfo.scriptName,
+ functionInfo.scriptId,
+ functionInfo.line,
+ functionInfo.column
+ ));
+ node = node.parent;
+ }
+ return result;
+ },
+
+ /**
+ * @param {number} allocationNodeId
+ * @return {!Array.<number>}
+ */
+ traceIds: function(allocationNodeId)
+ {
+ return this._ensureBottomUpNode(allocationNodeId).traceTopIds;
+ },
+
+ /**
+ * @param {number} nodeId
+ * @return {!WebInspector.BottomUpAllocationNode}
+ */
+ _ensureBottomUpNode: function(nodeId)
+ {
+ var node = this._idToNode[nodeId];
+ if (!node) {
+ var functionInfo = this._collapsedTopNodeIdToFunctionInfo[nodeId];
+ node = functionInfo.bottomUpRoot();
+ delete this._collapsedTopNodeIdToFunctionInfo[nodeId];
+ this._idToNode[nodeId] = node;
+ }
+ return node;
+ },
+
+ /**
+ * @param {!WebInspector.BottomUpAllocationNode} node
+ * @return {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode}
+ */
+ _serializeCaller: function(node)
+ {
+ var callerId = this._nextNodeId++;
+ this._idToNode[callerId] = node;
+ return this._serializeNode(
+ callerId,
+ node.functionInfo,
+ node.allocationCount,
+ node.allocationSize,
+ node.liveCount,
+ node.liveSize,
+ node.hasCallers());
+ },
+
+ /**
+ * @param {number} nodeId
+ * @param {!WebInspector.FunctionAllocationInfo} functionInfo
+ * @param {number} count
+ * @param {number} size
+ * @param {number} liveCount
+ * @param {number} liveSize
+ * @param {boolean} hasChildren
+ * @return {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode}
+ */
+ _serializeNode: function(nodeId, functionInfo, count, size, liveCount, liveSize, hasChildren)
+ {
+ return new WebInspector.HeapSnapshotCommon.SerializedAllocationNode(
+ nodeId,
+ functionInfo.functionName,
+ functionInfo.scriptName,
+ functionInfo.scriptId,
+ functionInfo.line,
+ functionInfo.column,
+ count,
+ size,
+ liveCount,
+ liveSize,
+ hasChildren
+ );
+ }
+}
+
+
+/**
+ * @constructor
+ * @param {number} id
+ * @param {!WebInspector.FunctionAllocationInfo} functionInfo
+ * @param {number} count
+ * @param {number} size
+ * @param {number} liveCount
+ * @param {number} liveSize
+ * @param {?WebInspector.TopDownAllocationNode} parent
+ */
+WebInspector.TopDownAllocationNode = function(id, functionInfo, count, size, liveCount, liveSize, parent)
+{
+ this.id = id;
+ this.functionInfo = functionInfo;
+ this.allocationCount = count;
+ this.allocationSize = size;
+ this.liveCount = liveCount;
+ this.liveSize = liveSize;
+ this.parent = parent;
+ this.children = [];
+}
+
+
+/**
+ * @constructor
+ * @param {!WebInspector.FunctionAllocationInfo} functionInfo
+ */
+WebInspector.BottomUpAllocationNode = function(functionInfo)
+{
+ this.functionInfo = functionInfo;
+ this.allocationCount = 0;
+ this.allocationSize = 0;
+ this.liveCount = 0;
+ this.liveSize = 0;
+ this.traceTopIds = [];
+ this._callers = [];
+}
+
+
+WebInspector.BottomUpAllocationNode.prototype = {
+ /**
+ * @param {!WebInspector.TopDownAllocationNode} traceNode
+ * @return {!WebInspector.BottomUpAllocationNode}
+ */
+ addCaller: function(traceNode)
+ {
+ var functionInfo = traceNode.functionInfo;
+ var result;
+ for (var i = 0; i < this._callers.length; i++) {
+ var caller = this._callers[i];
+ if (caller.functionInfo === functionInfo) {
+ result = caller;
+ break;
+ }
+ }
+ if (!result) {
+ result = new WebInspector.BottomUpAllocationNode(functionInfo);
+ this._callers.push(result);
+ }
+ return result;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.BottomUpAllocationNode>}
+ */
+ callers: function()
+ {
+ return this._callers;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasCallers: function()
+ {
+ return this._callers.length > 0;
+ }
+}
+
+
+/**
+ * @constructor
+ * @param {string} functionName
+ * @param {string} scriptName
+ * @param {number} scriptId
+ * @param {number} line
+ * @param {number} column
+ */
+WebInspector.FunctionAllocationInfo = function(functionName, scriptName, scriptId, line, column)
+{
+ this.functionName = functionName;
+ this.scriptName = scriptName;
+ this.scriptId = scriptId;
+ this.line = line;
+ this.column = column;
+ this.totalCount = 0;
+ this.totalSize = 0;
+ this.totalLiveCount = 0;
+ this.totalLiveSize = 0;
+ this._traceTops = [];
+}
+
+WebInspector.FunctionAllocationInfo.prototype = {
+ /**
+ * @param {!WebInspector.TopDownAllocationNode} node
+ */
+ addTraceTopNode: function(node)
+ {
+ if (node.allocationCount === 0)
+ return;
+ this._traceTops.push(node);
+ this.totalCount += node.allocationCount;
+ this.totalSize += node.allocationSize;
+ this.totalLiveCount += node.liveCount;
+ this.totalLiveSize += node.liveSize;
+ },
+
+ /**
+ * @return {?WebInspector.BottomUpAllocationNode}
+ */
+ bottomUpRoot: function()
+ {
+ if (!this._traceTops.length)
+ return null;
+ if (!this._bottomUpTree)
+ this._buildAllocationTraceTree();
+ return this._bottomUpTree;
+ },
+
+ _buildAllocationTraceTree: function()
+ {
+ this._bottomUpTree = new WebInspector.BottomUpAllocationNode(this);
+
+ for (var i = 0; i < this._traceTops.length; i++) {
+ var node = this._traceTops[i];
+ var bottomUpNode = this._bottomUpTree;
+ var count = node.allocationCount;
+ var size = node.allocationSize;
+ var liveCount = node.liveCount;
+ var liveSize = node.liveSize;
+ var traceId = node.id;
+ while (true) {
+ bottomUpNode.allocationCount += count;
+ bottomUpNode.allocationSize += size;
+ bottomUpNode.liveCount += liveCount;
+ bottomUpNode.liveSize += liveSize;
+ bottomUpNode.traceTopIds.push(traceId);
+ node = node.parent;
+ if (node === null) {
+ break;
+ }
+ bottomUpNode = bottomUpNode.addCaller(node);
+ }
+ }
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshot.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshot.js
new file mode 100644
index 00000000000..910b5a33f34
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshot.js
@@ -0,0 +1,2293 @@
+/*
+ * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.HeapSnapshotItem = function() { }
+
+WebInspector.HeapSnapshotItem.prototype = {
+ /**
+ * @return {number}
+ */
+ itemIndex: function() { },
+
+ /**
+ * @return {!Object}
+ */
+ serialize: function() { }
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItem}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ * @param {number=} edgeIndex
+ */
+WebInspector.HeapSnapshotEdge = function(snapshot, edgeIndex)
+{
+ this._snapshot = snapshot;
+ this._edges = snapshot._containmentEdges;
+ this.edgeIndex = edgeIndex || 0;
+}
+
+WebInspector.HeapSnapshotEdge.prototype = {
+ /**
+ * @return {!WebInspector.HeapSnapshotEdge}
+ */
+ clone: function()
+ {
+ return new WebInspector.HeapSnapshotEdge(this._snapshot, this.edgeIndex);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasStringName: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @return {string}
+ */
+ name: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotNode}
+ */
+ node: function()
+ {
+ return this._snapshot.createNode(this.nodeIndex());
+ },
+
+ /**
+ * @return {number}
+ */
+ nodeIndex: function()
+ {
+ return this._edges[this.edgeIndex + this._snapshot._edgeToNodeOffset];
+ },
+
+ /**
+ * @return {string}
+ */
+ toString: function()
+ {
+ return "HeapSnapshotEdge: " + this.name();
+ },
+
+ /**
+ * @return {string}
+ */
+ type: function()
+ {
+ return this._snapshot._edgeTypes[this._type()];
+ },
+
+ /**
+ * @override
+ * @return {number}
+ */
+ itemIndex: function()
+ {
+ return this.edgeIndex;
+ },
+
+ /**
+ * @override
+ * @return {!WebInspector.HeapSnapshotCommon.Edge}
+ */
+ serialize: function()
+ {
+ return new WebInspector.HeapSnapshotCommon.Edge(this.name(), this.node().serialize(), this.type(), this.edgeIndex);
+ },
+
+ _type: function()
+ {
+ return this._edges[this.edgeIndex + this._snapshot._edgeTypeOffset];
+ }
+};
+
+
+/**
+ * @interface
+ */
+WebInspector.HeapSnapshotItemIterator = function() { }
+
+WebInspector.HeapSnapshotItemIterator.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasNext: function() { },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotItem}
+ */
+ item: function() { },
+
+ next: function() { }
+};
+
+
+/**
+ * @interface
+ */
+WebInspector.HeapSnapshotItemIndexProvider = function() { }
+
+WebInspector.HeapSnapshotItemIndexProvider.prototype = {
+ /**
+ * @param {number} newIndex
+ * @return {!WebInspector.HeapSnapshotItem}
+ */
+ itemForIndex: function(newIndex) { },
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIndexProvider}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ */
+WebInspector.HeapSnapshotNodeIndexProvider = function(snapshot)
+{
+ this._node = snapshot.createNode();
+}
+
+WebInspector.HeapSnapshotNodeIndexProvider.prototype = {
+ /**
+ * @param {number} index
+ * @return {!WebInspector.HeapSnapshotNode}
+ */
+ itemForIndex: function(index)
+ {
+ this._node.nodeIndex = index;
+ return this._node;
+ }
+};
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIndexProvider}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ */
+WebInspector.HeapSnapshotEdgeIndexProvider = function(snapshot)
+{
+ this._edge = snapshot.createEdge(0);
+}
+
+WebInspector.HeapSnapshotEdgeIndexProvider.prototype = {
+ /**
+ * @param {number} index
+ * @return {!WebInspector.HeapSnapshotEdge}
+ */
+ itemForIndex: function(index)
+ {
+ this._edge.edgeIndex = index;
+ return this._edge;
+ }
+};
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIndexProvider}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ */
+WebInspector.HeapSnapshotRetainerEdgeIndexProvider = function(snapshot)
+{
+ this._retainerEdge = snapshot.createRetainingEdge(0);
+}
+
+WebInspector.HeapSnapshotRetainerEdgeIndexProvider.prototype = {
+ /**
+ * @param {number} index
+ * @return {!WebInspector.HeapSnapshotRetainerEdge}
+ */
+ itemForIndex: function(index)
+ {
+ this._retainerEdge.setRetainerIndex(index);
+ return this._retainerEdge;
+ }
+};
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIterator}
+ * @param {!WebInspector.HeapSnapshotNode} node
+ */
+WebInspector.HeapSnapshotEdgeIterator = function(node)
+{
+ this._sourceNode = node;
+ this.edge = node._snapshot.createEdge(node._edgeIndexesStart());
+}
+
+WebInspector.HeapSnapshotEdgeIterator.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasNext: function()
+ {
+ return this.edge.edgeIndex < this._sourceNode._edgeIndexesEnd();
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotEdge}
+ */
+ item: function()
+ {
+ return this.edge;
+ },
+
+ next: function()
+ {
+ this.edge.edgeIndex += this.edge._snapshot._edgeFieldsCount;
+ }
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItem}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ * @param {number} retainerIndex
+ */
+WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainerIndex)
+{
+ this._snapshot = snapshot;
+ this.setRetainerIndex(retainerIndex);
+}
+
+WebInspector.HeapSnapshotRetainerEdge.prototype = {
+ /**
+ * @return {!WebInspector.HeapSnapshotRetainerEdge}
+ */
+ clone: function()
+ {
+ return new WebInspector.HeapSnapshotRetainerEdge(this._snapshot, this.retainerIndex());
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasStringName: function()
+ {
+ return this._edge().hasStringName();
+ },
+
+ /**
+ * @return {string}
+ */
+ name: function()
+ {
+ return this._edge().name();
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotNode}
+ */
+ node: function()
+ {
+ return this._node();
+ },
+
+ /**
+ * @return {number}
+ */
+ nodeIndex: function()
+ {
+ return this._retainingNodeIndex;
+ },
+
+ /**
+ * @return {number}
+ */
+ retainerIndex: function()
+ {
+ return this._retainerIndex;
+ },
+
+ /**
+ * @param {number} retainerIndex
+ */
+ setRetainerIndex: function(retainerIndex)
+ {
+ if (retainerIndex === this._retainerIndex)
+ return;
+ this._retainerIndex = retainerIndex;
+ this._globalEdgeIndex = this._snapshot._retainingEdges[retainerIndex];
+ this._retainingNodeIndex = this._snapshot._retainingNodes[retainerIndex];
+ this._edgeInstance = null;
+ this._nodeInstance = null;
+ },
+
+ /**
+ * @param {number} edgeIndex
+ */
+ set edgeIndex(edgeIndex)
+ {
+ this.setRetainerIndex(edgeIndex);
+ },
+
+ _node: function()
+ {
+ if (!this._nodeInstance)
+ this._nodeInstance = this._snapshot.createNode(this._retainingNodeIndex);
+ return this._nodeInstance;
+ },
+
+ _edge: function()
+ {
+ if (!this._edgeInstance)
+ this._edgeInstance = this._snapshot.createEdge(this._globalEdgeIndex);
+ return this._edgeInstance;
+ },
+
+ /**
+ * @return {string}
+ */
+ toString: function()
+ {
+ return this._edge().toString();
+ },
+
+ /**
+ * @override
+ * @return {number}
+ */
+ itemIndex: function()
+ {
+ return this._retainerIndex;
+ },
+
+ /**
+ * @override
+ * @return {!WebInspector.HeapSnapshotCommon.Edge}
+ */
+ serialize: function()
+ {
+ return new WebInspector.HeapSnapshotCommon.Edge(this.name(), this.node().serialize(), this.type(), this._globalEdgeIndex);
+ },
+
+ /**
+ * @return {string}
+ */
+ type: function()
+ {
+ return this._edge().type();
+ }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIterator}
+ * @param {!WebInspector.HeapSnapshotNode} retainedNode
+ */
+WebInspector.HeapSnapshotRetainerEdgeIterator = function(retainedNode)
+{
+ var snapshot = retainedNode._snapshot;
+ var retainedNodeOrdinal = retainedNode._ordinal();
+ var retainerIndex = snapshot._firstRetainerIndex[retainedNodeOrdinal];
+ this._retainersEnd = snapshot._firstRetainerIndex[retainedNodeOrdinal + 1];
+ this.retainer = snapshot.createRetainingEdge(retainerIndex);
+}
+
+WebInspector.HeapSnapshotRetainerEdgeIterator.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasNext: function()
+ {
+ return this.retainer.retainerIndex() < this._retainersEnd;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotRetainerEdge}
+ */
+ item: function()
+ {
+ return this.retainer;
+ },
+
+ next: function()
+ {
+ this.retainer.setRetainerIndex(this.retainer.retainerIndex() + 1);
+ }
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItem}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ * @param {number=} nodeIndex
+ */
+WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex)
+{
+ this._snapshot = snapshot;
+ this.nodeIndex = nodeIndex || 0;
+}
+
+WebInspector.HeapSnapshotNode.prototype = {
+ /**
+ * @return {number}
+ */
+ distance: function()
+ {
+ return this._snapshot._nodeDistances[this.nodeIndex / this._snapshot._nodeFieldCount];
+ },
+
+ /**
+ * @return {string}
+ */
+ className: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @return {number}
+ */
+ classIndex: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @return {number}
+ */
+ dominatorIndex: function()
+ {
+ var nodeFieldCount = this._snapshot._nodeFieldCount;
+ return this._snapshot._dominatorsTree[this.nodeIndex / this._snapshot._nodeFieldCount] * nodeFieldCount;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotEdgeIterator}
+ */
+ edges: function()
+ {
+ return new WebInspector.HeapSnapshotEdgeIterator(this);
+ },
+
+ /**
+ * @return {number}
+ */
+ edgesCount: function()
+ {
+ return (this._edgeIndexesEnd() - this._edgeIndexesStart()) / this._snapshot._edgeFieldsCount;
+ },
+
+ /**
+ * @return {number}
+ */
+ id: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isRoot: function()
+ {
+ return this.nodeIndex === this._snapshot._rootNodeIndex;
+ },
+
+ /**
+ * @return {string}
+ */
+ name: function()
+ {
+ return this._snapshot._strings[this._name()];
+ },
+
+ /**
+ * @return {number}
+ */
+ retainedSize: function()
+ {
+ return this._snapshot._retainedSizes[this._ordinal()];
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotRetainerEdgeIterator}
+ */
+ retainers: function()
+ {
+ return new WebInspector.HeapSnapshotRetainerEdgeIterator(this);
+ },
+
+ /**
+ * @return {number}
+ */
+ retainersCount: function()
+ {
+ var snapshot = this._snapshot;
+ var ordinal = this._ordinal();
+ return snapshot._firstRetainerIndex[ordinal + 1] - snapshot._firstRetainerIndex[ordinal];
+ },
+
+ /**
+ * @return {number}
+ */
+ selfSize: function()
+ {
+ var snapshot = this._snapshot;
+ return snapshot._nodes[this.nodeIndex + snapshot._nodeSelfSizeOffset];
+ },
+
+ /**
+ * @return {string}
+ */
+ type: function()
+ {
+ return this._snapshot._nodeTypes[this._type()];
+ },
+
+ /**
+ * @return {number}
+ */
+ traceNodeId: function()
+ {
+ var snapshot = this._snapshot;
+ return snapshot._nodes[this.nodeIndex + snapshot._nodeTraceNodeIdOffset];
+ },
+
+ /**
+ * @override
+ * @return {number}
+ */
+ itemIndex: function()
+ {
+ return this.nodeIndex;
+ },
+
+ /**
+ * @override
+ * @return {!WebInspector.HeapSnapshotCommon.Node}
+ */
+ serialize: function()
+ {
+ return new WebInspector.HeapSnapshotCommon.Node(this.id(), this.name(), this.distance(), this.nodeIndex, this.retainedSize(), this.selfSize(), this.type());
+ },
+
+ /**
+ * @return {number}
+ */
+ _name: function()
+ {
+ var snapshot = this._snapshot;
+ return snapshot._nodes[this.nodeIndex + snapshot._nodeNameOffset];
+ },
+
+ /**
+ * @return {number}
+ */
+ _edgeIndexesStart: function()
+ {
+ return this._snapshot._firstEdgeIndexes[this._ordinal()];
+ },
+
+ /**
+ * @return {number}
+ */
+ _edgeIndexesEnd: function()
+ {
+ return this._snapshot._firstEdgeIndexes[this._ordinal() + 1];
+ },
+
+ /**
+ * @return {number}
+ */
+ _ordinal: function()
+ {
+ return this.nodeIndex / this._snapshot._nodeFieldCount;
+ },
+
+ /**
+ * @return {number}
+ */
+ _nextNodeIndex: function()
+ {
+ return this.nodeIndex + this._snapshot._nodeFieldCount;
+ },
+
+ /**
+ * @return {number}
+ */
+ _type: function()
+ {
+ var snapshot = this._snapshot;
+ return snapshot._nodes[this.nodeIndex + snapshot._nodeTypeOffset];
+ }
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIterator}
+ * @param {!WebInspector.HeapSnapshotNode} node
+ */
+WebInspector.HeapSnapshotNodeIterator = function(node)
+{
+ this.node = node;
+ this._nodesLength = node._snapshot._nodes.length;
+}
+
+WebInspector.HeapSnapshotNodeIterator.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasNext: function()
+ {
+ return this.node.nodeIndex < this._nodesLength;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotNode}
+ */
+ item: function()
+ {
+ return this.node;
+ },
+
+ next: function()
+ {
+ this.node.nodeIndex = this.node._nextNodeIndex();
+ }
+}
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIterator}
+ * @param {!WebInspector.HeapSnapshotItemIndexProvider} itemProvider
+ * @param {!Array.<number>|!Uint32Array} indexes
+ */
+WebInspector.HeapSnapshotIndexRangeIterator = function(itemProvider, indexes)
+{
+ this._itemProvider = itemProvider;
+ this._indexes = indexes;
+ this._position = 0;
+}
+
+WebInspector.HeapSnapshotIndexRangeIterator.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasNext: function()
+ {
+ return this._position < this._indexes.length
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotItem}
+ */
+ item: function()
+ {
+ var index = this._indexes[this._position];
+ return this._itemProvider.itemForIndex(index);
+ },
+
+ next: function()
+ {
+ ++this._position;
+ }
+}
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.HeapSnapshotItemIterator}
+ * @param {!WebInspector.HeapSnapshotItemIterator} iterator
+ * @param {function(!WebInspector.HeapSnapshotItem):boolean=} filter
+ */
+WebInspector.HeapSnapshotFilteredIterator = function(iterator, filter)
+{
+ this._iterator = iterator;
+ this._filter = filter;
+ this._skipFilteredItems();
+}
+
+WebInspector.HeapSnapshotFilteredIterator.prototype = {
+ /**
+ * @return {boolean}
+ */
+ hasNext: function()
+ {
+ return this._iterator.hasNext();
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotItem}
+ */
+ item: function()
+ {
+ return this._iterator.item();
+ },
+
+ next: function()
+ {
+ this._iterator.next();
+ this._skipFilteredItems();
+ },
+
+ _skipFilteredItems: function()
+ {
+ while (this._iterator.hasNext() && !this._filter(this._iterator.item())) {
+ this._iterator.next();
+ }
+ }
+}
+
+
+/**
+ * @param {!WebInspector.HeapSnapshotWorkerDispatcher=} dispatcher
+ * @constructor
+ */
+WebInspector.HeapSnapshotProgress = function(dispatcher)
+{
+ this._dispatcher = dispatcher;
+}
+
+WebInspector.HeapSnapshotProgress.prototype = {
+ /**
+ * @param {string} status
+ */
+ updateStatus: function(status)
+ {
+ this._sendUpdateEvent(WebInspector.UIString(status));
+ },
+
+ /**
+ * @param {string} title
+ * @param {number} value
+ * @param {number} total
+ */
+ updateProgress: function(title, value, total)
+ {
+ var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
+ this._sendUpdateEvent(WebInspector.UIString(title, percentValue));
+ },
+
+ /**
+ * @param {string} text
+ */
+ _sendUpdateEvent: function(text)
+ {
+ // May be undefined in tests.
+ if (this._dispatcher)
+ this._dispatcher.sendEvent(WebInspector.HeapSnapshotProgressEvent.Update, text);
+ }
+}
+
+
+/**
+ * @param {!Object} profile
+ * @param {!WebInspector.HeapSnapshotProgress} progress
+ * @param {boolean} showHiddenData
+ * @constructor
+ */
+WebInspector.HeapSnapshot = function(profile, progress, showHiddenData)
+{
+ this._nodes = profile.nodes;
+ this._containmentEdges = profile.edges;
+ /** @type {!HeapSnapshotMetainfo} */
+ this._metaNode = profile.snapshot.meta;
+ this._strings = profile.strings;
+ this._progress = progress;
+
+ this._noDistance = -5;
+ this._rootNodeIndex = 0;
+ if (profile.snapshot.root_index)
+ this._rootNodeIndex = profile.snapshot.root_index;
+
+ this._snapshotDiffs = {};
+ this._aggregatesForDiff = null;
+ this._aggregates = {};
+ this._aggregatesSortedFlags = {};
+ this._showHiddenData = showHiddenData;
+
+ this._init();
+
+ if (profile.snapshot.trace_function_count) {
+ this._progress.updateStatus("Buiding allocation statistics\u2026");
+ var nodes = this._nodes;
+ var nodesLength = nodes.length;
+ var nodeFieldCount = this._nodeFieldCount;
+ var node = this.rootNode();
+ var liveObjects = {};
+ for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
+ node.nodeIndex = nodeIndex;
+ var traceNodeId = node.traceNodeId();
+ var stats = liveObjects[traceNodeId];
+ if (!stats) {
+ liveObjects[traceNodeId] = stats = { count: 0, size: 0, ids: []};
+ }
+ stats.count++;
+ stats.size += node.selfSize();
+ stats.ids.push(node.id());
+ }
+ this._allocationProfile = new WebInspector.AllocationProfile(profile, liveObjects);
+ this._progress.updateStatus("Done");
+ }
+}
+
+/**
+ * @constructor
+ */
+function HeapSnapshotMetainfo()
+{
+ // New format.
+ this.node_fields = [];
+ this.node_types = [];
+ this.edge_fields = [];
+ this.edge_types = [];
+ this.trace_function_info_fields = [];
+ this.trace_node_fields = [];
+ this.type_strings = {};
+}
+
+/**
+ * @constructor
+ */
+function HeapSnapshotHeader()
+{
+ // New format.
+ this.title = "";
+ this.meta = new HeapSnapshotMetainfo();
+ this.node_count = 0;
+ this.edge_count = 0;
+}
+
+WebInspector.HeapSnapshot.prototype = {
+ _init: function()
+ {
+ var meta = this._metaNode;
+
+ this._nodeTypeOffset = meta.node_fields.indexOf("type");
+ this._nodeNameOffset = meta.node_fields.indexOf("name");
+ this._nodeIdOffset = meta.node_fields.indexOf("id");
+ this._nodeSelfSizeOffset = meta.node_fields.indexOf("self_size");
+ this._nodeEdgeCountOffset = meta.node_fields.indexOf("edge_count");
+ this._nodeTraceNodeIdOffset = meta.node_fields.indexOf("trace_node_id");
+ this._nodeFieldCount = meta.node_fields.length;
+
+ this._nodeTypes = meta.node_types[this._nodeTypeOffset];
+ this._nodeHiddenType = this._nodeTypes.indexOf("hidden");
+ this._nodeObjectType = this._nodeTypes.indexOf("object");
+ this._nodeNativeType = this._nodeTypes.indexOf("native");
+ this._nodeConsStringType = this._nodeTypes.indexOf("concatenated string");
+ this._nodeSlicedStringType = this._nodeTypes.indexOf("sliced string");
+ this._nodeCodeType = this._nodeTypes.indexOf("code");
+ this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic");
+
+ this._edgeFieldsCount = meta.edge_fields.length;
+ this._edgeTypeOffset = meta.edge_fields.indexOf("type");
+ this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index");
+ this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node");
+
+ this._edgeTypes = meta.edge_types[this._edgeTypeOffset];
+ this._edgeTypes.push("invisible");
+ this._edgeElementType = this._edgeTypes.indexOf("element");
+ this._edgeHiddenType = this._edgeTypes.indexOf("hidden");
+ this._edgeInternalType = this._edgeTypes.indexOf("internal");
+ this._edgeShortcutType = this._edgeTypes.indexOf("shortcut");
+ this._edgeWeakType = this._edgeTypes.indexOf("weak");
+ this._edgeInvisibleType = this._edgeTypes.indexOf("invisible");
+
+ this.nodeCount = this._nodes.length / this._nodeFieldCount;
+ this._edgeCount = this._containmentEdges.length / this._edgeFieldsCount;
+
+ this._progress.updateStatus("Building edge indexes\u2026");
+ this._buildEdgeIndexes();
+ this._progress.updateStatus("Building retainers\u2026");
+ this._buildRetainers();
+ this._progress.updateStatus("Calculating node flags\u2026");
+ this._calculateFlags();
+ this._progress.updateStatus("Calculating distances\u2026");
+ this._calculateDistances();
+ this._progress.updateStatus("Building postorder index\u2026");
+ var result = this._buildPostOrderIndex();
+ // Actually it is array that maps node ordinal number to dominator node ordinal number.
+ this._progress.updateStatus("Building dominator tree\u2026");
+ this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
+ this._progress.updateStatus("Calculating retained sizes\u2026");
+ this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal);
+ this._progress.updateStatus("Buiding dominated nodes\u2026");
+ this._buildDominatedNodes();
+ this._progress.updateStatus("Calculating statistics\u2026");
+ this._calculateStatistics();
+ this._progress.updateStatus("Finished processing.");
+ },
+
+ _buildEdgeIndexes: function()
+ {
+ var nodes = this._nodes;
+ var nodeCount = this.nodeCount;
+ var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1);
+ var nodeFieldCount = this._nodeFieldCount;
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var nodeEdgeCountOffset = this._nodeEdgeCountOffset;
+ firstEdgeIndexes[nodeCount] = this._containmentEdges.length;
+ for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
+ firstEdgeIndexes[nodeOrdinal] = edgeIndex;
+ edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount;
+ }
+ },
+
+ _buildRetainers: function()
+ {
+ var retainingNodes = this._retainingNodes = new Uint32Array(this._edgeCount);
+ var retainingEdges = this._retainingEdges = new Uint32Array(this._edgeCount);
+ // Index of the first retainer in the _retainingNodes and _retainingEdges
+ // arrays. Addressed by retained node index.
+ var firstRetainerIndex = this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1);
+
+ var containmentEdges = this._containmentEdges;
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var nodeFieldCount = this._nodeFieldCount;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var firstEdgeIndexes = this._firstEdgeIndexes;
+ var nodeCount = this.nodeCount;
+
+ for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) {
+ var toNodeIndex = containmentEdges[toNodeFieldIndex];
+ if (toNodeIndex % nodeFieldCount)
+ throw new Error("Invalid toNodeIndex " + toNodeIndex);
+ ++firstRetainerIndex[toNodeIndex / nodeFieldCount];
+ }
+ for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) {
+ var retainersCount = firstRetainerIndex[i];
+ firstRetainerIndex[i] = firstUnusedRetainerSlot;
+ retainingNodes[firstUnusedRetainerSlot] = retainersCount;
+ firstUnusedRetainerSlot += retainersCount;
+ }
+ firstRetainerIndex[nodeCount] = retainingNodes.length;
+
+ var nextNodeFirstEdgeIndex = firstEdgeIndexes[0];
+ for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) {
+ var firstEdgeIndex = nextNodeFirstEdgeIndex;
+ nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1];
+ var srcNodeIndex = srcNodeOrdinal * nodeFieldCount;
+ for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) {
+ var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ if (toNodeIndex % nodeFieldCount)
+ throw new Error("Invalid toNodeIndex " + toNodeIndex);
+ var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount];
+ var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]);
+ retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex;
+ retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex;
+ }
+ }
+ },
+
+ /**
+ * @param {number=} nodeIndex
+ */
+ createNode: function(nodeIndex)
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @param {number} edgeIndex
+ * @return {!WebInspector.JSHeapSnapshotEdge}
+ */
+ createEdge: function(edgeIndex)
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @param {number} retainerIndex
+ * @return {!WebInspector.JSHeapSnapshotRetainerEdge}
+ */
+ createRetainingEdge: function(retainerIndex)
+ {
+ throw new Error("Not implemented");
+ },
+
+ dispose: function()
+ {
+ delete this._nodes;
+ delete this._strings;
+ delete this._retainingEdges;
+ delete this._retainingNodes;
+ delete this._firstRetainerIndex;
+ delete this._aggregates;
+ delete this._aggregatesSortedFlags;
+ delete this._dominatedNodes;
+ delete this._firstDominatedNodeIndex;
+ delete this._nodeDistances;
+ delete this._dominatorsTree;
+ },
+
+ _allNodes: function()
+ {
+ return new WebInspector.HeapSnapshotNodeIterator(this.rootNode());
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotNode}
+ */
+ rootNode: function()
+ {
+ return this.createNode(this._rootNodeIndex);
+ },
+
+ get rootNodeIndex()
+ {
+ return this._rootNodeIndex;
+ },
+
+ get totalSize()
+ {
+ return this.rootNode().retainedSize();
+ },
+
+ _getDominatedIndex: function(nodeIndex)
+ {
+ if (nodeIndex % this._nodeFieldCount)
+ throw new Error("Invalid nodeIndex: " + nodeIndex);
+ return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount];
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @return {!Uint32Array}
+ */
+ _dominatedNodesOfNode: function(node)
+ {
+ var dominatedIndexFrom = this._getDominatedIndex(node.nodeIndex);
+ var dominatedIndexTo = this._getDominatedIndex(node._nextNodeIndex());
+ return this._dominatedNodes.subarray(dominatedIndexFrom, dominatedIndexTo);
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
+ * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>}
+ */
+ aggregatesWithFilter: function(nodeFilter)
+ {
+ var minNodeId = nodeFilter.minNodeId;
+ var maxNodeId = nodeFilter.maxNodeId;
+ var allocationNodeId = nodeFilter.allocationNodeId;
+ var key;
+ var filter;
+ if (typeof allocationNodeId === "number") {
+ filter = this._createAllocationStackFilter(allocationNodeId);
+ } else if (typeof minNodeId === "number" && typeof maxNodeId === "number") {
+ key = minNodeId + ".." + maxNodeId;
+ filter = this._createNodeIdFilter(minNodeId, maxNodeId);
+ } else {
+ key = "allObjects";
+ }
+ return this.aggregates(false, key, filter);
+ },
+
+ /**
+ * @param {number} minNodeId
+ * @param {number} maxNodeId
+ * @return {function(!WebInspector.HeapSnapshotNode):boolean}
+ */
+ _createNodeIdFilter: function(minNodeId, maxNodeId)
+ {
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @return {boolean}
+ */
+ function nodeIdFilter(node)
+ {
+ var id = node.id();
+ return id > minNodeId && id <= maxNodeId;
+ }
+ return nodeIdFilter;
+ },
+
+ /**
+ * @param {number} bottomUpAllocationNodeId
+ * @return {function(!WebInspector.HeapSnapshotNode):boolean|undefined}
+ */
+ _createAllocationStackFilter: function(bottomUpAllocationNodeId)
+ {
+ var traceIds = this._allocationProfile.traceIds(bottomUpAllocationNodeId);
+ if (!traceIds.length)
+ return undefined;
+ var set = {};
+ for (var i = 0; i < traceIds.length; i++)
+ set[traceIds[i]] = true;
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @return {boolean}
+ */
+ function traceIdFilter(node)
+ {
+ return !!set[node.traceNodeId()];
+ };
+ return traceIdFilter;
+ },
+
+ /**
+ * @param {boolean} sortedIndexes
+ * @param {string=} key
+ * @param {function(!WebInspector.HeapSnapshotNode):boolean=} filter
+ * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>}
+ */
+ aggregates: function(sortedIndexes, key, filter)
+ {
+ var aggregatesByClassName = key && this._aggregates[key];
+ if (!aggregatesByClassName) {
+ var aggregates = this._buildAggregates(filter);
+ this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter);
+ aggregatesByClassName = aggregates.aggregatesByClassName;
+ if (key)
+ this._aggregates[key] = aggregatesByClassName;
+ }
+
+ if (sortedIndexes && (!key || !this._aggregatesSortedFlags[key])) {
+ this._sortAggregateIndexes(aggregatesByClassName);
+ if (key)
+ this._aggregatesSortedFlags[key] = sortedIndexes;
+ }
+ return aggregatesByClassName;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>}
+ */
+ allocationTracesTops: function()
+ {
+ return this._allocationProfile.serializeTraceTops();
+ },
+
+ /**
+ * @param {number} nodeId
+ * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers}
+ */
+ allocationNodeCallers: function(nodeId)
+ {
+ return this._allocationProfile.serializeCallers(nodeId);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>}
+ */
+ allocationStack: function(nodeIndex)
+ {
+ var node = this.createNode(nodeIndex);
+ var allocationNodeId = node.traceNodeId();
+ if (!allocationNodeId)
+ return null;
+ return this._allocationProfile.serializeAllocationStack(allocationNodeId);
+ },
+
+ /**
+ * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>}
+ */
+ aggregatesForDiff: function()
+ {
+ if (this._aggregatesForDiff)
+ return this._aggregatesForDiff;
+
+ var aggregatesByClassName = this.aggregates(true, "allObjects");
+ this._aggregatesForDiff = {};
+
+ var node = this.createNode();
+ for (var className in aggregatesByClassName) {
+ var aggregate = aggregatesByClassName[className];
+ var indexes = aggregate.idxs;
+ var ids = new Array(indexes.length);
+ var selfSizes = new Array(indexes.length);
+ for (var i = 0; i < indexes.length; i++) {
+ node.nodeIndex = indexes[i];
+ ids[i] = node.id();
+ selfSizes[i] = node.selfSize();
+ }
+
+ this._aggregatesForDiff[className] = {
+ indexes: indexes,
+ ids: ids,
+ selfSizes: selfSizes
+ };
+ }
+ return this._aggregatesForDiff;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @return {!boolean}
+ */
+ _isUserRoot: function(node)
+ {
+ return true;
+ },
+
+ /**
+ * @param {function(!WebInspector.HeapSnapshotNode)} action
+ * @param {boolean=} userRootsOnly
+ */
+ forEachRoot: function(action, userRootsOnly)
+ {
+ for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+ var node = iter.edge.node();
+ if (!userRootsOnly || this._isUserRoot(node))
+ action(node);
+ }
+ },
+
+ _calculateDistances: function()
+ {
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodeCount = this.nodeCount;
+ var distances = this._nodeDistances = new Int32Array(nodeCount);
+ var noDistance = this._noDistance;
+ for (var i = 0; i < nodeCount; ++i)
+ distances[i] = noDistance;
+
+ var nodesToVisit = new Uint32Array(this.nodeCount);
+ var nodesToVisitLength = 0;
+
+ /**
+ * @param {number} distance
+ * @param {!WebInspector.HeapSnapshotNode} node
+ */
+ function enqueueNode(distance, node)
+ {
+ var ordinal = node._ordinal();
+ if (distances[ordinal] !== noDistance)
+ return;
+ distances[ordinal] = distance;
+ nodesToVisit[nodesToVisitLength++] = node.nodeIndex;
+ }
+
+ this.forEachRoot(enqueueNode.bind(null, 1), true);
+ this._bfs(nodesToVisit, nodesToVisitLength, distances);
+
+ // bfs for the rest of objects
+ nodesToVisitLength = 0;
+ this.forEachRoot(enqueueNode.bind(null, WebInspector.HeapSnapshotCommon.baseSystemDistance), false);
+ this._bfs(nodesToVisit, nodesToVisitLength, distances);
+ },
+
+ /**
+ * @param {!Uint32Array} nodesToVisit
+ * @param {!number} nodesToVisitLength
+ * @param {!Int32Array} distances
+ */
+ _bfs: function(nodesToVisit, nodesToVisitLength, distances)
+ {
+ // Preload fields into local variables for better performance.
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var nodeFieldCount = this._nodeFieldCount;
+ var containmentEdges = this._containmentEdges;
+ var firstEdgeIndexes = this._firstEdgeIndexes;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var edgeTypeOffset = this._edgeTypeOffset;
+ var nodeCount = this.nodeCount;
+ var containmentEdgesLength = containmentEdges.length;
+ var edgeWeakType = this._edgeWeakType;
+ var noDistance = this._noDistance;
+
+ var index = 0;
+ while (index < nodesToVisitLength) {
+ var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage.
+ var nodeOrdinal = nodeIndex / nodeFieldCount;
+ var distance = distances[nodeOrdinal] + 1;
+ var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+ var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
+ for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) {
+ var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
+ if (edgeType == edgeWeakType)
+ continue;
+ var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+ if (distances[childNodeOrdinal] !== noDistance)
+ continue;
+ distances[childNodeOrdinal] = distance;
+ nodesToVisit[nodesToVisitLength++] = childNodeIndex;
+ }
+ }
+ if (nodesToVisitLength > nodeCount)
+ throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")");
+ },
+
+ _buildAggregates: function(filter)
+ {
+ var aggregates = {};
+ var aggregatesByClassName = {};
+ var classIndexes = [];
+ var nodes = this._nodes;
+ var mapAndFlag = this.userObjectsMapAndFlag();
+ var flags = mapAndFlag ? mapAndFlag.map : null;
+ var flag = mapAndFlag ? mapAndFlag.flag : 0;
+ var nodesLength = nodes.length;
+ var nodeNativeType = this._nodeNativeType;
+ var nodeFieldCount = this._nodeFieldCount;
+ var selfSizeOffset = this._nodeSelfSizeOffset;
+ var nodeTypeOffset = this._nodeTypeOffset;
+ var node = this.rootNode();
+ var nodeDistances = this._nodeDistances;
+
+ for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
+ var nodeOrdinal = nodeIndex / nodeFieldCount;
+ if (flags && !(flags[nodeOrdinal] & flag))
+ continue;
+ node.nodeIndex = nodeIndex;
+ if (filter && !filter(node))
+ continue;
+ var selfSize = nodes[nodeIndex + selfSizeOffset];
+ if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType)
+ continue;
+ var classIndex = node.classIndex();
+ if (!(classIndex in aggregates)) {
+ var nodeType = node.type();
+ var nameMatters = nodeType === "object" || nodeType === "native";
+ var value = {
+ count: 1,
+ distance: nodeDistances[nodeOrdinal],
+ self: selfSize,
+ maxRet: 0,
+ type: nodeType,
+ name: nameMatters ? node.name() : null,
+ idxs: [nodeIndex]
+ };
+ aggregates[classIndex] = value;
+ classIndexes.push(classIndex);
+ aggregatesByClassName[node.className()] = value;
+ } else {
+ var clss = aggregates[classIndex];
+ clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]);
+ ++clss.count;
+ clss.self += selfSize;
+ clss.idxs.push(nodeIndex);
+ }
+ }
+
+ // Shave off provisionally allocated space.
+ for (var i = 0, l = classIndexes.length; i < l; ++i) {
+ var classIndex = classIndexes[i];
+ aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice();
+ }
+ return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates};
+ },
+
+ _calculateClassesRetainedSize: function(aggregates, filter)
+ {
+ var rootNodeIndex = this._rootNodeIndex;
+ var node = this.createNode(rootNodeIndex);
+ var list = [rootNodeIndex];
+ var sizes = [-1];
+ var classes = [];
+ var seenClassNameIndexes = {};
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodeTypeOffset = this._nodeTypeOffset;
+ var nodeNativeType = this._nodeNativeType;
+ var dominatedNodes = this._dominatedNodes;
+ var nodes = this._nodes;
+ var mapAndFlag = this.userObjectsMapAndFlag();
+ var flags = mapAndFlag ? mapAndFlag.map : null;
+ var flag = mapAndFlag ? mapAndFlag.flag : 0;
+ var firstDominatedNodeIndex = this._firstDominatedNodeIndex;
+
+ while (list.length) {
+ var nodeIndex = list.pop();
+ node.nodeIndex = nodeIndex;
+ var classIndex = node.classIndex();
+ var seen = !!seenClassNameIndexes[classIndex];
+ var nodeOrdinal = nodeIndex / nodeFieldCount;
+ var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal];
+ var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1];
+
+ if (!seen &&
+ (!flags || (flags[nodeOrdinal] & flag)) &&
+ (!filter || filter(node)) &&
+ (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType)
+ ) {
+ aggregates[classIndex].maxRet += node.retainedSize();
+ if (dominatedIndexFrom !== dominatedIndexTo) {
+ seenClassNameIndexes[classIndex] = true;
+ sizes.push(list.length);
+ classes.push(classIndex);
+ }
+ }
+ for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++)
+ list.push(dominatedNodes[i]);
+
+ var l = list.length;
+ while (sizes[sizes.length - 1] === l) {
+ sizes.pop();
+ classIndex = classes.pop();
+ seenClassNameIndexes[classIndex] = false;
+ }
+ }
+ },
+
+ _sortAggregateIndexes: function(aggregates)
+ {
+ var nodeA = this.createNode();
+ var nodeB = this.createNode();
+ for (var clss in aggregates)
+ aggregates[clss].idxs.sort(
+ function(idxA, idxB) {
+ nodeA.nodeIndex = idxA;
+ nodeB.nodeIndex = idxB;
+ return nodeA.id() < nodeB.id() ? -1 : 1;
+ });
+ },
+
+ _buildPostOrderIndex: function()
+ {
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodes = this._nodes;
+ var nodeCount = this.nodeCount;
+ var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var edgeTypeOffset = this._edgeTypeOffset;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var edgeShortcutType = this._edgeShortcutType;
+ var firstEdgeIndexes = this._firstEdgeIndexes;
+ var containmentEdges = this._containmentEdges;
+
+ var mapAndFlag = this.userObjectsMapAndFlag();
+ var flags = mapAndFlag ? mapAndFlag.map : null;
+ var flag = mapAndFlag ? mapAndFlag.flag : 0;
+
+ var stackNodes = new Uint32Array(nodeCount);
+ var stackCurrentEdge = new Uint32Array(nodeCount);
+ var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount);
+ var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount);
+ var visited = new Uint8Array(nodeCount);
+ var postOrderIndex = 0;
+
+ var stackTop = 0;
+ stackNodes[0] = rootNodeOrdinal;
+ stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal];
+ visited[rootNodeOrdinal] = 1;
+
+ while (stackTop >= 0) {
+ var nodeOrdinal = stackNodes[stackTop];
+ var edgeIndex = stackCurrentEdge[stackTop];
+ var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
+
+ if (edgeIndex < edgesEnd) {
+ stackCurrentEdge[stackTop] += edgeFieldsCount;
+ if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
+ continue;
+ var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+ if (visited[childNodeOrdinal])
+ continue;
+ var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
+ var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag);
+ // We are skipping the edges from non-page-owned nodes to page-owned nodes.
+ // Otherwise the dominators for the objects that also were retained by debugger would be affected.
+ if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag)
+ continue;
+ ++stackTop;
+ stackNodes[stackTop] = childNodeOrdinal;
+ stackCurrentEdge[stackTop] = firstEdgeIndexes[childNodeOrdinal];
+ visited[childNodeOrdinal] = 1;
+ } else {
+ // Done with all the node children
+ nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex;
+ postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal;
+ --stackTop;
+ }
+ }
+
+ if (postOrderIndex !== nodeCount) {
+ console.log("Error: Corrupted snapshot. " + (nodeCount - postOrderIndex) + " nodes are unreachable from the root:");
+ var dumpNode = this.rootNode();
+ for (var i = 0; i < nodeCount; ++i) {
+ if (!visited[i]) {
+ // Fix it by giving the node a postorder index anyway.
+ nodeOrdinal2PostOrderIndex[i] = postOrderIndex;
+ postOrderIndex2NodeOrdinal[postOrderIndex++] = i;
+ dumpNode.nodeIndex = i * nodeFieldCount;
+ console.log(JSON.stringify(dumpNode.serialize()));
+ for (var retainers = dumpNode.retainers(); retainers.hasNext(); retainers = retainers.item().node().retainers())
+ console.log(" edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className());
+ }
+ }
+ }
+
+ return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex};
+ },
+
+ // The algorithm is based on the article:
+ // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
+ // Softw. Pract. Exper. 4 (2001), pp. 1-10.
+ /**
+ * @param {!Array.<number>} postOrderIndex2NodeOrdinal
+ * @param {!Array.<number>} nodeOrdinal2PostOrderIndex
+ */
+ _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex)
+ {
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodes = this._nodes;
+ var firstRetainerIndex = this._firstRetainerIndex;
+ var retainingNodes = this._retainingNodes;
+ var retainingEdges = this._retainingEdges;
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var edgeTypeOffset = this._edgeTypeOffset;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var edgeShortcutType = this._edgeShortcutType;
+ var firstEdgeIndexes = this._firstEdgeIndexes;
+ var containmentEdges = this._containmentEdges;
+ var containmentEdgesLength = this._containmentEdges.length;
+ var rootNodeIndex = this._rootNodeIndex;
+
+ var mapAndFlag = this.userObjectsMapAndFlag();
+ var flags = mapAndFlag ? mapAndFlag.map : null;
+ var flag = mapAndFlag ? mapAndFlag.flag : 0;
+
+ var nodesCount = postOrderIndex2NodeOrdinal.length;
+ var rootPostOrderedIndex = nodesCount - 1;
+ var noEntry = nodesCount;
+ var dominators = new Uint32Array(nodesCount);
+ for (var i = 0; i < rootPostOrderedIndex; ++i)
+ dominators[i] = noEntry;
+ dominators[rootPostOrderedIndex] = rootPostOrderedIndex;
+
+ // The affected array is used to mark entries which dominators
+ // have to be racalculated because of changes in their retainers.
+ var affected = new Uint8Array(nodesCount);
+ var nodeOrdinal;
+
+ { // Mark the root direct children as affected.
+ nodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+ var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
+ var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
+ for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
+ toNodeFieldIndex < endEdgeToNodeFieldIndex;
+ toNodeFieldIndex += edgeFieldsCount) {
+ var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
+ affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
+ }
+ }
+
+ var changed = true;
+ while (changed) {
+ changed = false;
+ for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) {
+ if (affected[postOrderIndex] === 0)
+ continue;
+ affected[postOrderIndex] = 0;
+ // If dominator of the entry has already been set to root,
+ // then it can't propagate any further.
+ if (dominators[postOrderIndex] === rootPostOrderedIndex)
+ continue;
+ nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+ var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
+ var newDominatorIndex = noEntry;
+ var beginRetainerIndex = firstRetainerIndex[nodeOrdinal];
+ var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1];
+ for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) {
+ var retainerEdgeIndex = retainingEdges[retainerIndex];
+ var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset];
+ var retainerNodeIndex = retainingNodes[retainerIndex];
+ if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType)
+ continue;
+ var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount;
+ var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag);
+ // We are skipping the edges from non-page-owned nodes to page-owned nodes.
+ // Otherwise the dominators for the objects that also were retained by debugger would be affected.
+ if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag)
+ continue;
+ var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal];
+ if (dominators[retanerPostOrderIndex] !== noEntry) {
+ if (newDominatorIndex === noEntry)
+ newDominatorIndex = retanerPostOrderIndex;
+ else {
+ while (retanerPostOrderIndex !== newDominatorIndex) {
+ while (retanerPostOrderIndex < newDominatorIndex)
+ retanerPostOrderIndex = dominators[retanerPostOrderIndex];
+ while (newDominatorIndex < retanerPostOrderIndex)
+ newDominatorIndex = dominators[newDominatorIndex];
+ }
+ }
+ // If idom has already reached the root, it doesn't make sense
+ // to check other retainers.
+ if (newDominatorIndex === rootPostOrderedIndex)
+ break;
+ }
+ }
+ if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) {
+ dominators[postOrderIndex] = newDominatorIndex;
+ changed = true;
+ nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+ beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
+ endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
+ for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
+ toNodeFieldIndex < endEdgeToNodeFieldIndex;
+ toNodeFieldIndex += edgeFieldsCount) {
+ var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
+ affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
+ }
+ }
+ }
+ }
+
+ var dominatorsTree = new Uint32Array(nodesCount);
+ for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) {
+ nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+ dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]];
+ }
+ return dominatorsTree;
+ },
+
+ _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal)
+ {
+ var nodeCount = this.nodeCount;
+ var nodes = this._nodes;
+ var nodeSelfSizeOffset = this._nodeSelfSizeOffset;
+ var nodeFieldCount = this._nodeFieldCount;
+ var dominatorsTree = this._dominatorsTree;
+ var retainedSizes = this._retainedSizes = new Float64Array(nodeCount);
+
+ for (var nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal)
+ retainedSizes[nodeOrdinal] = nodes[nodeOrdinal * nodeFieldCount + nodeSelfSizeOffset];
+
+ // Propagate retained sizes for each node excluding root.
+ for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) {
+ var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+ var dominatorOrdinal = dominatorsTree[nodeOrdinal];
+ retainedSizes[dominatorOrdinal] += retainedSizes[nodeOrdinal];
+ }
+ },
+
+ _buildDominatedNodes: function()
+ {
+ // Builds up two arrays:
+ // - "dominatedNodes" is a continuous array, where each node owns an
+ // interval (can be empty) with corresponding dominated nodes.
+ // - "indexArray" is an array of indexes in the "dominatedNodes"
+ // with the same positions as in the _nodeIndex.
+ var indexArray = this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1);
+ // All nodes except the root have dominators.
+ var dominatedNodes = this._dominatedNodes = new Uint32Array(this.nodeCount - 1);
+
+ // Count the number of dominated nodes for each node. Skip the root (node at
+ // index 0) as it is the only node that dominates itself.
+ var nodeFieldCount = this._nodeFieldCount;
+ var dominatorsTree = this._dominatorsTree;
+
+ var fromNodeOrdinal = 0;
+ var toNodeOrdinal = this.nodeCount;
+ var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+ if (rootNodeOrdinal === fromNodeOrdinal)
+ fromNodeOrdinal = 1;
+ else if (rootNodeOrdinal === toNodeOrdinal - 1)
+ toNodeOrdinal = toNodeOrdinal - 1;
+ else
+ throw new Error("Root node is expected to be either first or last");
+ for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal)
+ ++indexArray[dominatorsTree[nodeOrdinal]];
+ // Put in the first slot of each dominatedNodes slice the count of entries
+ // that will be filled.
+ var firstDominatedNodeIndex = 0;
+ for (var i = 0, l = this.nodeCount; i < l; ++i) {
+ var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i];
+ indexArray[i] = firstDominatedNodeIndex;
+ firstDominatedNodeIndex += dominatedCount;
+ }
+ indexArray[this.nodeCount] = dominatedNodes.length;
+ // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at
+ // index 0) as it is the only node that dominates itself.
+ for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) {
+ var dominatorOrdinal = dominatorsTree[nodeOrdinal];
+ var dominatedRefIndex = indexArray[dominatorOrdinal];
+ dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]);
+ dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount;
+ }
+ },
+
+ _calculateFlags: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ _calculateStatistics: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ userObjectsMapAndFlag: function()
+ {
+ throw new Error("Not implemented");
+ },
+
+ /**
+ * @param {string} baseSnapshotId
+ * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} baseSnapshotAggregates
+ * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Diff>}
+ */
+ calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates)
+ {
+ var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
+ if (snapshotDiff)
+ return snapshotDiff;
+ snapshotDiff = {};
+
+ var aggregates = this.aggregates(true, "allObjects");
+ for (var className in baseSnapshotAggregates) {
+ var baseAggregate = baseSnapshotAggregates[className];
+ var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]);
+ if (diff)
+ snapshotDiff[className] = diff;
+ }
+ var emptyBaseAggregate = new WebInspector.HeapSnapshotCommon.AggregateForDiff();
+ for (var className in aggregates) {
+ if (className in baseSnapshotAggregates)
+ continue;
+ snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]);
+ }
+
+ this._snapshotDiffs[baseSnapshotId] = snapshotDiff;
+ return snapshotDiff;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.AggregateForDiff} baseAggregate
+ * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
+ * @return {?WebInspector.HeapSnapshotCommon.Diff}
+ */
+ _calculateDiffForClass: function(baseAggregate, aggregate)
+ {
+ var baseIds = baseAggregate.ids;
+ var baseIndexes = baseAggregate.indexes;
+ var baseSelfSizes = baseAggregate.selfSizes;
+
+ var indexes = aggregate ? aggregate.idxs : [];
+
+ var i = 0, l = baseIds.length;
+ var j = 0, m = indexes.length;
+ var diff = new WebInspector.HeapSnapshotCommon.Diff();
+
+ var nodeB = this.createNode(indexes[j]);
+ while (i < l && j < m) {
+ var nodeAId = baseIds[i];
+ if (nodeAId < nodeB.id()) {
+ diff.deletedIndexes.push(baseIndexes[i]);
+ diff.removedCount++;
+ diff.removedSize += baseSelfSizes[i];
+ ++i;
+ } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
+ diff.addedIndexes.push(indexes[j]);
+ diff.addedCount++;
+ diff.addedSize += nodeB.selfSize();
+ nodeB.nodeIndex = indexes[++j];
+ } else { // nodeAId === nodeB.id()
+ ++i;
+ nodeB.nodeIndex = indexes[++j];
+ }
+ }
+ while (i < l) {
+ diff.deletedIndexes.push(baseIndexes[i]);
+ diff.removedCount++;
+ diff.removedSize += baseSelfSizes[i];
+ ++i;
+ }
+ while (j < m) {
+ diff.addedIndexes.push(indexes[j]);
+ diff.addedCount++;
+ diff.addedSize += nodeB.selfSize();
+ nodeB.nodeIndex = indexes[++j];
+ }
+ diff.countDelta = diff.addedCount - diff.removedCount;
+ diff.sizeDelta = diff.addedSize - diff.removedSize;
+ if (!diff.addedCount && !diff.removedCount)
+ return null;
+ return diff;
+ },
+
+ _nodeForSnapshotObjectId: function(snapshotObjectId)
+ {
+ for (var it = this._allNodes(); it.hasNext(); it.next()) {
+ if (it.node.id() === snapshotObjectId)
+ return it.node;
+ }
+ return null;
+ },
+
+ /**
+ * @param {string} snapshotObjectId
+ * @return {?string}
+ */
+ nodeClassName: function(snapshotObjectId)
+ {
+ var node = this._nodeForSnapshotObjectId(snapshotObjectId);
+ if (node)
+ return node.className();
+ return null;
+ },
+
+ /**
+ * @param {string} name
+ * @return {!Array.<number>}
+ */
+ idsOfObjectsWithName: function(name)
+ {
+ var ids = [];
+ for (var it = this._allNodes(); it.hasNext(); it.next()) {
+ if (it.item().name() === name)
+ ids.push(it.item().id());
+ }
+ return ids;
+ },
+
+ /**
+ * @param {string} snapshotObjectId
+ * @return {?Array.<string>}
+ */
+ dominatorIdsForNode: function(snapshotObjectId)
+ {
+ var node = this._nodeForSnapshotObjectId(snapshotObjectId);
+ if (!node)
+ return null;
+ var result = [];
+ while (!node.isRoot()) {
+ result.push(node.id());
+ node.nodeIndex = node.dominatorIndex();
+ }
+ return result;
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {!WebInspector.HeapSnapshotEdgesProvider}
+ */
+ createEdgesProvider: function(nodeIndex)
+ {
+ var node = this.createNode(nodeIndex);
+ var filter = this.containmentEdgesFilter();
+ var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this);
+ return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter
+ * @return {!WebInspector.HeapSnapshotEdgesProvider}
+ */
+ createEdgesProviderForTest: function(nodeIndex, filter)
+ {
+ var node = this.createNode(nodeIndex);
+ var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this);
+ return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider);
+ },
+
+ /**
+ * @return {?function(!WebInspector.HeapSnapshotEdge):boolean}
+ */
+ retainingEdgesFilter: function()
+ {
+ return null;
+ },
+
+ /**
+ * @return {?function(!WebInspector.HeapSnapshotEdge):boolean}
+ */
+ containmentEdgesFilter: function()
+ {
+ return null;
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {!WebInspector.HeapSnapshotEdgesProvider}
+ */
+ createRetainingEdgesProvider: function(nodeIndex)
+ {
+ var node = this.createNode(nodeIndex);
+ var filter = this.retainingEdgesFilter();
+ var indexProvider = new WebInspector.HeapSnapshotRetainerEdgeIndexProvider(this);
+ return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers(), indexProvider);
+ },
+
+ /**
+ * @param {string} baseSnapshotId
+ * @param {string} className
+ * @return {!WebInspector.HeapSnapshotNodesProvider}
+ */
+ createAddedNodesProvider: function(baseSnapshotId, className)
+ {
+ var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
+ var diffForClass = snapshotDiff[className];
+ return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes);
+ },
+
+ /**
+ * @param {!Array.<number>} nodeIndexes
+ * @return {!WebInspector.HeapSnapshotNodesProvider}
+ */
+ createDeletedNodesProvider: function(nodeIndexes)
+ {
+ return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes);
+ },
+
+ /**
+ * @return {?function(!WebInspector.HeapSnapshotNode):boolean}
+ */
+ classNodesFilter: function()
+ {
+ return null;
+ },
+
+ /**
+ * @param {string} className
+ * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
+ * @return {!WebInspector.HeapSnapshotNodesProvider}
+ */
+ createNodesProviderForClass: function(className, nodeFilter)
+ {
+ return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregatesWithFilter(nodeFilter)[className].idxs);
+ },
+
+ /**
+ * @param {number} nodeIndex
+ * @return {!WebInspector.HeapSnapshotNodesProvider}
+ */
+ createNodesProviderForDominator: function(nodeIndex)
+ {
+ var node = this.createNode(nodeIndex);
+ return new WebInspector.HeapSnapshotNodesProvider(this, null, this._dominatedNodesOfNode(node));
+ },
+
+ /**
+ * @return {number}
+ */
+ _maxJsNodeId: function()
+ {
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodes = this._nodes;
+ var nodesLength = nodes.length;
+ var id = 0;
+ for (var nodeIndex = this._nodeIdOffset; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
+ var nextId = nodes[nodeIndex];
+ // JS objects have odd ids, skip native objects.
+ if (nextId % 2 === 0)
+ continue;
+ if (id < nextId)
+ id = nextId;
+ }
+ return id;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.StaticData}
+ */
+ updateStaticData: function()
+ {
+ return new WebInspector.HeapSnapshotCommon.StaticData(this.nodeCount, this._rootNodeIndex, this.totalSize, this._maxJsNodeId());
+ }
+};
+
+/**
+ * @constructor
+ * @param {!WebInspector.HeapSnapshotItemIterator} iterator
+ * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider
+ */
+WebInspector.HeapSnapshotItemProvider = function(iterator, indexProvider)
+{
+ this._iterator = iterator;
+ this._indexProvider = indexProvider;
+ this._isEmpty = !iterator.hasNext();
+ /** @type {?Array.<number>} */
+ this._iterationOrder = null;
+ this._currentComparator = null;
+ this._sortedPrefixLength = 0;
+ this._sortedSuffixLength = 0;
+}
+
+WebInspector.HeapSnapshotItemProvider.prototype = {
+ _createIterationOrder: function()
+ {
+ if (this._iterationOrder)
+ return;
+ this._iterationOrder = [];
+ for (var iterator = this._iterator; iterator.hasNext(); iterator.next())
+ this._iterationOrder.push(iterator.item().itemIndex());
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isEmpty: function()
+ {
+ return this._isEmpty;
+ },
+
+ /**
+ * @param {number} begin
+ * @param {number} end
+ * @return {!WebInspector.HeapSnapshotCommon.ItemsRange}
+ */
+ serializeItemsRange: function(begin, end)
+ {
+ this._createIterationOrder();
+ if (begin > end)
+ throw new Error("Start position > end position: " + begin + " > " + end);
+ if (end > this._iterationOrder.length)
+ end = this._iterationOrder.length;
+ if (this._sortedPrefixLength < end && begin < this._iterationOrder.length - this._sortedSuffixLength) {
+ this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1 - this._sortedSuffixLength, begin, end - 1);
+ if (begin <= this._sortedPrefixLength)
+ this._sortedPrefixLength = end;
+ if (end >= this._iterationOrder.length - this._sortedSuffixLength)
+ this._sortedSuffixLength = this._iterationOrder.length - begin;
+ }
+ var position = begin;
+ var count = end - begin;
+ var result = new Array(count);
+ var iterator = this._iterator;
+ for (var i = 0 ; i < count; ++i) {
+ var itemIndex = this._iterationOrder[position++];
+ var item = this._indexProvider.itemForIndex(itemIndex);
+ result[i] = item.serialize();
+ }
+ return new WebInspector.HeapSnapshotCommon.ItemsRange(begin, end, this._iterationOrder.length, result);
+ },
+
+ sortAndRewind: function(comparator)
+ {
+ this._currentComparator = comparator;
+ this._sortedPrefixLength = 0;
+ this._sortedSuffixLength = 0;
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotItemProvider}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter
+ * @param {!WebInspector.HeapSnapshotEdgeIterator} edgesIter
+ * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider
+ */
+WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter, indexProvider)
+{
+ this.snapshot = snapshot;
+ var iter = filter ? new WebInspector.HeapSnapshotFilteredIterator(edgesIter, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)) : edgesIter;
+ WebInspector.HeapSnapshotItemProvider.call(this, iter, indexProvider);
+}
+
+WebInspector.HeapSnapshotEdgesProvider.prototype = {
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
+ * @param {number} leftBound
+ * @param {number} rightBound
+ * @param {number} windowLeft
+ * @param {number} windowRight
+ */
+ sort: function(comparator, leftBound, rightBound, windowLeft, windowRight)
+ {
+ var fieldName1 = comparator.fieldName1;
+ var fieldName2 = comparator.fieldName2;
+ var ascending1 = comparator.ascending1;
+ var ascending2 = comparator.ascending2;
+
+ var edgeA = this._iterator.item().clone();
+ var edgeB = edgeA.clone();
+ var nodeA = this.snapshot.createNode();
+ var nodeB = this.snapshot.createNode();
+
+ function compareEdgeFieldName(ascending, indexA, indexB)
+ {
+ edgeA.edgeIndex = indexA;
+ edgeB.edgeIndex = indexB;
+ if (edgeB.name() === "__proto__") return -1;
+ if (edgeA.name() === "__proto__") return 1;
+ var result =
+ edgeA.hasStringName() === edgeB.hasStringName() ?
+ (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) :
+ (edgeA.hasStringName() ? -1 : 1);
+ return ascending ? result : -result;
+ }
+
+ function compareNodeField(fieldName, ascending, indexA, indexB)
+ {
+ edgeA.edgeIndex = indexA;
+ nodeA.nodeIndex = edgeA.nodeIndex();
+ var valueA = nodeA[fieldName]();
+
+ edgeB.edgeIndex = indexB;
+ nodeB.nodeIndex = edgeB.nodeIndex();
+ var valueB = nodeB[fieldName]();
+
+ var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
+ return ascending ? result : -result;
+ }
+
+ function compareEdgeAndNode(indexA, indexB) {
+ var result = compareEdgeFieldName(ascending1, indexA, indexB);
+ if (result === 0)
+ result = compareNodeField(fieldName2, ascending2, indexA, indexB);
+ if (result === 0)
+ return indexA - indexB;
+ return result;
+ }
+
+ function compareNodeAndEdge(indexA, indexB) {
+ var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
+ if (result === 0)
+ result = compareEdgeFieldName(ascending2, indexA, indexB);
+ if (result === 0)
+ return indexA - indexB;
+ return result;
+ }
+
+ function compareNodeAndNode(indexA, indexB) {
+ var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
+ if (result === 0)
+ result = compareNodeField(fieldName2, ascending2, indexA, indexB);
+ if (result === 0)
+ return indexA - indexB;
+ return result;
+ }
+
+ if (fieldName1 === "!edgeName")
+ this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, windowLeft, windowRight);
+ else if (fieldName2 === "!edgeName")
+ this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, windowLeft, windowRight);
+ else
+ this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, windowLeft, windowRight);
+ },
+
+ __proto__: WebInspector.HeapSnapshotItemProvider.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotItemProvider}
+ * @param {!WebInspector.HeapSnapshot} snapshot
+ * @param {?function(!WebInspector.HeapSnapshotNode):boolean} filter
+ * @param {(!Array.<number>|!Uint32Array)} nodeIndexes
+ */
+WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes)
+{
+ this.snapshot = snapshot;
+ var indexProvider = new WebInspector.HeapSnapshotNodeIndexProvider(snapshot);
+ var it = new WebInspector.HeapSnapshotIndexRangeIterator(indexProvider, nodeIndexes);
+
+ if (filter)
+ it = new WebInspector.HeapSnapshotFilteredIterator(it, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter));
+ WebInspector.HeapSnapshotItemProvider.call(this, it, indexProvider);
+}
+
+WebInspector.HeapSnapshotNodesProvider.prototype = {
+ /**
+ * @param {string} snapshotObjectId
+ * @return {number}
+ */
+ nodePosition: function(snapshotObjectId)
+ {
+ this._createIterationOrder();
+ var node = this.snapshot.createNode();
+ for (var i = 0; i < this._iterationOrder.length; i++) {
+ node.nodeIndex = this._iterationOrder[i];
+ if (node.id() === snapshotObjectId)
+ break;
+ }
+ if (i === this._iterationOrder.length)
+ return -1;
+ var targetNodeIndex = this._iterationOrder[i];
+ var smallerCount = 0;
+ var compare = this._buildCompareFunction(this._currentComparator);
+ for (var i = 0; i < this._iterationOrder.length; i++) {
+ if (compare(this._iterationOrder[i], targetNodeIndex) < 0)
+ ++smallerCount;
+ }
+ return smallerCount;
+ },
+
+ /**
+ * @return {function(number,number):number}
+ */
+ _buildCompareFunction: function(comparator)
+ {
+ var nodeA = this.snapshot.createNode();
+ var nodeB = this.snapshot.createNode();
+ var fieldAccessor1 = nodeA[comparator.fieldName1];
+ var fieldAccessor2 = nodeA[comparator.fieldName2];
+ var ascending1 = comparator.ascending1 ? 1 : -1;
+ var ascending2 = comparator.ascending2 ? 1 : -1;
+
+ /**
+ * @param {function():*} fieldAccessor
+ * @param {number} ascending
+ * @return {number}
+ */
+ function sortByNodeField(fieldAccessor, ascending)
+ {
+ var valueA = fieldAccessor.call(nodeA);
+ var valueB = fieldAccessor.call(nodeB);
+ return valueA < valueB ? -ascending : (valueA > valueB ? ascending : 0);
+ }
+
+ /**
+ * @param {number} indexA
+ * @param {number} indexB
+ * @return {number}
+ */
+ function sortByComparator(indexA, indexB)
+ {
+ nodeA.nodeIndex = indexA;
+ nodeB.nodeIndex = indexB;
+ var result = sortByNodeField(fieldAccessor1, ascending1);
+ if (result === 0)
+ result = sortByNodeField(fieldAccessor2, ascending2);
+ return result || indexA - indexB;
+ }
+
+ return sortByComparator;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
+ * @param {number} leftBound
+ * @param {number} rightBound
+ * @param {number} windowLeft
+ * @param {number} windowRight
+ */
+ sort: function(comparator, leftBound, rightBound, windowLeft, windowRight)
+ {
+ this._iterationOrder.sortRange(this._buildCompareFunction(comparator), leftBound, rightBound, windowLeft, windowRight);
+ },
+
+ __proto__: WebInspector.HeapSnapshotItemProvider.prototype
+}
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotLoader.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotLoader.js
new file mode 100644
index 00000000000..8285fa8ccf5
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotLoader.js
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2012 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
+ * @param {!WebInspector.HeapSnapshotWorkerDispatcher} dispatcher
+ */
+WebInspector.HeapSnapshotLoader = function(dispatcher)
+{
+ this._reset();
+ this._progress = new WebInspector.HeapSnapshotProgress(dispatcher);
+}
+
+WebInspector.HeapSnapshotLoader.prototype = {
+ dispose: function()
+ {
+ this._reset();
+ },
+
+ _reset: function()
+ {
+ this._json = "";
+ this._state = "find-snapshot-info";
+ this._snapshot = {};
+ },
+
+ close: function()
+ {
+ if (this._json)
+ this._parseStringsArray();
+ },
+
+ /**
+ * @param {boolean} showHiddenData
+ * @return {!WebInspector.JSHeapSnapshot}
+ */
+ buildSnapshot: function(showHiddenData)
+ {
+ this._progress.updateStatus("Processing snapshot\u2026");
+ var result = new WebInspector.JSHeapSnapshot(this._snapshot, this._progress, showHiddenData);
+ this._reset();
+ return result;
+ },
+
+ _parseUintArray: function()
+ {
+ var index = 0;
+ var char0 = "0".charCodeAt(0), char9 = "9".charCodeAt(0), closingBracket = "]".charCodeAt(0);
+ var length = this._json.length;
+ while (true) {
+ while (index < length) {
+ var code = this._json.charCodeAt(index);
+ if (char0 <= code && code <= char9)
+ break;
+ else if (code === closingBracket) {
+ this._json = this._json.slice(index + 1);
+ return false;
+ }
+ ++index;
+ }
+ if (index === length) {
+ this._json = "";
+ return true;
+ }
+ var nextNumber = 0;
+ var startIndex = index;
+ while (index < length) {
+ var code = this._json.charCodeAt(index);
+ if (char0 > code || code > char9)
+ break;
+ nextNumber *= 10;
+ nextNumber += (code - char0);
+ ++index;
+ }
+ if (index === length) {
+ this._json = this._json.slice(startIndex);
+ return true;
+ }
+ this._array[this._arrayIndex++] = nextNumber;
+ }
+ },
+
+ _parseStringsArray: function()
+ {
+ this._progress.updateStatus("Parsing strings\u2026");
+ var closingBracketIndex = this._json.lastIndexOf("]");
+ if (closingBracketIndex === -1)
+ throw new Error("Incomplete JSON");
+ this._json = this._json.slice(0, closingBracketIndex + 1);
+ this._snapshot.strings = JSON.parse(this._json);
+ },
+
+ /**
+ * @param {string} chunk
+ */
+ write: function(chunk)
+ {
+ this._json += chunk;
+ while (true) {
+ switch (this._state) {
+ case "find-snapshot-info": {
+ var snapshotToken = "\"snapshot\"";
+ var snapshotTokenIndex = this._json.indexOf(snapshotToken);
+ if (snapshotTokenIndex === -1)
+ throw new Error("Snapshot token not found");
+ this._json = this._json.slice(snapshotTokenIndex + snapshotToken.length + 1);
+ this._state = "parse-snapshot-info";
+ this._progress.updateStatus("Loading snapshot info\u2026");
+ break;
+ }
+ case "parse-snapshot-info": {
+ var closingBracketIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(this._json);
+ if (closingBracketIndex === -1)
+ return;
+ this._snapshot.snapshot = /** @type {!HeapSnapshotHeader} */ (JSON.parse(this._json.slice(0, closingBracketIndex)));
+ this._json = this._json.slice(closingBracketIndex);
+ this._state = "find-nodes";
+ break;
+ }
+ case "find-nodes": {
+ var nodesToken = "\"nodes\"";
+ var nodesTokenIndex = this._json.indexOf(nodesToken);
+ if (nodesTokenIndex === -1)
+ return;
+ var bracketIndex = this._json.indexOf("[", nodesTokenIndex);
+ if (bracketIndex === -1)
+ return;
+ this._json = this._json.slice(bracketIndex + 1);
+ var node_fields_count = this._snapshot.snapshot.meta.node_fields.length;
+ var nodes_length = this._snapshot.snapshot.node_count * node_fields_count;
+ this._array = new Uint32Array(nodes_length);
+ this._arrayIndex = 0;
+ this._state = "parse-nodes";
+ break;
+ }
+ case "parse-nodes": {
+ var hasMoreData = this._parseUintArray();
+ this._progress.updateProgress("Loading nodes\u2026 %d\%", this._arrayIndex, this._array.length);
+ if (hasMoreData)
+ return;
+ this._snapshot.nodes = this._array;
+ this._state = "find-edges";
+ this._array = null;
+ break;
+ }
+ case "find-edges": {
+ var edgesToken = "\"edges\"";
+ var edgesTokenIndex = this._json.indexOf(edgesToken);
+ if (edgesTokenIndex === -1)
+ return;
+ var bracketIndex = this._json.indexOf("[", edgesTokenIndex);
+ if (bracketIndex === -1)
+ return;
+ this._json = this._json.slice(bracketIndex + 1);
+ var edge_fields_count = this._snapshot.snapshot.meta.edge_fields.length;
+ var edges_length = this._snapshot.snapshot.edge_count * edge_fields_count;
+ this._array = new Uint32Array(edges_length);
+ this._arrayIndex = 0;
+ this._state = "parse-edges";
+ break;
+ }
+ case "parse-edges": {
+ var hasMoreData = this._parseUintArray();
+ this._progress.updateProgress("Loading edges\u2026 %d\%", this._arrayIndex, this._array.length);
+ if (hasMoreData)
+ return;
+ this._snapshot.edges = this._array;
+ this._array = null;
+ // If there is allocation info parse it, otherwise jump straight to strings.
+ if (this._snapshot.snapshot.trace_function_count)
+ this._state = "find-trace-function-infos";
+ else
+ this._state = "find-strings";
+ break;
+ }
+ case "find-trace-function-infos": {
+ var tracesToken = "\"trace_function_infos\"";
+ var tracesTokenIndex = this._json.indexOf(tracesToken);
+ if (tracesTokenIndex === -1)
+ return;
+ var bracketIndex = this._json.indexOf("[", tracesTokenIndex);
+ if (bracketIndex === -1)
+ return;
+ this._json = this._json.slice(bracketIndex + 1);
+
+ var trace_function_info_field_count = this._snapshot.snapshot.meta.trace_function_info_fields.length;
+ var trace_function_info_length = this._snapshot.snapshot.trace_function_count * trace_function_info_field_count;
+ this._array = new Uint32Array(trace_function_info_length);
+ this._arrayIndex = 0;
+ this._state = "parse-trace-function-infos";
+ break;
+ }
+ case "parse-trace-function-infos": {
+ if (this._parseUintArray())
+ return;
+ this._snapshot.trace_function_infos = this._array;
+ this._array = null;
+ this._state = "find-trace-tree";
+ break;
+ }
+ case "find-trace-tree": {
+ var tracesToken = "\"trace_tree\"";
+ var tracesTokenIndex = this._json.indexOf(tracesToken);
+ if (tracesTokenIndex === -1)
+ return;
+ var bracketIndex = this._json.indexOf("[", tracesTokenIndex);
+ if (bracketIndex === -1)
+ return;
+ this._json = this._json.slice(bracketIndex);
+ this._state = "parse-trace-tree";
+ break;
+ }
+ case "parse-trace-tree": {
+ var stringsToken = "\"strings\"";
+ var stringsTokenIndex = this._json.indexOf(stringsToken);
+ if (stringsTokenIndex === -1)
+ return;
+ var bracketIndex = this._json.lastIndexOf("]", stringsTokenIndex);
+ this._snapshot.trace_tree = JSON.parse(this._json.substring(0, bracketIndex + 1));
+ this._json = this._json.slice(bracketIndex);
+ this._state = "find-strings";
+ this._progress.updateStatus("Loading strings\u2026");
+ break;
+ }
+ case "find-strings": {
+ var stringsToken = "\"strings\"";
+ var stringsTokenIndex = this._json.indexOf(stringsToken);
+ if (stringsTokenIndex === -1)
+ return;
+ var bracketIndex = this._json.indexOf("[", stringsTokenIndex);
+ if (bracketIndex === -1)
+ return;
+ this._json = this._json.slice(bracketIndex);
+ this._state = "accumulate-strings";
+ break;
+ }
+ case "accumulate-strings":
+ return;
+ }
+ }
+ }
+};
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorker.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorker.js
new file mode 100644
index 00000000000..d187e2dfac9
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorker.js
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+WebInspector = {};
+
+importScripts("../../common/UIString.js");
+importScripts("../../common/utilities.js");
+importScripts("../../ui/TextUtils.js");
+importScripts("../HeapSnapshotCommon.js");
+importScripts("AllocationProfile.js");
+importScripts("HeapSnapshot.js");
+importScripts("HeapSnapshotLoader.js");
+importScripts("HeapSnapshotWorkerDispatcher.js");
+importScripts("JSHeapSnapshot.js");
+
+function postMessageWrapper(message)
+{
+ postMessage(message);
+}
+
+var dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(this, postMessageWrapper);
+addEventListener("message", dispatcher.dispatchMessage.bind(dispatcher), false);
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.js
new file mode 100644
index 00000000000..4f3daa763ff
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.js
@@ -0,0 +1,111 @@
+/*
+ * 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
+ */
+WebInspector.HeapSnapshotWorkerDispatcher = function(globalObject, postMessage)
+{
+ this._objects = [];
+ this._global = globalObject;
+ this._postMessage = postMessage;
+}
+
+WebInspector.HeapSnapshotWorkerDispatcher.prototype = {
+ _findFunction: function(name)
+ {
+ var path = name.split(".");
+ var result = this._global;
+ for (var i = 0; i < path.length; ++i)
+ result = result[path[i]];
+ return result;
+ },
+
+ /**
+ * @param {string} name
+ * @param {*} data
+ */
+ sendEvent: function(name, data)
+ {
+ this._postMessage({eventName: name, data: data});
+ },
+
+ dispatchMessage: function(event)
+ {
+ var data = /** @type {!WebInspector.HeapSnapshotCommon.WorkerCommand } */(event.data);
+ var response = {callId: data.callId};
+ try {
+ switch (data.disposition) {
+ case "create": {
+ var constructorFunction = this._findFunction(data.methodName);
+ this._objects[data.objectId] = new constructorFunction(this);
+ break;
+ }
+ case "dispose": {
+ delete this._objects[data.objectId];
+ break;
+ }
+ case "getter": {
+ var object = this._objects[data.objectId];
+ var result = object[data.methodName];
+ response.result = result;
+ break;
+ }
+ case "factory": {
+ var object = this._objects[data.objectId];
+ var result = object[data.methodName].apply(object, data.methodArguments);
+ if (result)
+ this._objects[data.newObjectId] = result;
+ response.result = !!result;
+ break;
+ }
+ case "method": {
+ var object = this._objects[data.objectId];
+ response.result = object[data.methodName].apply(object, data.methodArguments);
+ break;
+ }
+ case "evaluateForTest": {
+ try {
+ response.result = eval(data.source)
+ } catch (e) {
+ response.result = e.toString();
+ }
+ break;
+ }
+ }
+ } catch (e) {
+ response.error = e.toString();
+ response.errorCallStack = e.stack;
+ if (data.methodName)
+ response.errorMethodName = data.methodName;
+ }
+ this._postMessage(response);
+ }
+};
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/JSHeapSnapshot.js b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/JSHeapSnapshot.js
new file mode 100644
index 00000000000..f1c592ab7e6
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/heap_snapshot_worker/JSHeapSnapshot.js
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2012 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.HeapSnapshot}
+ * @param {!Object} profile
+ * @param {!WebInspector.HeapSnapshotProgress} progress
+ * @param {boolean} showHiddenData
+ */
+WebInspector.JSHeapSnapshot = function(profile, progress, showHiddenData)
+{
+ this._nodeFlags = { // bit flags
+ canBeQueried: 1,
+ detachedDOMTreeNode: 2,
+ pageObject: 4, // The idea is to track separately the objects owned by the page and the objects owned by debugger.
+
+ visitedMarkerMask: 0x0ffff, // bits: 0,1111,1111,1111,1111
+ visitedMarker: 0x10000 // bits: 1,0000,0000,0000,0000
+ };
+ this._lazyStringCache = { };
+ WebInspector.HeapSnapshot.call(this, profile, progress, showHiddenData);
+}
+
+WebInspector.JSHeapSnapshot.prototype = {
+ /**
+ * @param {number} nodeIndex
+ * @return {!WebInspector.JSHeapSnapshotNode}
+ */
+ createNode: function(nodeIndex)
+ {
+ return new WebInspector.JSHeapSnapshotNode(this, nodeIndex);
+ },
+
+ /**
+ * @override
+ * @param {number} edgeIndex
+ * @return {!WebInspector.JSHeapSnapshotEdge}
+ */
+ createEdge: function(edgeIndex)
+ {
+ return new WebInspector.JSHeapSnapshotEdge(this, edgeIndex);
+ },
+
+ /**
+ * @override
+ * @param {number} retainerIndex
+ * @return {!WebInspector.JSHeapSnapshotRetainerEdge}
+ */
+ createRetainingEdge: function(retainerIndex)
+ {
+ return new WebInspector.JSHeapSnapshotRetainerEdge(this, retainerIndex);
+ },
+
+ /**
+ * @override
+ * @return {?function(!WebInspector.JSHeapSnapshotNode):boolean}
+ */
+ classNodesFilter: function()
+ {
+ /**
+ * @param {!WebInspector.JSHeapSnapshotNode} node
+ * @return {boolean}
+ */
+ function filter(node)
+ {
+ return node.isUserObject();
+ }
+ return this._showHiddenData ? null : filter;
+ },
+
+ /**
+ * @return {function(!WebInspector.HeapSnapshotEdge):boolean}
+ */
+ containmentEdgesFilter: function()
+ {
+ var showHiddenData = this._showHiddenData;
+ function filter(edge) {
+ if (edge.isInvisible())
+ return false;
+ if (showHiddenData)
+ return true;
+ return !edge.isHidden() && !edge.node().isHidden();
+ }
+ return filter;
+ },
+
+ /**
+ * @return {function(!WebInspector.HeapSnapshotEdge):boolean}
+ */
+ retainingEdgesFilter: function()
+ {
+ var containmentEdgesFilter = this.containmentEdgesFilter();
+ function filter(edge)
+ {
+ return containmentEdgesFilter(edge) && !edge.node().isRoot() && !edge.isWeak();
+ }
+ return filter;
+ },
+
+ dispose: function()
+ {
+ WebInspector.HeapSnapshot.prototype.dispose.call(this);
+ delete this._flags;
+ },
+
+ _calculateFlags: function()
+ {
+ this._flags = new Uint32Array(this.nodeCount);
+ this._markDetachedDOMTreeNodes();
+ this._markQueriableHeapObjects();
+ this._markPageOwnedNodes();
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @return {!boolean}
+ */
+ _isUserRoot: function(node)
+ {
+ return node.isUserRoot() || node.isDocumentDOMTreesRoot();
+ },
+
+ /**
+ * @param {function(!WebInspector.HeapSnapshotNode)} action
+ * @param {boolean=} userRootsOnly
+ */
+ forEachRoot: function(action, userRootsOnly)
+ {
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @param {string} name
+ * @return {?WebInspector.HeapSnapshotNode}
+ */
+ function getChildNodeByName(node, name)
+ {
+ for (var iter = node.edges(); iter.hasNext(); iter.next()) {
+ var child = iter.edge.node();
+ if (child.name() === name)
+ return child;
+ }
+ return null;
+ }
+
+ var visitedNodes = {};
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ */
+ function doAction(node)
+ {
+ var ordinal = node._ordinal();
+ if (!visitedNodes[ordinal]) {
+ action(node);
+ visitedNodes[ordinal] = true;
+ }
+ }
+
+ var gcRoots = getChildNodeByName(this.rootNode(), "(GC roots)");
+ if (!gcRoots)
+ return;
+
+ if (userRootsOnly) {
+ for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+ var node = iter.edge.node();
+ if (this._isUserRoot(node))
+ doAction(node);
+ }
+ } else {
+ for (var iter = gcRoots.edges(); iter.hasNext(); iter.next()) {
+ var subRoot = iter.edge.node();
+ for (var iter2 = subRoot.edges(); iter2.hasNext(); iter2.next())
+ doAction(iter2.edge.node());
+ doAction(subRoot);
+ }
+ for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next())
+ doAction(iter.edge.node())
+ }
+ },
+
+ /**
+ * @return {?{map: !Uint32Array, flag: number}}
+ */
+ userObjectsMapAndFlag: function()
+ {
+ return this._showHiddenData ? null : {
+ map: this._flags,
+ flag: this._nodeFlags.pageObject
+ };
+ },
+
+ _flagsOfNode: function(node)
+ {
+ return this._flags[node.nodeIndex / this._nodeFieldCount];
+ },
+
+ _markDetachedDOMTreeNodes: function()
+ {
+ var flag = this._nodeFlags.detachedDOMTreeNode;
+ var detachedDOMTreesRoot;
+ for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+ var node = iter.edge.node();
+ if (node.name() === "(Detached DOM trees)") {
+ detachedDOMTreesRoot = node;
+ break;
+ }
+ }
+
+ if (!detachedDOMTreesRoot)
+ return;
+
+ var detachedDOMTreeRE = /^Detached DOM tree/;
+ for (var iter = detachedDOMTreesRoot.edges(); iter.hasNext(); iter.next()) {
+ var node = iter.edge.node();
+ if (detachedDOMTreeRE.test(node.className())) {
+ for (var edgesIter = node.edges(); edgesIter.hasNext(); edgesIter.next())
+ this._flags[edgesIter.edge.node().nodeIndex / this._nodeFieldCount] |= flag;
+ }
+ }
+ },
+
+ _markQueriableHeapObjects: function()
+ {
+ // Allow runtime properties query for objects accessible from Window objects
+ // via regular properties, and for DOM wrappers. Trying to access random objects
+ // can cause a crash due to insonsistent state of internal properties of wrappers.
+ var flag = this._nodeFlags.canBeQueried;
+ var hiddenEdgeType = this._edgeHiddenType;
+ var internalEdgeType = this._edgeInternalType;
+ var invisibleEdgeType = this._edgeInvisibleType;
+ var weakEdgeType = this._edgeWeakType;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var edgeTypeOffset = this._edgeTypeOffset;
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var containmentEdges = this._containmentEdges;
+ var nodes = this._nodes;
+ var nodeCount = this.nodeCount;
+ var nodeFieldCount = this._nodeFieldCount;
+ var firstEdgeIndexes = this._firstEdgeIndexes;
+
+ var flags = this._flags;
+ var list = [];
+
+ for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+ if (iter.edge.node().isUserRoot())
+ list.push(iter.edge.node().nodeIndex / nodeFieldCount);
+ }
+
+ while (list.length) {
+ var nodeOrdinal = list.pop();
+ if (flags[nodeOrdinal] & flag)
+ continue;
+ flags[nodeOrdinal] |= flag;
+ var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+ var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
+ for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
+ var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+ if (flags[childNodeOrdinal] & flag)
+ continue;
+ var type = containmentEdges[edgeIndex + edgeTypeOffset];
+ if (type === hiddenEdgeType || type === invisibleEdgeType || type === internalEdgeType || type === weakEdgeType)
+ continue;
+ list.push(childNodeOrdinal);
+ }
+ }
+ },
+
+ _markPageOwnedNodes: function()
+ {
+ var edgeShortcutType = this._edgeShortcutType;
+ var edgeElementType = this._edgeElementType;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var edgeTypeOffset = this._edgeTypeOffset;
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var edgeWeakType = this._edgeWeakType;
+ var firstEdgeIndexes = this._firstEdgeIndexes;
+ var containmentEdges = this._containmentEdges;
+ var containmentEdgesLength = containmentEdges.length;
+ var nodes = this._nodes;
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodesCount = this.nodeCount;
+
+ var flags = this._flags;
+ var flag = this._nodeFlags.pageObject;
+ var visitedMarker = this._nodeFlags.visitedMarker;
+ var visitedMarkerMask = this._nodeFlags.visitedMarkerMask;
+ var markerAndFlag = visitedMarker | flag;
+
+ var nodesToVisit = new Uint32Array(nodesCount);
+ var nodesToVisitLength = 0;
+
+ var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+ var node = this.rootNode();
+ for (var edgeIndex = firstEdgeIndexes[rootNodeOrdinal], endEdgeIndex = firstEdgeIndexes[rootNodeOrdinal + 1];
+ edgeIndex < endEdgeIndex;
+ edgeIndex += edgeFieldsCount) {
+ var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
+ var nodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ if (edgeType === edgeElementType) {
+ node.nodeIndex = nodeIndex;
+ if (!node.isDocumentDOMTreesRoot())
+ continue;
+ } else if (edgeType !== edgeShortcutType)
+ continue;
+ var nodeOrdinal = nodeIndex / nodeFieldCount;
+ nodesToVisit[nodesToVisitLength++] = nodeOrdinal;
+ flags[nodeOrdinal] |= visitedMarker;
+ }
+
+ while (nodesToVisitLength) {
+ var nodeOrdinal = nodesToVisit[--nodesToVisitLength];
+ flags[nodeOrdinal] |= flag;
+ flags[nodeOrdinal] &= visitedMarkerMask;
+ var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+ var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
+ for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
+ var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+ if (flags[childNodeOrdinal] & markerAndFlag)
+ continue;
+ var type = containmentEdges[edgeIndex + edgeTypeOffset];
+ if (type === edgeWeakType)
+ continue;
+ nodesToVisit[nodesToVisitLength++] = childNodeOrdinal;
+ flags[childNodeOrdinal] |= visitedMarker;
+ }
+ }
+ },
+
+ _calculateStatistics: function()
+ {
+ var nodeFieldCount = this._nodeFieldCount;
+ var nodes = this._nodes;
+ var nodesLength = nodes.length;
+ var nodeTypeOffset = this._nodeTypeOffset;
+ var nodeSizeOffset = this._nodeSelfSizeOffset;;
+ var nodeNativeType = this._nodeNativeType;
+ var nodeCodeType = this._nodeCodeType;
+ var nodeConsStringType = this._nodeConsStringType;
+ var nodeSlicedStringType = this._nodeSlicedStringType;
+ var sizeNative = 0;
+ var sizeCode = 0;
+ var sizeStrings = 0;
+ var sizeJSArrays = 0;
+ var node = this.rootNode();
+ for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
+ node.nodeIndex = nodeIndex;
+ var nodeType = nodes[nodeIndex + nodeTypeOffset];
+ var nodeSize = nodes[nodeIndex + nodeSizeOffset];
+ if (nodeType === nodeNativeType)
+ sizeNative += nodeSize;
+ else if (nodeType === nodeCodeType)
+ sizeCode += nodeSize;
+ else if (nodeType === nodeConsStringType || nodeType === nodeSlicedStringType || node.type() === "string")
+ sizeStrings += nodeSize;
+ else if (node.name() === "Array")
+ sizeJSArrays += this._calculateArraySize(node);
+ }
+ this._statistics = new WebInspector.HeapSnapshotCommon.Statistics();
+ this._statistics.total = this.totalSize;
+ this._statistics.v8heap = this.totalSize - sizeNative;
+ this._statistics.native = sizeNative;
+ this._statistics.code = sizeCode;
+ this._statistics.jsArrays = sizeJSArrays;
+ this._statistics.strings = sizeStrings;
+ },
+
+ /**
+ * @param {!WebInspector.HeapSnapshotNode} node
+ * @return {number}
+ */
+ _calculateArraySize: function(node)
+ {
+ var size = node.selfSize();
+ var beginEdgeIndex = node._edgeIndexesStart();
+ var endEdgeIndex = node._edgeIndexesEnd();
+ var containmentEdges = this._containmentEdges;
+ var strings = this._strings;
+ var edgeToNodeOffset = this._edgeToNodeOffset;
+ var edgeTypeOffset = this._edgeTypeOffset;
+ var edgeNameOffset = this._edgeNameOffset;
+ var edgeFieldsCount = this._edgeFieldsCount;
+ var edgeInternalType = this._edgeInternalType;
+ for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
+ var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
+ if (edgeType !== edgeInternalType)
+ continue;
+ var edgeName = strings[containmentEdges[edgeIndex + edgeNameOffset]];
+ if (edgeName !== "elements")
+ continue;
+ var elementsNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+ node.nodeIndex = elementsNodeIndex;
+ if (node.retainersCount() === 1)
+ size += node.selfSize();
+ break;
+ }
+ return size;
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.Statistics}
+ */
+ getStatistics: function()
+ {
+ return this._statistics;
+ },
+
+ __proto__: WebInspector.HeapSnapshot.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotNode}
+ * @param {!WebInspector.JSHeapSnapshot} snapshot
+ * @param {number=} nodeIndex
+ */
+WebInspector.JSHeapSnapshotNode = function(snapshot, nodeIndex)
+{
+ WebInspector.HeapSnapshotNode.call(this, snapshot, nodeIndex)
+}
+
+WebInspector.JSHeapSnapshotNode.prototype = {
+ /**
+ * @return {boolean}
+ */
+ canBeQueried: function()
+ {
+ var flags = this._snapshot._flagsOfNode(this);
+ return !!(flags & this._snapshot._nodeFlags.canBeQueried);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isUserObject: function()
+ {
+ var flags = this._snapshot._flagsOfNode(this);
+ return !!(flags & this._snapshot._nodeFlags.pageObject);
+ },
+
+
+ /**
+ * @return {string}
+ */
+ name: function() {
+ var snapshot = this._snapshot;
+ if (this._type() === snapshot._nodeConsStringType) {
+ var string = snapshot._lazyStringCache[this.nodeIndex];
+ if (typeof string === "undefined") {
+ string = this._consStringName();
+ snapshot._lazyStringCache[this.nodeIndex] = string;
+ }
+ return string;
+ }
+ return WebInspector.HeapSnapshotNode.prototype.name.call(this);
+ },
+
+ _consStringName: function()
+ {
+ var snapshot = this._snapshot;
+ var consStringType = snapshot._nodeConsStringType;
+ var edgeInternalType = snapshot._edgeInternalType;
+ var edgeFieldsCount = snapshot._edgeFieldsCount;
+ var edgeToNodeOffset = snapshot._edgeToNodeOffset;
+ var edgeTypeOffset = snapshot._edgeTypeOffset;
+ var edgeNameOffset = snapshot._edgeNameOffset;
+ var strings = snapshot._strings;
+ var edges = snapshot._containmentEdges;
+ var firstEdgeIndexes = snapshot._firstEdgeIndexes;
+ var nodeFieldCount = snapshot._nodeFieldCount;
+ var nodeTypeOffset = snapshot._nodeTypeOffset;
+ var nodeNameOffset = snapshot._nodeNameOffset;
+ var nodes = snapshot._nodes;
+ var nodesStack = [];
+ nodesStack.push(this.nodeIndex);
+ var name = "";
+
+ while (nodesStack.length && name.length < 1024) {
+ var nodeIndex = nodesStack.pop();
+ if (nodes[nodeIndex + nodeTypeOffset] !== consStringType) {
+ name += strings[nodes[nodeIndex + nodeNameOffset]];
+ continue;
+ }
+ var nodeOrdinal = nodeIndex / nodeFieldCount;
+ var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+ var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
+ var firstNodeIndex = 0;
+ var secondNodeIndex = 0;
+ for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex && (!firstNodeIndex || !secondNodeIndex); edgeIndex += edgeFieldsCount) {
+ var edgeType = edges[edgeIndex + edgeTypeOffset];
+ if (edgeType === edgeInternalType) {
+ var edgeName = strings[edges[edgeIndex + edgeNameOffset]];
+ if (edgeName === "first")
+ firstNodeIndex = edges[edgeIndex + edgeToNodeOffset];
+ else if (edgeName === "second")
+ secondNodeIndex = edges[edgeIndex + edgeToNodeOffset];
+ }
+ }
+ nodesStack.push(secondNodeIndex);
+ nodesStack.push(firstNodeIndex);
+ }
+ return name;
+ },
+
+ /**
+ * @return {string}
+ */
+ className: function()
+ {
+ var type = this.type();
+ switch (type) {
+ case "hidden":
+ return "(system)";
+ case "object":
+ case "native":
+ return this.name();
+ case "code":
+ return "(compiled code)";
+ default:
+ return "(" + type + ")";
+ }
+ },
+
+ /**
+ * @return {number}
+ */
+ classIndex: function()
+ {
+ var snapshot = this._snapshot;
+ var nodes = snapshot._nodes;
+ var type = nodes[this.nodeIndex + snapshot._nodeTypeOffset];;
+ if (type === snapshot._nodeObjectType || type === snapshot._nodeNativeType)
+ return nodes[this.nodeIndex + snapshot._nodeNameOffset];
+ return -1 - type;
+ },
+
+ /**
+ * @return {number}
+ */
+ id: function()
+ {
+ var snapshot = this._snapshot;
+ return snapshot._nodes[this.nodeIndex + snapshot._nodeIdOffset];
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isHidden: function()
+ {
+ return this._type() === this._snapshot._nodeHiddenType;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isSynthetic: function()
+ {
+ return this._type() === this._snapshot._nodeSyntheticType;
+ },
+
+ /**
+ * @return {!boolean}
+ */
+ isUserRoot: function()
+ {
+ return !this.isSynthetic();
+ },
+
+ /**
+ * @return {!boolean}
+ */
+ isDocumentDOMTreesRoot: function()
+ {
+ return this.isSynthetic() && this.name() === "(Document DOM trees)";
+ },
+
+ /**
+ * @return {!WebInspector.HeapSnapshotCommon.Node}
+ */
+ serialize: function()
+ {
+ var result = WebInspector.HeapSnapshotNode.prototype.serialize.call(this);
+ var flags = this._snapshot._flagsOfNode(this);
+ if (flags & this._snapshot._nodeFlags.canBeQueried)
+ result.canBeQueried = true;
+ if (flags & this._snapshot._nodeFlags.detachedDOMTreeNode)
+ result.detachedDOMTreeNode = true;
+ return result;
+ },
+
+ __proto__: WebInspector.HeapSnapshotNode.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotEdge}
+ * @param {!WebInspector.JSHeapSnapshot} snapshot
+ * @param {number=} edgeIndex
+ */
+WebInspector.JSHeapSnapshotEdge = function(snapshot, edgeIndex)
+{
+ WebInspector.HeapSnapshotEdge.call(this, snapshot, edgeIndex);
+}
+
+WebInspector.JSHeapSnapshotEdge.prototype = {
+ /**
+ * @return {!WebInspector.JSHeapSnapshotEdge}
+ */
+ clone: function()
+ {
+ var snapshot = /** @type {!WebInspector.JSHeapSnapshot} */ (this._snapshot);
+ return new WebInspector.JSHeapSnapshotEdge(snapshot, this.edgeIndex);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasStringName: function()
+ {
+ if (!this.isShortcut())
+ return this._hasStringName();
+ return isNaN(parseInt(this._name(), 10));
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isElement: function()
+ {
+ return this._type() === this._snapshot._edgeElementType;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isHidden: function()
+ {
+ return this._type() === this._snapshot._edgeHiddenType;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isWeak: function()
+ {
+ return this._type() === this._snapshot._edgeWeakType;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isInternal: function()
+ {
+ return this._type() === this._snapshot._edgeInternalType;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isInvisible: function()
+ {
+ return this._type() === this._snapshot._edgeInvisibleType;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isShortcut: function()
+ {
+ return this._type() === this._snapshot._edgeShortcutType;
+ },
+
+ /**
+ * @return {string}
+ */
+ name: function()
+ {
+ if (!this.isShortcut())
+ return this._name();
+ var numName = parseInt(this._name(), 10);
+ return isNaN(numName) ? this._name() : numName;
+ },
+
+ /**
+ * @return {string}
+ */
+ toString: function()
+ {
+ var name = this.name();
+ switch (this.type()) {
+ case "context": return "->" + name;
+ case "element": return "[" + name + "]";
+ case "weak": return "[[" + name + "]]";
+ case "property":
+ return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]";
+ case "shortcut":
+ if (typeof name === "string")
+ return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]";
+ else
+ return "[" + name + "]";
+ case "internal":
+ case "hidden":
+ case "invisible":
+ return "{" + name + "}";
+ };
+ return "?" + name + "?";
+ },
+
+ _hasStringName: function()
+ {
+ return !this.isElement() && !this.isHidden();
+ },
+
+ _name: function()
+ {
+ return this._hasStringName() ? this._snapshot._strings[this._nameOrIndex()] : this._nameOrIndex();
+ },
+
+ _nameOrIndex: function()
+ {
+ return this._edges[this.edgeIndex + this._snapshot._edgeNameOffset];
+ },
+
+ _type: function()
+ {
+ return this._edges[this.edgeIndex + this._snapshot._edgeTypeOffset];
+ },
+
+ __proto__: WebInspector.HeapSnapshotEdge.prototype
+};
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotRetainerEdge}
+ * @param {!WebInspector.JSHeapSnapshot} snapshot
+ * @param {number} retainerIndex
+ */
+WebInspector.JSHeapSnapshotRetainerEdge = function(snapshot, retainerIndex)
+{
+ WebInspector.HeapSnapshotRetainerEdge.call(this, snapshot, retainerIndex);
+}
+
+WebInspector.JSHeapSnapshotRetainerEdge.prototype = {
+ /**
+ * @return {!WebInspector.JSHeapSnapshotRetainerEdge}
+ */
+ clone: function()
+ {
+ var snapshot = /** @type {!WebInspector.JSHeapSnapshot} */ (this._snapshot);
+ return new WebInspector.JSHeapSnapshotRetainerEdge(snapshot, this.retainerIndex());
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isHidden: function()
+ {
+ return this._edge().isHidden();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isInternal: function()
+ {
+ return this._edge().isInternal();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isInvisible: function()
+ {
+ return this._edge().isInvisible();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isShortcut: function()
+ {
+ return this._edge().isShortcut();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isWeak: function()
+ {
+ return this._edge().isWeak();
+ },
+
+ __proto__: WebInspector.HeapSnapshotRetainerEdge.prototype
+}
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/profiler/module.json b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/module.json
new file mode 100644
index 00000000000..37a56799c4d
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/profiler/module.json
@@ -0,0 +1,31 @@
+{
+ "extensions": [
+ {
+ "type": "@WebInspector.Panel",
+ "name": "profiles",
+ "title": "Profiles",
+ "order": 4,
+ "className": "WebInspector.ProfilesPanel"
+ },
+ {
+ "type": "@WebInspector.ContextMenu.Provider",
+ "contextTypes": ["WebInspector.RemoteObject"],
+ "className": "WebInspector.ProfilesPanel.ContextMenuProvider"
+ },
+ {
+ "type": "ui-setting",
+ "section": "Profiler",
+ "title": "Show advanced heap snapshot properties",
+ "settingName": "showAdvancedHeapSnapshotProperties",
+ "settingType": "checkbox"
+ },
+ {
+ "type": "ui-setting",
+ "section": "Profiler",
+ "title": "High resolution CPU profiling",
+ "settingName": "highResolutionCpuProfiling",
+ "settingType": "checkbox"
+ }
+ ],
+ "scripts": [ "ProfilesPanel.js" ]
+}