Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (198195 => 198196)
--- trunk/Source/WebInspectorUI/ChangeLog 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/ChangeLog 2016-03-15 05:02:28 UTC (rev 198196)
@@ -1,3 +1,100 @@
+2016-03-14 Joseph Pecoraro <[email protected]>
+
+ Web Inspector: Show path from root to instances in the Heap Snapshot content view
+ https://bugs.webkit.org/show_bug.cgi?id=155478
+ <rdar://problem/25157408>
+
+ Reviewed by Timothy Hatcher.
+
+ * Localizations/en.lproj/localizedStrings.js:
+ * UserInterface/Main.html:
+ New strings and resources.
+
+ * UserInterface/Models/HeapSnapshotNode.js:
+ (WebInspector.HeapSnapshotNode.prototype.get shortestGCRootPath):
+ (WebInspector.HeapSnapshotNode.prototype._gcRootPaths.visitNode):
+ (WebInspector.HeapSnapshotNode.prototype._gcRootPaths):
+ Helper to get the shortest path from a GC root to the node.
+
+ * UserInterface/Models/HeapSnapshotRootPath.js: Added.
+ (WebInspector.HeapSnapshotRootPath):
+ (WebInspector.HeapSnapshotRootPath.emptyPath):
+ (WebInspector.HeapSnapshotRootPath.prototype.get node):
+ (WebInspector.HeapSnapshotRootPath.prototype.get parent):
+ (WebInspector.HeapSnapshotRootPath.prototype.get pathComponent):
+ (WebInspector.HeapSnapshotRootPath.prototype.get rootNode):
+ (WebInspector.HeapSnapshotRootPath.prototype.get fullPath):
+ (WebInspector.HeapSnapshotRootPath.prototype.isRoot):
+ (WebInspector.HeapSnapshotRootPath.prototype.isEmpty):
+ (WebInspector.HeapSnapshotRootPath.prototype.isGlobalScope):
+ (WebInspector.HeapSnapshotRootPath.prototype.isPathComponentImpossible):
+ (WebInspector.HeapSnapshotRootPath.prototype.isFullPathImpossible):
+ (WebInspector.HeapSnapshotRootPath.prototype.appendInternal):
+ (WebInspector.HeapSnapshotRootPath.prototype.appendArrayIndex):
+ (WebInspector.HeapSnapshotRootPath.prototype.appendPropertyName):
+ (WebInspector.HeapSnapshotRootPath.prototype.appendVariableName):
+ (WebInspector.HeapSnapshotRootPath.prototype.appendGlobalScopeName):
+ (WebInspector.HeapSnapshotRootPath.prototype.appendEdge):
+ (WebInspector.HeapSnapshotRootPath.prototype._canPropertyNameBeDotAccess):
+ Helper class, like PropertyPath, for building a string path to
+ a HeapSnapshotNode. Typically the path is built up with
+ HeapSnapshotEdges and so you can build a string such as:
+ `window.foo[0]["prop erty"]._foo`.
+
+ * UserInterface/Views/HeapAllocationsTimelineView.js:
+ (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotList):
+ (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotTimelineRecord):
+ (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotDiff):
+ (WebInspector.HeapAllocationsTimelineView.prototype.shown):
+ (WebInspector.HeapAllocationsTimelineView.prototype.hidden):
+ (WebInspector.HeapAllocationsTimelineView.prototype.closed):
+ Propogate shown/hidden to the contentViewContainer.
+ Cleanup the contentViewContainer when closing.
+
+ * UserInterface/Views/HeapSnapshotInstanceDataGridNode.js:
+ (WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode):
+ Helper for logging a HeapSnapshotNode value to the console. If the
+ path is possible from the root, just output the path in the console
+ otherwise use a synthetic "Heap Snapshot Object (@1234)" like string.
+ For strings, just get the preview as we won't get a real RemoteObject.
+
+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCellContent):
+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.appendPath):
+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.appendPathRow):
+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.sanitizeClassName):
+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.stringifyEdge):
+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler):
+ Give the @1234 id element a mouseover handler to display a popover
+ with the path from a root. Stop the path at "Window" if possible
+ to avoid displaying internals like "JSDOMWindowShell".
+
+ * UserInterface/Views/HeapSnapshotInstancesContentView.css:
+ (.heap-snapshot .object-id):
+ (.heap-snapshot .object-id:hover):
+ (.heap-snapshot > .data-grid tr:not(.selected) td .object-id): Deleted.
+ (.heap-snapshot .icon):
+ (.heap-snapshot-instance-popover-content):
+ (.heap-snapshot-instance-popover-content table):
+ (.heap-snapshot-instance-popover-content tr):
+ (.heap-snapshot-instance-popover-content td):
+ (.heap-snapshot-instance-popover-content td.edge-name):
+ (.heap-snapshot-instance-popover-content td.object-data):
+ (.heap-snapshot-instance-popover-content .node):
+ (.heap-snapshot-instance-popover-content .node *):
+ Styles for contents of the popover.
+
+ * UserInterface/Views/HeapSnapshotInstancesContentView.js:
+ (WebInspector.HeapSnapshotInstancesContentView.prototype.hidden):
+ * UserInterface/Views/HeapSnapshotInstancesDataGridTree.js:
+ (WebInspector.HeapSnapshotInstancesDataGridTree):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get popover):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get popoverNode):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.set popoverNode):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.hidden):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.willDismissPopover):
+ Have a single popover for the entire tree. Cache and clear
+ contents of the popover when appropriate.
+
2016-03-14 Daniel Bates <[email protected]>
Web Inspector: Display Content Security Policy hash in details sidebar for script and style elements
Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (198195 => 198196)
--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -20,7 +20,6 @@
localizedStrings["%d Errors, %d Warnings"] = "%d Errors, %d Warnings";
localizedStrings["%d More\u2026"] = "%d More\u2026";
localizedStrings["%d Warnings"] = "%d Warnings";
-localizedStrings["%d \u2A09 %d"] = "%d \u2A09 %d";
localizedStrings["%d \xd7 %d pixels"] = "%d \xd7 %d pixels";
localizedStrings["%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)"] = "%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)";
localizedStrings["%d matches"] = "%d matches";
@@ -118,6 +117,7 @@
localizedStrings["Breakdown of total allocation size by class"] = "Breakdown of total allocation size by class";
localizedStrings["Bubbling"] = "Bubbling";
localizedStrings["Busy"] = "Busy";
+localizedStrings["CSP Hash"] = "CSP Hash";
localizedStrings["Cached"] = "Cached";
localizedStrings["Call Stack"] = "Call Stack";
localizedStrings["Call Trees"] = "Call Trees";
@@ -188,7 +188,6 @@
localizedStrings["Container Regions"] = "Container Regions";
localizedStrings["Content"] = "Content";
localizedStrings["Content Flow"] = "Content Flow";
-localizedStrings["CSP Hash"] = "CSP Hash";
localizedStrings["Content Security Policy violation of directive: %s"] = "Content Security Policy violation of directive: %s";
localizedStrings["Continue script execution (%s or %s)"] = "Continue script execution (%s or %s)";
localizedStrings["Continue to Here"] = "Continue to Here";
@@ -566,7 +565,6 @@
localizedStrings["Readonly"] = "Readonly";
localizedStrings["Reasons for compositing:"] = "Reasons for compositing:";
localizedStrings["Recording"] = "Recording";
-localizedStrings["Records"] = "Records";
localizedStrings["Reference Issue"] = "Reference Issue";
localizedStrings["Reflection"] = "Reflection";
localizedStrings["Refresh"] = "Refresh";
@@ -603,6 +601,7 @@
localizedStrings["Role"] = "Role";
localizedStrings["Samples"] = "Samples";
localizedStrings["Scheme"] = "Scheme";
+localizedStrings["Scope"] = "Scope";
localizedStrings["Scope Chain"] = "Scope Chain";
localizedStrings["Script"] = "Script";
localizedStrings["Script Evaluated"] = "Script Evaluated";
@@ -657,7 +656,6 @@
localizedStrings["Snapshot %d"] = "Snapshot %d";
localizedStrings["Snapshot Comparison (%d and %d)"] = "Snapshot Comparison (%d and %d)";
localizedStrings["Snapshot List"] = "Snapshot List";
-localizedStrings["Snapshots"] = "Snapshots";
localizedStrings["Socket"] = "Socket";
localizedStrings["Sockets"] = "Sockets";
localizedStrings["Sort Ascending"] = "Sort Ascending";
@@ -705,6 +703,8 @@
localizedStrings["The webkit prefix is needed for this property.\nClick to insert a duplicate with the prefix."] = "The webkit prefix is needed for this property.\nClick to insert a duplicate with the prefix.";
localizedStrings["The webkit prefix is not necessary.\nClick to insert a duplicate without the prefix."] = "The webkit prefix is not necessary.\nClick to insert a duplicate without the prefix.";
localizedStrings["This Element"] = "This Element";
+localizedStrings["This object is a root"] = "This object is a root";
+localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
localizedStrings["This property needs a value.\nClick to open autocomplete."] = "This property needs a value.\nClick to open autocomplete.";
localizedStrings["Time"] = "Time";
localizedStrings["Time until the load event fired, click to show the Network Requests timeline"] = "Time until the load event fired, click to show the Network Requests timeline";
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2016-03-15 05:02:28 UTC (rev 198196)
@@ -308,8 +308,8 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
- <script src=""
<script src=""
<script src=""
<script src=""
@@ -319,6 +319,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -35,4 +35,62 @@
this.outgoingEdges = [];
this.incomingEdges = [];
}
+
+ // Public
+
+ get shortestGCRootPath()
+ {
+ // Returns an array from this node to a gcRoot node.
+ // E.g. [Node, Edge, Node, Edge, Node].
+ // Internal nodes are avoided, so if the path is empty this
+ // node is either a gcRoot or only reachable via Internal nodes.
+
+ if (this._shortestGCRootPath !== undefined)
+ return this._shortestGCRootPath;
+
+ let paths = this._gcRootPaths();
+ paths.sort((a, b) => a.length - b.length);
+ this._shortestGCRootPath = paths[0] || null;
+
+ return this._shortestGCRootPath;
+ }
+
+ // Private
+
+ _gcRootPaths()
+ {
+ if (this.gcRoot)
+ return [];
+
+ let paths = [];
+ let currentPath = [];
+ let visitedSet = new Set;
+
+ function visitNode(node) {
+ if (node.gcRoot) {
+ let fullPath = currentPath.slice();
+ fullPath.push(node);
+ paths.push(fullPath);
+ return;
+ }
+
+ if (visitedSet.has(node))
+ return;
+ visitedSet.add(node);
+
+ currentPath.push(node);
+ for (let parentEdge of node.incomingEdges) {
+ if (parentEdge.from.internal)
+ continue;
+ currentPath.push(parentEdge);
+ visitNode(parentEdge.from);
+ currentPath.pop();
+ }
+ currentPath.pop();
+ }
+
+ visitNode(this);
+
+ return paths;
+ }
};
Added: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js (0 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.HeapSnapshotRootPath = class HeapSnapshotRootPath extends WebInspector.Object
+{
+ constructor(node, pathComponent, parent, isGlobalScope)
+ {
+ super();
+
+ console.assert(!node || node instanceof WebInspector.HeapSnapshotNode);
+ console.assert(!pathComponent || typeof pathComponent === "string");
+ console.assert(!parent || parent instanceof WebInspector.HeapSnapshotRootPath);
+
+ this._node = node || null;
+ this._parent = parent || null;
+ this._pathComponent = typeof pathComponent === "string" ? pathComponent : null;
+ this._isGlobalScope = isGlobalScope || false;
+
+ // Become the new root when appended to an empty path.
+ if (this._parent && this._parent.isEmpty())
+ this._parent = null;
+ }
+
+ // Static
+
+ static emptyPath()
+ {
+ return new WebInspector.HeapSnapshotRootPath(null);
+ }
+
+ // Public
+
+ get node() { return this._node; }
+ get parent() { return this._parent; }
+ get pathComponent() { return this._pathComponent; }
+
+ get rootNode()
+ {
+ return this._parent ? this._parent.rootNode : this._node;
+ }
+
+ get fullPath()
+ {
+ let components = [];
+ for (let p = this; p && p.pathComponent; p = p.parent)
+ components.push(p.pathComponent);
+ components.reverse();
+ return components.join("");
+ }
+
+ isRoot()
+ {
+ return !this._parent;
+ }
+
+ isEmpty()
+ {
+ return !this._node;
+ }
+
+ isGlobalScope()
+ {
+ return this._isGlobalScope;
+ }
+
+ isPathComponentImpossible()
+ {
+ return this._pathComponent && this._pathComponent.startsWith("@");
+ }
+
+ isFullPathImpossible()
+ {
+ if (this.isEmpty())
+ return true;
+
+ if (this.isPathComponentImpossible())
+ return true;
+
+ if (this._parent)
+ return this._parent.isFullPathImpossible();
+
+ return false;
+ }
+
+ appendInternal(node)
+ {
+ return new WebInspector.HeapSnapshotRootPath(node, WebInspector.HeapSnapshotRootPath.SpecialPathComponent.InternalPropertyName, this);
+ }
+
+ appendArrayIndex(node, index)
+ {
+ let component = "[" + index + "]";
+ return new WebInspector.HeapSnapshotRootPath(node, component, this);
+ }
+
+ appendPropertyName(node, propertyName)
+ {
+ let component = this._canPropertyNameBeDotAccess(propertyName) ? "." + propertyName : "[" + doubleQuotedString(propertyName) + "]";
+ return new WebInspector.HeapSnapshotRootPath(node, component, this);
+ }
+
+ appendVariableName(node, variableName)
+ {
+ // Treat as a property of the global object, e.g. "window.foo".
+ if (this._isGlobalScope)
+ return this.appendPropertyName(node, variableName);
+ return new WebInspector.HeapSnapshotRootPath(node, variableName, this);
+ }
+
+ appendGlobalScopeName(node, globalScopeName)
+ {
+ return new WebInspector.HeapSnapshotRootPath(node, globalScopeName, this, true);
+ }
+
+ appendEdge(edge)
+ {
+ console.assert(edge instanceof WebInspector.HeapSnapshotEdge);
+
+ switch (edge.type) {
+ case WebInspector.HeapSnapshotEdge.EdgeType.Internal:
+ return this.appendInternal(edge.to);
+ case WebInspector.HeapSnapshotEdge.EdgeType.Index:
+ return this.appendArrayIndex(edge.to, edge.data);
+ case WebInspector.HeapSnapshotEdge.EdgeType.Property:
+ return this.appendPropertyName(edge.to, edge.data);
+ case WebInspector.HeapSnapshotEdge.EdgeType.Variable:
+ return this.appendVariableName(edge.to, edge.data);
+ }
+
+ console.error("Unexpected edge type", edge.type);
+ }
+
+ // Private
+
+ _canPropertyNameBeDotAccess(propertyName)
+ {
+ return /^(?![0-9])\w+$/.test(propertyName);
+ }
+};
+
+WebInspector.HeapSnapshotRootPath.SpecialPathComponent = {
+ InternalPropertyName: "@internal",
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -101,6 +101,7 @@
this._heapSnapshotDiff = null;
this._cancelSelectComparisonHeapSnapshots();
+ this._contentViewContainer.hidden();
this.removeSubview(this._contentViewContainer);
this.addSubview(this._dataGrid);
@@ -113,6 +114,7 @@
if (this._showingSnapshotList) {
this.removeSubview(this._dataGrid);
this.addSubview(this._contentViewContainer);
+ this._contentViewContainer.shown();
}
this._showingSnapshotList = false;
@@ -139,6 +141,7 @@
if (this._showingSnapshotList) {
this.removeSubview(this._dataGrid);
this.addSubview(this._contentViewContainer);
+ this._contentViewContainer.shown();
}
this._showingSnapshotList = false;
@@ -189,10 +192,28 @@
this.showHeapSnapshotTimelineRecord(timelineRecord);
}
+ shown()
+ {
+ super.shown();
+
+ if (!this._showingSnapshotList)
+ this._contentViewContainer.shown();
+ }
+
+ hidden()
+ {
+ super.hidden();
+
+ if (!this._showingSnapshotList)
+ this._contentViewContainer.hidden();
+ }
+
closed()
{
console.assert(this.representedObject instanceof WebInspector.Timeline);
this.representedObject.removeEventListener(null, null, this);
+
+ this._contentViewContainer.closeAllContentViews();
}
layout()
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -40,6 +40,51 @@
this._percent = (this._node.size / this._tree._heapSnapshot.totalSize) * 100;
}
+ // Static
+
+ static logHeapSnapshotNode(node)
+ {
+ let heapObjectIdentifier = node.id;
+
+ let synthetic = true;
+ let text = WebInspector.UIString("Heap Snapshot Object (@%d)").format(heapObjectIdentifier);
+
+ let gcRootPath = node.shortestGCRootPath;
+ if (gcRootPath) {
+ gcRootPath = gcRootPath.slice().reverse();
+ let windowIndex = gcRootPath.findIndex((x) => {
+ return x instanceof WebInspector.HeapSnapshotNode && x.className === "Window";
+ });
+
+ let heapSnapshotRootPath = WebInspector.HeapSnapshotRootPath.emptyPath();
+ for (let i = windowIndex === -1 ? 0 : windowIndex; i < gcRootPath.length; ++i) {
+ let component = gcRootPath[i];
+ if (component instanceof WebInspector.HeapSnapshotNode) {
+ if (component.className === "Window")
+ heapSnapshotRootPath = heapSnapshotRootPath.appendGlobalScopeName(component, "window");
+ } else if (component instanceof WebInspector.HeapSnapshotEdge)
+ heapSnapshotRootPath = heapSnapshotRootPath.appendEdge(component);
+ }
+
+ if (!heapSnapshotRootPath.isFullPathImpossible()) {
+ synthetic = false;
+ text = heapSnapshotRootPath.fullPath;
+ }
+ }
+
+ if (node.className === "string") {
+ HeapAgent.getPreview(heapObjectIdentifier, function(error, string, functionDetails, objectPreviewPayload) {
+ let remoteObject = error ? WebInspector.RemoteObject.fromPrimitiveValue(undefined) : WebInspector.RemoteObject.fromPrimitiveValue(string);
+ WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);
+ });
+ } else {
+ HeapAgent.getRemoteObject(heapObjectIdentifier, WebInspector.RuntimeManager.ConsoleObjectGroup, function(error, remoteObjectPayload) {
+ let remoteObject = error ? WebInspector.RemoteObject.fromPrimitiveValue(undefined) : WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+ WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);
+ });
+ }
+ }
+
// Protected
get data() { return this._node; }
@@ -77,8 +122,13 @@
let idElement = containerElement.appendChild(document.createElement("span"));
idElement.classList.add("object-id");
- idElement.textContent = "@" + id + " ";
+ idElement.textContent = "@" + id;
+ idElement.addEventListener("click", WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode.bind(null, this._node));
+ idElement.addEventListener("mouseover", this._mouseoverHandler.bind(this));
+ let spacerElement = containerElement.appendChild(document.createElement("span"));
+ spacerElement.textContent = " ";
+
HeapAgent.getPreview(id, (error, string, functionDetails, objectPreviewPayload) => {
if (error) {
let previewErrorElement = containerElement.appendChild(document.createElement("span"));
@@ -137,17 +187,116 @@
_contextMenuHandler(event)
{
let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
-
contextMenu.appendSeparator();
+ contextMenu.appendItem(WebInspector.UIString("Log Value"), WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode.bind(null, this._node));
+ }
- contextMenu.appendItem(WebInspector.UIString("Log Value"), () => {
- let heapObjectIdentifier = this._node.id;
- HeapAgent.getRemoteObject(heapObjectIdentifier, WebInspector.RuntimeManager.ConsoleObjectGroup, function(error, remoteObjectPayload) {
- const synthetic = true;
- let remoteObject = error ? WebInspector.RemoteObject.fromPrimitive(undefined) : WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
- let text = WebInspector.UIString("Heap Snapshot Object (@%d)").format(heapObjectIdentifier);
- WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);
+ _mouseoverHandler(event)
+ {
+ let targetFrame = WebInspector.Rect.rectFromClientRect(event.target.getBoundingClientRect());
+ if (!targetFrame.size.width && !targetFrame.size.height)
+ return;
+
+ if (this._tree.popoverNode === this._node)
+ return;
+
+ this._tree.popoverNode = this._node;
+
+ let popoverContentElement = document.createElement("div");
+ popoverContentElement.classList.add("heap-snapshot", "heap-snapshot-instance-popover-content");
+
+ function appendPath(path) {
+ let tableElement = popoverContentElement.appendChild(document.createElement("table"));
+ tableElement.classList.add("table");
+
+ path = path.slice().reverse();
+ let windowIndex = path.findIndex((x) => {
+ return x instanceof WebInspector.HeapSnapshotNode && x.className === "Window";
});
- });
+
+ let edge = null;
+ for (let i = windowIndex === -1 ? 0 : windowIndex; i < path.length; ++i) {
+ let component = path[i];
+ if (component instanceof WebInspector.HeapSnapshotEdge) {
+ edge = component;
+ continue;
+ }
+ appendPathRow(tableElement, edge, component);
+ edge = null;
+ }
+ }
+
+ function appendPathRow(tableElement, edge, node) {
+ let tableRow = tableElement.appendChild(document.createElement("tr"));
+
+ // Edge name.
+ let pathDataElement = tableRow.appendChild(document.createElement("td"));
+ pathDataElement.classList.add("edge-name");
+
+ if (node.className === "Window")
+ pathDataElement.textContent = "window";
+ else if (edge) {
+ let edgeString = stringifyEdge(edge);
+ pathDataElement.textContent = typeof edgeString === "string" ? edgeString : emDash;
+ } else
+ pathDataElement.textContent = emDash;
+
+ if (pathDataElement.textContent.length > 10)
+ pathDataElement.title = pathDataElement.textContent;
+
+ // Object.
+ let objectDataElement = tableRow.appendChild(document.createElement("td"));
+ objectDataElement.classList.add("object-data");
+
+ let containerElement = objectDataElement.appendChild(document.createElement("div"));
+ containerElement.classList.add("node");
+
+ let iconElement = containerElement.appendChild(document.createElement("img"));
+ iconElement.classList.add("icon", WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(node.className, node.internal));
+
+ let classNameElement = containerElement.appendChild(document.createElement("span"));
+ classNameElement.textContent = sanitizeClassName(node.className);
+
+ let idElement = containerElement.appendChild(document.createElement("span"));
+ idElement.classList.add("object-id");
+ idElement.textContent = " @" + node.id;
+ idElement.addEventListener("click", WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode.bind(null, node));
+ }
+
+ function sanitizeClassName(className) {
+ if (className.endsWith("LexicalEnvironment"))
+ return WebInspector.UIString("Scope");
+ return className;
+ }
+
+ function stringifyEdge(edge) {
+ switch(edge.type) {
+ case WebInspector.HeapSnapshotEdge.EdgeType.Property:
+ case WebInspector.HeapSnapshotEdge.EdgeType.Variable:
+ if (/^(?![0-9])\w+$/.test(edge.data))
+ return edge.data;
+ return "[" + doubleQuotedString(edge.data) + "]";
+ case WebInspector.HeapSnapshotEdge.EdgeType.Index:
+ return "[" + edge.data + "]";
+ case WebInspector.HeapSnapshotEdge.EdgeType.Internal:
+ default:
+ return null;
+ }
+ }
+
+ if (this._node.gcRoot) {
+ let textElement = popoverContentElement.appendChild(document.createElement("div"));
+ textElement.textContent = WebInspector.UIString("This object is a root");
+ } else {
+ let path = this._node.shortestGCRootPath;
+ if (path)
+ appendPath(path);
+ else {
+ let emptyElement = popoverContentElement.appendChild(document.createElement("div"));
+ emptyElement.textContent = WebInspector.UIString("This object is referenced by internal objects");
+ }
+ }
+
+ this._tree.popover.presentNewContentWithFrame(popoverContentElement, targetFrame.pad(2), [WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_X]);
}
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.css (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.css 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.css 2016-03-15 05:02:28 UTC (rev 198196)
@@ -37,10 +37,14 @@
display: inline-block;
}
-.heap-snapshot > .data-grid tr:not(.selected) td .object-id {
+.heap-snapshot .object-id {
color: gray;
}
+.heap-snapshot .object-id:hover {
+ text-decoration: underline;
+}
+
.heap-snapshot > .data-grid tr:not(.selected) td .preview-error {
color: red;
}
@@ -75,7 +79,7 @@
width: 16px;
height: 16px;
margin-top: 1px;
- margin-right: 3px;
+ margin-right: 4px;
content: url(../Images/TypeUndefined.svg);
}
@@ -121,3 +125,46 @@
display: inline-block;
height: 16px;
}
+
+.heap-snapshot-instance-popover-content {
+ white-space: pre;
+ padding: 5px 10px;
+}
+
+.heap-snapshot-instance-popover-content table {
+ border-collapse: collapse;
+}
+
+.heap-snapshot-instance-popover-content tr {
+ margin: 0;
+ padding: 1px 0;
+}
+
+.heap-snapshot-instance-popover-content td {
+ margin: 0;
+ padding: 0;
+}
+
+.heap-snapshot-instance-popover-content td.edge-name {
+ text-align: right;
+ max-width: 110px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ border-right: 1px solid var(--border-color);
+ padding-right: 5px;
+}
+
+.heap-snapshot-instance-popover-content td.object-data {
+ padding-left: 5px;
+}
+
+.heap-snapshot-instance-popover-content .node {
+ height: 17px;
+ line-height: 17px;
+}
+
+.heap-snapshot-instance-popover-content .node * {
+ display: inline-block;
+ vertical-align: top;
+}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -87,6 +87,15 @@
this._sortDataGrid();
}
+ // Protected
+
+ hidden()
+ {
+ super.hidden();
+
+ this._heapSnapshotDataGridTree.hidden();
+ }
+
// Private
_sortDataGrid()
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js (198195 => 198196)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js 2016-03-15 04:59:39 UTC (rev 198195)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js 2016-03-15 05:02:28 UTC (rev 198196)
@@ -37,6 +37,9 @@
this._sortComparator = sortComparator;
this._includeInternalObjects = includeInternalObjects;
+ this._popover = null;
+ this._popoverNode = null;
+
this._populateTopLevel();
this.sort();
}
@@ -80,6 +83,24 @@
this.sort();
}
+ get popover()
+ {
+ if (!this._popover)
+ this._popover = new WebInspector.Popover(this);
+
+ return this._popover;
+ }
+
+ get popoverNode()
+ {
+ return this._popoverNode;
+ }
+
+ set popoverNode(x)
+ {
+ this._popoverNode = x;
+ }
+
get children()
{
return this._children;
@@ -117,6 +138,19 @@
}
}
+ hidden()
+ {
+ if (this._popover && this._popover.visible)
+ this._popover.dismiss();
+ }
+
+ // Popover delegate
+
+ willDismissPopover(popover)
+ {
+ this._popoverNode = null;
+ }
+
// Private
_populateTopLevel()