summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js817
1 files changed, 817 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js b/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
new file mode 100644
index 000000000..c4266d0c0
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 University of Washington.
+ *
+ * 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.TimelineRecordingContentView = class TimelineRecordingContentView extends WebInspector.ContentView
+{
+ constructor(recording)
+ {
+ super(recording);
+
+ this._recording = recording;
+
+ this.element.classList.add("timeline-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);
+ this._timelineOverview.addEventListener(WebInspector.TimelineOverview.Event.EditingInstrumentsDidChange, this._editingInstrumentsDidChange, this);
+ this.addSubview(this._timelineOverview);
+
+ const disableBackForward = true;
+ const disableFindBanner = true;
+ this._timelineContentBrowser = new WebInspector.ContentBrowser(null, this, disableBackForward, disableFindBanner);
+ this._timelineContentBrowser.addEventListener(WebInspector.ContentBrowser.Event.CurrentContentViewDidChange, this._currentContentViewDidChange, this);
+
+ this._entireRecordingPathComponent = this._createTimelineRangePathComponent(WebInspector.UIString("Entire Recording"));
+ this._timelineSelectionPathComponent = this._createTimelineRangePathComponent();
+ this._timelineSelectionPathComponent.previousSibling = this._entireRecordingPathComponent;
+ this._selectedTimeRangePathComponent = this._entireRecordingPathComponent;
+
+ this._filterBarNavigationItem = new WebInspector.FilterBarNavigationItem;
+ this._filterBarNavigationItem.filterBar.placeholder = WebInspector.UIString("Filter Records");
+ this._filterBarNavigationItem.filterBar.addEventListener(WebInspector.FilterBar.Event.FilterDidChange, this._filterDidChange, this);
+ this._timelineContentBrowser.navigationBar.addNavigationItem(this._filterBarNavigationItem);
+ this.addSubview(this._timelineContentBrowser);
+
+ let clearImageDimensions = WebInspector.Platform.name === "mac" ? 16 : 15;
+ this._clearTimelineNavigationItem = new WebInspector.ButtonNavigationItem("clear-timeline", WebInspector.UIString("Clear Timeline"), "Images/NavigationItemClear.svg", clearImageDimensions, clearImageDimensions);
+ this._clearTimelineNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._clearTimeline, this);
+
+ this._overviewTimelineView = new WebInspector.OverviewTimelineView(recording);
+ this._overviewTimelineView.secondsPerPixel = this._timelineOverview.secondsPerPixel;
+
+ this._progressView = new WebInspector.TimelineRecordingProgressView;
+ this._timelineContentBrowser.addSubview(this._progressView);
+
+ this._timelineViewMap = new Map;
+ this._pathComponentMap = new Map;
+
+ this._updating = false;
+ this._currentTime = NaN;
+ this._discontinuityStartTime = NaN;
+ this._lastUpdateTimestamp = NaN;
+ this._startTimeNeedsReset = true;
+ this._renderingFrameTimeline = null;
+
+ this._recording.addEventListener(WebInspector.TimelineRecording.Event.InstrumentAdded, this._instrumentAdded, this);
+ this._recording.addEventListener(WebInspector.TimelineRecording.Event.InstrumentRemoved, this._instrumentRemoved, 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);
+
+ WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerPaused, this);
+ WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerResumed, this);
+
+ WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
+ WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SupplementalRepresentedObjectsDidChange, this._contentViewSupplementalRepresentedObjectsDidChange, this);
+
+ WebInspector.TimelineView.addEventListener(WebInspector.TimelineView.Event.RecordWasFiltered, this._recordWasFiltered, this);
+
+ WebInspector.notifications.addEventListener(WebInspector.Notification.VisibilityStateDidChange, this._inspectorVisibilityStateChanged, this);
+
+ for (let instrument of this._recording.instruments)
+ this._instrumentAdded(instrument);
+
+ this.showOverviewTimelineView();
+ }
+
+ // Public
+
+ showOverviewTimelineView()
+ {
+ this._timelineContentBrowser.showContentView(this._overviewTimelineView);
+ }
+
+ showTimelineViewForTimeline(timeline)
+ {
+ console.assert(timeline instanceof WebInspector.Timeline, timeline);
+ console.assert(this._timelineViewMap.has(timeline), timeline);
+ if (!this._timelineViewMap.has(timeline))
+ return;
+
+ this._timelineContentBrowser.showContentView(this._timelineViewMap.get(timeline));
+ }
+
+ get supportsSplitContentBrowser()
+ {
+ // The layout of the overview and split content browser don't work well.
+ return false;
+ }
+
+ get selectionPathComponents()
+ {
+ if (!this._timelineContentBrowser.currentContentView)
+ return [];
+
+ let pathComponents = [];
+ let representedObject = this._timelineContentBrowser.currentContentView.representedObject;
+ if (representedObject instanceof WebInspector.Timeline)
+ pathComponents.push(this._pathComponentMap.get(representedObject));
+
+ pathComponents.push(this._selectedTimeRangePathComponent);
+ return pathComponents;
+ }
+
+ get supplementalRepresentedObjects()
+ {
+ if (!this._timelineContentBrowser.currentContentView)
+ return [];
+ return this._timelineContentBrowser.currentContentView.supplementalRepresentedObjects;
+ }
+
+ get navigationItems()
+ {
+ return [this._clearTimelineNavigationItem];
+ }
+
+ get handleCopyEvent()
+ {
+ let currentContentView = this._timelineContentBrowser.currentContentView;
+ return currentContentView && typeof currentContentView.handleCopyEvent === "function" ? currentContentView.handleCopyEvent.bind(currentContentView) : null;
+ }
+
+ get supportsSave()
+ {
+ let currentContentView = this._timelineContentBrowser.currentContentView;
+ return currentContentView && currentContentView.supportsSave;
+ }
+
+ get saveData()
+ {
+ let currentContentView = this._timelineContentBrowser.currentContentView;
+ return currentContentView && currentContentView.saveData || null;
+ }
+
+ get currentTimelineView()
+ {
+ return this._timelineContentBrowser.currentContentView;
+ }
+
+ shown()
+ {
+ super.shown();
+
+ this._timelineOverview.shown();
+ this._timelineContentBrowser.shown();
+ this._clearTimelineNavigationItem.enabled = !this._recording.readonly && !isNaN(this._recording.startTime);
+
+ this._currentContentViewDidChange();
+
+ if (!this._updating && WebInspector.timelineManager.activeRecording === this._recording && WebInspector.timelineManager.isCapturing())
+ this._startUpdatingCurrentTime(this._currentTime);
+ }
+
+ hidden()
+ {
+ super.hidden();
+
+ this._timelineOverview.hidden();
+ this._timelineContentBrowser.hidden();
+
+ if (this._updating)
+ this._stopUpdatingCurrentTime();
+ }
+
+ closed()
+ {
+ super.closed();
+
+ this._timelineContentBrowser.contentViewContainer.closeAllContentViews();
+
+ this._recording.removeEventListener(null, null, this);
+
+ WebInspector.timelineManager.removeEventListener(null, null, this);
+ WebInspector.debuggerManager.removeEventListener(null, null, this);
+ WebInspector.ContentView.removeEventListener(null, null, this);
+ }
+
+ canGoBack()
+ {
+ return this._timelineContentBrowser.canGoBack();
+ }
+
+ canGoForward()
+ {
+ return this._timelineContentBrowser.canGoForward();
+ }
+
+ goBack()
+ {
+ this._timelineContentBrowser.goBack();
+ }
+
+ goForward()
+ {
+ this._timelineContentBrowser.goForward();
+ }
+
+ // ContentBrowser delegate
+
+ contentBrowserTreeElementForRepresentedObject(contentBrowser, representedObject)
+ {
+ if (!(representedObject instanceof WebInspector.Timeline) && !(representedObject instanceof WebInspector.TimelineRecording))
+ return null;
+
+ let iconClassName;
+ let title;
+ if (representedObject instanceof WebInspector.Timeline) {
+ iconClassName = WebInspector.TimelineTabContentView.iconClassNameForTimelineType(representedObject.type);
+ title = WebInspector.UIString("Details");
+ } else {
+ iconClassName = WebInspector.TimelineTabContentView.StopwatchIconStyleClass;
+ title = WebInspector.UIString("Overview");
+ }
+
+ const hasChildren = false;
+ 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)
+ {
+ let newViewMode;
+ let timelineView = this.currentTimelineView;
+ if (timelineView && timelineView.representedObject.type === WebInspector.TimelineRecord.Type.RenderingFrame)
+ newViewMode = WebInspector.TimelineOverview.ViewMode.RenderingFrames;
+ else
+ newViewMode = WebInspector.TimelineOverview.ViewMode.Timelines;
+
+ this._timelineOverview.viewMode = newViewMode;
+ this._updateTimelineOverviewHeight();
+ this._updateProgressView();
+ this._updateFilterBar();
+
+ if (timelineView) {
+ this._updateTimelineViewTimes(timelineView);
+ this._filterDidChange();
+
+ let timeline = null;
+ if (timelineView.representedObject instanceof WebInspector.Timeline)
+ timeline = timelineView.representedObject;
+
+ this._timelineOverview.selectedTimeline = timeline;
+ }
+
+ this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+ this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
+ }
+
+ _timelinePathComponentSelected(event)
+ {
+ let selectedTimeline = event.data.pathComponent.representedObject;
+ this.showTimelineViewForTimeline(selectedTimeline);
+ }
+
+ _timeRangePathComponentSelected(event)
+ {
+ let selectedPathComponent = event.data.pathComponent;
+ if (selectedPathComponent === this._selectedTimeRangePathComponent)
+ return;
+
+ let timelineRuler = this._timelineOverview.timelineRuler;
+ if (selectedPathComponent === this._entireRecordingPathComponent)
+ timelineRuler.selectEntireRange();
+ else {
+ let timelineRange = selectedPathComponent.representedObject;
+ timelineRuler.selectionStartTime = timelineRuler.zeroTime + timelineRange.startValue;
+ timelineRuler.selectionEndTime = timelineRuler.zeroTime + timelineRange.endValue;
+ }
+ }
+
+ _contentViewSelectionPathComponentDidChange(event)
+ {
+ if (!this.visible)
+ return;
+
+ if (event.target !== this._timelineContentBrowser.currentContentView)
+ return;
+
+ this._updateFilterBar();
+
+ this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+
+ if (this.currentTimelineView === this._overviewTimelineView)
+ return;
+
+ let record = null;
+ if (this.currentTimelineView.selectionPathComponents) {
+ let recordPathComponent = this.currentTimelineView.selectionPathComponents.find((element) => element.representedObject instanceof WebInspector.TimelineRecord);
+ record = recordPathComponent ? recordPathComponent.representedObject : null;
+ }
+
+ this._timelineOverview.selectRecord(event.target.representedObject, record);
+ }
+
+ _contentViewSupplementalRepresentedObjectsDidChange(event)
+ {
+ if (event.target !== this._timelineContentBrowser.currentContentView)
+ return;
+ this.dispatchEventToListeners(WebInspector.ContentView.Event.SupplementalRepresentedObjectsDidChange);
+ }
+
+ _inspectorVisibilityStateChanged()
+ {
+ if (WebInspector.timelineManager.activeRecording !== this._recording)
+ return;
+
+ // Stop updating since the results won't be rendered anyway.
+ if (!WebInspector.visible && this._updating) {
+ this._stopUpdatingCurrentTime();
+ return;
+ }
+
+ // Nothing else to do if the current time was not being updated.
+ if (!WebInspector.visible)
+ return;
+
+ let {startTime, endTime} = this.representedObject;
+ if (!WebInspector.timelineManager.isCapturing()) {
+ // Force the overview to render data from the entire recording.
+ // This is necessary if the recording was started when the inspector was not
+ // visible because the views were never updated with currentTime/endTime.
+ this._updateTimes(startTime, endTime, endTime);
+ return;
+ }
+
+ this._startUpdatingCurrentTime(endTime);
+ }
+
+ _update(timestamp)
+ {
+ // FIXME: <https://webkit.org/b/153634> Web Inspector: some background tabs think they are the foreground tab and do unnecessary work
+ if (!(WebInspector.tabBrowser.selectedTabContentView instanceof WebInspector.TimelineTabContentView))
+ return;
+
+ if (this._waitingToResetCurrentTime) {
+ requestAnimationFrame(this._updateCallback);
+ return;
+ }
+
+ var startTime = this._recording.startTime;
+ var currentTime = this._currentTime || startTime;
+ var endTime = this._recording.endTime;
+ var timespanSinceLastUpdate = (timestamp - this._lastUpdateTimestamp) / 1000 || 0;
+
+ currentTime += timespanSinceLastUpdate;
+
+ this._updateTimes(startTime, currentTime, endTime);
+
+ // Only stop updating if the current time is greater than the end time, or the end time is NaN.
+ // The recording end time will be NaN if no records were added.
+ if (!this._updating && (currentTime >= endTime || isNaN(endTime))) {
+ if (this.visible)
+ this._lastUpdateTimestamp = NaN;
+ return;
+ }
+
+ this._lastUpdateTimestamp = timestamp;
+
+ requestAnimationFrame(this._updateCallback);
+ }
+
+ _updateTimes(startTime, currentTime, endTime)
+ {
+ if (this._startTimeNeedsReset && !isNaN(startTime)) {
+ this._timelineOverview.startTime = startTime;
+ this._overviewTimelineView.zeroTime = startTime;
+ for (let timelineView of this._timelineViewMap.values())
+ timelineView.zeroTime = startTime;
+
+ this._startTimeNeedsReset = false;
+ }
+
+ this._timelineOverview.endTime = Math.max(endTime, currentTime);
+
+ this._currentTime = currentTime;
+ this._timelineOverview.currentTime = currentTime;
+
+ if (this.currentTimelineView)
+ this._updateTimelineViewTimes(this.currentTimelineView);
+
+ // Force a layout now since we are already in an animation frame and don't need to delay it until the next.
+ this._timelineOverview.updateLayoutIfNeeded();
+ if (this.currentTimelineView)
+ this.currentTimelineView.updateLayoutIfNeeded();
+ }
+
+ _startUpdatingCurrentTime(startTime)
+ {
+ console.assert(!this._updating);
+ if (this._updating)
+ return;
+
+ // Don't update the current time if the Inspector is not visible, as the requestAnimationFrames won't work.
+ if (!WebInspector.visible)
+ return;
+
+ if (typeof startTime === "number")
+ this._currentTime = startTime;
+ else if (!isNaN(this._currentTime)) {
+ // This happens when you stop and later restart recording.
+ // COMPATIBILITY (iOS 9): Timeline.recordingStarted events did not include a timestamp.
+ // We likely need to jump into the future to a better current time which we can
+ // ascertained from a new incoming timeline record, so we wait for a Timeline to update.
+ console.assert(!this._waitingToResetCurrentTime);
+ this._waitingToResetCurrentTime = true;
+ this._recording.addEventListener(WebInspector.TimelineRecording.Event.TimesUpdated, this._recordingTimesUpdated, this);
+ }
+
+ this._updating = true;
+
+ if (!this._updateCallback)
+ this._updateCallback = this._update.bind(this);
+
+ requestAnimationFrame(this._updateCallback);
+ }
+
+ _stopUpdatingCurrentTime()
+ {
+ console.assert(this._updating);
+ this._updating = false;
+
+ 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;
+ }
+ }
+
+ _capturingStarted(event)
+ {
+ this._updateProgressView();
+
+ let startTime = event.data.startTime;
+ if (!this._updating)
+ this._startUpdatingCurrentTime(startTime);
+ this._clearTimelineNavigationItem.enabled = !this._recording.readonly;
+
+ // A discontinuity occurs when the recording is stopped and resumed at
+ // a future time. Capturing started signals the end of the current
+ // discontinuity, if one exists.
+ if (!isNaN(this._discontinuityStartTime)) {
+ this._recording.addDiscontinuity(this._discontinuityStartTime, startTime);
+ this._discontinuityStartTime = NaN;
+ }
+ }
+
+ _capturingStopped(event)
+ {
+ this._updateProgressView();
+
+ if (this._updating)
+ this._stopUpdatingCurrentTime();
+
+ if (this.currentTimelineView)
+ this._updateTimelineViewTimes(this.currentTimelineView);
+
+ this._discontinuityStartTime = event.data.endTime || this._currentTime;
+ }
+
+ _debuggerPaused(event)
+ {
+ if (WebInspector.replayManager.sessionState === WebInspector.ReplayManager.SessionState.Replaying)
+ return;
+
+ if (this._updating)
+ this._stopUpdatingCurrentTime();
+ }
+
+ _debuggerResumed(event)
+ {
+ if (WebInspector.replayManager.sessionState === WebInspector.ReplayManager.SessionState.Replaying)
+ return;
+
+ if (!this._updating)
+ this._startUpdatingCurrentTime();
+ }
+
+ _recordingTimesUpdated(event)
+ {
+ if (!this._waitingToResetCurrentTime)
+ return;
+
+ // COMPATIBILITY (iOS 9): Timeline.recordingStarted events did not include a new startTime.
+ // Make the current time be the start time of the last added record. This is the best way
+ // currently to jump to the right period of time after recording starts.
+
+ for (var timeline of this._recording.timelines.values()) {
+ var lastRecord = timeline.records.lastValue;
+ if (!lastRecord)
+ continue;
+ this._currentTime = Math.max(this._currentTime, lastRecord.startTime);
+ }
+
+ this._recording.removeEventListener(WebInspector.TimelineRecording.Event.TimesUpdated, this._recordingTimesUpdated, this);
+ this._waitingToResetCurrentTime = false;
+ }
+
+ _clearTimeline(event)
+ {
+ if (WebInspector.timelineManager.activeRecording === this._recording && WebInspector.timelineManager.isCapturing())
+ WebInspector.timelineManager.stopCapturing();
+
+ this._recording.reset();
+ }
+
+ _updateTimelineOverviewHeight()
+ {
+ if (this._timelineOverview.editingInstruments)
+ this._timelineOverview.element.style.height = "";
+ else {
+ const rulerHeight = 23;
+
+ let styleValue = (rulerHeight + this._timelineOverview.height) + "px";
+ this._timelineOverview.element.style.height = styleValue;
+ this._timelineContentBrowser.element.style.top = styleValue;
+ }
+ }
+
+ _instrumentAdded(instrumentOrEvent)
+ {
+ let instrument = instrumentOrEvent instanceof WebInspector.Instrument ? instrumentOrEvent : instrumentOrEvent.data.instrument;
+ console.assert(instrument instanceof WebInspector.Instrument, instrument);
+
+ let timeline = this._recording.timelineForInstrument(instrument);
+ console.assert(!this._timelineViewMap.has(timeline), timeline);
+
+ this._timelineViewMap.set(timeline, WebInspector.ContentView.createFromRepresentedObject(timeline, {recording: this._recording}));
+ if (timeline.type === WebInspector.TimelineRecord.Type.RenderingFrame)
+ this._renderingFrameTimeline = timeline;
+
+ let displayName = WebInspector.TimelineTabContentView.displayNameForTimelineType(timeline.type);
+ let iconClassName = WebInspector.TimelineTabContentView.iconClassNameForTimelineType(timeline.type);
+ let pathComponent = new WebInspector.HierarchicalPathComponent(displayName, iconClassName, timeline);
+ pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._timelinePathComponentSelected, this);
+ this._pathComponentMap.set(timeline, pathComponent);
+
+ this._timelineCountChanged();
+ }
+
+ _instrumentRemoved(event)
+ {
+ let instrument = event.data.instrument;
+ console.assert(instrument instanceof WebInspector.Instrument);
+
+ let timeline = this._recording.timelineForInstrument(instrument);
+ console.assert(this._timelineViewMap.has(timeline), timeline);
+
+ let timelineView = this._timelineViewMap.take(timeline);
+ if (this.currentTimelineView === timelineView)
+ this.showOverviewTimelineView();
+ if (timeline.type === WebInspector.TimelineRecord.Type.RenderingFrame)
+ this._renderingFrameTimeline = null;
+
+ this._pathComponentMap.delete(timeline);
+
+ this._timelineCountChanged();
+ }
+
+ _timelineCountChanged()
+ {
+ var previousPathComponent = null;
+ for (var pathComponent of this._pathComponentMap.values()) {
+ if (previousPathComponent) {
+ previousPathComponent.nextSibling = pathComponent;
+ pathComponent.previousSibling = previousPathComponent;
+ }
+
+ previousPathComponent = pathComponent;
+ }
+
+ this._updateTimelineOverviewHeight();
+ }
+
+ _recordingReset(event)
+ {
+ for (let timelineView of this._timelineViewMap.values())
+ timelineView.reset();
+
+ this._currentTime = NaN;
+ this._discontinuityStartTime = NaN;
+
+ if (!this._updating) {
+ // Force the time ruler and views to reset to 0.
+ this._startTimeNeedsReset = true;
+ this._updateTimes(0, 0, 0);
+ }
+
+ this._lastUpdateTimestamp = NaN;
+ this._startTimeNeedsReset = true;
+
+ this._recording.removeEventListener(WebInspector.TimelineRecording.Event.TimesUpdated, this._recordingTimesUpdated, this);
+ this._waitingToResetCurrentTime = false;
+
+ this._timelineOverview.reset();
+ this._overviewTimelineView.reset();
+ this._clearTimelineNavigationItem.enabled = false;
+ }
+
+ _recordingUnloaded(event)
+ {
+ console.assert(!this._updating);
+
+ WebInspector.timelineManager.removeEventListener(WebInspector.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
+ WebInspector.timelineManager.removeEventListener(WebInspector.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
+ }
+
+ _timeRangeSelectionChanged(event)
+ {
+ console.assert(this.currentTimelineView);
+ if (!this.currentTimelineView)
+ return;
+
+ this._updateTimelineViewTimes(this.currentTimelineView);
+
+ let selectedPathComponent;
+ if (this._timelineOverview.timelineRuler.entireRangeSelected)
+ selectedPathComponent = this._entireRecordingPathComponent;
+ else {
+ let timelineRange = this._timelineSelectionPathComponent.representedObject;
+ timelineRange.startValue = this.currentTimelineView.startTime;
+ timelineRange.endValue = this.currentTimelineView.endTime;
+
+ if (!(this.currentTimelineView instanceof WebInspector.RenderingFrameTimelineView)) {
+ timelineRange.startValue -= this.currentTimelineView.zeroTime;
+ timelineRange.endValue -= this.currentTimelineView.zeroTime;
+ }
+
+ this._updateTimeRangePathComponents();
+ selectedPathComponent = this._timelineSelectionPathComponent;
+ }
+
+ if (this._selectedTimeRangePathComponent !== selectedPathComponent) {
+ this._selectedTimeRangePathComponent = selectedPathComponent;
+ this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+ }
+ }
+
+ _recordSelected(event)
+ {
+ let {record, timeline} = event.data;
+ let timelineView = this._timelineViewMap.get(timeline);
+
+ if (record && timelineView !== this.currentTimelineView)
+ this.showTimelineViewForTimeline(timeline);
+
+ timelineView.selectRecord(record);
+ }
+
+ _timelineSelected()
+ {
+ let timeline = this._timelineOverview.selectedTimeline;
+ if (timeline)
+ this.showTimelineViewForTimeline(timeline);
+ else
+ this.showOverviewTimelineView();
+ }
+
+ _updateTimeRangePathComponents()
+ {
+ let timelineRange = this._timelineSelectionPathComponent.representedObject;
+ let startValue = timelineRange.startValue;
+ let endValue = timelineRange.endValue;
+ if (isNaN(startValue) || isNaN(endValue)) {
+ this._entireRecordingPathComponent.nextSibling = null;
+ return;
+ }
+
+ this._entireRecordingPathComponent.nextSibling = this._timelineSelectionPathComponent;
+
+ let displayName;
+ if (this._timelineOverview.viewMode === WebInspector.TimelineOverview.ViewMode.Timelines) {
+ let selectionStart = Number.secondsToString(startValue, true);
+ let selectionEnd = Number.secondsToString(endValue, true);
+ displayName = WebInspector.UIString("%s \u2013 %s").format(selectionStart, selectionEnd);
+ } else {
+ startValue += 1; // Convert index to frame number.
+ if (startValue === endValue)
+ displayName = WebInspector.UIString("Frame %d").format(startValue);
+ else
+ displayName = WebInspector.UIString("Frames %d \u2013 %d").format(startValue, endValue);
+ }
+
+ this._timelineSelectionPathComponent.displayName = displayName;
+ this._timelineSelectionPathComponent.title = displayName;
+ }
+
+ _createTimelineRangePathComponent(title)
+ {
+ let range = new WebInspector.TimelineRange(NaN, NaN);
+ let pathComponent = new WebInspector.HierarchicalPathComponent(title || enDash, "time-icon", range);
+ pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._timeRangePathComponentSelected, this);
+
+ return pathComponent;
+ }
+
+ _updateTimelineViewTimes(timelineView)
+ {
+ let timelineRuler = this._timelineOverview.timelineRuler;
+ let entireRangeSelected = timelineRuler.entireRangeSelected;
+ let endTime = this._timelineOverview.selectionStartTime + this._timelineOverview.selectionDuration;
+
+ if (entireRangeSelected) {
+ if (timelineView instanceof WebInspector.RenderingFrameTimelineView) {
+ endTime = this._renderingFrameTimeline.records.length;
+ } else {
+ // Clamp selection to the end of the recording (with padding),
+ // so graph views will show an auto-sized graph without a lot of
+ // empty space at the end.
+ endTime = isNaN(this._recording.endTime) ? this._recording.currentTime : this._recording.endTime;
+ endTime += timelineRuler.minimumSelectionDuration;
+ }
+ }
+
+ timelineView.startTime = this._timelineOverview.selectionStartTime;
+ timelineView.currentTime = this._currentTime;
+ timelineView.endTime = endTime;
+ }
+
+ _editingInstrumentsDidChange(event)
+ {
+ let editingInstruments = this._timelineOverview.editingInstruments;
+ this.element.classList.toggle(WebInspector.TimelineOverview.EditInstrumentsStyleClassName, editingInstruments);
+
+ this._updateTimelineOverviewHeight();
+ }
+
+ _filterDidChange()
+ {
+ if (!this.currentTimelineView)
+ return;
+
+ this.currentTimelineView.updateFilter(this._filterBarNavigationItem.filterBar.filters);
+ }
+
+ _recordWasFiltered(event)
+ {
+ if (event.target !== this.currentTimelineView)
+ return;
+
+ console.assert(this.currentTimelineView);
+
+ let timeline = this.currentTimelineView.representedObject;
+ if (!(timeline instanceof WebInspector.Timeline))
+ return;
+
+ let record = event.data.record;
+ let filtered = event.data.filtered;
+ this._timelineOverview.recordWasFiltered(timeline, record, filtered);
+ }
+
+ _updateProgressView()
+ {
+ let isCapturing = WebInspector.timelineManager.isCapturing();
+ this._progressView.visible = isCapturing && this.currentTimelineView && !this.currentTimelineView.showsLiveRecordingData;
+ }
+
+ _updateFilterBar()
+ {
+ this._filterBarNavigationItem.hidden = !this.currentTimelineView || !this.currentTimelineView.showsFilterBar;
+ }
+};