| 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..e43c860cbd8a9902f606d22755bab182898816f0 100644
|
| --- a/extensions/renderer/api_event_handler_unittest.cc
|
| +++ b/extensions/renderer/api_event_handler_unittest.cc
|
| @@ -447,4 +447,63 @@ TEST_F(APIEventHandlerTest, TestDispatchFromJs) {
|
| context->Global(), context, "eventArgs"));
|
| }
|
|
|
| +// Test listeners that remove themselves in their handling of the event.
|
| +TEST_F(APIEventHandlerTest, RemovingListenersWhileHandlingEvent) {
|
| + v8::HandleScope handle_scope(isolate());
|
| + v8::Local<v8::Context> context = ContextLocal();
|
| +
|
| + APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
|
| + const char kEventName[] = "alpha";
|
| + v8::Local<v8::Object> event =
|
| + handler.CreateEventInstance(kEventName, context);
|
| + ASSERT_FALSE(event.IsEmpty());
|
| +
|
| + {
|
| + // Cache the event object on the global in order to allow for easy removal.
|
| + v8::Local<v8::Function> set_event_on_global =
|
| + FunctionFromString(
|
| + context,
|
| + "(function(event) { this.testEvent = event; })");
|
| + v8::Local<v8::Value> args[] = {event};
|
| + RunFunctionOnGlobal(set_event_on_global, context, arraysize(args), args);
|
| + EXPECT_EQ(event,
|
| + GetPropertyFromObject(context->Global(), context, "testEvent"));
|
| + }
|
| +
|
| + // A listener function that removes itself as a listener.
|
| + const char kListenerFunction[] =
|
| + "(function() {\n"
|
| + " return function listener() {\n"
|
| + " this.testEvent.removeListener(listener);\n"
|
| + " };\n"
|
| + "})();";
|
| +
|
| + // Create and add a bunch of listeners.
|
| + std::vector<v8::Local<v8::Function>> listeners;
|
| + const size_t kNumListeners = 20u;
|
| + listeners.reserve(kNumListeners);
|
| + for (size_t i = 0; i < kNumListeners; ++i)
|
| + listeners.push_back(FunctionFromString(context, kListenerFunction));
|
| +
|
| + const char kAddListenerFunction[] =
|
| + "(function(event, listener) { event.addListener(listener); })";
|
| + v8::Local<v8::Function> add_listener_function =
|
| + FunctionFromString(context, kAddListenerFunction);
|
| +
|
| + for (const auto& listener : listeners) {
|
| + v8::Local<v8::Value> argv[] = {event, listener};
|
| + RunFunctionOnGlobal(add_listener_function, context, arraysize(argv), argv);
|
| + }
|
| +
|
| + // Fire the event. All listeners should be removed (and we shouldn't crash).
|
| + EXPECT_EQ(kNumListeners,
|
| + handler.GetNumEventListenersForTesting(kEventName, context));
|
| + handler.FireEventInContext(kEventName, context, base::ListValue());
|
| + EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
|
| +
|
| + // TODO(devlin): Another possible test: register listener a and listener b,
|
| + // where a removes b and b removes a. Theoretically, only one should be
|
| + // notified. Investigate what we currently do in JS-style bindings.
|
| +}
|
| +
|
| } // namespace extensions
|
|
|