Revise AudioWorkletNode IDL (AudioWorkletProcessorState, onprocessorerror)

Spec PR: https://github.com/WebAudio/web-audio-api/pull/1509

1. Remove AudioWorkletProcessorState from the implementation.
2. Add `onprocessorerror` to AudioWorkletNode IDL.
3. Modify the existing test for AudioWorkletProcessorState
   to test onprocessorerror handler.

Bug: 816542
Test: http/tests/webaudio/audio-worklet/audio-worklet-node-onerror.html
Change-Id: I96280c7dbed5cdf2d608633e4ddb9c94e9b4f6e7
Reviewed-on: https://chromium-review.googlesource.com/937986
Reviewed-by: Raymond Toy <rtoy@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#539699}
diff --git a/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/audio-worklet-node-onerror.html b/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/audio-worklet-node-onerror.html
new file mode 100644
index 0000000..fd15186
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/audio-worklet-node-onerror.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test onprocessorerror handler in AudioWorkletNode
+    </title>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
+    <script src="../../../webaudio-resources/audit.js"></script>
+    <script src="audio-worklet-common.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      // TODO(hongchan): remove this assertion when AudioWorklet shipped.
+      assertAudioWorklet();
+
+      let audit = Audit.createTaskRunner();
+
+      const sampleRate = 48000;
+      const renderLength = sampleRate * 0.1;
+
+      let context = new OfflineAudioContext(1, renderLength, sampleRate);
+
+      // Test |onprocessorerror| called upon failure of processor constructor.
+      audit.define('constructor-error',
+          (task, should) => {
+            let constructorErrorWorkletNode =
+                new AudioWorkletNode(context, 'constructor-error');
+            constructorErrorWorkletNode.onprocessorerror = () => {
+              // Without 'processorerror' event callback, this test will be
+              // timed out.
+              task.done();
+            };
+          });
+
+      // Test |onprocessorerror| called upon failure of process() method.
+      audit.define('process-error',
+          (task, should) => {
+            let processErrorWorkletNode =
+                new AudioWorkletNode(context, 'process-error');
+            processErrorWorkletNode.connect(context.destination);
+            processErrorWorkletNode.onprocessorerror = () => {
+              // Without 'processorerror' event callback, this test will be
+              // timed out.
+              task.done();
+            };
+
+            context.startRendering();
+          });
+
+      // 'error-processor.js' contains 2 class definitions represents an error
+      // in the constructor and an error in the process method respectively.
+      context.audioWorklet.addModule('error-processor.js').then(() => {
+        audit.run();
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/audio-worklet-processor-state.html b/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/audio-worklet-processor-state.html
deleted file mode 100644
index c1c51740..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/audio-worklet-processor-state.html
+++ /dev/null
@@ -1,121 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>
-      Test AudioWorkletProcessorState in AudioWorkletNode
-    </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../../../webaudio-resources/audit.js"></script>
-    <script src="audio-worklet-common.js"></script>
-  </head>
-  <body>
-    <script id="layout-test-code">
-      // TODO(hongchan): remove this assertion when AudioWorklet shipped.
-      assertAudioWorklet();
-
-      let audit = Audit.createTaskRunner();
-
-      const sampleRate = 48000;
-      const renderLength = sampleRate * 0.1;
-
-      // Test "pending", "running" and "stopped" state transition.
-      audit.define('pending-running-stopped',
-          (task, should) => {
-            let context = new OfflineAudioContext(1, renderLength, sampleRate);
-            context.audioWorklet.addModule('state-processor.js').then(() => {
-              let timedWorkletNode = new AudioWorkletNode(context, 'timed');
-
-              // The construction of associated processor has not been
-              // completed. In this state, no audio processing can happen and
-              // all messages to the processor will be queued.
-              should(timedWorkletNode.processorState,
-                     'Checking the processor state upon the constructor call')
-                  .beEqualTo('pending');
-
-              timedWorkletNode.connect(context.destination);
-
-              // Checks the handler of |onprocessorstatechange|. Because the
-              // processor script is correct, the |running| state change MUST
-              // be fired.
-              let isFirstPhase = true;
-              timedWorkletNode.onprocessorstatechange = () => {
-                // The first phase should be "running".
-                if (isFirstPhase) {
-                  should(timedWorkletNode.processorState,
-                         'Checking the processor state upon ' +
-                         'processorstatechange event')
-                      .beEqualTo('running');
-                  isFirstPhase = false;
-                } else {
-                  // The second phase in this test must be "stopped".
-                  should(timedWorkletNode.processorState,
-                         'Checking the processor state after ' +
-                         'processor stopped processing')
-                      .beEqualTo('stopped');
-                  task.done();
-                }
-              };
-
-              context.startRendering();
-            });
-          });
-
-      // Test the error state caused by the failure of processor constructor.
-      audit.define('constructor-error',
-          (task, should) => {
-            let context = new OfflineAudioContext(1, renderLength, sampleRate);
-            context.audioWorklet.addModule('state-processor.js').then(() => {
-              let constructorErrorWorkletNode =
-                  new AudioWorkletNode(context, 'constructor-error');
-              should(constructorErrorWorkletNode.processorState,
-                     'constructorErrorWorkletNode.processorState after ' +
-                     'its construction')
-                  .beEqualTo('pending');
-              constructorErrorWorkletNode.onprocessorstatechange = () => {
-                should(constructorErrorWorkletNode.processorState,
-                       'workletNode.processorState upon processorstatechange ' +
-                       'event after the failure from processor.constructor()')
-                    .beEqualTo('error');
-                task.done();
-              };
-            });
-          });
-
-      // Test the error state caused by the failure of processor's process()
-      // function.
-      audit.define('process-error',
-          (task, should) => {
-            let context = new OfflineAudioContext(1, renderLength, sampleRate);
-            context.audioWorklet.addModule('state-processor.js').then(() => {
-              let processErrorWorkletNode =
-                  new AudioWorkletNode(context, 'process-error');
-              should(processErrorWorkletNode.processorState,
-                     'processErrorWorkletNode.processorState after ' +
-                     'its construction')
-                    .beEqualTo('pending');
-
-              processErrorWorkletNode.connect(context.destination);
-
-              let isFirstPhase = true;
-              processErrorWorkletNode.onprocessorstatechange = () => {
-                if (isFirstPhase) {
-                  // Ignore the first state change event, which is "running";
-                  isFirstPhase = false;
-                } else {
-                  should(processErrorWorkletNode.processorState,
-                         'workletNode.processorState upon processorstatechange ' +
-                         'event after the failure from processor.process()')
-                      .beEqualTo('error');
-                  task.done();
-                }
-              };
-
-              context.startRendering();
-            });
-          });
-
-      audit.run();
-    </script>
-  </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/error-processor.js b/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/error-processor.js
new file mode 100644
index 0000000..3b010db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/error-processor.js
@@ -0,0 +1,33 @@
+/**
+ * @class ConstructorErrorProcessor
+ * @extends AudioWorkletProcessor
+ */
+class ConstructorErrorProcessor extends AudioWorkletProcessor {
+  constructor() {
+    throw 'ConstructorErrorProcessor: an error thrown from constructor.';
+  }
+
+  process() {
+    return true;
+  }
+}
+
+
+/**
+ * @class ProcessErrorProcessor
+ * @extends AudioWorkletProcessor
+ */
+class ProcessErrorProcessor extends AudioWorkletProcessor {
+  constructor() {
+    super();
+  }
+
+  process() {
+    throw 'ProcessErrorProcessor: an error throw from process method.';
+    return true;
+  }
+}
+
+
+registerProcessor('constructor-error', ConstructorErrorProcessor);
+registerProcessor('process-error', ProcessErrorProcessor);
diff --git a/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/state-processor.js b/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/state-processor.js
deleted file mode 100644
index 66213c5..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/webaudio/audio-worklet/state-processor.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * @class TimedProcessor
- * @extends AudioWorkletProcessor
- *
- * This processor class is for the life cycle and the processor state event.
- * It only lives for 1 render quantum.
- */
-class TimedProcessor extends AudioWorkletProcessor {
-  constructor() {
-    super();
-    this.createdAt_ = currentTime;
-    this.lifetime_ = 128 / sampleRate;
-  }
-
-  process() {
-    return currentTime - this.createdAt_ > this.lifetime_ ? false : true;
-  }
-}
-
-
-/**
- * @class ConstructorErrorProcessor
- * @extends AudioWorkletProcessor
- */
-class ConstructorErrorProcessor extends AudioWorkletProcessor {
-  constructor() {
-    throw 'ConstructorErrorProcessor: an error thrown from constructor.';
-  }
-
-  process() {
-    return true;
-  }
-}
-
-
-/**
- * @class ProcessErrorProcessor
- * @extends AudioWorkletProcessor
- */
-class ProcessErrorProcessor extends AudioWorkletProcessor {
-  constructor() {
-    super();
-  }
-
-  process() {
-    throw 'ProcessErrorProcessor: an error throw from process method.';
-    return true;
-  }
-}
-
-
-registerProcessor('timed', TimedProcessor);
-registerProcessor('constructor-error', ConstructorErrorProcessor);
-registerProcessor('process-error', ProcessErrorProcessor);
diff --git a/third_party/WebKit/Source/core/events/event_type_names.json5 b/third_party/WebKit/Source/core/events/event_type_names.json5
index 276c0a1..74eb74c 100644
--- a/third_party/WebKit/Source/core/events/event_type_names.json5
+++ b/third_party/WebKit/Source/core/events/event_type_names.json5
@@ -203,7 +203,7 @@
     "pointerup",
     "popstate",
     "progress",
-    "processorstatechange",
+    "processorerror",
     "push",
     "ratechange",
     "reading",
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
index e9c9a1c6..e9bf8844 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
@@ -25,6 +25,7 @@
 #include "modules/webaudio/AudioParamDescriptor.h"
 #include "modules/webaudio/AudioWorkletProcessor.h"
 #include "modules/webaudio/AudioWorkletProcessorDefinition.h"
+#include "modules/webaudio/AudioWorkletProcessorErrorState.h"
 #include "modules/webaudio/CrossThreadAudioWorkletProcessorInfo.h"
 #include "platform/audio/AudioBus.h"
 #include "platform/audio/AudioUtilities.h"
@@ -175,7 +176,7 @@
     return nullptr;
   }
 
-  // ToImplWithTypeCheck() may return nullptr whenthe type does not match.
+  // ToImplWithTypeCheck() may return nullptr when the type does not match.
   AudioWorkletProcessor* processor =
       V8AudioWorkletProcessor::ToImplWithTypeCheck(isolate, result);
 
@@ -317,7 +318,7 @@
     // process() method call method call failed for some reason or an exception
     // was thrown by the user supplied code. Disable the processor to exclude
     // it from the subsequent rendering task.
-    processor->MarkNonRunnable();
+    processor->SetErrorState(AudioWorkletProcessorErrorState::kProcessError);
     return false;
   }
 
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp
index 657ae632..2da78f2 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp
@@ -79,8 +79,8 @@
 void AudioWorkletHandler::Process(size_t frames_to_process) {
   DCHECK(Context()->IsAudioThread());
 
-  // Render and update the node state when the processor is ready and runnable.
-  if (processor_ && processor_->IsRunnable()) {
+  // Render and update the node state when the processor is ready with no error.
+  if (processor_ && !processor_->hasErrorOccured()) {
     Vector<AudioBus*> inputBuses;
     Vector<AudioBus*> outputBuses;
     for (unsigned i = 0; i < NumberOfInputs(); ++i)
@@ -104,12 +104,12 @@
     // Run the render code and check the state of processor. Finish the
     // processor if needed.
     if (!processor_->Process(&inputBuses, &outputBuses, &param_value_map_) ||
-        !processor_->IsRunnable()) {
+        processor_->hasErrorOccured()) {
       FinishProcessorOnRenderThread();
     }
   } else {
     // The initialization of handler or the associated processor might not be
-    // ready yet or it is in 'non-runnable' state. If so, zero out the connected
+    // ready yet or it is in the error state. If so, zero out the connected
     // output.
     for (unsigned i = 0; i < NumberOfOutputs(); ++i) {
       Output(i).Bus()->Zero();
@@ -149,30 +149,31 @@
   DCHECK(!IsMainThread());
 
   // |processor| can be nullptr when the invocation of user-supplied constructor
-  // fails. That failure sets the processor to "error" state.
-  processor_ = processor;
-  AudioWorkletProcessorState new_state;
-  new_state = processor_ ? AudioWorkletProcessorState::kRunning
-                         : AudioWorkletProcessorState::kError;
-  PostCrossThreadTask(
-      *task_runner_, FROM_HERE,
-      CrossThreadBind(&AudioWorkletHandler::NotifyProcessorStateChange,
-                      WrapRefCounted(this), new_state));
+  // fails. That failure fires at the node's 'onprocessorerror' event handler.
+  if (processor) {
+    processor_ = processor;
+  } else {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBind(&AudioWorkletHandler::NotifyProcessorError,
+                        WrapRefCounted(this),
+                        AudioWorkletProcessorErrorState::kConstructionError));
+  }
 }
 
 void AudioWorkletHandler::FinishProcessorOnRenderThread() {
   DCHECK(Context()->IsAudioThread());
 
-  // The non-runnable processor means that the processor stopped due to an
-  // exception thrown by the user-supplied code.
-  AudioWorkletProcessorState new_state;
-  new_state = processor_->IsRunnable()
-      ? AudioWorkletProcessorState::kStopped
-      : AudioWorkletProcessorState::kError;
-  PostCrossThreadTask(
-      *task_runner_, FROM_HERE,
-      CrossThreadBind(&AudioWorkletHandler::NotifyProcessorStateChange,
-                      WrapRefCounted(this), new_state));
+  // If the user-supplied code is not runnable (i.e. threw an exception)
+  // anymore after the process() call above. Invoke error on the main thread.
+  AudioWorkletProcessorErrorState error_state = processor_->GetErrorState();
+  if (error_state == AudioWorkletProcessorErrorState::kProcessError) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBind(&AudioWorkletHandler::NotifyProcessorError,
+                        WrapRefCounted(this),
+                        error_state));
+  }
 
   // TODO(hongchan): After this point, The handler has no more pending activity
   // and ready for GC.
@@ -181,12 +182,13 @@
   tail_time_ = 0;
 }
 
-void AudioWorkletHandler::NotifyProcessorStateChange(
-    AudioWorkletProcessorState state) {
+void AudioWorkletHandler::NotifyProcessorError(
+    AudioWorkletProcessorErrorState error_state) {
   DCHECK(IsMainThread());
   if (!Context() || !Context()->GetExecutionContext() || !GetNode())
     return;
-  static_cast<AudioWorkletNode*>(GetNode())->SetProcessorState(state);
+
+  static_cast<AudioWorkletNode*>(GetNode())->FireProcessorError();
 }
 
 // ----------------------------------------------------------------
@@ -198,8 +200,7 @@
     const Vector<CrossThreadAudioParamInfo> param_info_list,
     MessagePort* node_port)
     : AudioNode(context),
-      node_port_(node_port),
-      processor_state_(AudioWorkletProcessorState::kPending) {
+      node_port_(node_port) {
   HeapHashMap<String, Member<AudioParam>> audio_param_map;
   HashMap<String, scoped_refptr<AudioParamHandler>> param_handler_map;
   for (const auto& param_info : param_info_list) {
@@ -348,51 +349,18 @@
   return !context()->IsContextClosed();
 }
 
-void AudioWorkletNode::SetProcessorState(AudioWorkletProcessorState new_state) {
-  DCHECK(IsMainThread());
-  switch (processor_state_) {
-    case AudioWorkletProcessorState::kPending:
-      DCHECK(new_state == AudioWorkletProcessorState::kRunning ||
-             new_state == AudioWorkletProcessorState::kError);
-      break;
-    case AudioWorkletProcessorState::kRunning:
-      DCHECK(new_state == AudioWorkletProcessorState::kStopped ||
-             new_state == AudioWorkletProcessorState::kError);
-      break;
-    case AudioWorkletProcessorState::kStopped:
-    case AudioWorkletProcessorState::kError:
-      NOTREACHED()
-          << "The state never changes once it reaches kStopped or kError.";
-      return;
-  }
-
-  processor_state_ = new_state;
-  DispatchEvent(Event::Create(EventTypeNames::processorstatechange));
-}
-
 AudioParamMap* AudioWorkletNode::parameters() const {
   return parameter_map_;
 }
 
-String AudioWorkletNode::processorState() const {
-  switch (processor_state_) {
-    case AudioWorkletProcessorState::kPending:
-      return "pending";
-    case AudioWorkletProcessorState::kRunning:
-      return "running";
-    case AudioWorkletProcessorState::kStopped:
-      return "stopped";
-    case AudioWorkletProcessorState::kError:
-      return "error";
-  }
-  NOTREACHED();
-  return g_empty_string;
-}
-
 MessagePort* AudioWorkletNode::port() const {
   return node_port_;
 }
 
+void AudioWorkletNode::FireProcessorError() {
+  DispatchEvent(Event::Create(EventTypeNames::processorerror));
+}
+
 AudioWorkletHandler& AudioWorkletNode::GetWorkletHandler() const {
   return static_cast<AudioWorkletHandler&>(Handler());
 }
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.h
index 7869e86..da611e7 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.h
@@ -9,6 +9,7 @@
 #include "modules/webaudio/AudioNode.h"
 #include "modules/webaudio/AudioParamMap.h"
 #include "modules/webaudio/AudioWorkletNodeOptions.h"
+#include "modules/webaudio/AudioWorkletProcessorErrorState.h"
 #include "platform/wtf/Threading.h"
 
 namespace blink {
@@ -21,13 +22,6 @@
 class MessagePort;
 class ScriptState;
 
-enum class AudioWorkletProcessorState {
-  kPending,
-  kRunning,
-  kStopped,
-  kError,
-};
-
 // AudioWorkletNode is a user-facing interface of custom audio processor in
 // Web Audio API. The integration of WebAudio renderer is done via
 // AudioWorkletHandler and the actual audio processing runs on
@@ -66,7 +60,7 @@
   // the user-supplied |process()| method returns false.
   void FinishProcessorOnRenderThread();
 
-  void NotifyProcessorStateChange(AudioWorkletProcessorState);
+  void NotifyProcessorError(AudioWorkletProcessorErrorState);
 
  private:
   AudioWorkletHandler(
@@ -107,13 +101,12 @@
   // ActiveScriptWrappable
   bool HasPendingActivity() const final;
 
-  void SetProcessorState(AudioWorkletProcessorState);
-
   // IDL
   AudioParamMap* parameters() const;
   MessagePort* port() const;
-  String processorState() const;
-  DEFINE_ATTRIBUTE_EVENT_LISTENER(processorstatechange);
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(processorerror);
+
+  void FireProcessorError();
 
   virtual void Trace(blink::Visitor*);
 
@@ -126,7 +119,6 @@
 
   Member<AudioParamMap> parameter_map_;
   Member<MessagePort> node_port_;
-  AudioWorkletProcessorState processor_state_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl
index 7283c0d..a4632e9 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl
@@ -4,13 +4,6 @@
 
 // https://webaudio.github.io/web-audio-api/#AudioWorkletNode
 
-enum AudioWorkletProcessorState {
-    "pending",
-    "running",
-    "stopped",
-    "error"
-};
-
 [
     ActiveScriptWrappable,
     Constructor(BaseAudioContext context, DOMString name, optional AudioWorkletNodeOptions options),
@@ -22,6 +15,5 @@
 ] interface AudioWorkletNode : AudioNode {
     readonly attribute AudioParamMap parameters;
     readonly attribute MessagePort port;
-    readonly attribute AudioWorkletProcessorState processorState;
-    attribute EventHandler onprocessorstatechange;
+    attribute EventHandler onprocessorerror;
 };
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.cpp
index f513a6d..061c88a 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.cpp
@@ -37,11 +37,24 @@
     Vector<AudioBus*>* output_buses,
     HashMap<String, std::unique_ptr<AudioFloatArray>>* param_value_map) {
   DCHECK(global_scope_->IsContextThread());
-  DCHECK(IsRunnable());
+  DCHECK(!hasErrorOccured());
   return global_scope_->Process(this, input_buses, output_buses,
                                 param_value_map);
 }
 
+void AudioWorkletProcessor::SetErrorState(
+    AudioWorkletProcessorErrorState error_state) {
+  error_state_ = error_state;
+}
+
+AudioWorkletProcessorErrorState AudioWorkletProcessor::GetErrorState() const {
+  return error_state_;
+}
+
+bool AudioWorkletProcessor::hasErrorOccured() const {
+  return error_state_ != AudioWorkletProcessorErrorState::kNoError;
+}
+
 MessagePort* AudioWorkletProcessor::port() const {
   return processor_port_.Get();
 }
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.h
index 35df1e5..66e702b6 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.h
@@ -6,6 +6,7 @@
 #define AudioWorkletProcessor_h
 
 #include "modules/ModulesExport.h"
+#include "modules/webaudio/AudioWorkletProcessorErrorState.h"
 #include "platform/audio/AudioArray.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/bindings/TraceWrapperV8Reference.h"
@@ -47,11 +48,9 @@
 
   const String& Name() const { return name_; }
 
-  // Mark this processor as "not runnable" so it does not execute the
-  // user-supplied code even after the associated node is connected to the
-  // graph.
-  void MarkNonRunnable() { is_runnable_ = false; }
-  bool IsRunnable() { return is_runnable_; }
+  void SetErrorState(AudioWorkletProcessorErrorState);
+  AudioWorkletProcessorErrorState GetErrorState() const;
+  bool hasErrorOccured() const;
 
   // IDL
   MessagePort* port() const;
@@ -68,9 +67,8 @@
 
   const String name_;
 
-  // Becomes |false| when Process() method throws an exception from the the
-  // user-supplied code. It is an irreversible transition.
-  bool is_runnable_ = true;
+  AudioWorkletProcessorErrorState error_state_ =
+      AudioWorkletProcessorErrorState::kNoError;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorErrorState.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorErrorState.h
new file mode 100644
index 0000000..9968f86
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessorErrorState.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef AudioWorkletProcessorErrorState_h
+#define AudioWorkletProcessorErrorState_h
+
+namespace blink {
+
+// A list of state regarding the error in AudioWorkletProcessor object.
+enum class AudioWorkletProcessorErrorState : unsigned {
+  // The constructor or the process method in the processor has not thrown any
+  // exception.
+  kNoError = 0,
+
+  // An exception thrown from the construction failure.
+  kConstructionError = 1,
+
+  // An exception thrown from the process method.
+  kProcessError = 2,
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/WebKit/Source/modules/webaudio/BUILD.gn b/third_party/WebKit/Source/modules/webaudio/BUILD.gn
index ee8d3e8..8c70ef0d 100644
--- a/third_party/WebKit/Source/modules/webaudio/BUILD.gn
+++ b/third_party/WebKit/Source/modules/webaudio/BUILD.gn
@@ -56,6 +56,7 @@
     "AudioWorkletProcessor.h",
     "AudioWorkletProcessorDefinition.cpp",
     "AudioWorkletProcessorDefinition.h",
+    "AudioWorkletProcessorErrorState.h",
     "AudioWorkletThread.cpp",
     "AudioWorkletThread.h",
     "BaseAudioContext.cpp",