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

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

Issue 2469593002: [Extensions Bindings] Add Events support (Closed)
Patch Set: jbromans III 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
37 // hopefully clean up all the various paths.
38 for (int i = 0; i < 5; i++) {
39 instance_->isolate()->RequestGarbageCollectionForTesting(
40 v8::Isolate::kFullGarbageCollection);
41 }
42
43 holder_.reset();
44 gin::V8Test::TearDown();
Devlin 2016/11/04 17:59:30 After this point, the context and PerContextData s
jbroman 2016/11/04 18:33:04 That's a good question. I'm not aware of a general
Devlin 2016/11/05 01:24:19 Great idea! That worked - passes as-is and fails
45 }
46
47 void CallFunctionOnObject(v8::Local<v8::Context> context,
48 v8::Local<v8::Object> object,
49 const std::string& script_source) {
50 std::string wrapped_script_source =
51 base::StringPrintf("(function(obj) { %s })", script_source.c_str());
52 v8::Local<v8::Function> func =
53 FunctionFromString(context, wrapped_script_source);
54 ASSERT_FALSE(func.IsEmpty());
55
56 v8::Local<v8::Value> argv[] = {object};
57 RunFunction(func, context, object, 1, argv);
58 }
59
60 private:
61 std::unique_ptr<gin::ContextHolder> holder_;
62
63 DISALLOW_COPY_AND_ASSIGN(APIEventHandlerTest);
64 };
65
66 // Tests adding, removing, and querying event listeners by calling the
67 // associated methods on the JS object.
68 TEST_F(APIEventHandlerTest, AddingRemovingAndQueryingEventListeners) {
69 const char kEventName[] = "alpha";
70 v8::Isolate* isolate = instance_->isolate();
71 v8::HandleScope handle_scope(instance_->isolate());
72 v8::Local<v8::Context> context =
73 v8::Local<v8::Context>::New(instance_->isolate(), context_);
74
75 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
76 v8::Local<v8::Object> event =
77 handler.CreateEventInstance(kEventName, context);
78 ASSERT_FALSE(event.IsEmpty());
79
80 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
81
82 const char kListenerFunction[] = "(function() {})";
83 v8::Local<v8::Function> listener_function =
84 FunctionFromString(context, kListenerFunction);
85 ASSERT_FALSE(listener_function.IsEmpty());
86
87 const char kAddListenerFunction[] =
88 "(function(event, listener) { event.addListener(listener); })";
89 v8::Local<v8::Function> add_listener_function =
90 FunctionFromString(context, kAddListenerFunction);
91
92 {
93 v8::Local<v8::Value> argv[] = {event, listener_function};
94 RunFunction(add_listener_function, context, arraysize(argv), argv);
95 }
96 // There should only be one listener on the event.
97 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
98
99 {
100 v8::Local<v8::Value> argv[] = {event, listener_function};
101 RunFunction(add_listener_function, context, arraysize(argv), argv);
102 }
103 // Trying to add the same listener again should be a no-op.
104 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
105
106 // Test hasListener returns true for a listener that is present.
107 const char kHasListenerFunction[] =
108 "(function(event, listener) { return event.hasListener(listener); })";
109 v8::Local<v8::Function> has_listener_function =
110 FunctionFromString(context, kHasListenerFunction);
111 {
112 v8::Local<v8::Value> argv[] = {event, listener_function};
113 v8::Local<v8::Value> result =
114 RunFunction(has_listener_function, context, arraysize(argv), argv);
115 bool has_listener = false;
116 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener));
117 EXPECT_TRUE(has_listener);
118 }
119
120 // Test that hasListener returns false for a listener that isn't present.
121 {
122 v8::Local<v8::Function> not_a_listener =
123 FunctionFromString(context, "(function() {})");
124 v8::Local<v8::Value> argv[] = {event, not_a_listener};
125 v8::Local<v8::Value> result =
126 RunFunction(has_listener_function, context, arraysize(argv), argv);
127 bool has_listener = false;
128 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener));
129 EXPECT_FALSE(has_listener);
130 }
131
132 // Test hasListeners returns true
133 const char kHasListenersFunction[] =
134 "(function(event) { return event.hasListeners(); })";
135 v8::Local<v8::Function> has_listeners_function =
136 FunctionFromString(context, kHasListenersFunction);
137 {
138 v8::Local<v8::Value> argv[] = {event};
139 v8::Local<v8::Value> result =
140 RunFunction(has_listeners_function, context, arraysize(argv), argv);
141 bool has_listeners = false;
142 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners));
143 EXPECT_TRUE(has_listeners);
144 }
145
146 const char kRemoveListenerFunction[] =
147 "(function(event, listener) { event.removeListener(listener); })";
148 v8::Local<v8::Function> remove_listener_function =
149 FunctionFromString(context, kRemoveListenerFunction);
150 {
151 v8::Local<v8::Value> argv[] = {event, listener_function};
152 RunFunction(remove_listener_function, context, arraysize(argv), argv);
153 }
154 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
155
156 {
157 v8::Local<v8::Value> argv[] = {event};
158 v8::Local<v8::Value> result =
159 RunFunction(has_listeners_function, context, arraysize(argv), argv);
160 bool has_listeners = false;
161 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners));
162 EXPECT_FALSE(has_listeners);
163 }
164 }
165
166 // Tests listening for and firing different events.
167 TEST_F(APIEventHandlerTest, FiringEvents) {
168 const char kAlphaName[] = "alpha";
169 const char kBetaName[] = "beta";
170 v8::HandleScope handle_scope(instance_->isolate());
171 v8::Local<v8::Context> context =
172 v8::Local<v8::Context>::New(instance_->isolate(), context_);
173
174 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
175 v8::Local<v8::Object> alpha_event =
176 handler.CreateEventInstance(kAlphaName, context);
177 v8::Local<v8::Object> beta_event =
178 handler.CreateEventInstance(kBetaName, context);
179 ASSERT_FALSE(alpha_event.IsEmpty());
180 ASSERT_FALSE(beta_event.IsEmpty());
181
182 const char kAlphaListenerFunction1[] =
183 "(function() {\n"
184 " if (!this.alphaCount1) this.alphaCount1 = 0;\n"
185 " ++this.alphaCount1;\n"
186 "});\n";
187 v8::Local<v8::Function> alpha_listener1 =
188 FunctionFromString(context, kAlphaListenerFunction1);
189 const char kAlphaListenerFunction2[] =
190 "(function() {\n"
191 " if (!this.alphaCount2) this.alphaCount2 = 0;\n"
192 " ++this.alphaCount2;\n"
193 "});\n";
194 v8::Local<v8::Function> alpha_listener2 =
195 FunctionFromString(context, kAlphaListenerFunction2);
196 const char kBetaListenerFunction[] =
197 "(function() {\n"
198 " if (!this.betaCount) this.betaCount = 0;\n"
199 " ++this.betaCount;\n"
200 "});\n";
201 v8::Local<v8::Function> beta_listener =
202 FunctionFromString(context, kBetaListenerFunction);
203 ASSERT_FALSE(alpha_listener1.IsEmpty());
204 ASSERT_FALSE(alpha_listener2.IsEmpty());
205 ASSERT_FALSE(beta_listener.IsEmpty());
206
207 {
208 const char kAddListenerFunction[] =
209 "(function(event, listener) { event.addListener(listener); })";
210 v8::Local<v8::Function> add_listener_function =
211 FunctionFromString(context, kAddListenerFunction);
212 {
213 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener1};
214 RunFunction(add_listener_function, context, arraysize(argv), argv);
215 }
216 {
217 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener2};
218 RunFunction(add_listener_function, context, arraysize(argv), argv);
219 }
220 {
221 v8::Local<v8::Value> argv[] = {beta_event, beta_listener};
222 RunFunction(add_listener_function, context, arraysize(argv), argv);
223 }
224 }
225
226 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kAlphaName, context));
227 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kBetaName, context));
228
229 auto get_fired_count = [&context](const char* name) {
230 v8::Local<v8::Value> res =
231 GetPropertyFromObject(context->Global(), context, name);
232 if (res->IsUndefined())
233 return 0;
234 int32_t count = 0;
235 EXPECT_TRUE(
236 gin::Converter<int32_t>::FromV8(context->GetIsolate(), res, &count))
237 << name;
238 return count;
239 };
240
241 EXPECT_EQ(0, get_fired_count("alphaCount1"));
242 EXPECT_EQ(0, get_fired_count("alphaCount2"));
243 EXPECT_EQ(0, get_fired_count("betaCount"));
244
245 handler.FireEventInContext(kAlphaName, context, base::ListValue());
246 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kAlphaName, context));
247 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kBetaName, context));
248
249 EXPECT_EQ(1, get_fired_count("alphaCount1"));
250 EXPECT_EQ(1, get_fired_count("alphaCount2"));
251 EXPECT_EQ(0, get_fired_count("betaCount"));
252
253 handler.FireEventInContext(kAlphaName, context, base::ListValue());
254 EXPECT_EQ(2, get_fired_count("alphaCount1"));
255 EXPECT_EQ(2, get_fired_count("alphaCount2"));
256 EXPECT_EQ(0, get_fired_count("betaCount"));
257
258 handler.FireEventInContext(kBetaName, context, base::ListValue());
259 EXPECT_EQ(2, get_fired_count("alphaCount1"));
260 EXPECT_EQ(2, get_fired_count("alphaCount2"));
261 EXPECT_EQ(1, get_fired_count("betaCount"));
262 }
263
264 // Tests firing events with arguments.
265 TEST_F(APIEventHandlerTest, EventArguments) {
266 v8::Isolate* isolate = instance_->isolate();
267 v8::HandleScope handle_scope(isolate);
268 v8::Local<v8::Context> context =
269 v8::Local<v8::Context>::New(isolate, context_);
270
271 const char kEventName[] = "alpha";
272 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
273 v8::Local<v8::Object> event =
274 handler.CreateEventInstance(kEventName, context);
275 ASSERT_FALSE(event.IsEmpty());
276
277 const char kListenerFunction[] =
278 "(function() { this.eventArgs = Array.from(arguments); })";
279 v8::Local<v8::Function> listener_function =
280 FunctionFromString(context, kListenerFunction);
281 ASSERT_FALSE(listener_function.IsEmpty());
282
283 {
284 const char kAddListenerFunction[] =
285 "(function(event, listener) { event.addListener(listener); })";
286 v8::Local<v8::Function> add_listener_function =
287 FunctionFromString(context, kAddListenerFunction);
288 v8::Local<v8::Value> argv[] = {event, listener_function};
289 RunFunction(add_listener_function, context, arraysize(argv), argv);
290 }
291
292 const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
293 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
294 ASSERT_TRUE(event_args);
295 handler.FireEventInContext(kEventName, context, *event_args);
296
297 std::unique_ptr<base::Value> result =
298 GetBaseValuePropertyFromObject(context->Global(), context, "eventArgs");
299 ASSERT_TRUE(result);
300 EXPECT_EQ(ReplaceSingleQuotes(kArguments), ValueToString(*result));
301 }
302
303 // Test dispatching events to multiple contexts.
304 TEST_F(APIEventHandlerTest, MultipleContexts) {
305 v8::Isolate* isolate = instance_->isolate();
306 v8::HandleScope handle_scope(instance_->isolate());
307
308 v8::Local<v8::Context> context_a =
309 v8::Local<v8::Context>::New(isolate, context_);
310 v8::Local<v8::Context> context_b = v8::Context::New(isolate);
311 gin::ContextHolder holder_b(isolate);
312 holder_b.SetContext(context_b);
313
314 const char kEventName[] = "onFoo";
315
316 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
317
318 v8::Local<v8::Function> listener_a = FunctionFromString(
319 context_a, "(function(arg) { this.eventArgs = arg + 'alpha'; })");
320 ASSERT_FALSE(listener_a.IsEmpty());
321 v8::Local<v8::Function> listener_b = FunctionFromString(
322 context_b, "(function(arg) { this.eventArgs = arg + 'beta'; })");
323 ASSERT_FALSE(listener_b.IsEmpty());
324
325 // Create two instances of the same event in different contexts.
326 v8::Local<v8::Object> event_a =
327 handler.CreateEventInstance(kEventName, context_a);
328 ASSERT_FALSE(event_a.IsEmpty());
329 v8::Local<v8::Object> event_b =
330 handler.CreateEventInstance(kEventName, context_b);
331 ASSERT_FALSE(event_b.IsEmpty());
332
333 // Add two separate listeners to the event, one in each context.
334 const char kAddListenerFunction[] =
335 "(function(event, listener) { event.addListener(listener); })";
336 {
337 v8::Local<v8::Function> add_listener_a =
338 FunctionFromString(context_a, kAddListenerFunction);
339 v8::Local<v8::Value> argv[] = {event_a, listener_a};
340 RunFunction(add_listener_a, context_a, arraysize(argv), argv);
341 }
342 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_a));
343 EXPECT_EQ(0u, 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, handler.GetNumEventListenersForTesting(kEventName, context_a));
352 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_b));
353
354 // Dispatch the event in context_a - the listener in context_b should not be
355 // notified.
356 std::unique_ptr<base::ListValue> arguments_a =
357 ListValueFromString("['result_a:']");
358 ASSERT_TRUE(arguments_a);
359
360 handler.FireEventInContext(kEventName, context_a, *arguments_a);
361 {
362 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
363 context_a->Global(), context_a, "eventArgs");
364 ASSERT_TRUE(result_a);
365 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
366 }
367 {
368 v8::Local<v8::Value> result_b =
369 GetPropertyFromObject(context_b->Global(), context_b, "eventArgs");
370 ASSERT_FALSE(result_b.IsEmpty());
371 EXPECT_TRUE(result_b->IsUndefined());
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 {
381 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
382 context_a->Global(), context_a, "eventArgs");
383 ASSERT_TRUE(result_a);
384 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
385 }
386 {
387 std::unique_ptr<base::Value> result_b = GetBaseValuePropertyFromObject(
388 context_b->Global(), context_b, "eventArgs");
389 ASSERT_TRUE(result_b);
390 EXPECT_EQ("\"result_b:beta\"", ValueToString(*result_b));
391 }
392 }
393
394 TEST_F(APIEventHandlerTest, EventArgumentsFoo) {
395 v8::Isolate* isolate = instance_->isolate();
396 v8::HandleScope handle_scope(isolate);
397 v8::Local<v8::Context> context =
398 v8::Local<v8::Context>::New(isolate, context_);
399
400 const char kEventName[] = "alpha";
401 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
402 v8::Local<v8::Object> event =
403 handler.CreateEventInstance(kEventName, context);
404 ASSERT_FALSE(event.IsEmpty());
405
406 const char kAddListenerOnNull[] =
407 "(function(event) {\n"
408 " event.addListener.call(null, function() {});\n"
409 "})";
410 {
411 v8::Local<v8::Value> args[] = {event};
412 // TODO(devlin): This is the generic type error that gin throws. It's not
413 // very descriptive, nor does it match the web (which would just say e.g.
414 // "Illegal invocation"). Might be worth updating later.
415 RunFunctionAndExpectError(
416 FunctionFromString(context, kAddListenerOnNull),
417 context, 1, args,
418 "Uncaught TypeError: Error processing argument at index -1,"
419 " conversion failure from undefined");
420 }
421 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
422
423 const char kAddListenerOnEvent[] =
424 "(function(event) {\n"
425 " event.addListener.call(event, function() {});\n"
426 "})";
427 {
428 v8::Local<v8::Value> args[] = {event};
429 RunFunction(FunctionFromString(context, kAddListenerOnEvent),
430 context, 1, args);
431 }
432 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
433
434 const char kAddListenerOnEventWithCapture[] =
435 "(function(event) {\n"
436 " event.addListener(function listener() {\n"
437 " event.hasListener(listener);\n"
438 " });\n"
439 "})";
440 {
441 v8::Local<v8::Value> args[] = {event};
442 RunFunction(FunctionFromString(context, kAddListenerOnEventWithCapture),
443 context, 1, args);
444 }
445 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kEventName, context));
446 }
447
448 } // 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