Web Inspector: JavaScript Heap Allocations Timeline
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Mar 2016 21:37:56 +0000 (21:37 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Mar 2016 21:37:56 +0000 (21:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155287
<rdar://problem/25078088>

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/InjectedScriptSource.js:
(InjectedScript.prototype._describe):
(InjectedScript.prototype._nodeDescription):
Provide the nicer node preview more often.

Source/WebInspectorUI:

Initial JavaScript Heap Allocations Timeline includes:

    - Snapshot markers in the timeline
    - Initial/Periodic/End snapshots during recording
    - Ability to manually take a snapshot
    - View of all objects in a Snapshot and Diff between snapshots
      - Summary view - rough display of the size/count of large objects
      - Instances view - view each of the individual objects

* UserInterface/Main.html:
* UserInterface/Test.html:
* UserInterface/Images/Compare.svg: Added.
* UserInterface/Images/HeapSnapshot.svg: Added.
* UserInterface/Images/HeapSnapshotDiff.svg: Added.
* UserInterface/Images/HeapSnapshotInstances.svg: Added.
* UserInterface/Images/HeapSnapshotSummary.svg: Added.
* Localizations/en.lproj/localizedStrings.js:
New resources and strings.

* UserInterface/Controllers/TimelineManager.js:
(WebInspector.TimelineManager.prototype.heapTrackingStarted):
(WebInspector.TimelineManager.prototype.heapTrackingCompleted):
(WebInspector.TimelineManager.prototype.heapSnapshotAdded):
* UserInterface/Protocol/HeapObserver.js:
(WebInspector.HeapObserver.prototype.trackingStart):
(WebInspector.HeapObserver.prototype.trackingComplete):
(WebInspector.HeapObserver):
Add snapshot records to the active recording's timeline.

* UserInterface/Models/HeapAllocationsInstrument.js: Added.
(WebInspector.HeapAllocationsInstrument):
(WebInspector.HeapAllocationsInstrument.supported):
(WebInspector.HeapAllocationsInstrument.prototype.get timelineRecordType):
(WebInspector.HeapAllocationsInstrument.prototype.startInstrumentation):
(WebInspector.HeapAllocationsInstrument.prototype.stopInstrumentation):
(WebInspector.HeapAllocationsInstrument.prototype._takeHeapSnapshot):
Start, stop, and periodic snapshots.

* UserInterface/Models/HeapAllocationsTimelineRecord.js:
(WebInspector.HeapAllocationsTimelineRecord):
(WebInspector.HeapAllocationsTimelineRecord.prototype.get timestamp):
(WebInspector.HeapAllocationsTimelineRecord.prototype.get heapSnapshot):
* UserInterface/Models/TimelineRecord.js:
* UserInterface/Models/TimelineRecording.js:
(WebInspector.TimelineRecording):
(WebInspector.TimelineRecording.prototype.addRecord):
* UserInterface/Views/TimelineOverviewGraph.js:
(WebInspector.TimelineOverviewGraph.createForTimeline):
* UserInterface/Views/TimelineTabContentView.js:
(WebInspector.TimelineTabContentView.displayNameForTimeline):
(WebInspector.TimelineTabContentView.iconClassNameForTimeline):
(WebInspector.TimelineTabContentView.genericClassNameForTimeline):
(WebInspector.TimelineTabContentView.iconClassNameForRecord):
(WebInspector.TimelineTabContentView.displayNameForRecord):
New timeline and record type.

* UserInterface/Models/HeapSnapshotDiff.js: Added.
(WebInspector.HeapSnapshotDiff):
(WebInspector.HeapSnapshotDiff.prototype.get snapshot1):
(WebInspector.HeapSnapshotDiff.prototype.get snapshot2):
(WebInspector.HeapSnapshotDiff.prototype.get addedInstances):
(WebInspector.HeapSnapshotDiff.prototype.get removedInstances):
(WebInspector.HeapSnapshotDiff.prototype.get sizeDifference):
(WebInspector.HeapSnapshotDiff.prototype.get growth):
(WebInspector.HeapSnapshotDiff.prototype.snapshotForDiff):
Compare two snapshots and create a "diff snapshot" which is just
the newly added objects.

* UserInterface/Views/ContentView.js:
(WebInspector.ContentView.createFromRepresentedObject):
(WebInspector.ContentView.isViewable):
A HeapSnapshot creates a HeapSnapshotClusterView.

* UserInterface/Views/HeapAllocationsTimelineDataGridNode.js: Added.
(WebInspector.HeapAllocationsTimelineDataGridNode):
(WebInspector.HeapAllocationsTimelineDataGridNode.prototype.get record):
(WebInspector.HeapAllocationsTimelineDataGridNode.prototype.get data):
(WebInspector.HeapAllocationsTimelineDataGridNode.prototype.createCellContent):
(WebInspector.HeapAllocationsTimelineDataGridNode.prototype.markAsBaseline):
(WebInspector.HeapAllocationsTimelineDataGridNode.prototype.clearBaseline):
* UserInterface/Views/HeapAllocationsTimelineOverviewGraph.css: Copied from Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js.
(.timeline-overview-graph.heap-allocations):
(.timeline-overview-graph.heap-allocations > img.snapshot):
* UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js: Added.
(WebInspector.HeapAllocationsTimelineOverviewGraph):
(WebInspector.HeapAllocationsTimelineOverviewGraph.prototype.reset):
(WebInspector.HeapAllocationsTimelineOverviewGraph.prototype.layout.xScale):
(WebInspector.HeapAllocationsTimelineOverviewGraph.prototype.layout):
(WebInspector.HeapAllocationsTimelineOverviewGraph.prototype._visibleRecords):
(WebInspector.HeapAllocationsTimelineOverviewGraph.prototype._heapAllocationTimelineRecordAdded):
* UserInterface/Views/HeapAllocationsTimelineView.css: Copied from Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js.
(.timeline-view.heap-allocations > .data-grid):
(.timeline-view.heap-allocations > .data-grid td .icon.heap-snapshot):
(.timeline-view.heap-allocations > .data-grid tr.baseline):
(.timeline-view.heap-allocations > .content-view-container):
(.timeline-view.heap-allocations > .content-view-container > .content-view):
* UserInterface/Views/HeapAllocationsTimelineView.js: Added.
(WebInspector.HeapAllocationsTimelineView):
(WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotList):
(WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotTimelineRecord):
(WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotDiff):
(WebInspector.HeapAllocationsTimelineView.prototype.get navigationItems):
(WebInspector.HeapAllocationsTimelineView.prototype.get selectionPathComponents):
(WebInspector.HeapAllocationsTimelineView.prototype.get navigationSidebarTreeOutlineLabel):
(WebInspector.HeapAllocationsTimelineView.prototype.treeElementPathComponentSelected):
(WebInspector.HeapAllocationsTimelineView.prototype.userSelectedRecordFromOverview):
(WebInspector.HeapAllocationsTimelineView.prototype.closed):
(WebInspector.HeapAllocationsTimelineView.prototype.layout):
(WebInspector.HeapAllocationsTimelineView.prototype.reset):
(WebInspector.HeapAllocationsTimelineView.prototype._heapAllocationsTimelineRecordAdded):
(WebInspector.HeapAllocationsTimelineView.prototype._snapshotListPathComponentClicked):
(WebInspector.HeapAllocationsTimelineView.prototype._snapshotPathComponentSelected):
(WebInspector.HeapAllocationsTimelineView.prototype._currentContentViewDidChange):
(WebInspector.HeapAllocationsTimelineView.prototype._contentViewSelectionPathComponentDidChange):
(WebInspector.HeapAllocationsTimelineView.prototype._updateCompareHeapSnapshotButton):
(WebInspector.HeapAllocationsTimelineView.prototype._takeHeapSnapshotClicked):
(WebInspector.HeapAllocationsTimelineView.prototype._cancelSelectComparisonHeapSnapshots):
(WebInspector.HeapAllocationsTimelineView.prototype._compareHeapSnapshotsClicked):
(WebInspector.HeapAllocationsTimelineView.prototype._dataGridNodeSelected):
* UserInterface/Views/HeapSnapshotClassDataGridNode.js: Added.
(WebInspector.HeapSnapshotClassDataGridNode):
(WebInspector.HeapSnapshotClassDataGridNode.prototype.get data):
(WebInspector.HeapSnapshotClassDataGridNode.prototype.createCellContent):
(WebInspector.HeapSnapshotClassDataGridNode.prototype.sort):
(WebInspector.HeapSnapshotClassDataGridNode.prototype._populate):
(WebInspector.HeapSnapshotClassDataGridNode.prototype._fetchBatch):
(WebInspector.HeapSnapshotClassDataGridNode.prototype._updateBatchedSort):
(WebInspector.HeapSnapshotClassDataGridNode.prototype._updateBatchedChildren):
(WebInspector.HeapSnapshotClassDataGridNode.prototype._removeFetchMoreDataGridNode):
(WebInspector.HeapSnapshotClassDataGridNode.prototype._appendFetchMoreDataGridNode):
* UserInterface/Views/HeapSnapshotClusterContentView.js: Added.
(WebInspector.HeapSnapshotClusterContentView.createPathComponent):
(WebInspector.HeapSnapshotClusterContentView):
(WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName):
(WebInspector.HeapSnapshotClusterContentView.prototype.get heapSnapshot):
(WebInspector.HeapSnapshotClusterContentView.prototype.get summaryContentView):
(WebInspector.HeapSnapshotClusterContentView.prototype.get instancesContentView):
(WebInspector.HeapSnapshotClusterContentView.prototype.get navigationItems):
(WebInspector.HeapSnapshotClusterContentView.prototype.get selectionPathComponents):
(WebInspector.HeapSnapshotClusterContentView.prototype.shown):
(WebInspector.HeapSnapshotClusterContentView.prototype.closed):
(WebInspector.HeapSnapshotClusterContentView.prototype.saveToCookie):
(WebInspector.HeapSnapshotClusterContentView.prototype.restoreFromCookie):
(WebInspector.HeapSnapshotClusterContentView.prototype.showSummary):
(WebInspector.HeapSnapshotClusterContentView.prototype.showInstances):
(WebInspector.HeapSnapshotClusterContentView.prototype._contentViewExtraArguments):
(WebInspector.HeapSnapshotClusterContentView.prototype._pathComponentForContentView):
(WebInspector.HeapSnapshotClusterContentView.prototype._identifierForContentView):
(WebInspector.HeapSnapshotClusterContentView.prototype._showContentViewForIdentifier):
(WebInspector.HeapSnapshotClusterContentView.prototype._pathComponentSelected):
(WebInspector.HeapSnapshotClusterContentView.prototype._toggleShowInternalObjectsSetting):
(WebInspector.HeapSnapshotClusterContentView.prototype._updateViewsForShowInternalObjectsSettingValue):
(WebInspector.HeapSnapshotClusterContentView.prototype._updateShowInternalObjectsButtonNavigationItem):
* UserInterface/Views/HeapSnapshotInstanceDataGridNode.js: Added.
(WebInspector.HeapSnapshotInstanceDataGridNode):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype.get data):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype.get selectable):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCells):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCellContent):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype.sort):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype._contextMenuHandler.):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype._contextMenuHandler):
* UserInterface/Views/HeapSnapshotInstanceFetchMoreDataGridNode.js: Added.
(WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode):
(WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode.prototype.createCellContent):
(WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode.prototype.sort):
* UserInterface/Views/HeapSnapshotInstancesContentView.css: Added.
* UserInterface/Views/HeapSnapshotInstancesContentView.js: Added.
(WebInspector.HeapSnapshotInstancesContentView):
(WebInspector.HeapSnapshotInstancesContentView.prototype.get showInternalObjects):
(WebInspector.HeapSnapshotInstancesContentView.prototype.set showInternalObjects):
(WebInspector.HeapSnapshotInstancesContentView.prototype._sortDataGrid):
* UserInterface/Views/HeapSnapshotInstancesDataGridTree.js: Added.
(WebInspector.HeapSnapshotInstancesDataGridTree):
(WebInspector.HeapSnapshotInstancesDataGridTree.buildSortComparator):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get heapSnapshot):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get includeInternalObjects):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.set includeInternalObjects):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get children):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.appendChild):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.insertChild):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.removeChildren):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.set sortComparator):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.sort):
(WebInspector.HeapSnapshotInstancesDataGridTree.prototype._populateTopLevel):
* UserInterface/Views/HeapSnapshotSummaryContentView.css: Added.
* UserInterface/Views/HeapSnapshotSummaryContentView.js: Added.
(WebInspector.HeapSnapshotSummaryContentView.createChartContainer):
(WebInspector.HeapSnapshotSummaryContentView.appendLegendRow):
(WebInspector.HeapSnapshotSummaryContentView.appendEmptyMessage):
(WebInspector.HeapSnapshotSummaryContentView):
(WebInspector.HeapSnapshotSummaryContentView.prototype.layout):
* UserInterface/Views/PathComponentIcons.css:
(.heap-snapshot-summary-icon .icon):
(.heap-snapshot-instances-icon .icon):
(.snapshot-list-icon .icon):
(.snapshot-diff-icon .icon):
(body:not(.mac-platform, .windows-platform) .snapshot-diff-icon .icon):
(body:not(.mac-platform, .windows-platform) .call-trees-icon .icon): Deleted.
* UserInterface/Views/TextNavigationItem.css:
(.navigation-bar .item.text):
* UserInterface/Views/TextNavigationItem.js:
(WebInspector.TextNavigationItem):
(WebInspector.TextNavigationItem.prototype.get text):
(WebInspector.TextNavigationItem.prototype.set text):
* UserInterface/Views/TimelineIcons.css:
(.heap-snapshot-record .icon):
* UserInterface/Views/Variables.css:
(:root):
New views.

* UserInterface/Views/FormattedValue.js:
(WebInspector.FormattedValue.createElementForNodePreview):
(WebInspector.FormattedValue.createElementForFunctionWithName):
(WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForObjectPreview):
Better display for a raw object preview.

* UserInterface/Views/TimelineOverview.js:
(WebInspector.TimelineOverview):
(WebInspector.TimelineOverview.prototype.userSelectedRecord):
* UserInterface/Views/TimelineRecordingContentView.js:
(WebInspector.TimelineRecordingContentView):
(WebInspector.TimelineRecordingContentView.prototype.timelineOverviewUserSelectedRecord):
* UserInterface/Views/TimelineRuler.js:
(WebInspector.TimelineRuler.prototype.set allowsTimeRangeSelection):
(WebInspector.TimelineRuler.prototype._handleClick):
(WebInspector.TimelineRuler.prototype._handleMouseDown):
(WebInspector.TimelineRuler.prototype._handleMouseMove):
* UserInterface/Views/TimelineView.js:
(WebInspector.TimelineView.prototype.userSelectedRecordFromOverview):
Hook up a way for clicking in the TimelineOverview / TimelineRuler
to redispatch to an OverviewGraph element, and provide a patch for
the Overview -> RecordingContentView -> TimelineView for records.

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

45 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/InjectedScriptSource.js
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js
Source/WebInspectorUI/UserInterface/Images/Compare.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/HeapSnapshot.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/HeapSnapshotDiff.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/HeapSnapshotInstances.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/HeapSnapshotSummary.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js
Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js
Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js
Source/WebInspectorUI/UserInterface/Test.html
Source/WebInspectorUI/UserInterface/Views/ContentView.js
Source/WebInspectorUI/UserInterface/Views/FormattedValue.js
Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineDataGridNode.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceFetchMoreDataGridNode.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/PathComponentIcons.css
Source/WebInspectorUI/UserInterface/Views/TextNavigationItem.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/TextNavigationItem.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/TimelineIcons.css
Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js
Source/WebInspectorUI/UserInterface/Views/TimelineOverviewGraph.js
Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js
Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineView.js
Source/WebInspectorUI/UserInterface/Views/Variables.css

index 90107cb..41bba52 100644 (file)
@@ -1,3 +1,16 @@
+2016-03-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: JavaScript Heap Allocations Timeline
+        https://bugs.webkit.org/show_bug.cgi?id=155287
+        <rdar://problem/25078088>
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/InjectedScriptSource.js:
+        (InjectedScript.prototype._describe):
+        (InjectedScript.prototype._nodeDescription):
+        Provide the nicer node preview more often.
+
 2016-03-10  Saam barati  <sbarati@apple.com>
 
         Assignment to new.target should be an early error
index cd23a67..7f2618e 100644 (file)
@@ -803,27 +803,6 @@ InjectedScript.prototype = {
         return null;
     },
 
-    _nodeDescription: function(node)
-    {
-        var isXMLDocument = node.ownerDocument && !!node.ownerDocument.xmlVersion;
-        var description = isXMLDocument ? node.nodeName : node.nodeName.toLowerCase();
-
-        switch (node.nodeType) {
-        case 1: // Node.ELEMENT_NODE
-            if (node.id)
-                description += "#" + node.id;
-            if (node.hasAttribute("class")) {
-                // Using .getAttribute() is a workaround for SVG*Element.className returning SVGAnimatedString,
-                // which doesn't have any useful String methods. See <https://webkit.org/b/145363/>.
-                description += "." + node.getAttribute("class").trim().replace(/\s+/g, ".");
-            }
-            return description;
-
-        default:
-            return description;
-        }
-    },
-
     _classPreview: function(classConstructorValue)
     {
         return "class " + classConstructorValue.name;
@@ -878,7 +857,7 @@ InjectedScript.prototype = {
             return toString(obj);
 
         if (subtype === "node")
-            return this._nodeDescription(obj);
+            return this._nodePreview(obj);
 
         var className = InjectedScriptHost.internalConstructorName(obj);
         if (subtype === "array")
index aa73ac0..8b87d33 100644 (file)
@@ -1,3 +1,245 @@
+2016-03-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: JavaScript Heap Allocations Timeline
+        https://bugs.webkit.org/show_bug.cgi?id=155287
+        <rdar://problem/25078088>
+
+        Reviewed by Timothy Hatcher.
+
+        Initial JavaScript Heap Allocations Timeline includes:
+
+            - Snapshot markers in the timeline
+            - Initial/Periodic/End snapshots during recording
+            - Ability to manually take a snapshot
+            - View of all objects in a Snapshot and Diff between snapshots
+              - Summary view - rough display of the size/count of large objects
+              - Instances view - view each of the individual objects
+
+        * UserInterface/Main.html:
+        * UserInterface/Test.html:
+        * UserInterface/Images/Compare.svg: Added.
+        * UserInterface/Images/HeapSnapshot.svg: Added.
+        * UserInterface/Images/HeapSnapshotDiff.svg: Added.
+        * UserInterface/Images/HeapSnapshotInstances.svg: Added.
+        * UserInterface/Images/HeapSnapshotSummary.svg: Added.
+        * Localizations/en.lproj/localizedStrings.js:
+        New resources and strings.
+
+        * UserInterface/Controllers/TimelineManager.js:
+        (WebInspector.TimelineManager.prototype.heapTrackingStarted):
+        (WebInspector.TimelineManager.prototype.heapTrackingCompleted):
+        (WebInspector.TimelineManager.prototype.heapSnapshotAdded):
+        * UserInterface/Protocol/HeapObserver.js:
+        (WebInspector.HeapObserver.prototype.trackingStart):
+        (WebInspector.HeapObserver.prototype.trackingComplete):
+        (WebInspector.HeapObserver):
+        Add snapshot records to the active recording's timeline.
+
+        * UserInterface/Models/HeapAllocationsInstrument.js: Added.
+        (WebInspector.HeapAllocationsInstrument):
+        (WebInspector.HeapAllocationsInstrument.supported):
+        (WebInspector.HeapAllocationsInstrument.prototype.get timelineRecordType):
+        (WebInspector.HeapAllocationsInstrument.prototype.startInstrumentation):
+        (WebInspector.HeapAllocationsInstrument.prototype.stopInstrumentation):
+        (WebInspector.HeapAllocationsInstrument.prototype._takeHeapSnapshot):
+        Start, stop, and periodic snapshots.
+
+        * UserInterface/Models/HeapAllocationsTimelineRecord.js:
+        (WebInspector.HeapAllocationsTimelineRecord):
+        (WebInspector.HeapAllocationsTimelineRecord.prototype.get timestamp):
+        (WebInspector.HeapAllocationsTimelineRecord.prototype.get heapSnapshot):
+        * UserInterface/Models/TimelineRecord.js:
+        * UserInterface/Models/TimelineRecording.js:
+        (WebInspector.TimelineRecording):
+        (WebInspector.TimelineRecording.prototype.addRecord):
+        * UserInterface/Views/TimelineOverviewGraph.js:
+        (WebInspector.TimelineOverviewGraph.createForTimeline):
+        * UserInterface/Views/TimelineTabContentView.js:
+        (WebInspector.TimelineTabContentView.displayNameForTimeline):
+        (WebInspector.TimelineTabContentView.iconClassNameForTimeline):
+        (WebInspector.TimelineTabContentView.genericClassNameForTimeline):
+        (WebInspector.TimelineTabContentView.iconClassNameForRecord):
+        (WebInspector.TimelineTabContentView.displayNameForRecord):
+        New timeline and record type.
+
+        * UserInterface/Models/HeapSnapshotDiff.js: Added.
+        (WebInspector.HeapSnapshotDiff):
+        (WebInspector.HeapSnapshotDiff.prototype.get snapshot1):
+        (WebInspector.HeapSnapshotDiff.prototype.get snapshot2):
+        (WebInspector.HeapSnapshotDiff.prototype.get addedInstances):
+        (WebInspector.HeapSnapshotDiff.prototype.get removedInstances):
+        (WebInspector.HeapSnapshotDiff.prototype.get sizeDifference):
+        (WebInspector.HeapSnapshotDiff.prototype.get growth):
+        (WebInspector.HeapSnapshotDiff.prototype.snapshotForDiff):
+        Compare two snapshots and create a "diff snapshot" which is just
+        the newly added objects.
+
+        * UserInterface/Views/ContentView.js:
+        (WebInspector.ContentView.createFromRepresentedObject):
+        (WebInspector.ContentView.isViewable):
+        A HeapSnapshot creates a HeapSnapshotClusterView.
+
+        * UserInterface/Views/HeapAllocationsTimelineDataGridNode.js: Added.
+        (WebInspector.HeapAllocationsTimelineDataGridNode):
+        (WebInspector.HeapAllocationsTimelineDataGridNode.prototype.get record):
+        (WebInspector.HeapAllocationsTimelineDataGridNode.prototype.get data):
+        (WebInspector.HeapAllocationsTimelineDataGridNode.prototype.createCellContent):
+        (WebInspector.HeapAllocationsTimelineDataGridNode.prototype.markAsBaseline):
+        (WebInspector.HeapAllocationsTimelineDataGridNode.prototype.clearBaseline):
+        * UserInterface/Views/HeapAllocationsTimelineOverviewGraph.css: Copied from Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js.
+        (.timeline-overview-graph.heap-allocations):
+        (.timeline-overview-graph.heap-allocations > img.snapshot):
+        * UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js: Added.
+        (WebInspector.HeapAllocationsTimelineOverviewGraph):
+        (WebInspector.HeapAllocationsTimelineOverviewGraph.prototype.reset):
+        (WebInspector.HeapAllocationsTimelineOverviewGraph.prototype.layout.xScale):
+        (WebInspector.HeapAllocationsTimelineOverviewGraph.prototype.layout):
+        (WebInspector.HeapAllocationsTimelineOverviewGraph.prototype._visibleRecords):
+        (WebInspector.HeapAllocationsTimelineOverviewGraph.prototype._heapAllocationTimelineRecordAdded):
+        * UserInterface/Views/HeapAllocationsTimelineView.css: Copied from Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js.
+        (.timeline-view.heap-allocations > .data-grid):
+        (.timeline-view.heap-allocations > .data-grid td .icon.heap-snapshot):
+        (.timeline-view.heap-allocations > .data-grid tr.baseline):
+        (.timeline-view.heap-allocations > .content-view-container):
+        (.timeline-view.heap-allocations > .content-view-container > .content-view):
+        * UserInterface/Views/HeapAllocationsTimelineView.js: Added.
+        (WebInspector.HeapAllocationsTimelineView):
+        (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotList):
+        (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotTimelineRecord):
+        (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotDiff):
+        (WebInspector.HeapAllocationsTimelineView.prototype.get navigationItems):
+        (WebInspector.HeapAllocationsTimelineView.prototype.get selectionPathComponents):
+        (WebInspector.HeapAllocationsTimelineView.prototype.get navigationSidebarTreeOutlineLabel):
+        (WebInspector.HeapAllocationsTimelineView.prototype.treeElementPathComponentSelected):
+        (WebInspector.HeapAllocationsTimelineView.prototype.userSelectedRecordFromOverview):
+        (WebInspector.HeapAllocationsTimelineView.prototype.closed):
+        (WebInspector.HeapAllocationsTimelineView.prototype.layout):
+        (WebInspector.HeapAllocationsTimelineView.prototype.reset):
+        (WebInspector.HeapAllocationsTimelineView.prototype._heapAllocationsTimelineRecordAdded):
+        (WebInspector.HeapAllocationsTimelineView.prototype._snapshotListPathComponentClicked):
+        (WebInspector.HeapAllocationsTimelineView.prototype._snapshotPathComponentSelected):
+        (WebInspector.HeapAllocationsTimelineView.prototype._currentContentViewDidChange):
+        (WebInspector.HeapAllocationsTimelineView.prototype._contentViewSelectionPathComponentDidChange):
+        (WebInspector.HeapAllocationsTimelineView.prototype._updateCompareHeapSnapshotButton):
+        (WebInspector.HeapAllocationsTimelineView.prototype._takeHeapSnapshotClicked):
+        (WebInspector.HeapAllocationsTimelineView.prototype._cancelSelectComparisonHeapSnapshots):
+        (WebInspector.HeapAllocationsTimelineView.prototype._compareHeapSnapshotsClicked):
+        (WebInspector.HeapAllocationsTimelineView.prototype._dataGridNodeSelected):
+        * UserInterface/Views/HeapSnapshotClassDataGridNode.js: Added.
+        (WebInspector.HeapSnapshotClassDataGridNode):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype.get data):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype.createCellContent):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype.sort):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._populate):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._fetchBatch):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._updateBatchedSort):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._updateBatchedChildren):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._removeFetchMoreDataGridNode):
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._appendFetchMoreDataGridNode):
+        * UserInterface/Views/HeapSnapshotClusterContentView.js: Added.
+        (WebInspector.HeapSnapshotClusterContentView.createPathComponent):
+        (WebInspector.HeapSnapshotClusterContentView):
+        (WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.get heapSnapshot):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.get summaryContentView):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.get instancesContentView):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.get navigationItems):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.get selectionPathComponents):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.shown):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.closed):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.saveToCookie):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.restoreFromCookie):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.showSummary):
+        (WebInspector.HeapSnapshotClusterContentView.prototype.showInstances):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._contentViewExtraArguments):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._pathComponentForContentView):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._identifierForContentView):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._showContentViewForIdentifier):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._pathComponentSelected):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._toggleShowInternalObjectsSetting):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._updateViewsForShowInternalObjectsSettingValue):
+        (WebInspector.HeapSnapshotClusterContentView.prototype._updateShowInternalObjectsButtonNavigationItem):
+        * UserInterface/Views/HeapSnapshotInstanceDataGridNode.js: Added.
+        (WebInspector.HeapSnapshotInstanceDataGridNode):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.get data):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.get selectable):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCells):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCellContent):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.sort):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._contextMenuHandler.):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._contextMenuHandler):
+        * UserInterface/Views/HeapSnapshotInstanceFetchMoreDataGridNode.js: Added.
+        (WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode):
+        (WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode.prototype.createCellContent):
+        (WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode.prototype.sort):
+        * UserInterface/Views/HeapSnapshotInstancesContentView.css: Added.
+        * UserInterface/Views/HeapSnapshotInstancesContentView.js: Added.
+        (WebInspector.HeapSnapshotInstancesContentView):
+        (WebInspector.HeapSnapshotInstancesContentView.prototype.get showInternalObjects):
+        (WebInspector.HeapSnapshotInstancesContentView.prototype.set showInternalObjects):
+        (WebInspector.HeapSnapshotInstancesContentView.prototype._sortDataGrid):
+        * UserInterface/Views/HeapSnapshotInstancesDataGridTree.js: Added.
+        (WebInspector.HeapSnapshotInstancesDataGridTree):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.buildSortComparator):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get heapSnapshot):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get includeInternalObjects):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.set includeInternalObjects):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.get children):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.appendChild):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.insertChild):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.removeChildren):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.set sortComparator):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.sort):
+        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype._populateTopLevel):
+        * UserInterface/Views/HeapSnapshotSummaryContentView.css: Added.
+        * UserInterface/Views/HeapSnapshotSummaryContentView.js: Added.
+        (WebInspector.HeapSnapshotSummaryContentView.createChartContainer):
+        (WebInspector.HeapSnapshotSummaryContentView.appendLegendRow):
+        (WebInspector.HeapSnapshotSummaryContentView.appendEmptyMessage):
+        (WebInspector.HeapSnapshotSummaryContentView):
+        (WebInspector.HeapSnapshotSummaryContentView.prototype.layout):
+        * UserInterface/Views/PathComponentIcons.css:
+        (.heap-snapshot-summary-icon .icon):
+        (.heap-snapshot-instances-icon .icon):
+        (.snapshot-list-icon .icon):
+        (.snapshot-diff-icon .icon):
+        (body:not(.mac-platform, .windows-platform) .snapshot-diff-icon .icon):
+        (body:not(.mac-platform, .windows-platform) .call-trees-icon .icon): Deleted.
+        * UserInterface/Views/TextNavigationItem.css:
+        (.navigation-bar .item.text):
+        * UserInterface/Views/TextNavigationItem.js:
+        (WebInspector.TextNavigationItem):
+        (WebInspector.TextNavigationItem.prototype.get text):
+        (WebInspector.TextNavigationItem.prototype.set text):
+        * UserInterface/Views/TimelineIcons.css:
+        (.heap-snapshot-record .icon):
+        * UserInterface/Views/Variables.css:
+        (:root):
+        New views.
+
+        * UserInterface/Views/FormattedValue.js:
+        (WebInspector.FormattedValue.createElementForNodePreview):
+        (WebInspector.FormattedValue.createElementForFunctionWithName):
+        (WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForObjectPreview):
+        Better display for a raw object preview.
+
+        * UserInterface/Views/TimelineOverview.js:
+        (WebInspector.TimelineOverview):
+        (WebInspector.TimelineOverview.prototype.userSelectedRecord):
+        * UserInterface/Views/TimelineRecordingContentView.js:
+        (WebInspector.TimelineRecordingContentView):
+        (WebInspector.TimelineRecordingContentView.prototype.timelineOverviewUserSelectedRecord):
+        * UserInterface/Views/TimelineRuler.js:
+        (WebInspector.TimelineRuler.prototype.set allowsTimeRangeSelection):
+        (WebInspector.TimelineRuler.prototype._handleClick):
+        (WebInspector.TimelineRuler.prototype._handleMouseDown):
+        (WebInspector.TimelineRuler.prototype._handleMouseMove):
+        * UserInterface/Views/TimelineView.js:
+        (WebInspector.TimelineView.prototype.userSelectedRecordFromOverview):
+        Hook up a way for clicking in the TimelineOverview / TimelineRuler
+        to redispatch to an OverviewGraph element, and provide a patch for
+        the Overview -> RecordingContentView -> TimelineView for records.
+
 2016-03-09  Matt Baker  <mattbaker@apple.com>
 
         Web Inspector: LayoutTimelineView path components should reflect grid row nesting
index b70328f..d50a08b 100644 (file)
Binary files a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js and b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js differ
index 536197c..73bf917 100644 (file)
@@ -305,6 +305,29 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object
         // Called from WebInspector.MemoryObserver.
     }
 
+    heapTrackingStarted(timestamp, snapshot)
+    {
+        // Called from WebInspector.HeapObserver.
+
+        this._addRecord(new WebInspector.HeapAllocationsTimelineRecord(timestamp, snapshot));
+
+        this.capturingStarted(timestamp);
+    }
+
+    heapTrackingCompleted(timestamp, snapshot)
+    {
+        // Called from WebInspector.HeapObserver.
+
+        this._addRecord(new WebInspector.HeapAllocationsTimelineRecord(timestamp, snapshot));
+    }
+
+    heapSnapshotAdded(timestamp, snapshot)
+    {
+        // Called from WebInspector.HeapAllocationsInstrument.
+
+        this._addRecord(new WebInspector.HeapAllocationsTimelineRecord(timestamp, snapshot));
+    }
+
     // Private
 
     _processRecord(recordPayload, parentRecordPayload)
diff --git a/Source/WebInspectorUI/UserInterface/Images/Compare.svg b/Source/WebInspectorUI/UserInterface/Images/Compare.svg
new file mode 100644 (file)
index 0000000..deb7c8a
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2016 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <rect fill="none" stroke="currentColor" x="0.5" y="0.5" width="15" height="15" rx="2"/>
+    <path fill="none" stroke-linecap="round" stroke="currentColor" d="M 3.5 5.5 L 12.5 5.5"/>
+    <path fill="none" stroke-linecap="round" stroke="currentColor" d="M 12.5 5.5 L 9.5 2.5"/>
+    <path fill="none" stroke-linecap="round" stroke="currentColor" d="M 12.5 5.5 L 9.5 8.5"/>
+    <path fill="none" stroke-linecap="round" stroke="currentColor" d="M 3.5 10.5 L 12.5 10.5"/>
+    <path fill="none" stroke-linecap="round" stroke="currentColor" d="M 3.5 10.5 L 6.5 7.5"/>
+    <path fill="none" stroke-linecap="round" stroke="currentColor" d="M 3.5 10.5 L 6.5 13.5"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/HeapSnapshot.svg b/Source/WebInspectorUI/UserInterface/Images/HeapSnapshot.svg
new file mode 100644 (file)
index 0000000..7019422
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2013 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(228, 198, 172)" d="M 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 L 3 1 Z M 3 1"/>
+    <path fill="rgb(186, 157, 132)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(186, 157, 132)" d="M 5.19849793 12.6584609 C 5.2831472 12.676395 6.98277851 12.9487305 7.62939453 12.9487305 C 8.79640591 12.9487305 9.76331344 12.6806542 10.5034462 12.1146703 C 11.3037555 11.5026691 11.7177734 10.6286312 11.7177734 9.58544922 C 11.7177734 8.84534902 11.4998181 8.18341061 11.0628295 7.63852362 C 10.6767371 7.15709971 10.0557113 6.69185669 9.18734192 6.20492057 L 8.61524891 5.88648574 C 8.27558142 5.69523066 8.15431716 5.69238281 8.23632812 5.69238281 C 8.56703836 5.69238281 9.10333246 5.81936052 9.8138795 6.08542439 L 11.1645508 6.59118236 L 11.1645508 5.14892578 L 11.1645508 3.87597656 L 11.1645508 3.07965768 L 10.3884483 2.90136387 C 9.52406853 2.70279014 8.73593121 2.60205078 8.02148438 2.60205078 C 7.01081351 2.60205078 6.14909735 2.87110984 5.47554965 3.42697448 C 4.76046595 4.01711791 4.38964844 4.82767811 4.38964844 5.77734375 C 4.38964844 6.49239045 4.61205052 7.14196725 5.04920932 7.68841575 C 5.43639142 8.17239338 6.03437082 8.62885808 6.84888633 9.08727887 L 7.38545082 9.38538774 C 7.71699471 9.57330366 7.92926232 9.72631865 7.99164827 9.79813503 C 7.94460492 9.81851991 7.82252674 9.85302734 7.60253906 9.85302734 C 7.14294036 9.85302734 6.53493744 9.70437137 5.79174704 9.39341305 L 4.40576172 8.81350287 L 4.40576172 10.315918 L 4.40576172 11.6801758 L 4.40576172 12.4905083 L 5.19849793 12.6584609 C 5.52713094 12.7280865 5.52713094 12.7280865 5.57407851 12.738033 L 5.19849793 12.6584609 Z M 5.19849793 12.6584609"/>
+    <path fill="white" d="M 5.40576172 11.6801758 L 5.40576172 10.315918 C 6.26156027 10.6739927 6.99381206 10.8530273 7.60253906 10.8530273 C 8.02864796 10.8530273 8.36433797 10.758139 8.60961914 10.5683594 C 8.85490031 10.3785798 8.97753906 10.1207699 8.97753906 9.79492188 C 8.97753906 9.53710809 8.90055416 9.31958096 8.74658203 9.14233398 C 8.59260991 8.965087 8.3007834 8.75472127 7.87109375 8.51123047 L 7.33935547 8.21582031 C 6.6267868 7.81477664 6.12369938 7.43074728 5.83007812 7.0637207 C 5.53645687 6.69669413 5.38964844 6.2679061 5.38964844 5.77734375 C 5.38964844 5.12206704 5.63045007 4.59570511 6.11206055 4.19824219 C 6.59367103 3.80077926 7.23013927 3.60205078 8.02148438 3.60205078 C 8.65527661 3.60205078 9.36962493 3.69335846 10.1645508 3.87597656 L 10.1645508 5.14892578 C 9.3517212 4.84456228 8.70898674 4.69238281 8.23632812 4.69238281 C 7.87109192 4.69238281 7.57837024 4.77384359 7.3581543 4.93676758 C 7.13793835 5.09969157 7.02783203 5.313638 7.02783203 5.57861328 C 7.02783203 5.79703885 7.10481694 5.98860595 7.25878906 6.15332031 C 7.41276119 6.31803468 7.70279735 6.52034385 8.12890625 6.76025391 L 8.69824219 7.07714844 C 9.46452206 7.50683809 9.99267433 7.9025047 10.2827148 8.26416016 C 10.5727554 8.62581561 10.7177734 9.06624089 10.7177734 9.58544922 C 10.7177734 10.3230831 10.4438504 10.9013651 9.89599609 11.3203125 C 9.34814179 11.7392599 8.59261549 11.9487305 7.62939453 11.9487305 C 6.99202155 11.9487305 6.25081803 11.8592131 5.40576172 11.6801758 C 5.40576172 11.6801758 6.25081803 11.8592131 5.40576172 11.6801758 L 5.40576172 11.6801758 L 5.40576172 11.6801758 Z M 5.40576172 11.6801758"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/HeapSnapshotDiff.svg b/Source/WebInspectorUI/UserInterface/Images/HeapSnapshotDiff.svg
new file mode 100644 (file)
index 0000000..2419672
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2013 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(165, 202, 164)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(118, 153, 116)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(128, 166, 126)" d="M 4.445312 12.742188 C 3.890625 12.742188 3.445312 12.292969 3.445312 11.742188 L 3.445312 3.792969 C 3.445312 3.242188 3.890625 2.792969 4.445312 2.792969 L 7.867188 2.792969 C 9.367188 2.792969 10.554688 3.222656 11.402344 4.0625 C 12.25 4.910156 12.679688 6.101562 12.679688 7.601562 C 12.679688 9.171875 12.234375 10.429688 11.359375 11.347656 C 10.472656 12.273438 9.246094 12.742188 7.71875 12.742188 L 4.445312 12.742188 M 7.335938 9.613281 C 8.1875 9.613281 8.449219 9.324219 8.535156 9.226562 C 8.789062 8.941406 8.921875 8.414062 8.921875 7.699219 C 8.921875 7.140625 8.8125 6.703125 8.578125 6.363281 C 8.457031 6.183594 8.328125 6.070312 8.1875 6.011719 C 8.0625 5.964844 7.761719 5.882812 7.097656 5.882812 C 7.097656 6.960938 7.097656 8.539062 7.097656 9.613281 C 7.171875 9.613281 7.335938 9.613281 7.335938 9.613281"/>
+    <path fill="white" d="M 4.445312 11.742188 L 4.445312 3.792969 L 7.867188 3.792969 C 9.097656 3.792969 10.039062 4.121094 10.695312 4.773438 C 11.351562 5.425781 11.679688 6.371094 11.679688 7.601562 C 11.679688 8.910156 11.332031 9.929688 10.636719 10.65625 C 9.941406 11.378906 8.96875 11.742188 7.71875 11.742188 Z M 6.097656 10.613281 L 7.332031 10.613281 C 8.203125 10.613281 8.851562 10.375 9.28125 9.890625 C 9.707031 9.410156 9.921875 8.679688 9.921875 7.699219 C 9.921875 6.9375 9.75 6.308594 9.40625 5.800781 C 9.171875 5.453125 8.886719 5.214844 8.554688 5.082031 C 8.21875 4.949219 7.734375 4.882812 7.097656 4.882812 L 6.097656 4.882812 Z"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/HeapSnapshotInstances.svg b/Source/WebInspectorUI/UserInterface/Images/HeapSnapshotInstances.svg
new file mode 100644 (file)
index 0000000..fb11a43
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2015 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path d="M 13 1 L 3 1 C 1.9 1 1 1.9 1 3 L 1 13 C 1 14.1 1.9 15 3 15 L 13 15 C 14.1 15 15 14.1 15 13 L 15 3 C 15 1.9 14.1 1 13 1 L 13 1 Z" fill="rgb(246, 222, 146)"/>
+    <path d="M 13 1 L 3 1 C 1.9 1 1 1.9 1 3 L 1 13 C 1 14.1 1.9 15 3 15 L 13 15 C 14.1 15 15 14.1 15 13 L 15 3 C 15 1.9 14.1 1 13 1 M 13 2 C 13.552 2 14 2.449 14 3 L 14 13 C 14 13.552 13.552 14 13 14 L 3 14 C 2.449 14 2 13.552 2 13 L 2 3 C 2 2.449 2.449 2 3 2 L 13 2" fill="rgb(204, 181, 108)"/>
+    <path d="M 7.9736 11.041 C 8.6766 11.041 9.2346 10.757999 9.6436 10.193 C 10.0536 9.629 10.2586 8.864 10.2586 7.898 C 10.2586 6.962 10.0526 6.210999 9.6406 5.645 C 9.2286 5.079 8.683599 4.795 8.0056 4.795 C 7.3206 4.795 6.7726 5.079 6.3626 5.645 C 5.9526 6.210999 5.7476 6.967999 5.7476 7.915 C 5.7476 8.855 5.9516 9.611 6.3596 10.183 C 6.7676 10.754999 7.3066 11.041 7.9736 11.041 M 7.9526 12.15 C 6.7556 12.15 5.7986 11.766 5.0816 10.997 C 4.3646 10.226999 4.0056 9.202 4.0056 7.92 C 4.0056 6.624 4.3666 5.596 5.0866 4.833 C 5.8076 4.071 6.7806 3.69 8.0056 3.69 C 9.223599 3.69 10.1936 4.071 10.9156 4.833 C 11.6386 5.596 11.9996 6.62 11.9996 7.903999 C 11.9996 9.217999 11.6386 10.254999 10.9156 11.013 C 10.1936 11.771 9.2056 12.15 7.9526 12.15" fill="white"/>
+    <path d="M 7.9736 10.041 C 7.6326 10.041 7.3936 9.91 7.1736 9.601999 C 6.8906 9.205999 6.7476 8.638 6.7476 7.915 C 6.7476 7.186999 6.8906 6.620999 7.1726 6.23 C 7.3936 5.926 7.6426 5.795 8.0056 5.795 C 8.3616 5.795 8.6086 5.926 8.8316 6.233 C 9.1156 6.622 9.2586 7.183 9.2586 7.898 C 9.2586 8.643999 9.1166 9.217999 8.834599 9.605 C 8.6136 9.910999 8.355599 10.041 7.9736 10.041 M 8.0056 2.69 C 6.5006 2.69 5.2736 3.18 4.3606 4.146 C 3.4616 5.097 3.0056 6.367 3.0056 7.92 C 3.0056 9.455999 3.4576 10.721 4.3496 11.679 C 5.2616 12.655 6.4736 13.15 7.9526 13.15 C 9.4846 13.15 10.7246 12.663 11.6406 11.702 C 12.5416 10.756 12.9996 9.478 12.9996 7.903999 C 12.9996 6.360999 12.5426 5.096 11.6416 4.145 C 10.7256 3.18 9.5026 2.69 8.0056 2.69 M 7.9736 11.041 C 8.6766 11.041 9.2346 10.757999 9.6436 10.193 C 10.0536 9.629 10.2586 8.864 10.2586 7.898 C 10.2586 6.962 10.0526 6.210999 9.6406 5.645 C 9.2286 5.079 8.683599 4.795 8.0056 4.795 C 7.3206 4.795 6.7726 5.079 6.3626 5.645 C 5.9526 6.210999 5.7476 6.967999 5.7476 7.915 C 5.7476 8.855 5.9516 9.611 6.3596 10.183 C 6.7676 10.754999 7.3066 11.041 7.9736 11.041 M 8.0056 3.69 C 9.223599 3.69 10.1936 4.071 10.9156 4.833 C 11.6386 5.596 11.9996 6.62 11.9996 7.903999 C 11.9996 9.217999 11.6386 10.254999 10.9156 11.013 C 10.1936 11.771 9.2056 12.15 7.9526 12.15 C 6.7556 12.15 5.7986 11.766 5.0816 10.997 C 4.3646 10.226999 4.0056 9.202 4.0056 7.92 C 4.0056 6.624 4.3666 5.596 5.0866 4.833 C 5.8076 4.071 6.7806 3.69 8.0056 3.69" fill="rgb(216, 193, 115)"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/HeapSnapshotSummary.svg b/Source/WebInspectorUI/UserInterface/Images/HeapSnapshotSummary.svg
new file mode 100644 (file)
index 0000000..7600350
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2013 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(245, 245, 245)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(191, 191, 191)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(203, 203, 203)" d="M 12 6 L 4 6 L 4 4 L 12 4 Z"/>
+    <path fill="rgb(175, 178, 217)" d="M 12 9 L 4 9 L 4 7 L 12 7 Z"/>
+    <path fill="rgb(203, 203, 203)" d="M 12 12 L 4 12 L 4 10 L 12 10 Z"/>
+</svg>
index a1650ca..9e27791 100644 (file)
     <link rel="stylesheet" href="Views/GoToLineDialog.css">
     <link rel="stylesheet" href="Views/GradientEditor.css">
     <link rel="stylesheet" href="Views/GradientSlider.css">
+    <link rel="stylesheet" href="Views/HeapAllocationsTimelineOverviewGraph.css">
+    <link rel="stylesheet" href="Views/HeapAllocationsTimelineView.css">
+    <link rel="stylesheet" href="Views/HeapSnapshotInstancesContentView.css">
+    <link rel="stylesheet" href="Views/HeapSnapshotSummaryContentView.css">
     <link rel="stylesheet" href="Views/HierarchicalPathComponent.css">
     <link rel="stylesheet" href="Views/HoverMenu.css">
     <link rel="stylesheet" href="Views/ImageResourceContentView.css">
     <link rel="stylesheet" href="Views/TabBrowser.css">
     <link rel="stylesheet" href="Views/TextContentView.css">
     <link rel="stylesheet" href="Views/TextEditor.css">
+    <link rel="stylesheet" href="Views/TextNavigationItem.css">
     <link rel="stylesheet" href="Views/TextResourceContentView.css">
     <link rel="stylesheet" href="Views/TimelineDataGrid.css">
     <link rel="stylesheet" href="Views/TimelineIcons.css">
     <script src="Models/GarbageCollection.js"></script>
     <script src="Models/Geometry.js"></script>
     <script src="Models/Gradient.js"></script>
+    <script src="Models/HeapAllocationsInstrument.js"></script>
+    <script src="Models/HeapAllocationsTimelineRecord.js"></script>
     <script src="Models/HeapSnapshot.js"></script>
+    <script src="Models/HeapSnapshotDiff.js"></script>
     <script src="Models/HeapSnapshotEdge.js"></script>
     <script src="Models/HeapSnapshotNode.js"></script>
     <script src="Models/IndexedDatabase.js"></script>
     <script src="Views/GoToLineDialog.js"></script>
     <script src="Views/GradientEditor.js"></script>
     <script src="Views/GradientSlider.js"></script>
+    <script src="Views/HeapAllocationsTimelineOverviewGraph.js"></script>
+    <script src="Views/HeapAllocationsTimelineView.js"></script>
+    <script src="Views/HeapAllocationsTimelineDataGridNode.js"></script>
+    <script src="Views/HeapSnapshotClassDataGridNode.js"></script>
+    <script src="Views/HeapSnapshotClusterContentView.js"></script>
+    <script src="Views/HeapSnapshotInstanceDataGridNode.js"></script>
+    <script src="Views/HeapSnapshotInstanceFetchMoreDataGridNode.js"></script>
+    <script src="Views/HeapSnapshotInstancesContentView.js"></script>
+    <script src="Views/HeapSnapshotInstancesDataGridTree.js"></script>
+    <script src="Views/HeapSnapshotSummaryContentView.js"></script>
     <script src="Views/HierarchicalPathNavigationItem.js"></script>
     <script src="Views/HoverMenu.js"></script>
     <script src="Views/ImageResourceContentView.js"></script>
     <script src="Views/StorageSidebarPanel.js"></script>
     <script src="Views/SyntaxHighlightingSupport.js"></script>
     <script src="Views/TextContentView.js"></script>
+    <script src="Views/TextNavigationItem.js"></script>
     <script src="Views/TextResourceContentView.js"></script>
     <script src="Views/TimelineDataGridNodePathComponent.js"></script>
     <script src="Views/TimelineRecordBar.js"></script>
diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js
new file mode 100644 (file)
index 0000000..4cd4178
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.HeapAllocationsInstrument = class HeapAllocationsInstrument extends WebInspector.Instrument
+{
+    constructor()
+    {
+        super();
+
+        console.assert(WebInspector.HeapAllocationsInstrument.supported());
+
+        this._snapshotIntervalIdentifier = undefined;
+    }
+
+    // Static
+
+    static supported()
+    {
+        // COMPATIBILITY (iOS 9): HeapAgent did not exist.
+        return window.HeapAgent;
+    }
+
+    // Protected
+
+    get timelineRecordType()
+    {
+        return WebInspector.TimelineRecord.Type.HeapAllocations;
+    }
+
+    startInstrumentation()
+    {
+        // FIXME: Include a "track allocations" option for this instrument.
+        // FIXME: Include a periodic snapshot interval option for this instrument.
+
+        HeapAgent.startTracking();
+
+        // Periodic snapshots.
+        const snapshotInterval = 10000;
+        this._snapshotIntervalIdentifier = setInterval(this._takeHeapSnapshot.bind(this), snapshotInterval);
+    }
+
+    stopInstrumentation()
+    {
+        HeapAgent.stopTracking();
+
+        window.clearInterval(this._snapshotIntervalIdentifier);
+        this._snapshotIntervalIdentifier = undefined;
+    }
+
+    // Private
+
+    _takeHeapSnapshot()
+    {
+        HeapAgent.snapshot(function(error, timestamp, snapshotStringData) {
+            let payload = JSON.parse(snapshotStringData);
+            let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
+            WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
+        });
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js
new file mode 100644 (file)
index 0000000..c696044
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.HeapAllocationsTimelineRecord = class HeapAllocationsTimelineRecord extends WebInspector.TimelineRecord
+{
+    constructor(timestamp, heapSnapshot)
+    {
+        super(WebInspector.TimelineRecord.Type.HeapAllocations, timestamp, timestamp);
+
+        console.assert(typeof timestamp === "number");
+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
+
+        this._timestamp = timestamp;
+        this._heapSnapshot = heapSnapshot;
+    }
+
+    // Public
+
+    get timestamp() { return this._timestamp; }
+    get heapSnapshot() { return this._heapSnapshot; }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js b/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js
new file mode 100644 (file)
index 0000000..255b38e
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.HeapSnapshotDiff = class HeapSnapshotDiff extends WebInspector.Object
+{
+    constructor(snapshot1, snapshot2)
+    {
+        super();
+
+        console.assert(snapshot1 instanceof WebInspector.HeapSnapshot);
+        console.assert(snapshot2 instanceof WebInspector.HeapSnapshot);
+
+        this._snapshot1 = snapshot1;
+        this._snapshot2 = snapshot2;
+
+        let known = new Map;
+        for (let instance of snapshot1.instances)
+            known.set(instance.id, instance);
+
+        let added = [];
+        for (let instance of snapshot2.instances) {
+            if (known.has(instance.id))
+                known.delete(instance.id);
+            else
+                added.push(instance);
+        }
+
+        let removed = [...known.values()];
+
+        this._addedInstances = added;
+        this._removedInstances = removed;
+    }
+
+    // Public
+
+    get snapshot1() { return this._snapshot1; }
+    get snapshot2() { return this._snapshot2; }
+    get addedInstances() { return this._addedInstances; }
+    get removedInstances() { return this._removedInstances; }
+
+    get sizeDifference()
+    {
+        return this._snapshot2.totalSize - this._snapshot1.totalSize;
+    }
+
+    get growth()
+    {
+        return this._addedInstances.reduce((sum, x) => sum += x.size, 0);
+    }
+
+    snapshotForDiff()
+    {
+        // FIXME: This only includes the newly added instances. Should we do anything with the removed instances?
+        return new WebInspector.HeapSnapshot(null, this._addedInstances, this._snapshot2.nodeMap);
+    }
+};
index 18ea199..99f4431 100644 (file)
@@ -159,6 +159,7 @@ WebInspector.TimelineRecord.Type = {
     Script: "timeline-record-type-script",
     RenderingFrame: "timeline-record-type-rendering-frame",
     Memory: "timeline-record-type-memory",
+    HeapAllocations: "timeline-record-type-heap-allocations",
 };
 
 WebInspector.TimelineRecord.TypeIdentifier = "timeline-record";
index 0fe4f1e..fa2345f 100644 (file)
@@ -44,6 +44,7 @@ WebInspector.TimelineRecording = class TimelineRecording extends WebInspector.Ob
             WebInspector.TimelineRecord.Type.Script,
             WebInspector.TimelineRecord.Type.RenderingFrame,
             WebInspector.TimelineRecord.Type.Memory,
+            WebInspector.TimelineRecord.Type.HeapAllocations,
         ];
 
         for (let type of timelines) {
@@ -243,7 +244,8 @@ WebInspector.TimelineRecording = class TimelineRecording extends WebInspector.Ob
         // Some records don't have source code timelines.
         if (record.type === WebInspector.TimelineRecord.Type.Network
             || record.type === WebInspector.TimelineRecord.Type.RenderingFrame
-            || record.type === WebInspector.TimelineRecord.Type.Memory)
+            || record.type === WebInspector.TimelineRecord.Type.Memory
+            || record.type === WebInspector.TimelineRecord.Type.HeapAllocations)
             return;
 
         if (!WebInspector.TimelineRecording.sourceCodeTimelinesSupported())
index 38839f2..2fa1608 100644 (file)
@@ -36,13 +36,13 @@ WebInspector.HeapObserver = class HeapObserver
     {
         let payload = JSON.parse(snapshotData);
         let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-        // FIXME: Heap Allocations Timeline.
+        WebInspector.timelineManager.heapTrackingStarted(timestamp, snapshot);
     }
 
     trackingComplete(timestamp, snapshotData)
     {
         let payload = JSON.parse(snapshotData);
         let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-        // FIXME: Heap Allocations Timeline.
+        WebInspector.timelineManager.heapTrackingCompleted(timestamp, snapshot);
     }
 };
index e10c756..113bfbe 100644 (file)
     <script src="Models/Frame.js"></script>
     <script src="Models/GarbageCollection.js"></script>
     <script src="Models/Geometry.js"></script>
+    <script src="Models/HeapAllocationsInstrument.js"></script>
+    <script src="Models/HeapAllocationsTimelineRecord.js"></script>
     <script src="Models/HeapSnapshot.js"></script>
+    <script src="Models/HeapSnapshotDiff.js"></script>
     <script src="Models/HeapSnapshotEdge.js"></script>
     <script src="Models/HeapSnapshotNode.js"></script>
     <script src="Models/IndexedDatabase.js"></script>
index 5a2041a..413fa68 100644 (file)
@@ -73,6 +73,9 @@ WebInspector.ContentView = class ContentView extends WebInspector.View
 
             if (timelineType === WebInspector.TimelineRecord.Type.Memory)
                 return new WebInspector.MemoryTimelineView(representedObject, extraArguments);
+
+            if (timelineType === WebInspector.TimelineRecord.Type.HeapAllocations)
+                return new WebInspector.HeapAllocationsTimelineView(representedObject, extraArguments);
         }
 
         if (representedObject instanceof WebInspector.Breakpoint) {
@@ -135,6 +138,9 @@ WebInspector.ContentView = class ContentView extends WebInspector.View
         if (representedObject instanceof WebInspector.CallingContextTree)
             return new WebInspector.ProfileView(representedObject, extraArguments);
 
+        if (representedObject instanceof WebInspector.HeapSnapshot)
+            return new WebInspector.HeapSnapshotClusterContentView(representedObject, extraArguments);
+
         if (typeof representedObject === "string" || representedObject instanceof String)
             return new WebInspector.TextContentView(representedObject, extraArguments);
 
@@ -231,6 +237,8 @@ WebInspector.ContentView = class ContentView extends WebInspector.View
             return true;
         if (representedObject instanceof WebInspector.CallingContextTree)
             return true;
+        if (representedObject instanceof WebInspector.HeapSnapshot)
+            return true;
         if (typeof representedObject === "string" || representedObject instanceof String)
             return true;
         return false;
index 352f44f..c0329d0 100644 (file)
@@ -94,7 +94,7 @@ WebInspector.FormattedValue.createElementForError = function(object)
 
 WebInspector.FormattedValue.createElementForNodePreview = function(preview)
 {
-    var value = preview.value;
+    var value = preview.value || preview.description;
     var span = document.createElement("span");
     span.className = "formatted-node-preview syntax-highlighted";
 
@@ -153,6 +153,14 @@ WebInspector.FormattedValue.createElementForNodePreview = function(preview)
     return span;
 };
 
+WebInspector.FormattedValue.createElementForFunctionWithName = function(description)
+{
+    var span = document.createElement("span");
+    span.classList.add("formatted-function");
+    span.textContent = description.substring(0, description.indexOf("("));
+    return span;
+}
+
 WebInspector.FormattedValue.createElementForTypesAndValue = function(type, subtype, displayString, size, isPreview, hadException)
 {
     var span = document.createElement("span");
@@ -208,6 +216,17 @@ WebInspector.FormattedValue.createElementForPropertyPreview = function(propertyP
     return WebInspector.FormattedValue.createElementForTypesAndValue(propertyPreview.type, propertyPreview.subtype, propertyPreview.value, undefined, true, false);
 };
 
+WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForObjectPreview = function(objectPreview, previewViewMode)
+{
+    if (objectPreview.subtype === "node")
+        return WebInspector.FormattedValue.createElementForNodePreview(objectPreview);
+
+    if (objectPreview.type === "function")
+        return WebInspector.FormattedValue.createElementForFunctionWithName(objectPreview.description);
+
+    return new WebInspector.ObjectPreviewView(objectPreview, previewViewMode).element;
+}
+
 WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForRemoteObject = function(object, previewViewMode)
 {
     if (object.subtype === "node")
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineDataGridNode.js
new file mode 100644 (file)
index 0000000..6f37a22
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.HeapAllocationsTimelineDataGridNode = class HeapAllocationsTimelineDataGridNode extends WebInspector.TimelineDataGridNode
+{
+    constructor(heapAllocationsTimelineRecord, zeroTime, heapAllocationsView)
+    {
+        super(false, null);
+
+        this._record = heapAllocationsTimelineRecord;
+        this._heapAllocationsView = heapAllocationsView;
+
+        this._data = {
+            name: WebInspector.UIString("Snapshot %d").format(this._record.heapSnapshot.identifier),
+            timestamp: this._record.timestamp - zeroTime,
+            size: this._record.heapSnapshot.totalSize,
+        };
+    }
+
+    // Public
+
+    get record() { return this._record; }
+    get data() { return this._data; }
+
+    createCellContent(columnIdentifier, cell)
+    {
+        switch (columnIdentifier) {
+        case "name":
+            let fragment = document.createDocumentFragment();
+            let iconElement = fragment.appendChild(document.createElement("img"));
+            iconElement.classList.add("icon", "heap-snapshot");
+            let titleElement = fragment.appendChild(document.createElement("span"));
+            titleElement.textContent = this._data.name;
+            let goToButton = fragment.appendChild(WebInspector.createGoToArrowButton());
+            goToButton.addEventListener("click", (event) => {
+                this._heapAllocationsView.showHeapSnapshotTimelineRecord(this._record);
+            });
+            return fragment;
+
+        case "timestamp":
+            return Number.secondsToString(this._data.timestamp, true);
+
+        case "size":
+            return Number.bytesToString(this._data.size);
+        }
+
+        return super.createCellContent(columnIdentifier, cell);
+    }
+
+    markAsBaseline()
+    {
+        this.element.classList.add("baseline");
+    }
+
+    clearBaseline()
+    {
+        this.element.classList.remove("baseline");
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.css b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.css
new file mode 100644 (file)
index 0000000..165564b
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+.timeline-overview-graph.heap-allocations {
+    position: relative;
+}
+
+.timeline-overview-graph.heap-allocations > img.snapshot {
+    content: url(../Images/HeapSnapshot.svg);
+    position: absolute;
+    top: 9px;
+    width: 16px;
+    height: 16px;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js
new file mode 100644 (file)
index 0000000..98202d9
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.HeapAllocationsTimelineOverviewGraph = class HeapAllocationsTimelineOverviewGraph extends WebInspector.TimelineOverviewGraph
+{
+    constructor(timeline, timelineOverview)
+    {
+        super(timelineOverview);
+
+        this.element.classList.add("heap-allocations");
+
+        this._heapAllocationsTimeline = timeline;
+        this._heapAllocationsTimeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._heapAllocationTimelineRecordAdded, this);
+
+        this.reset();
+    }
+
+    // Protected
+
+    reset()
+    {
+        super.reset();
+
+        this.element.removeChildren();
+    }
+
+    layout()
+    {
+        this.element.removeChildren();
+
+        // This may display records past the current time marker.
+        let visibleRecords = this._visibleRecords(this.startTime, this.endTime);
+        if (!visibleRecords.length)
+            return;
+
+        let graphStartTime = this.startTime;
+        let secondsPerPixel = this.timelineOverview.secondsPerPixel;
+
+        function xScale(time) {
+            return (time - graphStartTime) / secondsPerPixel;
+        }
+
+        for (let record of visibleRecords) {
+            const halfImageWidth = 8;
+            let x = xScale(record.timestamp) - halfImageWidth;
+            if (x <= 1)
+                x = 1;
+
+            let imageElement = this.element.appendChild(document.createElement("img"));
+            imageElement.classList.add("snapshot");
+            imageElement.style.left = x + "px";
+            imageElement.addEventListener("click", (event) => {
+                this.timelineOverview.userSelectedRecord(record);
+            });
+        }
+    }
+
+    // Private
+
+    _visibleRecords(startTime, endTime)
+    {
+        let records = this._heapAllocationsTimeline.records;
+        let lowerIndex = records.lowerBound(startTime, (time, record) => time - record.timestamp);
+        let upperIndex = records.upperBound(endTime, (time, record) => time - record.timestamp);
+        return records.slice(lowerIndex, upperIndex);
+    }
+
+    _heapAllocationTimelineRecordAdded(event)
+    {
+        this.needsLayout();
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.css b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.css
new file mode 100644 (file)
index 0000000..2085476
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+.timeline-view.heap-allocations > .data-grid {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.timeline-view.heap-allocations > .data-grid td .icon.heap-snapshot {
+    content: url(../Images/HeapSnapshot.svg);
+}
+
+.timeline-view.heap-allocations > .data-grid tr.baseline {
+    background: hsl(129, 29%, 77%);
+}
+
+.timeline-view.heap-allocations > .content-view-container {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.timeline-view.heap-allocations > .content-view-container > .content-view {
+    border-top: 1px solid var(--border-color);
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js
new file mode 100644 (file)
index 0000000..8b3a36e
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * 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.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WebInspector.TimelineView
+{
+    constructor(timeline, extraArguments)
+    {
+        super(timeline, extraArguments);
+
+        console.assert(timeline.type === WebInspector.TimelineRecord.Type.HeapAllocations, timeline);
+
+        this.element.classList.add("heap-allocations");
+
+        let columns = {
+            name: {
+                title: WebInspector.UIString("Name"),
+                width: "150px",
+            },
+            timestamp: {
+                title: WebInspector.UIString("Time"),
+                width: "80px",
+                sortable: true,
+                aligned: "right",
+            },
+            size: {
+                title: WebInspector.UIString("Size"),
+                width: "80px",
+                sortable: true,
+                aligned: "right",
+            },
+        };
+
+        let snapshotTooltip = WebInspector.UIString("Take snapshot");
+        this._takeHeapSnapshotButtonItem = new WebInspector.ButtonNavigationItem("take-snapshot", snapshotTooltip, "Images/HeapSnapshot.svg", 16, 16);
+        this._takeHeapSnapshotButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._takeHeapSnapshotClicked, this);
+
+        let defaultToolTip = WebInspector.UIString("Compare snapshots");
+        let activatedToolTip = WebInspector.UIString("Cancel comparison");
+        this._compareHeapSnapshotsButtonItem = new WebInspector.ActivateButtonNavigationItem("compare-heap-snapshots", defaultToolTip, activatedToolTip, "Images/Compare.svg", 16, 16);
+        this._compareHeapSnapshotsButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._compareHeapSnapshotsClicked, this);
+        this._compareHeapSnapshotsButtonItem.enabled = false;
+        this._compareHeapSnapshotsButtonItem.activated = false;
+
+        this._compareHeapSnapshotHelpTextItem = new WebInspector.TextNavigationItem("compare-heap-snapshot-help-text", "");
+
+        this._selectingComparisonHeapSnapshots = false;
+        this._baselineDataGridNode = null;
+        this._baselineHeapSnapshotTimelineRecord = null;
+        this._heapSnapshotDiff = null;
+
+        this._showingSnapshotList = true;
+
+        this._snapshotListPathComponent = new WebInspector.HierarchicalPathComponent(WebInspector.UIString("Snapshot List"), "snapshot-list-icon", "snapshot-list", false, false);
+        this._snapshotListPathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.Clicked, this._snapshotListPathComponentClicked, this);
+
+        this._dataGrid = new WebInspector.TimelineDataGrid(this.navigationSidebarTreeOutline, columns);
+        this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("heap-allocations-timeline-view-sort", "timestamp");
+        this._dataGrid.sortOrderSetting = new WebInspector.Setting("heap-allocations-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
+        this.addSubview(this._dataGrid);
+
+        this._contentViewContainer = new WebInspector.ContentViewContainer;
+        this._contentViewContainer.addEventListener(WebInspector.ContentViewContainer.Event.CurrentContentViewDidChange, this._currentContentViewDidChange, this);
+        WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
+
+        this._pendingRecords = [];
+
+        timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._heapAllocationsTimelineRecordAdded, this);
+    }
+
+    // Public
+
+    showHeapSnapshotList()
+    {
+        if (this._showingSnapshotList)
+            return;
+
+        this._showingSnapshotList = true;
+        this._heapSnapshotDiff = null;
+        this._cancelSelectComparisonHeapSnapshots();
+
+        this.removeSubview(this._contentViewContainer);
+        this.addSubview(this._dataGrid);
+
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
+    }
+
+    showHeapSnapshotTimelineRecord(heapSnapshotTimelineRecord)
+    {
+        if (this._showingSnapshotList) {
+            this.removeSubview(this._dataGrid);
+            this.addSubview(this._contentViewContainer);
+        }
+
+        this._showingSnapshotList = false;
+        this._heapSnapshotDiff = null;
+        this._cancelSelectComparisonHeapSnapshots();
+
+        let treeElement = this.navigationSidebarTreeOutline.findTreeElement(heapSnapshotTimelineRecord);
+        console.assert(treeElement, "Should have a TreeElement for the HeapSnapshot");
+        if (treeElement)
+            treeElement.select();
+
+        let shouldManuallyTriggerContentViewUpdate = this._contentViewContainer.currentContentView && this._contentViewContainer.currentContentView.representedObject === heapSnapshotTimelineRecord.heapSnapshot;
+
+        this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotTimelineRecord.heapSnapshot);
+
+        if (shouldManuallyTriggerContentViewUpdate)
+            this._currentContentViewDidChange();
+    }
+
+    showHeapSnapshotDiff(heapSnapshotDiff)
+    {
+        if (this._showingSnapshotList) {
+            this.removeSubview(this._dataGrid);
+            this.addSubview(this._contentViewContainer);
+        }
+
+        this._showingSnapshotList = false;
+        this._heapSnapshotDiff = heapSnapshotDiff;
+
+        this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotDiff.snapshotForDiff());
+    }
+
+    // Protected
+
+    get navigationItems()
+    {
+        if (this._showingSnapshotList) {
+            let items = [this._takeHeapSnapshotButtonItem, this._compareHeapSnapshotsButtonItem];
+            if (this._selectingComparisonHeapSnapshots)
+                items.push(this._compareHeapSnapshotHelpTextItem);
+            return items;
+        }
+
+        return this._contentViewContainer.currentContentView.navigationItems;
+    }
+
+    get selectionPathComponents()
+    {
+        let components = [this._snapshotListPathComponent];
+
+        if (this._showingSnapshotList)
+            return components;
+
+        if (this._heapSnapshotDiff) {
+            let firstSnapshotIdentifier = this._heapSnapshotDiff.snapshot1.identifier;
+            let secondSnapshotIdentifier = this._heapSnapshotDiff.snapshot2.identifier;
+            let diffComponent = new WebInspector.HierarchicalPathComponent(WebInspector.UIString("Snapshot Comparison (%d and %d)").format(firstSnapshotIdentifier, secondSnapshotIdentifier), "snapshot-diff-icon", "snapshot-diff");
+            components.push(diffComponent);
+        } else {
+            if (this.navigationSidebarTreeOutline.selectedTreeElement) {
+                let heapSnapshotPathComponent = new WebInspector.GeneralTreeElementPathComponent(this.navigationSidebarTreeOutline.selectedTreeElement);
+                heapSnapshotPathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._snapshotPathComponentSelected, this);
+                components.push(heapSnapshotPathComponent);
+            }
+        }
+
+        return components.concat(this._contentViewContainer.currentContentView.selectionPathComponents);
+    }
+
+    get navigationSidebarTreeOutlineLabel()
+    {
+        // FIXME: Nothing. The sidebar will soon be removed.
+        return WebInspector.UIString("Snapshots");
+    }
+
+    userSelectedRecordFromOverview(timelineRecord)
+    {
+        this.showHeapSnapshotTimelineRecord(timelineRecord);
+    }
+
+    closed()
+    {
+        console.assert(this.representedObject instanceof WebInspector.Timeline);
+        this.representedObject.removeEventListener(null, null, this);
+    }
+
+    layout()
+    {
+        // Wait to show records until our zeroTime has been set.
+        if (this._pendingRecords.length && this.zeroTime) {
+            for (let heapAllocationsTimelineRecord of this._pendingRecords) {
+                let treeElement = new WebInspector.TimelineRecordTreeElement(heapAllocationsTimelineRecord);
+                let dataGridNode = new WebInspector.HeapAllocationsTimelineDataGridNode(heapAllocationsTimelineRecord, this.zeroTime, this);
+                this._dataGrid.addRowInSortOrder(treeElement, dataGridNode);
+            }
+
+            this._pendingRecords = [];
+            this._updateCompareHeapSnapshotButton();
+        }
+    }
+
+    reset()
+    {
+        super.reset();
+
+        this.showHeapSnapshotList();
+        this._pendingRecords = [];
+        this._updateCompareHeapSnapshotButton();
+    }
+
+    // Private
+
+    _heapAllocationsTimelineRecordAdded(event)
+    {
+        this._pendingRecords.push(event.data.record);
+
+        this.needsLayout();
+    }
+
+    _snapshotListPathComponentClicked(event)
+    {
+        this.showHeapSnapshotList();
+    }
+
+    _snapshotPathComponentSelected(event)
+    {
+        console.assert(event.data.pathComponent.representedObject instanceof WebInspector.HeapAllocationsTimelineRecord);
+        this.showHeapSnapshotTimelineRecord(event.data.pathComponent.representedObject);
+    }
+
+    _currentContentViewDidChange(event)
+    {
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
+    }
+
+    _contentViewSelectionPathComponentDidChange(event)
+    {
+        if (event.target !== this._contentViewContainer.currentContentView)
+            return;
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+    }
+
+    _updateCompareHeapSnapshotButton()
+    {
+        let hasAtLeastTwoSnapshots = false;
+
+        let count = 0;
+        for (let node of this._dataGrid.children) {
+            if (node.revealed && !node.hidden) {
+                count++;
+                if (count === 2) {
+                    hasAtLeastTwoSnapshots = true;
+                    break;
+                }
+            }
+        }
+
+        this._compareHeapSnapshotsButtonItem.enabled = hasAtLeastTwoSnapshots;
+    }
+
+    _takeHeapSnapshotClicked()
+    {
+        HeapAgent.snapshot(function(error, timestamp, snapshotStringData) {
+            let payload = JSON.parse(snapshotStringData);
+            let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
+            WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
+        });
+    }
+
+    _cancelSelectComparisonHeapSnapshots()
+    {
+        if (!this._selectingComparisonHeapSnapshots)
+            return;
+
+        if (this._baselineDataGridNode)
+            this._baselineDataGridNode.clearBaseline();
+
+        this._selectingComparisonHeapSnapshots = false;
+        this._baselineDataGridNode = null;
+        this._baselineHeapSnapshotTimelineRecord = null;
+
+        this._compareHeapSnapshotsButtonItem.activated = false;
+
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
+    }
+
+    _compareHeapSnapshotsClicked(event)
+    {
+        let newActivated = !this._compareHeapSnapshotsButtonItem.activated;
+        this._compareHeapSnapshotsButtonItem.activated = newActivated;
+
+        if (!newActivated) {
+            this._cancelSelectComparisonHeapSnapshots();
+            return;
+        }
+
+        if (this.navigationSidebarTreeOutline.selectedTreeElement)
+            this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
+
+        this._selectingComparisonHeapSnapshots = true;
+        this._baselineHeapSnapshotTimelineRecord = null;
+        this._compareHeapSnapshotHelpTextItem.text = WebInspector.UIString("Select baseline snapshot");
+
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
+    }
+
+    _dataGridNodeSelected(event)
+    {
+        if (!this._selectingComparisonHeapSnapshots)
+            return;
+
+        let dataGridNode = this._dataGrid.selectedNode;
+        if (!dataGridNode)
+            return;
+
+        let heapAllocationsTimelineRecord = dataGridNode.record;
+        if (this._baselineHeapSnapshotTimelineRecord === heapAllocationsTimelineRecord) {
+            this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
+            return;
+        }
+
+        // Selected Baseline.
+        if (!this._baselineHeapSnapshotTimelineRecord) {
+            this._baselineDataGridNode = dataGridNode;
+            this._baselineDataGridNode.markAsBaseline();
+            this._baselineHeapSnapshotTimelineRecord = heapAllocationsTimelineRecord;
+            this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
+            this._compareHeapSnapshotHelpTextItem.text = WebInspector.UIString("Select comparison snapshot");
+            this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
+            return;
+        }
+
+        // Selected Comparison.
+        let diff = new WebInspector.HeapSnapshotDiff(this._baselineHeapSnapshotTimelineRecord.heapSnapshot, heapAllocationsTimelineRecord.heapSnapshot);
+        this.showHeapSnapshotDiff(diff);
+
+        this._baselineDataGridNode.clearBaseline();
+        this._selectingComparisonHeapSnapshots = false;
+        this._compareHeapSnapshotsButtonItem.activated = false;
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js
new file mode 100644 (file)
index 0000000..0b27de4
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+* 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.HeapSnapshotClassDataGridNode = class HeapSnapshotClassDataGridNode extends WebInspector.DataGridNode
+{
+    constructor(data, tree)
+    {
+        super(data, true);
+
+        this._data = data;
+        this._tree = tree;
+
+        this._batched = false;
+        this._instances = null;
+
+        this.addEventListener("populate", this._populate, this);
+    }
+
+    // Protected
+
+    get data() { return this._data; }
+
+    createCellContent(columnIdentifier)
+    {
+        if (columnIdentifier === "size") {
+            let {size, percent} = this._data;
+            let fragment = document.createDocumentFragment();
+            let timeElement = fragment.appendChild(document.createElement("span"));
+            timeElement.classList.add("size");
+            timeElement.textContent = Number.bytesToString(size);
+            let percentElement = fragment.appendChild(document.createElement("span"));
+            percentElement.classList.add("percentage");
+            percentElement.textContent = Number.percentageString(percent);
+            return fragment;
+        }
+
+        if (columnIdentifier === "className") {
+            let {className, allInternal} = this._data;
+            let fragment = document.createDocumentFragment();
+            let iconElement = fragment.appendChild(document.createElement("img"));
+            iconElement.classList.add("icon", WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, allInternal));
+            let nameElement = fragment.appendChild(document.createElement("span"));
+            nameElement.classList.add("class-name");
+            nameElement.textContent = className;
+            return fragment;
+        }
+
+        return super.createCellContent(columnIdentifier);
+    }
+
+    sort()
+    {
+        if (this._batched) {
+            this._removeFetchMoreDataGridNode();
+            this._updateBatchedSort();
+            this._updateBatchedChildren();
+            this._appendFetchMoreDataGridNode();
+            return;
+        }
+
+        let children = this.children;
+        children.sort(this._tree._sortComparator);
+
+        for (let i = 0; i < children.length; ++i) {
+            children[i]._recalculateSiblings(i);
+            children[i].sort();
+        }
+    }
+
+    // Private
+
+    _populate()
+    {
+        this.removeEventListener("populate", this._populate, this);
+
+        let instances = this._tree.heapSnapshot.instancesWithClassName(this._data.className);
+
+        // Batch.
+        if (instances.length > WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit) {
+            // FIXME: This should respect the this._tree.includeInternalObjects setting.
+            this._instances = instances;
+            this._batched = true;
+            this._updateBatchedSort();
+            this._fetchBatch(WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit);
+            return;
+        }
+
+        for (let instance of instances) {
+            if (instance.internal && !this._tree.includeInternalObjects)
+                continue;
+            this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(instance, this._tree));
+        }
+
+        this.sort();
+    }
+
+    _fetchBatch(batchSize)
+    {
+        if (this._batched && this.children.length)
+            this._removeFetchMoreDataGridNode();
+
+        let oldCount = this.children.length;
+        let newCount = oldCount + batchSize;
+
+        if (newCount >= this._instances.length) {
+            newCount = this._instances.length - 1;
+            this._batched = false;
+        }
+
+        let count = newCount - oldCount;
+        for (let i = 0; i <= count; ++i) {
+            let instance = this._instances[oldCount + i];
+            this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(instance, this._tree));
+        }
+
+        if (this._batched)
+            this._appendFetchMoreDataGridNode();
+    }
+
+    _updateBatchedSort()
+    {
+        this._instances.sort((a, b) => {
+            let fakeDataGridNodeA = {data: a};
+            let fakeDataGridNodeB = {data: b};
+            return this._tree._sortComparator(fakeDataGridNodeA, fakeDataGridNodeB);
+        });
+    }
+
+    _updateBatchedChildren()
+    {
+        let count = this.children.length;
+
+        this.removeChildren();
+
+        for (let i = 0; i < count; ++i)
+            this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(this._instances[i], this._tree));
+    }
+
+    _removeFetchMoreDataGridNode()
+    {
+        console.assert(this.children[this.children.length - 1] instanceof WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode);
+
+        this.removeChild(this.children[this.children.length - 1]);
+    }
+
+    _appendFetchMoreDataGridNode()
+    {
+        console.assert(!(this.children[this.children.length - 1] instanceof WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode));
+
+        let count = this.children.length;
+        let totalCount = this._instances.length;
+        let remainingCount = totalCount - count;
+        let batchSize = remainingCount >= WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit ? WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit : 0;
+
+        this.appendChild(new WebInspector.HeapSnapshotInstanceFetchMoreDataGridNode(this._tree, batchSize, remainingCount, this._fetchBatch.bind(this)));
+    }
+};
+
+WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit = 100;
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js
new file mode 100644 (file)
index 0000000..57c3071
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * 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.HeapSnapshotClusterContentView = class HeapSnapshotClusterContentView extends WebInspector.ClusterContentView
+{
+    constructor(heapSnapshot)
+    {
+        super(heapSnapshot);
+
+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
+
+        this._heapSnapshot = heapSnapshot;
+
+        function createPathComponent(displayName, className, identifier)
+        {
+            let pathComponent = new WebInspector.HierarchicalPathComponent(displayName, className, identifier, false, true);
+            pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
+            return pathComponent;
+        }
+
+        this._shownInitialContent = false;
+        this._summaryContentView = null;
+        this._instancesContentView = null;
+
+        if (!WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting)
+            WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting = new WebInspector.Setting("heap-snapshot-cluster-content-view-show-internal-objects", false);
+
+        // FIXME: Need an image for showing / hiding internal objects.
+        this._showInternalObjectsButtonNavigationItem = new WebInspector.ActivateButtonNavigationItem("show-internal-objects", WebInspector.UIString("Show internal objects"), WebInspector.UIString("Hide internal objects"), "Images/ShadowDOM.svg", 13, 13);
+        this._showInternalObjectsButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleShowInternalObjectsSetting, this);
+        this._updateShowInternalObjectsButtonNavigationItem();
+
+        this._summaryPathComponent = createPathComponent.call(this, WebInspector.UIString("Summary"), "heap-snapshot-summary-icon", WebInspector.HeapSnapshotClusterContentView.SummaryIdentifier);
+        this._instancesPathComponent = createPathComponent.call(this, WebInspector.UIString("Instances"), "heap-snapshot-instances-icon", WebInspector.HeapSnapshotClusterContentView.InstancesIdentifier);
+
+        this._summaryPathComponent.nextSibling = this._instancesPathComponent;
+        this._instancesPathComponent.previousSibling = this._summaryPathComponent;
+
+        this._currentContentViewSetting = new WebInspector.Setting("heap-snapshot-cluster-current-view", WebInspector.HeapSnapshotClusterContentView.SummaryIdentifier);
+    }
+
+    // Static
+
+    static iconStyleClassNameForClassName(className, internal)
+    {
+        if (internal)
+            return "unknown";
+
+        switch (className) {
+        case "Object":
+        case "Array":
+        case "Map":
+        case "Set":
+        case "WeakMap":
+        case "WeakSet":
+        case "Promise":
+        case "Error":
+        case "Window":
+        case "Map Iterator":
+        case "Set Iterator":
+        case "Math":
+        case "JSON":
+        case "GlobalObject":
+            return "object";
+        case "Function":
+            return "function";
+        case "RegExp":
+            return "regex";
+        case "Number":
+            return "number";
+        case "Boolean":
+            return "boolean";
+        case "String":
+        case "string":
+            return "string";
+        case "Symbol":
+        case "symbol":
+            return "symbol";
+        }
+
+        if (className.endsWith("Prototype"))
+            return "object";
+        if (className.endsWith("Element") || className === "Node" || className === "Text")
+            return "node";
+
+        return "native";
+    }
+
+    // Public
+
+    get heapSnapshot() { return this._heapSnapshot; }
+
+    get summaryContentView()
+    {
+        if (!this._summaryContentView)
+            this._summaryContentView = new WebInspector.HeapSnapshotSummaryContentView(this._heapSnapshot, this._contentViewExtraArguments());
+        return this._summaryContentView;
+    }
+
+    get instancesContentView()
+    {
+        if (!this._instancesContentView)
+            this._instancesContentView = new WebInspector.HeapSnapshotInstancesContentView(this._heapSnapshot, this._contentViewExtraArguments());
+        return this._instancesContentView;
+    }
+
+    get navigationItems()
+    {
+        return [this._showInternalObjectsButtonNavigationItem];
+    }
+
+    get selectionPathComponents()
+    {
+        let currentContentView = this._contentViewContainer.currentContentView;
+        if (!currentContentView)
+            return [];
+
+        let components = [this._pathComponentForContentView(currentContentView)];
+        return components.concat(currentContentView.selectionPathComponents);
+    }
+
+    shown()
+    {
+        super.shown();
+
+        if (this._shownInitialContent) {
+            this._updateViewsForShowInternalObjectsSettingValue();
+            return;
+        }
+
+        this._showContentViewForIdentifier(this._currentContentViewSetting.value);
+    }
+
+    closed()
+    {
+        super.closed();
+
+        this._shownInitialContent = false;
+    }
+
+    saveToCookie(cookie)
+    {
+        cookie[WebInspector.HeapSnapshotClusterContentView.ContentViewIdentifierCookieKey] = this._currentContentViewSetting.value;
+    }
+
+    restoreFromCookie(cookie)
+    {
+        this._showContentViewForIdentifier(cookie[WebInspector.HeapSnapshotClusterContentView.ContentViewIdentifierCookieKey]);
+    }
+
+    showSummary()
+    {
+        this._shownInitialContent = true;
+        return this._showContentViewForIdentifier(WebInspector.HeapSnapshotClusterContentView.SummaryIdentifier);
+    }
+
+    showInstances()
+    {
+        this._shownInitialContent = true;
+        return this._showContentViewForIdentifier(WebInspector.HeapSnapshotClusterContentView.InstancesIdentifier);
+    }
+
+    // Private
+
+    _contentViewExtraArguments()
+    {
+        return {showInternalObjects: WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting.value};
+    }
+
+    _pathComponentForContentView(contentView)
+    {
+        console.assert(contentView);
+        if (!contentView)
+            return null;
+        if (contentView === this._summaryContentView)
+            return this._summaryPathComponent;
+        if (contentView === this._instancesContentView)
+            return this._instancesPathComponent;
+        console.error("Unknown contentView.");
+        return null;
+    }
+
+    _identifierForContentView(contentView)
+    {
+        console.assert(contentView);
+        if (!contentView)
+            return null;
+        if (contentView === this._summaryContentView)
+            return WebInspector.HeapSnapshotClusterContentView.SummaryIdentifier;
+        if (contentView === this._instancesContentView)
+            return WebInspector.HeapSnapshotClusterContentView.InstancesIdentifier;
+        console.error("Unknown contentView.");
+        return null;
+    }
+
+    _showContentViewForIdentifier(identifier)
+    {
+        let contentViewToShow = null;
+
+        switch (identifier) {
+        case WebInspector.HeapSnapshotClusterContentView.SummaryIdentifier:
+            contentViewToShow = this.summaryContentView;
+            break;
+        case WebInspector.HeapSnapshotClusterContentView.InstancesIdentifier:
+            contentViewToShow = this.instancesContentView;
+            break;
+        }
+
+        if (!contentViewToShow)
+            contentViewToShow = this.instancesContentView;
+
+        console.assert(contentViewToShow);
+
+        this._shownInitialContent = true;
+
+        this._currentContentViewSetting.value = this._identifierForContentView(contentViewToShow);
+
+        return this.contentViewContainer.showContentView(contentViewToShow);
+    }
+
+    _pathComponentSelected(event)
+    {
+        this._showContentViewForIdentifier(event.data.pathComponent.representedObject);
+    }
+
+    _toggleShowInternalObjectsSetting(event)
+    {
+        WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting.value = !WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting.value;
+
+        this._updateViewsForShowInternalObjectsSettingValue();
+    }
+
+    _updateViewsForShowInternalObjectsSettingValue(force)
+    {
+        let value = WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting.value;
+        if (this._showInternalObjectsButtonNavigationItem.activated === value)
+            return;
+
+        if (this._instancesContentView)
+            this._instancesContentView.showInternalObjects = value;
+
+        this._updateShowInternalObjectsButtonNavigationItem();
+    }
+
+    _updateShowInternalObjectsButtonNavigationItem()
+    {
+        this._showInternalObjectsButtonNavigationItem.activated = WebInspector.HeapSnapshotClusterContentView.showInternalObjectsSetting.value;
+    }
+};
+
+WebInspector.HeapSnapshotClusterContentView.ContentViewIdentifierCookieKey = "heap-snapshot-cluster-content-view-identifier";
+
+WebInspector.HeapSnapshotClusterContentView.SummaryIdentifier = "summary";
+WebInspector.HeapSnapshotClusterContentView.InstancesIdentifier = "instances";
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js
new file mode 100644 (file)
index 0000000..57e61db
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+* 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.HeapSnapshotInstanceDataGridNode = class HeapSnapshotInstanceDataGridNode extends WebInspector.DataGridNode
+{
+    constructor(node, tree)
+    {
+        super(node, false);
+
+        console.assert(node instanceof WebInspector.HeapSnapshotNode);
+
+        this._node = node;
+        this._tree = tree;
+
+        // FIXME: Make instance grid nodes copyable.
+        this.copyable = false;
+
+        this._percent = (this._node.size / this._tree._heapSnapshot.totalSize) * 100;
+    }
+
+    // Protected
+
+    get data() { return this._node; }
+    get selectable() { return false; }
+
+    createCells()
+    {
+        super.createCells();
+
+        this.element.classList.add("instance");
+    }
+
+    createCellContent(columnIdentifier)
+    {
+        if (columnIdentifier === "size") {
+            let size = this._node.size;
+            let percent = this._percent;
+            let fragment = document.createDocumentFragment();
+            let timeElement = fragment.appendChild(document.createElement("span"));
+            timeElement.classList.add("size");
+            timeElement.textContent = Number.bytesToString(size);
+            let percentElement = fragment.appendChild(document.createElement("span"));
+            percentElement.classList.add("percentage");
+            percentElement.textContent = Number.percentageString(percent);
+            return fragment;
+        }
+
+        if (columnIdentifier === "className") {
+            let {className, id, internal} = this._node;
+            let containerElement = document.createElement("span");
+            containerElement.addEventListener("contextmenu", this._contextMenuHandler.bind(this));
+
+            let iconElement = containerElement.appendChild(document.createElement("img"));
+            iconElement.classList.add("icon", WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal));
+
+            let idElement = containerElement.appendChild(document.createElement("span"));
+            idElement.classList.add("object-id");
+            idElement.textContent = "@" + id + " ";
+
+            HeapAgent.getPreview(id, (error, string, functionDetails, objectPreviewPayload) => {
+                if (error) {
+                    let previewErrorElement = containerElement.appendChild(document.createElement("span"));
+                    previewErrorElement.classList.add("preview-error");
+                    previewErrorElement.textContent = internal ? WebInspector.UIString("Internal object") : WebInspector.UIString("No preview available");
+                    return;
+                }
+
+                if (string) {
+                    let primitiveRemoteObject = WebInspector.RemoteObject.fromPrimitiveValue(string);
+                    containerElement.appendChild(WebInspector.FormattedValue.createElementForRemoteObject(primitiveRemoteObject));
+                    return;
+                }
+
+                if (functionDetails) {
+                    let {location, name, displayName} = functionDetails;
+                    let functionNameElement = containerElement.appendChild(document.createElement("span"));
+                    functionNameElement.classList.add("function-name");
+                    functionNameElement.textContent = name || displayName || WebInspector.UIString("(anonymous function)");
+                    let sourceCode = WebInspector.debuggerManager.scriptForIdentifier(location.scriptId);
+                    if (sourceCode) {
+                        let locationElement = containerElement.appendChild(document.createElement("span"));
+                        locationElement.classList.add("location");
+                        let sourceCodeLocation = sourceCode.createSourceCodeLocation(location.lineNumber, location.columnNumber);
+                        sourceCodeLocation.populateLiveDisplayLocationString(locationElement, "textContent", WebInspector.SourceCodeLocation.ColumnStyle.Hidden, WebInspector.SourceCodeLocation.NameStyle.Short);
+
+                        const dontFloat = true;
+                        const useGoToArrowButton = true;
+                        let goToArrowButtonLink = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, dontFloat, useGoToArrowButton);
+                        containerElement.appendChild(goToArrowButtonLink);
+                    }
+                    return;
+                }
+
+                if (objectPreviewPayload) {
+                    let objectPreview = WebInspector.ObjectPreview.fromPayload(objectPreviewPayload);
+                    let previewElement = WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForObjectPreview(objectPreview);
+                    containerElement.appendChild(previewElement);
+                    return;
+                }
+            });
+
+            return containerElement;
+        }
+
+        return super.createCellContent(columnIdentifier);
+    }
+
+    sort()
+    {
+        // No children to sort.
+    }
+
+    // Private
+
+    _contextMenuHandler(event)
+    {
+        let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
+
+        contextMenu.appendSeparator();
+
+        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);
+            });
+        });
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceFetchMoreDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceFetchMoreDataGridNode.js
new file mode 100644 (file)
index 0000000..2f7cf46
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+* 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.HeapSnapshotInstanceFetchMoreDataGridNode = class HeapSnapshotInstanceFetchMoreDataGridNode extends WebInspector.DataGridNode
+{
+    constructor(tree, batchCount, remainingCount, fetchCallback)
+    {
+        super({}, false);
+
+        console.assert(typeof batchCount === "number");
+        console.assert(typeof remainingCount === "number");
+        console.assert(typeof fetchCallback === "function");
+
+        this._tree = tree;
+        this._batchCount = batchCount;
+        this._remainingCount = remainingCount;
+        this._fetchCallback = fetchCallback;
+    }
+
+    // Protected
+
+    createCellContent(columnIdentifier)
+    {
+        if (columnIdentifier !== "className")
+            return "";
+
+        let fragment = document.createDocumentFragment();
+
+        if (this._batchCount) {
+            let buttonElement = fragment.appendChild(document.createElement("span"));
+            buttonElement.classList.add("more");
+            buttonElement.textContent = WebInspector.UIString("Show %d More").format(this._batchCount);
+            buttonElement.addEventListener("click", (event) => { this._fetchCallback(this._batchCount); });
+        }
+
+        let buttonElement = fragment.appendChild(document.createElement("span"));
+        buttonElement.classList.add("more");
+        buttonElement.textContent = WebInspector.UIString("Show Remaining (%d)").format(this._remainingCount);
+        buttonElement.addEventListener("click", (event) => { this._fetchCallback(this._remainingCount); });
+
+        return fragment;
+    }
+
+    sort()
+    {
+        // No children to sort.
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.css b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.css
new file mode 100644 (file)
index 0000000..2e3cf04
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+.heap-snapshot > .data-grid {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.heap-snapshot > .data-grid td .percentage {
+    width: 45px;
+    margin-left: 4px;
+    display: inline-block;
+}
+
+.heap-snapshot > .data-grid tr:not(.selected) td .object-id {
+    color: gray;
+}
+
+.heap-snapshot > .data-grid tr:not(.selected) td .preview-error {
+    color: red;
+}
+
+.heap-snapshot > .data-grid td .location {
+    margin-left: 1ex;
+    color: hsl(0, 0%, 60%);
+}
+
+.heap-snapshot > .data-grid:matches(:focus, .force-focus) tr.selected td .location {
+    color: hsl(0, 0%, 85%);
+}
+
+.heap-snapshot > .data-grid tr:matches(.selected, :hover) td .go-to-arrow {
+    float: none;
+    display: inline-block;
+    vertical-align: top;
+    position: relative;
+    width: 16px;
+    height: 16px;
+    margin-top: 1px;
+    margin-left: 2px;
+}
+
+.heap-snapshot > .data-grid td .more {
+    text-decoration: underline;
+    margin-right: 1ex;
+}
+
+.heap-snapshot .icon {
+    position: relative;
+    width: 16px;
+    height: 16px;
+    margin-top: 1px;
+    margin-right: 3px;
+    content: url(../Images/TypeUndefined.svg);
+}
+
+.heap-snapshot .icon.native {
+    content: url(../Images/Native.svg);
+}
+
+.heap-snapshot .icon.boolean {
+    content: url(../Images/TypeBoolean.svg);
+}
+
+.heap-snapshot .icon.function {
+    content: url(../Images/Function.svg);
+}
+
+.heap-snapshot .icon.number {
+    content: url(../Images/TypeNumber.svg);
+}
+
+.heap-snapshot .icon.object {
+    content: url(../Images/TypeObject.svg);
+}
+
+.heap-snapshot .icon.node {
+    content: url(../Images/DOMElement.svg);
+}
+
+.heap-snapshot .icon.regex {
+    content: url(../Images/TypeRegex.svg);
+}
+
+.heap-snapshot .icon.string {
+    content: url(../Images/TypeString.svg);
+}
+
+.heap-snapshot .icon.symbol {
+    content: url(../Images/TypeSymbol.svg);
+}
+
+.heap-snapshot > .data-grid td .formatted-node-preview,
+.heap-snapshot > .data-grid td .object-preview {
+    text-indent: 0; /* Do not inherit the DataGrid's indent. */
+    display: inline-block;
+    height: 16px;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js
new file mode 100644 (file)
index 0000000..454d293
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.HeapSnapshotInstancesContentView = class HeapSnapshotInstancesContentView extends WebInspector.ContentView
+{
+    constructor(representedObject, extraArguments)
+    {
+        console.assert(representedObject instanceof WebInspector.HeapSnapshot);
+
+        super(representedObject);
+
+        this.element.classList.add("heap-snapshot");
+
+        let {showInternalObjects} = extraArguments;
+
+        let columns = {
+            size: {
+                title: WebInspector.UIString("Total Size"),
+                width: "140px",
+                aligned: "right",
+                sortable: true,
+            },
+            count: {
+                title: WebInspector.UIString("Count"),
+                width: "80px",
+                aligned: "right",
+                sortable: true,
+            },
+            className: {
+                title: WebInspector.UIString("Class Name"),
+                sortable: true,
+                disclosure: true,
+            }
+        };
+
+        this._dataGrid = new WebInspector.DataGrid(columns);
+        this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("heap-snapshot-content-view-sort", "size");
+        this._dataGrid.sortOrderSetting = new WebInspector.Setting("heap-snapshot-content-view-sort-order", WebInspector.DataGrid.SortOrder.Descending);
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, this._sortDataGrid, this);
+
+        let sortComparator = WebInspector.HeapSnapshotInstancesDataGridTree.buildSortComparator(this._dataGrid.sortColumnIdentifier, this._dataGrid.sortOrder);
+        this._heapSnapshotDataGridTree = new WebInspector.HeapSnapshotInstancesDataGridTree(this.representedObject, sortComparator, showInternalObjects);
+
+        for (let child of this._heapSnapshotDataGridTree.children)
+            this._dataGrid.appendChild(child);
+
+        this.addSubview(this._dataGrid);
+        this._dataGrid.updateLayout();
+    }
+
+    // Public
+
+    get showInternalObjects()
+    {
+        return this._heapSnapshotDataGridTree.includeInternalObjects;
+    }
+
+    set showInternalObjects(showInternalObjects)
+    {
+        if (this.showInternalObjects === showInternalObjects)
+            return;
+
+        this._heapSnapshotDataGridTree.includeInternalObjects = showInternalObjects;
+
+        this._sortDataGrid();
+    }
+
+    // Private
+
+    _sortDataGrid()
+    {
+        if (!this._heapSnapshotDataGridTree)
+            return;
+
+        this._heapSnapshotDataGridTree.sortComparator = WebInspector.HeapSnapshotInstancesDataGridTree.buildSortComparator(this._dataGrid.sortColumnIdentifier, this._dataGrid.sortOrder);
+
+        this._dataGrid.removeChildren();
+        for (let child of this._heapSnapshotDataGridTree.children)
+            this._dataGrid.appendChild(child);
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js
new file mode 100644 (file)
index 0000000..2eb8f92
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+* 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.HeapSnapshotInstancesDataGridTree = class HeapSnapshotInstancesDataGridTree extends WebInspector.Object
+{
+    constructor(heapSnapshot, sortComparator, includeInternalObjects)
+    {
+        super();
+
+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
+
+        this._heapSnapshot = heapSnapshot;
+
+        this._children = [];
+        this._sortComparator = sortComparator;
+        this._includeInternalObjects = includeInternalObjects;
+
+        this._populateTopLevel();
+        this.sort();
+    }
+
+    // Static
+
+    static buildSortComparator(columnIdentifier, sortOrder)
+    {
+        let multiplier = sortOrder === WebInspector.DataGrid.SortOrder.Ascending ? 1 : -1;
+        let numberCompare = (columnIdentifier, a, b) => multiplier * (a.data[columnIdentifier] - b.data[columnIdentifier]);
+        let localeCompare = (columnIdentifier, a, b) => multiplier * (a.data[columnIdentifier].localeCompare(b.data[columnIdentifier]));
+
+        let comparator;
+        switch (columnIdentifier) {
+        case "size":
+            return numberCompare.bind(this, "size");
+        case "count":
+            return numberCompare.bind(this, "count");
+        case "className":
+            return localeCompare.bind(this, "className");
+        }
+    }
+
+    // Public
+
+    get heapSnapshot() { return this._heapSnapshot; }
+
+    get includeInternalObjects()
+    {
+        return this._includeInternalObjects;
+    }
+
+    set includeInternalObjects(includeInternal)
+    {
+        if (this._includeInternalObjects === includeInternal)
+            return;
+
+        this._includeInternalObjects = includeInternal;
+
+        this._populateTopLevel();
+        this.sort();
+    }
+
+    get children()
+    {
+        return this._children;
+    }
+
+    appendChild(node)
+    {
+        this._children.push(node);
+    }
+
+    insertChild(node, index)
+    {
+        this._children.splice(index, 0, node);
+    }
+
+    removeChildren()
+    {
+        this._children = [];
+    }
+
+    set sortComparator(comparator)
+    {
+        this._sortComparator = comparator;
+        this.sort();
+    }
+
+    sort()
+    {
+        let children = this._children;
+        children.sort(this._sortComparator);
+
+        for (let i = 0; i < children.length; ++i) {
+            children[i]._recalculateSiblings(i);
+            children[i].sort();
+        }
+    }
+
+    // Private
+
+    _populateTopLevel()
+    {
+        this.removeChildren();
+
+        // Populate the first level with the different classes.
+        let totalSize = this._heapSnapshot.totalSize;
+        for (let [className, {size, count, internalCount}] of this._heapSnapshot.categories) {
+            let allInternal = count === internalCount;
+            if (!this._includeInternalObjects && allInternal)
+                continue;
+            let percent = (size / totalSize) * 100;
+            this.appendChild(new WebInspector.HeapSnapshotClassDataGridNode({className, size, count, percent, allInternal}, this));
+        }
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.css b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.css
new file mode 100644 (file)
index 0000000..e0f4373
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+.content-view-container > .content-view.heap-snapshot-summary {
+    overflow: scroll;
+}
+
+.heap-snapshot-summary > .content {
+    width: 470px;
+    margin: 10px auto;
+}
+
+.heap-snapshot-summary > .content > .overview {
+    padding: 10px;
+    margin-bottom: 10px;
+    border-bottom: 1px solid var(--border-color);
+}
+
+.heap-snapshot-summary > .content .subtitle {
+    font-family: '-webkit-system-font';
+    font-size: 14px;
+}
+
+.heap-snapshot-summary > .content > .overview {
+    display: flex;
+    justify-content: center;
+}
+
+.heap-snapshot-summary > .content > .overview > .chart {
+    width: 420px;
+    text-align: center;
+}
+
+.heap-snapshot-summary > .content > .overview > .chart > .subtitle {
+    margin-bottom: 1em;
+}
+
+.heap-snapshot-summary > .content > .overview > .chart > .container {
+    display: flex;
+    justify-content: center;
+}
+
+.heap-snapshot-summary > .content > .overview .total-size,
+.heap-snapshot-summary > .content > .overview .total-count,
+.heap-snapshot-summary > .content > .overview .average-allocation-size {
+    margin: auto;
+    color: hsl(0, 0%, 70%);
+}
+
+.heap-snapshot-summary .legend {
+    width: 120px;
+    padding-left: 20px;
+    text-align: left;
+}
+
+.heap-snapshot-summary .legend > .row {
+    position: relative;
+    height: 27px;
+}
+
+.heap-snapshot-summary .legend > .row > .swatch {
+    position: absolute;
+    top: 1px;
+    left: 0;
+    width: 1em;
+    height: 1em;
+}
+
+.heap-snapshot-summary .legend > .row > p {
+    margin: 0;
+}
+
+.heap-snapshot-summary .legend > .row > .label {
+    position: absolute;
+    top: 0;
+    left: 20px;
+}
+
+.heap-snapshot-summary .legend > .row > .size {
+    position: absolute;
+    top: 12px;
+    left: 20px;
+    color: hsl(0, 0%, 70%);
+}
+
+.heap-snapshot-summary .legend > .row > .swatch.top-class-1,
+.heap-snapshot-summary .legend > .row > .swatch.small {
+    border: 1px solid var(--memory-javascript-stroke-color);
+    background: var(--memory-javascript-fill-color);
+}
+
+.heap-snapshot-summary .legend > .row > .swatch.top-class-2,
+.heap-snapshot-summary .legend > .row > .swatch.medium {
+    border: 1px solid var(--memory-images-stroke-color);
+    background: var(--memory-images-fill-color);
+}
+
+.heap-snapshot-summary .legend > .row > .swatch.top-class-3,
+.heap-snapshot-summary .legend > .row > .swatch.large {
+    border: 1px solid var(--memory-layers-stroke-color);
+    background: var(--memory-layers-fill-color);
+}
+
+.heap-snapshot-summary .legend > .row > .swatch.top-class-4,
+.heap-snapshot-summary .legend > .row > .swatch.very-large {
+    border: 1px solid var(--memory-page-stroke-color);
+    background: var(--memory-page-fill-color);
+}
+
+.heap-snapshot-summary .legend > .row > .swatch.top-class-5 {
+    border: 1px solid var(--memory-extra-stroke-color);
+    background: var(--memory-extra-fill-color);
+}
+
+.heap-snapshot-summary .legend > .row > .swatch.other {
+    border: 1px solid var(--memory-max-comparison-stroke-color);
+    background: var(--memory-max-comparison-fill-color);
+}
+
+.heap-snapshot-summary .circle-chart > svg > path.top-class-1,
+.heap-snapshot-summary .circle-chart > svg > path.small {
+    stroke: var(--memory-javascript-stroke-color);
+    fill: var(--memory-javascript-fill-color);
+}
+
+.heap-snapshot-summary .circle-chart > svg > path.top-class-2,
+.heap-snapshot-summary .circle-chart > svg > path.medium {
+    stroke: var(--memory-images-stroke-color);
+    fill: var(--memory-images-fill-color);
+}
+
+.heap-snapshot-summary .circle-chart > svg > path.top-class-3,
+.heap-snapshot-summary .circle-chart > svg > path.large {
+    stroke: var(--memory-layers-stroke-color);
+    fill: var(--memory-layers-fill-color);
+}
+
+.heap-snapshot-summary .circle-chart > svg > path.top-class-4,
+.heap-snapshot-summary .circle-chart > svg > path.very-large {
+    stroke: var(--memory-page-stroke-color);
+    fill: var(--memory-page-fill-color);
+}
+
+.heap-snapshot-summary .circle-chart > svg > path.top-class-5 {
+    stroke: var(--memory-extra-stroke-color);
+    fill: var(--memory-extra-fill-color);
+}
+
+.heap-snapshot-summary .circle-chart > svg > path.other {
+    stroke: var(--memory-max-comparison-stroke-color);
+    fill: var(--memory-max-comparison-fill-color);
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js
new file mode 100644 (file)
index 0000000..0ae7802
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * 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.HeapSnapshotSummaryContentView = class HeapSnapshotSummaryContentView extends WebInspector.ContentView
+{
+    constructor(heapSnapshot, extraArguments)
+    {
+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
+
+        super(heapSnapshot);
+
+        this._heapSnapshot = heapSnapshot;
+
+        // FIXME: Show/hide internal objects.
+        let {showInternalObjects} = extraArguments;
+
+        this.element.classList.add("heap-snapshot-summary");
+
+        let contentElement = this.element.appendChild(document.createElement("div"));
+        contentElement.classList.add("content");
+
+        let overviewElement = contentElement.appendChild(document.createElement("div"));
+        overviewElement.classList.add("overview");
+
+        function createChartContainer(parentElement, subtitle, tooltip)
+        {
+            let chartElement = parentElement.appendChild(document.createElement("div"));
+            chartElement.classList.add("chart");
+
+            let chartSubtitleElement = chartElement.appendChild(document.createElement("div"));
+            chartSubtitleElement.classList.add("subtitle");
+            chartSubtitleElement.textContent = subtitle;
+            chartSubtitleElement.title = tooltip;
+
+            let chartFlexContainerElement = chartElement.appendChild(document.createElement("div"));
+            chartFlexContainerElement.classList.add("container");
+            return chartFlexContainerElement;
+        }
+
+        const circleChartConfig = {size: 120, innerRadiusRatio: 0.5};
+
+        let classSizeBreakdownTooltip = WebInspector.UIString("Breakdown of total allocation size by class");
+        let classSizeBreakdownChartContainerElement = createChartContainer(overviewElement, WebInspector.UIString("Class Size Breakdown"), classSizeBreakdownTooltip);
+        this._classSizeBreakdownCircleChart = new WebInspector.CircleChart(circleChartConfig);
+        classSizeBreakdownChartContainerElement.appendChild(this._classSizeBreakdownCircleChart.element);
+        this._classSizeBreakdownLegendElement = classSizeBreakdownChartContainerElement.appendChild(document.createElement("div"));
+        this._classSizeBreakdownLegendElement.classList.add("legend", "class-breakdown");
+
+        let secondOverviewElement = contentElement.appendChild(document.createElement("div"));
+        secondOverviewElement.classList.add("overview");
+
+        let classCountBreakdownTooltip = WebInspector.UIString("Breakdown of object counts by class");
+        let classCountBreakdownChartContainerElement = createChartContainer(secondOverviewElement, WebInspector.UIString("Class Count Breakdown"), classCountBreakdownTooltip);
+        this._classCountBreakdownCircleChart = new WebInspector.CircleChart(circleChartConfig);
+        classCountBreakdownChartContainerElement.appendChild(this._classCountBreakdownCircleChart.element);
+        this._classCountBreakdownLegendElement = classCountBreakdownChartContainerElement.appendChild(document.createElement("div"));
+        this._classCountBreakdownLegendElement.classList.add("legend", "count-breakdown");
+
+        let thirdOverviewElement = contentElement.appendChild(document.createElement("div"));
+        thirdOverviewElement.classList.add("overview");
+
+        let allocationSizeBreakdownTooltip = WebInspector.UIString("Breakdown of allocations into size classes");
+        let allocationSizeBreakdownChartContainerElement = createChartContainer(thirdOverviewElement, WebInspector.UIString("Allocation Size Breakdown"), allocationSizeBreakdownTooltip);
+        this._allocationSizeBreakdownCircleChart = new WebInspector.CircleChart(circleChartConfig);
+        allocationSizeBreakdownChartContainerElement.appendChild(this._allocationSizeBreakdownCircleChart.element);
+        this._allocationSizeBreakdownLegendElement = allocationSizeBreakdownChartContainerElement.appendChild(document.createElement("div"));
+        this._allocationSizeBreakdownLegendElement.classList.add("legend", "allocation-size-breakdown");
+
+        function appendLegendRow(legendElement, swatchClass, label, subtitle, tooltip) {
+            let rowElement = legendElement.appendChild(document.createElement("div"));
+            rowElement.classList.add("row");
+            let swatchElement = rowElement.appendChild(document.createElement("div"));
+            swatchElement.classList.add("swatch", swatchClass);
+            let labelElement = rowElement.appendChild(document.createElement("p"));
+            labelElement.classList.add("label");
+            labelElement.textContent = label;
+            let sizeElement = rowElement.appendChild(document.createElement("p"));
+            sizeElement.classList.add("size");
+            sizeElement.textContent = subtitle;
+            if (tooltip)
+                rowElement.title = tooltip;
+        }
+
+        function appendEmptyMessage(legendElement, text) {
+            let emptyElement = legendElement.appendChild(document.createElement("div"));
+            emptyElement.classList.add("empty");
+            emptyElement.textContent = text;
+        }
+
+        // Largest classes by size.
+        let categoriesBySize = [...this._heapSnapshot.categories.values()].sort((a, b) => b.size - a.size);
+        let categoriesBySizeLimit = Math.min(categoriesBySize.length, 5);
+        if (categoriesBySizeLimit) {
+            let topClassSizes = categoriesBySize.splice(0, categoriesBySizeLimit);
+            let otherClassSize = categoriesBySize.reduce((sum, category) => sum += category.size, 0);
+
+            let segments = [];
+            let values = [];
+
+            for (let i = 0; i < categoriesBySizeLimit; ++i) {
+                let topClassSize = topClassSizes[i];
+                let segmentName = "top-class-" + (i + 1);
+                segments.push(segmentName);
+                values.push(topClassSize.size);
+                appendLegendRow.call(this, this._classSizeBreakdownLegendElement, segmentName, topClassSize.className, Number.bytesToString(topClassSize.size));
+            }
+
+            if (otherClassSize) {
+                let otherSegmentName = "other";
+                segments.push(otherSegmentName);
+                values.push(otherClassSize);
+                appendLegendRow.call(this, this._classSizeBreakdownLegendElement, otherSegmentName, WebInspector.UIString("Other"), Number.bytesToString(otherClassSize));
+            }
+
+            this._classSizeBreakdownCircleChart.segments = segments;
+            this._classSizeBreakdownCircleChart.values = values;
+            this._classSizeBreakdownCircleChart.updateLayout();
+
+            let totalSizeElement = this._classSizeBreakdownCircleChart.centerElement.appendChild(document.createElement("div"));
+            totalSizeElement.classList.add("total-size");
+            totalSizeElement.appendChild(document.createElement("span")); // firstChild
+            totalSizeElement.appendChild(document.createElement("br"));
+            totalSizeElement.appendChild(document.createElement("span")); // lastChild
+            let totalSize = Number.bytesToString(this._heapSnapshot.totalSize).split(/\s+/);
+            totalSizeElement.firstChild.textContent = totalSize[0];
+            totalSizeElement.lastChild.textContent = totalSize[1];
+            totalSizeElement.title = WebInspector.UIString("Total size of heap");
+        } else
+            appendEmptyMessage.call(this, this._classSizeBreakdownLegendElement, WebInspector.UIString("No objects"));
+
+        // Largest classes by count.
+        let categoriesByCount = [...this._heapSnapshot.categories.values()].sort((a, b) => b.count - a.count);
+        let categoriesByCountLimit = Math.min(categoriesByCount.length, 5);
+        if (categoriesByCountLimit) {
+            let topClassCounts = categoriesByCount.splice(0, categoriesByCountLimit);
+            let otherClassCount = categoriesByCount.reduce((sum, category) => sum += category.count, 0);
+
+            let segments = [];
+            let values = [];
+
+            for (let i = 0; i < categoriesByCountLimit; ++i) {
+                let topClassCount = topClassCounts[i];
+                let segmentName = "top-class-" + (i + 1);
+                segments.push(segmentName);
+                values.push(topClassCount.count);
+                appendLegendRow.call(this, this._classCountBreakdownLegendElement, segmentName, topClassCount.className, topClassCount.count);
+            }
+
+            if (otherClassCount) {
+                let otherSegmentName = "other";
+                segments.push(otherSegmentName);
+                values.push(otherClassCount);
+                appendLegendRow.call(this, this._classCountBreakdownLegendElement, otherSegmentName, WebInspector.UIString("Other"), otherClassCount);
+            }
+
+            this._classCountBreakdownCircleChart.segments = segments;
+            this._classCountBreakdownCircleChart.values = values;
+            this._classCountBreakdownCircleChart.updateLayout();
+
+            let totalCountElement = this._classCountBreakdownCircleChart.centerElement.appendChild(document.createElement("div"));
+            totalCountElement.classList.add("total-count");
+            totalCountElement.textContent = this._heapSnapshot.totalObjectCount;
+            totalCountElement.title = WebInspector.UIString("Total object count");
+        } else
+            appendEmptyMessage.call(this, this._classCountBreakdownLegendElement, WebInspector.UIString("No objects"));
+
+        // Allocation size groups.
+        let small = 0;
+        let medium = 0;
+        let large = 0;
+        let veryLarge = 0;
+
+        const smallAllocationSize = 48;
+        const mediumAllocationSize = 128;
+        const largeAllocationSize = 512;
+
+        this._heapSnapshot.instances.forEach(({size}) => {
+            if (size < smallAllocationSize)
+                small++;
+            else if (size < mediumAllocationSize)
+                medium++;
+            else if (size < largeAllocationSize)
+                large++;
+            else
+                veryLarge++;
+        });
+
+        if (small + medium + large + veryLarge) {
+            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, "small", WebInspector.UIString("Small %s").format(Number.bytesToString(smallAllocationSize)), small);
+            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, "medium", WebInspector.UIString("Medium %s").format(Number.bytesToString(mediumAllocationSize)), medium);
+            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, "large", WebInspector.UIString("Large %s").format(Number.bytesToString(largeAllocationSize)), large);
+            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, "very-large", WebInspector.UIString("Very Large"), veryLarge);
+
+            this._allocationSizeBreakdownCircleChart.segments = ["small", "medium", "large", "very-large"];
+            this._allocationSizeBreakdownCircleChart.values = [small, medium, large, veryLarge];
+            this._allocationSizeBreakdownCircleChart.updateLayout();
+
+            let averageAllocationSizeElement = this._allocationSizeBreakdownCircleChart.centerElement.appendChild(document.createElement("div"));
+            averageAllocationSizeElement.classList.add("average-allocation-size");
+            averageAllocationSizeElement.textContent = Number.bytesToString(this._heapSnapshot.totalSize / this._heapSnapshot.totalObjectCount);
+            averageAllocationSizeElement.title = WebInspector.UIString("Average allocation size");
+        } else
+            appendEmptyMessage.call(this, this._allocationSizeBreakdownLegendElement, WebInspector.UIString("No objects"));
+    }
+
+    // Protected
+
+    layout()
+    {
+        // Nothing to change.
+    }
+};
index 09cd741..1bae21a 100644 (file)
     content: url(../Images/Response.svg);
 }
 
+.heap-snapshot-summary-icon .icon {
+    content: url(../Images/HeapSnapshotSummary.svg);
+}
+
+.heap-snapshot-instances-icon .icon {
+    content: url(../Images/HeapSnapshotInstances.svg);
+}
+
 .dom-element-icon .icon {
     content: url(../Images/DOMElement.svg);
 }
     content: url(../Images/CallTrees.svg);
 }
 
+.snapshot-list-icon .icon {
+    content: url(../Images/Events.svg);
+}
+
+.snapshot-diff-icon .icon {
+    content: url(../Images/HeapSnapshotDiff.svg);
+}
+
 /* FIXME: <https://webkit.org/b/155077> [GTK] Web Inspector: Add new GTK+ icons for different Script Timeline Views */
+/* FIXME: <https://webkit.org/b/155282> [GTK] Web Inspector: Add new GTK+ icons for different Heap Allocations Timeline Views (Heap Snapshots) */
 body:not(.mac-platform, .windows-platform) .events-icon .icon,
-body:not(.mac-platform, .windows-platform) .call-trees-icon .icon {
+body:not(.mac-platform, .windows-platform) .call-trees-icon .icon,
+body:not(.mac-platform, .windows-platform) .heap-snapshot-record .icon,
+body:not(.mac-platform, .windows-platform) .heap-snapshot-summary-icon .icon,
+body:not(.mac-platform, .windows-platform) .heap-snapshot-instances-icon .icon,
+body:not(.mac-platform, .windows-platform) .snapshot-list-icon .icon,
+body:not(.mac-platform, .windows-platform) .snapshot-diff-icon .icon {
     content: url(../Images/ResultLine.svg);
 }
diff --git a/Source/WebInspectorUI/UserInterface/Views/TextNavigationItem.css b/Source/WebInspectorUI/UserInterface/Views/TextNavigationItem.css
new file mode 100644 (file)
index 0000000..e4a6225
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+.navigation-bar .item.text {
+    width: auto;
+    padding: 1px 8px 3px;
+    margin: 0 2px;
+
+    line-height: 11px;
+    color: hsl(0, 0%, 18%);
+    text-align: center;
+    height: auto;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/TextNavigationItem.js b/Source/WebInspectorUI/UserInterface/Views/TextNavigationItem.js
new file mode 100644 (file)
index 0000000..1ab2e55
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.TextNavigationItem = class TextNavigationItem extends WebInspector.NavigationItem
+{
+    constructor(identifier, label)
+    {
+        super(identifier);
+
+        console.assert(identifier);
+
+        this._element.classList.add("text");
+        this._element.textContent = label || "";
+    }
+
+    // Public
+
+    get text()
+    {
+        return this._element.textContent;
+    }
+
+    set text(x)
+    {
+        this._element.textContent = x || "";
+    }
+};
index c2acf79..e80ae64 100644 (file)
@@ -142,3 +142,7 @@ body:not(.mac-platform, .windows-platform) .stopwatch-icon .icon {
 .animation-record .icon {
     content: url(../Images/TimelineRecordAnimation.svg);
 }
+
+.heap-snapshot-record .icon {
+    content: url(../Images/HeapSnapshot.svg);
+}
index 29e44bc..d24ab8a 100644 (file)
@@ -25,7 +25,7 @@
 
 WebInspector.TimelineOverview = class TimelineOverview extends WebInspector.View
 {
-    constructor(timelineRecording)
+    constructor(timelineRecording, delegate)
     {
         super();
 
@@ -45,6 +45,8 @@ WebInspector.TimelineOverview = class TimelineOverview extends WebInspector.View
         this._recording.addEventListener(WebInspector.TimelineRecording.Event.MarkerAdded, this._markerAdded, this);
         this._recording.addEventListener(WebInspector.TimelineRecording.Event.Reset, this._recordingReset, this);
 
+        this._delegate = delegate;
+
         this.element.classList.add("timeline-overview");
         this.element.addEventListener("wheel", this._handleWheelEvent.bind(this));
         this.element.addEventListener("gesturestart", this._handleGestureStart.bind(this));
@@ -376,6 +378,12 @@ WebInspector.TimelineOverview = class TimelineOverview extends WebInspector.View
         overviewGraph.selectedRecord = record;
     }
 
+    userSelectedRecord(record)
+    {
+        if (this._delegate && this._delegate.timelineOverviewUserSelectedRecord)
+            this._delegate.timelineOverviewUserSelectedRecord(this, record);
+    }
+
     updateLayoutIfNeeded()
     {
         if (this.layoutPending) {
index a28b3a6..05d2b27 100644 (file)
@@ -65,6 +65,9 @@ WebInspector.TimelineOverviewGraph = class TimelineOverviewGraph extends WebInsp
         if (timelineType === WebInspector.TimelineRecord.Type.Memory)
             return new WebInspector.MemoryTimelineOverviewGraph(timeline, timelineOverview);
 
+        if (timelineType === WebInspector.TimelineRecord.Type.HeapAllocations)
+            return new WebInspector.HeapAllocationsTimelineOverviewGraph(timeline, timelineOverview);
+
         throw new Error("Can't make a graph for an unknown timeline.");
     }
 
index c4fadb0..e49f852 100644 (file)
@@ -38,7 +38,7 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
 
         this.element.classList.add("timeline-recording");
 
-        this._timelineOverview = new WebInspector.TimelineOverview(this._recording);
+        this._timelineOverview = new WebInspector.TimelineOverview(this._recording, this);
         this._timelineOverview.addEventListener(WebInspector.TimelineOverview.Event.TimeRangeSelectionChanged, this._timeRangeSelectionChanged, this);
         this._timelineOverview.addEventListener(WebInspector.TimelineOverview.Event.RecordSelected, this._recordSelected, this);
         this._timelineOverview.addEventListener(WebInspector.TimelineOverview.Event.TimelineSelected, this._timelineSelected, this);
@@ -351,6 +351,25 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
         return new WebInspector.GeneralTreeElement(iconClassName, title, representedObject, hasChildren);
     }
 
+    // TimelineOverview delegate
+
+    timelineOverviewUserSelectedRecord(timelineOverview, timelineRecord)
+    {
+        let timelineViewForRecord = null;
+        for (let timelineView of this._timelineViewMap.values()) {
+            if (timelineView.representedObject.type === timelineRecord.type) {
+                timelineViewForRecord = timelineView;
+                break;
+            }
+        }
+
+        if (!timelineViewForRecord)
+            return;
+
+        this._timelineContentBrowser.showContentView(timelineViewForRecord);
+        timelineViewForRecord.userSelectedRecordFromOverview(timelineRecord);
+    }
+
     // Private
 
     _currentContentViewDidChange(event)
@@ -364,7 +383,7 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
 
         this._timelineOverview.viewMode = newViewMode;
 
-        if (timelineView) {
+        if (timelineView instanceof WebInspector.TimelineView) {
             this._timelineSidebarPanel.contentTreeOutline = timelineView.navigationSidebarTreeOutline;
             this._timelineSidebarPanel.contentTreeOutlineLabel = timelineView.navigationSidebarTreeOutlineLabel;
 
index 951bd4b..abe6055 100644 (file)
@@ -105,8 +105,10 @@ WebInspector.TimelineRuler = class TimelineRuler extends WebInspector.View
         this._allowsTimeRangeSelection = x;
 
         if (x) {
+            this._clickEventListener = this._handleClick.bind(this);
             this._doubleClickEventListener = this._handleDoubleClick.bind(this);
             this._mouseDownEventListener = this._handleMouseDown.bind(this);
+            this.element.addEventListener("click", this._clickEventListener);
             this.element.addEventListener("dblclick", this._doubleClickEventListener);
             this.element.addEventListener("mousedown", this._mouseDownEventListener);
 
@@ -133,8 +135,10 @@ WebInspector.TimelineRuler = class TimelineRuler extends WebInspector.View
 
             this._needsSelectionLayout();
         } else {
+            this.element.removeEventListener("click", this._clickEventListener);
             this.element.removeEventListener("dblclick", this._doubleClickEventListener);
             this.element.removeEventListener("mousedown", this._mouseDownEventListener);
+            this._clickEventListener = null;
             this._doubleClickEventListener = null;
             this._mouseDownEventListener = null;
 
@@ -694,6 +698,19 @@ WebInspector.TimelineRuler = class TimelineRuler extends WebInspector.View
         this._needsMarkerLayout();
     }
 
+    _handleClick(event)
+    {
+        if (this._mouseMoved)
+            return;
+
+        this.element.style.pointerEvents = "none";
+        let newTarget = document.elementFromPoint(event.pageX, event.pageY);
+        this.element.style.pointerEvents = "all";
+
+        if (newTarget && newTarget.click)
+            newTarget.click();
+    }
+
     _handleDoubleClick(event)
     {
         if (this.entireRangeSelected)
@@ -719,6 +736,8 @@ WebInspector.TimelineRuler = class TimelineRuler extends WebInspector.View
         } else
             this._mouseDownPosition = event.pageX - this._rulerBoundingClientRect.left;
 
+        this._mouseMoved = false;
+
         this._mouseMoveEventListener = this._handleMouseMove.bind(this);
         this._mouseUpEventListener = this._handleMouseUp.bind(this);
 
@@ -734,6 +753,8 @@ WebInspector.TimelineRuler = class TimelineRuler extends WebInspector.View
     {
         console.assert(event.button === 0);
 
+        this._mouseMoved = true;
+
         let currentMousePosition;
         if (this._selectionIsMove) {
             currentMousePosition = Math.max(this._moveSelectionMaximumLeftOffset, Math.min(this._moveSelectionMaximumRightOffset, event.pageX));
index 77461ab..71a67bb 100644 (file)
@@ -122,6 +122,8 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn
             return WebInspector.UIString("Rendering Frames");
         case WebInspector.TimelineRecord.Type.Memory:
             return WebInspector.UIString("Memory");
+        case WebInspector.TimelineRecord.Type.HeapAllocations:
+            return WebInspector.UIString("JavaScript Allocations");
         default:
             console.error("Unknown Timeline type:", timeline.type);
         }
@@ -138,6 +140,9 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn
             return "layout-icon";
         case WebInspector.TimelineRecord.Type.Memory:
             return "memory-icon";
+        case WebInspector.TimelineRecord.Type.HeapAllocations:
+            // FIXME: HeapAllocation Timeline needs a new icon.
+            return "memory-icon";
         case WebInspector.TimelineRecord.Type.Script:
             return "script-icon";
         case WebInspector.TimelineRecord.Type.RenderingFrame:
@@ -158,6 +163,8 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn
             return "colors";
         case WebInspector.TimelineRecord.Type.Memory:
             return "memory";
+        case WebInspector.TimelineRecord.Type.HeapAllocations:
+            return "heap-allocations";
         case WebInspector.TimelineRecord.Type.Script:
             return "script";
         case WebInspector.TimelineRecord.Type.RenderingFrame:
@@ -224,6 +231,9 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn
         case WebInspector.TimelineRecord.Type.RenderingFrame:
             return WebInspector.TimelineRecordTreeElement.RenderingFrameRecordIconStyleClass;
 
+        case WebInspector.TimelineRecord.Type.HeapAllocations:
+            return "heap-snapshot-record";
+
         case WebInspector.TimelineRecord.Type.Memory:
             // Not used. Fall through to error just in case.
 
@@ -245,6 +255,8 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn
             return WebInspector.ScriptTimelineRecord.EventType.displayName(timelineRecord.eventType, timelineRecord.details, includeDetailsInMainTitle);
         case WebInspector.TimelineRecord.Type.RenderingFrame:
             return WebInspector.UIString("Frame %d").format(timelineRecord.frameNumber);
+        case WebInspector.TimelineRecord.Type.HeapAllocations:
+            return WebInspector.UIString("Snapshot %d").format(timelineRecord.heapSnapshot.identifier);
         case WebInspector.TimelineRecord.Type.Memory:
             // Not used. Fall through to error just in case.
         default:
index 7a7b8b4..f348839 100644 (file)
@@ -245,6 +245,11 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
         this.showContentViewForTreeElement(treeElement);
     }
 
+    userSelectedRecordFromOverview(timelineRecord)
+    {
+        // Implemented by sub-classes if needed.
+    }
+
     // Private
 
     _treeSelectionDidChange(event)
index 9f81511..e94feda 100644 (file)
@@ -67,6 +67,8 @@
     --memory-page-stroke-color: hsl(22, 40%, 50%);
     --memory-max-comparison-fill-color: hsl(220, 10%, 75%);
     --memory-max-comparison-stroke-color: hsl(220, 10%, 55%);
+    --memory-extra-fill-color: hsl(211, 53%, 70%);
+    --memory-extra-stroke-color: hsl(211, 53%, 55%);
 }
 
 body.window-inactive {