Title: [198196] trunk/Source/WebInspectorUI
Revision
198196
Author
[email protected]
Date
2016-03-14 22:02:28 -0700 (Mon, 14 Mar 2016)

Log Message

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>

Patch by Joseph Pecoraro <[email protected]> on 2016-03-14
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.

Modified Paths

Added Paths

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()
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to