1 // Copyright 2016 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 
5 #include "extensions/renderer/bindings/api_event_handler.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback_helpers.h"
10 #include "base/macros.h"
11 #include "base/optional.h"
12 #include "base/run_loop.h"
13 #include "base/stl_util.h"
14 #include "base/test/bind_test_util.h"
15 #include "base/test/mock_callback.h"
16 #include "base/values.h"
17 #include "extensions/common/event_filtering_info.h"
18 #include "extensions/renderer/bindings/api_binding_test.h"
19 #include "extensions/renderer/bindings/api_binding_test_util.h"
20 #include "extensions/renderer/bindings/exception_handler.h"
21 #include "extensions/renderer/bindings/test_js_runner.h"
22 #include "gin/arguments.h"
23 #include "gin/converter.h"
24 #include "gin/public/context_holder.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 
27 namespace extensions {
28 
29 namespace {
30 
31 const char kAddListenerFunction[] =
32     "(function(event, listener) { event.addListener(listener); })";
33 const char kRemoveListenerFunction[] =
34     "(function(event, listener) { event.removeListener(listener); })";
35 
36 using MockEventChangeHandler = ::testing::StrictMock<
37     base::MockCallback<APIEventListeners::ListenersUpdated>>;
38 
GetContextOwner(v8::Local<v8::Context> context)39 std::string GetContextOwner(v8::Local<v8::Context> context) {
40   return "context";
41 }
42 
43 // TODO(devlin): Use these handy functions more places.
AddListener(v8::Local<v8::Context> context,v8::Local<v8::Function> listener,v8::Local<v8::Object> event)44 void AddListener(v8::Local<v8::Context> context,
45                  v8::Local<v8::Function> listener,
46                  v8::Local<v8::Object> event) {
47   v8::Local<v8::Function> add_listener =
48       FunctionFromString(context, kAddListenerFunction);
49   v8::Local<v8::Value> argv[] = {event, listener};
50   RunFunction(add_listener, context, base::size(argv), argv);
51 }
52 
RemoveListener(v8::Local<v8::Context> context,v8::Local<v8::Function> listener,v8::Local<v8::Object> event)53 void RemoveListener(v8::Local<v8::Context> context,
54                     v8::Local<v8::Function> listener,
55                     v8::Local<v8::Object> event) {
56   v8::Local<v8::Function> remove_listener =
57       FunctionFromString(context, kRemoveListenerFunction);
58   v8::Local<v8::Value> argv[] = {event, listener};
59   RunFunction(remove_listener, context, base::size(argv), argv);
60 }
61 
62 class APIEventHandlerTest : public APIBindingTest {
63  protected:
APIEventHandlerTest()64   APIEventHandlerTest() {}
~APIEventHandlerTest()65   ~APIEventHandlerTest() override {}
66 
SetUp()67   void SetUp() override {
68     APIBindingTest::SetUp();
69     handler_ = std::make_unique<APIEventHandler>(
70         base::DoNothing(), base::BindRepeating(&GetContextOwner), nullptr);
71   }
72 
TearDown()73   void TearDown() override {
74     DisposeAllContexts();
75     handler_.reset();
76     APIBindingTest::TearDown();
77   }
78 
OnWillDisposeContext(v8::Local<v8::Context> context)79   void OnWillDisposeContext(v8::Local<v8::Context> context) override {
80     ASSERT_TRUE(handler_);
81     handler_->InvalidateContext(context);
82   }
83 
SetHandler(std::unique_ptr<APIEventHandler> handler)84   void SetHandler(std::unique_ptr<APIEventHandler> handler) {
85     handler_ = std::move(handler);
86   }
87 
handler()88   APIEventHandler* handler() { return handler_.get(); }
89 
90  private:
91   std::unique_ptr<APIEventHandler> handler_;
92 
93   DISALLOW_COPY_AND_ASSIGN(APIEventHandlerTest);
94 };
95 
96 }  // namespace
97 
98 // Tests adding, removing, and querying event listeners by calling the
99 // associated methods on the JS object.
TEST_F(APIEventHandlerTest,AddingRemovingAndQueryingEventListeners)100 TEST_F(APIEventHandlerTest, AddingRemovingAndQueryingEventListeners) {
101   const char kEventName[] = "alpha";
102   v8::HandleScope handle_scope(isolate());
103   v8::Local<v8::Context> context = MainContext();
104 
105   v8::Local<v8::Object> event = handler()->CreateEventInstance(
106       kEventName, false, true, binding::kNoListenerMax, true, context);
107   ASSERT_FALSE(event.IsEmpty());
108 
109   EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
110 
111   const char kListenerFunction[] = "(function() {})";
112   v8::Local<v8::Function> listener_function =
113       FunctionFromString(context, kListenerFunction);
114   ASSERT_FALSE(listener_function.IsEmpty());
115 
116   v8::Local<v8::Function> add_listener_function =
117       FunctionFromString(context, kAddListenerFunction);
118 
119   {
120     v8::Local<v8::Value> argv[] = {event, listener_function};
121     RunFunction(add_listener_function, context, base::size(argv), argv);
122   }
123   // There should only be one listener on the event.
124   EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
125 
126   {
127     v8::Local<v8::Value> argv[] = {event, listener_function};
128     RunFunction(add_listener_function, context, base::size(argv), argv);
129   }
130   // Trying to add the same listener again should be a no-op.
131   EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
132 
133   // Test hasListener returns true for a listener that is present.
134   const char kHasListenerFunction[] =
135       "(function(event, listener) { return event.hasListener(listener); })";
136   v8::Local<v8::Function> has_listener_function =
137       FunctionFromString(context, kHasListenerFunction);
138   {
139     v8::Local<v8::Value> argv[] = {event, listener_function};
140     v8::Local<v8::Value> result =
141         RunFunction(has_listener_function, context, base::size(argv), argv);
142     bool has_listener = false;
143     EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate(), result, &has_listener));
144     EXPECT_TRUE(has_listener);
145   }
146 
147   // Test that hasListener returns false for a listener that isn't present.
148   {
149     v8::Local<v8::Function> not_a_listener =
150         FunctionFromString(context, "(function() {})");
151     v8::Local<v8::Value> argv[] = {event, not_a_listener};
152     v8::Local<v8::Value> result =
153         RunFunction(has_listener_function, context, base::size(argv), argv);
154     bool has_listener = false;
155     EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate(), result, &has_listener));
156     EXPECT_FALSE(has_listener);
157   }
158 
159   // Test hasListeners returns true
160   const char kHasListenersFunction[] =
161       "(function(event) { return event.hasListeners(); })";
162   v8::Local<v8::Function> has_listeners_function =
163       FunctionFromString(context, kHasListenersFunction);
164   {
165     v8::Local<v8::Value> argv[] = {event};
166     v8::Local<v8::Value> result =
167         RunFunction(has_listeners_function, context, base::size(argv), argv);
168     bool has_listeners = false;
169     EXPECT_TRUE(
170         gin::Converter<bool>::FromV8(isolate(), result, &has_listeners));
171     EXPECT_TRUE(has_listeners);
172   }
173 
174   v8::Local<v8::Function> remove_listener_function =
175       FunctionFromString(context, kRemoveListenerFunction);
176   {
177     v8::Local<v8::Value> argv[] = {event, listener_function};
178     RunFunction(remove_listener_function, context, base::size(argv), argv);
179   }
180   EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
181 
182   {
183     v8::Local<v8::Value> argv[] = {event};
184     v8::Local<v8::Value> result =
185         RunFunction(has_listeners_function, context, base::size(argv), argv);
186     bool has_listeners = false;
187     EXPECT_TRUE(
188         gin::Converter<bool>::FromV8(isolate(), result, &has_listeners));
189     EXPECT_FALSE(has_listeners);
190   }
191 }
192 
193 // Tests listening for and firing different events.
TEST_F(APIEventHandlerTest,FiringEvents)194 TEST_F(APIEventHandlerTest, FiringEvents) {
195   const char kAlphaName[] = "alpha";
196   const char kBetaName[] = "beta";
197   v8::HandleScope handle_scope(isolate());
198   v8::Local<v8::Context> context = MainContext();
199 
200   v8::Local<v8::Object> alpha_event = handler()->CreateEventInstance(
201       kAlphaName, false, true, binding::kNoListenerMax, true, context);
202   v8::Local<v8::Object> beta_event = handler()->CreateEventInstance(
203       kBetaName, false, true, binding::kNoListenerMax, true, context);
204   ASSERT_FALSE(alpha_event.IsEmpty());
205   ASSERT_FALSE(beta_event.IsEmpty());
206 
207   const char kAlphaListenerFunction1[] =
208       "(function() {\n"
209       "  if (!this.alphaCount1) this.alphaCount1 = 0;\n"
210       "  ++this.alphaCount1;\n"
211       "});\n";
212   v8::Local<v8::Function> alpha_listener1 =
213       FunctionFromString(context, kAlphaListenerFunction1);
214   const char kAlphaListenerFunction2[] =
215       "(function() {\n"
216       "  if (!this.alphaCount2) this.alphaCount2 = 0;\n"
217       "  ++this.alphaCount2;\n"
218       "});\n";
219   v8::Local<v8::Function> alpha_listener2 =
220       FunctionFromString(context, kAlphaListenerFunction2);
221   const char kBetaListenerFunction[] =
222       "(function() {\n"
223       "  if (!this.betaCount) this.betaCount = 0;\n"
224       "  ++this.betaCount;\n"
225       "});\n";
226   v8::Local<v8::Function> beta_listener =
227       FunctionFromString(context, kBetaListenerFunction);
228   ASSERT_FALSE(alpha_listener1.IsEmpty());
229   ASSERT_FALSE(alpha_listener2.IsEmpty());
230   ASSERT_FALSE(beta_listener.IsEmpty());
231 
232   {
233     v8::Local<v8::Function> add_listener_function =
234         FunctionFromString(context, kAddListenerFunction);
235     {
236       v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener1};
237       RunFunction(add_listener_function, context, base::size(argv), argv);
238     }
239     {
240       v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener2};
241       RunFunction(add_listener_function, context, base::size(argv), argv);
242     }
243     {
244       v8::Local<v8::Value> argv[] = {beta_event, beta_listener};
245       RunFunction(add_listener_function, context, base::size(argv), argv);
246     }
247   }
248 
249   EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kAlphaName, context));
250   EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kBetaName, context));
251 
252   auto get_fired_count = [&context](const char* name) {
253     v8::Local<v8::Value> res =
254         GetPropertyFromObject(context->Global(), context, name);
255     if (res->IsUndefined())
256       return 0;
257     int32_t count = 0;
258     EXPECT_TRUE(
259         gin::Converter<int32_t>::FromV8(context->GetIsolate(), res, &count))
260         << name;
261     return count;
262   };
263 
264   EXPECT_EQ(0, get_fired_count("alphaCount1"));
265   EXPECT_EQ(0, get_fired_count("alphaCount2"));
266   EXPECT_EQ(0, get_fired_count("betaCount"));
267 
268   handler()->FireEventInContext(kAlphaName, context, base::ListValue(),
269                                 nullptr);
270   EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kAlphaName, context));
271   EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kBetaName, context));
272 
273   EXPECT_EQ(1, get_fired_count("alphaCount1"));
274   EXPECT_EQ(1, get_fired_count("alphaCount2"));
275   EXPECT_EQ(0, get_fired_count("betaCount"));
276 
277   handler()->FireEventInContext(kAlphaName, context, base::ListValue(),
278                                 nullptr);
279   EXPECT_EQ(2, get_fired_count("alphaCount1"));
280   EXPECT_EQ(2, get_fired_count("alphaCount2"));
281   EXPECT_EQ(0, get_fired_count("betaCount"));
282 
283   handler()->FireEventInContext(kBetaName, context, base::ListValue(), nullptr);
284   EXPECT_EQ(2, get_fired_count("alphaCount1"));
285   EXPECT_EQ(2, get_fired_count("alphaCount2"));
286   EXPECT_EQ(1, get_fired_count("betaCount"));
287 }
288 
289 // Tests firing events with arguments.
TEST_F(APIEventHandlerTest,EventArguments)290 TEST_F(APIEventHandlerTest, EventArguments) {
291   v8::HandleScope handle_scope(isolate());
292   v8::Local<v8::Context> context = MainContext();
293 
294   const char kEventName[] = "alpha";
295   v8::Local<v8::Object> event = handler()->CreateEventInstance(
296       kEventName, false, true, binding::kNoListenerMax, true, context);
297   ASSERT_FALSE(event.IsEmpty());
298 
299   const char kListenerFunction[] =
300       "(function() { this.eventArgs = Array.from(arguments); })";
301   v8::Local<v8::Function> listener_function =
302       FunctionFromString(context, kListenerFunction);
303   ASSERT_FALSE(listener_function.IsEmpty());
304 
305   {
306     v8::Local<v8::Function> add_listener_function =
307         FunctionFromString(context, kAddListenerFunction);
308     v8::Local<v8::Value> argv[] = {event, listener_function};
309     RunFunction(add_listener_function, context, base::size(argv), argv);
310   }
311 
312   const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
313   std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
314   ASSERT_TRUE(event_args);
315   handler()->FireEventInContext(kEventName, context, *event_args, nullptr);
316 
317   EXPECT_EQ(
318       ReplaceSingleQuotes(kArguments),
319       GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
320 }
321 
322 // Test dispatching events to multiple contexts.
TEST_F(APIEventHandlerTest,MultipleContexts)323 TEST_F(APIEventHandlerTest, MultipleContexts) {
324   v8::HandleScope handle_scope(isolate());
325 
326   v8::Local<v8::Context> context_a = MainContext();
327   v8::Local<v8::Context> context_b = AddContext();
328 
329   const char kEventName[] = "onFoo";
330 
331 
332   v8::Local<v8::Function> listener_a = FunctionFromString(
333       context_a, "(function(arg) { this.eventArgs = arg + 'alpha'; })");
334   ASSERT_FALSE(listener_a.IsEmpty());
335   v8::Local<v8::Function> listener_b = FunctionFromString(
336       context_b, "(function(arg) { this.eventArgs = arg + 'beta'; })");
337   ASSERT_FALSE(listener_b.IsEmpty());
338 
339   // Create two instances of the same event in different contexts.
340   v8::Local<v8::Object> event_a = handler()->CreateEventInstance(
341       kEventName, false, true, binding::kNoListenerMax, true, context_a);
342   ASSERT_FALSE(event_a.IsEmpty());
343   v8::Local<v8::Object> event_b = handler()->CreateEventInstance(
344       kEventName, false, true, binding::kNoListenerMax, true, context_b);
345   ASSERT_FALSE(event_b.IsEmpty());
346 
347   // Add two separate listeners to the event, one in each context.
348   {
349     v8::Local<v8::Function> add_listener_a =
350         FunctionFromString(context_a, kAddListenerFunction);
351     v8::Local<v8::Value> argv[] = {event_a, listener_a};
352     RunFunction(add_listener_a, context_a, base::size(argv), argv);
353   }
354   EXPECT_EQ(1u,
355             handler()->GetNumEventListenersForTesting(kEventName, context_a));
356   EXPECT_EQ(0u,
357             handler()->GetNumEventListenersForTesting(kEventName, context_b));
358 
359   {
360     v8::Local<v8::Function> add_listener_b =
361         FunctionFromString(context_b, kAddListenerFunction);
362     v8::Local<v8::Value> argv[] = {event_b, listener_b};
363     RunFunction(add_listener_b, context_b, base::size(argv), argv);
364   }
365   EXPECT_EQ(1u,
366             handler()->GetNumEventListenersForTesting(kEventName, context_a));
367   EXPECT_EQ(1u,
368             handler()->GetNumEventListenersForTesting(kEventName, context_b));
369 
370   // Dispatch the event in context_a - the listener in context_b should not be
371   // notified.
372   std::unique_ptr<base::ListValue> arguments_a =
373       ListValueFromString("['result_a:']");
374   ASSERT_TRUE(arguments_a);
375 
376   handler()->FireEventInContext(kEventName, context_a, *arguments_a, nullptr);
377   {
378     EXPECT_EQ("\"result_a:alpha\"",
379               GetStringPropertyFromObject(context_a->Global(), context_a,
380                                           "eventArgs"));
381   }
382   {
383     EXPECT_EQ("undefined", GetStringPropertyFromObject(context_b->Global(),
384                                                        context_b, "eventArgs"));
385   }
386 
387   // Dispatch the event in context_b - the listener in context_a should not be
388   // notified.
389   std::unique_ptr<base::ListValue> arguments_b =
390       ListValueFromString("['result_b:']");
391   ASSERT_TRUE(arguments_b);
392   handler()->FireEventInContext(kEventName, context_b, *arguments_b, nullptr);
393   {
394     EXPECT_EQ("\"result_a:alpha\"",
395               GetStringPropertyFromObject(context_a->Global(), context_a,
396                                           "eventArgs"));
397   }
398   {
399     EXPECT_EQ("\"result_b:beta\"",
400               GetStringPropertyFromObject(context_b->Global(), context_b,
401                                           "eventArgs"));
402   }
403 }
404 
TEST_F(APIEventHandlerTest,DifferentCallingMethods)405 TEST_F(APIEventHandlerTest, DifferentCallingMethods) {
406   v8::HandleScope handle_scope(isolate());
407   v8::Local<v8::Context> context = MainContext();
408 
409   const char kEventName[] = "alpha";
410   v8::Local<v8::Object> event = handler()->CreateEventInstance(
411       kEventName, false, true, binding::kNoListenerMax, true, context);
412   ASSERT_FALSE(event.IsEmpty());
413 
414   const char kAddListenerOnNull[] =
415       "(function(event) {\n"
416       "  event.addListener.call(null, function() {});\n"
417       "})";
418   {
419     v8::Local<v8::Value> args[] = {event};
420     RunFunctionAndExpectError(
421         FunctionFromString(context, kAddListenerOnNull), context, 1, args,
422         "Uncaught TypeError: Illegal invocation: Function must be called on "
423         "an object of type Event");
424   }
425   EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
426 
427   const char kAddListenerOnEvent[] =
428       "(function(event) {\n"
429       "  event.addListener.call(event, function() {});\n"
430       "})";
431   {
432     v8::Local<v8::Value> args[] = {event};
433     RunFunction(FunctionFromString(context, kAddListenerOnEvent),
434                 context, 1, args);
435   }
436   EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
437 
438   // Call addListener with a function that captures the event, creating a cycle.
439   // If we don't properly clean up, the context will leak.
440   const char kAddListenerOnEventWithCapture[] =
441       "(function(event) {\n"
442       "  event.addListener(function listener() {\n"
443       "    event.hasListener(listener);\n"
444       "  });\n"
445       "})";
446   {
447     v8::Local<v8::Value> args[] = {event};
448     RunFunction(FunctionFromString(context, kAddListenerOnEventWithCapture),
449                 context, 1, args);
450   }
451   EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kEventName, context));
452 }
453 
TEST_F(APIEventHandlerTest,TestDispatchFromJs)454 TEST_F(APIEventHandlerTest, TestDispatchFromJs) {
455   v8::HandleScope handle_scope(isolate());
456   v8::Local<v8::Context> context = MainContext();
457 
458   v8::Local<v8::Object> event = handler()->CreateEventInstance(
459       "alpha", false, true, binding::kNoListenerMax, true, context);
460   ASSERT_FALSE(event.IsEmpty());
461 
462   const char kListenerFunction[] =
463       "(function() {\n"
464       "  this.eventArgs = Array.from(arguments);\n"
465       "});";
466   v8::Local<v8::Function> listener =
467       FunctionFromString(context, kListenerFunction);
468 
469   v8::Local<v8::Function> add_listener_function =
470       FunctionFromString(context, kAddListenerFunction);
471 
472   {
473     v8::Local<v8::Value> argv[] = {event, listener};
474     RunFunctionOnGlobal(add_listener_function, context, base::size(argv), argv);
475   }
476 
477   v8::Local<v8::Function> fire_event_function =
478       FunctionFromString(
479           context,
480           "(function(event) { event.dispatch(42, 'foo', {bar: 'baz'}); })");
481   {
482     v8::Local<v8::Value> argv[] = {event};
483     RunFunctionOnGlobal(fire_event_function, context, base::size(argv), argv);
484   }
485 
486   EXPECT_EQ("[42,\"foo\",{\"bar\":\"baz\"}]",
487             GetStringPropertyFromObject(
488                 context->Global(), context, "eventArgs"));
489 }
490 
491 // Test listeners that remove themselves in their handling of the event.
TEST_F(APIEventHandlerTest,RemovingListenersWhileHandlingEvent)492 TEST_F(APIEventHandlerTest, RemovingListenersWhileHandlingEvent) {
493   v8::HandleScope handle_scope(isolate());
494   v8::Local<v8::Context> context = MainContext();
495 
496   const char kEventName[] = "alpha";
497   v8::Local<v8::Object> event = handler()->CreateEventInstance(
498       kEventName, false, true, binding::kNoListenerMax, true, context);
499   ASSERT_FALSE(event.IsEmpty());
500   {
501     // Cache the event object on the global in order to allow for easy removal.
502     v8::Local<v8::Function> set_event_on_global =
503         FunctionFromString(
504             context,
505            "(function(event) { this.testEvent = event; })");
506     v8::Local<v8::Value> args[] = {event};
507     RunFunctionOnGlobal(set_event_on_global, context, base::size(args), args);
508     EXPECT_EQ(event,
509               GetPropertyFromObject(context->Global(), context, "testEvent"));
510   }
511 
512   // A listener function that removes itself as a listener.
513   const char kListenerFunction[] =
514       "(function() {\n"
515       "  return function listener() {\n"
516       "    this.testEvent.removeListener(listener);\n"
517       "  };\n"
518       "})();";
519 
520   // Create and add a bunch of listeners.
521   std::vector<v8::Local<v8::Function>> listeners;
522   const size_t kNumListeners = 20u;
523   listeners.reserve(kNumListeners);
524   for (size_t i = 0; i < kNumListeners; ++i)
525     listeners.push_back(FunctionFromString(context, kListenerFunction));
526 
527   v8::Local<v8::Function> add_listener_function =
528       FunctionFromString(context, kAddListenerFunction);
529 
530   for (const auto& listener : listeners) {
531     v8::Local<v8::Value> argv[] = {event, listener};
532     RunFunctionOnGlobal(add_listener_function, context, base::size(argv), argv);
533   }
534 
535   // Fire the event. All listeners should be removed (and we shouldn't crash).
536   EXPECT_EQ(kNumListeners,
537             handler()->GetNumEventListenersForTesting(kEventName, context));
538   handler()->FireEventInContext(kEventName, context, base::ListValue(),
539                                 nullptr);
540   EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
541 
542   // TODO(devlin): Another possible test: register listener a and listener b,
543   // where a removes b and b removes a. Theoretically, only one should be
544   // notified. Investigate what we currently do in JS-style bindings.
545 }
546 
547 // Test an event listener throwing an exception.
TEST_F(APIEventHandlerTest,TestEventListenersThrowingExceptions)548 TEST_F(APIEventHandlerTest, TestEventListenersThrowingExceptions) {
549   auto log_error =
550       [](std::vector<std::string>* errors_out, v8::Local<v8::Context> context,
551          const std::string& error) { errors_out->push_back(error); };
552 
553   std::vector<std::string> logged_errors;
554   ExceptionHandler exception_handler(
555       base::BindRepeating(log_error, &logged_errors));
556   SetHandler(std::make_unique<APIEventHandler>(
557       base::DoNothing(), base::BindRepeating(&GetContextOwner),
558       &exception_handler));
559 
560   v8::HandleScope handle_scope(isolate());
561   v8::Local<v8::Context> context = MainContext();
562 
563   const char kEventName[] = "alpha";
564   v8::Local<v8::Object> event = handler()->CreateEventInstance(
565       kEventName, false, true, binding::kNoListenerMax, true, context);
566   ASSERT_FALSE(event.IsEmpty());
567 
568   // A listener that will throw an exception. We guarantee that we throw the
569   // exception first so that we don't rely on event listener ordering.
570   const char kListenerFunction[] =
571       "(function() {\n"
572       "  if (!this.didThrow) {\n"
573       "    this.didThrow = true;\n"
574       "    throw new Error('Event handler error');\n"
575       "  }\n"
576       "  this.eventArgs = Array.from(arguments);\n"
577       "});";
578 
579   v8::Local<v8::Function> add_listener_function =
580       FunctionFromString(context, kAddListenerFunction);
581 
582   for (int i = 0; i < 2; ++i) {
583     v8::Local<v8::Function> listener =
584         FunctionFromString(context, kListenerFunction);
585     v8::Local<v8::Value> argv[] = {event, listener};
586     RunFunctionOnGlobal(add_listener_function, context, base::size(argv), argv);
587   }
588   EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kEventName, context));
589 
590   std::unique_ptr<base::ListValue> event_args = ListValueFromString("[42]");
591   ASSERT_TRUE(event_args);
592 
593   {
594     TestJSRunner::AllowErrors allow_errors;
595     handler()->FireEventInContext(kEventName, context, *event_args, nullptr);
596   }
597 
598   // An exception should have been thrown by the first listener and the second
599   // listener should have recorded the event arguments.
600   EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
601                                                 "didThrow"));
602   EXPECT_EQ("[42]", GetStringPropertyFromObject(context->Global(), context,
603                                                 "eventArgs"));
604   ASSERT_EQ(1u, logged_errors.size());
605   EXPECT_THAT(logged_errors[0],
606               testing::StartsWith("Error in event handler: Error: "
607                                   "Event handler error"));
608 }
609 
610 // Tests being notified as listeners are added or removed from events.
TEST_F(APIEventHandlerTest,CallbackNotifications)611 TEST_F(APIEventHandlerTest, CallbackNotifications) {
612   MockEventChangeHandler change_handler;
613   SetHandler(std::make_unique<APIEventHandler>(
614       change_handler.Get(), base::BindRepeating(&GetContextOwner), nullptr));
615 
616   v8::HandleScope handle_scope(isolate());
617 
618   v8::Local<v8::Context> context_a = MainContext();
619   v8::Local<v8::Context> context_b = AddContext();
620 
621   const char kEventName1[] = "onFoo";
622   const char kEventName2[] = "onBar";
623   v8::Local<v8::Object> event1_a = handler()->CreateEventInstance(
624       kEventName1, false, true, binding::kNoListenerMax, true, context_a);
625   ASSERT_FALSE(event1_a.IsEmpty());
626   v8::Local<v8::Object> event2_a = handler()->CreateEventInstance(
627       kEventName2, false, true, binding::kNoListenerMax, true, context_a);
628   ASSERT_FALSE(event2_a.IsEmpty());
629   v8::Local<v8::Object> event1_b = handler()->CreateEventInstance(
630       kEventName1, false, true, binding::kNoListenerMax, true, context_b);
631   ASSERT_FALSE(event1_b.IsEmpty());
632 
633   // Add a listener to the first event. The APIEventHandler should notify
634   // since it's a change in state (no listeners -> listeners).
635   v8::Local<v8::Function> add_listener =
636       FunctionFromString(context_a, kAddListenerFunction);
637   v8::Local<v8::Function> listener1 =
638       FunctionFromString(context_a, "(function() {})");
639   {
640     EXPECT_CALL(change_handler,
641                 Run(kEventName1,
642                     binding::EventListenersChanged::
643                         kFirstUnfilteredListenerForContextOwnerAdded,
644                     nullptr, true, context_a))
645         .Times(1);
646     v8::Local<v8::Value> argv[] = {event1_a, listener1};
647     RunFunction(add_listener, context_a, base::size(argv), argv);
648     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
649   }
650   EXPECT_EQ(1u,
651             handler()->GetNumEventListenersForTesting(kEventName1, context_a));
652 
653   // Add a second listener to the same event. We should not be notified, since
654   // the event already had listeners.
655   v8::Local<v8::Function> listener2 =
656       FunctionFromString(context_a, "(function() {})");
657   {
658     v8::Local<v8::Value> argv[] = {event1_a, listener2};
659     RunFunction(add_listener, context_a, base::size(argv), argv);
660   }
661   EXPECT_EQ(2u,
662             handler()->GetNumEventListenersForTesting(kEventName1, context_a));
663 
664   // Remove the first listener of the event. Again, since the event has
665   // listeners, we shouldn't be notified.
666   v8::Local<v8::Function> remove_listener =
667       FunctionFromString(context_a, kRemoveListenerFunction);
668   {
669     v8::Local<v8::Value> argv[] = {event1_a, listener1};
670     RunFunction(remove_listener, context_a, base::size(argv), argv);
671   }
672 
673   EXPECT_EQ(1u,
674             handler()->GetNumEventListenersForTesting(kEventName1, context_a));
675 
676   // Remove the final listener from the event. We should be notified that the
677   // event no longer has listeners.
678   {
679     EXPECT_CALL(change_handler,
680                 Run(kEventName1,
681                     binding::EventListenersChanged::
682                         kLastUnfilteredListenerForContextOwnerRemoved,
683                     nullptr, true, context_a))
684         .Times(1);
685     v8::Local<v8::Value> argv[] = {event1_a, listener2};
686     RunFunction(remove_listener, context_a, base::size(argv), argv);
687     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
688   }
689   EXPECT_EQ(0u,
690             handler()->GetNumEventListenersForTesting(kEventName1, context_a));
691 
692   // Add a listener to a separate event to ensure we receive the right
693   // notifications.
694   v8::Local<v8::Function> listener3 =
695       FunctionFromString(context_a, "(function() {})");
696   {
697     EXPECT_CALL(change_handler,
698                 Run(kEventName2,
699                     binding::EventListenersChanged::
700                         kFirstUnfilteredListenerForContextOwnerAdded,
701                     nullptr, true, context_a))
702         .Times(1);
703     v8::Local<v8::Value> argv[] = {event2_a, listener3};
704     RunFunction(add_listener, context_a, base::size(argv), argv);
705     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
706   }
707   EXPECT_EQ(1u,
708             handler()->GetNumEventListenersForTesting(kEventName2, context_a));
709 
710   {
711     EXPECT_CALL(change_handler,
712                 Run(kEventName1,
713                     binding::EventListenersChanged::
714                         kFirstUnfilteredListenerForContextOwnerAdded,
715                     nullptr, true, context_b))
716         .Times(1);
717     // And add a listener to an event in a different context to make sure the
718     // associated context is correct.
719     v8::Local<v8::Function> add_listener =
720         FunctionFromString(context_b, kAddListenerFunction);
721     v8::Local<v8::Function> listener =
722         FunctionFromString(context_b, "(function() {})");
723     v8::Local<v8::Value> argv[] = {event1_b, listener};
724     RunFunction(add_listener, context_b, base::size(argv), argv);
725     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
726   }
727   EXPECT_EQ(1u,
728             handler()->GetNumEventListenersForTesting(kEventName1, context_b));
729 
730   // When the contexts are invalidated, we should receive listener removed
731   // notifications. Additionally, since this was the context being torn down,
732   // rather than a removeListener call, was_manual should be false.
733   EXPECT_CALL(change_handler,
734               Run(kEventName2,
735                   binding::EventListenersChanged::
736                       kLastUnfilteredListenerForContextOwnerRemoved,
737                   nullptr, false, context_a))
738       .Times(1);
739   DisposeContext(context_a);
740   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
741 
742   EXPECT_CALL(change_handler,
743               Run(kEventName1,
744                   binding::EventListenersChanged::
745                       kLastUnfilteredListenerForContextOwnerRemoved,
746                   nullptr, false, context_b))
747       .Times(1);
748   DisposeContext(context_b);
749   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
750 }
751 
752 // Test registering an argument massager for a given event.
TEST_F(APIEventHandlerTest,TestArgumentMassagers)753 TEST_F(APIEventHandlerTest, TestArgumentMassagers) {
754   v8::HandleScope handle_scope(isolate());
755   v8::Local<v8::Context> context = MainContext();
756 
757   const char kEventName[] = "alpha";
758   v8::Local<v8::Object> event = handler()->CreateEventInstance(
759       kEventName, false, true, binding::kNoListenerMax, true, context);
760   ASSERT_FALSE(event.IsEmpty());
761 
762   const char kArgumentMassager[] =
763       "(function(originalArgs, dispatch) {\n"
764       "  this.originalArgs = originalArgs;\n"
765       "  dispatch(['primary', 'secondary']);\n"
766       "});";
767   v8::Local<v8::Function> massager =
768       FunctionFromString(context, kArgumentMassager);
769   handler()->RegisterArgumentMassager(context, "alpha", massager);
770 
771   const char kListenerFunction[] =
772       "(function() { this.eventArgs = Array.from(arguments); })";
773   v8::Local<v8::Function> listener_function =
774       FunctionFromString(context, kListenerFunction);
775   ASSERT_FALSE(listener_function.IsEmpty());
776 
777   {
778     v8::Local<v8::Function> add_listener_function =
779         FunctionFromString(context, kAddListenerFunction);
780     v8::Local<v8::Value> argv[] = {event, listener_function};
781     RunFunction(add_listener_function, context, base::size(argv), argv);
782   }
783 
784   const char kArguments[] = "['first','second']";
785   std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
786   ASSERT_TRUE(event_args);
787   handler()->FireEventInContext(kEventName, context, *event_args, nullptr);
788 
789   EXPECT_EQ(
790       "[\"first\",\"second\"]",
791       GetStringPropertyFromObject(context->Global(), context, "originalArgs"));
792   EXPECT_EQ(
793       "[\"primary\",\"secondary\"]",
794       GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
795 }
796 
797 // Test registering an argument massager for a given event and dispatching
798 // asynchronously.
TEST_F(APIEventHandlerTest,TestArgumentMassagersAsyncDispatch)799 TEST_F(APIEventHandlerTest, TestArgumentMassagersAsyncDispatch) {
800   v8::HandleScope handle_scope(isolate());
801   v8::Local<v8::Context> context = MainContext();
802 
803   const char kEventName[] = "alpha";
804   v8::Local<v8::Object> event = handler()->CreateEventInstance(
805       kEventName, false, true, binding::kNoListenerMax, true, context);
806   ASSERT_FALSE(event.IsEmpty());
807 
808   const char kArgumentMassager[] =
809       "(function(originalArgs, dispatch) {\n"
810       "  this.originalArgs = originalArgs;\n"
811       "  this.dispatch = dispatch;\n"
812       "});";
813   v8::Local<v8::Function> massager =
814       FunctionFromString(context, kArgumentMassager);
815   handler()->RegisterArgumentMassager(context, "alpha", massager);
816 
817   const char kListenerFunction[] =
818       "(function() { this.eventArgs = Array.from(arguments); })";
819   v8::Local<v8::Function> listener_function =
820       FunctionFromString(context, kListenerFunction);
821   ASSERT_FALSE(listener_function.IsEmpty());
822 
823   {
824     v8::Local<v8::Function> add_listener_function =
825         FunctionFromString(context, kAddListenerFunction);
826     v8::Local<v8::Value> argv[] = {event, listener_function};
827     RunFunction(add_listener_function, context, base::size(argv), argv);
828   }
829 
830   const char kArguments[] = "['first','second']";
831   std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
832   ASSERT_TRUE(event_args);
833   handler()->FireEventInContext(kEventName, context, *event_args, nullptr);
834 
835   // The massager should have been triggered, but since it doesn't call
836   // dispatch(), the listener shouldn't have been notified.
837   EXPECT_EQ(
838       "[\"first\",\"second\"]",
839       GetStringPropertyFromObject(context->Global(), context, "originalArgs"));
840   EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
841                                                      "eventArgs"));
842 
843   // Dispatch the event.
844   v8::Local<v8::Value> dispatch_value =
845       GetPropertyFromObject(context->Global(), context, "dispatch");
846   ASSERT_FALSE(dispatch_value.IsEmpty());
847   ASSERT_TRUE(dispatch_value->IsFunction());
848   v8::Local<v8::Value> dispatch_args[] = {
849       V8ValueFromScriptSource(context, "['primary', 'secondary']"),
850   };
851   RunFunction(dispatch_value.As<v8::Function>(), context,
852               base::size(dispatch_args), dispatch_args);
853 
854   EXPECT_EQ(
855       "[\"primary\",\"secondary\"]",
856       GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
857 }
858 
859 // Test registering an argument massager and never dispatching.
TEST_F(APIEventHandlerTest,TestArgumentMassagersNeverDispatch)860 TEST_F(APIEventHandlerTest, TestArgumentMassagersNeverDispatch) {
861   v8::HandleScope handle_scope(isolate());
862   v8::Local<v8::Context> context = MainContext();
863 
864   const char kEventName[] = "alpha";
865   v8::Local<v8::Object> event = handler()->CreateEventInstance(
866       kEventName, false, true, binding::kNoListenerMax, true, context);
867   ASSERT_FALSE(event.IsEmpty());
868 
869   // A massager that never dispatches.
870   const char kArgumentMassager[] = "(function(originalArgs, dispatch) {})";
871   v8::Local<v8::Function> massager =
872       FunctionFromString(context, kArgumentMassager);
873   handler()->RegisterArgumentMassager(context, "alpha", massager);
874 
875   const char kListenerFunction[] = "(function() {})";
876   v8::Local<v8::Function> listener_function =
877       FunctionFromString(context, kListenerFunction);
878   ASSERT_FALSE(listener_function.IsEmpty());
879 
880   v8::Local<v8::Function> add_listener_function =
881       FunctionFromString(context, kAddListenerFunction);
882   v8::Local<v8::Value> argv[] = {event, listener_function};
883   RunFunction(add_listener_function, context, base::size(argv), argv);
884 
885   handler()->FireEventInContext(kEventName, context, base::ListValue(),
886                                 nullptr);
887 
888   // Nothing should blow up. (We tested in the previous test that the event
889   // isn't notified without calling dispatch, so all there is to test here is
890   // that we don't crash.)
891 }
892 
893 // Test that event results of dispatch are passed to the calling argument
894 // massager. Regression test for https://crbug.com/867310.
TEST_F(APIEventHandlerTest,TestArgumentMassagersDispatchResult)895 TEST_F(APIEventHandlerTest, TestArgumentMassagersDispatchResult) {
896   v8::HandleScope handle_scope(isolate());
897   v8::Local<v8::Context> context = MainContext();
898 
899   const char kEventName[] = "alpha";
900   v8::Local<v8::Object> event = handler()->CreateEventInstance(
901       kEventName, false, true, binding::kNoListenerMax, true, context);
902   ASSERT_FALSE(event.IsEmpty());
903 
904   const char kArgumentMassager[] =
905       R"((function(originalArgs, dispatch) {
906            this.dispatchResult = dispatch(['primary']);
907          });)";
908   v8::Local<v8::Function> massager =
909       FunctionFromString(context, kArgumentMassager);
910   handler()->RegisterArgumentMassager(context, kEventName, massager);
911 
912   const char kListenerFunction[] =
913       R"((function(arg) {
914            let res = arg == 'primary' ? 'listenerSuccess' : 'listenerFailure';
915            this.listenerResult = res;
916            return res;
917          });)";
918   v8::Local<v8::Function> listener_function =
919       FunctionFromString(context, kListenerFunction);
920   ASSERT_FALSE(listener_function.IsEmpty());
921 
922   {
923     v8::Local<v8::Function> add_listener_function =
924         FunctionFromString(context, kAddListenerFunction);
925     v8::Local<v8::Value> argv[] = {event, listener_function};
926     RunFunction(add_listener_function, context, base::size(argv), argv);
927   }
928 
929   handler()->FireEventInContext(kEventName, context, base::ListValue(),
930                                 nullptr);
931 
932   EXPECT_EQ(
933       R"({"results":["listenerSuccess"]})",
934       GetStringPropertyFromObject(context->Global(), context,
935                                   "dispatchResult"));
936   EXPECT_EQ(
937       R"("listenerSuccess")",
938       GetStringPropertyFromObject(context->Global(), context,
939                                   "listenerResult"));
940 }
941 
942 // Test creating a custom event, as is done by a few of our custom bindings.
TEST_F(APIEventHandlerTest,TestCreateCustomEvent)943 TEST_F(APIEventHandlerTest, TestCreateCustomEvent) {
944   v8::HandleScope handle_scope(isolate());
945   v8::Local<v8::Context> context = MainContext();
946 
947   MockEventChangeHandler change_handler;
948   APIEventHandler handler(change_handler.Get(),
949                           base::BindRepeating(&GetContextOwner), nullptr);
950 
951   v8::Local<v8::Object> event = handler.CreateAnonymousEventInstance(context);
952   ASSERT_FALSE(event.IsEmpty());
953 
954   const char kAddListenerFunction[] =
955       "(function(event) {\n"
956       "  event.addListener(function() {\n"
957       "    this.eventArgs = Array.from(arguments);\n"
958       "  });\n"
959       "})";
960   v8::Local<v8::Value> add_listener_argv[] = {event};
961   RunFunction(FunctionFromString(context, kAddListenerFunction), context,
962               base::size(add_listener_argv), add_listener_argv);
963 
964   // Test dispatching to the listeners.
965   const char kDispatchEventFunction[] =
966       "(function(event) { event.dispatch(1, 2, 3); })";
967   v8::Local<v8::Function> dispatch_function =
968       FunctionFromString(context, kDispatchEventFunction);
969 
970   v8::Local<v8::Value> dispatch_argv[] = {event};
971   RunFunction(dispatch_function, context, base::size(dispatch_argv),
972               dispatch_argv);
973 
974   EXPECT_EQ("[1,2,3]", GetStringPropertyFromObject(context->Global(), context,
975                                                    "eventArgs"));
976 
977   // Clean up so we can re-check eventArgs.
978   ASSERT_TRUE(context->Global()
979                   ->Delete(context, gin::StringToSymbol(isolate(), "eventArgs"))
980                   .FromJust());
981 
982   // Invalidate the event and try dispatching again. Nothing should happen.
983   handler.InvalidateCustomEvent(context, event);
984   RunFunction(dispatch_function, context, base::size(dispatch_argv),
985               dispatch_argv);
986   EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
987                                                      "eventArgs"));
988 }
989 
990 // Test adding a custom event with a cyclic dependency. Nothing should leak.
TEST_F(APIEventHandlerTest,TestCreateCustomEventWithCyclicDependency)991 TEST_F(APIEventHandlerTest, TestCreateCustomEventWithCyclicDependency) {
992   v8::HandleScope handle_scope(isolate());
993   v8::Local<v8::Context> context = MainContext();
994 
995   MockEventChangeHandler change_handler;
996   APIEventHandler handler(change_handler.Get(),
997                           base::BindRepeating(&GetContextOwner), nullptr);
998 
999   v8::Local<v8::Object> event = handler.CreateAnonymousEventInstance(context);
1000   ASSERT_FALSE(event.IsEmpty());
1001 
1002   const char kAddListenerFunction[] =
1003       "(function(event) {\n"
1004       "  event.addListener(function() {}.bind(null, event));\n"
1005       "})";
1006   v8::Local<v8::Value> add_listener_argv[] = {event};
1007   RunFunction(FunctionFromString(context, kAddListenerFunction), context,
1008               base::size(add_listener_argv), add_listener_argv);
1009 
1010   DisposeContext(context);
1011 }
1012 
TEST_F(APIEventHandlerTest,TestUnmanagedEvents)1013 TEST_F(APIEventHandlerTest, TestUnmanagedEvents) {
1014   v8::HandleScope handle_scope(isolate());
1015   v8::Local<v8::Context> context = MainContext();
1016 
1017   auto fail_on_notified =
1018       [](const std::string& event_name, binding::EventListenersChanged changed,
1019          const base::DictionaryValue* filter, bool was_manual,
1020          v8::Local<v8::Context> context) { ADD_FAILURE(); };
1021 
1022   APIEventHandler handler(base::BindRepeating(fail_on_notified),
1023                           base::BindRepeating(&GetContextOwner), nullptr);
1024 
1025   const char kEventName[] = "alpha";
1026   v8::Local<v8::Object> event = handler.CreateEventInstance(
1027       kEventName, false, true, binding::kNoListenerMax, false, context);
1028 
1029   const char kListener[] =
1030       "(function() {\n"
1031       "  this.eventArgs = Array.from(arguments);\n"
1032       "});";
1033   v8::Local<v8::Function> listener = FunctionFromString(context, kListener);
1034 
1035   {
1036     const char kAddListener[] =
1037         "(function(event, listener) { event.addListener(listener); })";
1038     v8::Local<v8::Value> args[] = {event, listener};
1039     RunFunction(FunctionFromString(context, kAddListener), context,
1040                 base::size(args), args);
1041   }
1042 
1043   EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
1044 
1045   handler.FireEventInContext(kEventName, context,
1046                              *ListValueFromString("[1, 'foo']"), nullptr);
1047 
1048   EXPECT_EQ("[1,\"foo\"]", GetStringPropertyFromObject(context->Global(),
1049                                                        context, "eventArgs"));
1050 
1051   {
1052     const char kRemoveListener[] =
1053         "(function(event, listener) { event.removeListener(listener); })";
1054     v8::Local<v8::Value> args[] = {event, listener};
1055     RunFunction(FunctionFromString(context, kRemoveListener), context,
1056                 base::size(args), args);
1057   }
1058 
1059   EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
1060 }
1061 
1062 // Test callback notifications for events that don't support lazy listeners.
TEST_F(APIEventHandlerTest,TestEventsWithoutLazyListeners)1063 TEST_F(APIEventHandlerTest, TestEventsWithoutLazyListeners) {
1064   MockEventChangeHandler change_handler;
1065   APIEventHandler handler(change_handler.Get(),
1066                           base::BindRepeating(&GetContextOwner), nullptr);
1067 
1068   v8::HandleScope handle_scope(isolate());
1069   v8::Local<v8::Context> context = MainContext();
1070 
1071   const char kLazyListenersSupported[] = "supportsLazyListeners";
1072   const char kLazyListenersNotSupported[] = "noLazyListeners";
1073   v8::Local<v8::Object> lazy_listeners_supported =
1074       handler.CreateEventInstance(kLazyListenersSupported, false, true,
1075                                   binding::kNoListenerMax, true, context);
1076   v8::Local<v8::Object> lazy_listeners_not_supported =
1077       handler.CreateEventInstance(kLazyListenersNotSupported, false, false,
1078                                   binding::kNoListenerMax, true, context);
1079   ASSERT_FALSE(lazy_listeners_not_supported.IsEmpty());
1080 
1081   v8::Local<v8::Function> add_listener =
1082       FunctionFromString(context, kAddListenerFunction);
1083   v8::Local<v8::Function> listener =
1084       FunctionFromString(context, "(function() {})");
1085   {
1086     EXPECT_CALL(change_handler,
1087                 Run(kLazyListenersSupported,
1088                     binding::EventListenersChanged::
1089                         kFirstUnfilteredListenerForContextOwnerAdded,
1090                     nullptr, true, context))
1091         .Times(1);
1092     v8::Local<v8::Value> argv[] = {lazy_listeners_supported, listener};
1093     RunFunction(add_listener, context, base::size(argv), argv);
1094     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1095   }
1096 
1097   {
1098     EXPECT_CALL(change_handler,
1099                 Run(kLazyListenersNotSupported,
1100                     binding::EventListenersChanged::
1101                         kFirstUnfilteredListenerForContextOwnerAdded,
1102                     nullptr, false, context))
1103         .Times(1);
1104     v8::Local<v8::Value> argv[] = {lazy_listeners_not_supported, listener};
1105     RunFunction(add_listener, context, base::size(argv), argv);
1106     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1107   }
1108 
1109   v8::Local<v8::Function> remove_listener =
1110       FunctionFromString(context, kRemoveListenerFunction);
1111   {
1112     EXPECT_CALL(change_handler,
1113                 Run(kLazyListenersSupported,
1114                     binding::EventListenersChanged::
1115                         kLastUnfilteredListenerForContextOwnerRemoved,
1116                     nullptr, true, context))
1117         .Times(1);
1118     v8::Local<v8::Value> argv[] = {lazy_listeners_supported, listener};
1119     RunFunction(remove_listener, context, base::size(argv), argv);
1120     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1121   }
1122 
1123   {
1124     EXPECT_CALL(change_handler,
1125                 Run(kLazyListenersNotSupported,
1126                     binding::EventListenersChanged::
1127                         kLastUnfilteredListenerForContextOwnerRemoved,
1128                     nullptr, false, context))
1129         .Times(1);
1130     v8::Local<v8::Value> argv[] = {lazy_listeners_not_supported, listener};
1131     RunFunction(remove_listener, context, base::size(argv), argv);
1132     ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1133   }
1134 
1135   DisposeContext(context);
1136 }
1137 
1138 // Tests dispatching events while script is suspended.
TEST_F(APIEventHandlerTest,TestDispatchingEventsWhileScriptSuspended)1139 TEST_F(APIEventHandlerTest, TestDispatchingEventsWhileScriptSuspended) {
1140   const char kEventName[] = "alpha";
1141   v8::HandleScope handle_scope(isolate());
1142   v8::Local<v8::Context> context = MainContext();
1143 
1144   v8::Local<v8::Object> event = handler()->CreateEventInstance(
1145       kEventName, false, true, binding::kNoListenerMax, true, context);
1146 
1147   const char kListenerFunction[] = "(function() { this.eventFired = true; });";
1148   v8::Local<v8::Function> listener =
1149       FunctionFromString(context, kListenerFunction);
1150 
1151   {
1152     v8::Local<v8::Function> add_listener_function =
1153         FunctionFromString(context, kAddListenerFunction);
1154     v8::Local<v8::Value> argv[] = {event, listener};
1155     RunFunction(add_listener_function, context, base::size(argv), argv);
1156   }
1157 
1158   {
1159     // Suspend script and fire an event. The listener should *not* be notified
1160     // while script is suspended.
1161     TestJSRunner::Suspension script_suspension;
1162     handler()->FireEventInContext(kEventName, context, base::ListValue(),
1163                                   nullptr);
1164     base::RunLoop().RunUntilIdle();
1165     EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(),
1166                                                        context, "eventFired"));
1167   }
1168 
1169   // After script resumes, the listener should be notified.
1170   EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
1171                                                 "eventFired"));
1172 }
1173 
1174 // Tests catching errors thrown by listeners while dispatching after script
1175 // suspension.
TEST_F(APIEventHandlerTest,TestListenersThrowingExceptionsAfterScriptSuspension)1176 TEST_F(APIEventHandlerTest,
1177        TestListenersThrowingExceptionsAfterScriptSuspension) {
1178   auto log_error =
1179       [](std::vector<std::string>* errors_out, v8::Local<v8::Context> context,
1180          const std::string& error) { errors_out->push_back(error); };
1181 
1182   std::vector<std::string> logged_errors;
1183   ExceptionHandler exception_handler(
1184       base::BindRepeating(log_error, &logged_errors));
1185   SetHandler(std::make_unique<APIEventHandler>(
1186       base::DoNothing(), base::BindRepeating(&GetContextOwner),
1187       &exception_handler));
1188 
1189   const char kEventName[] = "alpha";
1190   v8::HandleScope handle_scope(isolate());
1191   v8::Local<v8::Context> context = MainContext();
1192 
1193   v8::Local<v8::Object> event = handler()->CreateEventInstance(
1194       kEventName, false, true, binding::kNoListenerMax, true, context);
1195 
1196   const char kListenerFunction[] =
1197       "(function() {\n"
1198       "  this.eventFired = true;\n"
1199       "  throw new Error('hahaha');\n"
1200       "});";
1201   v8::Local<v8::Function> listener =
1202       FunctionFromString(context, kListenerFunction);
1203 
1204   {
1205     v8::Local<v8::Function> add_listener_function =
1206         FunctionFromString(context, kAddListenerFunction);
1207     v8::Local<v8::Value> argv[] = {event, listener};
1208     RunFunction(add_listener_function, context, base::size(argv), argv);
1209   }
1210 
1211   TestJSRunner::AllowErrors allow_errors;
1212   {
1213     // Suspend script and fire an event. The listener should not be notified,
1214     // and no errors should be logged.
1215     TestJSRunner::Suspension script_suspension;
1216     handler()->FireEventInContext(kEventName, context, base::ListValue(),
1217                                   nullptr);
1218     base::RunLoop().RunUntilIdle();
1219     EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(),
1220                                                        context, "eventFired"));
1221     EXPECT_TRUE(logged_errors.empty());
1222   }
1223 
1224   // Once script resumes, the listener should have been notifed, and we should
1225   // have caught the error.
1226   EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
1227                                                 "eventFired"));
1228   ASSERT_EQ(1u, logged_errors.size());
1229   EXPECT_THAT(logged_errors[0],
1230               testing::StartsWith("Error in event handler: Error: hahaha"));
1231 }
1232 
1233 // Tests dispatching events when listeners are removed between when an event
1234 // was fired (during script suspension) and when the script runs.
TEST_F(APIEventHandlerTest,TestDispatchingEventAfterListenersRemovedAfterScriptSuspension)1235 TEST_F(APIEventHandlerTest,
1236        TestDispatchingEventAfterListenersRemovedAfterScriptSuspension) {
1237   const char kEventName[] = "alpha";
1238   v8::HandleScope handle_scope(isolate());
1239   v8::Local<v8::Context> context = MainContext();
1240 
1241   v8::Local<v8::Object> event = handler()->CreateEventInstance(
1242       kEventName, false, true, binding::kNoListenerMax, true, context);
1243 
1244   const char kListenerFunction1[] =
1245       "(function() { this.eventFired1 = true; });";
1246   const char kListenerFunction2[] =
1247       "(function() { this.eventFired2 = true; });";
1248   v8::Local<v8::Function> listener1 =
1249       FunctionFromString(context, kListenerFunction1);
1250   v8::Local<v8::Function> listener2 =
1251       FunctionFromString(context, kListenerFunction2);
1252 
1253   // Add two event listeners.
1254   {
1255     v8::Local<v8::Function> add_listener_function =
1256         FunctionFromString(context, kAddListenerFunction);
1257     {
1258       v8::Local<v8::Value> argv[] = {event, listener1};
1259       RunFunction(add_listener_function, context, base::size(argv), argv);
1260     }
1261     {
1262       v8::Local<v8::Value> argv[] = {event, listener2};
1263       RunFunction(add_listener_function, context, base::size(argv), argv);
1264     }
1265   }
1266   EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kEventName, context));
1267 
1268   {
1269     // Suspend script, and then queue up a call to remove the first listener.
1270     TestJSRunner::Suspension script_suspension;
1271     v8::Local<v8::Function> remove_listener_function =
1272         FunctionFromString(context, kRemoveListenerFunction);
1273     {
1274       v8::Local<v8::Value> argv[] = {event, listener1};
1275       // Note: Use JSRunner() so that script suspension is respected.
1276       JSRunner::Get(context)->RunJSFunction(remove_listener_function, context,
1277                                             base::size(argv), argv);
1278     }
1279 
1280     // Since script has been suspended, there should still be two listeners, and
1281     // neither should have been notified.
1282     EXPECT_EQ(2u,
1283               handler()->GetNumEventListenersForTesting(kEventName, context));
1284     handler()->FireEventInContext(kEventName, context, base::ListValue(),
1285                                   nullptr);
1286     base::RunLoop().RunUntilIdle();
1287     EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(),
1288                                                        context, "eventFired1"));
1289     EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(),
1290                                                        context, "eventFired2"));
1291   }
1292 
1293   // Once script resumes, the first listener should have been removed and the
1294   // event should have been fired. Since the listener was removed before the
1295   // event dispatch ran in JS, the first listener should *not* have been
1296   // notified.
1297   EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
1298   EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
1299                                                      "eventFired1"));
1300   EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
1301                                                 "eventFired2"));
1302 }
1303 
1304 // Test that notifications are properly fired for multiple events with the
1305 // same context owner.
TEST_F(APIEventHandlerTest,TestListenersFromDifferentContextsWithTheSameOwner)1306 TEST_F(APIEventHandlerTest,
1307        TestListenersFromDifferentContextsWithTheSameOwner) {
1308   v8::HandleScope handle_scope(isolate());
1309   v8::Local<v8::Context> context_alpha1 = MainContext();
1310   v8::Local<v8::Context> context_alpha2 = AddContext();
1311   v8::Local<v8::Context> context_beta1 = AddContext();
1312 
1313   // Associate two v8::Contexts with the same owner, and a third with a separate
1314   // owner.
1315   auto get_context_owner = [context_alpha1, context_alpha2,
1316                             context_beta1](v8::Local<v8::Context> context) {
1317     if (context == context_alpha1 || context == context_alpha2)
1318       return std::string("alpha");
1319     if (context == context_beta1)
1320       return std::string("beta");
1321     ADD_FAILURE();
1322     return std::string();
1323   };
1324 
1325   MockEventChangeHandler change_handler;
1326   APIEventHandler handler(change_handler.Get(),
1327                           base::BindLambdaForTesting(get_context_owner),
1328                           nullptr);
1329 
1330   const char kEventName[] = "alpha";
1331   v8::Local<v8::Object> event_alpha1 = handler.CreateEventInstance(
1332       kEventName, false, true, binding::kNoListenerMax, true, context_alpha1);
1333   ASSERT_FALSE(event_alpha1.IsEmpty());
1334   v8::Local<v8::Object> event_alpha2 = handler.CreateEventInstance(
1335       kEventName, false, true, binding::kNoListenerMax, true, context_alpha2);
1336   ASSERT_FALSE(event_alpha2.IsEmpty());
1337   v8::Local<v8::Object> event_beta1 = handler.CreateEventInstance(
1338       kEventName, false, true, binding::kNoListenerMax, true, context_beta1);
1339   ASSERT_FALSE(event_beta1.IsEmpty());
1340 
1341   // Add a listener to the first event. The APIEventHandler should notify
1342   // since it's a change in state (no listeners -> listeners).
1343   v8::Local<v8::Function> listener_alpha1 =
1344       FunctionFromString(context_alpha1, "(function() {})");
1345   EXPECT_CALL(change_handler,
1346               Run(kEventName,
1347                   binding::EventListenersChanged::
1348                       kFirstUnfilteredListenerForContextOwnerAdded,
1349                   nullptr, true, context_alpha1))
1350       .Times(1);
1351   AddListener(context_alpha1, listener_alpha1, event_alpha1);
1352   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1353 
1354   // Adding a listener to the same event in a different context that is still
1355   // associated with the same owner should fire a notification for the context,
1356   // but not the context owner.
1357   EXPECT_CALL(change_handler, Run(kEventName,
1358                                   binding::EventListenersChanged::
1359                                       kFirstUnfilteredListenerForContextAdded,
1360                                   nullptr, true, context_alpha2))
1361       .Times(1);
1362   v8::Local<v8::Function> listener_alpha2 =
1363       FunctionFromString(context_alpha2, "(function() {})");
1364   AddListener(context_alpha2, listener_alpha2, event_alpha2);
1365   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1366 
1367   // Adding a listener in a separate context should fire a notification.
1368   v8::Local<v8::Function> listener_beta1 =
1369       FunctionFromString(context_alpha1, "(function() {})");
1370   EXPECT_CALL(change_handler,
1371               Run(kEventName,
1372                   binding::EventListenersChanged::
1373                       kFirstUnfilteredListenerForContextOwnerAdded,
1374                   nullptr, true, context_beta1))
1375       .Times(1);
1376   AddListener(context_beta1, listener_beta1, event_beta1);
1377   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1378 
1379   // Removing one of the listeners from the alpha context should notify about
1380   // the context, but not the context owner (since there are multiple listeners
1381   // for the context owner).
1382   EXPECT_CALL(change_handler, Run(kEventName,
1383                                   binding::EventListenersChanged::
1384                                       kLastUnfilteredListenerForContextRemoved,
1385                                   nullptr, true, context_alpha1))
1386       .Times(1);
1387   RemoveListener(context_alpha1, listener_alpha1, event_alpha1);
1388   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1389 
1390   // Removing the final listener should fire a notification for the context
1391   // owner.
1392   EXPECT_CALL(change_handler,
1393               Run(kEventName,
1394                   binding::EventListenersChanged::
1395                       kLastUnfilteredListenerForContextOwnerRemoved,
1396                   nullptr, true, context_alpha2))
1397       .Times(1);
1398   RemoveListener(context_alpha2, listener_alpha2, event_alpha2);
1399   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1400 
1401   // And removing the only listener for the beta context should fire a
1402   // notification.
1403   EXPECT_CALL(change_handler,
1404               Run(kEventName,
1405                   binding::EventListenersChanged::
1406                       kLastUnfilteredListenerForContextOwnerRemoved,
1407                   nullptr, true, context_beta1))
1408       .Times(1);
1409   RemoveListener(context_beta1, listener_beta1, event_beta1);
1410   ::testing::Mock::VerifyAndClearExpectations(&change_handler);
1411 }
1412 
1413 }  // namespace extensions
1414