Web Inspector: support adding and removing timelines to the timeline sidebar panel...
authorburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Feb 2015 19:00:03 +0000 (19:00 +0000)
committerburg@cs.washington.edu <burg@cs.washington.edu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Feb 2015 19:00:03 +0000 (19:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=138434

Reviewed by Timothy Hatcher.

A timeline recording's timelines should not be static. This patch adds relevant machinery to
dynamically add and remove timelines from the Timeline model object and its various views.

From the model side, this is a simple change that adds TimelineAdded and TimelineRemoved events.
The timeline views and timeline sidebar require more extensive changes to support this functionality.
Instead of keeping a fixed set of timeline tree elements, the sidebar view now adds and removes
tree elements to reflect the available timelines for the displayed timeline recording.

This change also includes several minor cleanups, such as appending 'Element' to view properties that
are DOM elements, and renaming TimelineContentView to TimelineRecordingContentView.

* UserInterface/Controllers/TimelineManager.js:
(WebInspector.TimelineManager.prototype._loadNewRecording):
Populate predefined network, layout, and script timelines here after constructing the recording.

* UserInterface/Main.html:
* UserInterface/Models/Timeline.js:
Move hardcoded class names and localized strings to these base class methods. This is not implemented
using overridden methods because not all timeline types have their own subclasses. Add a dummy
implementation of saveIdentityToCookie() to avoid warnings when saving sidebar panel selection state.

(WebInspector.Timeline.prototype.get type):
(WebInspector.Timeline.prototype.get displayName): Added.
(WebInspector.Timeline.prototype.get iconClassName): Added.
(WebInspector.Timeline.prototype.addRecord):
* UserInterface/Models/TimelineRecording.js:
(WebInspector.TimelineRecording):
(WebInspector.TimelineRecording.prototype.addTimeline.set timeline): Added.
(WebInspector.TimelineRecording.prototype.addTimeline): Added.
(WebInspector.TimelineRecording.prototype.removeTimeline.get this): Added.
(WebInspector.TimelineRecording.prototype.removeTimeline): Added.
* UserInterface/Views/ContentView.js:
(WebInspector.ContentView):
* UserInterface/Views/LayoutTimelineView.css:
(.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.layout .item .subtitle):
(.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.layout .item .subtitle): Deleted.
* UserInterface/Views/LayoutTimelineView.js:
(WebInspector.LayoutTimelineView.prototype._treeElementSelected):
(WebInspector.LayoutTimelineView.prototype._closeStatusButtonClicked):
* UserInterface/Views/NavigationSidebarPanel.js:
(WebInspector.NavigationSidebarPanel.prototype.saveStateToCookie): Fix error message formatting.
(WebInspector.NavigationSidebarPanel.prototype._updateFilter):
* UserInterface/Views/NetworkTimelineView.css:
(.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.network .item .subtitle):
(.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.network .item .subtitle): Deleted.
* UserInterface/Views/NetworkTimelineView.js:
(WebInspector.NetworkTimelineView.prototype._closeStatusButtonClicked):
* UserInterface/Views/ScriptTimelineView.css:
(.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.script .item .subtitle):
(.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.script .item .subtitle): Deleted.
* UserInterface/Views/ScriptTimelineView.js:
(WebInspector.ScriptTimelineView.prototype._treeElementSelected):
(WebInspector.ScriptTimelineView.prototype._closeStatusButtonClicked):
* UserInterface/Views/TimelineOverview.js:
Convert this class to use the representedObject class pattern. It manages its own mapping from timelines
to overview graph views. Append 'Element' to some properties holding DOM elements.
(WebInspector.TimelineOverview):
(WebInspector.TimelineOverview.prototype.get visibleDuration):
(WebInspector.TimelineOverview.prototype.reset):
(WebInspector.TimelineOverview.prototype.updateLayout):
(WebInspector.TimelineOverview.prototype._handleScrollEvent):
(WebInspector.TimelineOverview.prototype._handleWheelEvent):
(WebInspector.TimelineOverview.prototype._timelineRemoved):
(WebInspector.TimelineOverview.prototype.updateLayoutIfNeeded):
(WebInspector.TimelineOverview.prototype._timeRangeSelectionChanged):
* UserInterface/Views/TimelineRecordingContentView.css: Renamed from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.css.
Remove hard-coded top offsets and heights that need to be dynamically calculated.
(.content-view.timeline-recording > .timeline-overview):
(.content-view.timeline-recording > .view-container):
(.content-view.timeline-recording > .view-container > .timeline-view > .data-grid td):
(.content-view.timeline-recording > .view-container > .timeline-view > .data-grid table.data):
* UserInterface/Views/TimelineRecordingContentView.js: Renamed from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js.
Manage timeline views and path components dynamically. Adjust view heights as necessary. Append 'Element'
to some properties holding DOM elements.
(WebInspector.TimelineRecordingContentView):
(WebInspector.TimelineRecordingContentView.prototype.showOverviewTimelineView):
(WebInspector.TimelineRecordingContentView.prototype.showTimelineViewForTimeline):
(WebInspector.TimelineRecordingContentView.prototype.get allowedNavigationSidebarPanels):
(WebInspector.TimelineRecordingContentView.prototype.get supportsSplitContentBrowser):
(WebInspector.TimelineRecordingContentView.prototype.get navigationItems):
(WebInspector.TimelineRecordingContentView.prototype.get currentTimelineView):
(WebInspector.TimelineRecordingContentView.prototype.shown):
(WebInspector.TimelineRecordingContentView.prototype.hidden):
(WebInspector.TimelineRecordingContentView.prototype.filterDidChange):
(WebInspector.TimelineRecordingContentView.prototype.updateLayout):
(WebInspector.TimelineRecordingContentView.prototype.saveToCookie):
(WebInspector.TimelineRecordingContentView.prototype.restoreFromCookie):
(WebInspector.TimelineRecordingContentView.prototype.get matchTreeElementAgainstCustomFilters.checkTimeBounds):
(WebInspector.TimelineRecordingContentView.prototype.get matchTreeElementAgainstCustomFilters):
(WebInspector.TimelineRecordingContentView.prototype._pathComponentSelected):
(WebInspector.TimelineRecordingContentView.prototype._timelineViewSelectionPathComponentsDidChange):
(WebInspector.TimelineRecordingContentView.prototype._showTimelineView):
(WebInspector.TimelineRecordingContentView.prototype._update):
(WebInspector.TimelineRecordingContentView.prototype._updateTimes):
(WebInspector.TimelineRecordingContentView.prototype._startUpdatingCurrentTime):
(WebInspector.TimelineRecordingContentView.prototype._stopUpdatingCurrentTime):
(WebInspector.TimelineRecordingContentView.prototype._capturingStarted):
(WebInspector.TimelineRecordingContentView.prototype._capturingStopped):
(WebInspector.TimelineRecordingContentView.prototype._debuggerPaused):
(WebInspector.TimelineRecordingContentView.prototype._debuggerResumed):
Fix a minor regression where we make a useless call to stop updating the time.

(WebInspector.TimelineRecordingContentView.prototype._recordingTimesUpdated):
(WebInspector.TimelineRecordingContentView.prototype._clearTimeline):
(WebInspector.TimelineRecordingContentView.prototype._timelineRemoved):
(WebInspector.TimelineRecordingContentView.prototype._timelineCountChanged):
(WebInspector.TimelineRecordingContentView.prototype._recordingReset):
(WebInspector.TimelineRecordingContentView.prototype._recordingUnloaded):
(WebInspector.TimelineRecordingContentView.prototype._timeRangeSelectionChanged):
* UserInterface/Views/TimelineSidebarPanel.css:
(.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing > .content):
(.sidebar > .panel.navigation.timeline.timeline-content-view-showing > .content): Deleted.
* UserInterface/Views/TimelineSidebarPanel.js:
Manage timeline tree elements dynamically. Adjust view heights as necessary. Append 'Element'
to some properties holding DOM elements. Keep track of the displayed recording and view explicitly.
(WebInspector.TimelineSidebarPanel):
(WebInspector.TimelineSidebarPanel.prototype.shown):
(WebInspector.TimelineSidebarPanel.prototype.showDefaultContentView):
(WebInspector.TimelineSidebarPanel.prototype.treeElementForRepresentedObject.looselyCompareRepresentedObjects):
(WebInspector.TimelineSidebarPanel.prototype.showTimelineOverview):
(WebInspector.TimelineSidebarPanel.prototype.updateFilter):
(WebInspector.TimelineSidebarPanel.prototype.matchTreeElementAgainstCustomFilters):
(WebInspector.TimelineSidebarPanel.prototype.canShowDifferentContentView):
(WebInspector.TimelineSidebarPanel.prototype.saveStateToCookie):
(WebInspector.TimelineSidebarPanel.prototype.get if):
(WebInspector.TimelineSidebarPanel.prototype._contentBrowserCurrentContentViewDidChange):
(WebInspector.TimelineSidebarPanel.prototype._recordingCountChanged):
(WebInspector.TimelineSidebarPanel.prototype._recordingSelected):
(WebInspector.TimelineSidebarPanel.prototype._recordingLoaded):
(WebInspector.TimelineSidebarPanel.prototype._timelineRemoved):
(WebInspector.TimelineSidebarPanel.prototype._timelineCountChanged):
(WebInspector.TimelineSidebarPanel.createTimelineTreeElement): Deleted.
(WebInspector.TimelineSidebarPanel.prototype.restoreStateFromCookie):
* UserInterface/Views/TimelineView.css:
(.panel.navigation.timeline.timeline-recording-content-view-showing > .content > .navigation-sidebar-panel-content-tree-outline):
(.panel.navigation.timeline.timeline-content-view-showing > .content > .navigation-sidebar-panel-content-tree-outline): Deleted.

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

19 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/Timeline.js
Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js
Source/WebInspectorUI/UserInterface/Views/ContentView.js
Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.css
Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.js
Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.css
Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.js
Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.css
Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.js
Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js
Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.css [moved from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.css with 85% similarity]
Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js [moved from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js with 79% similarity]
Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.css
Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/TimelineView.css

index 40f9687be9d444471d6279508362c6e066a3e688..6009e9aa79dd7ac526d799dd59a8b10b435c3a21 100644 (file)
@@ -1,3 +1,147 @@
+2015-02-12  Brian J. Burg  <burg@cs.washington.edu>
+
+        Web Inspector: support adding and removing timelines to the timeline sidebar panel and overview
+        https://bugs.webkit.org/show_bug.cgi?id=138434
+
+        Reviewed by Timothy Hatcher.
+
+        A timeline recording's timelines should not be static. This patch adds relevant machinery to
+        dynamically add and remove timelines from the Timeline model object and its various views.
+
+        From the model side, this is a simple change that adds TimelineAdded and TimelineRemoved events.
+        The timeline views and timeline sidebar require more extensive changes to support this functionality.
+        Instead of keeping a fixed set of timeline tree elements, the sidebar view now adds and removes
+        tree elements to reflect the available timelines for the displayed timeline recording.
+
+        This change also includes several minor cleanups, such as appending 'Element' to view properties that
+        are DOM elements, and renaming TimelineContentView to TimelineRecordingContentView.
+
+        * UserInterface/Controllers/TimelineManager.js:
+        (WebInspector.TimelineManager.prototype._loadNewRecording):
+        Populate predefined network, layout, and script timelines here after constructing the recording.
+
+        * UserInterface/Main.html:
+        * UserInterface/Models/Timeline.js:
+        Move hardcoded class names and localized strings to these base class methods. This is not implemented
+        using overridden methods because not all timeline types have their own subclasses. Add a dummy
+        implementation of saveIdentityToCookie() to avoid warnings when saving sidebar panel selection state.
+
+        (WebInspector.Timeline.prototype.get type):
+        (WebInspector.Timeline.prototype.get displayName): Added.
+        (WebInspector.Timeline.prototype.get iconClassName): Added.
+        (WebInspector.Timeline.prototype.addRecord):
+        * UserInterface/Models/TimelineRecording.js:
+        (WebInspector.TimelineRecording):
+        (WebInspector.TimelineRecording.prototype.addTimeline.set timeline): Added.
+        (WebInspector.TimelineRecording.prototype.addTimeline): Added.
+        (WebInspector.TimelineRecording.prototype.removeTimeline.get this): Added.
+        (WebInspector.TimelineRecording.prototype.removeTimeline): Added.
+        * UserInterface/Views/ContentView.js:
+        (WebInspector.ContentView):
+        * UserInterface/Views/LayoutTimelineView.css:
+        (.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.layout .item .subtitle):
+        (.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.layout .item .subtitle): Deleted.
+        * UserInterface/Views/LayoutTimelineView.js:
+        (WebInspector.LayoutTimelineView.prototype._treeElementSelected):
+        (WebInspector.LayoutTimelineView.prototype._closeStatusButtonClicked):
+        * UserInterface/Views/NavigationSidebarPanel.js:
+        (WebInspector.NavigationSidebarPanel.prototype.saveStateToCookie): Fix error message formatting.
+        (WebInspector.NavigationSidebarPanel.prototype._updateFilter):
+        * UserInterface/Views/NetworkTimelineView.css:
+        (.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.network .item .subtitle):
+        (.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.network .item .subtitle): Deleted.
+        * UserInterface/Views/NetworkTimelineView.js:
+        (WebInspector.NetworkTimelineView.prototype._closeStatusButtonClicked):
+        * UserInterface/Views/ScriptTimelineView.css:
+        (.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.script .item .subtitle):
+        (.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.script .item .subtitle): Deleted.
+        * UserInterface/Views/ScriptTimelineView.js:
+        (WebInspector.ScriptTimelineView.prototype._treeElementSelected):
+        (WebInspector.ScriptTimelineView.prototype._closeStatusButtonClicked):
+        * UserInterface/Views/TimelineOverview.js:
+        Convert this class to use the representedObject class pattern. It manages its own mapping from timelines
+        to overview graph views. Append 'Element' to some properties holding DOM elements.
+        (WebInspector.TimelineOverview):
+        (WebInspector.TimelineOverview.prototype.get visibleDuration):
+        (WebInspector.TimelineOverview.prototype.reset):
+        (WebInspector.TimelineOverview.prototype.updateLayout):
+        (WebInspector.TimelineOverview.prototype._handleScrollEvent):
+        (WebInspector.TimelineOverview.prototype._handleWheelEvent):
+        (WebInspector.TimelineOverview.prototype._timelineRemoved):
+        (WebInspector.TimelineOverview.prototype.updateLayoutIfNeeded):
+        (WebInspector.TimelineOverview.prototype._timeRangeSelectionChanged):
+        * UserInterface/Views/TimelineRecordingContentView.css: Renamed from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.css.
+        Remove hard-coded top offsets and heights that need to be dynamically calculated.
+        (.content-view.timeline-recording > .timeline-overview):
+        (.content-view.timeline-recording > .view-container):
+        (.content-view.timeline-recording > .view-container > .timeline-view > .data-grid td):
+        (.content-view.timeline-recording > .view-container > .timeline-view > .data-grid table.data):
+        * UserInterface/Views/TimelineRecordingContentView.js: Renamed from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js.
+        Manage timeline views and path components dynamically. Adjust view heights as necessary. Append 'Element'
+        to some properties holding DOM elements.
+        (WebInspector.TimelineRecordingContentView):
+        (WebInspector.TimelineRecordingContentView.prototype.showOverviewTimelineView):
+        (WebInspector.TimelineRecordingContentView.prototype.showTimelineViewForTimeline):
+        (WebInspector.TimelineRecordingContentView.prototype.get allowedNavigationSidebarPanels):
+        (WebInspector.TimelineRecordingContentView.prototype.get supportsSplitContentBrowser):
+        (WebInspector.TimelineRecordingContentView.prototype.get navigationItems):
+        (WebInspector.TimelineRecordingContentView.prototype.get currentTimelineView):
+        (WebInspector.TimelineRecordingContentView.prototype.shown):
+        (WebInspector.TimelineRecordingContentView.prototype.hidden):
+        (WebInspector.TimelineRecordingContentView.prototype.filterDidChange):
+        (WebInspector.TimelineRecordingContentView.prototype.updateLayout):
+        (WebInspector.TimelineRecordingContentView.prototype.saveToCookie):
+        (WebInspector.TimelineRecordingContentView.prototype.restoreFromCookie):
+        (WebInspector.TimelineRecordingContentView.prototype.get matchTreeElementAgainstCustomFilters.checkTimeBounds):
+        (WebInspector.TimelineRecordingContentView.prototype.get matchTreeElementAgainstCustomFilters):
+        (WebInspector.TimelineRecordingContentView.prototype._pathComponentSelected):
+        (WebInspector.TimelineRecordingContentView.prototype._timelineViewSelectionPathComponentsDidChange):
+        (WebInspector.TimelineRecordingContentView.prototype._showTimelineView):
+        (WebInspector.TimelineRecordingContentView.prototype._update):
+        (WebInspector.TimelineRecordingContentView.prototype._updateTimes):
+        (WebInspector.TimelineRecordingContentView.prototype._startUpdatingCurrentTime):
+        (WebInspector.TimelineRecordingContentView.prototype._stopUpdatingCurrentTime):
+        (WebInspector.TimelineRecordingContentView.prototype._capturingStarted):
+        (WebInspector.TimelineRecordingContentView.prototype._capturingStopped):
+        (WebInspector.TimelineRecordingContentView.prototype._debuggerPaused):
+        (WebInspector.TimelineRecordingContentView.prototype._debuggerResumed):
+        Fix a minor regression where we make a useless call to stop updating the time.
+
+        (WebInspector.TimelineRecordingContentView.prototype._recordingTimesUpdated):
+        (WebInspector.TimelineRecordingContentView.prototype._clearTimeline):
+        (WebInspector.TimelineRecordingContentView.prototype._timelineRemoved):
+        (WebInspector.TimelineRecordingContentView.prototype._timelineCountChanged):
+        (WebInspector.TimelineRecordingContentView.prototype._recordingReset):
+        (WebInspector.TimelineRecordingContentView.prototype._recordingUnloaded):
+        (WebInspector.TimelineRecordingContentView.prototype._timeRangeSelectionChanged):
+        * UserInterface/Views/TimelineSidebarPanel.css:
+        (.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing > .content):
+        (.sidebar > .panel.navigation.timeline.timeline-content-view-showing > .content): Deleted.
+        * UserInterface/Views/TimelineSidebarPanel.js:
+        Manage timeline tree elements dynamically. Adjust view heights as necessary. Append 'Element'
+        to some properties holding DOM elements. Keep track of the displayed recording and view explicitly.
+        (WebInspector.TimelineSidebarPanel):
+        (WebInspector.TimelineSidebarPanel.prototype.shown):
+        (WebInspector.TimelineSidebarPanel.prototype.showDefaultContentView):
+        (WebInspector.TimelineSidebarPanel.prototype.treeElementForRepresentedObject.looselyCompareRepresentedObjects):
+        (WebInspector.TimelineSidebarPanel.prototype.showTimelineOverview):
+        (WebInspector.TimelineSidebarPanel.prototype.updateFilter):
+        (WebInspector.TimelineSidebarPanel.prototype.matchTreeElementAgainstCustomFilters):
+        (WebInspector.TimelineSidebarPanel.prototype.canShowDifferentContentView):
+        (WebInspector.TimelineSidebarPanel.prototype.saveStateToCookie):
+        (WebInspector.TimelineSidebarPanel.prototype.get if):
+        (WebInspector.TimelineSidebarPanel.prototype._contentBrowserCurrentContentViewDidChange):
+        (WebInspector.TimelineSidebarPanel.prototype._recordingCountChanged):
+        (WebInspector.TimelineSidebarPanel.prototype._recordingSelected):
+        (WebInspector.TimelineSidebarPanel.prototype._recordingLoaded):
+        (WebInspector.TimelineSidebarPanel.prototype._timelineRemoved):
+        (WebInspector.TimelineSidebarPanel.prototype._timelineCountChanged):
+        (WebInspector.TimelineSidebarPanel.createTimelineTreeElement): Deleted.
+        (WebInspector.TimelineSidebarPanel.prototype.restoreStateFromCookie):
+        * UserInterface/Views/TimelineView.css:
+        (.panel.navigation.timeline.timeline-recording-content-view-showing > .content > .navigation-sidebar-panel-content-tree-outline):
+        (.panel.navigation.timeline.timeline-content-view-showing > .content > .navigation-sidebar-panel-content-tree-outline): Deleted.
+
 2015-02-12  Brian J. Burg  <burg@cs.washington.edu>
 
         Web Inspector: Large background image fails to load in inspector
index 3f716422e9c5aa0e45f2ac0c029dfe49caf3a6f1..aa653a3f83ac188e2ce9ef37e54c79f6f73d53f3 100644 (file)
@@ -411,6 +411,10 @@ WebInspector.TimelineManager.prototype = {
 
         var identifier = this._nextRecordingIdentifier++;
         var newRecording = new WebInspector.TimelineRecording(identifier, WebInspector.UIString("Timeline Recording %d").format(identifier));
+        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Network));
+        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Layout));
+        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Script));
+
         this._recordings.push(newRecording);
         this.dispatchEventToListeners(WebInspector.TimelineManager.Event.RecordingCreated, {recording: newRecording});
 
index 7b915558d246b95d010215e1222e099678fe1b6f..46f31a1acd6c11d8d41143bbb8904a08ef74fecf 100644 (file)
     <link rel="stylesheet" href="Views/TextContentView.css">
     <link rel="stylesheet" href="Views/TextEditor.css">
     <link rel="stylesheet" href="Views/TextResourceContentView.css">
-    <link rel="stylesheet" href="Views/TimelineContentView.css">
     <link rel="stylesheet" href="Views/TimelineDataGrid.css">
     <link rel="stylesheet" href="Views/TimelineIcons.css">
     <link rel="stylesheet" href="Views/TimelineOverview.css">
     <link rel="stylesheet" href="Views/TimelineRecordBar.css">
+    <link rel="stylesheet" href="Views/TimelineRecordingContentView.css">
     <link rel="stylesheet" href="Views/TimelineRuler.css">
     <link rel="stylesheet" href="Views/TimelineSidebarPanel.css">
     <link rel="stylesheet" href="Views/TimelineView.css">
     <script src="Views/SyntaxHighlightingSupport.js"></script>
     <script src="Views/TextContentView.js"></script>
     <script src="Views/TextResourceContentView.js"></script>
-    <script src="Views/TimelineContentView.js"></script>
     <script src="Views/TimelineOverview.js"></script>
     <script src="Views/TimelineRecordBar.js"></script>
+    <script src="Views/TimelineRecordingContentView.js"></script>
     <script src="Views/TimelineRuler.js"></script>
     <script src="Views/TimelineSidebarPanel.js"></script>
     <script src="Views/ToggleButtonNavigationItem.js"></script>
index bbfde56303a7bacb6974fc5f000f25ac3a26b5c8..ed0ba5e9fc172567c99d7ec12a83c72ff667d687 100644 (file)
@@ -44,6 +44,8 @@ WebInspector.Timeline.Event = {
     TimesUpdated: "timeline-times-updated"
 };
 
+WebInspector.Timeline.TimelineTypeCookieKey = "timeline-type";
+
 WebInspector.Timeline.prototype = {
     constructor: WebInspector.Timeline,
     __proto__: WebInspector.Object.prototype,
@@ -70,6 +72,30 @@ WebInspector.Timeline.prototype = {
         return this._type;
     },
 
+    get displayName()
+    {
+        if (this._type === WebInspector.TimelineRecord.Type.Network)
+            return WebInspector.UIString("Network Requests");
+        if (this._type === WebInspector.TimelineRecord.Type.Layout)
+            return WebInspector.UIString("Layout & Rendering");
+        if (this._type === WebInspector.TimelineRecord.Type.Script)
+            return WebInspector.UIString("JavaScript & Events");
+
+        console.error("Timeline has unknown type:", this._type, this);
+    },
+
+    get iconClassName()
+    {
+        if (this._type === WebInspector.TimelineRecord.Type.Network)
+            return WebInspector.TimelineSidebarPanel.NetworkIconStyleClass;
+        if (this._type === WebInspector.TimelineRecord.Type.Layout)
+            return WebInspector.TimelineSidebarPanel.ColorsIconStyleClass;
+        if (this._type === WebInspector.TimelineRecord.Type.Script)
+            return WebInspector.TimelineSidebarPanel.ScriptIconStyleClass;
+
+        console.error("Timeline has unknown type:", this._type, this);
+    },
+
     reset: function(suppressEvents)
     {
         this._records = [];
@@ -94,6 +120,11 @@ WebInspector.Timeline.prototype = {
         this.dispatchEventToListeners(WebInspector.Timeline.Event.RecordAdded, {record: record});
     },
 
+    saveIdentityToCookie: function(cookie)
+    {
+        cookie[WebInspector.Timeline.TimelineTypeCookieKey] = this._type;
+    },
+
     // Private
 
     _updateTimesIfNeeded: function(record)
index 4e99a44394357f630b9c347ff03247effd07446f..d20a2a444b8d933c8fdfdbf3255844c4f07898c9 100644 (file)
@@ -35,13 +35,6 @@ WebInspector.TimelineRecording = function(identifier, displayName)
     // For legacy backends, we compute the elapsed time of records relative to this timestamp.
     this._legacyFirstRecordedTimestamp = NaN;
 
-    for (var key of Object.keys(WebInspector.TimelineRecord.Type)) {
-        var type = WebInspector.TimelineRecord.Type[key];
-        var timeline = new WebInspector.Timeline(type);
-        this._timelines.set(type, timeline);
-        timeline.addEventListener(WebInspector.Timeline.Event.TimesUpdated, this._timelineTimesUpdated, this);
-    }
-
     this.reset(true);
 };
 
@@ -49,6 +42,8 @@ WebInspector.TimelineRecording.Event = {
     Reset: "timeline-recording-reset",
     Unloaded: "timeline-recording-unloaded",
     SourceCodeTimelineAdded: "timeline-recording-source-code-timeline-added",
+    TimelineAdded: "timeline-recording-timeline-added",
+    TimelineRemoved: "timeline-recording-timeline-removed",
     TimesUpdated: "timeline-recording-times-updated"
 };
 
@@ -141,6 +136,29 @@ WebInspector.TimelineRecording.prototype = {
         return [...timelines.values()];
     },
 
+    addTimeline: function(timeline)
+    {
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(!this._timelines.has(timeline), this._timelines, timeline);
+
+        this._timelines.set(timeline.type, timeline);
+
+        timeline.addEventListener(WebInspector.Timeline.Event.TimesUpdated, this._timelineTimesUpdated, this);
+        this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.TimelineAdded, {timeline: timeline});
+    },
+
+    removeTimeline: function(timeline)
+    {
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(this._timelines.has(timeline.type), this._timelines, timeline);
+        console.assert(this._timelines.get(timeline.type) === timeline, this._timelines, timeline);
+
+        this._timelines.delete(timeline.type);
+
+        timeline.removeEventListener(WebInspector.Timeline.Event.TimesUpdated, this._timelineTimesUpdated, this);
+        this.dispatchEventToListeners(WebInspector.TimelineRecording.Event.TimelineRemoved, {timeline: timeline});
+    },
+
     addEventMarker: function(eventMarker)
     {
         this._eventMarkers.push(eventMarker);
@@ -148,6 +166,11 @@ WebInspector.TimelineRecording.prototype = {
 
     addRecord: function(record)
     {
+        var hasCorrespondingTimeline = this._timelines.has(record.type);
+        console.assert(hasCorrespondingTimeline, record, this._timelines);
+        if (!hasCorrespondingTimeline)
+            return;
+
         // Add the record to the global timeline by type.
         this._timelines.get(record.type).addRecord(record);
 
index 6d784a87cf2c4bc8037685039921aeed690eaeea..4fea6609df25e705f25058e452d6de2e4aee29ba 100644 (file)
@@ -40,7 +40,7 @@ WebInspector.ContentView = function(representedObject)
             return new WebInspector.ScriptContentView(representedObject);
 
         if (representedObject instanceof WebInspector.TimelineRecording)
-            return new WebInspector.TimelineContentView(representedObject);
+            return new WebInspector.TimelineRecordingContentView(representedObject);
 
         if (representedObject instanceof WebInspector.DOMStorageObject)
             return new WebInspector.DOMStorageContentView(representedObject);
index b0b81ef5442b5a8ed8e48ff3e21acf7cc4aaf167..2222d19ef29e44a789160bf9de7a7d2e2397807b 100644 (file)
@@ -35,6 +35,6 @@
     border-right: none;
 }
 
-.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.layout .item .subtitle {
+.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.layout .item .subtitle {
     display: none;
 }
index 5de64d89a81f779752dccfed7a16cdbd9c138523..79cbc9648cd7c43933d28468d7cd0bb99948d761 100644 (file)
@@ -229,7 +229,7 @@ WebInspector.LayoutTimelineView.prototype = {
         this._updateHighlight();
 
         if (!treeElement.record.sourceCodeLocation) {
-            WebInspector.timelineSidebarPanel.showTimelineViewForType(WebInspector.TimelineRecord.Type.Layout);
+            WebInspector.timelineSidebarPanel.showTimelineViewForTimeline(this.representedObject);
             return;
         }
 
@@ -255,7 +255,7 @@ WebInspector.LayoutTimelineView.prototype = {
     _closeStatusButtonClicked: function(event)
     {
         this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
-        WebInspector.timelineSidebarPanel.showTimelineViewForType(WebInspector.TimelineRecord.Type.Layout);
+        WebInspector.timelineSidebarPanel.showTimelineViewForTimeline(this.representedObject);
     },
 
     _updateHighlight: function()
index e8e152848ef9c62836a22fe36b32bc58522e8503..4a1f930f8debbe7f20fbc7a0f1d50a9b2f258f85 100644 (file)
@@ -225,7 +225,7 @@ WebInspector.NavigationSidebarPanel.prototype = {
         if (representedObject.saveIdentityToCookie)
             representedObject.saveIdentityToCookie(cookie);
         else
-            console.error("Error: TreeElement.representedObject is missing a saveIdentityToCookie implementation. TreeElement.constructor: %s", selectedTreeElement.constructor);
+            console.error("Error: TreeElement.representedObject is missing a saveIdentityToCookie implementation. TreeElement.constructor: ", selectedTreeElement.constructor);
     },
 
     // This can be supplemented by subclasses that admit a simpler strategy for static tree elements.
@@ -521,7 +521,7 @@ WebInspector.NavigationSidebarPanel.prototype = {
         // Filter may have hidden the selected resource in the timeline view, which should now notify its listeners.
         if (selectedTreeElement && selectedTreeElement.hidden !== selectionWasHidden) {
             var currentContentView = WebInspector.contentBrowser.currentContentView;
-            if (currentContentView instanceof WebInspector.TimelineContentView && typeof currentContentView.currentTimelineView.filterUpdated === "function")
+            if (currentContentView instanceof WebInspector.TimelineRecordingContentView && typeof currentContentView.currentTimelineView.filterUpdated === "function")
                 currentContentView.currentTimelineView.filterUpdated();
         }
     },
index 83273f7812a9c2a9158b14670249098932865d04..74d8c439d6cc9ab4b8ef2d360e575590d8966d72 100644 (file)
@@ -31,6 +31,6 @@
     bottom: 0;
 }
 
-.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.network .item .subtitle {
+.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.network .item .subtitle {
     display: none;
 }
index 75feb8d107a1859eb536f1b5b22a8fc2ad93c251..0dff4926d766122e8bf433bfd85b2c9126d9b2ba 100644 (file)
@@ -244,6 +244,6 @@ WebInspector.NetworkTimelineView.prototype = {
     _closeStatusButtonClicked: function(event)
     {
         this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
-        WebInspector.timelineSidebarPanel.showTimelineViewForType(WebInspector.TimelineRecord.Type.Network);
+        WebInspector.timelineSidebarPanel.showTimelineViewForTimeline(this.representedObject);
     }
 };
index 9bb46c58776ea8dbb2e0bde41970babd5df55a6f..cafa900f016ca368e6bd6f09bebba7b0ef4a1e7f 100644 (file)
@@ -35,6 +35,6 @@
     border-right: none;
 }
 
-.sidebar > .panel.navigation.timeline.timeline-content-view-showing .navigation-sidebar-panel-content-tree-outline.script .item .subtitle {
+.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing .navigation-sidebar-panel-content-tree-outline.script .item .subtitle {
     display: none;
 }
index e686da567ef0a12942f2ddbe17e1bdbafa2afee0..9502f871b5ff3d91d02514145645c136ee031623 100644 (file)
@@ -272,7 +272,7 @@ WebInspector.ScriptTimelineView.prototype = {
             console.error("Unknown tree element selected.");
 
         if (!sourceCodeLocation) {
-            WebInspector.timelineSidebarPanel.showTimelineViewForType(WebInspector.TimelineRecord.Type.Script);
+            WebInspector.timelineSidebarPanel.showTimelineViewForTimeline(this.representedObject);
             return;
         }
 
@@ -298,6 +298,6 @@ WebInspector.ScriptTimelineView.prototype = {
     _closeStatusButtonClicked: function(event)
     {
         this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
-        WebInspector.timelineSidebarPanel.showTimelineViewForType(WebInspector.TimelineRecord.Type.Script);
+        WebInspector.timelineSidebarPanel.showTimelineViewForTimeline(this.representedObject);
     }
 };
index 5d86069fe2ed459cc54b79e77f6873fd121fe1f5..9011bbbf04233c80500b3b672162c0190fafcc77 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.TimelineOverview = function(timelineOverviewGraphsMap)
+WebInspector.TimelineOverview = function(timelineRecording)
 {
     WebInspector.Object.call(this);
 
+    this._recording = timelineRecording;
+    this._recording.addEventListener(WebInspector.TimelineRecording.Event.TimelineAdded, this._timelineAdded, this);
+    this._recording.addEventListener(WebInspector.TimelineRecording.Event.TimelineRemoved, this._timelineRemoved, this);
+
     this._element = document.createElement("div");
     this._element.className = WebInspector.TimelineOverview.StyleClassName;
     this._element.addEventListener("wheel", this._handleWheelEvent.bind(this));
 
-    this._graphsContainer = document.createElement("div");
-    this._graphsContainer.className = WebInspector.TimelineOverview.GraphsContainerStyleClassName;
-    this._element.appendChild(this._graphsContainer);
-
-    this._timelineOverviewGraphsMap = timelineOverviewGraphsMap;
+    this._graphsContainerElement = document.createElement("div");
+    this._graphsContainerElement.className = WebInspector.TimelineOverview.GraphsContainerStyleClassName;
+    this._element.appendChild(this._graphsContainerElement);
 
-    for (var timelineOverviewGraph of this._timelineOverviewGraphsMap.values()) {
-        timelineOverviewGraph.timelineOverview = this;
-        this._graphsContainer.appendChild(timelineOverviewGraph.element);
-    }
+    this._timelineOverviewGraphsMap = new Map;
 
     this._timelineRuler = new WebInspector.TimelineRuler;
     this._timelineRuler.allowsClippedLabels = true;
@@ -51,14 +50,14 @@ WebInspector.TimelineOverview = function(timelineOverviewGraphsMap)
     this._currentTimeMarker = new WebInspector.TimelineMarker(0, WebInspector.TimelineMarker.Type.CurrentTime);
     this._timelineRuler.addMarker(this._currentTimeMarker);
 
-    this._scrollContainer = document.createElement("div");
-    this._scrollContainer.className = WebInspector.TimelineOverview.ScrollContainerStyleClassName;
-    this._scrollContainer.addEventListener("scroll", this._handleScrollEvent.bind(this));
-    this._element.appendChild(this._scrollContainer);
+    this._scrollContainerElement = document.createElement("div");
+    this._scrollContainerElement.className = WebInspector.TimelineOverview.ScrollContainerStyleClassName;
+    this._scrollContainerElement.addEventListener("scroll", this._handleScrollEvent.bind(this));
+    this._element.appendChild(this._scrollContainerElement);
 
     this._scrollWidthSizer = document.createElement("div");
     this._scrollWidthSizer.className = WebInspector.TimelineOverview.ScrollWidthSizerStyleClassName;
-    this._scrollContainer.appendChild(this._scrollWidthSizer);
+    this._scrollContainerElement.appendChild(this._scrollWidthSizer);
 
     this._secondsPerPixelSetting = new WebInspector.Setting("timeline-overview-seconds-per-pixel", 0.01);
     this._selectionStartTimeSetting = new WebInspector.Setting("timeline-overview-selection-start-time", 0);
@@ -73,6 +72,9 @@ WebInspector.TimelineOverview = function(timelineOverviewGraphsMap)
 
     this.selectionStartTime = this._selectionStartTimeSetting.value;
     this.selectionDuration = this._selectionDurationSetting.value;
+
+    for (var timeline of this._recording.timelines.values())
+        this._timelineAdded(timeline);
 };
 
 WebInspector.TimelineOverview.StyleClassName = "timeline-overview";
@@ -179,7 +181,7 @@ WebInspector.TimelineOverview.prototype = {
     get visibleDuration()
     {
         if (isNaN(this._cachedScrollContainerWidth)) {
-            this._cachedScrollContainerWidth = this._scrollContainer.offsetWidth;
+            this._cachedScrollContainerWidth = this._scrollContainerElement.offsetWidth;
             console.assert(this._cachedScrollContainerWidth > 0);
         }
 
@@ -234,6 +236,12 @@ WebInspector.TimelineOverview.prototype = {
             timelineOverviewGraph.hidden();
     },
 
+    reset: function()
+    {
+        for (var timelineOverviewGraph of this._timelineOverviewGraphsMap.values())
+            timelineOverviewGraph.reset();
+    },
+
     addMarker: function(marker)
     {
         this._timelineRuler.addMarker(marker);
@@ -283,7 +291,7 @@ WebInspector.TimelineOverview.prototype = {
 
         if (!this._dontUpdateScrollLeft) {
             this._ignoreNextScrollEvent = true;
-            this._scrollContainer.scrollLeft = Math.ceil((scrollStartTime - this._startTime) / this._secondsPerPixel);
+            this._scrollContainerElement.scrollLeft = Math.ceil((scrollStartTime - this._startTime) / this._secondsPerPixel);
         }
 
         this._timelineRuler.updateLayout();
@@ -339,7 +347,7 @@ WebInspector.TimelineOverview.prototype = {
 
         this._dontUpdateScrollLeft = true;
 
-        var scrollOffset = this._scrollContainer.scrollLeft;
+        var scrollOffset = this._scrollContainerElement.scrollLeft;
         this.scrollStartTime = this._startTime + (scrollOffset * this._secondsPerPixel);
 
         // Force layout so we can update with the scroll position synchronously.
@@ -361,7 +369,7 @@ WebInspector.TimelineOverview.prototype = {
             var newWheelEvent = new event.constructor(event.type, event);
             newWheelEvent.__cloned = true;
 
-            this._scrollContainer.dispatchEvent(newWheelEvent);
+            this._scrollContainerElement.dispatchEvent(newWheelEvent);
             return;
         }
 
@@ -379,6 +387,32 @@ WebInspector.TimelineOverview.prototype = {
         event.stopPropagation();
     },
 
+    _timelineAdded: function(timelineOrEvent)
+    {
+        var timeline = timelineOrEvent;
+        if (!(timeline instanceof WebInspector.Timeline))
+            timeline = timelineOrEvent.data.timeline;
+
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(!this._timelineOverviewGraphsMap.has(timeline), timeline);
+
+        var overviewGraph = new WebInspector.TimelineOverviewGraph(timeline);
+        overviewGraph.timelineOverview = this;
+        this._timelineOverviewGraphsMap.set(timeline, overviewGraph);
+        this._graphsContainerElement.appendChild(overviewGraph.element);
+    },
+
+    _timelineRemoved: function(event)
+    {
+        var timeline = event.data.timeline;
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(this._timelineOverviewGraphsMap.has(timeline), timeline);
+
+        var overviewGraph = this._timelineOverviewGraphsMap.take(timeline);
+        overviewGraph.timelineOverview = null;
+        this._graphsContainerElement.removeChild(overviewGraph.element);
+    },
+
     _timeRangeSelectionChanged: function(event)
     {
         this._selectionStartTimeSetting.value = this.selectionStartTime - this._startTime;
similarity index 85%
rename from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.css
rename to Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.css
index 60ec0d68823f842d45b64cf4f606a281cf0e5069..392bebd31825a2cafaa797eca59d1c97f506368a 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-.content-view.timeline > .timeline-overview {
+.content-view.timeline-recording > .timeline-overview {
     position: absolute;
     top: 0;
     left: 0;
     right: 0;
-    height: 130px;
 }
 
-.content-view.timeline > .view-container {
+.content-view.timeline-recording > .view-container {
     position: absolute;
-    top: 130px;
     left: 0;
     right: 0;
     bottom: 0;
     overflow: hidden;
 }
 
-.content-view.timeline > .view-container > .timeline-view > .data-grid td {
+.content-view.timeline-recording > .view-container > .timeline-view > .data-grid td {
     height: 16px;
     line-height: 16px;
 }
 
-.content-view.timeline > .view-container > .timeline-view > .data-grid table.data {
+.content-view.timeline-recording > .view-container > .timeline-view > .data-grid table.data {
     background-image: linear-gradient(to bottom, white, white 50%, rgb(243, 243, 243) 50%, rgb(243, 243, 243));
     background-size: 100% 40px;
 }
similarity index 79%
rename from Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js
rename to Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
index 1d3e925ffd1471221e43cb12c2b35c62a128f4ae..6b1b441287937c0611ad71a170bdd4836a23a2a5 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.TimelineContentView = function(recording)
+WebInspector.TimelineRecordingContentView = function(recording)
 {
     WebInspector.ContentView.call(this, recording);
 
     this._recording = recording;
 
-    this.element.classList.add(WebInspector.TimelineContentView.StyleClassName);
+    this.element.classList.add(WebInspector.TimelineRecordingContentView.StyleClassName);
 
-    this._discreteTimelineOverviewGraphMap = new Map;
-    for (var [identifier, timeline] of recording.timelines)
-        this._discreteTimelineOverviewGraphMap.set(timeline, new WebInspector.TimelineOverviewGraph(timeline));
-
-    this._timelineOverview = new WebInspector.TimelineOverview(this._discreteTimelineOverviewGraphMap);
+    this._timelineOverview = new WebInspector.TimelineOverview(this._recording);
     this._timelineOverview.addEventListener(WebInspector.TimelineOverview.Event.TimeRangeSelectionChanged, this._timeRangeSelectionChanged, this);
     this.element.appendChild(this._timelineOverview.element);
 
-    this._viewContainer = document.createElement("div");
-    this._viewContainer.classList.add(WebInspector.TimelineContentView.ViewContainerStyleClassName);
-    this.element.appendChild(this._viewContainer);
+    this._viewContainerElement = document.createElement("div");
+    this._viewContainerElement.classList.add(WebInspector.TimelineRecordingContentView.ViewContainerStyleClassName);
+    this.element.appendChild(this._viewContainerElement);
 
     var trashImage;
     if (WebInspector.Platform.isLegacyMacOS)
@@ -55,35 +51,8 @@ WebInspector.TimelineContentView = function(recording)
     this._overviewTimelineView = new WebInspector.OverviewTimelineView(recording);
     this._overviewTimelineView.secondsPerPixel = this._timelineOverview.secondsPerPixel;
 
-    this._discreteTimelineViewMap = new Map;
-    for (var [identifier, timeline] of recording.timelines)
-        this._discreteTimelineViewMap.set(timeline, new WebInspector.TimelineView(timeline));
-
-    function createPathComponent(displayName, className, representedObject)
-    {
-        var pathComponent = new WebInspector.HierarchicalPathComponent(displayName, className, representedObject);
-        pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
-        return pathComponent;
-    }
-
-    var networkTimeline = recording.timelines.get(WebInspector.TimelineRecord.Type.Network);
-    var layoutTimeline = recording.timelines.get(WebInspector.TimelineRecord.Type.Layout);
-    var scriptTimeline = recording.timelines.get(WebInspector.TimelineRecord.Type.Script);
-
+    this._timelineViewMap = new Map;
     this._pathComponentMap = new Map;
-    this._pathComponentMap.set(networkTimeline, createPathComponent.call(this, WebInspector.UIString("Network Requests"), WebInspector.TimelineSidebarPanel.NetworkIconStyleClass, networkTimeline));
-    this._pathComponentMap.set(layoutTimeline, createPathComponent.call(this, WebInspector.UIString("Layout & Rendering"), WebInspector.TimelineSidebarPanel.ColorsIconStyleClass, layoutTimeline));
-    this._pathComponentMap.set(scriptTimeline, createPathComponent.call(this, WebInspector.UIString("JavaScript & Events"), WebInspector.TimelineSidebarPanel.ScriptIconStyleClass, scriptTimeline));
-
-    var previousPathComponent = null;
-    for (var pathComponent of this._pathComponentMap.values()) {
-        if (previousPathComponent) {
-            previousPathComponent.nextSibling = pathComponent;
-            pathComponent.previousSibling = previousPathComponent;
-        }
-
-        previousPathComponent = pathComponent;
-    }
 
     this._currentTimelineView = null;
     this._currentTimelineViewIdentifier = null;
@@ -93,8 +62,10 @@ WebInspector.TimelineContentView = function(recording)
     this._lastUpdateTimestamp = NaN;
     this._startTimeNeedsReset = true;
 
-    recording.addEventListener(WebInspector.TimelineRecording.Event.Reset, this._recordingReset, this);
-    recording.addEventListener(WebInspector.TimelineRecording.Event.Unloaded, this._recordingUnloaded, this);
+    this._recording.addEventListener(WebInspector.TimelineRecording.Event.TimelineAdded, this._timelineAdded, this);
+    this._recording.addEventListener(WebInspector.TimelineRecording.Event.TimelineRemoved, this._timelineRemoved, this);
+    this._recording.addEventListener(WebInspector.TimelineRecording.Event.Reset, this._recordingReset, this);
+    this._recording.addEventListener(WebInspector.TimelineRecording.Event.Unloaded, this._recordingUnloaded, this);
 
     WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
     WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
@@ -102,17 +73,20 @@ WebInspector.TimelineContentView = function(recording)
     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerPaused, this);
     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerResumed, this);
 
+    for (var timeline of this._recording.timelines.values())
+        this._timelineAdded(timeline);
+
     this.showOverviewTimelineView();
 };
 
-WebInspector.TimelineContentView.StyleClassName = "timeline";
-WebInspector.TimelineContentView.ViewContainerStyleClassName = "view-container";
+WebInspector.TimelineRecordingContentView.StyleClassName = "timeline-recording";
+WebInspector.TimelineRecordingContentView.ViewContainerStyleClassName = "view-container";
 
-WebInspector.TimelineContentView.SelectedTimelineTypeCookieKey = "timeline-content-view-selected-timeline-type";
-WebInspector.TimelineContentView.OverviewTimelineViewCookieValue = "timeline-content-view-overview-timeline-view";
+WebInspector.TimelineRecordingContentView.SelectedTimelineTypeCookieKey = "timeline-recording-content-view-selected-timeline-type";
+WebInspector.TimelineRecordingContentView.OverviewTimelineViewCookieValue = "timeline-recording-content-view-overview-timeline-view";
 
-WebInspector.TimelineContentView.prototype = {
-    constructor: WebInspector.TimelineContentView,
+WebInspector.TimelineRecordingContentView.prototype = {
+    constructor: WebInspector.TimelineRecordingContentView,
     __proto__: WebInspector.ContentView.prototype,
 
     // Public
@@ -125,11 +99,11 @@ WebInspector.TimelineContentView.prototype = {
     showTimelineViewForTimeline: function(timeline)
     {
         console.assert(timeline instanceof WebInspector.Timeline, timeline);
-        console.assert(this._discreteTimelineViewMap.has(timeline), timeline);
-        if (!this._discreteTimelineViewMap.has(timeline))
+        console.assert(this._timelineViewMap.has(timeline), timeline);
+        if (!this._timelineViewMap.has(timeline))
             return;
 
-        this._showTimelineView(this._discreteTimelineViewMap.get(timeline));
+        this._showTimelineView(this._timelineViewMap.get(timeline));
     },
 
     get allowedNavigationSidebarPanels()
@@ -210,16 +184,16 @@ WebInspector.TimelineContentView.prototype = {
         cookie.type = WebInspector.ContentViewCookieType.Timelines;
 
         if (!this._currentTimelineView || this._currentTimelineView === this._overviewTimelineView)
-            cookie[WebInspector.TimelineContentView.SelectedTimelineTypeCookieKey] = WebInspector.TimelineContentView.OverviewTimelineViewCookieValue;
+            cookie[WebInspector.TimelineRecordingContentView.SelectedTimelineTypeCookieKey] = WebInspector.TimelineRecordingContentView.OverviewTimelineViewCookieValue;
         else
-            cookie[WebInspector.TimelineContentView.SelectedTimelineTypeCookieKey] = this._currentTimelineView.representedObject.type;
+            cookie[WebInspector.TimelineRecordingContentView.SelectedTimelineTypeCookieKey] = this._currentTimelineView.representedObject.type;
     },
 
     restoreFromCookie: function(cookie)
     {
-        var timelineType = cookie[WebInspector.TimelineContentView.SelectedTimelineTypeCookieKey];
+        var timelineType = cookie[WebInspector.TimelineRecordingContentView.SelectedTimelineTypeCookieKey];
 
-        if (timelineType === WebInspector.TimelineContentView.OverviewTimelineViewCookieValue)
+        if (timelineType === WebInspector.TimelineRecordingContentView.OverviewTimelineViewCookieValue)
             this.showOverviewTimelineView();
         else
             this.showTimelineViewForTimeline(this.representedObject.timelines.get(timelineType));
@@ -285,7 +259,7 @@ WebInspector.TimelineContentView.prototype = {
 
     _pathComponentSelected: function(event)
     {
-        WebInspector.timelineSidebarPanel.showTimelineViewForType(event.data.pathComponent.representedObject.type);
+        WebInspector.timelineSidebarPanel.showTimelineViewForTimeline(event.data.pathComponent.representedObject);
     },
 
     _timelineViewSelectionPathComponentsDidChange: function()
@@ -317,7 +291,7 @@ WebInspector.TimelineContentView.prototype = {
         if (this._currentTimelineView) {
             this._currentTimelineView.addEventListener(WebInspector.TimelineView.Event.SelectionPathComponentsDidChange, this._timelineViewSelectionPathComponentsDidChange, this);
 
-            this._viewContainer.appendChild(this._currentTimelineView.element);
+            this._viewContainerElement.appendChild(this._currentTimelineView.element);
 
             this._currentTimelineView.startTime = this._timelineOverview.selectionStartTime;
             this._currentTimelineView.endTime = this._timelineOverview.selectionStartTime + this._timelineOverview.selectionDuration;
@@ -366,7 +340,7 @@ WebInspector.TimelineContentView.prototype = {
             this._timelineOverview.selectionStartTime = startTime + selectionOffset;
 
             this._overviewTimelineView.zeroTime = startTime;
-            for (var timelineView of this._discreteTimelineViewMap.values())
+            for (var timelineView of this._timelineViewMap.values())
                 timelineView.zeroTime = startTime;
 
             delete this._startTimeNeedsReset;
@@ -415,7 +389,7 @@ WebInspector.TimelineContentView.prototype = {
         if (this._waitingToResetCurrentTime) {
             // Did not get any event while waiting for the current time, but we should stop waiting.
             this._recording.removeEventListener(WebInspector.TimelineRecording.Event.TimesUpdated, this._recordingTimesUpdated, this);
-            this._waitingToResetCurrentTime = false;            
+            this._waitingToResetCurrentTime = false;
         }
     },
 
@@ -426,7 +400,8 @@ WebInspector.TimelineContentView.prototype = {
 
     _capturingStopped: function(event)
     {
-        this._stopUpdatingCurrentTime();
+        if (this._updating)
+            this._stopUpdatingCurrentTime();
     },
 
     _debuggerPaused: function(event)
@@ -471,6 +446,58 @@ WebInspector.TimelineContentView.prototype = {
         this._recording.reset();
     },
 
+    _timelineAdded: function(timelineOrEvent)
+    {
+        var timeline = timelineOrEvent;
+        if (!(timeline instanceof WebInspector.Timeline))
+            timeline = timelineOrEvent.data.timeline;
+
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(!this._timelineViewMap.has(timeline), timeline);
+
+        this._timelineViewMap.set(timeline, new WebInspector.TimelineView(timeline));
+
+        var pathComponent = new WebInspector.HierarchicalPathComponent(timeline.displayName, timeline.iconClassName, timeline);
+        pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
+        this._pathComponentMap.set(timeline, pathComponent);
+
+        this._timelineCountChanged();
+    },
+
+    _timelineRemoved: function(event)
+    {
+        var timeline = event.data.timeline;
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(this._timelineViewMap.has(timeline), timeline);
+
+        var timelineView = this._timelineViewMap.take(timeline);
+        if (this._currentTimelineView === timelineView)
+            this.showOverviewTimelineView();
+
+        this._pathComponentMap.delete(timeline);
+
+        this._timelineCountChanged();
+    },
+
+    _timelineCountChanged: function()
+    {
+        var previousPathComponent = null;
+        for (var pathComponent of this._pathComponentMap.values()) {
+            if (previousPathComponent) {
+                previousPathComponent.nextSibling = pathComponent;
+                pathComponent.previousSibling = previousPathComponent;
+            }
+
+            previousPathComponent = pathComponent;
+        }
+
+        var timelineCount = this._recording.timelines.size;
+        const timelineHeight = 36;
+        const extraOffset = 22;
+        this._timelineOverview.element.style.height = (timelineCount * timelineHeight + extraOffset) + "px";
+        this._viewContainerElement.style.top = (timelineCount * timelineHeight + extraOffset) + "px";
+    },
+
     _recordingReset: function(event)
     {
         this._currentTime = NaN;
@@ -487,12 +514,10 @@ WebInspector.TimelineContentView.prototype = {
         this._recording.removeEventListener(WebInspector.TimelineRecording.Event.TimesUpdated, this._recordingTimesUpdated, this);
         this._waitingToResetCurrentTime = false;
 
+        this._timelineOverview.reset();
         this._overviewTimelineView.reset();
-        for (var timelineView of this._discreteTimelineViewMap.values())
+        for (var timelineView of this._timelineViewMap.values())
             timelineView.reset();
-
-        for (var timelineOverviewGraph of this._discreteTimelineOverviewGraphMap.values())
-            timelineOverviewGraph.reset();
     },
 
     _recordingUnloaded: function(event)
index b53392294241807240a38480e0b5dd0f706445e1..625a3d61de56b75776f0b4e2f615e0bd67f41a40 100644 (file)
@@ -239,7 +239,7 @@ body.mac-platform.legacy .sidebar > .panel.navigation.timeline > :matches(.conte
     top: 175px;
 }
 
-.sidebar > .panel.navigation.timeline.timeline-content-view-showing > .content {
+.sidebar > .panel.navigation.timeline.timeline-recording-content-view-showing > .content {
  /* This hides the scrollbar. The view shows a scrollbar, we don't need two. */
     padding-right: 16px;
     right: -16px;
index 4c70f7b765b0c56135522009a957759bf636ab3f..b664cf7cfe03acb388272b689197a179fb569112 100644 (file)
@@ -34,9 +34,12 @@ WebInspector.TimelineSidebarPanel = function()
 
     this.contentTreeOutlineLabel = "";
 
-    this._timelinesContentContainer = document.createElement("div");
-    this._timelinesContentContainer.classList.add(WebInspector.TimelineSidebarPanel.TimelinesContentContainerStyleClass);
-    this.element.insertBefore(this._timelinesContentContainer, this.element.firstChild);
+    this._timelinesContentContainerElement = document.createElement("div");
+    this._timelinesContentContainerElement.classList.add(WebInspector.TimelineSidebarPanel.TimelinesContentContainerStyleClass);
+    this.element.insertBefore(this._timelinesContentContainerElement, this.element.firstChild);
+
+    this._displayedRecording = null;
+    this._displayedContentView = null;
 
     // Maintain an invisible tree outline containing tree elements for all recordings.
     // The visible recording's tree element is selected when the content view changes.
@@ -45,36 +48,15 @@ WebInspector.TimelineSidebarPanel = function()
     this._recordingsTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
     this._recordingsTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementHiddenStyleClassName);
     this._recordingsTreeOutline.onselect = this._recordingsTreeElementSelected.bind(this);
-    this._timelinesContentContainer.appendChild(this._recordingsTreeOutline.element);
+    this._timelinesContentContainerElement.appendChild(this._recordingsTreeOutline.element);
 
+    // Maintain a tree outline with tree elements for each timeline of the selected recording.
     this._timelinesTreeOutline = this.createContentTreeOutline(true, true);
     this._timelinesTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
     this._timelinesTreeOutline.onselect = this._timelinesTreeElementSelected.bind(this);
-    this._timelinesContentContainer.appendChild(this._timelinesTreeOutline.element);
-
-    function createTimelineTreeElement(label, iconClass, identifier)
-    {
-        var treeElement = new WebInspector.GeneralTreeElement([iconClass, WebInspector.TimelineSidebarPanel.LargeIconStyleClass], label, null, identifier);
-
-        const tooltip = WebInspector.UIString("Close %s timeline view").format(label);
-        wrappedSVGDocument(platformImagePath("CloseLarge.svg"), WebInspector.TimelineSidebarPanel.CloseButtonStyleClass, tooltip, function(element) {
-            var button = new WebInspector.TreeElementStatusButton(element);
-            button.addEventListener(WebInspector.TreeElementStatusButton.Event.Clicked, this.showTimelineOverview, this);
-            treeElement.status = button.element;
-        }.bind(this));
+    this._timelinesContentContainerElement.appendChild(this._timelinesTreeOutline.element);
 
-        return treeElement;
-    }
-
-    // Timeline elements are reused; clicking them displays a TimelineView
-    // for the relevant timeline of the active recording.
     this._timelineTreeElementMap = new Map;
-    this._timelineTreeElementMap.set(WebInspector.TimelineRecord.Type.Network, createTimelineTreeElement.call(this, WebInspector.UIString("Network Requests"), WebInspector.TimelineSidebarPanel.NetworkIconStyleClass, WebInspector.TimelineRecord.Type.Network));
-    this._timelineTreeElementMap.set(WebInspector.TimelineRecord.Type.Layout, createTimelineTreeElement.call(this, WebInspector.UIString("Layout & Rendering"), WebInspector.TimelineSidebarPanel.ColorsIconStyleClass, WebInspector.TimelineRecord.Type.Layout));
-    this._timelineTreeElementMap.set(WebInspector.TimelineRecord.Type.Script, createTimelineTreeElement.call(this, WebInspector.UIString("JavaScript & Events"), WebInspector.TimelineSidebarPanel.ScriptIconStyleClass, WebInspector.TimelineRecord.Type.Script));
-
-    for (var timelineTreeElement of this._timelineTreeElementMap.values())
-        this._timelinesTreeOutline.appendChild(timelineTreeElement);
 
     var timelinesTitleBarElement = document.createElement("div");
     timelinesTitleBarElement.textContent = WebInspector.UIString("Timelines");
@@ -157,9 +139,9 @@ WebInspector.TimelineSidebarPanel.StopwatchIconStyleClass = "stopwatch-icon";
 WebInspector.TimelineSidebarPanel.NetworkIconStyleClass = "network-icon";
 WebInspector.TimelineSidebarPanel.ColorsIconStyleClass = "colors-icon";
 WebInspector.TimelineSidebarPanel.ScriptIconStyleClass = "script-icon";
-WebInspector.TimelineSidebarPanel.TimelineContentViewShowingStyleClass = "timeline-content-view-showing";
+WebInspector.TimelineSidebarPanel.TimelineRecordingContentViewShowingStyleClass = "timeline-recording-content-view-showing";
 
-WebInspector.TimelineSidebarPanel.ShowingTimelineContentViewCookieKey = "timeline-sidebar-panel-showing-timeline-content-view";
+WebInspector.TimelineSidebarPanel.ShowingTimelineRecordingContentViewCookieKey = "timeline-sidebar-panel-showing-timeline-recording-content-view";
 WebInspector.TimelineSidebarPanel.SelectedTimelineViewIdentifierCookieKey = "timeline-sidebar-panel-selected-timeline-view-identifier";
 WebInspector.TimelineSidebarPanel.OverviewTimelineIdentifierCookieValue = "overview";
 
@@ -173,13 +155,13 @@ WebInspector.TimelineSidebarPanel.prototype = {
     {
         WebInspector.NavigationSidebarPanel.prototype.shown.call(this);
 
-        if (this._activeContentView)
-            WebInspector.contentBrowser.showContentView(this._activeContentView);
+        if (this._displayedContentView)
+            WebInspector.contentBrowser.showContentView(this._displayedContentView);
     },
 
     showDefaultContentView: function()
     {
-        if (this._activeContentView)
+        if (this._displayedContentView)
             this.showTimelineOverview();
     },
 
@@ -193,6 +175,13 @@ WebInspector.TimelineSidebarPanel.prototype = {
         if (representedObject instanceof WebInspector.TimelineRecording)
             return this._recordingTreeElementMap.get(representedObject);
 
+        // This fails if the timeline does not belong to the selected recording.
+        if (representedObject instanceof WebInspector.Timeline) {
+            var foundTreeElement = this._timelineTreeElementMap.get(representedObject);
+            if (foundTreeElement)
+                return foundTreeElement;
+        }
+
         // The main resource is used as the representedObject instead of Frame in our tree.
         if (representedObject instanceof WebInspector.Frame)
             representedObject = representedObject.mainResource;
@@ -215,6 +204,11 @@ WebInspector.TimelineSidebarPanel.prototype = {
                 if (candidateRepresentedObject.sourceCode === representedObject)
                     return true;
                 return false;
+            } else if (candidateRepresentedObject instanceof WebInspector.Timeline && representedObject instanceof WebInspector.Timeline) {
+                // Reopen to the same timeline, even if a different parent recording is currently shown.
+                if (candidateRepresentedObject.type === representedObject.type)
+                    return true;
+                return false;
             }
 
             if (candidateRepresentedObject instanceof WebInspector.TimelineRecord) {
@@ -265,20 +259,19 @@ WebInspector.TimelineSidebarPanel.prototype = {
         if (this._timelinesTreeOutline.selectedTreeElement)
             this._timelinesTreeOutline.selectedTreeElement.deselect();
 
-        this._activeContentView.showOverviewTimelineView();
-        WebInspector.contentBrowser.showContentView(this._activeContentView);
+        this._displayedContentView.showOverviewTimelineView();
+        WebInspector.contentBrowser.showContentView(this._displayedContentView);
     },
 
-    showTimelineViewForType: function(timelineType)
+    showTimelineViewForTimeline: function(timeline)
     {
-        console.assert(this._timelineTreeElementMap.has(timelineType), timelineType);
-        if (!this._timelineTreeElementMap.has(timelineType))
-            return;
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(this._timelineTreeElementMap.has(timeline), "Cannot show timeline because it does not belong to the shown recording.", timeline);
 
         // Defer showing the relevant timeline to the onselect handler of the timelines tree element.
         const wasSelectedByUser = true;
         const shouldSuppressOnSelect = false;
-        this._timelineTreeElementMap.get(timelineType).select(true, wasSelectedByUser, shouldSuppressOnSelect, true);
+        this._timelineTreeElementMap.get(timeline).select(true, wasSelectedByUser, shouldSuppressOnSelect, true);
     },
 
     // Protected
@@ -287,7 +280,7 @@ WebInspector.TimelineSidebarPanel.prototype = {
     {
         WebInspector.NavigationSidebarPanel.prototype.updateFilter.call(this);
 
-        this._activeContentView.filterDidChange();
+        this._displayedContentView.filterDidChange();
     },
 
     hasCustomFilters: function()
@@ -297,22 +290,22 @@ WebInspector.TimelineSidebarPanel.prototype = {
 
     matchTreeElementAgainstCustomFilters: function(treeElement)
     {
-        if (!this._activeContentView)
+        if (!this._displayedContentView)
             return true;
 
-        return this._activeContentView.matchTreeElementAgainstCustomFilters(treeElement);
+        return this._displayedContentView.matchTreeElementAgainstCustomFilters(treeElement);
     },
 
     canShowDifferentContentView: function()
     {
-        return !this.restoringState || !this._restoredShowingTimelineContentView;
+        return !this.restoringState || !this._restoredShowingTimelineRecordingContentView;
     },
 
     saveStateToCookie: function(cookie)
     {
         console.assert(cookie);
 
-        cookie[WebInspector.TimelineSidebarPanel.ShowingTimelineContentViewCookieKey] = WebInspector.contentBrowser.currentContentView instanceof WebInspector.TimelineContentView;
+        cookie[WebInspector.TimelineSidebarPanel.ShowingTimelineRecordingContentViewCookieKey] = WebInspector.contentBrowser.currentContentView instanceof WebInspector.TimelineRecordingContentView;
 
         var selectedTreeElement = this._timelinesTreeOutline.selectedTreeElement;
         if (selectedTreeElement)
@@ -327,20 +320,22 @@ WebInspector.TimelineSidebarPanel.prototype = {
     {
         console.assert(cookie);
 
-        // The _activeContentView is not ready on initial load, so delay the restore.
+        // The _displayedContentView is not ready on initial load, so delay the restore.
         // This matches the delayed work in the WebInspector.TimelineSidebarPanel constructor.
-        if (!this._activeContentView) {
+        if (!this._displayedContentView) {
             setTimeout(this.restoreStateFromCookie.bind(this, cookie, relaxedMatchDelay), 0);
             return;
         }
 
-        this._restoredShowingTimelineContentView = cookie[WebInspector.TimelineSidebarPanel.ShowingTimelineContentViewCookieKey];
+        this._restoredShowingTimelineRecordingContentView = cookie[WebInspector.TimelineSidebarPanel.ShowingTimelineRecordingContentViewCookieKey];
 
         var selectedTimelineViewIdentifier = cookie[WebInspector.TimelineSidebarPanel.SelectedTimelineViewIdentifierCookieKey];
         if (!selectedTimelineViewIdentifier || selectedTimelineViewIdentifier === WebInspector.TimelineSidebarPanel.OverviewTimelineIdentifierCookieValue)
             this.showTimelineOverview();
+        else if (this._displayedRecording.timelines.has(selectedTimelineViewIdentifier))
+            this.showTimelineViewForTimeline(this._displayedRecording.timelines.get(selectedTimelineViewIdentifier));
         else
-            this.showTimelineViewForType(selectedTimelineViewIdentifier);
+            this.showTimelineOverview();
 
         // Don't call NavigationSidebarPanel.restoreStateFromCookie, because it tries to match based
         // on type only as a last resort. This would cause the first recording to be reselected on reload.
@@ -353,14 +348,14 @@ WebInspector.TimelineSidebarPanel.prototype = {
         console.assert(treeElement.representedObject instanceof WebInspector.TimelineRecording);
         console.assert(!selectedByUser, "Recording tree elements should be hidden and only programmatically selectable.");
 
-        this._activeContentView = WebInspector.contentBrowser.contentViewForRepresentedObject(treeElement.representedObject);
+        this._recordingSelected(treeElement.representedObject);
 
         // Deselect or re-select the timeline tree element for the timeline view being displayed.
-        var currentTimelineView = this._activeContentView.currentTimelineView;
+        var currentTimelineView = this._displayedContentView.currentTimelineView;
         if (currentTimelineView && currentTimelineView.representedObject instanceof WebInspector.Timeline) {
             const wasSelectedByUser = false; // This is a simulated selection.
             const shouldSuppressOnSelect = false;
-            this._timelineTreeElementMap.get(currentTimelineView.representedObject.type).select(true, wasSelectedByUser, shouldSuppressOnSelect, true);
+            this._timelineTreeElementMap.get(currentTimelineView.representedObject).select(true, wasSelectedByUser, shouldSuppressOnSelect, true);
         } else if (this._timelinesTreeOutline.selectedTreeElement)
             this._timelinesTreeOutline.selectedTreeElement.deselect();
 
@@ -373,20 +368,22 @@ WebInspector.TimelineSidebarPanel.prototype = {
 
         // If not selected by user, then this selection merely synced the tree element with the content view's contents.
         if (!selectedByUser) {
-            console.assert(this._activeContentView.currentTimelineView.representedObject.type === treeElement.representedObject);
+            console.assert(this._displayedContentView.currentTimelineView.representedObject === treeElement.representedObject);
             return;
         }
 
-        var timelineType = treeElement.representedObject;
-        var timeline = this._activeContentView.representedObject.timelines.get(timelineType);
-        this._activeContentView.showTimelineViewForTimeline(timeline);
-        WebInspector.contentBrowser.showContentView(this._activeContentView);
+        var timeline = treeElement.representedObject;
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(this._displayedRecording.timelines.get(timeline.type) === timeline, timeline);
+
+        this._displayedContentView.showTimelineViewForTimeline(timeline);
+        WebInspector.contentBrowser.showContentView(this._displayedContentView);
     },
 
     _contentBrowserCurrentContentViewDidChange: function(event)
     {
-        var didShowTimelineContentView = WebInspector.contentBrowser.currentContentView instanceof WebInspector.TimelineContentView;
-        this.element.classList.toggle(WebInspector.TimelineSidebarPanel.TimelineContentViewShowingStyleClass, didShowTimelineContentView);
+        var didShowTimelineRecordingContentView = WebInspector.contentBrowser.currentContentView instanceof WebInspector.TimelineRecordingContentView;
+        this.element.classList.toggle(WebInspector.TimelineSidebarPanel.TimelineRecordingContentViewShowingStyleClass, didShowTimelineRecordingContentView);
     },
 
     _capturingStarted: function(event)
@@ -410,6 +407,11 @@ WebInspector.TimelineSidebarPanel.prototype = {
         this._recordingTreeElementMap.set(recording, recordingTreeElement);
         this._recordingsTreeOutline.appendChild(recordingTreeElement);
 
+        this._recordingCountChanged();
+    },
+
+    _recordingCountChanged: function()
+    {
         var previousTreeElement = null;
         for (var treeElement of this._recordingTreeElementMap.values()) {
             if (previousTreeElement) {
@@ -421,12 +423,93 @@ WebInspector.TimelineSidebarPanel.prototype = {
         }
     },
 
-    _recordingLoaded: function()
+    _recordingSelected: function(recording)
     {
-        this._activeContentView = WebInspector.contentBrowser.contentViewForRepresentedObject(WebInspector.timelineManager.activeRecording);
+        console.assert(recording instanceof WebInspector.TimelineRecording, recording);
 
+        var oldRecording = this._displayedRecording || null;
+        if (oldRecording) {
+            oldRecording.removeEventListener(WebInspector.TimelineRecording.Event.TimelineAdded, this._timelineAdded, this);
+            oldRecording.removeEventListener(WebInspector.TimelineRecording.Event.TimelineRemoved, this._timelineRemoved, this);
+
+            // Destroy tree elements in one operation to avoid unnecessary fixups.
+            this._timelinesTreeOutline.removeChildren();
+            this._timelineTreeElementMap.clear();
+        }
+
+        this._displayedRecording = recording;
+        this._displayedRecording.addEventListener(WebInspector.TimelineRecording.Event.TimelineAdded, this._timelineAdded, this);
+        this._displayedRecording.addEventListener(WebInspector.TimelineRecording.Event.TimelineRemoved, this._timelineRemoved, this);
+
+        for (var timeline of recording.timelines.values())
+            this._timelineAdded(timeline);
+
+        this._displayedContentView = WebInspector.contentBrowser.contentViewForRepresentedObject(this._displayedRecording);
         if (this.selected)
-            WebInspector.contentBrowser.showContentView(this._activeContentView);
+            WebInspector.contentBrowser.showContentView(this._displayedContentView);
+    },
+
+    _recordingLoaded: function(event)
+    {
+        this._recordingSelected(WebInspector.timelineManager.activeRecording);
+    },
+
+    _timelineAdded: function(timelineOrEvent)
+    {
+        var timeline = timelineOrEvent;
+        if (!(timeline instanceof WebInspector.Timeline))
+            timeline = timelineOrEvent.data.timeline;
+
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(!this._timelineTreeElementMap.has(timeline), timeline);
+
+        var timelineTreeElement = new WebInspector.GeneralTreeElement([timeline.iconClassName, WebInspector.TimelineSidebarPanel.LargeIconStyleClass], timeline.displayName, null, timeline);
+        const tooltip = WebInspector.UIString("Close %s timeline view").format(timeline.displayName);
+        wrappedSVGDocument(platformImagePath("CloseLarge.svg"), WebInspector.TimelineSidebarPanel.CloseButtonStyleClass, tooltip, function(element) {
+            var button = new WebInspector.TreeElementStatusButton(element);
+            button.addEventListener(WebInspector.TreeElementStatusButton.Event.Clicked, this.showTimelineOverview, this);
+            timelineTreeElement.status = button.element;
+        }.bind(this));
+        this._timelinesTreeOutline.appendChild(timelineTreeElement);
+        this._timelineTreeElementMap.set(timeline, timelineTreeElement);
+
+        this._timelineCountChanged();
+    },
+
+    _timelineRemoved: function(event)
+    {
+        var timeline = event.data.timeline;
+        console.assert(timeline instanceof WebInspector.Timeline, timeline);
+        console.assert(this._timelineTreeElementMap.has(timeline), timeline);
+
+        var timelineTreeElement = this._timelineTreeElementMap.take(timeline);
+        const shouldSuppressOnDeselect = false;
+        const shouldSuppressSelectSibling = true;
+        this._timelinesTreeOutline.removeChild(timelineTreeElement, shouldSuppressOnDeselect, shouldSuppressSelectSibling);
+        this._timelineTreeElementMap.delete(timeline);
+
+        this._timelineCountChanged();
+    },
+
+    _timelineCountChanged: function()
+    {
+        var previousTreeElement = null;
+        for (var treeElement of this._timelineTreeElementMap.values()) {
+            if (previousTreeElement) {
+                previousTreeElement.nextSibling = treeElement;
+                treeElement.previousSibling = previousTreeElement;
+            }
+
+            previousTreeElement = treeElement;
+        }
+
+        const timelineHeight = 36;
+        const eventTitleBarOffset = 51;
+        const contentElementOffset = 74;
+        var timelineCount = this._displayedRecording.timelines.size;
+        this._timelinesContentContainerElement.style.height = (timelineHeight * timelineCount) + "px";
+        this._timelineEventsTitleBarElement.style.top = (timelineHeight * timelineCount + eventTitleBarOffset) + "px";
+        this.contentElement.style.top = (timelineHeight * timelineCount + contentElementOffset) + "px";
     },
 
     _recordGlyphMousedOver: function(event)
index 944ab80e3beffcdaab53687a552716dffb8139ac..a2292653a1440fa935c3a63b13d121c2831d21d6 100644 (file)
@@ -36,7 +36,7 @@
     background-image: none;
 }
 
-.panel.navigation.timeline.timeline-content-view-showing > .content > .navigation-sidebar-panel-content-tree-outline {
+.panel.navigation.timeline.timeline-recording-content-view-showing > .content > .navigation-sidebar-panel-content-tree-outline {
     background-image: linear-gradient(to bottom, transparent, transparent 50%, rgba(0, 0, 0, 0.03) 50%, rgba(0, 0, 0, 0.03));
     background-size: 100% 40px;
 }