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

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

Issue 2768093002: [Reland][Extensions Bindings] Add support for filtered events (Closed)
Patch Set: Fix Created 3 years, 9 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_listeners.cc ('k') | extensions/renderer/chrome_setting.cc » ('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 2017 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_listeners.h"
6
7 #include "base/test/mock_callback.h"
8 #include "base/values.h"
9 #include "extensions/common/event_filter.h"
10 #include "extensions/renderer/api_binding_test.h"
11 #include "extensions/renderer/api_binding_test_util.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13
14 namespace extensions {
15
16 namespace {
17
18 using APIEventListenersTest = APIBindingTest;
19 using MockEventChangeHandler = ::testing::StrictMock<
20 base::MockCallback<APIEventListeners::ListenersUpdated>>;
21
22 void DoNothingOnUpdate(binding::EventListenersChanged changed,
23 const base::DictionaryValue* filter,
24 v8::Local<v8::Context> context) {}
25
26 const char kFunction[] = "(function() {})";
27 const char kEvent[] = "event";
28
29 } // namespace
30
31 // Test unfiltered listeners.
32 TEST_F(APIEventListenersTest, UnfilteredListeners) {
33 v8::HandleScope handle_scope(isolate());
34 v8::Local<v8::Context> context = MainContext();
35
36 MockEventChangeHandler handler;
37 UnfilteredEventListeners listeners(handler.Get());
38
39 // Starting out, there should be no listeners.
40 v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction);
41 EXPECT_EQ(0u, listeners.GetNumListeners());
42 EXPECT_FALSE(listeners.HasListener(function_a));
43
44 std::string error;
45 v8::Local<v8::Object> filter;
46
47 // Adding a new listener should trigger the callback (0 -> 1).
48 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
49 nullptr, context));
50 EXPECT_TRUE(listeners.AddListener(function_a, filter, context, &error));
51 ::testing::Mock::VerifyAndClearExpectations(&handler);
52
53 // function_a should be registered as a listener, and should be returned when
54 // we get the listeners.
55 EXPECT_TRUE(listeners.HasListener(function_a));
56 EXPECT_EQ(1u, listeners.GetNumListeners());
57 EXPECT_THAT(listeners.GetListeners(nullptr, context),
58 testing::UnorderedElementsAre(function_a));
59
60 // Trying to add function_a again should have no effect.
61 EXPECT_FALSE(listeners.AddListener(function_a, filter, context, &error));
62 EXPECT_TRUE(listeners.HasListener(function_a));
63 EXPECT_EQ(1u, listeners.GetNumListeners());
64
65 v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction);
66
67 // We should not yet have function_b registered, and trying to remove it
68 // should have no effect.
69 EXPECT_FALSE(listeners.HasListener(function_b));
70 listeners.RemoveListener(function_b, context);
71 EXPECT_EQ(1u, listeners.GetNumListeners());
72 EXPECT_THAT(listeners.GetListeners(nullptr, context),
73 testing::UnorderedElementsAre(function_a));
74
75 // Add function_b; there should now be two listeners, and both should be
76 // returned when we get the listeners. However, the callback shouldn't be
77 // triggered, since this isn't a 0 -> 1 or 1 -> 0 transition.
78 EXPECT_TRUE(listeners.AddListener(function_b, filter, context, &error));
79 EXPECT_TRUE(listeners.HasListener(function_b));
80 EXPECT_EQ(2u, listeners.GetNumListeners());
81 EXPECT_THAT(listeners.GetListeners(nullptr, context),
82 testing::UnorderedElementsAre(function_a, function_b));
83
84 // Remove function_a; there should now be only one listener. The callback
85 // shouldn't be triggered.
86 listeners.RemoveListener(function_a, context);
87 EXPECT_FALSE(listeners.HasListener(function_a));
88 EXPECT_EQ(1u, listeners.GetNumListeners());
89 EXPECT_THAT(listeners.GetListeners(nullptr, context),
90 testing::UnorderedElementsAre(function_b));
91
92 // Remove function_b (the final listener). No more listeners should remain.
93 EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS,
94 nullptr, context));
95 listeners.RemoveListener(function_b, context);
96 ::testing::Mock::VerifyAndClearExpectations(&handler);
97 EXPECT_FALSE(listeners.HasListener(function_b));
98 EXPECT_EQ(0u, listeners.GetNumListeners());
99 EXPECT_TRUE(listeners.GetListeners(nullptr, context).empty());
100 }
101
102 // Tests the invalidation of unfiltered listeners.
103 TEST_F(APIEventListenersTest, UnfilteredListenersInvalidation) {
104 v8::HandleScope handle_scope(isolate());
105 v8::Local<v8::Context> context = MainContext();
106
107 MockEventChangeHandler handler;
108 UnfilteredEventListeners listeners(handler.Get());
109
110 listeners.Invalidate(context);
111
112 v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction);
113 v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction);
114 std::string error;
115 v8::Local<v8::Object> filter;
116 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
117 nullptr, context));
118 EXPECT_TRUE(listeners.AddListener(function_a, filter, context, &error));
119 ::testing::Mock::VerifyAndClearExpectations(&handler);
120 EXPECT_TRUE(listeners.AddListener(function_b, filter, context, &error));
121
122 EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS,
123 nullptr, context));
124 listeners.Invalidate(context);
125 ::testing::Mock::VerifyAndClearExpectations(&handler);
126
127 EXPECT_EQ(0u, listeners.GetNumListeners());
128 }
129
130 // Tests that unfiltered listeners ignore the filtering info.
131 TEST_F(APIEventListenersTest, UnfilteredListenersIgnoreFilteringInfo) {
132 v8::HandleScope handle_scope(isolate());
133 v8::Local<v8::Context> context = MainContext();
134
135 UnfilteredEventListeners listeners(base::Bind(&DoNothingOnUpdate));
136 v8::Local<v8::Function> function = FunctionFromString(context, kFunction);
137 std::string error;
138 v8::Local<v8::Object> filter;
139 EXPECT_TRUE(listeners.AddListener(function, filter, context, &error));
140 std::unique_ptr<base::DictionaryValue> filtering_info_dict =
141 DictionaryValueFromString("{'url': 'http://example.com/foo'}");
142 EventFilteringInfo filtering_info(*filtering_info_dict);
143 EXPECT_THAT(listeners.GetListeners(&filtering_info, context),
144 testing::UnorderedElementsAre(function));
145 }
146
147 // Tests filtered listeners.
148 TEST_F(APIEventListenersTest, FilteredListeners) {
149 v8::HandleScope handle_scope(isolate());
150 v8::Local<v8::Context> context = MainContext();
151
152 MockEventChangeHandler handler;
153 EventFilter event_filter;
154 FilteredEventListeners listeners(handler.Get(), kEvent, &event_filter);
155
156 // Starting out, there should be no listeners registered.
157 v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction);
158 EXPECT_EQ(0u, listeners.GetNumListeners());
159 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent));
160 EXPECT_FALSE(listeners.HasListener(function_a));
161
162 v8::Local<v8::Object> empty_filter;
163 std::string error;
164
165 // Register function_a with no filter; this is equivalent to registering for
166 // all events. The callback should be triggered since this is a 0 -> 1
167 // transition.
168 // Note that we don't test the passed filter here. This is mostly because it's
169 // a pain to match against a DictionaryValue (which doesn't have an
170 // operator==).
171 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
172 testing::NotNull(), context));
173 EXPECT_TRUE(listeners.AddListener(function_a, empty_filter, context, &error));
174 ::testing::Mock::VerifyAndClearExpectations(&handler);
175
176 // function_a should be registered, and should be returned when we get the
177 // listeners.
178 EXPECT_TRUE(listeners.HasListener(function_a));
179 EXPECT_EQ(1u, listeners.GetNumListeners());
180 EXPECT_THAT(listeners.GetListeners(nullptr, context),
181 testing::UnorderedElementsAre(function_a));
182
183 // It should also be registered in the event filter.
184 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kEvent));
185
186 // Since function_a has no filter, associating a specific url should still
187 // return function_a.
188 std::unique_ptr<base::DictionaryValue> filtering_info_match_dict =
189 DictionaryValueFromString("{'url': 'http://example.com/foo'}");
190 ASSERT_TRUE(filtering_info_match_dict);
191 EventFilteringInfo filtering_info_match(*filtering_info_match_dict);
192 EXPECT_THAT(listeners.GetListeners(&filtering_info_match, context),
193 testing::UnorderedElementsAre(function_a));
194
195 // Trying to add function_a again should have no effect.
196 EXPECT_FALSE(
197 listeners.AddListener(function_a, empty_filter, context, &error));
198 EXPECT_TRUE(listeners.HasListener(function_a));
199 EXPECT_EQ(1u, listeners.GetNumListeners());
200
201 v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction);
202
203 // function_b should not yet be registered, and trying to remove it should
204 // have no effect.
205 EXPECT_FALSE(listeners.HasListener(function_b));
206 listeners.RemoveListener(function_b, context);
207 EXPECT_EQ(1u, listeners.GetNumListeners());
208 EXPECT_THAT(listeners.GetListeners(nullptr, context),
209 testing::UnorderedElementsAre(function_a));
210
211 // Register function_b with a filter for pathContains: 'foo'. Unlike
212 // unfiltered listeners, this *should* trigger the callback, since there is
213 // no other listener registered with this same filter.
214 v8::Local<v8::Object> path_filter;
215 {
216 v8::Local<v8::Value> val =
217 V8ValueFromScriptSource(context, "({url: [{pathContains: 'foo'}]})");
218 ASSERT_TRUE(val->IsObject());
219 path_filter = val.As<v8::Object>();
220 }
221 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
222 testing::NotNull(), context));
223 EXPECT_TRUE(listeners.AddListener(function_b, path_filter, context, &error));
224 ::testing::Mock::VerifyAndClearExpectations(&handler);
225
226 // function_b should be present.
227 EXPECT_TRUE(listeners.HasListener(function_b));
228 EXPECT_EQ(2u, listeners.GetNumListeners());
229 EXPECT_EQ(2, event_filter.GetMatcherCountForEventForTesting(kEvent));
230
231 // function_b should ignore calls that don't specify an url, since they, by
232 // definition, don't match.
233 EXPECT_THAT(listeners.GetListeners(nullptr, context),
234 testing::UnorderedElementsAre(function_a));
235 // function_b should be included for matching urls...
236 EXPECT_THAT(listeners.GetListeners(&filtering_info_match, context),
237 testing::UnorderedElementsAre(function_a, function_b));
238 // ... but not urls that don't match.
239 std::unique_ptr<base::DictionaryValue> filtering_info_no_match_dict =
240 DictionaryValueFromString("{'url': 'http://example.com/bar'}");
241 ASSERT_TRUE(filtering_info_no_match_dict);
242 EventFilteringInfo filtering_info_no_match(*filtering_info_no_match_dict);
243 EXPECT_THAT(listeners.GetListeners(&filtering_info_no_match, context),
244 testing::UnorderedElementsAre(function_a));
245
246 // Remove function_a. Since filtered listeners notify whenever there's a
247 // change in listeners registered with a specific filter, this should trigger
248 // the callback.
249 EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS,
250 testing::NotNull(), context));
251 listeners.RemoveListener(function_a, context);
252 ::testing::Mock::VerifyAndClearExpectations(&handler);
253 EXPECT_FALSE(listeners.HasListener(function_a));
254 EXPECT_EQ(1u, listeners.GetNumListeners());
255 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kEvent));
256 // function_b should be the only listener remaining, so we shouldn't find
257 // any listeners for events without matching filters.
258 EXPECT_TRUE(listeners.GetListeners(nullptr, context).empty());
259 EXPECT_THAT(listeners.GetListeners(&filtering_info_match, context),
260 testing::UnorderedElementsAre(function_b));
261 EXPECT_TRUE(
262 listeners.GetListeners(&filtering_info_no_match, context).empty());
263
264 // Remove function_b. No listeners should remain.
265 EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS,
266 testing::NotNull(), context));
267 listeners.RemoveListener(function_b, context);
268 ::testing::Mock::VerifyAndClearExpectations(&handler);
269 EXPECT_FALSE(listeners.HasListener(function_b));
270 EXPECT_EQ(0u, listeners.GetNumListeners());
271 EXPECT_TRUE(listeners.GetListeners(nullptr, context).empty());
272 EXPECT_TRUE(listeners.GetListeners(&filtering_info_match, context).empty());
273 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent));
274 }
275
276 // Tests that adding multiple listeners with the same filter doesn't trigger
277 // the update callback.
278 TEST_F(APIEventListenersTest,
279 UnfilteredListenersWithSameFilterDontTriggerUpdate) {
280 v8::HandleScope handle_scope(isolate());
281 v8::Local<v8::Context> context = MainContext();
282
283 MockEventChangeHandler handler;
284 EventFilter event_filter;
285 FilteredEventListeners listeners(handler.Get(), kEvent, &event_filter);
286
287 auto get_filter = [context]() {
288 return V8ValueFromScriptSource(context, "({url: [{pathContains: 'foo'}]})")
289 .As<v8::Object>();
290 };
291
292 v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction);
293
294 std::string error;
295 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
296 testing::NotNull(), context));
297 EXPECT_TRUE(listeners.AddListener(function_a, get_filter(), context, &error));
298 ::testing::Mock::VerifyAndClearExpectations(&handler);
299 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kEvent));
300
301 v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction);
302 v8::Local<v8::Function> function_c = FunctionFromString(context, kFunction);
303 EXPECT_TRUE(listeners.AddListener(function_b, get_filter(), context, &error));
304 EXPECT_TRUE(listeners.AddListener(function_c, get_filter(), context, &error));
305 EXPECT_EQ(3u, listeners.GetNumListeners());
306 EXPECT_EQ(3, event_filter.GetMatcherCountForEventForTesting(kEvent));
307
308 std::unique_ptr<base::DictionaryValue> filtering_info_match_dict =
309 DictionaryValueFromString("{'url': 'http://example.com/foo'}");
310 ASSERT_TRUE(filtering_info_match_dict);
311 EventFilteringInfo filtering_info_match(*filtering_info_match_dict);
312 EXPECT_THAT(
313 listeners.GetListeners(&filtering_info_match, context),
314 testing::UnorderedElementsAre(function_a, function_b, function_c));
315
316 listeners.RemoveListener(function_c, context);
317 listeners.RemoveListener(function_b, context);
318
319 EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS,
320 testing::NotNull(), context));
321 listeners.RemoveListener(function_a, context);
322 ::testing::Mock::VerifyAndClearExpectations(&handler);
323 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent));
324 }
325
326 // Tests that trying to add a listener with an invalid filter fails.
327 TEST_F(APIEventListenersTest, UnfilteredListenersError) {
328 v8::HandleScope handle_scope(isolate());
329 v8::Local<v8::Context> context = MainContext();
330
331 EventFilter event_filter;
332 FilteredEventListeners listeners(base::Bind(&DoNothingOnUpdate), kEvent,
333 &event_filter);
334
335 v8::Local<v8::Object> invalid_filter =
336 V8ValueFromScriptSource(context, "({url: 'some string'})")
337 .As<v8::Object>();
338 v8::Local<v8::Function> function = FunctionFromString(context, kFunction);
339 std::string error;
340 EXPECT_FALSE(
341 listeners.AddListener(function, invalid_filter, context, &error));
342 EXPECT_FALSE(error.empty());
343 }
344
345 // Tests that adding listeners for multiple different events is correctly
346 // recorded in the EventFilter.
347 TEST_F(APIEventListenersTest, MultipleUnfilteredListenerEvents) {
348 v8::HandleScope handle_scope(isolate());
349 v8::Local<v8::Context> context = MainContext();
350
351 const char kAlpha[] = "alpha";
352 const char kBeta[] = "beta";
353
354 EventFilter event_filter;
355 FilteredEventListeners listeners_a(base::Bind(&DoNothingOnUpdate), kAlpha,
356 &event_filter);
357 FilteredEventListeners listeners_b(base::Bind(&DoNothingOnUpdate), kBeta,
358 &event_filter);
359
360 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kAlpha));
361 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta));
362
363 std::string error;
364 v8::Local<v8::Object> filter;
365
366 v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction);
367 EXPECT_TRUE(listeners_a.AddListener(function_a, filter, context, &error));
368 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kAlpha));
369 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta));
370
371 v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction);
372 EXPECT_TRUE(listeners_b.AddListener(function_b, filter, context, &error));
373 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kAlpha));
374 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kBeta));
375
376 listeners_b.RemoveListener(function_b, context);
377 EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kAlpha));
378 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta));
379
380 listeners_a.RemoveListener(function_a, context);
381 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kAlpha));
382 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta));
383 }
384
385 // Tests the invalidation of filtered listeners.
386 TEST_F(APIEventListenersTest, FilteredListenersInvalidation) {
387 v8::HandleScope handle_scope(isolate());
388 v8::Local<v8::Context> context = MainContext();
389
390 MockEventChangeHandler handler;
391 EventFilter event_filter;
392 FilteredEventListeners listeners(handler.Get(), kEvent, &event_filter);
393 listeners.Invalidate(context);
394
395 v8::Local<v8::Object> empty_filter;
396 v8::Local<v8::Object> filter =
397 V8ValueFromScriptSource(context, "({url: [{pathContains: 'foo'}]})")
398 .As<v8::Object>();
399 std::string error;
400
401 v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction);
402 v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction);
403 v8::Local<v8::Function> function_c = FunctionFromString(context, kFunction);
404
405 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
406 testing::NotNull(), context));
407 EXPECT_TRUE(listeners.AddListener(function_a, empty_filter, context, &error));
408 ::testing::Mock::VerifyAndClearExpectations(&handler);
409 EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS,
410 testing::NotNull(), context));
411 EXPECT_TRUE(listeners.AddListener(function_b, filter, context, &error));
412 ::testing::Mock::VerifyAndClearExpectations(&handler);
413 EXPECT_TRUE(listeners.AddListener(function_c, filter, context, &error));
414
415 // Since two listener filters are present in the list, we should be notified
416 // of each going away when we invalidate the context.
417 EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS,
418 testing::NotNull(), context))
419 .Times(2);
420 listeners.Invalidate(context);
421 ::testing::Mock::VerifyAndClearExpectations(&handler);
422
423 EXPECT_EQ(0u, listeners.GetNumListeners());
424 EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent));
425 }
426
427 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_event_listeners.cc ('k') | extensions/renderer/chrome_setting.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698