Web Inspector: Timelines: can't reliably stop/start a recording
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Apr 2019 22:48:54 +0000 (22:48 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Apr 2019 22:48:54 +0000 (22:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196778
<rdar://problem/47606798>

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/protocol/ScriptProfiler.json:
* inspector/protocol/Timeline.json:
It is possible to determine when programmatic capturing starts/stops in the frontend based
on the state when the backend causes the state to change, such as if the state is "inactive"
when the frontend is told that the backend has started capturing.

* inspector/protocol/CPUProfiler.json:
* inspector/protocol/Memory.json:
Send an end timestamp to match other instruments.

* inspector/JSGlobalObjectConsoleClient.cpp:
(Inspector::JSGlobalObjectConsoleClient::startConsoleProfile):
(Inspector::JSGlobalObjectConsoleClient::stopConsoleProfile):

* inspector/agents/InspectorScriptProfilerAgent.h:
* inspector/agents/InspectorScriptProfilerAgent.cpp:
(Inspector::InspectorScriptProfilerAgent::trackingComplete):
(Inspector::InspectorScriptProfilerAgent::programmaticCaptureStarted): Deleted.
(Inspector::InspectorScriptProfilerAgent::programmaticCaptureStopped): Deleted.

Source/WebCore:

* inspector/agents/InspectorTimelineAgent.cpp:
(WebCore::InspectorTimelineAgent::startProgrammaticCapture):
(WebCore::InspectorTimelineAgent::stopProgrammaticCapture):
It is possible to determine when programmatic capturing starts/stops in the frontend based
on the state when the backend causes the state to change, such as if the state is "inactive"
when the frontend is told that the backend has started capturing.

* inspector/agents/InspectorCPUProfilerAgent.cpp:
(WebCore::InspectorCPUProfilerAgent::stopTracking):
* inspector/agents/InspectorMemoryAgent.cpp:
(WebCore::InspectorMemoryAgent::stopTracking):
Send an end timestamp to match other instruments.

Source/WebInspectorUI:

Rather than have a binary state of capturing/not-capturing, we should use a four state:
 1. inactive (when the backend has stopped capturing)
 2. starting (when the frontend requests capturing to start)
 3. active (when the backend has started capturing)
 4. stopping (when the frontend requests capturing to stop)

Capturing is considered "on" when not in an "inactive" state. Prevent the frontend from
starting/stopping capturing unless we're in a "stable" ("inactive" or "active") state, not a
"transition" ("starting" or "stopping") state.

One "side effect" of this change is that since the capturing is considered active until the
backend has stopped capturing, we will continue to process records in the frontend even if
the frontend has requested to stop capturing. <https://webkit.org/b/152904>

* UserInterface/Controllers/TimelineManager.js:
(WI.TimelineManager):
(WI.TimelineManager.prototype.get capturingState): Added.
(WI.TimelineManager.prototype.reset):
(WI.TimelineManager.prototype.get activeRecording):
(WI.TimelineManager.prototype.set autoCaptureOnPageLoad):
(WI.TimelineManager.prototype.isCapturing):
(WI.TimelineManager.prototype.startCapturing):
(WI.TimelineManager.prototype.stopCapturing):
(WI.TimelineManager.prototype.processJSON):
(WI.TimelineManager.prototype.capturingStarted):
(WI.TimelineManager.prototype.capturingStopped):
(WI.TimelineManager.prototype.autoCaptureStarted):
(WI.TimelineManager.prototype.eventRecorded):
(WI.TimelineManager.prototype.pageDOMContentLoadedEventFired):
(WI.TimelineManager.prototype.pageLoadEventFired):
(WI.TimelineManager.prototype.cpuProfilerTrackingUpdated):
(WI.TimelineManager.prototype.cpuProfilerTrackingCompleted):
(WI.TimelineManager.prototype.memoryTrackingUpdated):
(WI.TimelineManager.prototype.memoryTrackingCompleted):
(WI.TimelineManager.prototype.heapTrackingStarted):
(WI.TimelineManager.prototype.heapTrackingCompleted):
(WI.TimelineManager.prototype.heapSnapshotAdded):
(WI.TimelineManager.prototype._updateCapturingState): Added.
(WI.TimelineManager.prototype._processRecord):
(WI.TimelineManager.prototype._processEvent):
(WI.TimelineManager.prototype._loadNewRecording):
(WI.TimelineManager.prototype._addRecord):
(WI.TimelineManager.prototype._attemptAutoCapturingForFrame):
(WI.TimelineManager.prototype._legacyAttemptStartAutoCapturingForFrame):
(WI.TimelineManager.prototype._stopAutoRecordingSoon):
(WI.TimelineManager.prototype._resetAutoRecordingDeadTimeTimeout):
(WI.TimelineManager.prototype._mainResourceDidChange):
(WI.TimelineManager.prototype._resourceWasAdded):
(WI.TimelineManager.prototype._garbageCollected):
(WI.TimelineManager.prototype._memoryPressure):
(WI.TimelineManager.prototype._handleTimelinesAutoStopSettingChanged):
(WI.TimelineManager.prototype.scriptProfilerTrackingCompleted):
(WI.TimelineManager.prototype._handleDOMNodeDidFireEvent):
(WI.TimelineManager.prototype._handleDOMNodeLowPowerChanged):
(WI.TimelineManager.prototype.unloadRecording): Deleted.
(WI.TimelineManager.prototype.programmaticCaptureStarted): Deleted.
(WI.TimelineManager.prototype.programmaticCaptureStopped): Deleted.
(WI.TimelineManager.prototype.scriptProfilerProgrammaticCaptureStarted): Deleted.
(WI.TimelineManager.prototype.scriptProfilerProgrammaticCaptureStopped): Deleted.

* UserInterface/Protocol/ScriptProfilerObserver.js:
(WI.ScriptProfilerObserver.prototype.trackingComplete):
(WI.ScriptProfilerObserver.prototype.programmaticCaptureStarted):
(WI.ScriptProfilerObserver.prototype.programmaticCaptureStopped):
* UserInterface/Protocol/TimelineObserver.js:
(WI.TimelineObserver.prototype.programmaticCaptureStarted):
(WI.TimelineObserver.prototype.programmaticCaptureStopped):
It is possible to determine when programmatic capturing starts/stops in the frontend based
on the state when the backend causes the state to change, such as if the state is "inactive"
when the frontend is told that the backend has started capturing.

* UserInterface/Protocol/CPUProfilerObserver.js:
(WI.CPUProfilerObserver.prototype.trackingComplete):
* UserInterface/Protocol/MemoryObserver.js:
(WI.MemoryObserver.prototype.trackingComplete):
Send an end timestamp to match other instruments.

* UserInterface/Controllers/DebuggerManager.js:
(WI.DebuggerManager):
(WI.DebuggerManager.prototype._handleTimelineCapturingStateChanged): Added.
(WI.DebuggerManager.prototype._timelineCapturingWillStart): Deleted.
(WI.DebuggerManager.prototype._timelineCapturingStopped): Deleted.
* UserInterface/Models/DefaultDashboard.js:
(WI.DefaultDashboard):
(WI.DefaultDashboard.prototype._handleTimelineCapturingStateChanged): Added.
(WI.DefaultDashboard.prototype._capturingStopped): Deleted.
* UserInterface/Views/DebuggerSidebarPanel.js:
(WI.DebuggerSidebarPanel):
(WI.DebuggerSidebarPanel.prototype._handleTimelineCapturingStateChanged): Added.
(WI.DebuggerSidebarPanel.prototype._timelineCapturingWillStart): Deleted.
(WI.DebuggerSidebarPanel.prototype._timelineCapturingStopped): Deleted.
* UserInterface/Views/SourcesNavigationSidebarPanel.js:
(WI.SourcesNavigationSidebarPanel):
(WI.SourcesNavigationSidebarPanel.prototype._handleTimelineCapturingStateChanged): Added.
(WI.SourcesNavigationSidebarPanel.prototype._handleTimelineCapturingWillStart): Deleted.
(WI.SourcesNavigationSidebarPanel.prototype._handleTimelineCapturingStopped): Deleted.
* UserInterface/Views/TimelineOverview.js:
(WI.TimelineOverview):
(WI.TimelineOverview.prototype._handleTimelineCapturingStateChanged): Added.
(WI.TimelineOverview.prototype._capturingStarted): Deleted.
(WI.TimelineOverview.prototype._capturingStopped): Deleted.
* UserInterface/Views/TimelineRecordingContentView.js:
(WI.TimelineRecordingContentView):
(WI.TimelineRecordingContentView.prototype._handleTimelineCapturingStateChanged): Added.
(WI.TimelineRecordingContentView.prototype._recordingUnloaded):
(WI.TimelineRecordingContentView.prototype._capturingStarted): Deleted.
(WI.TimelineRecordingContentView.prototype._capturingStopped): Deleted.
* UserInterface/Views/TimelineTabContentView.js:
(WI.TimelineTabContentView):
(WI.TimelineTabContentView.prototype._handleTimelineCapturingStateChanged): Added.
(WI.TimelineTabContentView.prototype._capturingStartedOrStopped): Deleted.
Use the new single event for all Timelines capture state changes.
Prevent the record button from being clicked when capturing is in a transition state.

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

25 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp
Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp
Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h
Source/JavaScriptCore/inspector/protocol/CPUProfiler.json
Source/JavaScriptCore/inspector/protocol/Memory.json
Source/JavaScriptCore/inspector/protocol/ScriptProfiler.json
Source/JavaScriptCore/inspector/protocol/Timeline.json
Source/WebCore/ChangeLog
Source/WebCore/inspector/agents/InspectorCPUProfilerAgent.cpp
Source/WebCore/inspector/agents/InspectorMemoryAgent.cpp
Source/WebCore/inspector/agents/InspectorTimelineAgent.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js
Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js
Source/WebInspectorUI/UserInterface/Models/DefaultDashboard.js
Source/WebInspectorUI/UserInterface/Protocol/CPUProfilerObserver.js
Source/WebInspectorUI/UserInterface/Protocol/MemoryObserver.js
Source/WebInspectorUI/UserInterface/Protocol/ScriptProfilerObserver.js
Source/WebInspectorUI/UserInterface/Protocol/TimelineObserver.js
Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js
Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js

index ad93f95..88d60bb 100644 (file)
@@ -1,3 +1,31 @@
+2019-04-10  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Timelines: can't reliably stop/start a recording
+        https://bugs.webkit.org/show_bug.cgi?id=196778
+        <rdar://problem/47606798>
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/protocol/ScriptProfiler.json:
+        * inspector/protocol/Timeline.json:
+        It is possible to determine when programmatic capturing starts/stops in the frontend based
+        on the state when the backend causes the state to change, such as if the state is "inactive"
+        when the frontend is told that the backend has started capturing.
+
+        * inspector/protocol/CPUProfiler.json:
+        * inspector/protocol/Memory.json:
+        Send an end timestamp to match other instruments.
+
+        * inspector/JSGlobalObjectConsoleClient.cpp:
+        (Inspector::JSGlobalObjectConsoleClient::startConsoleProfile):
+        (Inspector::JSGlobalObjectConsoleClient::stopConsoleProfile):
+
+        * inspector/agents/InspectorScriptProfilerAgent.h:
+        * inspector/agents/InspectorScriptProfilerAgent.cpp:
+        (Inspector::InspectorScriptProfilerAgent::trackingComplete):
+        (Inspector::InspectorScriptProfilerAgent::programmaticCaptureStarted): Deleted.
+        (Inspector::InspectorScriptProfilerAgent::programmaticCaptureStopped): Deleted.
+
 2019-04-10  Tadeu Zagallo  <tzagallo@apple.com>
 
         Unreviewed, fix watch build after r244143
index d1bd391..0cd9227 100644 (file)
@@ -121,10 +121,6 @@ void JSGlobalObjectConsoleClient::startConsoleProfile()
 {
     ErrorString unused;
 
-    // FIXME: <https://webkit.org/b/158753> Generalize the concept of Instruments on the backend to work equally for JSContext and Web inspection
-    if (m_scriptProfilerAgent)
-        m_scriptProfilerAgent->programmaticCaptureStarted();
-
     if (m_debuggerAgent) {
         m_profileRestoreBreakpointActiveValue = m_debuggerAgent->breakpointsActive();
         m_debuggerAgent->setBreakpointsActive(unused, false);
@@ -145,10 +141,6 @@ void JSGlobalObjectConsoleClient::stopConsoleProfile()
 
     if (m_debuggerAgent)
         m_debuggerAgent->setBreakpointsActive(unused, m_profileRestoreBreakpointActiveValue);
-
-    // FIXME: <https://webkit.org/b/158753> Generalize the concept of Instruments on the backend to work equally for JSContext and Web inspection
-    if (m_scriptProfilerAgent)
-        m_scriptProfilerAgent->programmaticCaptureStopped();
 }
 
 void JSGlobalObjectConsoleClient::takeHeapSnapshot(JSC::ExecState*, const String& title)
index 1ec25c4..56fc1b7 100644 (file)
@@ -203,6 +203,8 @@ static Ref<Protocol::ScriptProfiler::Samples> buildSamples(VM& vm, Vector<Sampli
 
 void InspectorScriptProfilerAgent::trackingComplete()
 {
+    auto timestamp = m_environment.executionStopwatch()->elapsedTime().seconds();
+
 #if ENABLE(SAMPLING_PROFILER)
     if (m_enabledSamplingProfiler) {
         VM& vm = m_environment.scriptDebugServer().vm();
@@ -220,11 +222,11 @@ void InspectorScriptProfilerAgent::trackingComplete()
 
         m_enabledSamplingProfiler = false;
 
-        m_frontendDispatcher->trackingComplete(WTFMove(samples));
+        m_frontendDispatcher->trackingComplete(timestamp, WTFMove(samples));
     } else
-        m_frontendDispatcher->trackingComplete(nullptr);
+        m_frontendDispatcher->trackingComplete(timestamp, nullptr);
 #else
-    m_frontendDispatcher->trackingComplete(nullptr);
+    m_frontendDispatcher->trackingComplete(timestamp, nullptr);
 #endif // ENABLE(SAMPLING_PROFILER)
 }
 
@@ -246,14 +248,4 @@ void InspectorScriptProfilerAgent::stopSamplingWhenDisconnecting()
 #endif
 }
 
-void InspectorScriptProfilerAgent::programmaticCaptureStarted()
-{
-    m_frontendDispatcher->programmaticCaptureStarted();
-}
-
-void InspectorScriptProfilerAgent::programmaticCaptureStopped()
-{
-    m_frontendDispatcher->programmaticCaptureStopped();
-}
-
 } // namespace Inspector
index dab774f..8a287d8 100644 (file)
@@ -53,9 +53,6 @@ public:
     void startTracking(ErrorString&, const bool* includeSamples) override;
     void stopTracking(ErrorString&) override;
 
-    void programmaticCaptureStarted();
-    void programmaticCaptureStopped();
-
     // Debugger::ProfilingClient
     bool isAlreadyProfiling() const override;
     Seconds willEvaluateScript() override;
index cddf9ca..1f60d9d 100644 (file)
         },
         {
             "name": "trackingComplete",
-            "description": "Tracking stopped. Includes any buffered data during tracking, such as profiling information."
+            "description": "Tracking stopped.",
+            "parameters": [
+                { "name": "timestamp", "type": "number" }
+            ]
         }
     ]
 }
index b6b3db9..1c3d01b 100644 (file)
         },
         {
             "name": "trackingComplete",
-            "description": "Tracking stopped."
+            "description": "Tracking stopped.",
+            "parameters": [
+                { "name": "timestamp", "type": "number" }
+            ]
         }
     ]
 }
index 35032a5..60ca687 100644 (file)
             "name": "trackingComplete",
             "description": "Tracking stopped. Includes any buffered data during tracking, such as profiling information.",
             "parameters": [
+                { "name": "timestamp", "type": "number" },
                 { "name": "samples", "$ref": "Samples", "optional": true, "description": "Stack traces." }
             ]
-        },
-        {
-            "name": "programmaticCaptureStarted",
-            "description": "Fired when programmatic capture starts (console.profile). JSContext inspection only."
-        },
-        {
-            "name": "programmaticCaptureStopped",
-            "description": "Fired when programmatic capture stops (console.profileEnd). JSContext inspection only."
         }
     ]
 }
index b9c4f84..9880032 100644 (file)
         {
             "name": "autoCaptureStarted",
             "description": "Fired when auto capture started."
-        },
-        {
-            "name": "programmaticCaptureStarted",
-            "description": "Fired when programmatic capture starts (console.profile)."
-        },
-        {
-            "name": "programmaticCaptureStopped",
-            "description": "Fired when programmatic capture stops (console.profileEnd)."
         }
     ]
 }
index 05068e8..f737c8a 100644 (file)
@@ -1,3 +1,24 @@
+2019-04-10  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Timelines: can't reliably stop/start a recording
+        https://bugs.webkit.org/show_bug.cgi?id=196778
+        <rdar://problem/47606798>
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/agents/InspectorTimelineAgent.cpp:
+        (WebCore::InspectorTimelineAgent::startProgrammaticCapture):
+        (WebCore::InspectorTimelineAgent::stopProgrammaticCapture):
+        It is possible to determine when programmatic capturing starts/stops in the frontend based
+        on the state when the backend causes the state to change, such as if the state is "inactive"
+        when the frontend is told that the backend has started capturing.
+
+        * inspector/agents/InspectorCPUProfilerAgent.cpp:
+        (WebCore::InspectorCPUProfilerAgent::stopTracking):
+        * inspector/agents/InspectorMemoryAgent.cpp:
+        (WebCore::InspectorMemoryAgent::stopTracking):
+        Send an end timestamp to match other instruments.
+
 2019-04-10  Tim Horton  <timothy_horton@apple.com>
 
         Add modern API for overriding the page's specified viewport configuration
index 8f15504..6a2972d 100644 (file)
@@ -77,7 +77,7 @@ void InspectorCPUProfilerAgent::stopTracking(ErrorString&)
 
     m_tracking = false;
 
-    m_frontendDispatcher->trackingComplete();
+    m_frontendDispatcher->trackingComplete(m_environment.executionStopwatch()->elapsedTime().seconds());
 }
 
 static Ref<Protocol::CPUProfiler::ThreadInfo> buildThreadInfo(const ThreadCPUInfo& thread)
index 5bc56ea..22d47f5 100644 (file)
@@ -92,7 +92,7 @@ void InspectorMemoryAgent::stopTracking(ErrorString&)
 
     m_tracking = false;
 
-    m_frontendDispatcher->trackingComplete();
+    m_frontendDispatcher->trackingComplete(m_environment.executionStopwatch()->elapsedTime().seconds());
 }
 
 void InspectorMemoryAgent::didHandleMemoryPressure(Critical critical)
index 81dedce..a1d3d91 100644 (file)
@@ -477,8 +477,6 @@ void InspectorTimelineAgent::startProgrammaticCapture()
     } else
         m_programmaticCaptureRestoreBreakpointActiveValue = false;
 
-    m_frontendDispatcher->programmaticCaptureStarted();
-
     toggleScriptProfilerInstrument(InstrumentState::Start); // Ensure JavaScript samping data.
     toggleTimelineInstrument(InstrumentState::Start); // Ensure Console Profile event records.
     toggleInstruments(InstrumentState::Start); // Any other instruments the frontend wants us to record.
@@ -500,8 +498,6 @@ void InspectorTimelineAgent::stopProgrammaticCapture()
             debuggerAgent->setBreakpointsActive(unused, true);
         }
     }
-
-    m_frontendDispatcher->programmaticCaptureStopped();
 }
 
 void InspectorTimelineAgent::toggleInstruments(InstrumentState state)
index 6bf4c29..b3a371f 100644 (file)
@@ -1,5 +1,127 @@
 2019-04-10  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: Timelines: can't reliably stop/start a recording
+        https://bugs.webkit.org/show_bug.cgi?id=196778
+        <rdar://problem/47606798>
+
+        Reviewed by Timothy Hatcher.
+
+        Rather than have a binary state of capturing/not-capturing, we should use a four state:
+         1. inactive (when the backend has stopped capturing)
+         2. starting (when the frontend requests capturing to start)
+         3. active (when the backend has started capturing)
+         4. stopping (when the frontend requests capturing to stop)
+
+        Capturing is considered "on" when not in an "inactive" state. Prevent the frontend from
+        starting/stopping capturing unless we're in a "stable" ("inactive" or "active") state, not a
+        "transition" ("starting" or "stopping") state.
+
+        One "side effect" of this change is that since the capturing is considered active until the
+        backend has stopped capturing, we will continue to process records in the frontend even if
+        the frontend has requested to stop capturing. <https://webkit.org/b/152904>
+
+        * UserInterface/Controllers/TimelineManager.js:
+        (WI.TimelineManager):
+        (WI.TimelineManager.prototype.get capturingState): Added.
+        (WI.TimelineManager.prototype.reset):
+        (WI.TimelineManager.prototype.get activeRecording):
+        (WI.TimelineManager.prototype.set autoCaptureOnPageLoad):
+        (WI.TimelineManager.prototype.isCapturing):
+        (WI.TimelineManager.prototype.startCapturing):
+        (WI.TimelineManager.prototype.stopCapturing):
+        (WI.TimelineManager.prototype.processJSON):
+        (WI.TimelineManager.prototype.capturingStarted):
+        (WI.TimelineManager.prototype.capturingStopped):
+        (WI.TimelineManager.prototype.autoCaptureStarted):
+        (WI.TimelineManager.prototype.eventRecorded):
+        (WI.TimelineManager.prototype.pageDOMContentLoadedEventFired):
+        (WI.TimelineManager.prototype.pageLoadEventFired):
+        (WI.TimelineManager.prototype.cpuProfilerTrackingUpdated):
+        (WI.TimelineManager.prototype.cpuProfilerTrackingCompleted):
+        (WI.TimelineManager.prototype.memoryTrackingUpdated):
+        (WI.TimelineManager.prototype.memoryTrackingCompleted):
+        (WI.TimelineManager.prototype.heapTrackingStarted):
+        (WI.TimelineManager.prototype.heapTrackingCompleted):
+        (WI.TimelineManager.prototype.heapSnapshotAdded):
+        (WI.TimelineManager.prototype._updateCapturingState): Added.
+        (WI.TimelineManager.prototype._processRecord):
+        (WI.TimelineManager.prototype._processEvent):
+        (WI.TimelineManager.prototype._loadNewRecording):
+        (WI.TimelineManager.prototype._addRecord):
+        (WI.TimelineManager.prototype._attemptAutoCapturingForFrame):
+        (WI.TimelineManager.prototype._legacyAttemptStartAutoCapturingForFrame):
+        (WI.TimelineManager.prototype._stopAutoRecordingSoon):
+        (WI.TimelineManager.prototype._resetAutoRecordingDeadTimeTimeout):
+        (WI.TimelineManager.prototype._mainResourceDidChange):
+        (WI.TimelineManager.prototype._resourceWasAdded):
+        (WI.TimelineManager.prototype._garbageCollected):
+        (WI.TimelineManager.prototype._memoryPressure):
+        (WI.TimelineManager.prototype._handleTimelinesAutoStopSettingChanged):
+        (WI.TimelineManager.prototype.scriptProfilerTrackingCompleted):
+        (WI.TimelineManager.prototype._handleDOMNodeDidFireEvent):
+        (WI.TimelineManager.prototype._handleDOMNodeLowPowerChanged):
+        (WI.TimelineManager.prototype.unloadRecording): Deleted.
+        (WI.TimelineManager.prototype.programmaticCaptureStarted): Deleted.
+        (WI.TimelineManager.prototype.programmaticCaptureStopped): Deleted.
+        (WI.TimelineManager.prototype.scriptProfilerProgrammaticCaptureStarted): Deleted.
+        (WI.TimelineManager.prototype.scriptProfilerProgrammaticCaptureStopped): Deleted.
+
+        * UserInterface/Protocol/ScriptProfilerObserver.js:
+        (WI.ScriptProfilerObserver.prototype.trackingComplete):
+        (WI.ScriptProfilerObserver.prototype.programmaticCaptureStarted):
+        (WI.ScriptProfilerObserver.prototype.programmaticCaptureStopped):
+        * UserInterface/Protocol/TimelineObserver.js:
+        (WI.TimelineObserver.prototype.programmaticCaptureStarted):
+        (WI.TimelineObserver.prototype.programmaticCaptureStopped):
+        It is possible to determine when programmatic capturing starts/stops in the frontend based
+        on the state when the backend causes the state to change, such as if the state is "inactive"
+        when the frontend is told that the backend has started capturing.
+
+        * UserInterface/Protocol/CPUProfilerObserver.js:
+        (WI.CPUProfilerObserver.prototype.trackingComplete):
+        * UserInterface/Protocol/MemoryObserver.js:
+        (WI.MemoryObserver.prototype.trackingComplete):
+        Send an end timestamp to match other instruments.
+
+        * UserInterface/Controllers/DebuggerManager.js:
+        (WI.DebuggerManager):
+        (WI.DebuggerManager.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.DebuggerManager.prototype._timelineCapturingWillStart): Deleted.
+        (WI.DebuggerManager.prototype._timelineCapturingStopped): Deleted.
+        * UserInterface/Models/DefaultDashboard.js:
+        (WI.DefaultDashboard):
+        (WI.DefaultDashboard.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.DefaultDashboard.prototype._capturingStopped): Deleted.
+        * UserInterface/Views/DebuggerSidebarPanel.js:
+        (WI.DebuggerSidebarPanel):
+        (WI.DebuggerSidebarPanel.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.DebuggerSidebarPanel.prototype._timelineCapturingWillStart): Deleted.
+        (WI.DebuggerSidebarPanel.prototype._timelineCapturingStopped): Deleted.
+        * UserInterface/Views/SourcesNavigationSidebarPanel.js:
+        (WI.SourcesNavigationSidebarPanel):
+        (WI.SourcesNavigationSidebarPanel.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.SourcesNavigationSidebarPanel.prototype._handleTimelineCapturingWillStart): Deleted.
+        (WI.SourcesNavigationSidebarPanel.prototype._handleTimelineCapturingStopped): Deleted.
+        * UserInterface/Views/TimelineOverview.js:
+        (WI.TimelineOverview):
+        (WI.TimelineOverview.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.TimelineOverview.prototype._capturingStarted): Deleted.
+        (WI.TimelineOverview.prototype._capturingStopped): Deleted.
+        * UserInterface/Views/TimelineRecordingContentView.js:
+        (WI.TimelineRecordingContentView):
+        (WI.TimelineRecordingContentView.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.TimelineRecordingContentView.prototype._recordingUnloaded):
+        (WI.TimelineRecordingContentView.prototype._capturingStarted): Deleted.
+        (WI.TimelineRecordingContentView.prototype._capturingStopped): Deleted.
+        * UserInterface/Views/TimelineTabContentView.js:
+        (WI.TimelineTabContentView):
+        (WI.TimelineTabContentView.prototype._handleTimelineCapturingStateChanged): Added.
+        (WI.TimelineTabContentView.prototype._capturingStartedOrStopped): Deleted.
+        Use the new single event for all Timelines capture state changes.
+        Prevent the record button from being clicked when capturing is in a transition state.
+
+2019-04-10  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: REGRESSION: Audit: result UI shown on first open if an audit was previously selected
         https://bugs.webkit.org/show_bug.cgi?id=196723
         <rdar://problem/49722252>
index 2a6a20f..68df3af 100644 (file)
@@ -38,8 +38,7 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._breakpointEditablePropertyDidChange, this);
         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleBreakpointActionsDidChange, this);
 
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._timelineCapturingWillStart, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._timelineCapturingStopped, this);
+        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
 
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
@@ -1137,17 +1136,19 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
         this.breakpointsEnabled = restoreState;
     }
 
-    _timelineCapturingWillStart(event)
+    _handleTimelineCapturingStateChanged(event)
     {
-        this._startDisablingBreakpointsTemporarily();
-
-        if (this.paused)
-            this.resume();
-    }
+        switch (WI.timelineManager.capturingState) {
+        case WI.TimelineManager.CapturingState.Starting:
+            this._startDisablingBreakpointsTemporarily();
+            if (this.paused)
+                this.resume();
+            break;
 
-    _timelineCapturingStopped(event)
-    {
-        this._stopDisablingBreakpointsTemporarily();
+        case WI.TimelineManager.CapturingState.Inactive:
+            this._stopDisablingBreakpointsTemporarily();
+            break;
+        }
     }
 
     _handleAuditManagerTestScheduled(event)
index e4b72ec..78aea0d 100644 (file)
@@ -33,30 +33,27 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
         WI.Frame.addEventListener(WI.Frame.Event.ProvisionalLoadStarted, this._provisionalLoadStarted, this);
         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
-        WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
-        WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this);
-
-        WI.heapManager.addEventListener(WI.HeapManager.Event.GarbageCollected, this._garbageCollected, this);
-        WI.memoryManager.addEventListener(WI.MemoryManager.Event.MemoryPressure, this._memoryPressure, this);
-
-        WI.settings.timelinesAutoStop.addEventListener(WI.Setting.Event.Changed, this._handleTimelinesAutoStopSettingChanged, this);
 
         this._enabledTimelineTypesSetting = new WI.Setting("enabled-instrument-types", WI.TimelineManager.defaultTimelineTypes());
 
-        this._isCapturing = false;
+        this._capturingState = TimelineManager.CapturingState.Inactive;
+        this._capturingInstrumentCount = 0;
+        this._capturingStartTime = NaN;
+        this._capturingEndTime = NaN;
+
         this._initiatedByBackendStart = false;
         this._initiatedByBackendStop = false;
-        this._waitingForCapturingStartedEvent = false;
+
         this._isCapturingPageReload = false;
         this._autoCaptureOnPageLoad = false;
         this._mainResourceForAutoCapturing = null;
         this._shouldSetAutoCapturingMainResource = false;
         this._transitioningPageTarget = false;
-        this._boundStopCapturing = this.stopCapturing.bind(this);
 
         this._webTimelineScriptRecordsExpectingScriptProfilerEvents = null;
         this._scriptProfilerRecords = null;
 
+        this._boundStopCapturing = this.stopCapturing.bind(this);
         this._stopCapturingTimeout = undefined;
         this._deadTimeTimeout = undefined;
         this._lastDeadTimeTickle = 0;
@@ -153,9 +150,11 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     // Public
 
+    get capturingState() { return this._capturingState; }
+
     reset()
     {
-        if (this._isCapturing)
+        if (this.isCapturing())
             this.stopCapturing();
 
         this._recordings = [];
@@ -168,7 +167,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
     // The current recording that new timeline records will be appended to, if any.
     get activeRecording()
     {
-        console.assert(this._activeRecording || !this._isCapturing);
+        console.assert(this._activeRecording || !this.isCapturing());
         return this._activeRecording;
     }
 
@@ -191,8 +190,10 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
         this._autoCaptureOnPageLoad = autoCapture;
 
-        if (window.TimelineAgent && TimelineAgent.setAutoCaptureEnabled)
-            TimelineAgent.setAutoCaptureEnabled(this._autoCaptureOnPageLoad);
+        for (let target of WI.targets) {
+            if (target.TimelineAgent)
+                target.TimelineAgent.setAutoCaptureEnabled(this._autoCaptureOnPageLoad);
+        }
     }
 
     get enabledTimelineTypes()
@@ -210,7 +211,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     isCapturing()
     {
-        return this._isCapturing;
+        return this._capturingState !== TimelineManager.CapturingState.Inactive;
     }
 
     isCapturingPageReload()
@@ -238,40 +239,29 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     startCapturing(shouldCreateRecording)
     {
-        console.assert(!this._isCapturing, "TimelineManager is already capturing.");
+        console.assert(this._capturingState === TimelineManager.CapturingState.Stopping || this._capturingState === TimelineManager.CapturingState.Inactive, "TimelineManager is already capturing.");
+        if (this._capturingState !== TimelineManager.CapturingState.Stopping && this._capturingState !== TimelineManager.CapturingState.Inactive)
+            return;
 
         if (!this._activeRecording || shouldCreateRecording)
             this._loadNewRecording();
 
-        this._waitingForCapturingStartedEvent = true;
-
-        this.dispatchEventToListeners(WI.TimelineManager.Event.CapturingWillStart);
+        this._updateCapturingState(TimelineManager.CapturingState.Starting);
 
+        this._capturingStartTime = NaN;
         this._activeRecording.start(this._initiatedByBackendStart);
     }
 
     stopCapturing()
     {
-        console.assert(this._isCapturing, "TimelineManager is not capturing.");
-
-        this._activeRecording.stop(this._initiatedByBackendStop);
-
-        // NOTE: Always stop immediately instead of waiting for a Timeline.recordingStopped event.
-        // This way the UI feels as responsive to a stop as possible.
-        // FIXME: <https://webkit.org/b/152904> Web Inspector: Timeline UI should keep up with processing all incoming records
-        this.capturingStopped();
-    }
-
-    unloadRecording()
-    {
-        if (!this._activeRecording)
+        console.assert(this._capturingState === TimelineManager.CapturingState.Starting || this._capturingState === TimelineManager.CapturingState.Active, "TimelineManager is not capturing.");
+        if (this._capturingState !== TimelineManager.CapturingState.Starting && this._capturingState !== TimelineManager.CapturingState.Active)
             return;
 
-        if (this._isCapturing)
-            this.stopCapturing();
+        this._updateCapturingState(TimelineManager.CapturingState.Stopping);
 
-        this._activeRecording.unloaded();
-        this._activeRecording = null;
+        this._capturingEndTime = NaN;
+        this._activeRecording.stop(this._initiatedByBackendStop);
     }
 
     processJSON({filename, json, error})
@@ -305,7 +295,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
         this.dispatchEventToListeners(WI.TimelineManager.Event.RecordingCreated, {recording: newRecording});
 
-        if (this._isCapturing)
+        if (this.isCapturing())
             this.stopCapturing();
 
         let oldRecording = this._activeRecording;
@@ -339,51 +329,95 @@ WI.TimelineManager = class TimelineManager extends WI.Object
     {
         // Called from WI.TimelineObserver.
 
-        if (this._isCapturing)
+        // The frontend didn't start capturing, so this was a programmatic start.
+        if (this._capturingState === TimelineManager.CapturingState.Inactive) {
+            this._initiatedByBackendStart = true;
+            this._activeRecording.addScriptInstrumentForProgrammaticCapture();
+            this.startCapturing();
+        }
+
+        if (!isNaN(startTime)) {
+            if (isNaN(this._capturingStartTime) || startTime < this._capturingStartTime)
+                this._capturingStartTime = startTime;
+
+            this._activeRecording.initializeTimeBoundsIfNecessary(startTime);
+        }
+
+        this._capturingInstrumentCount++;
+        console.assert(this._capturingInstrumentCount);
+        if (this._capturingInstrumentCount > 1)
             return;
 
-        this._waitingForCapturingStartedEvent = false;
-        this._isCapturing = true;
+        if (this._capturingState === TimelineManager.CapturingState.Active)
+            return;
 
         this._lastDeadTimeTickle = 0;
 
-        if (startTime)
-            this.activeRecording.initializeTimeBoundsIfNecessary(startTime);
-
         this._webTimelineScriptRecordsExpectingScriptProfilerEvents = [];
 
+        WI.settings.timelinesAutoStop.addEventListener(WI.Setting.Event.Changed, this._handleTimelinesAutoStopSettingChanged, this);
+
+        WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+        WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this);
+
+        WI.heapManager.addEventListener(WI.HeapManager.Event.GarbageCollected, this._garbageCollected, this);
+
+        WI.memoryManager.addEventListener(WI.MemoryManager.Event.MemoryPressure, this._memoryPressure, this);
+
         WI.DOMNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleDOMNodeDidFireEvent, this);
         WI.DOMNode.addEventListener(WI.DOMNode.Event.LowPowerChanged, this._handleDOMNodeLowPowerChanged, this);
 
-        this.dispatchEventToListeners(WI.TimelineManager.Event.CapturingStarted, {startTime});
+        this._updateCapturingState(TimelineManager.CapturingState.Active, {startTime: this._capturingStartTime});
     }
 
     capturingStopped(endTime)
     {
         // Called from WI.TimelineObserver.
 
-        if (!this._isCapturing)
+        // The frontend didn't stop capturing, so this was a programmatic stop.
+        if (this._capturingState === TimelineManager.CapturingState.Active) {
+            this._initiatedByBackendStop = true;
+            this.stopCapturing();
+        }
+
+        if (!isNaN(endTime)) {
+            if (isNaN(this._capturingEndTime) || endTime > this._capturingEndTime)
+                this._capturingEndTime = endTime;
+        }
+
+        this._capturingInstrumentCount--;
+        console.assert(this._capturingInstrumentCount >= 0);
+        if (this._capturingInstrumentCount)
+            return;
+
+        if (this._capturingState === TimelineManager.CapturingState.Inactive)
             return;
 
         WI.DOMNode.removeEventListener(null, null, this);
+        WI.memoryManager.removeEventListener(null, null, this);
+        WI.heapManager.removeEventListener(null, null, this);
+        WI.Target.removeEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this);
+        WI.Frame.removeEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+        WI.settings.timelinesAutoStop.removeEventListener(null, null, this);
 
         this.relaxAutoStop();
 
-        this._isCapturing = false;
         this._isCapturingPageReload = false;
         this._shouldSetAutoCapturingMainResource = false;
         this._mainResourceForAutoCapturing = null;
         this._initiatedByBackendStart = false;
         this._initiatedByBackendStop = false;
 
-        this.dispatchEventToListeners(WI.TimelineManager.Event.CapturingStopped, {endTime});
+        this._updateCapturingState(TimelineManager.CapturingState.Inactive, {endTime: this._capturingEndTime});
     }
 
     autoCaptureStarted()
     {
         // Called from WI.TimelineObserver.
 
-        if (this._isCapturing)
+        let waitingForCapturingStartedEvent = this._capturingState === TimelineManager.CapturingState.Starting;
+
+        if (this.isCapturing())
             this.stopCapturing();
 
         this._initiatedByBackendStart = true;
@@ -391,7 +425,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         // We may already have an fresh TimelineRecording created if autoCaptureStarted is received
         // between sending the Timeline.start command and receiving Timeline.capturingStarted event.
         // In that case, there is no need to call startCapturing again. Reuse the fresh recording.
-        if (!this._waitingForCapturingStartedEvent) {
+        if (!waitingForCapturingStartedEvent) {
             const createNewRecording = true;
             this.startCapturing(createNewRecording);
         }
@@ -399,37 +433,12 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         this._shouldSetAutoCapturingMainResource = true;
     }
 
-    programmaticCaptureStarted()
-    {
-        // Called from WI.TimelineObserver.
-
-        this._initiatedByBackendStart = true;
-
-        this._activeRecording.addScriptInstrumentForProgrammaticCapture();
-
-        const createNewRecording = false;
-        this.startCapturing(createNewRecording);
-    }
-
-    programmaticCaptureStopped()
-    {
-        // Called from WI.TimelineObserver.
-
-        this._initiatedByBackendStop = true;
-
-        // FIXME: This is purely to avoid a noisy assert. Previously
-        // it was impossible to stop without stopping from the UI.
-        console.assert(!this._isCapturing);
-        this._isCapturing = true;
-
-        this.stopCapturing();
-    }
-
     eventRecorded(recordPayload)
     {
         // Called from WI.TimelineObserver.
 
-        if (!this._isCapturing)
+        console.assert(this.isCapturing());
+        if (!this.isCapturing())
             return;
 
         var records = [];
@@ -469,8 +478,6 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         }
     }
 
-    // Protected
-
     pageDOMContentLoadedEventFired(timestamp)
     {
         // Called from WI.PageObserver.
@@ -478,7 +485,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         console.assert(this._activeRecording);
         console.assert(isNaN(WI.networkManager.mainFrame.domContentReadyEventTimestamp));
 
-        let computedTimestamp = this.activeRecording.computeElapsedTime(timestamp);
+        let computedTimestamp = this._activeRecording.computeElapsedTime(timestamp);
 
         WI.networkManager.mainFrame.markDOMContentReadyEvent(computedTimestamp);
 
@@ -493,7 +500,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         console.assert(this._activeRecording);
         console.assert(isNaN(WI.networkManager.mainFrame.loadEventTimestamp));
 
-        let computedTimestamp = this.activeRecording.computeElapsedTime(timestamp);
+        let computedTimestamp = this._activeRecording.computeElapsedTime(timestamp);
 
         WI.networkManager.mainFrame.markLoadEvent(computedTimestamp);
 
@@ -514,15 +521,18 @@ WI.TimelineManager = class TimelineManager extends WI.Object
     {
         // Called from WI.CPUProfilerObserver.
 
-        if (!this._isCapturing)
+        console.assert(this.isCapturing());
+        if (!this.isCapturing())
             return;
 
         this._addRecord(new WI.CPUTimelineRecord(event));
     }
 
-    cpuProfilerTrackingCompleted()
+    cpuProfilerTrackingCompleted(timestamp)
     {
         // Called from WI.CPUProfilerObserver.
+
+        this.capturingStopped(timestamp);
     }
 
     memoryTrackingStarted(timestamp)
@@ -536,24 +546,27 @@ WI.TimelineManager = class TimelineManager extends WI.Object
     {
         // Called from WI.MemoryObserver.
 
-        if (!this._isCapturing)
+        console.assert(this.isCapturing());
+        if (!this.isCapturing())
             return;
 
         this._addRecord(new WI.MemoryTimelineRecord(event.timestamp, event.categories));
     }
 
-    memoryTrackingCompleted()
+    memoryTrackingCompleted(timestamp)
     {
         // Called from WI.MemoryObserver.
+
+        this.capturingStopped(timestamp);
     }
 
     heapTrackingStarted(timestamp, snapshot)
     {
         // Called from WI.HeapObserver.
 
-        this._addRecord(new WI.HeapAllocationsTimelineRecord(timestamp, snapshot));
-
         this.capturingStarted(timestamp);
+
+        this._addRecord(new WI.HeapAllocationsTimelineRecord(timestamp, snapshot));
     }
 
     heapTrackingCompleted(timestamp, snapshot)
@@ -561,21 +574,39 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         // Called from WI.HeapObserver.
 
         this._addRecord(new WI.HeapAllocationsTimelineRecord(timestamp, snapshot));
+
+        this.capturingStopped();
     }
 
     heapSnapshotAdded(timestamp, snapshot)
     {
         // Called from WI.HeapAllocationsInstrument.
 
+        console.assert(this.isCapturing());
+        if (!this.isCapturing())
+            return;
+
         this._addRecord(new WI.HeapAllocationsTimelineRecord(timestamp, snapshot));
     }
 
     // Private
 
+    _updateCapturingState(state, data = {})
+    {
+        if (this._capturingState === state)
+            return;
+
+        this._capturingState = state;
+
+        this.dispatchEventToListeners(TimelineManager.Event.CapturingStateChanged, data);
+    }
+
     _processRecord(recordPayload, parentRecordPayload)
     {
-        var startTime = this.activeRecording.computeElapsedTime(recordPayload.startTime);
-        var endTime = this.activeRecording.computeElapsedTime(recordPayload.endTime);
+        console.assert(this.isCapturing());
+
+        var startTime = this._activeRecording.computeElapsedTime(recordPayload.startTime);
+        var endTime = this._activeRecording.computeElapsedTime(recordPayload.endTime);
         var callFrames = this._callFramesFromPayload(recordPayload.stackTrace);
 
         var significantCallFrame = null;
@@ -762,9 +793,11 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     _processEvent(recordPayload, parentRecordPayload)
     {
+        console.assert(this.isCapturing());
+
         switch (recordPayload.type) {
         case TimelineAgent.EventType.TimeStamp:
-            var timestamp = this.activeRecording.computeElapsedTime(recordPayload.startTime);
+            var timestamp = this._activeRecording.computeElapsedTime(recordPayload.startTime);
             var eventMarker = new WI.TimelineMarker(timestamp, WI.TimelineMarker.Type.TimeStamp, recordPayload.data.message);
             this._activeRecording.addEventMarker(eventMarker);
             break;
@@ -794,7 +827,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         this._recordings.push(newRecording);
         this.dispatchEventToListeners(WI.TimelineManager.Event.RecordingCreated, {recording: newRecording});
 
-        if (this._isCapturing)
+        if (this.isCapturing())
             this.stopCapturing();
 
         var oldRecording = this._activeRecording;
@@ -827,6 +860,8 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     _addRecord(record)
     {
+        console.assert(this.isCapturing());
+
         this._activeRecording.addRecord(record);
 
         // Only worry about dead time after the load event.
@@ -844,15 +879,15 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
         // COMPATIBILITY (iOS 9): Timeline.setAutoCaptureEnabled did not exist.
         // Perform auto capture in the frontend.
-        if (!window.TimelineAgent)
+        if (!InspectorBackend.domains.Timeline)
             return false;
-        if (!TimelineAgent.setAutoCaptureEnabled)
+        if (!InspectorBackend.domains.Timeline.setAutoCaptureEnabled)
             return this._legacyAttemptStartAutoCapturingForFrame(frame);
 
         if (!this._shouldSetAutoCapturingMainResource)
             return false;
 
-        console.assert(this._isCapturing, "We saw autoCaptureStarted so we should already be capturing");
+        console.assert(this.isCapturing(), "We saw autoCaptureStarted so we should already be capturing");
 
         let mainResource = frame.provisionalMainResource || frame.mainResource;
         if (mainResource === this._mainResourceForAutoCapturing)
@@ -874,7 +909,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     _legacyAttemptStartAutoCapturingForFrame(frame)
     {
-        if (this._isCapturing && !this._mainResourceForAutoCapturing)
+        if (this.isCapturing() && !this._mainResourceForAutoCapturing)
             return false;
 
         let mainResource = frame.provisionalMainResource || frame.mainResource;
@@ -884,7 +919,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         let oldMainResource = frame.mainResource || null;
         this._isCapturingPageReload = oldMainResource !== null && oldMainResource.url === mainResource.url;
 
-        if (this._isCapturing)
+        if (this.isCapturing())
             this.stopCapturing();
 
         this._mainResourceForAutoCapturing = mainResource;
@@ -906,7 +941,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
             return;
 
         // Only auto stop when auto capturing.
-        if (!this._isCapturing || !this._mainResourceForAutoCapturing)
+        if (!this.isCapturing() || !this._mainResourceForAutoCapturing)
             return;
 
         if (this._stopCapturingTimeout)
@@ -930,7 +965,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
             return;
 
         // Only monitor dead time when auto capturing.
-        if (!this._isCapturing || !this._mainResourceForAutoCapturing)
+        if (!this.isCapturing() || !this._mainResourceForAutoCapturing)
             return;
 
         // Avoid unnecessary churning of timeout identifier by not tickling until 10ms have passed.
@@ -971,7 +1006,8 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         if (this._attemptAutoCapturingForFrame(frame))
             return;
 
-        if (!this._isCapturing)
+        console.assert(this.isCapturing());
+        if (!this.isCapturing())
             return;
 
         let mainResource = frame.mainResource;
@@ -983,40 +1019,27 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     _resourceWasAdded(event)
     {
-
         // Ignore resource events when there isn't a main frame yet. Those events are triggered by
         // loading the cached resources when the inspector opens, and they do not have timing information.
         if (!WI.networkManager.mainFrame)
             return;
 
-        if (!this._isCapturing)
-            return;
-
         this._addRecord(new WI.ResourceTimelineRecord(event.data.resource));
     }
 
     _garbageCollected(event)
     {
-        if (!this._isCapturing)
-            return;
-
-        let collection = event.data.collection;
+        let {collection} = event.data;
         this._addRecord(new WI.ScriptTimelineRecord(WI.ScriptTimelineRecord.EventType.GarbageCollected, collection.startTime, collection.endTime, null, null, collection));
     }
 
     _memoryPressure(event)
     {
-        if (!this._isCapturing)
-            return;
-
-        this.activeRecording.addMemoryPressureEvent(event.data.memoryPressureEvent);
+        this._activeRecording.addMemoryPressureEvent(event.data.memoryPressureEvent);
     }
 
     _handleTimelinesAutoStopSettingChanged(event)
     {
-        if (!this._isCapturing)
-            return;
-
         if (WI.settings.timelinesAutoStop.value) {
             if (this._mainResourceForAutoCapturing && !isNaN(this._mainResourceForAutoCapturing.parentFrame.loadEventTimestamp))
                 this._stopAutoRecordingSoon();
@@ -1039,24 +1062,6 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         }
     }
 
-    scriptProfilerProgrammaticCaptureStarted()
-    {
-        // FIXME: <https://webkit.org/b/158753> Generalize the concept of Instruments on the backend to work equally for JSContext and Web inspection
-        console.assert(WI.sharedApp.debuggableType === WI.DebuggableType.JavaScript);
-        console.assert(!this._isCapturing);
-
-        this.programmaticCaptureStarted();
-    }
-
-    scriptProfilerProgrammaticCaptureStopped()
-    {
-        // FIXME: <https://webkit.org/b/158753> Generalize the concept of Instruments on the backend to work equally for JSContext and Web inspection
-        console.assert(WI.sharedApp.debuggableType === WI.DebuggableType.JavaScript);
-        console.assert(this._isCapturing);
-
-        this.programmaticCaptureStopped();
-    }
-
     scriptProfilerTrackingStarted(timestamp)
     {
         this._scriptProfilerRecords = [];
@@ -1080,13 +1085,13 @@ WI.TimelineManager = class TimelineManager extends WI.Object
             this._addRecord(record);
     }
 
-    scriptProfilerTrackingCompleted(samples)
+    scriptProfilerTrackingCompleted(timestamp, samples)
     {
         console.assert(!this._webTimelineScriptRecordsExpectingScriptProfilerEvents || this._scriptProfilerRecords.length >= this._webTimelineScriptRecordsExpectingScriptProfilerEvents.length);
 
         if (samples) {
             let {stackTraces} = samples;
-            let topDownCallingContextTree = this.activeRecording.topDownCallingContextTree;
+            let topDownCallingContextTree = this._activeRecording.topDownCallingContextTree;
 
             // Calculate a per-sample duration.
             let timestampIndex = 0;
@@ -1120,7 +1125,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
             if (timestampIndex < timestampCount)
                 sampleDurations.fill(defaultDuration, sampleDurationIndex);
 
-            this.activeRecording.initializeCallingContextTrees(stackTraces, sampleDurations);
+            this._activeRecording.initializeCallingContextTrees(stackTraces, sampleDurations);
 
             // FIXME: This transformation should not be needed after introducing ProfileView.
             // Once we eliminate ProfileNodeTreeElements and ProfileNodeDataGridNodes.
@@ -1140,8 +1145,10 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
         this._scriptProfilerRecords = null;
 
-        let timeline = this.activeRecording.timelineForRecordType(WI.TimelineRecord.Type.Script);
+        let timeline = this._activeRecording.timelineForRecordType(WI.TimelineRecord.Type.Script);
         timeline.refresh();
+
+        this.capturingStopped(timestamp);
     }
 
     _mergeScriptProfileRecords()
@@ -1240,8 +1247,6 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     _handleDOMNodeDidFireEvent(event)
     {
-        console.assert(this._isCapturing);
-
         let {domEvent} = event.data;
 
         this._addRecord(new WI.MediaTimelineRecord(WI.MediaTimelineRecord.EventType.DOMEvent, domEvent.timestamp, {
@@ -1252,8 +1257,6 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
     _handleDOMNodeLowPowerChanged(event)
     {
-        console.assert(this._isCapturing);
-
         let {timestamp, isLowPower} = event.data;
 
         this._addRecord(new WI.MediaTimelineRecord(WI.MediaTimelineRecord.EventType.LowPower, timestamp, {
@@ -1263,13 +1266,18 @@ WI.TimelineManager = class TimelineManager extends WI.Object
     }
 };
 
+WI.TimelineManager.CapturingState = {
+    Inactive: "inactive",
+    Starting: "starting",
+    Active: "active",
+    Stopping: "stopping",
+};
+
 WI.TimelineManager.Event = {
+    CapturingStateChanged: "timeline-manager-capturing-started",
     RecordingCreated: "timeline-manager-recording-created",
     RecordingLoaded: "timeline-manager-recording-loaded",
     RecordingImported: "timeline-manager-recording-imported",
-    CapturingWillStart: "timeline-manager-capturing-will-start",
-    CapturingStarted: "timeline-manager-capturing-started",
-    CapturingStopped: "timeline-manager-capturing-stopped"
 };
 
 WI.TimelineManager.MaximumAutoRecordDuration = 90000; // 90 seconds
index 645f993..c8cb9f5 100644 (file)
@@ -33,7 +33,7 @@ WI.DefaultDashboard = class DefaultDashboard extends WI.Object
 
         // Necessary event required to track page load time and resource sizes.
         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
+        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
 
         // Necessary events required to track load of resources.
         WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
@@ -163,8 +163,11 @@ WI.DefaultDashboard = class DefaultDashboard extends WI.Object
             this._transitioningPageTarget = false;
     }
 
-    _capturingStopped(event)
+    _handleTimelineCapturingStateChanged(event)
     {
+        if (WI.timelineManager.isCapturing())
+            return;
+
         // If recording stops, we should stop the timer if it hasn't stopped already.
         this._stopUpdatingTime();
     }
index 49a2345..0513470 100644 (file)
@@ -37,8 +37,8 @@ WI.CPUProfilerObserver = class CPUProfilerObserver
         WI.timelineManager.cpuProfilerTrackingUpdated(event);
     }
 
-    trackingComplete(samples)
+    trackingComplete(timestamp)
     {
-        WI.timelineManager.cpuProfilerTrackingCompleted(samples);
+        WI.timelineManager.cpuProfilerTrackingCompleted(timestamp);
     }
 };
index 7224de2..e84225b 100644 (file)
@@ -42,8 +42,8 @@ WI.MemoryObserver = class MemoryObserver
         WI.timelineManager.memoryTrackingUpdated(event);
     }
 
-    trackingComplete()
+    trackingComplete(timestamp)
     {
-        WI.timelineManager.memoryTrackingCompleted();
+        WI.timelineManager.memoryTrackingCompleted(timestamp);
     }
 };
index f1b54cc..6b7b558 100644 (file)
@@ -37,18 +37,18 @@ WI.ScriptProfilerObserver = class ScriptProfilerObserver
         WI.timelineManager.scriptProfilerTrackingUpdated(event);
     }
 
-    trackingComplete(samples)
+    trackingComplete(timestamp, samples)
     {
-        WI.timelineManager.scriptProfilerTrackingCompleted(samples);
+        WI.timelineManager.scriptProfilerTrackingCompleted(timestamp, samples);
     }
 
     programmaticCaptureStarted()
     {
-        WI.timelineManager.scriptProfilerProgrammaticCaptureStarted();
+        // COMPATIBILITY (iOS 12.2): ScriptProfiler.programmaticCaptureStarted was removed after iOS 12.2.
     }
 
     programmaticCaptureStopped()
     {
-        WI.timelineManager.scriptProfilerProgrammaticCaptureStopped();
+        // COMPATIBILITY (iOS 12.2): ScriptProfiler.programmaticCaptureStopped was removed after iOS 12.2.
     }
 };
index ba9d019..c9b0aa7 100644 (file)
@@ -49,11 +49,11 @@ WI.TimelineObserver = class TimelineObserver
 
     programmaticCaptureStarted()
     {
-        WI.timelineManager.programmaticCaptureStarted();
+        // COMPATIBILITY (iOS 12.2): Timeline.programmaticCaptureStarted was removed after iOS 12.2.
     }
 
     programmaticCaptureStopped()
     {
-        WI.timelineManager.programmaticCaptureStopped();
+        // COMPATIBILITY (iOS 12.2): Timeline.programmaticCaptureStopped was removed after iOS 12.2.
     }
 };
index 8c1c56b..4e7c40a 100644 (file)
@@ -55,8 +55,7 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
 
         WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this);
 
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._timelineCapturingWillStart, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._timelineCapturingStopped, this);
+        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
 
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
@@ -251,8 +250,7 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             this._debuggerDidPause(null);
 
         if (WI.debuggerManager.breakpointsDisabledTemporarily) {
-            if (WI.timelineManager.isCapturing())
-                this._timelineCapturingWillStart();
+            this._handleTimelineCapturingStateChanged();
 
             if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping)
                 this._handleAuditManagerTestScheduled();
@@ -648,19 +646,20 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
         }
     }
 
-    _timelineCapturingWillStart(event)
+    _handleTimelineCapturingStateChanged(event)
     {
         this._updateTemporarilyDisabledBreakpointsButtons();
 
-        this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
-        this._updateBreakpointsDisabledBanner();
-    }
+        switch (WI.timelineManager.capturingState) {
+        case WI.TimelineManager.CapturingState.Starting:
+            this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
+            break;
 
-    _timelineCapturingStopped(event)
-    {
-        this._updateTemporarilyDisabledBreakpointsButtons();
+        case WI.TimelineManager.CapturingState.Inactive:
+            this._timelineRecordingWarningElement.remove();
+            break;
+        }
 
-        this._timelineRecordingWarningElement.remove();
         this._updateBreakpointsDisabledBanner();
     }
 
index 568a3cd..f0e94fb 100644 (file)
@@ -282,8 +282,7 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
         WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._handleConsoleIssueAdded, this);
         WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._handleConsoleCleared, this);
 
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._handleTimelineCapturingWillStart, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._handleTimelineCapturingStopped, this);
+        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
 
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
@@ -337,8 +336,7 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
             this._handleDebuggerPaused();
 
         if (WI.debuggerManager.breakpointsDisabledTemporarily) {
-            if (WI.timelineManager.isCapturing())
-                this._handleTimelineCapturingWillStart();
+            this._handleTimelineCapturingStateChanged();
 
             if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping)
                 this._handleAuditManagerTestScheduled();
@@ -1820,34 +1818,33 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
         issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement));
     }
 
-    _handleTimelineCapturingWillStart(event)
+    _handleTimelineCapturingStateChanged(event)
     {
         this._updateTemporarilyDisabledBreakpointsButtons();
 
-        if (!this._timelineRecordingWarningElement) {
-            let stopRecordingButton = document.createElement("button");
-            stopRecordingButton.textContent = WI.UIString("Stop recording");
-            stopRecordingButton.addEventListener("click", () => {
-                WI.timelineManager.stopCapturing();
-            });
-
-            this._timelineRecordingWarningElement = document.createElement("div");
-            this._timelineRecordingWarningElement.classList.add("warning-banner");
-            this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), document.createElement("br"), stopRecordingButton);
-        }
-
-        this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
+        switch (WI.timelineManager.capturingState) {
+        case WI.TimelineManager.CapturingState.Starting:
+            if (!this._timelineRecordingWarningElement) {
+                let stopRecordingButton = document.createElement("button");
+                stopRecordingButton.textContent = WI.UIString("Stop recording");
+                stopRecordingButton.addEventListener("click", () => {
+                    WI.timelineManager.stopCapturing();
+                });
 
-        this._updateBreakpointsDisabledBanner();
-    }
+                this._timelineRecordingWarningElement = document.createElement("div");
+                this._timelineRecordingWarningElement.classList.add("warning-banner");
+                this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), document.createElement("br"), stopRecordingButton);
+            }
 
-    _handleTimelineCapturingStopped(event)
-    {
-        this._updateTemporarilyDisabledBreakpointsButtons();
+            this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
+            break;
 
-        if (this._timelineRecordingWarningElement) {
-            this._timelineRecordingWarningElement.remove();
-            this._timelineRecordingWarningElement = null;
+        case WI.TimelineManager.CapturingState.Inactive:
+            if (this._timelineRecordingWarningElement) {
+                this._timelineRecordingWarningElement.remove();
+                this._timelineRecordingWarningElement = null;
+            }
+            break;
         }
 
         this._updateBreakpointsDisabledBanner();
index a1560e9..3cec4fb 100644 (file)
@@ -116,9 +116,8 @@ WI.TimelineOverview = class TimelineOverview extends WI.View
 
         this._viewModeDidChange();
 
+        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
         WI.timelineManager.addEventListener(WI.TimelineManager.Event.RecordingImported, this._recordingImported, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
     }
 
     // Import / Export
@@ -1032,6 +1031,20 @@ WI.TimelineOverview = class TimelineOverview extends WI.View
         this._editingInstrumentsDidChange();
     }
 
+    _handleTimelineCapturingStateChanged(event)
+    {
+        switch (WI.timelineManager.capturingState) {
+        case WI.TimelineManager.CapturingState.Active:
+            this._editInstrumentsButton.enabled = false;
+            this._stopEditingInstruments();
+            break;
+
+        case WI.TimelineManager.CapturingState.Inactive:
+            this._editInstrumentsButton.enabled = true;
+            break;
+        }
+    }
+
     _recordingImported(event)
     {
         let {overviewData} = event.data;
@@ -1055,17 +1068,6 @@ WI.TimelineOverview = class TimelineOverview extends WI.View
         }
     }
 
-    _capturingStarted(event)
-    {
-        this._editInstrumentsButton.enabled = false;
-        this._stopEditingInstruments();
-    }
-
-    _capturingStopped(event)
-    {
-        this._editInstrumentsButton.enabled = true;
-    }
-
     _compareTimelineTreeElements(a, b)
     {
         let aTimelineType = a.representedObject.type;
index 0e16973..56292cc 100644 (file)
@@ -102,8 +102,7 @@ WI.TimelineRecordingContentView = class TimelineRecordingContentView extends WI.
         this._recording.addEventListener(WI.TimelineRecording.Event.Reset, this._recordingReset, this);
         this._recording.addEventListener(WI.TimelineRecording.Event.Unloaded, this._recordingUnloaded, this);
 
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
+        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
 
         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerPaused, this);
         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerResumed, this);
@@ -510,38 +509,41 @@ WI.TimelineRecordingContentView = class TimelineRecordingContentView extends WI.
         }
     }
 
-    _capturingStarted(event)
+    _handleTimelineCapturingStateChanged(event)
     {
+        let {startTime, endTime} = event.data;
+
         this._updateProgressView();
 
-        let startTime = event.data.startTime;
-        if (!this._updating)
-            this._startUpdatingCurrentTime(startTime);
-        this._clearTimelineNavigationItem.enabled = !this._recording.readonly;
-        this._exportButtonNavigationItem.enabled = false;
+        switch (WI.timelineManager.capturingState) {
+        case WI.TimelineManager.CapturingState.Active:
+            if (!this._updating)
+                this._startUpdatingCurrentTime(startTime);
 
-        // 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;
-        }
-    }
+            this._clearTimelineNavigationItem.enabled = !this._recording.readonly;
+            this._exportButtonNavigationItem.enabled = false;
 
-    _capturingStopped(event)
-    {
-        this._updateProgressView();
+            // 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;
+            }
+            break;
 
-        if (this._updating)
-            this._stopUpdatingCurrentTime();
+        case WI.TimelineManager.CapturingState.Inactive:
+            if (this._updating)
+                this._stopUpdatingCurrentTime();
 
-        if (this.currentTimelineView)
-            this._updateTimelineViewTimes(this.currentTimelineView);
+            if (this.currentTimelineView)
+                this._updateTimelineViewTimes(this.currentTimelineView);
 
-        this._discontinuityStartTime = event.data.endTime || this._currentTime;
+            this._discontinuityStartTime = endTime || this._currentTime;
 
-        this._exportButtonNavigationItem.enabled = this._recording.canExport();
+            this._exportButtonNavigationItem.enabled = this._recording.canExport();
+            break;
+        }
     }
 
     _debuggerPaused(event)
@@ -731,8 +733,7 @@ WI.TimelineRecordingContentView = class TimelineRecordingContentView extends WI.
     {
         console.assert(!this._updating);
 
-        WI.timelineManager.removeEventListener(WI.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
-        WI.timelineManager.removeEventListener(WI.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
+        WI.timelineManager.removeEventListener(null, null, this);
     }
 
     _timeRangeSelectionChanged(event)
index 4d085f7..8dea771 100644 (file)
@@ -71,12 +71,10 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
             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.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStarted, this._capturingStartedOrStopped, this);
-        WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._capturingStartedOrStopped, this);
-
         WI.notifications.addEventListener(WI.Notification.VisibilityStateDidChange, this._inspectorVisibilityChanged, this);
         WI.notifications.addEventListener(WI.Notification.GlobalModifierKeysDidChange, this._globalModifierKeysDidChange, this);
 
@@ -425,10 +423,15 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
             this._showContinueButton();
     }
 
-    _capturingStartedOrStopped(event)
+    _handleTimelineCapturingStateChanged(event)
     {
-        let isCapturing = WI.timelineManager.isCapturing();
-        this._recordButton.toggled = isCapturing;
+        let enabled = WI.timelineManager.capturingState === WI.TimelineManager.CapturingState.Active || WI.timelineManager.capturingState === WI.TimelineManager.CapturingState.Inactive;
+
+        this._toggleRecordingShortcut.disabled = !enabled;
+        this._toggleNewRecordingShortcut.disabled = !enabled;
+
+        this._recordButton.toggled = WI.timelineManager.isCapturing();
+        this._recordButton.enabled = enabled;
 
         this._updateNavigationBarButtons();
     }