event_emitter_unittest.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/bindings/event_emitter.h"
  5. #include "base/bind.h"
  6. #include "base/callback_helpers.h"
  7. #include "base/values.h"
  8. #include "extensions/common/mojom/event_dispatcher.mojom.h"
  9. #include "extensions/renderer/bindings/api_binding_test.h"
  10. #include "extensions/renderer/bindings/api_binding_test_util.h"
  11. #include "extensions/renderer/bindings/api_event_listeners.h"
  12. #include "extensions/renderer/bindings/exception_handler.h"
  13. #include "extensions/renderer/bindings/listener_tracker.h"
  14. #include "extensions/renderer/bindings/test_js_runner.h"
  15. #include "gin/handle.h"
  16. #include "testing/gmock/include/gmock/gmock.h"
  17. namespace extensions {
  18. namespace {
  19. APIEventListeners::ContextOwnerIdGetter CreateContextOwnerIdGetter() {
  20. return base::BindRepeating(
  21. [](v8::Local<v8::Context>) { return std::string("context"); });
  22. }
  23. } // namespace
  24. class EventEmitterUnittest : public APIBindingTest {
  25. public:
  26. EventEmitterUnittest() = default;
  27. EventEmitterUnittest(const EventEmitterUnittest&) = delete;
  28. EventEmitterUnittest& operator=(const EventEmitterUnittest&) = delete;
  29. ~EventEmitterUnittest() override = default;
  30. // A helper method to dispose of a context and set a flag.
  31. void DisposeContextWrapper(bool* did_invalidate,
  32. v8::Local<v8::Context> context) {
  33. EXPECT_FALSE(*did_invalidate);
  34. *did_invalidate = true;
  35. DisposeContext(context);
  36. }
  37. };
  38. TEST_F(EventEmitterUnittest, TestDispatchMethod) {
  39. v8::HandleScope handle_scope(isolate());
  40. v8::Local<v8::Context> context = MainContext();
  41. ListenerTracker tracker;
  42. auto listeners = std::make_unique<UnfilteredEventListeners>(
  43. base::DoNothing(), "event", CreateContextOwnerIdGetter(),
  44. binding::kNoListenerMax, true, &tracker);
  45. auto log_error = [](std::vector<std::string>* errors,
  46. v8::Local<v8::Context> context,
  47. const std::string& error) { errors->push_back(error); };
  48. std::vector<std::string> logged_errors;
  49. ExceptionHandler exception_handler(
  50. base::BindRepeating(log_error, &logged_errors));
  51. gin::Handle<EventEmitter> event = gin::CreateHandle(
  52. isolate(),
  53. new EventEmitter(false, std::move(listeners), &exception_handler));
  54. v8::Local<v8::Value> v8_event = event.ToV8();
  55. const char kAddListener[] =
  56. "(function(event, listener) { event.addListener(listener); })";
  57. v8::Local<v8::Function> add_listener_function =
  58. FunctionFromString(context, kAddListener);
  59. auto add_listener = [context, v8_event,
  60. add_listener_function](base::StringPiece listener) {
  61. v8::Local<v8::Function> listener_function =
  62. FunctionFromString(context, listener);
  63. v8::Local<v8::Value> args[] = {v8_event, listener_function};
  64. RunFunction(add_listener_function, context, std::size(args), args);
  65. };
  66. const char kListener1[] =
  67. "(function() {\n"
  68. " this.eventArgs1 = Array.from(arguments);\n"
  69. " return 'listener1';\n"
  70. "})";
  71. add_listener(kListener1);
  72. const char kListener2[] =
  73. "(function() {\n"
  74. " this.eventArgs2 = Array.from(arguments);\n"
  75. " return {listener: 'listener2'};\n"
  76. "})";
  77. add_listener(kListener2);
  78. // Listener3 throws, but shouldn't stop the event from reaching other
  79. // listeners.
  80. const char kListener3[] =
  81. "(function() {\n"
  82. " this.eventArgs3 = Array.from(arguments);\n"
  83. " throw new Error('hahaha');\n"
  84. "})";
  85. add_listener(kListener3);
  86. // Returning undefined should not be added to the array of results from
  87. // dispatch.
  88. const char kListener4[] =
  89. "(function() {\n"
  90. " this.eventArgs4 = Array.from(arguments);\n"
  91. "})";
  92. add_listener(kListener4);
  93. const char kDispatch[] =
  94. "(function(event) {\n"
  95. " return event.dispatch('arg1', 2);\n"
  96. "})";
  97. v8::Local<v8::Value> dispatch_args[] = {v8_event};
  98. TestJSRunner::AllowErrors allow_errors;
  99. v8::Local<v8::Value> dispatch_result =
  100. RunFunctionOnGlobal(FunctionFromString(context, kDispatch), context,
  101. std::size(dispatch_args), dispatch_args);
  102. const char kExpectedEventArgs[] = "[\"arg1\",2]";
  103. for (const char* property :
  104. {"eventArgs1", "eventArgs2", "eventArgs3", "eventArgs4"}) {
  105. EXPECT_EQ(kExpectedEventArgs, GetStringPropertyFromObject(
  106. context->Global(), context, property));
  107. }
  108. EXPECT_EQ("{\"results\":[\"listener1\",{\"listener\":\"listener2\"}]}",
  109. V8ToString(dispatch_result, context));
  110. ASSERT_EQ(1u, logged_errors.size());
  111. EXPECT_THAT(logged_errors[0],
  112. testing::StartsWith("Error in event handler: Error: hahaha"));
  113. }
  114. // Test dispatching an event when the first listener invalidates the context.
  115. // Nothing should break, and we shouldn't continue to dispatch the event.
  116. TEST_F(EventEmitterUnittest, ListenersDestroyingContext) {
  117. v8::HandleScope handle_scope(isolate());
  118. v8::Local<v8::Context> context = MainContext();
  119. struct ListenerClosureData {
  120. EventEmitterUnittest& test;
  121. bool did_invalidate_context;
  122. } closure_data = {*this, false};
  123. // A wrapper that just calls DisposeContextWrapper() on the curried in data.
  124. auto listener_wrapper = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
  125. ASSERT_TRUE(info.Data()->IsExternal());
  126. auto& data = *static_cast<ListenerClosureData*>(
  127. info.Data().As<v8::External>()->Value());
  128. data.test.DisposeContextWrapper(&data.did_invalidate_context,
  129. info.GetIsolate()->GetCurrentContext());
  130. };
  131. ListenerTracker tracker;
  132. auto listeners = std::make_unique<UnfilteredEventListeners>(
  133. base::DoNothing(), "event", CreateContextOwnerIdGetter(),
  134. binding::kNoListenerMax, true, &tracker);
  135. ExceptionHandler exception_handler(base::BindRepeating(
  136. [](v8::Local<v8::Context> context, const std::string& error) {}));
  137. gin::Handle<EventEmitter> event = gin::CreateHandle(
  138. isolate(),
  139. new EventEmitter(false, std::move(listeners), &exception_handler));
  140. v8::Local<v8::Value> v8_event = event.ToV8();
  141. const char kAddListener[] =
  142. "(function(event, listener) { event.addListener(listener); })";
  143. v8::Local<v8::Function> add_listener_function =
  144. FunctionFromString(context, kAddListener);
  145. // Queue up three listeners. The first triggered will invalidate the context.
  146. // The others should never be triggered.
  147. constexpr size_t kNumListeners = 3;
  148. for (size_t i = 0; i < kNumListeners; ++i) {
  149. v8::Local<v8::Function> listener =
  150. v8::Function::New(context, listener_wrapper,
  151. v8::External::New(isolate(), &closure_data))
  152. .ToLocalChecked();
  153. v8::Local<v8::Value> args[] = {v8_event, listener};
  154. RunFunction(add_listener_function, context, std::size(args), args);
  155. }
  156. EXPECT_EQ(kNumListeners, event->GetNumListeners());
  157. std::vector<v8::Local<v8::Value>> args;
  158. event->Fire(context, &args, nullptr, JSRunner::ResultCallback());
  159. EXPECT_TRUE(closure_data.did_invalidate_context);
  160. }
  161. } // namespace extensions