Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Side by Side Diff: extensions/renderer/api_event_handler_unittest.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/renderer/api_event_handler.cc ('k') | extensions/renderer/api_event_listeners.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/api_event_handler.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/optional.h"
11 #include "base/test/mock_callback.h"
12 #include "base/values.h"
13 #include "extensions/common/event_filtering_info.h"
14 #include "extensions/renderer/api_binding_test.h"
15 #include "extensions/renderer/api_binding_test_util.h"
16 #include "gin/arguments.h"
17 #include "gin/converter.h"
18 #include "gin/public/context_holder.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20
21 namespace extensions {
22
23 namespace {
24
25 using MockEventChangeHandler = ::testing::StrictMock<
26 base::MockCallback<APIEventHandler::EventListenersChangedMethod>>;
27
28 void DoNothingOnEventListenersChanged(const std::string& event_name,
29 binding::EventListenersChanged change,
30 const base::DictionaryValue* value,
31 bool was_manual,
32 v8::Local<v8::Context> context) {}
33
34 class APIEventHandlerTest : public APIBindingTest {
35 protected:
36 APIEventHandlerTest() {}
37 ~APIEventHandlerTest() override {}
38
39 void SetUp() override {
40 APIBindingTest::SetUp();
41 handler_ = base::MakeUnique<APIEventHandler>(
42 base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
43 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
44 base::Bind(&DoNothingOnEventListenersChanged));
45 }
46
47 void TearDown() override {
48 DisposeAllContexts();
49 handler_.reset();
50 APIBindingTest::TearDown();
51 }
52
53 void OnWillDisposeContext(v8::Local<v8::Context> context) override {
54 ASSERT_TRUE(handler_);
55 handler_->InvalidateContext(context);
56 }
57
58 void SetHandler(std::unique_ptr<APIEventHandler> handler) {
59 handler_ = std::move(handler);
60 }
61
62 APIEventHandler* handler() { return handler_.get(); }
63
64 private:
65 std::unique_ptr<APIEventHandler> handler_;
66
67 DISALLOW_COPY_AND_ASSIGN(APIEventHandlerTest);
68 };
69
70 } // namespace
71
72 // Tests adding, removing, and querying event listeners by calling the
73 // associated methods on the JS object.
74 TEST_F(APIEventHandlerTest, AddingRemovingAndQueryingEventListeners) {
75 const char kEventName[] = "alpha";
76 v8::HandleScope handle_scope(isolate());
77 v8::Local<v8::Context> context = MainContext();
78
79 v8::Local<v8::Object> event = handler()->CreateEventInstance(
80 kEventName, false, binding::kNoListenerMax, true, context);
81 ASSERT_FALSE(event.IsEmpty());
82
83 EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
84
85 const char kListenerFunction[] = "(function() {})";
86 v8::Local<v8::Function> listener_function =
87 FunctionFromString(context, kListenerFunction);
88 ASSERT_FALSE(listener_function.IsEmpty());
89
90 const char kAddListenerFunction[] =
91 "(function(event, listener) { event.addListener(listener); })";
92 v8::Local<v8::Function> add_listener_function =
93 FunctionFromString(context, kAddListenerFunction);
94
95 {
96 v8::Local<v8::Value> argv[] = {event, listener_function};
97 RunFunction(add_listener_function, context, arraysize(argv), argv);
98 }
99 // There should only be one listener on the event.
100 EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
101
102 {
103 v8::Local<v8::Value> argv[] = {event, listener_function};
104 RunFunction(add_listener_function, context, arraysize(argv), argv);
105 }
106 // Trying to add the same listener again should be a no-op.
107 EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
108
109 // Test hasListener returns true for a listener that is present.
110 const char kHasListenerFunction[] =
111 "(function(event, listener) { return event.hasListener(listener); })";
112 v8::Local<v8::Function> has_listener_function =
113 FunctionFromString(context, kHasListenerFunction);
114 {
115 v8::Local<v8::Value> argv[] = {event, listener_function};
116 v8::Local<v8::Value> result =
117 RunFunction(has_listener_function, context, arraysize(argv), argv);
118 bool has_listener = false;
119 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate(), result, &has_listener));
120 EXPECT_TRUE(has_listener);
121 }
122
123 // Test that hasListener returns false for a listener that isn't present.
124 {
125 v8::Local<v8::Function> not_a_listener =
126 FunctionFromString(context, "(function() {})");
127 v8::Local<v8::Value> argv[] = {event, not_a_listener};
128 v8::Local<v8::Value> result =
129 RunFunction(has_listener_function, context, arraysize(argv), argv);
130 bool has_listener = false;
131 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate(), result, &has_listener));
132 EXPECT_FALSE(has_listener);
133 }
134
135 // Test hasListeners returns true
136 const char kHasListenersFunction[] =
137 "(function(event) { return event.hasListeners(); })";
138 v8::Local<v8::Function> has_listeners_function =
139 FunctionFromString(context, kHasListenersFunction);
140 {
141 v8::Local<v8::Value> argv[] = {event};
142 v8::Local<v8::Value> result =
143 RunFunction(has_listeners_function, context, arraysize(argv), argv);
144 bool has_listeners = false;
145 EXPECT_TRUE(
146 gin::Converter<bool>::FromV8(isolate(), result, &has_listeners));
147 EXPECT_TRUE(has_listeners);
148 }
149
150 const char kRemoveListenerFunction[] =
151 "(function(event, listener) { event.removeListener(listener); })";
152 v8::Local<v8::Function> remove_listener_function =
153 FunctionFromString(context, kRemoveListenerFunction);
154 {
155 v8::Local<v8::Value> argv[] = {event, listener_function};
156 RunFunction(remove_listener_function, context, arraysize(argv), argv);
157 }
158 EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
159
160 {
161 v8::Local<v8::Value> argv[] = {event};
162 v8::Local<v8::Value> result =
163 RunFunction(has_listeners_function, context, arraysize(argv), argv);
164 bool has_listeners = false;
165 EXPECT_TRUE(
166 gin::Converter<bool>::FromV8(isolate(), result, &has_listeners));
167 EXPECT_FALSE(has_listeners);
168 }
169 }
170
171 // Tests listening for and firing different events.
172 TEST_F(APIEventHandlerTest, FiringEvents) {
173 const char kAlphaName[] = "alpha";
174 const char kBetaName[] = "beta";
175 v8::HandleScope handle_scope(isolate());
176 v8::Local<v8::Context> context = MainContext();
177
178 v8::Local<v8::Object> alpha_event = handler()->CreateEventInstance(
179 kAlphaName, false, binding::kNoListenerMax, true, context);
180 v8::Local<v8::Object> beta_event = handler()->CreateEventInstance(
181 kBetaName, false, binding::kNoListenerMax, true, context);
182 ASSERT_FALSE(alpha_event.IsEmpty());
183 ASSERT_FALSE(beta_event.IsEmpty());
184
185 const char kAlphaListenerFunction1[] =
186 "(function() {\n"
187 " if (!this.alphaCount1) this.alphaCount1 = 0;\n"
188 " ++this.alphaCount1;\n"
189 "});\n";
190 v8::Local<v8::Function> alpha_listener1 =
191 FunctionFromString(context, kAlphaListenerFunction1);
192 const char kAlphaListenerFunction2[] =
193 "(function() {\n"
194 " if (!this.alphaCount2) this.alphaCount2 = 0;\n"
195 " ++this.alphaCount2;\n"
196 "});\n";
197 v8::Local<v8::Function> alpha_listener2 =
198 FunctionFromString(context, kAlphaListenerFunction2);
199 const char kBetaListenerFunction[] =
200 "(function() {\n"
201 " if (!this.betaCount) this.betaCount = 0;\n"
202 " ++this.betaCount;\n"
203 "});\n";
204 v8::Local<v8::Function> beta_listener =
205 FunctionFromString(context, kBetaListenerFunction);
206 ASSERT_FALSE(alpha_listener1.IsEmpty());
207 ASSERT_FALSE(alpha_listener2.IsEmpty());
208 ASSERT_FALSE(beta_listener.IsEmpty());
209
210 {
211 const char kAddListenerFunction[] =
212 "(function(event, listener) { event.addListener(listener); })";
213 v8::Local<v8::Function> add_listener_function =
214 FunctionFromString(context, kAddListenerFunction);
215 {
216 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener1};
217 RunFunction(add_listener_function, context, arraysize(argv), argv);
218 }
219 {
220 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener2};
221 RunFunction(add_listener_function, context, arraysize(argv), argv);
222 }
223 {
224 v8::Local<v8::Value> argv[] = {beta_event, beta_listener};
225 RunFunction(add_listener_function, context, arraysize(argv), argv);
226 }
227 }
228
229 EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kAlphaName, context));
230 EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kBetaName, context));
231
232 auto get_fired_count = [&context](const char* name) {
233 v8::Local<v8::Value> res =
234 GetPropertyFromObject(context->Global(), context, name);
235 if (res->IsUndefined())
236 return 0;
237 int32_t count = 0;
238 EXPECT_TRUE(
239 gin::Converter<int32_t>::FromV8(context->GetIsolate(), res, &count))
240 << name;
241 return count;
242 };
243
244 EXPECT_EQ(0, get_fired_count("alphaCount1"));
245 EXPECT_EQ(0, get_fired_count("alphaCount2"));
246 EXPECT_EQ(0, get_fired_count("betaCount"));
247
248 handler()->FireEventInContext(kAlphaName, context, base::ListValue(),
249 EventFilteringInfo());
250 EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kAlphaName, context));
251 EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kBetaName, context));
252
253 EXPECT_EQ(1, get_fired_count("alphaCount1"));
254 EXPECT_EQ(1, get_fired_count("alphaCount2"));
255 EXPECT_EQ(0, get_fired_count("betaCount"));
256
257 handler()->FireEventInContext(kAlphaName, context, base::ListValue(),
258 EventFilteringInfo());
259 EXPECT_EQ(2, get_fired_count("alphaCount1"));
260 EXPECT_EQ(2, get_fired_count("alphaCount2"));
261 EXPECT_EQ(0, get_fired_count("betaCount"));
262
263 handler()->FireEventInContext(kBetaName, context, base::ListValue(),
264 EventFilteringInfo());
265 EXPECT_EQ(2, get_fired_count("alphaCount1"));
266 EXPECT_EQ(2, get_fired_count("alphaCount2"));
267 EXPECT_EQ(1, get_fired_count("betaCount"));
268 }
269
270 // Tests firing events with arguments.
271 TEST_F(APIEventHandlerTest, EventArguments) {
272 v8::HandleScope handle_scope(isolate());
273 v8::Local<v8::Context> context = MainContext();
274
275 const char kEventName[] = "alpha";
276 v8::Local<v8::Object> event = handler()->CreateEventInstance(
277 kEventName, false, binding::kNoListenerMax, true, context);
278 ASSERT_FALSE(event.IsEmpty());
279
280 const char kListenerFunction[] =
281 "(function() { this.eventArgs = Array.from(arguments); })";
282 v8::Local<v8::Function> listener_function =
283 FunctionFromString(context, kListenerFunction);
284 ASSERT_FALSE(listener_function.IsEmpty());
285
286 {
287 const char kAddListenerFunction[] =
288 "(function(event, listener) { event.addListener(listener); })";
289 v8::Local<v8::Function> add_listener_function =
290 FunctionFromString(context, kAddListenerFunction);
291 v8::Local<v8::Value> argv[] = {event, listener_function};
292 RunFunction(add_listener_function, context, arraysize(argv), argv);
293 }
294
295 const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
296 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
297 ASSERT_TRUE(event_args);
298 handler()->FireEventInContext(kEventName, context, *event_args,
299 EventFilteringInfo());
300
301 EXPECT_EQ(
302 ReplaceSingleQuotes(kArguments),
303 GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
304 }
305
306 // Test dispatching events to multiple contexts.
307 TEST_F(APIEventHandlerTest, MultipleContexts) {
308 v8::HandleScope handle_scope(isolate());
309
310 v8::Local<v8::Context> context_a = MainContext();
311 v8::Local<v8::Context> context_b = AddContext();
312
313 const char kEventName[] = "onFoo";
314
315
316 v8::Local<v8::Function> listener_a = FunctionFromString(
317 context_a, "(function(arg) { this.eventArgs = arg + 'alpha'; })");
318 ASSERT_FALSE(listener_a.IsEmpty());
319 v8::Local<v8::Function> listener_b = FunctionFromString(
320 context_b, "(function(arg) { this.eventArgs = arg + 'beta'; })");
321 ASSERT_FALSE(listener_b.IsEmpty());
322
323 // Create two instances of the same event in different contexts.
324 v8::Local<v8::Object> event_a = handler()->CreateEventInstance(
325 kEventName, false, binding::kNoListenerMax, true, context_a);
326 ASSERT_FALSE(event_a.IsEmpty());
327 v8::Local<v8::Object> event_b = handler()->CreateEventInstance(
328 kEventName, false, binding::kNoListenerMax, true, context_b);
329 ASSERT_FALSE(event_b.IsEmpty());
330
331 // Add two separate listeners to the event, one in each context.
332 const char kAddListenerFunction[] =
333 "(function(event, listener) { event.addListener(listener); })";
334 {
335 v8::Local<v8::Function> add_listener_a =
336 FunctionFromString(context_a, kAddListenerFunction);
337 v8::Local<v8::Value> argv[] = {event_a, listener_a};
338 RunFunction(add_listener_a, context_a, arraysize(argv), argv);
339 }
340 EXPECT_EQ(1u,
341 handler()->GetNumEventListenersForTesting(kEventName, context_a));
342 EXPECT_EQ(0u,
343 handler()->GetNumEventListenersForTesting(kEventName, context_b));
344
345 {
346 v8::Local<v8::Function> add_listener_b =
347 FunctionFromString(context_b, kAddListenerFunction);
348 v8::Local<v8::Value> argv[] = {event_b, listener_b};
349 RunFunction(add_listener_b, context_b, arraysize(argv), argv);
350 }
351 EXPECT_EQ(1u,
352 handler()->GetNumEventListenersForTesting(kEventName, context_a));
353 EXPECT_EQ(1u,
354 handler()->GetNumEventListenersForTesting(kEventName, context_b));
355
356 // Dispatch the event in context_a - the listener in context_b should not be
357 // notified.
358 std::unique_ptr<base::ListValue> arguments_a =
359 ListValueFromString("['result_a:']");
360 ASSERT_TRUE(arguments_a);
361
362 handler()->FireEventInContext(kEventName, context_a, *arguments_a,
363 EventFilteringInfo());
364 {
365 EXPECT_EQ("\"result_a:alpha\"",
366 GetStringPropertyFromObject(context_a->Global(), context_a,
367 "eventArgs"));
368 }
369 {
370 EXPECT_EQ("undefined", GetStringPropertyFromObject(context_b->Global(),
371 context_b, "eventArgs"));
372 }
373
374 // Dispatch the event in context_b - the listener in context_a should not be
375 // notified.
376 std::unique_ptr<base::ListValue> arguments_b =
377 ListValueFromString("['result_b:']");
378 ASSERT_TRUE(arguments_b);
379 handler()->FireEventInContext(kEventName, context_b, *arguments_b,
380 EventFilteringInfo());
381 {
382 EXPECT_EQ("\"result_a:alpha\"",
383 GetStringPropertyFromObject(context_a->Global(), context_a,
384 "eventArgs"));
385 }
386 {
387 EXPECT_EQ("\"result_b:beta\"",
388 GetStringPropertyFromObject(context_b->Global(), context_b,
389 "eventArgs"));
390 }
391 }
392
393 TEST_F(APIEventHandlerTest, DifferentCallingMethods) {
394 v8::HandleScope handle_scope(isolate());
395 v8::Local<v8::Context> context = MainContext();
396
397 const char kEventName[] = "alpha";
398 v8::Local<v8::Object> event = handler()->CreateEventInstance(
399 kEventName, false, binding::kNoListenerMax, true, context);
400 ASSERT_FALSE(event.IsEmpty());
401
402 const char kAddListenerOnNull[] =
403 "(function(event) {\n"
404 " event.addListener.call(null, function() {});\n"
405 "})";
406 {
407 v8::Local<v8::Value> args[] = {event};
408 // TODO(devlin): This is the generic type error that gin throws. It's not
409 // very descriptive, nor does it match the web (which would just say e.g.
410 // "Illegal invocation"). Might be worth updating later.
411 RunFunctionAndExpectError(
412 FunctionFromString(context, kAddListenerOnNull),
413 context, 1, args,
414 "Uncaught TypeError: Error processing argument at index -1,"
415 " conversion failure from undefined");
416 }
417 EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
418
419 const char kAddListenerOnEvent[] =
420 "(function(event) {\n"
421 " event.addListener.call(event, function() {});\n"
422 "})";
423 {
424 v8::Local<v8::Value> args[] = {event};
425 RunFunction(FunctionFromString(context, kAddListenerOnEvent),
426 context, 1, args);
427 }
428 EXPECT_EQ(1u, handler()->GetNumEventListenersForTesting(kEventName, context));
429
430 // Call addListener with a function that captures the event, creating a cycle.
431 // If we don't properly clean up, the context will leak.
432 const char kAddListenerOnEventWithCapture[] =
433 "(function(event) {\n"
434 " event.addListener(function listener() {\n"
435 " event.hasListener(listener);\n"
436 " });\n"
437 "})";
438 {
439 v8::Local<v8::Value> args[] = {event};
440 RunFunction(FunctionFromString(context, kAddListenerOnEventWithCapture),
441 context, 1, args);
442 }
443 EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kEventName, context));
444 }
445
446 TEST_F(APIEventHandlerTest, TestDispatchFromJs) {
447 v8::HandleScope handle_scope(isolate());
448 v8::Local<v8::Context> context = MainContext();
449
450 v8::Local<v8::Object> event = handler()->CreateEventInstance(
451 "alpha", false, binding::kNoListenerMax, true, context);
452 ASSERT_FALSE(event.IsEmpty());
453
454 const char kListenerFunction[] =
455 "(function() {\n"
456 " this.eventArgs = Array.from(arguments);\n"
457 "});";
458 v8::Local<v8::Function> listener =
459 FunctionFromString(context, kListenerFunction);
460
461 const char kAddListenerFunction[] =
462 "(function(event, listener) { event.addListener(listener); })";
463 v8::Local<v8::Function> add_listener_function =
464 FunctionFromString(context, kAddListenerFunction);
465
466 {
467 v8::Local<v8::Value> argv[] = {event, listener};
468 RunFunctionOnGlobal(add_listener_function, context, arraysize(argv), argv);
469 }
470
471 v8::Local<v8::Function> fire_event_function =
472 FunctionFromString(
473 context,
474 "(function(event) { event.dispatch(42, 'foo', {bar: 'baz'}); })");
475 {
476 v8::Local<v8::Value> argv[] = {event};
477 RunFunctionOnGlobal(fire_event_function, context, arraysize(argv), argv);
478 }
479
480 EXPECT_EQ("[42,\"foo\",{\"bar\":\"baz\"}]",
481 GetStringPropertyFromObject(
482 context->Global(), context, "eventArgs"));
483 }
484
485 // Test listeners that remove themselves in their handling of the event.
486 TEST_F(APIEventHandlerTest, RemovingListenersWhileHandlingEvent) {
487 v8::HandleScope handle_scope(isolate());
488 v8::Local<v8::Context> context = MainContext();
489
490 const char kEventName[] = "alpha";
491 v8::Local<v8::Object> event = handler()->CreateEventInstance(
492 kEventName, false, binding::kNoListenerMax, true, context);
493 ASSERT_FALSE(event.IsEmpty());
494 {
495 // Cache the event object on the global in order to allow for easy removal.
496 v8::Local<v8::Function> set_event_on_global =
497 FunctionFromString(
498 context,
499 "(function(event) { this.testEvent = event; })");
500 v8::Local<v8::Value> args[] = {event};
501 RunFunctionOnGlobal(set_event_on_global, context, arraysize(args), args);
502 EXPECT_EQ(event,
503 GetPropertyFromObject(context->Global(), context, "testEvent"));
504 }
505
506 // A listener function that removes itself as a listener.
507 const char kListenerFunction[] =
508 "(function() {\n"
509 " return function listener() {\n"
510 " this.testEvent.removeListener(listener);\n"
511 " };\n"
512 "})();";
513
514 // Create and add a bunch of listeners.
515 std::vector<v8::Local<v8::Function>> listeners;
516 const size_t kNumListeners = 20u;
517 listeners.reserve(kNumListeners);
518 for (size_t i = 0; i < kNumListeners; ++i)
519 listeners.push_back(FunctionFromString(context, kListenerFunction));
520
521 const char kAddListenerFunction[] =
522 "(function(event, listener) { event.addListener(listener); })";
523 v8::Local<v8::Function> add_listener_function =
524 FunctionFromString(context, kAddListenerFunction);
525
526 for (const auto& listener : listeners) {
527 v8::Local<v8::Value> argv[] = {event, listener};
528 RunFunctionOnGlobal(add_listener_function, context, arraysize(argv), argv);
529 }
530
531 // Fire the event. All listeners should be removed (and we shouldn't crash).
532 EXPECT_EQ(kNumListeners,
533 handler()->GetNumEventListenersForTesting(kEventName, context));
534 handler()->FireEventInContext(kEventName, context, base::ListValue(),
535 EventFilteringInfo());
536 EXPECT_EQ(0u, handler()->GetNumEventListenersForTesting(kEventName, context));
537
538 // TODO(devlin): Another possible test: register listener a and listener b,
539 // where a removes b and b removes a. Theoretically, only one should be
540 // notified. Investigate what we currently do in JS-style bindings.
541 }
542
543 // Test an event listener throwing an exception.
544 TEST_F(APIEventHandlerTest, TestEventListenersThrowingExceptions) {
545 // The default test util methods (RunFunction*) assume no errors will ever
546 // be encountered. Instead, use an implementation that allows errors.
547 auto run_js_and_expect_error = [](v8::Local<v8::Function> function,
548 v8::Local<v8::Context> context, int argc,
549 v8::Local<v8::Value> argv[]) {
550 v8::MaybeLocal<v8::Value> maybe_result =
551 function->Call(context, context->Global(), argc, argv);
552 v8::Global<v8::Value> result;
553 v8::Local<v8::Value> local;
554 if (maybe_result.ToLocal(&local))
555 result.Reset(context->GetIsolate(), local);
556 };
557
558 SetHandler(base::MakeUnique<APIEventHandler>(
559 base::Bind(run_js_and_expect_error),
560 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
561 base::Bind(&DoNothingOnEventListenersChanged)));
562
563 v8::HandleScope handle_scope(isolate());
564 v8::Local<v8::Context> context = MainContext();
565
566 const char kEventName[] = "alpha";
567 v8::Local<v8::Object> event = handler()->CreateEventInstance(
568 kEventName, false, binding::kNoListenerMax, true, context);
569 ASSERT_FALSE(event.IsEmpty());
570
571 bool did_throw = false;
572 auto message_listener = [](v8::Local<v8::Message> message,
573 v8::Local<v8::Value> data) {
574 ASSERT_FALSE(data.IsEmpty());
575 ASSERT_TRUE(data->IsExternal());
576 bool* did_throw = static_cast<bool*>(data.As<v8::External>()->Value());
577 *did_throw = true;
578 EXPECT_EQ("Uncaught Error: Event handler error",
579 gin::V8ToString(message->Get()));
580 };
581
582 isolate()->AddMessageListener(message_listener,
583 v8::External::New(isolate(), &did_throw));
584 base::ScopedClosureRunner remove_message_listener(base::Bind(
585 [](v8::Isolate* isolate, v8::MessageCallback listener) {
586 isolate->RemoveMessageListeners(listener);
587 },
588 isolate(), message_listener));
589
590 // A listener that will throw an exception. We guarantee that we throw the
591 // exception first so that we don't rely on event listener ordering.
592 const char kListenerFunction[] =
593 "(function() {\n"
594 " if (!this.didThrow) {\n"
595 " this.didThrow = true;\n"
596 " throw new Error('Event handler error');\n"
597 " }\n"
598 " this.eventArgs = Array.from(arguments);\n"
599 "});";
600
601 const char kAddListenerFunction[] =
602 "(function(event, listener) { event.addListener(listener); })";
603 v8::Local<v8::Function> add_listener_function =
604 FunctionFromString(context, kAddListenerFunction);
605
606 for (int i = 0; i < 2; ++i) {
607 v8::Local<v8::Function> listener =
608 FunctionFromString(context, kListenerFunction);
609 v8::Local<v8::Value> argv[] = {event, listener};
610 RunFunctionOnGlobal(add_listener_function, context, arraysize(argv), argv);
611 }
612 EXPECT_EQ(2u, handler()->GetNumEventListenersForTesting(kEventName, context));
613
614 std::unique_ptr<base::ListValue> event_args = ListValueFromString("[42]");
615 ASSERT_TRUE(event_args);
616 handler()->FireEventInContext(kEventName, context, *event_args,
617 EventFilteringInfo());
618
619 // An exception should have been thrown by the first listener and the second
620 // listener should have recorded the event arguments.
621 EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
622 "didThrow"));
623 EXPECT_EQ("[42]", GetStringPropertyFromObject(context->Global(), context,
624 "eventArgs"));
625 EXPECT_TRUE(did_throw);
626 }
627
628 // Tests being notified as listeners are added or removed from events.
629 TEST_F(APIEventHandlerTest, CallbackNotifications) {
630 MockEventChangeHandler change_handler;
631 SetHandler(base::MakeUnique<APIEventHandler>(
632 base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
633 base::Bind(&RunFunctionOnGlobalAndReturnHandle), change_handler.Get()));
634
635 v8::HandleScope handle_scope(isolate());
636
637 v8::Local<v8::Context> context_a = MainContext();
638 v8::Local<v8::Context> context_b = AddContext();
639
640 const char kEventName1[] = "onFoo";
641 const char kEventName2[] = "onBar";
642 v8::Local<v8::Object> event1_a = handler()->CreateEventInstance(
643 kEventName1, false, binding::kNoListenerMax, true, context_a);
644 ASSERT_FALSE(event1_a.IsEmpty());
645 v8::Local<v8::Object> event2_a = handler()->CreateEventInstance(
646 kEventName2, false, binding::kNoListenerMax, true, context_a);
647 ASSERT_FALSE(event2_a.IsEmpty());
648 v8::Local<v8::Object> event1_b = handler()->CreateEventInstance(
649 kEventName1, false, binding::kNoListenerMax, true, context_b);
650 ASSERT_FALSE(event1_b.IsEmpty());
651
652 const char kAddListenerFunction[] =
653 "(function(event, listener) { event.addListener(listener); })";
654 const char kRemoveListenerFunction[] =
655 "(function(event, listener) { event.removeListener(listener); })";
656
657 // Add a listener to the first event. The APIEventHandler should notify
658 // since it's a change in state (no listeners -> listeners).
659 v8::Local<v8::Function> add_listener =
660 FunctionFromString(context_a, kAddListenerFunction);
661 v8::Local<v8::Function> listener1 =
662 FunctionFromString(context_a, "(function() {})");
663 {
664 EXPECT_CALL(change_handler,
665 Run(kEventName1, binding::EventListenersChanged::HAS_LISTENERS,
666 nullptr, true, context_a))
667 .Times(1);
668 v8::Local<v8::Value> argv[] = {event1_a, listener1};
669 RunFunction(add_listener, context_a, arraysize(argv), argv);
670 ::testing::Mock::VerifyAndClearExpectations(&change_handler);
671 }
672 EXPECT_EQ(1u,
673 handler()->GetNumEventListenersForTesting(kEventName1, context_a));
674
675 // Add a second listener to the same event. We should not be notified, since
676 // the event already had listeners.
677 v8::Local<v8::Function> listener2 =
678 FunctionFromString(context_a, "(function() {})");
679 {
680 v8::Local<v8::Value> argv[] = {event1_a, listener2};
681 RunFunction(add_listener, context_a, arraysize(argv), argv);
682 }
683 EXPECT_EQ(2u,
684 handler()->GetNumEventListenersForTesting(kEventName1, context_a));
685
686 // Remove the first listener of the event. Again, since the event has
687 // listeners, we shouldn't be notified.
688 v8::Local<v8::Function> remove_listener =
689 FunctionFromString(context_a, kRemoveListenerFunction);
690 {
691 v8::Local<v8::Value> argv[] = {event1_a, listener1};
692 RunFunction(remove_listener, context_a, arraysize(argv), argv);
693 }
694
695 EXPECT_EQ(1u,
696 handler()->GetNumEventListenersForTesting(kEventName1, context_a));
697
698 // Remove the final listener from the event. We should be notified that the
699 // event no longer has listeners.
700 {
701 EXPECT_CALL(change_handler,
702 Run(kEventName1, binding::EventListenersChanged::NO_LISTENERS,
703 nullptr, true, context_a))
704 .Times(1);
705 v8::Local<v8::Value> argv[] = {event1_a, listener2};
706 RunFunction(remove_listener, context_a, arraysize(argv), argv);
707 ::testing::Mock::VerifyAndClearExpectations(&change_handler);
708 }
709 EXPECT_EQ(0u,
710 handler()->GetNumEventListenersForTesting(kEventName1, context_a));
711
712 // Add a listener to a separate event to ensure we receive the right
713 // notifications.
714 v8::Local<v8::Function> listener3 =
715 FunctionFromString(context_a, "(function() {})");
716 {
717 EXPECT_CALL(change_handler,
718 Run(kEventName2, binding::EventListenersChanged::HAS_LISTENERS,
719 nullptr, true, context_a))
720 .Times(1);
721 v8::Local<v8::Value> argv[] = {event2_a, listener3};
722 RunFunction(add_listener, context_a, arraysize(argv), argv);
723 ::testing::Mock::VerifyAndClearExpectations(&change_handler);
724 }
725 EXPECT_EQ(1u,
726 handler()->GetNumEventListenersForTesting(kEventName2, context_a));
727
728 {
729 EXPECT_CALL(change_handler,
730 Run(kEventName1, binding::EventListenersChanged::HAS_LISTENERS,
731 nullptr, true, context_b))
732 .Times(1);
733 // And add a listener to an event in a different context to make sure the
734 // associated context is correct.
735 v8::Local<v8::Function> add_listener =
736 FunctionFromString(context_b, kAddListenerFunction);
737 v8::Local<v8::Function> listener =
738 FunctionFromString(context_b, "(function() {})");
739 v8::Local<v8::Value> argv[] = {event1_b, listener};
740 RunFunction(add_listener, context_b, arraysize(argv), argv);
741 ::testing::Mock::VerifyAndClearExpectations(&change_handler);
742 }
743 EXPECT_EQ(1u,
744 handler()->GetNumEventListenersForTesting(kEventName1, context_b));
745
746 // When the contexts are invalidated, we should receive listener removed
747 // notifications. Additionally, since this was the context being torn down,
748 // rather than a removeListener call, was_manual should be false.
749 EXPECT_CALL(change_handler,
750 Run(kEventName2, binding::EventListenersChanged::NO_LISTENERS,
751 nullptr, false, context_a))
752 .Times(1);
753 DisposeContext(context_a);
754 ::testing::Mock::VerifyAndClearExpectations(&change_handler);
755
756 EXPECT_CALL(change_handler,
757 Run(kEventName1, binding::EventListenersChanged::NO_LISTENERS,
758 nullptr, false, context_b))
759 .Times(1);
760 DisposeContext(context_b);
761 ::testing::Mock::VerifyAndClearExpectations(&change_handler);
762 }
763
764 // Test registering an argument massager for a given event.
765 TEST_F(APIEventHandlerTest, TestArgumentMassagers) {
766 v8::HandleScope handle_scope(isolate());
767 v8::Local<v8::Context> context = MainContext();
768
769 const char kEventName[] = "alpha";
770 v8::Local<v8::Object> event = handler()->CreateEventInstance(
771 kEventName, false, binding::kNoListenerMax, true, context);
772 ASSERT_FALSE(event.IsEmpty());
773
774 const char kArgumentMassager[] =
775 "(function(originalArgs, dispatch) {\n"
776 " this.originalArgs = originalArgs;\n"
777 " dispatch(['primary', 'secondary']);\n"
778 "});";
779 v8::Local<v8::Function> massager =
780 FunctionFromString(context, kArgumentMassager);
781 handler()->RegisterArgumentMassager(context, "alpha", massager);
782
783 const char kListenerFunction[] =
784 "(function() { this.eventArgs = Array.from(arguments); })";
785 v8::Local<v8::Function> listener_function =
786 FunctionFromString(context, kListenerFunction);
787 ASSERT_FALSE(listener_function.IsEmpty());
788
789 {
790 const char kAddListenerFunction[] =
791 "(function(event, listener) { event.addListener(listener); })";
792 v8::Local<v8::Function> add_listener_function =
793 FunctionFromString(context, kAddListenerFunction);
794 v8::Local<v8::Value> argv[] = {event, listener_function};
795 RunFunction(add_listener_function, context, arraysize(argv), argv);
796 }
797
798 const char kArguments[] = "['first','second']";
799 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
800 ASSERT_TRUE(event_args);
801 handler()->FireEventInContext(kEventName, context, *event_args,
802 EventFilteringInfo());
803
804 EXPECT_EQ(
805 "[\"first\",\"second\"]",
806 GetStringPropertyFromObject(context->Global(), context, "originalArgs"));
807 EXPECT_EQ(
808 "[\"primary\",\"secondary\"]",
809 GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
810 }
811
812 // Test registering an argument massager for a given event and dispatching
813 // asynchronously.
814 TEST_F(APIEventHandlerTest, TestArgumentMassagersAsyncDispatch) {
815 v8::HandleScope handle_scope(isolate());
816 v8::Local<v8::Context> context = MainContext();
817
818 const char kEventName[] = "alpha";
819 v8::Local<v8::Object> event = handler()->CreateEventInstance(
820 kEventName, false, binding::kNoListenerMax, true, context);
821 ASSERT_FALSE(event.IsEmpty());
822
823 const char kArgumentMassager[] =
824 "(function(originalArgs, dispatch) {\n"
825 " this.originalArgs = originalArgs;\n"
826 " this.dispatch = dispatch;\n"
827 "});";
828 v8::Local<v8::Function> massager =
829 FunctionFromString(context, kArgumentMassager);
830 handler()->RegisterArgumentMassager(context, "alpha", massager);
831
832 const char kListenerFunction[] =
833 "(function() { this.eventArgs = Array.from(arguments); })";
834 v8::Local<v8::Function> listener_function =
835 FunctionFromString(context, kListenerFunction);
836 ASSERT_FALSE(listener_function.IsEmpty());
837
838 {
839 const char kAddListenerFunction[] =
840 "(function(event, listener) { event.addListener(listener); })";
841 v8::Local<v8::Function> add_listener_function =
842 FunctionFromString(context, kAddListenerFunction);
843 v8::Local<v8::Value> argv[] = {event, listener_function};
844 RunFunction(add_listener_function, context, arraysize(argv), argv);
845 }
846
847 const char kArguments[] = "['first','second']";
848 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
849 ASSERT_TRUE(event_args);
850 handler()->FireEventInContext(kEventName, context, *event_args,
851 EventFilteringInfo());
852
853 // The massager should have been triggered, but since it doesn't call
854 // dispatch(), the listener shouldn't have been notified.
855 EXPECT_EQ(
856 "[\"first\",\"second\"]",
857 GetStringPropertyFromObject(context->Global(), context, "originalArgs"));
858 EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
859 "eventArgs"));
860
861 // Dispatch the event.
862 v8::Local<v8::Value> dispatch_value =
863 GetPropertyFromObject(context->Global(), context, "dispatch");
864 ASSERT_FALSE(dispatch_value.IsEmpty());
865 ASSERT_TRUE(dispatch_value->IsFunction());
866 v8::Local<v8::Value> dispatch_args[] = {
867 V8ValueFromScriptSource(context, "['primary', 'secondary']"),
868 };
869 RunFunction(dispatch_value.As<v8::Function>(), context,
870 arraysize(dispatch_args), dispatch_args);
871
872 EXPECT_EQ(
873 "[\"primary\",\"secondary\"]",
874 GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
875 }
876
877 // Test registering an argument massager and never dispatching.
878 TEST_F(APIEventHandlerTest, TestArgumentMassagersNeverDispatch) {
879 v8::HandleScope handle_scope(isolate());
880 v8::Local<v8::Context> context = MainContext();
881
882 const char kEventName[] = "alpha";
883 v8::Local<v8::Object> event = handler()->CreateEventInstance(
884 kEventName, false, binding::kNoListenerMax, true, context);
885 ASSERT_FALSE(event.IsEmpty());
886
887 // A massager that never dispatches.
888 const char kArgumentMassager[] = "(function(originalArgs, dispatch) {})";
889 v8::Local<v8::Function> massager =
890 FunctionFromString(context, kArgumentMassager);
891 handler()->RegisterArgumentMassager(context, "alpha", massager);
892
893 const char kListenerFunction[] = "(function() {})";
894 v8::Local<v8::Function> listener_function =
895 FunctionFromString(context, kListenerFunction);
896 ASSERT_FALSE(listener_function.IsEmpty());
897
898 const char kAddListenerFunction[] =
899 "(function(event, listener) { event.addListener(listener); })";
900 v8::Local<v8::Function> add_listener_function =
901 FunctionFromString(context, kAddListenerFunction);
902 v8::Local<v8::Value> argv[] = {event, listener_function};
903 RunFunction(add_listener_function, context, arraysize(argv), argv);
904
905 handler()->FireEventInContext(kEventName, context, base::ListValue(),
906 EventFilteringInfo());
907
908 // Nothing should blow up. (We tested in the previous test that the event
909 // isn't notified without calling dispatch, so all there is to test here is
910 // that we don't crash.)
911 }
912
913 // Test creating a custom event, as is done by a few of our custom bindings.
914 TEST_F(APIEventHandlerTest, TestCreateCustomEvent) {
915 v8::HandleScope handle_scope(isolate());
916 v8::Local<v8::Context> context = MainContext();
917
918 MockEventChangeHandler change_handler;
919 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
920 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
921 change_handler.Get());
922
923 v8::Local<v8::Object> event = handler.CreateAnonymousEventInstance(context);
924 ASSERT_FALSE(event.IsEmpty());
925
926 const char kAddListenerFunction[] =
927 "(function(event) {\n"
928 " event.addListener(function() {\n"
929 " this.eventArgs = Array.from(arguments);\n"
930 " });\n"
931 "})";
932 v8::Local<v8::Value> add_listener_argv[] = {event};
933 RunFunction(FunctionFromString(context, kAddListenerFunction), context,
934 arraysize(add_listener_argv), add_listener_argv);
935
936 // Test dispatching to the listeners.
937 const char kDispatchEventFunction[] =
938 "(function(event) { event.dispatch(1, 2, 3); })";
939 v8::Local<v8::Function> dispatch_function =
940 FunctionFromString(context, kDispatchEventFunction);
941
942 v8::Local<v8::Value> dispatch_argv[] = {event};
943 RunFunction(dispatch_function, context, arraysize(dispatch_argv),
944 dispatch_argv);
945
946 EXPECT_EQ("[1,2,3]", GetStringPropertyFromObject(context->Global(), context,
947 "eventArgs"));
948
949 // Clean up so we can re-check eventArgs.
950 ASSERT_TRUE(context->Global()
951 ->Delete(context, gin::StringToSymbol(isolate(), "eventArgs"))
952 .FromJust());
953
954 // Invalidate the event and try dispatching again. Nothing should happen.
955 handler.InvalidateCustomEvent(context, event);
956 RunFunction(dispatch_function, context, arraysize(dispatch_argv),
957 dispatch_argv);
958 EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
959 "eventArgs"));
960 }
961
962 // Test adding a custom event with a cyclic dependency. Nothing should leak.
963 TEST_F(APIEventHandlerTest, TestCreateCustomEventWithCyclicDependency) {
964 v8::HandleScope handle_scope(isolate());
965 v8::Local<v8::Context> context = MainContext();
966
967 MockEventChangeHandler change_handler;
968 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
969 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
970 change_handler.Get());
971
972 v8::Local<v8::Object> event = handler.CreateAnonymousEventInstance(context);
973 ASSERT_FALSE(event.IsEmpty());
974
975 const char kAddListenerFunction[] =
976 "(function(event) {\n"
977 " event.addListener(function() {}.bind(null, event));\n"
978 "})";
979 v8::Local<v8::Value> add_listener_argv[] = {event};
980 RunFunction(FunctionFromString(context, kAddListenerFunction), context,
981 arraysize(add_listener_argv), add_listener_argv);
982
983 DisposeContext(context);
984 }
985
986 TEST_F(APIEventHandlerTest, TestUnmanagedEvents) {
987 v8::HandleScope handle_scope(isolate());
988 v8::Local<v8::Context> context = MainContext();
989
990 auto fail_on_notified =
991 [](const std::string& event_name, binding::EventListenersChanged changed,
992 const base::DictionaryValue* filter, bool was_manual,
993 v8::Local<v8::Context> context) { ADD_FAILURE(); };
994
995 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
996 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
997 base::Bind(fail_on_notified));
998
999 const char kEventName[] = "alpha";
1000 v8::Local<v8::Object> event = handler.CreateEventInstance(
1001 kEventName, false, binding::kNoListenerMax, false, context);
1002
1003 const char kListener[] =
1004 "(function() {\n"
1005 " this.eventArgs = Array.from(arguments);\n"
1006 "});";
1007 v8::Local<v8::Function> listener = FunctionFromString(context, kListener);
1008
1009 {
1010 const char kAddListener[] =
1011 "(function(event, listener) { event.addListener(listener); })";
1012 v8::Local<v8::Value> args[] = {event, listener};
1013 RunFunction(FunctionFromString(context, kAddListener), context,
1014 arraysize(args), args);
1015 }
1016
1017 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
1018
1019 handler.FireEventInContext(kEventName, context,
1020 *ListValueFromString("[1, 'foo']"),
1021 EventFilteringInfo());
1022
1023 EXPECT_EQ("[1,\"foo\"]", GetStringPropertyFromObject(context->Global(),
1024 context, "eventArgs"));
1025
1026 {
1027 const char kRemoveListener[] =
1028 "(function(event, listener) { event.removeListener(listener); })";
1029 v8::Local<v8::Value> args[] = {event, listener};
1030 RunFunction(FunctionFromString(context, kRemoveListener), context,
1031 arraysize(args), args);
1032 }
1033
1034 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
1035 }
1036
1037 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_event_handler.cc ('k') | extensions/renderer/api_event_listeners.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698