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