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

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

Issue 2469593002: [Extensions Bindings] Add Events support (Closed)
Patch Set: jbromans II 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
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 holder_.reset();
35 gin::V8Test::TearDown();
36 }
37
38 void CallFunctionOnObject(v8::Local<v8::Context> context,
39 v8::Local<v8::Object> object,
40 const std::string& script_source) {
41 std::string wrapped_script_source =
42 base::StringPrintf("(function(obj) { %s })", script_source.c_str());
43 v8::Local<v8::Function> func =
44 FunctionFromString(context, wrapped_script_source);
45 ASSERT_FALSE(func.IsEmpty());
46
47 v8::Local<v8::Value> argv[] = {object};
48 RunFunction(func, context, object, 1, argv);
49 }
50
51 private:
52 std::unique_ptr<gin::ContextHolder> holder_;
53
54 DISALLOW_COPY_AND_ASSIGN(APIEventHandlerTest);
55 };
56
57 // Tests adding, removing, and querying event listeners by calling the
58 // associated methods on the JS object.
59 TEST_F(APIEventHandlerTest, AddingRemovingAndQueryingEventListeners) {
60 const char kEventName[] = "alpha";
61 v8::Isolate* isolate = instance_->isolate();
62 v8::HandleScope handle_scope(instance_->isolate());
63 v8::Local<v8::Context> context =
64 v8::Local<v8::Context>::New(instance_->isolate(), context_);
65
66 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
67 v8::Local<v8::Object> event =
68 handler.CreateEventInstance(kEventName, context);
69 ASSERT_FALSE(event.IsEmpty());
70
71 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
72
73 const char kListenerFunction[] = "(function() {})";
74 v8::Local<v8::Function> listener_function =
75 FunctionFromString(context, kListenerFunction);
76 ASSERT_FALSE(listener_function.IsEmpty());
77
78 const char kAddListenerFunction[] =
79 "(function(event, listener) { event.addListener(listener); })";
80 v8::Local<v8::Function> add_listener_function =
81 FunctionFromString(context, kAddListenerFunction);
82
83 {
84 v8::Local<v8::Value> argv[] = {event, listener_function};
85 RunFunction(add_listener_function, context, arraysize(argv), argv);
86 }
87 // There should only be one listener on the event.
88 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
89
90 {
91 v8::Local<v8::Value> argv[] = {event, listener_function};
92 RunFunction(add_listener_function, context, arraysize(argv), argv);
93 }
94 // Trying to add the same listener again should be a no-op.
95 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context));
96
97 // Test hasListener returns true for a listener that is present.
98 const char kHasListenerFunction[] =
99 "(function(event, listener) { return event.hasListener(listener); })";
100 v8::Local<v8::Function> has_listener_function =
101 FunctionFromString(context, kHasListenerFunction);
102 {
103 v8::Local<v8::Value> argv[] = {event, listener_function};
104 v8::Local<v8::Value> result =
105 RunFunction(has_listener_function, context, arraysize(argv), argv);
106 bool has_listener = false;
107 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener));
108 EXPECT_TRUE(has_listener);
109 }
110
111 // Test that hasListener returns false for a listener that isn't present.
112 {
113 v8::Local<v8::Function> not_a_listener =
114 FunctionFromString(context, "(function() {})");
115 v8::Local<v8::Value> argv[] = {event, not_a_listener};
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_FALSE(has_listener);
121 }
122
123 // Test hasListeners returns true
124 const char kHasListenersFunction[] =
125 "(function(event) { return event.hasListeners(); })";
126 v8::Local<v8::Function> has_listeners_function =
127 FunctionFromString(context, kHasListenersFunction);
128 {
129 v8::Local<v8::Value> argv[] = {event};
130 v8::Local<v8::Value> result =
131 RunFunction(has_listeners_function, context, arraysize(argv), argv);
132 bool has_listeners = false;
133 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners));
134 EXPECT_TRUE(has_listeners);
135 }
136
137 const char kRemoveListenerFunction[] =
138 "(function(event, listener) { event.removeListener(listener); })";
139 v8::Local<v8::Function> remove_listener_function =
140 FunctionFromString(context, kRemoveListenerFunction);
141 {
142 v8::Local<v8::Value> argv[] = {event, listener_function};
143 RunFunction(remove_listener_function, context, arraysize(argv), argv);
144 }
145 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context));
146
147 {
148 v8::Local<v8::Value> argv[] = {event};
149 v8::Local<v8::Value> result =
150 RunFunction(has_listeners_function, context, arraysize(argv), argv);
151 bool has_listeners = false;
152 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners));
153 EXPECT_FALSE(has_listeners);
154 }
155 }
156
157 // Tests listening for and firing different events.
158 TEST_F(APIEventHandlerTest, FiringEvents) {
159 const char kAlphaName[] = "alpha";
160 const char kBetaName[] = "beta";
161 v8::HandleScope handle_scope(instance_->isolate());
162 v8::Local<v8::Context> context =
163 v8::Local<v8::Context>::New(instance_->isolate(), context_);
164
165 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
166 v8::Local<v8::Object> alpha_event =
167 handler.CreateEventInstance(kAlphaName, context);
168 v8::Local<v8::Object> beta_event =
169 handler.CreateEventInstance(kBetaName, context);
170 ASSERT_FALSE(alpha_event.IsEmpty());
171 ASSERT_FALSE(beta_event.IsEmpty());
172
173 const char kAlphaListenerFunction1[] =
174 "(function() {\n"
175 " if (!this.alphaCount1) this.alphaCount1 = 0;\n"
176 " ++this.alphaCount1;\n"
177 "});\n";
178 v8::Local<v8::Function> alpha_listener1 =
179 FunctionFromString(context, kAlphaListenerFunction1);
180 const char kAlphaListenerFunction2[] =
181 "(function() {\n"
182 " if (!this.alphaCount2) this.alphaCount2 = 0;\n"
183 " ++this.alphaCount2;\n"
184 "});\n";
185 v8::Local<v8::Function> alpha_listener2 =
186 FunctionFromString(context, kAlphaListenerFunction2);
187 const char kBetaListenerFunction[] =
188 "(function() {\n"
189 " if (!this.betaCount) this.betaCount = 0;\n"
190 " ++this.betaCount;\n"
191 "});\n";
192 v8::Local<v8::Function> beta_listener =
193 FunctionFromString(context, kBetaListenerFunction);
194 ASSERT_FALSE(alpha_listener1.IsEmpty());
195 ASSERT_FALSE(alpha_listener2.IsEmpty());
196 ASSERT_FALSE(beta_listener.IsEmpty());
197
198 {
199 const char kAddListenerFunction[] =
200 "(function(event, listener) { event.addListener(listener); })";
201 v8::Local<v8::Function> add_listener_function =
202 FunctionFromString(context, kAddListenerFunction);
203 {
204 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener1};
205 RunFunction(add_listener_function, context, arraysize(argv), argv);
206 }
207 {
208 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener2};
209 RunFunction(add_listener_function, context, arraysize(argv), argv);
210 }
211 {
212 v8::Local<v8::Value> argv[] = {beta_event, beta_listener};
213 RunFunction(add_listener_function, context, arraysize(argv), argv);
214 }
215 }
216
217 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kAlphaName, context));
218 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kBetaName, context));
219
220 auto get_fired_count = [&context](const char* name) {
221 v8::Local<v8::Value> res =
222 GetPropertyFromObject(context->Global(), context, name);
223 if (res->IsUndefined())
224 return 0;
225 int32_t count = 0;
226 EXPECT_TRUE(
227 gin::Converter<int32_t>::FromV8(context->GetIsolate(), res, &count))
228 << name;
229 return count;
230 };
231
232 EXPECT_EQ(0, get_fired_count("alphaCount1"));
233 EXPECT_EQ(0, get_fired_count("alphaCount2"));
234 EXPECT_EQ(0, get_fired_count("betaCount"));
235
236 handler.FireEventInContext(kAlphaName, context, base::ListValue());
237 EXPECT_EQ(2u, handler.GetNumEventListenersForTesting(kAlphaName, context));
238 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kBetaName, context));
239
240 EXPECT_EQ(1, get_fired_count("alphaCount1"));
241 EXPECT_EQ(1, get_fired_count("alphaCount2"));
242 EXPECT_EQ(0, get_fired_count("betaCount"));
243
244 handler.FireEventInContext(kAlphaName, context, base::ListValue());
245 EXPECT_EQ(2, get_fired_count("alphaCount1"));
246 EXPECT_EQ(2, get_fired_count("alphaCount2"));
247 EXPECT_EQ(0, get_fired_count("betaCount"));
248
249 handler.FireEventInContext(kBetaName, context, base::ListValue());
250 EXPECT_EQ(2, get_fired_count("alphaCount1"));
251 EXPECT_EQ(2, get_fired_count("alphaCount2"));
252 EXPECT_EQ(1, get_fired_count("betaCount"));
253 }
254
255 // Tests firing events with arguments.
256 TEST_F(APIEventHandlerTest, EventArguments) {
257 v8::Isolate* isolate = instance_->isolate();
258 v8::HandleScope handle_scope(isolate);
259 v8::Local<v8::Context> context =
260 v8::Local<v8::Context>::New(isolate, context_);
261
262 const char kEventName[] = "alpha";
263 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
264 v8::Local<v8::Object> event =
265 handler.CreateEventInstance(kEventName, context);
266 ASSERT_FALSE(event.IsEmpty());
267
268 const char kListenerFunction[] =
269 "(function() { this.eventArgs = Array.from(arguments); })";
270 v8::Local<v8::Function> listener_function =
271 FunctionFromString(context, kListenerFunction);
272 ASSERT_FALSE(listener_function.IsEmpty());
273
274 {
275 const char kAddListenerFunction[] =
276 "(function(event, listener) { event.addListener(listener); })";
277 v8::Local<v8::Function> add_listener_function =
278 FunctionFromString(context, kAddListenerFunction);
279 v8::Local<v8::Value> argv[] = {event, listener_function};
280 RunFunction(add_listener_function, context, arraysize(argv), argv);
281 }
282
283 const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
284 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments);
285 ASSERT_TRUE(event_args);
286 handler.FireEventInContext(kEventName, context, *event_args);
287
288 std::unique_ptr<base::Value> result =
289 GetBaseValuePropertyFromObject(context->Global(), context, "eventArgs");
290 ASSERT_TRUE(result);
291 EXPECT_EQ(ReplaceSingleQuotes(kArguments), ValueToString(*result));
292 }
293
294 // Test dispatching events to multiple contexts.
295 TEST_F(APIEventHandlerTest, MultipleContexts) {
296 v8::Isolate* isolate = instance_->isolate();
297 v8::HandleScope handle_scope(instance_->isolate());
298
299 v8::Local<v8::Context> context_a =
300 v8::Local<v8::Context>::New(isolate, context_);
301 v8::Local<v8::Context> context_b = v8::Context::New(isolate);
302 gin::ContextHolder holder_b(isolate);
303 holder_b.SetContext(context_b);
304
305 const char kEventName[] = "onFoo";
306
307 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
308
309 v8::Local<v8::Function> listener_a = FunctionFromString(
310 context_a, "(function(arg) { this.eventArgs = arg + 'alpha'; })");
311 ASSERT_FALSE(listener_a.IsEmpty());
312 v8::Local<v8::Function> listener_b = FunctionFromString(
313 context_b, "(function(arg) { this.eventArgs = arg + 'beta'; })");
314 ASSERT_FALSE(listener_b.IsEmpty());
315
316 // Create two instances of the same event in different contexts.
317 v8::Local<v8::Object> event_a =
318 handler.CreateEventInstance(kEventName, context_a);
319 ASSERT_FALSE(event_a.IsEmpty());
320 v8::Local<v8::Object> event_b =
321 handler.CreateEventInstance(kEventName, context_b);
322 ASSERT_FALSE(event_b.IsEmpty());
323
324 // Add two separate listeners to the event, one in each context.
325 const char kAddListenerFunction[] =
326 "(function(event, listener) { event.addListener(listener); })";
327 {
328 v8::Local<v8::Function> add_listener_a =
329 FunctionFromString(context_a, kAddListenerFunction);
330 v8::Local<v8::Value> argv[] = {event_a, listener_a};
331 RunFunction(add_listener_a, context_a, arraysize(argv), argv);
332 }
333 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_a));
334 EXPECT_EQ(0u, handler.GetNumEventListenersForTesting(kEventName, context_b));
335
336 {
337 v8::Local<v8::Function> add_listener_b =
338 FunctionFromString(context_b, kAddListenerFunction);
339 v8::Local<v8::Value> argv[] = {event_b, listener_b};
340 RunFunction(add_listener_b, context_b, arraysize(argv), argv);
341 }
342 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_a));
343 EXPECT_EQ(1u, handler.GetNumEventListenersForTesting(kEventName, context_b));
344
345 // Dispatch the event in context_a - the listener in context_b should not be
346 // notified.
347 std::unique_ptr<base::ListValue> arguments_a =
348 ListValueFromString("['result_a:']");
349 ASSERT_TRUE(arguments_a);
350
351 handler.FireEventInContext(kEventName, context_a, *arguments_a);
352 {
353 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
354 context_a->Global(), context_a, "eventArgs");
355 ASSERT_TRUE(result_a);
356 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
357 }
358 {
359 v8::Local<v8::Value> result_b =
360 GetPropertyFromObject(context_b->Global(), context_b, "eventArgs");
361 ASSERT_FALSE(result_b.IsEmpty());
362 EXPECT_TRUE(result_b->IsUndefined());
363 }
364
365 // Dispatch the event in context_b - the listener in context_a should not be
366 // notified.
367 std::unique_ptr<base::ListValue> arguments_b =
368 ListValueFromString("['result_b:']");
369 ASSERT_TRUE(arguments_b);
370 handler.FireEventInContext(kEventName, context_b, *arguments_b);
371 {
372 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
373 context_a->Global(), context_a, "eventArgs");
374 ASSERT_TRUE(result_a);
375 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
376 }
377 {
378 std::unique_ptr<base::Value> result_b = GetBaseValuePropertyFromObject(
379 context_b->Global(), context_b, "eventArgs");
380 ASSERT_TRUE(result_b);
381 EXPECT_EQ("\"result_b:beta\"", ValueToString(*result_b));
382 }
383 }
384
385 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698