/* * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrowserTabContentView { constructor() { super(TimelineTabContentView.tabInfo()); // Maintain an invisible tree outline containing tree elements for all recordings. // The visible recording's tree element is selected when the content view changes. this._recordingTreeElementMap = new Map; this._recordingsTreeOutline = new WI.TreeOutline; this._recordingsTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._recordingsTreeSelectionDidChange, this); this._toggleRecordingShortcut = new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Space, this._toggleRecordingOnSpacebar.bind(this)); this._toggleRecordingShortcut.implicitlyPreventsDefault = false; this._toggleRecordingShortcut.disabled = true; this._toggleNewRecordingShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.Shift, WI.KeyboardShortcut.Key.Space, this._toggleNewRecordingOnSpacebar.bind(this)); this._toggleNewRecordingShortcut.implicitlyPreventsDefault = false; this._toggleNewRecordingShortcut.disabled = true; let toolTip = WI.UIString("Start recording (%s)\nCreate new recording (%s)").format(this._toggleRecordingShortcut.displayName, this._toggleNewRecordingShortcut.displayName); let altToolTip = WI.UIString("Stop recording (%s)").format(this._toggleRecordingShortcut.displayName); this._recordButton = new WI.ToggleButtonNavigationItem("record-start-stop", toolTip, altToolTip, "Images/Record.svg", "Images/Stop.svg", 13, 13); this._recordButton.visibilityPriority = WI.NavigationItem.VisibilityPriority.High; this._recordButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._recordButtonClicked, this); this._recordStoppingSpinner = new WI.IndeterminateProgressSpinnerNavigationItem("record-stopping", WI.UIString("Stopping recording")); this._recordStoppingSpinner.visibilityPriority = WI.NavigationItem.VisibilityPriority.High; this._recordStoppingSpinner.hidden = true; this._continueButton = new WI.ButtonNavigationItem("record-continue", WI.UIString("Continue without automatically stopping"), "Images/Resume.svg", 13, 13); this._continueButton.visibilityPriority = WI.NavigationItem.VisibilityPriority.High; this._continueButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._continueButtonClicked, this); this._continueButton.hidden = true; this.contentBrowser.navigationBar.insertNavigationItem(this._recordButton, 0); this.contentBrowser.navigationBar.insertNavigationItem(this._recordStoppingSpinner, 1); this.contentBrowser.navigationBar.insertNavigationItem(this._continueButton, 2); if (WI.sharedApp.isWebDebuggable()) { let timelinesNavigationItem = new WI.RadioButtonNavigationItem(WI.TimelineOverview.ViewMode.Timelines, WI.UIString("Events")); let renderingFramesNavigationItem = new WI.RadioButtonNavigationItem(WI.TimelineOverview.ViewMode.RenderingFrames, WI.UIString("Frames")); let viewModeGroup = new WI.GroupNavigationItem([timelinesNavigationItem, renderingFramesNavigationItem]); viewModeGroup.visibilityPriority = WI.NavigationItem.VisibilityPriority.High; this.contentBrowser.navigationBar.insertNavigationItem(viewModeGroup, 3); this.contentBrowser.navigationBar.addEventListener(WI.NavigationBar.Event.NavigationItemSelected, this._viewModeSelected, this); } WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this); WI.timelineManager.addEventListener(WI.TimelineManager.Event.RecordingCreated, this._recordingCreated, this); WI.timelineManager.addEventListener(WI.TimelineManager.Event.RecordingLoaded, this._recordingLoaded, this); WI.notifications.addEventListener(WI.Notification.VisibilityStateDidChange, this._inspectorVisibilityChanged, this); WI.notifications.addEventListener(WI.Notification.GlobalModifierKeysDidChange, this._globalModifierKeysDidChange, this); this._displayedRecording = null; this._displayedContentView = null; this._viewMode = null; this._previousSelectedTimelineType = null; const selectedByUser = false; this._changeViewMode(WI.TimelineOverview.ViewMode.Timelines, selectedByUser); WI.heapManager.enable(); WI.memoryManager.enable(); WI.timelineManager.enable(); } // Static static tabInfo() { return { identifier: TimelineTabContentView.Type, image: "Images/Timeline.svg", displayName: WI.UIString("Timelines", "Timelines Tab Name", "Name of Timelines Tab"), }; } static isTabAllowed() { return InspectorBackend.hasDomain("Timeline") || InspectorBackend.hasDomain("ScriptProfiler"); } static displayNameForTimelineType(timelineType) { switch (timelineType) { case WI.TimelineRecord.Type.Network: return WI.UIString("Network Requests"); case WI.TimelineRecord.Type.Layout: return WI.UIString("Layout & Rendering"); case WI.TimelineRecord.Type.Script: return WI.UIString("JavaScript & Events"); case WI.TimelineRecord.Type.RenderingFrame: return WI.UIString("Rendering Frames"); case WI.TimelineRecord.Type.CPU: return WI.UIString("CPU"); case WI.TimelineRecord.Type.Memory: return WI.UIString("Memory"); case WI.TimelineRecord.Type.HeapAllocations: return WI.UIString("JavaScript Allocations"); case WI.TimelineRecord.Type.Media: // COMPATIBILITY (iOS 13): Animation domain did not exist yet. if (InspectorBackend.hasDomain("Animation")) return WI.UIString("Media & Animations"); return WI.UIString("Media"); case WI.TimelineRecord.Type.Screenshots: return WI.UIString("Screenshots"); default: console.error("Unknown Timeline type:", timelineType); } return null; } static iconClassNameForTimelineType(timelineType) { switch (timelineType) { case WI.TimelineRecord.Type.Network: return "network-icon"; case WI.TimelineRecord.Type.Layout: return "layout-icon"; case WI.TimelineRecord.Type.CPU: return "cpu-icon"; case WI.TimelineRecord.Type.Memory: return "memory-icon"; case WI.TimelineRecord.Type.HeapAllocations: return "heap-allocations-icon"; case WI.TimelineRecord.Type.Script: return "script-icon"; case WI.TimelineRecord.Type.RenderingFrame: return "rendering-frame-icon"; case WI.TimelineRecord.Type.Media: return "media-icon"; case WI.TimelineRecord.Type.Screenshots: return "screenshots-icon"; default: console.error("Unknown Timeline type:", timelineType); } return null; } static genericClassNameForTimelineType(timelineType) { switch (timelineType) { case WI.TimelineRecord.Type.Network: return "network"; case WI.TimelineRecord.Type.Layout: return "colors"; case WI.TimelineRecord.Type.CPU: return "cpu"; case WI.TimelineRecord.Type.Memory: return "memory"; case WI.TimelineRecord.Type.HeapAllocations: return "heap-allocations"; case WI.TimelineRecord.Type.Script: return "script"; case WI.TimelineRecord.Type.RenderingFrame: return "rendering-frame"; case WI.TimelineRecord.Type.Media: return "media"; case WI.TimelineRecord.Type.Screenshots: return "screenshots"; default: console.error("Unknown Timeline type:", timelineType); } return null; } static iconClassNameForRecord(timelineRecord) { switch (timelineRecord.type) { case WI.TimelineRecord.Type.Layout: switch (timelineRecord.eventType) { case WI.LayoutTimelineRecord.EventType.InvalidateStyles: case WI.LayoutTimelineRecord.EventType.RecalculateStyles: return WI.TimelineRecordTreeElement.StyleRecordIconStyleClass; case WI.LayoutTimelineRecord.EventType.InvalidateLayout: case WI.LayoutTimelineRecord.EventType.ForcedLayout: case WI.LayoutTimelineRecord.EventType.Layout: return WI.TimelineRecordTreeElement.LayoutRecordIconStyleClass; case WI.LayoutTimelineRecord.EventType.Paint: return WI.TimelineRecordTreeElement.PaintRecordIconStyleClass; case WI.LayoutTimelineRecord.EventType.Composite: return WI.TimelineRecordTreeElement.CompositeRecordIconStyleClass; default: console.error("Unknown LayoutTimelineRecord eventType: " + timelineRecord.eventType, timelineRecord); } break; case WI.TimelineRecord.Type.Script: switch (timelineRecord.eventType) { case WI.ScriptTimelineRecord.EventType.APIScriptEvaluated: return WI.TimelineRecordTreeElement.APIRecordIconStyleClass; case WI.ScriptTimelineRecord.EventType.ScriptEvaluated: return WI.TimelineRecordTreeElement.EvaluatedRecordIconStyleClass; case WI.ScriptTimelineRecord.EventType.MicrotaskDispatched: case WI.ScriptTimelineRecord.EventType.EventDispatched: case WI.ScriptTimelineRecord.EventType.ObserverCallback: return WI.TimelineRecordTreeElement.EventRecordIconStyleClass; case WI.ScriptTimelineRecord.EventType.ProbeSampleRecorded: return WI.TimelineRecordTreeElement.ProbeRecordIconStyleClass; case WI.ScriptTimelineRecord.EventType.ConsoleProfileRecorded: return WI.TimelineRecordTreeElement.ConsoleProfileIconStyleClass; case WI.ScriptTimelineRecord.EventType.GarbageCollected: return WI.TimelineRecordTreeElement.GarbageCollectionIconStyleClass; case WI.ScriptTimelineRecord.EventType.TimerInstalled: return WI.TimelineRecordTreeElement.TimerRecordIconStyleClass; case WI.ScriptTimelineRecord.EventType.TimerFired: case WI.ScriptTimelineRecord.EventType.TimerRemoved: return WI.TimelineRecordTreeElement.TimerRecordIconStyleClass; case WI.ScriptTimelineRecord.EventType.AnimationFrameFired: case WI.ScriptTimelineRecord.EventType.AnimationFrameRequested: case WI.ScriptTimelineRecord.EventType.AnimationFrameCanceled: return "animation-frame-record"; default: console.error("Unknown ScriptTimelineRecord eventType: " + timelineRecord.eventType, timelineRecord); } break; case WI.TimelineRecord.Type.RenderingFrame: return WI.TimelineRecordTreeElement.RenderingFrameRecordIconStyleClass; case WI.TimelineRecord.Type.HeapAllocations: return "heap-snapshot-record"; case WI.TimelineRecord.Type.Media: switch (timelineRecord.eventType) { case WI.MediaTimelineRecord.EventType.CSSAnimation: return "css-animation-record"; case WI.MediaTimelineRecord.EventType.CSSTransition: return "css-transition-record"; case WI.MediaTimelineRecord.EventType.MediaElement: return "media-element-record"; default: console.error("Unknown MediaTimelineRecord eventType: " + timelineRecord.eventType, timelineRecord); } break; case WI.TimelineRecord.Type.CPU: case WI.TimelineRecord.Type.Memory: case WI.TimelineRecord.Type.Screenshots: // Not used. Fall through to error just in case. default: console.error("Unknown TimelineRecord type: " + timelineRecord.type, timelineRecord); } return null; } static displayNameForRecord(timelineRecord, includeDetailsInMainTitle) { switch (timelineRecord.type) { case WI.TimelineRecord.Type.Network: return WI.displayNameForURL(timelineRecord.resource.url, timelineRecord.resource.urlComponents); case WI.TimelineRecord.Type.Layout: return WI.LayoutTimelineRecord.displayNameForEventType(timelineRecord.eventType); case WI.TimelineRecord.Type.Script: return WI.ScriptTimelineRecord.EventType.displayName(timelineRecord.eventType, timelineRecord.details, includeDetailsInMainTitle); case WI.TimelineRecord.Type.RenderingFrame: if (timelineRecord.name) return WI.UIString("Frame %d \u2014 %s").format(timelineRecord.frameNumber, timelineRecord.name); return WI.UIString("Frame %d").format(timelineRecord.frameNumber); case WI.TimelineRecord.Type.HeapAllocations: if (timelineRecord.heapSnapshot.imported) return WI.UIString("Imported \u2014 %s").format(timelineRecord.heapSnapshot.title); if (timelineRecord.heapSnapshot.title) return WI.UIString("Snapshot %d \u2014 %s").format(timelineRecord.heapSnapshot.identifier, timelineRecord.heapSnapshot.title); return WI.UIString("Snapshot %d").format(timelineRecord.heapSnapshot.identifier); case WI.TimelineRecord.Type.Media: // Since the `displayName` can be specific to an `animation-name`/`transition-property`, // use the generic `subtitle` text instead of we are rendering from the overview. if (includeDetailsInMainTitle && timelineRecord.subtitle) return timelineRecord.subtitle; return timelineRecord.displayName; case WI.TimelineRecord.Type.CPU: case WI.TimelineRecord.Type.Memory: case WI.TimelineRecord.Type.Screenshots: // Not used. Fall through to error just in case. default: console.error("Unknown TimelineRecord type: " + timelineRecord.type, timelineRecord); } return null; } // Public get type() { return WI.TimelineTabContentView.Type; } attached() { super.attached(); this._toggleRecordingShortcut.disabled = false; this._toggleNewRecordingShortcut.disabled = false; if (WI.visible) WI.timelineManager.autoCaptureOnPageLoad = true; } detached() { this._toggleRecordingShortcut.disabled = true; this._toggleNewRecordingShortcut.disabled = true; WI.timelineManager.autoCaptureOnPageLoad = false; super.detached(); } closed() { WI.timelineManager.disable(); WI.memoryManager.disable(); WI.heapManager.disable(); if (WI.sharedApp.isWebDebuggable()) this.contentBrowser.navigationBar.removeEventListener(WI.NavigationBar.Event.NavigationItemSelected, this._viewModeSelected, this); WI.timelineManager.removeEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this); WI.timelineManager.removeEventListener(WI.TimelineManager.Event.RecordingCreated, this._recordingCreated, this); WI.timelineManager.removeEventListener(WI.TimelineManager.Event.RecordingLoaded, this._recordingLoaded, this); WI.notifications.removeEventListener(WI.Notification.VisibilityStateDidChange, this._inspectorVisibilityChanged, this); WI.notifications.removeEventListener(WI.Notification.GlobalModifierKeysDidChange, this._globalModifierKeysDidChange, this); super.closed(); } canShowRepresentedObject(representedObject) { return representedObject instanceof WI.TimelineRecording; } get canHandleFindEvent() { console.assert(this._displayedContentView); return this._displayedContentView.canFocusFilterBar; } handleFindEvent(event) { console.assert(this._displayedContentView); this._displayedContentView.focusFilterBar(); } // DropZoneView delegate dropZoneShouldAppearForDragEvent(dropZone, event) { return event.dataTransfer.types.includes("Files"); } dropZoneHandleDrop(dropZone, event) { let files = event.dataTransfer.files; if (files.length !== 1) { InspectorFrontendHost.beep(); return; } WI.FileUtilities.readJSON(files, (result) => WI.timelineManager.processJSON(result)); } // Protected initialLayout() { super.initialLayout(); let dropZoneView = new WI.DropZoneView(this); dropZoneView.text = WI.UIString("Import Recording"); dropZoneView.targetElement = this.element; this.addSubview(dropZoneView); } restoreFromCookie(cookie) { console.assert(cookie); this._restoredShowingTimelineRecordingContentView = cookie[WI.TimelineTabContentView.ShowingTimelineRecordingContentViewCookieKey]; if (!this._restoredShowingTimelineRecordingContentView) { if (!this.contentBrowser.currentContentView) { // If this is the first time opening the tab, render the currently active recording. if (!this._displayedRecording && WI.timelineManager.activeRecording) this._recordingLoaded(); this._showTimelineViewForType(WI.TimelineTabContentView.OverviewTimelineIdentifierCookieValue); } return; } let selectedTimelineViewIdentifier = cookie[WI.TimelineTabContentView.SelectedTimelineViewIdentifierCookieKey]; this._showTimelineViewForType(selectedTimelineViewIdentifier); super.restoreFromCookie(cookie); } saveToCookie(cookie) { console.assert(cookie); cookie[WI.TimelineTabContentView.ShowingTimelineRecordingContentViewCookieKey] = this.contentBrowser.currentContentView instanceof WI.TimelineRecordingContentView; if (this._viewMode === WI.TimelineOverview.ViewMode.RenderingFrames) cookie[WI.TimelineTabContentView.SelectedTimelineViewIdentifierCookieKey] = WI.TimelineRecord.Type.RenderingFrame; else { let selectedTimeline = this._getTimelineForCurrentContentView(); if (selectedTimeline) cookie[WI.TimelineTabContentView.SelectedTimelineViewIdentifierCookieKey] = selectedTimeline.type; else cookie[WI.TimelineTabContentView.SelectedTimelineViewIdentifierCookieKey] = WI.TimelineTabContentView.OverviewTimelineIdentifierCookieValue; } super.saveToCookie(cookie); } treeElementForRepresentedObject(representedObject) { // This can be called by the base class constructor before the map is created. if (!this._recordingTreeElementMap) return null; if (representedObject instanceof WI.TimelineRecording) return this._recordingTreeElementMap.get(representedObject); return null; } // Private _showRecordButton() { this._recordButton.hidden = false; this._recordStoppingSpinner.hidden = true; this._continueButton.hidden = true; } _showRecordStoppingSpinner() { this._recordButton.hidden = true; this._recordStoppingSpinner.hidden = false; this._continueButton.hidden = true; } _showContinueButton() { this._recordButton.hidden = true; this._recordStoppingSpinner.hidden = true; this._continueButton.hidden = false; } _updateNavigationBarButtons() { if (WI.timelineManager.capturingState === WI.TimelineManager.CapturingState.Stopping) this._showRecordStoppingSpinner(); else if (!WI.modifierKeys.altKey || !WI.timelineManager.willAutoStop()) this._showRecordButton(); else this._showContinueButton(); } _handleTimelineCapturingStateChanged(event) { let enabled = WI.timelineManager.capturingState === WI.TimelineManager.CapturingState.Active || WI.timelineManager.capturingState === WI.TimelineManager.CapturingState.Inactive; let stopping = WI.timelineManager.capturingState === WI.TimelineManager.CapturingState.Stopping; this._toggleRecordingShortcut.disabled = !enabled || stopping; this._toggleNewRecordingShortcut.disabled = !enabled || stopping; this._recordButton.toggled = WI.timelineManager.isCapturing(); this._recordButton.enabled = enabled; this._updateNavigationBarButtons(); } _inspectorVisibilityChanged(event) { WI.timelineManager.autoCaptureOnPageLoad = !!this.isAttached && !!WI.visible; } _globalModifierKeysDidChange(event) { this._updateNavigationBarButtons(); } _toggleRecordingOnSpacebar(event) { if (WI.timelineManager.activeRecording.readonly) return; if (WI.isEventTargetAnEditableField(event)) return; this._toggleRecording(); event.preventDefault(); } _toggleNewRecordingOnSpacebar(event) { if (WI.isEventTargetAnEditableField(event)) return; this._toggleRecording(true); event.preventDefault(); } _toggleRecording(shouldCreateRecording) { let isCapturing = WI.timelineManager.isCapturing(); this._recordButton.toggled = isCapturing; if (isCapturing) WI.timelineManager.stopCapturing(); else { WI.timelineManager.startCapturing(shouldCreateRecording); // Show the timeline to which events will be appended. this._recordingLoaded(); } } _recordButtonClicked(event) { let shouldCreateNewRecording = window.event ? window.event.shiftKey : false; if (WI.timelineManager.activeRecording.readonly) shouldCreateNewRecording = true; this._recordButton.toggled = !WI.timelineManager.isCapturing(); this._toggleRecording(shouldCreateNewRecording); } _continueButtonClicked(event) { console.assert(WI.timelineManager.willAutoStop()); WI.timelineManager.relaxAutoStop(); this._updateNavigationBarButtons(); } _recordingsTreeSelectionDidChange(event) { let treeElement = this._recordingsTreeOutline.selectedTreeElement; if (!treeElement) return; console.assert(treeElement.representedObject instanceof WI.TimelineRecording); this._recordingSelected(treeElement.representedObject); } _recordingCreated(event) { this._addRecording(event.data.recording); this._recordingCountChanged(); } _addRecording(recording) { console.assert(recording instanceof WI.TimelineRecording, recording); let recordingTreeElement = new WI.GeneralTreeElement(WI.TimelineTabContentView.StopwatchIconStyleClass, recording.displayName, null, recording); this._recordingTreeElementMap.set(recording, recordingTreeElement); this._recordingsTreeOutline.appendChild(recordingTreeElement); } _recordingCountChanged() { let previousTreeElement = null; for (let treeElement of this._recordingTreeElementMap.values()) { if (previousTreeElement) { previousTreeElement.nextSibling = treeElement; treeElement.previousSibling = previousTreeElement; } previousTreeElement = treeElement; } } _recordingSelected(recording) { console.assert(recording instanceof WI.TimelineRecording, recording); this._displayedRecording = recording; // Save the current state incase we need to restore it to a new recording. let cookie = {}; this.saveToCookie(cookie); if (this._displayedContentView) this._displayedContentView.removeEventListener(WI.ContentView.Event.NavigationItemsDidChange, this._displayedContentViewNavigationItemsDidChange, this); // Try to get the recording content view if it exists already, if it does we don't want to restore the cookie. let onlyExisting = true; this._displayedContentView = this.contentBrowser.contentViewForRepresentedObject(this._displayedRecording, onlyExisting); if (this._displayedContentView) { this._displayedContentView.addEventListener(WI.ContentView.Event.NavigationItemsDidChange, this._displayedContentViewNavigationItemsDidChange, this); // Show the timeline that was being shown to update the sidebar tree state. let currentTimelineView = this._displayedContentView.currentTimelineView; let timelineType = currentTimelineView && currentTimelineView.representedObject instanceof WI.Timeline ? currentTimelineView.representedObject.type : null; this._showTimelineViewForType(timelineType); return; } onlyExisting = false; this._displayedContentView = this.contentBrowser.contentViewForRepresentedObject(this._displayedRecording, onlyExisting); if (this._displayedContentView) this._displayedContentView.addEventListener(WI.ContentView.Event.NavigationItemsDidChange, this._displayedContentViewNavigationItemsDidChange, this); // Restore the cookie to carry over the previous recording view state to the new recording. this.restoreFromCookie(cookie); } _recordingLoaded(event) { this._recordingSelected(WI.timelineManager.activeRecording); } _viewModeSelected(event) { let selectedNavigationItem = event.target.selectedNavigationItem; console.assert(selectedNavigationItem); if (!selectedNavigationItem) return; const selectedByUser = true; this._changeViewMode(selectedNavigationItem.identifier, selectedByUser); } _changeViewMode(mode, selectedByUser) { if (this._viewMode === mode) return; let navigationItemForViewMode = this.contentBrowser.navigationBar.findNavigationItem(mode); console.assert(navigationItemForViewMode, "Couldn't find navigation item for this view mode."); if (!navigationItemForViewMode) return; this._viewMode = mode; this.contentBrowser.navigationBar.selectedNavigationItem = navigationItemForViewMode; if (!selectedByUser) return; let timelineType = this._previousSelectedTimelineType; if (this._viewMode === WI.TimelineOverview.ViewMode.RenderingFrames) { let timeline = this._getTimelineForCurrentContentView(); this._previousSelectedTimelineType = timeline ? timeline.type : null; timelineType = WI.TimelineRecord.Type.RenderingFrame; } this._showTimelineViewForType(timelineType); } _showTimelineViewForType(timelineType) { console.assert(this._displayedRecording); console.assert(this._displayedContentView); let timeline = timelineType ? this._displayedRecording.timelines.get(timelineType) : null; if (timeline) this._displayedContentView.showTimelineViewForTimeline(timeline); else this._displayedContentView.showOverviewTimelineView(); if (this.contentBrowser.currentContentView !== this._displayedContentView) this.contentBrowser.showContentView(this._displayedContentView); } _displayedContentViewNavigationItemsDidChange(event) { let timeline = this._getTimelineForCurrentContentView(); let newViewMode = WI.TimelineOverview.ViewMode.Timelines; if (timeline && timeline.type === WI.TimelineRecord.Type.RenderingFrame) newViewMode = WI.TimelineOverview.ViewMode.RenderingFrames; const selectedByUser = false; this._changeViewMode(newViewMode, selectedByUser); } _getTimelineForCurrentContentView() { let currentContentView = this.contentBrowser.currentContentView; if (!(currentContentView instanceof WI.TimelineRecordingContentView)) return null; let timelineView = currentContentView.currentTimelineView; return (timelineView && timelineView.representedObject instanceof WI.Timeline) ? timelineView.representedObject : null; } }; WI.TimelineTabContentView.Type = "timeline"; WI.TimelineTabContentView.ShowingTimelineRecordingContentViewCookieKey = "timeline-sidebar-panel-showing-timeline-recording-content-view"; WI.TimelineTabContentView.SelectedTimelineViewIdentifierCookieKey = "timeline-sidebar-panel-selected-timeline-view-identifier"; WI.TimelineTabContentView.OverviewTimelineIdentifierCookieValue = "overview"; WI.TimelineTabContentView.StopwatchIconStyleClass = "stopwatch-icon";