Index: extensions/renderer/api_event_handler_unittest.cc |
diff --git a/extensions/renderer/api_event_handler_unittest.cc b/extensions/renderer/api_event_handler_unittest.cc |
index ce1993779b74cf469e82d8be4e1bce1cd76ee3c5..21fd381b3608f0b4cc52de2660090b67ed735057 100644 |
--- a/extensions/renderer/api_event_handler_unittest.cc |
+++ b/extensions/renderer/api_event_handler_unittest.cc |
@@ -447,4 +447,65 @@ TEST_F(APIEventHandlerTest, TestDispatchFromJs) { |
context->Global(), context, "eventArgs")); |
} |
+// Test an event listener throwing an exception. |
+TEST_F(APIEventHandlerTest, TestEventListenersThrowingExceptions) { |
+ auto run_js_and_expect_error = [](v8::Local<v8::Function> function, |
jbroman
2017/01/05 19:52:51
Isn't it the RunJSFunction that's supposed to be c
Devlin
2017/01/05 21:01:46
No, RunJSFunction doesn't always catch exceptions
jbroman
2017/01/05 22:22:14
Should we be catching exceptions in the event disp
Devlin
2017/01/10 21:28:07
Fixed (thanks for the help!). Note that we still
|
+ v8::Local<v8::Context> context, |
+ int argc, |
+ v8::Local<v8::Value> argv[]) { |
+ v8::MaybeLocal<v8::Value> maybe_result = |
+ function->Call(context, context->Global(), argc, argv); |
+ v8::Global<v8::Value> result; |
+ v8::Local<v8::Value> local; |
+ if (maybe_result.ToLocal(&local)) |
+ result.Reset(context->GetIsolate(), local); |
+ }; |
+ |
+ v8::HandleScope handle_scope(isolate()); |
+ v8::Local<v8::Context> context = ContextLocal(); |
+ |
+ APIEventHandler handler(base::Bind(run_js_and_expect_error)); |
+ const char kEventName[] = "alpha"; |
+ v8::Local<v8::Object> event = |
+ handler.CreateEventInstance(kEventName, context); |
+ ASSERT_FALSE(event.IsEmpty()); |
+ |
+ // A listener that will throw an exception. We guarantee that we throw the |
+ // exception first so that we don't rely on event listener ordering. |
+ const char kListenerFunction[] = |
+ "(function() {\n" |
+ " if (!this.didThrow) {\n" |
+ " this.didThrow = true;\n" |
+ " throw new Error('Event handler error');\n" |
+ " }\n" |
+ " this.eventArgs = Array.from(arguments);\n" |
+ "});"; |
+ |
+ const char kAddListenerFunction[] = |
+ "(function(event, listener) { event.addListener(listener); })"; |
+ v8::Local<v8::Function> add_listener_function = |
+ FunctionFromString(context, kAddListenerFunction); |
+ |
+ for (int i = 0; i < 2; ++i) { |
+ v8::Local<v8::Function> listener = |
+ FunctionFromString(context, kListenerFunction); |
+ v8::Local<v8::Value> argv[] = {event, listener}; |
+ RunFunctionOnGlobal(add_listener_function, context, arraysize(argv), argv); |
+ } |
+ EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kEventName, context)); |
+ |
+ std::unique_ptr<base::ListValue> event_args = ListValueFromString("[42]"); |
+ ASSERT_TRUE(event_args); |
+ handler.FireEventInContext(kEventName, context, *event_args); |
+ |
+ // An exception should have been thrown by the first listener and the second |
+ // listener should have recorded the event arguments. |
+ EXPECT_EQ("true", |
+ GetStringPropertyFromObject( |
+ context->Global(), context, "didThrow")); |
+ EXPECT_EQ("[42]", |
+ GetStringPropertyFromObject( |
+ context->Global(), context, "eventArgs")); |
+} |
+ |
} // namespace extensions |