2011-03-02 Mikhail Naganov <mnaganov@chromium.org>
authormnaganov@chromium.org <mnaganov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Mar 2011 16:45:37 +0000 (16:45 +0000)
committermnaganov@chromium.org <mnaganov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Mar 2011 16:45:37 +0000 (16:45 +0000)
        Reviewed by Pavel Feldman.

        Web Inspector: [Chromium] Landing detailed heap snapshots, part 4.
        https://bugs.webkit.org/show_bug.cgi?id=55563

        This part adds implementations for data grids used to display
        different heap snapshots projections. We are almost done.

        * English.lproj/localizedStrings.js:
        * WebCore.gypi:
        * bindings/v8/ScriptHeapSnapshot.cpp:
        (WebCore::ScriptHeapSnapshot::getExactRetainedSize):
        * bindings/v8/ScriptHeapSnapshot.h:
        * inspector/Inspector.idl:
        * inspector/InspectorProfilerAgent.cpp:
        (WebCore::InspectorProfilerAgent::getExactHeapSnapshotNodeRetainedSize):
        * inspector/InspectorProfilerAgent.h:
        * inspector/front-end/DetailedHeapshotGridNodes.js:
        (WebInspector.HeapSnapshotObjectNode):
        (WebInspector.HeapSnapshotObjectNode.prototype._createProvider):
        (WebInspector.HeapSnapshotInstanceNode):
        (WebInspector.HeapSnapshotInstanceNode.prototype._createProvider):
        (WebInspector.HeapSnapshotDominatorObjectNode):
        (WebInspector.HeapSnapshotDominatorObjectNode.prototype._createProvider):
        (MixInSnapshotNodeFunctions):
        * inspector/front-end/DetailedHeapshotView.js:
        (WebInspector.HeapSnapshotContainmentDataGrid):
        (WebInspector.HeapSnapshotSortableDataGrid):
        (WebInspector.HeapSnapshotConstructorsDataGrid):
        (WebInspector.HeapSnapshotDiffDataGrid):
        (WebInspector.HeapSnapshotDominatorsDataGrid):
        (WebInspector.HeapSnapshotRetainingPathsList):
        (WebInspector.DetailedHeapshotView.profileCallback):
        (WebInspector.DetailedHeapshotView):
        * inspector/front-end/HeapSnapshot.js:
        (WebInspector.HeapSnapshotEdge.prototype.get isInvisible):
        (WebInspector.HeapSnapshotEdge.prototype.toString):
        (WebInspector.HeapSnapshot.prototype._init):
        (WebInspector.HeapSnapshot.prototype._buildAggregatesIndexes):
        (WebInspector.HeapSnapshot.prototype._markInvisibleEdges):
        (WebInspector.HeapSnapshotPathFinder.prototype._skipEdge):
        * inspector/front-end/Images/helpButtonGlyph.png: Added.
        * inspector/front-end/Panel.js:
        (WebInspector.Panel.prototype.reset):
        * inspector/front-end/Popover.js:
        (WebInspector.Popover):
        (WebInspector.Popover.prototype.show):
        (WebInspector.Popover.prototype.hide):
        (WebInspector.Popover.prototype.get visible):
        * inspector/front-end/ProfilesPanel.js:
        (WebInspector.ProfilesPanel.prototype._reset):
        (WebInspector.ProfilesPanel.prototype.getProfile):
        * inspector/front-end/heapProfiler.css:
        * inspector/front-end/inspector.js:
        (WebInspector.resetFocusElement):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@80135 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
Source/WebCore/ChangeLog
Source/WebCore/English.lproj/localizedStrings.js
Source/WebCore/WebCore.gypi
Source/WebCore/bindings/js/ScriptHeapSnapshot.h
Source/WebCore/bindings/v8/ScriptHeapSnapshot.cpp
Source/WebCore/bindings/v8/ScriptHeapSnapshot.h
Source/WebCore/inspector/Inspector.idl
Source/WebCore/inspector/InspectorProfilerAgent.cpp
Source/WebCore/inspector/InspectorProfilerAgent.h
Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js
Source/WebCore/inspector/front-end/DetailedHeapshotView.js
Source/WebCore/inspector/front-end/HeapSnapshot.js
Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png [new file with mode: 0644]
Source/WebCore/inspector/front-end/Panel.js
Source/WebCore/inspector/front-end/Popover.js
Source/WebCore/inspector/front-end/ProfilesPanel.js
Source/WebCore/inspector/front-end/heapProfiler.css
Source/WebCore/inspector/front-end/inspector.js

index a49655a..de11abe 100644 (file)
@@ -1,3 +1,61 @@
+2011-03-02  Mikhail Naganov  <mnaganov@chromium.org>
+
+        Reviewed by Pavel Feldman.
+
+        Web Inspector: [Chromium] Landing detailed heap snapshots, part 4.
+        https://bugs.webkit.org/show_bug.cgi?id=55563
+
+        This part adds implementations for data grids used to display
+        different heap snapshots projections. We are almost done.
+
+        * English.lproj/localizedStrings.js:
+        * WebCore.gypi:
+        * bindings/v8/ScriptHeapSnapshot.cpp:
+        (WebCore::ScriptHeapSnapshot::getExactRetainedSize):
+        * bindings/v8/ScriptHeapSnapshot.h:
+        * inspector/Inspector.idl:
+        * inspector/InspectorProfilerAgent.cpp:
+        (WebCore::InspectorProfilerAgent::getExactHeapSnapshotNodeRetainedSize):
+        * inspector/InspectorProfilerAgent.h:
+        * inspector/front-end/DetailedHeapshotGridNodes.js:
+        (WebInspector.HeapSnapshotObjectNode):
+        (WebInspector.HeapSnapshotObjectNode.prototype._createProvider):
+        (WebInspector.HeapSnapshotInstanceNode):
+        (WebInspector.HeapSnapshotInstanceNode.prototype._createProvider):
+        (WebInspector.HeapSnapshotDominatorObjectNode):
+        (WebInspector.HeapSnapshotDominatorObjectNode.prototype._createProvider):
+        (MixInSnapshotNodeFunctions):
+        * inspector/front-end/DetailedHeapshotView.js:
+        (WebInspector.HeapSnapshotContainmentDataGrid):
+        (WebInspector.HeapSnapshotSortableDataGrid):
+        (WebInspector.HeapSnapshotConstructorsDataGrid):
+        (WebInspector.HeapSnapshotDiffDataGrid):
+        (WebInspector.HeapSnapshotDominatorsDataGrid):
+        (WebInspector.HeapSnapshotRetainingPathsList):
+        (WebInspector.DetailedHeapshotView.profileCallback):
+        (WebInspector.DetailedHeapshotView):
+        * inspector/front-end/HeapSnapshot.js:
+        (WebInspector.HeapSnapshotEdge.prototype.get isInvisible):
+        (WebInspector.HeapSnapshotEdge.prototype.toString):
+        (WebInspector.HeapSnapshot.prototype._init):
+        (WebInspector.HeapSnapshot.prototype._buildAggregatesIndexes):
+        (WebInspector.HeapSnapshot.prototype._markInvisibleEdges):
+        (WebInspector.HeapSnapshotPathFinder.prototype._skipEdge):
+        * inspector/front-end/Images/helpButtonGlyph.png: Added.
+        * inspector/front-end/Panel.js:
+        (WebInspector.Panel.prototype.reset):
+        * inspector/front-end/Popover.js:
+        (WebInspector.Popover):
+        (WebInspector.Popover.prototype.show):
+        (WebInspector.Popover.prototype.hide):
+        (WebInspector.Popover.prototype.get visible):
+        * inspector/front-end/ProfilesPanel.js:
+        (WebInspector.ProfilesPanel.prototype._reset):
+        (WebInspector.ProfilesPanel.prototype.getProfile):
+        * inspector/front-end/heapProfiler.css:
+        * inspector/front-end/inspector.js:
+        (WebInspector.resetFocusElement):
+
 2011-03-02  David Kilzer  <ddkilzer@apple.com>
 
         <http://webkit.org/b/55534> Clean up macros in Extensions3DOpenGL.cpp
index 6f759a2..cd4e6aa 100644 (file)
Binary files a/Source/WebCore/English.lproj/localizedStrings.js and b/Source/WebCore/English.lproj/localizedStrings.js differ
index f6d80e1..f9fabb3 100644 (file)
             'inspector/front-end/Images/goArrow.png',
             'inspector/front-end/Images/graphLabelCalloutLeft.png',
             'inspector/front-end/Images/graphLabelCalloutRight.png',
+            'inspector/front-end/Images/helpButtonGlyph.png',
             'inspector/front-end/Images/largerResourcesButtonGlyph.png',
             'inspector/front-end/Images/localStorage.png',
             'inspector/front-end/Images/networkIcon.png',
index a341ddc..6b40e20 100644 (file)
@@ -50,6 +50,7 @@ public:
     unsigned int uid() const { return 0; }
 
     void writeJSON(OutputStream*) { }
+    int exactRetainedSize(uint64_t) { return -1; }
 
 private:
     ScriptHeapSnapshot() { }
index c35d508..09e1e54 100644 (file)
@@ -76,4 +76,10 @@ void ScriptHeapSnapshot::writeJSON(ScriptHeapSnapshot::OutputStream* stream)
     m_snapshot->Serialize(&outputStream, v8::HeapSnapshot::kJSON);
 }
 
+int ScriptHeapSnapshot::exactRetainedSize(uint64_t nodeId)
+{
+    const v8::HeapGraphNode* node = m_snapshot->GetNodeById(nodeId);
+    return node ? node->GetRetainedSize(true) : -1;
+}
+
 } // namespace WebCore
index d3ae022..6cfd76d 100644 (file)
@@ -59,6 +59,7 @@ public:
     String title() const;
     unsigned int uid() const;
     void writeJSON(OutputStream* stream);
+    int exactRetainedSize(uint64_t nodeId);
 
 private:
     ScriptHeapSnapshot(const v8::HeapSnapshot* snapshot)
index 92c156f..f4eaa4d 100644 (file)
@@ -264,6 +264,7 @@ module core {
 
         // FIXME: split into Profiler and HeapProfiler.
         void takeHeapSnapshot(in boolean detailed);
+        void getExactHeapSnapshotNodeRetainedSize(in unsigned long uid, in unsigned long nodeId, out long size);
         [event] void addProfileHeader(out Object header);
         [event] void addHeapSnapshotChunk(out unsigned long uid, out String chunk);
         [event] void finishHeapSnapshot(out unsigned long uid);
index 93852c7..92d8f7e 100644 (file)
@@ -151,6 +151,16 @@ String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool increment
     return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
 }
 
+void InspectorProfilerAgent::getExactHeapSnapshotNodeRetainedSize(ErrorString*, unsigned long uid, unsigned long nodeId, long* size)
+{
+    HeapSnapshotsMap::iterator it = m_snapshots.find(uid);
+    if (it != m_snapshots.end()) {
+        RefPtr<ScriptHeapSnapshot> snapshot = it->second;
+        *size = snapshot->exactRetainedSize(nodeId);
+    } else
+        *size = -1;
+}
+
 void InspectorProfilerAgent::getProfileHeaders(ErrorString*, RefPtr<InspectorArray>* headers)
 {
     ProfilesMap::iterator profilesEnd = m_profiles.end();
index d436418..e952c59 100644 (file)
@@ -63,6 +63,7 @@ public:
     void enable(bool skipRecompile);
     bool enabled() { return m_enabled; }
     String getCurrentUserInitiatedProfileName(bool incrementProfileNumber = false);
+    void getExactHeapSnapshotNodeRetainedSize(ErrorString*, unsigned long uid, unsigned long nodeId, long* size);
     void getProfileHeaders(ErrorString* error, RefPtr<InspectorArray>* headers);
     void getProfile(ErrorString* error, const String& type, unsigned uid, RefPtr<InspectorObject>* profileObject);
     bool isRecordingUserInitiatedProfile() { return m_recordingUserInitiatedProfile; }
index 06a7cc8..14ba142 100644 (file)
@@ -232,7 +232,7 @@ WebInspector.HeapSnapshotGenericObjectNode.prototype.__proto__ = WebInspector.He
 WebInspector.HeapSnapshotObjectNode = function(tree, edge)
 {
     var node = edge.node;
-    var provider = this._createEdgesProvider(tree.snapshot, node.rawEdges);
+    var provider = this._createProvider(tree.snapshot, node.rawEdges);
     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node, !provider.isEmpty, 100);
     this._referenceName = edge.name;
     this._referenceType = edge.type;
@@ -245,7 +245,7 @@ WebInspector.HeapSnapshotObjectNode.prototype = {
         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, provider.item);
     },
 
-    _createEdgesProvider: function(snapshot, rawEdges)
+    _createProvider: function(snapshot, rawEdges)
     {
         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
         return new WebInspector.HeapSnapshotEdgesProvider(
@@ -321,7 +321,7 @@ WebInspector.HeapSnapshotObjectNode.prototype.__proto__ = WebInspector.HeapSnaps
 
 WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node)
 {
-    var provider = this._createEdgesProvider(baseSnapshot || snapshot, node.rawEdges);  
+    var provider = this._createProvider(baseSnapshot || snapshot, node.rawEdges);  
     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node, !provider.isEmpty, 100);
     this._isDeletedNode = !!baseSnapshot;
     this._provider = provider;    
@@ -333,7 +333,7 @@ WebInspector.HeapSnapshotInstanceNode.prototype = {
         return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, provider.item);
     },
 
-    _createEdgesProvider: function(snapshot, rawEdges)
+    _createProvider: function(snapshot, rawEdges)
     {
         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
         return new WebInspector.HeapSnapshotEdgesProvider(
@@ -639,7 +639,7 @@ WebInspector.HeapSnapshotDiffNode.prototype.__proto__ = WebInspector.HeapSnapsho
 
 WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node)
 {
-    var provider = this._createNodesProvider(tree.snapshot, node.nodeIndex);
+    var provider = this._createProvider(tree.snapshot, node.nodeIndex);
     WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node, !provider.isEmpty, 25);
     this._provider = provider;
 };
@@ -650,7 +650,7 @@ WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
         return new WebInspector.HeapSnapshotDominatorObjectNode(this.dataGrid, provider.item);
     },
 
-    _createNodesProvider: function(snapshot, nodeIndex)
+    _createProvider: function(snapshot, nodeIndex)
     {
         var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData;
         return new WebInspector.HeapSnapshotNodesProvider(
@@ -693,3 +693,15 @@ WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
 };
 
 WebInspector.HeapSnapshotDominatorObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
+
+function MixInSnapshotNodeFunctions(sourcePrototype, targetPrototype)
+{
+    targetPrototype._childHashForEntity = sourcePrototype._childHashForEntity;
+    targetPrototype._childHashForNode = sourcePrototype._childHashForNode;
+    targetPrototype.comparator = sourcePrototype.comparator;
+    targetPrototype._createChildNode = sourcePrototype._createChildNode;
+    targetPrototype._createProvider = sourcePrototype._createProvider;
+    targetPrototype.populateChildren = sourcePrototype.populateChildren;
+    targetPrototype._saveChildren = sourcePrototype._saveChildren;
+    targetPrototype.sort = sourcePrototype.sort;
+}
index 5291bf2..9f51a4f 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+WebInspector.HeapSnapshotContainmentDataGrid = function()
+{
+    var columns = {
+        object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true, sort: "ascending" },
+        shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
+        retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sortable: true }
+    };
+    WebInspector.DataGrid.call(this, columns);
+    this.addEventListener("sorting changed", this.sort, this);
+    this._defaultPopulateCount = 100;
+}
+
+WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
+    setDataSource: function(snapshotView, snapshot)
+    {
+        this.snapshotView = snapshotView;
+        this.snapshot = snapshot;
+        this.snapshotNodeIndex = this.snapshot._rootNodeIndex;
+        this._provider = this._createProvider(snapshot, snapshot.rootNode.rawEdges);
+        this.sort();
+    }
+};
+
+MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotObjectNode.prototype, WebInspector.HeapSnapshotContainmentDataGrid.prototype);
+WebInspector.HeapSnapshotContainmentDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype;
+
+WebInspector.HeapSnapshotSortableDataGrid = function(columns)
+{
+    WebInspector.DataGrid.call(this, columns);
+    this.addEventListener("sorting changed", this.sortingChanged, this);
+}
+
+WebInspector.HeapSnapshotSortableDataGrid.prototype = {
+    sortingChanged: function()
+    {
+        var sortAscending = this.sortOrder === "ascending";
+        var sortColumnIdentifier = this.sortColumnIdentifier;
+        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)
+    {
+        var children = this.children;
+        this.removeChildren();
+        children.sort(sortFunction);
+        for (var i = 0, l = children.length; i < l; ++i) {
+            var child = children[i];
+            this.appendChild(child);
+            if (child.expanded)
+                child.sort();
+        }
+    }
+};
+
+WebInspector.HeapSnapshotSortableDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype;
+
+WebInspector.HeapSnapshotConstructorsDataGrid = function()
+{
+    var columns = {
+        object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true },
+        count: { title: WebInspector.UIString("#"), width: "45px", sortable: true },
+        shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
+        retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true }
+    };
+    WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+}
+
+WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
+    _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]
+        }[sortColumn];
+    },
+
+    setDataSource: function(snapshotView, snapshot)
+    {
+        this.snapshotView = snapshotView;
+        this.snapshot = snapshot;
+        this.populateChildren();
+        this.sortingChanged();
+    },
+
+    populateChildren: function()
+    {
+        var aggregates = this.snapshot.aggregates();
+        for (var constructor in aggregates)
+            this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor]));
+    }
+};
+
+WebInspector.HeapSnapshotConstructorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
+
+WebInspector.HeapSnapshotDiffDataGrid = function()
+{
+    var columns = {
+        object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true },
+        // \xb1 is a "plus-minus" sign.
+        addedCount: { title: WebInspector.UIString("# New"), width: "72px", sortable: true, sort: "descending" },
+        removedCount: { title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true },
+        // \u0394 is a Greek delta letter.
+        countDelta: { title: "\u0394", width: "40px", sortable: true },
+        addedSize: { title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true },
+        removedSize: { title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true },
+        sizeDelta: { title: "\u0394", width: "72px", sortable: true }
+    };
+    WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+}
+
+WebInspector.HeapSnapshotDiffDataGrid.prototype = {
+    _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(snapshotView, snapshot)
+    {
+        this.snapshotView = snapshotView;
+        this.snapshot = snapshot;
+    },
+
+    setBaseDataSource: function(baseSnapshot)
+    {
+        this.baseSnapshot = baseSnapshot;
+        this.removeChildren();
+        if (this.baseSnapshot !== this.snapshot) {
+            this.populateChildren();
+            this.sortingChanged();
+        }
+    },
+
+    populateChildren: function()
+    {
+        var baseClasses = this.baseSnapshot.aggregates(true);
+        var classes = this.snapshot.aggregates(true);
+        for (var clss in baseClasses) {
+            var node = new WebInspector.HeapSnapshotDiffNode(this, clss, baseClasses[clss], classes[clss]);
+            if (!node.zeroDiff)
+                this.appendChild(node);
+        }
+        for (clss in classes) {
+            if (!(clss in baseClasses)) {
+                var node = new WebInspector.HeapSnapshotDiffNode(this, clss, null, classes[clss]);
+                if (!node.zeroDiff)
+                    this.appendChild(node);
+            }
+        }
+    }
+};
+
+WebInspector.HeapSnapshotDiffDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
+
+WebInspector.HeapSnapshotDominatorsDataGrid = function()
+{
+    var columns = {
+        object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true },
+        shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
+        retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true }
+    };
+    WebInspector.DataGrid.call(this, columns);
+    this.addEventListener("sorting changed", this.sort, this);
+    this._defaultPopulateCount = 25;
+}
+
+WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
+    setDataSource: function(snapshotView, snapshot)
+    {
+        this.snapshotView = snapshotView;
+        this.snapshot = snapshot;
+        this.snapshotNodeIndex = this.snapshot._rootNodeIndex;
+        this._provider = this._createProvider(snapshot, this.snapshotNodeIndex);
+        this.sort();
+    }
+};
+
+MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotDominatorObjectNode.prototype, WebInspector.HeapSnapshotDominatorsDataGrid.prototype);
+WebInspector.HeapSnapshotDominatorsDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype;
+
+WebInspector.HeapSnapshotRetainingPathsList = function()
+{
+    var columns = {
+        path: { title: WebInspector.UIString("Retaining path"), sortable: true },
+        len: { title: WebInspector.UIString("Length"), width: "90px", sortable: true, sort: "ascending" }
+    };
+    WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+}
+
+WebInspector.HeapSnapshotRetainingPathsList.prototype = {
+    _sortFields: function(sortColumn, sortAscending)
+    {
+        return {
+            path: ["path", sortAscending, "len", true],
+            len: ["len", sortAscending, "path", true]
+        }[sortColumn];
+    },
+
+    setDataSource: function(snapshotView, snapshot, nodeIndex, prefix)
+    {
+        this.snapshotView = snapshotView;
+        this._prefix = prefix;
+
+        if (this.pathFinder)
+            this.searchCancelled();
+
+        this.pathFinder = new WebInspector.HeapSnapshotPathFinder(snapshot, nodeIndex);
+
+        this.removeChildren();
+
+        this._counter = 0;
+        this.showNext(10);
+    },
+
+    showNext: function(pathsCount)
+    {
+        WebInspector.PleaseWaitMessage.prototype.show(this.element, this.searchCancelled.bind(this, pathsCount));
+        window.setTimeout(startSearching.bind(this), 500);
+
+        function startSearching()
+        {
+            if (this._cancel !== this.pathFinder) {
+                if (this._counter < pathsCount) {
+                    var result = this.pathFinder.findNext();
+                    if (result === null) {
+                        WebInspector.PleaseWaitMessage.prototype.hide();
+                        if (!this.children.length)
+                            this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("This object is either only accessible via hidden properties, or current path search depth isn't enough."), len:""}, false));
+                        return;
+                    } else if (result !== false) {
+                        if (this._prefix)
+                            result.path = this._prefix + result.path;
+                        this.appendChild(new WebInspector.DataGridNode(result, false));
+                        ++this._counter;
+                    }
+                    window.setTimeout(startSearching.bind(this), 0);
+                    return;
+                } else
+                    this.searchCancelled.call(this, pathsCount);
+            }
+            this._cancel = false;
+        }
+    },
+
+    searchCancelled: function(pathsCount)
+    {
+        WebInspector.PleaseWaitMessage.prototype.hide();
+        this._counter = 0;
+        this._cancel = this.pathFinder;
+        if (pathsCount) {
+            this.appendChild(new WebInspector.ShowMoreDataGridNode(this.showNext.bind(this), pathsCount));
+            this.sortingChanged();
+        }
+    },
+
+    _performSorting: function(sortFunction)
+    {
+        function DataExtractorWrapper(nodeA, nodeB)
+        {
+            return sortFunction(nodeA.data, nodeB.data);
+        }
+
+        this.sortNodes(DataExtractorWrapper);
+    }
+};
+
+WebInspector.HeapSnapshotRetainingPathsList.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
+
 WebInspector.DetailedHeapshotView = function(parent, profile)
 {
     WebInspector.View.call(this);
 
-    this.element.addStyleClass("heap-snapshot-view");
+    this.element.addStyleClass("detailed-heapshot-view");
 
     this.parent = parent;
-    this.profile = profile;
+    this.parent.addEventListener("profile added", this._updateBaseOptions, this);
+
+    this.showCountAsPercent = false;
+    this.showShallowSizeAsPercent = false;
+    this.showRetainedSizeAsPercent = false;
+
+    this.containmentView = new WebInspector.View();
+    this.containmentView.element.addStyleClass("view");
+    this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid();
+    this.containmentDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true);
+    this.containmentDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true);
+    this.containmentView.element.appendChild(this.containmentDataGrid.element);
+    this.element.appendChild(this.containmentView.element);
+
+    this.constructorsView = new WebInspector.View();
+    this.constructorsView.element.addStyleClass("view");
+    this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid();
+    this.constructorsDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true);
+    this.constructorsDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true);
+    this.constructorsView.element.appendChild(this.constructorsDataGrid.element);
+    this.element.appendChild(this.constructorsView.element);
+
+    this.diffView = new WebInspector.View();
+    this.diffView.element.addStyleClass("view");
+    this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid();
+    this.diffDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true);
+    this.diffDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true);
+    this.diffView.element.appendChild(this.diffDataGrid.element);
+    this.element.appendChild(this.diffView.element);
+
+    this.dominatorView = new WebInspector.View();
+    this.dominatorView.element.addStyleClass("view");
+    this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid();
+    this.dominatorDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true);
+    this.dominatorDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true);
+    this.dominatorView.element.appendChild(this.dominatorDataGrid.element);
+    this.element.appendChild(this.dominatorView.element);
+
+    var retainmentView = new WebInspector.View();
+    retainmentView.element.addStyleClass("view retaining-paths-view");
+    var retainingPathsTitleDiv = document.createElement("div");
+    retainingPathsTitleDiv.className = "title";
+    var retainingPathsTitle = document.createElement("span");
+    retainingPathsTitle.textContent = WebInspector.UIString("Retaining paths of the selected object");
+    retainingPathsTitleDiv.appendChild(retainingPathsTitle);
+    retainmentView.element.appendChild(retainingPathsTitleDiv);
+    this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainingPathsList();
+    retainmentView.element.appendChild(this.retainmentDataGrid.element);
+    retainmentView.visible = true;
+    this.element.appendChild(retainmentView.element);
+
+    this.dataGrid = this.constructorsDataGrid;
+    this.currentView = this.constructorsView;
+
+    this.viewSelectElement = document.createElement("select");
+    this.viewSelectElement.className = "status-bar-item";
+    this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false);
+
+    var classesViewOption = document.createElement("option");
+    classesViewOption.label = WebInspector.UIString("Summary");
+    var diffViewOption = document.createElement("option");
+    diffViewOption.label = WebInspector.UIString("Comparison");
+    var containmentViewOption = document.createElement("option");
+    containmentViewOption.label = WebInspector.UIString("Containment");
+    var dominatorsViewOption = document.createElement("option");
+    dominatorsViewOption.label = WebInspector.UIString("Dominators");
+    this.viewSelectElement.appendChild(classesViewOption);
+    this.viewSelectElement.appendChild(diffViewOption);
+    this.viewSelectElement.appendChild(containmentViewOption);
+    this.viewSelectElement.appendChild(dominatorsViewOption);
+    this.views = ["Summary", "Comparison", "Containment", "Dominators"];
+    this.views.current = 0;
+
+    this._profileUid = profile.uid;
+
+    this.baseSelectElement = document.createElement("select");
+    this.baseSelectElement.className = "status-bar-item hidden";
+    this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false);
+    this._updateBaseOptions();
+
+    this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item");
+    this.percentButton.addEventListener("click", this._percentClicked.bind(this), false);
+    this.helpButton = new WebInspector.StatusBarButton("", "heapshot-help-status-bar-item status-bar-item");
+    this.helpButton.addEventListener("click", this._helpClicked.bind(this), false);
+
+    this._loadProfile(this._profileUid, profileCallback.bind(this));
+
+    function profileCallback(profile)
+    {
+        var list = this._profiles();
+        var profileIndex;
+        for (var i = 0; i < list.length; ++i)
+            if (list[i].uid === profile.uid) {
+                profileIndex = i;
+                break;
+            }
+        if (profileIndex > 0)
+            this.baseSelectElement.selectedIndex = profileIndex - 1;
+        else
+            this.baseSelectElement.selectedIndex = profileIndex;
+        this.dataGrid.setDataSource(this, this.profileWrapper); 
+        this._updatePercentButton();
+    }
 }
 
 WebInspector.DetailedHeapshotView.prototype = {
+    dispose: function()
+    {
+        if (this._profileWrapper)
+            this._profileWrapper.dispose();
+        if (this._baseProfileWrapper)
+            this._baseProfileWrapper.dispose();
+    },
+
+    get statusBarItems()
+    {
+        return [this.viewSelectElement, this.baseSelectElement, this.percentButton.element, this.helpButton.element];
+    },
+
     get profile()
     {
-        return this._profile;
+        return this.parent.getProfile(WebInspector.HeapSnapshotProfileType.TypeId, this._profileUid);
+    },
+
+    get profileWrapper()
+    {
+        if (!this._profileWrapper)
+            this._profileWrapper = new WebInspector.HeapSnapshot(this.profile);
+        return this._profileWrapper;
     },
 
-    set profile(profile)
+    get baseProfile()
     {
-        this._profile = profile;
+        return this.parent.getProfile(WebInspector.HeapSnapshotProfileType.TypeId, this._baseProfileUid);
+    },
+
+    get baseProfileWrapper()
+    {
+        if (!this._baseProfileWrapper) {
+            if (this.baseProfile !== this.profile)
+                this._baseProfileWrapper = new WebInspector.HeapSnapshot(this.baseProfile);
+            else
+                this._baseProfileWrapper = this.profileWrapper;
+        }
+        return this._baseProfileWrapper;
+    },
+
+    show: function(parentElement)
+    {
+        WebInspector.View.prototype.show.call(this, parentElement);
+        if (!this.profile._loaded)
+            this._loadProfile(this._profileUid, profileCallback1.bind(this));
+        else
+            profileCallback1.call(this, this.profile);
+
+        function profileCallback1(profile) {
+            this.profileWrapper.restore(profile);
+            if (this.baseProfile && !this.baseProfile._loaded)
+                this._loadProfile(this._baseProfileUid, profileCallback2.bind(this));
+            else
+                profileCallback2.call(this, this.baseProfile);
+        }
+
+        function profileCallback2(profile) {
+            if (profile)
+                this.baseProfileWrapper.restore(profile);
+            this.currentView.show();
+            this.dataGrid.updateWidths();
+        }
+    },
+
+    hide: function()
+    {
+        WebInspector.View.prototype.hide.call(this);
+        this._currentSearchResultIndex = -1;
+    },
+
+    resize: function()
+    {
+        if (this.dataGrid)
+            this.dataGrid.updateWidths();
+    },
+
+    refreshShowAsPercents: function()
+    {
+        this._updatePercentButton();
+        this.refreshVisibleData();
+    },
+
+    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 = [];
+    },
+
+    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;
+        if (this.currentView !== this.constructorsView && this.currentView !== this.diffView)
+            return;
+
+        this._searchFinishedCallback = finishedCallback;
+
+        function matchesByName(gridNode) {
+            return ("name" in gridNode) && gridNode.name.hasSubstring(query, true);
+        }
+
+        function matchesById(gridNode) {
+            return ("snapshotNodeId" in gridNode) && gridNode.snapshotNodeId === query;
+        }
+
+        var matchPredicate;
+        if (query.charAt(0) !== "@")
+            matchPredicate = matchesByName;
+        else {
+            query = parseInt(query.substring(1), 10);
+            matchPredicate = matchesById;
+        }
+
+        function matchesQuery(gridNode)
+        {
+            delete gridNode._searchMatched;
+            if (matchPredicate(gridNode)) {
+                gridNode._searchMatched = true;
+                gridNode.refresh();
+                return true;
+            }
+            return false;
+        }
+
+        var current = this.dataGrid.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);
+    },
+
+    showingFirstSearchResult: function()
+    {
+        return (this._currentSearchResultIndex === 0);
+    },
+
+    showingLastSearchResult: function()
+    {
+        return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
+    },
+
+    _jumpToSearchResult: function(index)
+    {
+        var searchResult = this._searchResults[index];
+        if (!searchResult)
+            return;
+
+        var node = searchResult.node;
+        node.reveal();
+        node.select();
+    },
+
+    refreshVisibleData: function()
+    {
+        var child = this.dataGrid.children[0];
+        while (child) {
+            child.refresh();
+            child = child.traverseNextNode(false, null, true);
+        }
+    },
+
+    _changeBase: function()
+    {
+        if (this._baseProfileUid === this._profiles()[this.baseSelectElement.selectedIndex].uid)
+            return;
+
+        this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid;
+        this._loadProfile(this._baseProfileUid, baseProfileLoaded.bind(this));
+
+        function baseProfileLoaded(profile)
+        {
+            delete this._baseProfileWrapper;
+            this.baseProfile._lastShown = Date.now();
+            WebInspector.PleaseWaitMessage.prototype.startAction(this.currentView.element, showDiffData.bind(this));
+        }
+
+        function showDiffData()
+        {
+            this.diffDataGrid.setBaseDataSource(this.baseProfileWrapper);
+        }
+
+        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);
+    },
+
+    _profiles: function()
+    {
+        return WebInspector.panels.profiles.getProfiles(WebInspector.HeapSnapshotProfileType.TypeId);
+    },
+
+    _loadProfile: function(profileUid, callback)
+    {
+        WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback);
+    },
+
+    processLoadedSnapshot: function(profile, snapshot)
+    {
+        profile.nodes = snapshot.nodes;
+        profile.strings = snapshot.strings;
+        var s = new WebInspector.HeapSnapshot(profile);
+        profile.sideBarElement.subtitle = Number.bytesToString(s.totalSize);
+    },
+
+    _dblClickInContainmentGrid: function(event)
+    {
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!cell || (!cell.hasStyleClass("retainedSize-column")))
+            return;
+        var nodeItem = event.target.enclosingNodeOrSelfWithNodeName("tr")._dataGridNode;
+        ProfilerAgent.getExactHeapSnapshotNodeRetainedSize(this._profileUid, nodeItem.snapshotNodeId, setExactRetainedSize);
+
+        function setExactRetainedSize(exactSize) {
+            if (exactSize && exactSize != -1)
+                nodeItem.exactRetainedSize = exactSize;
+        }
+    },
+
+    _mouseClickInContainmentGrid: function(event)
+    {
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!cell || !(cell.hasStyleClass("object-column") ||  cell.hasStyleClass("shallowSize-column")))
+            return;
+        var row = event.target.enclosingNodeOrSelfWithNodeName("tr");
+        if (!row)
+            return;
+        var nodeItem = row._dataGridNode;
+        if (!nodeItem || nodeItem.isEventWithinDisclosureTriangle(event) || !nodeItem.snapshotNodeIndex)
+            return;
+
+        this.retainmentDataGrid.setDataSource(this, nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex, nodeItem.isDeletedNode ? this.baseSelectElement.childNodes[this.baseSelectElement.selectedIndex].label + " | " : "");
+    },
+
+    _changeView: function(event)
+    {
+        if (!event || !this._profileUid)
+            return;
+        if (event.target.selectedIndex === this.views.current)
+            return;
+
+        this.views.current = event.target.selectedIndex;
+        this.currentView.hide();
+        if (this.views[this.views.current] === "Containment") {
+            this.currentView = this.containmentView;
+            this.dataGrid = this.containmentDataGrid;
+        } else if (this.views[this.views.current] === "Summary") {
+            this.currentView = this.constructorsView;
+            this.dataGrid = this.constructorsDataGrid;
+        } else if (this.views[this.views.current] === "Comparison") {
+            this.currentView = this.diffView;
+            this.dataGrid = this.diffDataGrid;
+        } else if (this.views[this.views.current] === "Dominators") {
+            this.currentView = this.dominatorView;
+            this.dataGrid = this.dominatorDataGrid;
+        }
+        this.currentView.show();
+        this.refreshVisibleData();
+        if (this.currentView === this.diffView) {
+            this.baseSelectElement.removeStyleClass("hidden");
+            if (!this.dataGrid.snapshotView) {
+                this.dataGrid.setDataSource(this, this.profileWrapper);
+                this._changeBase();
+            }
+        } else {
+            this.baseSelectElement.addStyleClass("hidden");
+            if (!this.dataGrid.snapshotView)
+                WebInspector.PleaseWaitMessage.prototype.startAction(this.currentView.element, loadData.bind(this));
+        }
+
+        function loadData()
+        {
+            this.dataGrid.setDataSource(this, this.profileWrapper);
+        }
+
+        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);
+    },
+
+    get _isShowingAsPercent()
+    {
+        return this.showCountAsPercent && this.showShallowSizeAsPercent && this.showRetainedSizeAsPercent;
+    },
+
+    _percentClicked: function(event)
+    {
+        var currentState = this._isShowingAsPercent;
+        this.showCountAsPercent = !currentState;
+        this.showShallowSizeAsPercent = !currentState;
+        this.showRetainedSizeAsPercent = !currentState;
+        this.refreshShowAsPercents();
+    },
+
+    _helpClicked: function(event)
+    {
+        if (!this.helpPopover) {
+            var refTypes = ["a:", "console-formatted-name", WebInspector.UIString("property"),
+                            "0:", "console-formatted-name", WebInspector.UIString("element"),
+                            "a:", "console-formatted-number", WebInspector.UIString("context var"),
+                            "a:", "console-formatted-null", WebInspector.UIString("system prop")];
+            var objTypes = [" a ", "console-formatted-object", "Object",
+                            "\"a\"", "console-formatted-string", "String",
+                            "/a/", "console-formatted-string", "RegExp",
+                            "a()", "console-formatted-function", "Function",
+                            "a[]", "console-formatted-object", "Array",
+                            "num", "console-formatted-number", "Number",
+                            " a ", "console-formatted-null", "System"];
+
+            var contentElement = document.createElement("table");
+            contentElement.className = "heapshot-help";
+            var headerRow = document.createElement("tr");
+            var propsHeader = document.createElement("th");
+            propsHeader.textContent = WebInspector.UIString("Property types:");
+            headerRow.appendChild(propsHeader);
+            var objsHeader = document.createElement("th");
+            objsHeader.textContent = WebInspector.UIString("Object types:");
+            headerRow.appendChild(objsHeader);
+            contentElement.appendChild(headerRow);
+            var len = Math.max(refTypes.length, objTypes.length);
+            for (var i = 0; i < len; i += 3) {
+                var row = document.createElement("tr");
+                var refCell = document.createElement("td");
+                if (refTypes[i])
+                    appendHelp(refTypes, i, refCell);
+                row.appendChild(refCell);
+                var objCell = document.createElement("td");
+                if (objTypes[i])
+                    appendHelp(objTypes, i, objCell);
+                row.appendChild(objCell);
+                contentElement.appendChild(row);
+            }
+            this.helpPopover = new WebInspector.Popover(contentElement);
+
+            function appendHelp(help, index, cell)
+            {
+                var div = document.createElement("div");
+                div.className = "source-code event-properties";
+                var name = document.createElement("span");
+                name.textContent = help[index];
+                name.className = help[index + 1];
+                div.appendChild(name);
+                var desc = document.createElement("span");
+                desc.textContent = " " + help[index + 2];
+                div.appendChild(desc);
+                cell.appendChild(div);
+            }
+        }
+        if (this.helpPopover.visible)
+            this.helpPopover.hide();
+        else
+            this.helpPopover.show(this.helpButton.element);
+    },
+
+    _updateBaseOptions: function()
+    {
+        var list = this._profiles();
+        // We're assuming that snapshots can only be added.
+        if (this.baseSelectElement.length === list.length)
+            return;
+
+        for (var i = this.baseSelectElement.length, n = list.length; i < n; ++i) {
+            var baseOption = document.createElement("option");
+            var title = list[i].title;
+            if (!title.indexOf(UserInitiatedProfileName))
+                title = WebInspector.UIString("Snapshot %d", title.substring(UserInitiatedProfileName.length + 1));
+            baseOption.label = title;
+            this.baseSelectElement.appendChild(baseOption);
+        }
+    },
+
+    _updatePercentButton: function()
+    {
+        if (this._isShowingAsPercent) {
+            this.percentButton.title = WebInspector.UIString("Show absolute counts and sizes.");
+            this.percentButton.toggled = true;
+        } else {
+            this.percentButton.title = WebInspector.UIString("Show counts and sizes as percentages.");
+            this.percentButton.toggled = false;
+        }
     }
 };
 
 WebInspector.DetailedHeapshotView.prototype.__proto__ = WebInspector.View.prototype;
 
+WebInspector.DetailedHeapshotView.prototype.showHiddenData = true;
+
 WebInspector.DetailedHeapshotProfileType = function()
 {
     WebInspector.ProfileType.call(this, WebInspector.HeapSnapshotProfileType.TypeId, WebInspector.UIString("HEAP SNAPSHOTS"));
index 27a29ca..215f31c 100644 (file)
@@ -80,6 +80,11 @@ WebInspector.HeapSnapshotEdge.prototype = {
         return this._type() === this._snapshot._edgeInternalType;
     },
 
+    get isInvisible()
+    {
+        return this._type() === this._snapshot._edgeInvisibleType;
+    },
+
     get isShortcut()
     {
         return this._type() === this._snapshot._edgeShortcutType;
@@ -123,6 +128,7 @@ WebInspector.HeapSnapshotEdge.prototype = {
                 return "[" + this.name + "]";
         case "internal":
         case "hidden":
+        case "invisible":
             return "{" + this.name + "}";
         };
         return "?" + this.name + "?";
@@ -377,6 +383,10 @@ WebInspector.HeapSnapshot.prototype = {
         this._edgeHiddenType = this._edgeTypes.indexOf("hidden");
         this._edgeInternalType = this._edgeTypes.indexOf("internal");
         this._edgeShortcutType = this._edgeTypes.indexOf("shortcut");
+        this._edgeInvisibleType = this._edgeTypes.length;
+        this._edgeTypes.push("invisible");
+
+        this._markInvisibleEdges();
     },
 
     dispose: function()
@@ -549,6 +559,33 @@ WebInspector.HeapSnapshot.prototype = {
                 });
 
         this._aggregatesWithIndexes = true;
+    },
+
+    _markInvisibleEdges: function()
+    {
+        // Mark hidden edges of global objects as invisible.
+        // FIXME: This is a temporary measure. Normally, we should
+        // really hide all hidden nodes.
+        for (var iter = this.rootNode.edges; iter.hasNext(); iter.next()) {
+            var edge = iter.edge;
+            if (!edge.isShortcut)
+                continue;
+            var node = edge.node;
+            var propNames = {};
+            for (var innerIter = node.edges; innerIter.hasNext(); innerIter.next()) {
+                var globalObjEdge = innerIter.edge;
+                if (globalObjEdge.isShortcut)
+                    propNames[globalObjEdge._nameOrIndex] = true;
+            }
+            for (innerIter.first(); innerIter.hasNext(); innerIter.next()) {
+                var globalObjEdge = innerIter.edge;
+                if (!globalObjEdge.isShortcut
+                    && globalObjEdge.node.isHidden
+                    && globalObjEdge._hasStringName
+                    && (globalObjEdge._nameOrIndex in propNames))
+                    this._nodes[globalObjEdge._edges._start + globalObjEdge.edgeIndex + this._edgeTypeOffset] = this._edgeInvisibleType;
+            }
+        }
     }
 };
 
@@ -849,7 +886,8 @@ WebInspector.HeapSnapshotPathFinder.prototype = {
 
     _skipEdge: function(edge)
     {
-        return (this._skipHidden && (edge.isHidden || edge.node.isHidden))
+        return edge.isInvisible
+            || (this._skipHidden && (edge.isHidden || edge.node.isHidden))
             || this._hasInPath(edge.nodeIndex);
     },
 
diff --git a/Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png b/Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png
new file mode 100644 (file)
index 0000000..92fe59a
Binary files /dev/null and b/Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png differ
index d9b54d4..1b99dd4 100644 (file)
@@ -88,6 +88,12 @@ WebInspector.Panel.prototype = {
             this._toolbarItem.removeStyleClass("toggled-on");
     },
 
+    reset: function()
+    {
+        this.searchCanceled();
+        WebInspector.resetFocusElement();
+    },
+
     get defaultFocusedElement()
     {
         return this.sidebarTreeElement || this.element;
index 32535e9..f20b8c6 100644 (file)
@@ -40,6 +40,7 @@ WebInspector.Popover = function(contentElement)
     this.contentElement = contentElement;
     this._contentDiv = document.createElement("div");
     this._contentDiv.className = "content";
+    this._visible = false;
 }
 
 WebInspector.Popover.prototype = {
@@ -60,6 +61,7 @@ WebInspector.Popover.prototype = {
         this.element.appendChild(this._contentDiv);
         document.body.appendChild(this.element);
         this._positionElement(anchor, preferredWidth, preferredHeight);
+        this._visible = true;
     },
 
     hide: function()
@@ -68,6 +70,12 @@ WebInspector.Popover.prototype = {
             delete WebInspector.Popover._popoverElement;
             document.body.removeChild(this.element);
         }
+        this._visible = false;
+    },
+
+    get visible()
+    {
+        return this._visible;
     },
 
     _positionElement: function(anchorElement, preferredWidth, preferredHeight)
index 56c7f81..5940dd0 100644 (file)
@@ -185,8 +185,19 @@ WebInspector.ProfilesPanel.prototype = {
 
     _reset: function()
     {
-        for (var i = 0; i < this._profiles.length; ++i)
+        WebInspector.Panel.prototype.reset.call(this);
+
+        for (var i = 0; i < this._profiles.length; ++i) {
+            var view = this._profiles[i]._profileView;
+            if (view && ("dispose" in view))
+                view.dispose();
             delete this._profiles[i]._profileView;
+            var profile = this._profiles[i];
+            if (profile.nodes) {
+                delete profile.nodes;
+                delete profile.strings;
+            }
+        }
         delete this.visibleView;
 
         delete this.currentQuery;
@@ -409,6 +420,11 @@ WebInspector.ProfilesPanel.prototype = {
         return !!this._profilesIdMap[this._makeKey(profile.uid, profile.typeId)];
     },
 
+    getProfile: function(typeId, uid)
+    {
+        return this._profilesIdMap[this._makeKey(uid, typeId)];
+    },
+
     loadHeapSnapshot: function(uid, callback)
     {
         var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.HeapSnapshotProfileType.TypeId)];
index 03a6dd0..add02a1 100644 (file)
     width: 50%;
     left: 25%;
 }
+
+.detailed-heapshot-view {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    -webkit-box-orient: vertical;
+}
+
+.detailed-heapshot-view.visible {
+    display: -webkit-box;
+}
+
+.detailed-heapshot-view .view {
+    display: none;
+    -webkit-box-flex: 3;
+    -webkit-box-orient: vertical;
+}
+
+.detailed-heapshot-view .view.visible {
+    display: -webkit-box;
+}
+
+.detailed-heapshot-view .data-grid {
+    border: none;
+    position: relative;
+    -webkit-box-flex: 1;
+}
+
+.detailed-heapshot-view .data-grid td.count-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.addedCount-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.removedCount-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.countDelta-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.addedSize-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.removedSize-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.sizeDelta-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.shallowSize-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .data-grid td.retainedSize-column {
+    text-align: right;
+}
+
+.detailed-heapshot-view .console-formatted-object, .console-formatted-node {
+    display: inline;
+    position: static;
+}
+
+.detailed-heapshot-view .delimiter {
+    height: 24px;
+    background-color: #d6dde5;
+}
+
+.detailed-heapshot-view .retaining-paths-view {
+    -webkit-box-flex: 1;
+}
+
+.detailed-heapshot-view .retaining-paths-view .title {
+    background-color: rgb(235, 235, 235);
+    background-image: url(Images/statusbarBackground.png);
+    background-repeat: repeat-x;
+    height: 23px;
+    font: -webkit-small-control;
+    font-weight: bold;
+    color: rgb(48, 48, 48);
+    text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0;
+}
+
+.detailed-heapshot-view .retaining-paths-view .title > span {
+    vertical-align: middle;
+    margin-left: 4px;
+}
+
+.heapshot-help-status-bar-item .glyph {
+    -webkit-mask-image: url(Images/helpButtonGlyph.png);
+}
+
+table.heapshot-help {
+    border-spacing: 12px 2px;
+}
index c3a17bd..4b28d5f 100644 (file)
@@ -138,6 +138,12 @@ var WebInspector = {
             this._previousFocusElement.blur();
     },
 
+    resetFocusElement: function()
+    {
+        this.currentFocusElement = null;
+        this._previousFocusElement = null;        
+    },
+
     get currentPanel()
     {
         return this._currentPanel;