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

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

Issue 2469593002: [Extensions Bindings] Add Events support (Closed)
Patch Set: rebase Created 4 years, 1 month 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/event_emitter.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/memory/ptr_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "extensions/renderer/api_binding_test_util.h"
12 #include "gin/converter.h"
13 #include "gin/public/context_holder.h"
14 #include "gin/public/isolate_holder.h"
15 #include "gin/test/v8_test.h"
16 #include "gin/try_catch.h"
17
18 namespace extensions {
19
20 class APIEventHandlerTest : public gin::V8Test {
21 protected:
22 APIEventHandlerTest() {}
23 ~APIEventHandlerTest() override {}
24
25 void SetUp() override {
26 gin::V8Test::SetUp();
27 v8::HandleScope handle_scope(instance_->isolate());
28 holder_ = base::MakeUnique<gin::ContextHolder>(instance_->isolate());
29 holder_->SetContext(
30 v8::Local<v8::Context>::New(instance_->isolate(), context_));
31 }
32
33 void TearDown() override {
34 // Garbage collect everything so that we find any issues where we might be
35 // double-freeing.
36 // '5' is a magic number stolen from Blink; arbitrarily large enough to
jbroman 2016/11/05 20:55:55 nit: move this comment down next to the loop it de
Devlin 2016/11/07 19:17:30 Whoops! Done.
Devlin 2016/11/07 19:29:28 In a case of the Mondays, I did this, and then lef
37 // hopefully clean up all the various paths.
38 v8::Global<v8::Context> weak_context(instance_->isolate(), context_);
39 weak_context.SetWeak();
40
41 holder_.reset();
42
43 // NOTE: We explicitly do NOT call gin::V8Test::TearDown() here because we
44 // do intermittent validation by doing a garbage collection after context
45 // destruction and ensuring the context is fully released (which wouldn't
46 // happen in cycles).
47 // TODO(devlin): It might be time to move off V8Test if we're doing this.
48 {
49 v8::HandleScope handle_scope(instance_->isolate());
50 v8::Local<v8::Context>::New(instance_->isolate(), context_)->Exit();
51 context_.Reset();
52 }
53
54 for (int i = 0; i < 5; i++) {
55 instance_->isolate()->RequestGarbageCollectionForTesting(
56 v8::Isolate::kFullGarbageCollection);
57 }
58
59 ASSERT_TRUE(weak_context.IsEmpty());
60
61 instance_->isolate()->Exit();
62 instance_.reset();
63 }
64
65 void CallFunctionOnObject(v8::Local<v8::Context> context,
66 v8::Local<v8::Object> object,
67 const std::string& script_source) {
68 std::string wrapped_script_source =
69 base::StringPrintf("(function(obj) { %s })", script_source.c_str());
70 v8::Local<v8::Function> func =
71 FunctionFromString(context, wrapped_script_source);
72 ASSERT_FALSE(func.IsEmpty());
73
74 v8::Local<v8::Value> argv[] = {object};
75 RunFunction(func, context, object, 1, argv);
76 }
77
78 private:
79 std::unique_ptr<gin::ContextHolder> holder_;
80
81 DISALLOW_COPY_AND_ASSIGN(APIEventHandlerTest);
82 };
83
84 // Tests adding, removing, and querying event listeners by calling the
85 // associated methods on the JS object.
86 TEST_F(APIEventHandlerTest, AddingRemovingAndQueryingEventListeners) {
87 const char kEventName[] = "alpha";
88 v8::Isolate* isolate = instance_->isolate();
89 v8::HandleScope handle_scope(instance_->isolate());
90 v8::Local<v8::Context> context =
91 v8::Local<v8::Context>::New(instance_->isolate(), context_);
92
93 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
94 v8::Local<v8::Object> event =
95 handler.CreateEventInstance(kEventName, context);
96 ASSERT_FALSE(event.IsEmpty());
97
98 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
99
100 const char kListenerFunction[] = "(function() {})";
101 v8::Local<v8::Function> listener_function =
102 FunctionFromString(context, kListenerFunction);
103 ASSERT_FALSE(listener_function.IsEmpty());
104
105 const char kAddListenerFunction[] =
106 "(function(event, listener) { event.addListener(listener); })";
107 v8::Local<v8::Function> add_listener_function =
108 FunctionFromString(context, kAddListenerFunction);
109
110 {
111 v8::Local<v8::Value> argv[] = {event, listener_function};
112 RunFunction(add_listener_function, context, arraysize(argv), argv);
113 }
114 // There should only be one listener on the event.
115 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
116
117 {
118 v8::Local<v8::Value> argv[] = {event, listener_function};
119 RunFunction(add_listener_function, context, arraysize(argv), argv);
120 }
121 // Trying to add the same listener again should be a no-op.
122 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
123
124 // Test hasListener returns true for a listener that is present.
125 const char kHasListenerFunction[] =
126 "(function(event, listener) { return event.hasListener(listener); })";
127 v8::Local<v8::Function> has_listener_function =
128 FunctionFromString(context, kHasListenerFunction);
129 {
130 v8::Local<v8::Value> argv[] = {event, listener_function};
131 v8::Local<v8::Value> result =
132 RunFunction(has_listener_function, context, arraysize(argv), argv);
133 bool has_listener = false;
134 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener));
135 EXPECT_TRUE(has_listener);
136 }
137
138 // Test that hasListener returns false for a listener that isn't present.
139 {
140 v8::Local<v8::Function> not_a_listener =
141 FunctionFromString(context, "(function() {})");
142 v8::Local<v8::Value> argv[] = {event, not_a_listener};
143 v8::Local<v8::Value> result =
144 RunFunction(has_listener_function, context, arraysize(argv), argv);
145 bool has_listener = false;
146 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener));
147 EXPECT_FALSE(has_listener);
148 }
149
150 // Test hasListeners returns true
151 const char kHasListenersFunction[] =
152 "(function(event) { return event.hasListeners(); })";
153 v8::Local<v8::Function> has_listeners_function =
154 FunctionFromString(context, kHasListenersFunction);
155 {
156 v8::Local<v8::Value> argv[] = {event};
157 v8::Local<v8::Value> result =
158 RunFunction(has_listeners_function, context, arraysize(argv), argv);
159 bool has_listeners = false;
160 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners));
161 EXPECT_TRUE(has_listeners);
162 }
163
164 const char kRemoveListenerFunction[] =
165 "(function(event, listener) { event.removeListener(listener); })";
166 v8::Local<v8::Function> remove_listener_function =
167 FunctionFromString(context, kRemoveListenerFunction);
168 {
169 v8::Local<v8::Value> argv[] = {event, listener_function};
170 RunFunction(remove_listener_function, context, arraysize(argv), argv);
171 }
172 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
173
174 {
175 v8::Local<v8::Value> argv[] = {event};
176 v8::Local<v8::Value> result =
177 RunFunction(has_listeners_function, context, arraysize(argv), argv);
178 bool has_listeners = false;
179 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners));
180 EXPECT_FALSE(has_listeners);
181 }
182 }
183
184 // Tests listening for and firing different events.
185 TEST_F(APIEventHandlerTest, FiringEvents) {
186 const char kAlphaName[] = "alpha";
187 const char kBetaName[] = "beta";
188 v8::HandleScope handle_scope(instance_->isolate());
189 v8::Local<v8::Context> context =
190 v8::Local<v8::Context>::New(instance_->isolate(), context_);
191
192 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
193 v8::Local<v8::Object> alpha_event =
194 handler.CreateEventInstance(kAlphaName, context);
195 v8::Local<v8::Object> beta_event =
196 handler.CreateEventInstance(kBetaName, context);
197 ASSERT_FALSE(alpha_event.IsEmpty());
198 ASSERT_FALSE(beta_event.IsEmpty());
199
200 const char kAlphaListenerFunction1[] =
201 "(function() {\n"
202 " if (!this.alphaCount1) this.alphaCount1 = 0;\n"
203 " ++this.alphaCount1;\n"
204 "});\n";
205 v8::Local<v8::Function> alpha_listener1 =
206 FunctionFromString(context, kAlphaListenerFunction1);
207 const char kAlphaListenerFunction2[] =
208 "(function() {\n"
209 " if (!this.alphaCount2) this.alphaCount2 = 0;\n"
210 " ++this.alphaCount2;\n"
211 "});\n";
212 v8::Local<v8::Function> alpha_listener2 =
213 FunctionFromString(context, kAlphaListenerFunction2);
214 const char kBetaListenerFunction[] =
215 "(function() {\n"
216 " if (!this.betaCount) this.betaCount = 0;\n"
217 " ++this.betaCount;\n"
218 "});\n";
219 v8::Local<v8::Function> beta_listener =
220 FunctionFromString(context, kBetaListenerFunction);
221 ASSERT_FALSE(alpha_listener1.IsEmpty());
222 ASSERT_FALSE(alpha_listener2.IsEmpty());
223 ASSERT_FALSE(beta_listener.IsEmpty());
224
225 {
226 const char kAddListenerFunction[] =
227 "(function(event, listener) { event.addListener(listener); })";
228 v8::Local<v8::Function> add_listener_function =
229 FunctionFromString(context, kAddListenerFunction);
230 {
231 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener1};
232 RunFunction(add_listener_function, context, arraysize(argv), argv);
233 }
234 {
235 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener2};
236 RunFunction(add_listener_function, context, arraysize(argv), argv);
237 }
238 {
239 v8::Local<v8::Value> argv[] = {beta_event, beta_listener};
240 RunFunction(add_listener_function, context, arraysize(argv), argv);
241 }
242 }
243
244 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kAlphaName, context));
245 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kBetaName, context));
246
247 auto get_fired_count = [&context](const char* name) {
248 v8::Local<v8::Value> res =
249 GetPropertyFromObject(context->Global(), context, name);
250 if (res->IsUndefined())
251 return 0;
252 int32_t count = 0;
253 EXPECT_TRUE(
254 gin::Converter<int32_t>::FromV8(context->GetIsolate(), res, &count))
255 << name;
256 return count;
257 };
258
259 EXPECT_EQ(0, get_fired_count("alphaCount1"));
260 EXPECT_EQ(0, get_fired_count("alphaCount2"));
261 EXPECT_EQ(0, get_fired_count("betaCount"));
262
263 handler.FireEventInContext(kAlphaName, context, base::ListValue());
264 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kAlphaName, context));
265 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kBetaName, context));
266
267 EXPECT_EQ(1, get_fired_count("alphaCount1"));
268 EXPECT_EQ(1, get_fired_count("alphaCount2"));
269 EXPECT_EQ(0, get_fired_count("betaCount"));
270
271 handler.FireEventInContext(kAlphaName, context, base::ListValue());
272 EXPECT_EQ(2, get_fired_count("alphaCount1"));
273 EXPECT_EQ(2, get_fired_count("alphaCount2"));
274 EXPECT_EQ(0, get_fired_count("betaCount"));
275
276 handler.FireEventInContext(kBetaName, context, base::ListValue());
277 EXPECT_EQ(2, get_fired_count("alphaCount1"));
278 EXPECT_EQ(2, get_fired_count("alphaCount2"));
279 EXPECT_EQ(1, get_fired_count("betaCount"));
280 }
281
282 // Tests firing events with arguments.
283 TEST_F(APIEventHandlerTest, EventArguments) {
284 v8::Isolate* isolate = instance_->isolate();
285 v8::HandleScope handle_scope(isolate);
286 v8::Local<v8::Context> context =
287 v8::Local<v8::Context>::New(isolate, context_);
288
289 const char kEventName[] = "alpha";
290 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
291 v8::Local<v8::Object> event =
292 handler.CreateEventInstance(kEventName, context);
293 ASSERT_FALSE(event.IsEmpty());
294
295 const char kListenerFunction[] =
296 "(function() { this.eventArgs = Array.from(arguments); })";
297 v8::Local<v8::Function> listener_function =
298 FunctionFromString(context, kListenerFunction);
299 ASSERT_FALSE(listener_function.IsEmpty());
300
301 {
302 const char kAddListenerFunction[] =
303 "(function(event, listener) { event.addListener(listener); })";
304 v8::Local<v8::Function> add_listener_function =
305 FunctionFromString(context, kAddListenerFunction);
306 v8::Local<v8::Value> argv[] = {event, listener_function};
307 RunFunction(add_listener_function, context, arraysize(argv), argv);
308 }
309
310 const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
311 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
312 ASSERT_TRUE(event_args);
313 handler.FireEventInContext(kEventName, context, *event_args);
314
315 std::unique_ptr<base::Value> result =
316 GetBaseValuePropertyFromObject(context->Global(), context, "eventArgs");
317 ASSERT_TRUE(result);
318 EXPECT_EQ(ReplaceSingleQuotes(kArguments), ValueToString(*result));
319 }
320
321 // Test dispatching events to multiple contexts.
322 TEST_F(APIEventHandlerTest, MultipleContexts) {
323 v8::Isolate* isolate = instance_->isolate();
324 v8::HandleScope handle_scope(instance_->isolate());
325
326 v8::Local<v8::Context> context_a =
327 v8::Local<v8::Context>::New(isolate, context_);
328 v8::Local<v8::Context> context_b = v8::Context::New(isolate);
329 gin::ContextHolder holder_b(isolate);
330 holder_b.SetContext(context_b);
331
332 const char kEventName[] = "onFoo";
333
334 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
335
336 v8::Local<v8::Function> listener_a = FunctionFromString(
337 context_a, "(function(arg) { this.eventArgs = arg + 'alpha'; })");
338 ASSERT_FALSE(listener_a.IsEmpty());
339 v8::Local<v8::Function> listener_b = FunctionFromString(
340 context_b, "(function(arg) { this.eventArgs = arg + 'beta'; })");
341 ASSERT_FALSE(listener_b.IsEmpty());
342
343 // Create two instances of the same event in different contexts.
344 v8::Local<v8::Object> event_a =
345 handler.CreateEventInstance(kEventName, context_a);
346 ASSERT_FALSE(event_a.IsEmpty());
347 v8::Local<v8::Object> event_b =
348 handler.CreateEventInstance(kEventName, context_b);
349 ASSERT_FALSE(event_b.IsEmpty());
350
351 // Add two separate listeners to the event, one in each context.
352 const char kAddListenerFunction[] =
353 "(function(event, listener) { event.addListener(listener); })";
354 {
355 v8::Local<v8::Function> add_listener_a =
356 FunctionFromString(context_a, kAddListenerFunction);
357 v8::Local<v8::Value> argv[] = {event_a, listener_a};
358 RunFunction(add_listener_a, context_a, arraysize(argv), argv);
359 }
360 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_a));
361 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context_b));
362
363 {
364 v8::Local<v8::Function> add_listener_b =
365 FunctionFromString(context_b, kAddListenerFunction);
366 v8::Local<v8::Value> argv[] = {event_b, listener_b};
367 RunFunction(add_listener_b, context_b, arraysize(argv), argv);
368 }
369 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_a));
370 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_b));
371
372 // Dispatch the event in context_a - the listener in context_b should not be
373 // notified.
374 std::unique_ptr<base::ListValue> arguments_a =
375 ListValueFromString("['result_a:']");
376 ASSERT_TRUE(arguments_a);
377
378 handler.FireEventInContext(kEventName, context_a, *arguments_a);
379 {
380 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
381 context_a->Global(), context_a, "eventArgs");
382 ASSERT_TRUE(result_a);
383 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
384 }
385 {
386 v8::Local<v8::Value> result_b =
387 GetPropertyFromObject(context_b->Global(), context_b, "eventArgs");
388 ASSERT_FALSE(result_b.IsEmpty());
389 EXPECT_TRUE(result_b->IsUndefined());
390 }
391
392 // Dispatch the event in context_b - the listener in context_a should not be
393 // notified.
394 std::unique_ptr<base::ListValue> arguments_b =
395 ListValueFromString("['result_b:']");
396 ASSERT_TRUE(arguments_b);
397 handler.FireEventInContext(kEventName, context_b, *arguments_b);
398 {
399 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
400 context_a->Global(), context_a, "eventArgs");
401 ASSERT_TRUE(result_a);
402 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
403 }
404 {
405 std::unique_ptr<base::Value> result_b = GetBaseValuePropertyFromObject(
406 context_b->Global(), context_b, "eventArgs");
407 ASSERT_TRUE(result_b);
408 EXPECT_EQ("\"result_b:beta\"", ValueToString(*result_b));
409 }
410 }
411
412 TEST_F(APIEventHandlerTest, DifferentCallingMethods) {
413 v8::Isolate* isolate = instance_->isolate();
414 v8::HandleScope handle_scope(isolate);
415 v8::Local<v8::Context> context =
416 v8::Local<v8::Context>::New(isolate, context_);
417
418 const char kEventName[] = "alpha";
419 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
420 v8::Local<v8::Object> event =
421 handler.CreateEventInstance(kEventName, context);
422 ASSERT_FALSE(event.IsEmpty());
423
424 const char kAddListenerOnNull[] =
425 "(function(event) {\n"
426 " event.addListener.call(null, function() {});\n"
427 "})";
428 {
429 v8::Local<v8::Value> args[] = {event};
430 // TODO(devlin): This is the generic type error that gin throws. It's not
431 // very descriptive, nor does it match the web (which would just say e.g.
432 // "Illegal invocation"). Might be worth updating later.
433 RunFunctionAndExpectError(
434 FunctionFromString(context, kAddListenerOnNull),
435 context, 1, args,
436 "Uncaught TypeError: Error processing argument at index -1,"
437 " conversion failure from undefined");
438 }
439 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
440
441 const char kAddListenerOnEvent[] =
442 "(function(event) {\n"
443 " event.addListener.call(event, function() {});\n"
444 "})";
445 {
446 v8::Local<v8::Value> args[] = {event};
447 RunFunction(FunctionFromString(context, kAddListenerOnEvent),
448 context, 1, args);
449 }
450 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
451
452 // Call addListener with a function that captures the event, creating a cycle.
453 // If we don't properly clean up, the context will leak.
454 const char kAddListenerOnEventWithCapture[] =
455 "(function(event) {\n"
456 " event.addListener(function listener() {\n"
457 " event.hasListener(listener);\n"
458 " });\n"
459 "})";
460 {
461 v8::Local<v8::Value> args[] = {event};
462 RunFunction(FunctionFromString(context, kAddListenerOnEventWithCapture),
463 context, 1, args);
464 }
465 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kEventName, context));
466 }
467
468 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_event_handler.cc ('k') | extensions/renderer/event_emitter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698