| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/renderer/bindings/api_event_listeners.h" |
| |
| #include "base/test/mock_callback.h" |
| #include "base/values.h" |
| #include "extensions/common/event_filter.h" |
| #include "extensions/renderer/bindings/api_binding_test.h" |
| #include "extensions/renderer/bindings/api_binding_test_util.h" |
| #include "extensions/renderer/bindings/api_binding_types.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| using APIEventListenersTest = APIBindingTest; |
| using MockEventChangeHandler = ::testing::StrictMock< |
| base::MockCallback<APIEventListeners::ListenersUpdated>>; |
| |
| void DoNothingOnUpdate(binding::EventListenersChanged changed, |
| const base::DictionaryValue* filter, |
| bool was_manual, |
| v8::Local<v8::Context> context) {} |
| |
| const char kFunction[] = "(function() {})"; |
| const char kEvent[] = "event"; |
| |
| } // namespace |
| |
| // Test unfiltered listeners. |
| TEST_F(APIEventListenersTest, UnfilteredListeners) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| UnfilteredEventListeners listeners(handler.Get(), binding::kNoListenerMax, |
| true); |
| |
| // Starting out, there should be no listeners. |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| EXPECT_FALSE(listeners.HasListener(function_a)); |
| |
| std::string error; |
| v8::Local<v8::Object> filter; |
| |
| // Adding a new listener should trigger the callback (0 -> 1). |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| nullptr, true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_a, filter, context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| // function_a should be registered as a listener, and should be returned when |
| // we get the listeners. |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_a)); |
| |
| // Trying to add function_a again should have no effect. |
| EXPECT_FALSE(listeners.AddListener(function_a, filter, context, &error)); |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| |
| // We should not yet have function_b registered, and trying to remove it |
| // should have no effect. |
| EXPECT_FALSE(listeners.HasListener(function_b)); |
| listeners.RemoveListener(function_b, context); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_a)); |
| |
| // Add function_b; there should now be two listeners, and both should be |
| // returned when we get the listeners. However, the callback shouldn't be |
| // triggered, since this isn't a 0 -> 1 or 1 -> 0 transition. |
| EXPECT_TRUE(listeners.AddListener(function_b, filter, context, &error)); |
| EXPECT_TRUE(listeners.HasListener(function_b)); |
| EXPECT_EQ(2u, listeners.GetNumListeners()); |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_a, function_b)); |
| |
| // Remove function_a; there should now be only one listener. The callback |
| // shouldn't be triggered. |
| listeners.RemoveListener(function_a, context); |
| EXPECT_FALSE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_b)); |
| |
| // Remove function_b (the final listener). No more listeners should remain. |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| nullptr, true, context)); |
| listeners.RemoveListener(function_b, context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_FALSE(listeners.HasListener(function_b)); |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| EXPECT_TRUE(listeners.GetListeners(nullptr, context).empty()); |
| } |
| |
| // Tests the invalidation of unfiltered listeners. |
| TEST_F(APIEventListenersTest, UnfilteredListenersInvalidation) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| UnfilteredEventListeners listeners(handler.Get(), binding::kNoListenerMax, |
| true); |
| |
| listeners.Invalidate(context); |
| |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| std::string error; |
| v8::Local<v8::Object> filter; |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| nullptr, true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_a, filter, context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_TRUE(listeners.AddListener(function_b, filter, context, &error)); |
| |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| nullptr, false, context)); |
| listeners.Invalidate(context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| } |
| |
| // Tests that unfiltered listeners ignore the filtering info. |
| TEST_F(APIEventListenersTest, UnfilteredListenersIgnoreFilteringInfo) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| UnfilteredEventListeners listeners(base::Bind(&DoNothingOnUpdate), |
| binding::kNoListenerMax, true); |
| v8::Local<v8::Function> function = FunctionFromString(context, kFunction); |
| std::string error; |
| v8::Local<v8::Object> filter; |
| EXPECT_TRUE(listeners.AddListener(function, filter, context, &error)); |
| EventFilteringInfo filtering_info; |
| filtering_info.url = GURL("http://example.com/foo"); |
| EXPECT_THAT(listeners.GetListeners(&filtering_info, context), |
| testing::UnorderedElementsAre(function)); |
| } |
| |
| TEST_F(APIEventListenersTest, UnfilteredListenersMaxListenersTest) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| UnfilteredEventListeners listeners(base::Bind(&DoNothingOnUpdate), 1, true); |
| |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| |
| std::string error; |
| v8::Local<v8::Object> filter; |
| EXPECT_TRUE(listeners.AddListener(function_a, filter, context, &error)); |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| EXPECT_FALSE(listeners.AddListener(function_b, filter, context, &error)); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_FALSE(listeners.HasListener(function_b)); |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| } |
| |
| TEST_F(APIEventListenersTest, UnfilteredListenersLazyListeners) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| UnfilteredEventListeners listeners(handler.Get(), binding::kNoListenerMax, |
| false); |
| |
| v8::Local<v8::Function> listener = FunctionFromString(context, kFunction); |
| std::string error; |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| nullptr, false, context)); |
| listeners.AddListener(listener, v8::Local<v8::Object>(), context, &error); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| nullptr, false, context)); |
| listeners.RemoveListener(listener, context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| } |
| |
| // Tests filtered listeners. |
| TEST_F(APIEventListenersTest, FilteredListeners) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| EventFilter event_filter; |
| FilteredEventListeners listeners( |
| handler.Get(), kEvent, binding::kNoListenerMax, true, &event_filter); |
| |
| // Starting out, there should be no listeners registered. |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| EXPECT_FALSE(listeners.HasListener(function_a)); |
| |
| v8::Local<v8::Object> empty_filter; |
| std::string error; |
| |
| // Register function_a with no filter; this is equivalent to registering for |
| // all events. The callback should be triggered since this is a 0 -> 1 |
| // transition. |
| // Note that we don't test the passed filter here. This is mostly because it's |
| // a pain to match against a DictionaryValue (which doesn't have an |
| // operator==). |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| testing::NotNull(), true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_a, empty_filter, context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| // function_a should be registered, and should be returned when we get the |
| // listeners. |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_a)); |
| |
| // It should also be registered in the event filter. |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| |
| // Since function_a has no filter, associating a specific url should still |
| // return function_a. |
| EventFilteringInfo filtering_info_match; |
| filtering_info_match.url = GURL("http://example.com/foo"); |
| EXPECT_THAT(listeners.GetListeners(&filtering_info_match, context), |
| testing::UnorderedElementsAre(function_a)); |
| |
| // Trying to add function_a again should have no effect. |
| EXPECT_FALSE( |
| listeners.AddListener(function_a, empty_filter, context, &error)); |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| |
| // function_b should not yet be registered, and trying to remove it should |
| // have no effect. |
| EXPECT_FALSE(listeners.HasListener(function_b)); |
| listeners.RemoveListener(function_b, context); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_a)); |
| |
| // Register function_b with a filter for pathContains: 'foo'. Unlike |
| // unfiltered listeners, this *should* trigger the callback, since there is |
| // no other listener registered with this same filter. |
| v8::Local<v8::Object> path_filter; |
| { |
| v8::Local<v8::Value> val = |
| V8ValueFromScriptSource(context, "({url: [{pathContains: 'foo'}]})"); |
| ASSERT_TRUE(val->IsObject()); |
| path_filter = val.As<v8::Object>(); |
| } |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| testing::NotNull(), true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_b, path_filter, context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| // function_b should be present. |
| EXPECT_TRUE(listeners.HasListener(function_b)); |
| EXPECT_EQ(2u, listeners.GetNumListeners()); |
| EXPECT_EQ(2, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| |
| // function_b should ignore calls that don't specify an url, since they, by |
| // definition, don't match. |
| EXPECT_THAT(listeners.GetListeners(nullptr, context), |
| testing::UnorderedElementsAre(function_a)); |
| // function_b should be included for matching urls... |
| EXPECT_THAT(listeners.GetListeners(&filtering_info_match, context), |
| testing::UnorderedElementsAre(function_a, function_b)); |
| // ... but not urls that don't match. |
| EventFilteringInfo filtering_info_no_match; |
| filtering_info_no_match.url = GURL("http://example.com/bar"); |
| EXPECT_THAT(listeners.GetListeners(&filtering_info_no_match, context), |
| testing::UnorderedElementsAre(function_a)); |
| |
| // Remove function_a. Since filtered listeners notify whenever there's a |
| // change in listeners registered with a specific filter, this should trigger |
| // the callback. |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| testing::NotNull(), true, context)); |
| listeners.RemoveListener(function_a, context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_FALSE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| // function_b should be the only listener remaining, so we shouldn't find |
| // any listeners for events without matching filters. |
| EXPECT_TRUE(listeners.GetListeners(nullptr, context).empty()); |
| EXPECT_THAT(listeners.GetListeners(&filtering_info_match, context), |
| testing::UnorderedElementsAre(function_b)); |
| EXPECT_TRUE( |
| listeners.GetListeners(&filtering_info_no_match, context).empty()); |
| |
| // Remove function_b. No listeners should remain. |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| testing::NotNull(), true, context)); |
| listeners.RemoveListener(function_b, context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_FALSE(listeners.HasListener(function_b)); |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| EXPECT_TRUE(listeners.GetListeners(nullptr, context).empty()); |
| EXPECT_TRUE(listeners.GetListeners(&filtering_info_match, context).empty()); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| } |
| |
| // Tests that adding multiple listeners with the same filter doesn't trigger |
| // the update callback. |
| TEST_F(APIEventListenersTest, |
| UnfilteredListenersWithSameFilterDontTriggerUpdate) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| EventFilter event_filter; |
| FilteredEventListeners listeners( |
| handler.Get(), kEvent, binding::kNoListenerMax, true, &event_filter); |
| |
| auto get_filter = [context]() { |
| return V8ValueFromScriptSource(context, "({url: [{pathContains: 'foo'}]})") |
| .As<v8::Object>(); |
| }; |
| |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| |
| std::string error; |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| testing::NotNull(), true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_a, get_filter(), context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| v8::Local<v8::Function> function_c = FunctionFromString(context, kFunction); |
| EXPECT_TRUE(listeners.AddListener(function_b, get_filter(), context, &error)); |
| EXPECT_TRUE(listeners.AddListener(function_c, get_filter(), context, &error)); |
| EXPECT_EQ(3u, listeners.GetNumListeners()); |
| EXPECT_EQ(3, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| |
| EventFilteringInfo filtering_info_match; |
| filtering_info_match.url = GURL("http://example.com/foo"); |
| EXPECT_THAT( |
| listeners.GetListeners(&filtering_info_match, context), |
| testing::UnorderedElementsAre(function_a, function_b, function_c)); |
| |
| listeners.RemoveListener(function_c, context); |
| listeners.RemoveListener(function_b, context); |
| |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| testing::NotNull(), true, context)); |
| listeners.RemoveListener(function_a, context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| } |
| |
| // Tests that trying to add a listener with an invalid filter fails. |
| TEST_F(APIEventListenersTest, UnfilteredListenersError) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| EventFilter event_filter; |
| FilteredEventListeners listeners(base::Bind(&DoNothingOnUpdate), kEvent, |
| binding::kNoListenerMax, true, |
| &event_filter); |
| |
| v8::Local<v8::Object> invalid_filter = |
| V8ValueFromScriptSource(context, "({url: 'some string'})") |
| .As<v8::Object>(); |
| v8::Local<v8::Function> function = FunctionFromString(context, kFunction); |
| std::string error; |
| EXPECT_FALSE( |
| listeners.AddListener(function, invalid_filter, context, &error)); |
| EXPECT_FALSE(error.empty()); |
| } |
| |
| // Tests that adding listeners for multiple different events is correctly |
| // recorded in the EventFilter. |
| TEST_F(APIEventListenersTest, MultipleUnfilteredListenerEvents) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| const char kAlpha[] = "alpha"; |
| const char kBeta[] = "beta"; |
| |
| EventFilter event_filter; |
| FilteredEventListeners listeners_a(base::Bind(&DoNothingOnUpdate), kAlpha, |
| binding::kNoListenerMax, true, |
| &event_filter); |
| FilteredEventListeners listeners_b(base::Bind(&DoNothingOnUpdate), kBeta, |
| binding::kNoListenerMax, true, |
| &event_filter); |
| |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kAlpha)); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta)); |
| |
| std::string error; |
| v8::Local<v8::Object> filter; |
| |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| EXPECT_TRUE(listeners_a.AddListener(function_a, filter, context, &error)); |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kAlpha)); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta)); |
| |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| EXPECT_TRUE(listeners_b.AddListener(function_b, filter, context, &error)); |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kAlpha)); |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kBeta)); |
| |
| listeners_b.RemoveListener(function_b, context); |
| EXPECT_EQ(1, event_filter.GetMatcherCountForEventForTesting(kAlpha)); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta)); |
| |
| listeners_a.RemoveListener(function_a, context); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kAlpha)); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kBeta)); |
| } |
| |
| // Tests the invalidation of filtered listeners. |
| TEST_F(APIEventListenersTest, FilteredListenersInvalidation) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| EventFilter event_filter; |
| FilteredEventListeners listeners( |
| handler.Get(), kEvent, binding::kNoListenerMax, true, &event_filter); |
| listeners.Invalidate(context); |
| |
| v8::Local<v8::Object> empty_filter; |
| v8::Local<v8::Object> filter = |
| V8ValueFromScriptSource(context, "({url: [{pathContains: 'foo'}]})") |
| .As<v8::Object>(); |
| std::string error; |
| |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| v8::Local<v8::Function> function_c = FunctionFromString(context, kFunction); |
| |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| testing::NotNull(), true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_a, empty_filter, context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| testing::NotNull(), true, context)); |
| EXPECT_TRUE(listeners.AddListener(function_b, filter, context, &error)); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| EXPECT_TRUE(listeners.AddListener(function_c, filter, context, &error)); |
| |
| // Since two listener filters are present in the list, we should be notified |
| // of each going away when we invalidate the context. |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| testing::NotNull(), false, context)) |
| .Times(2); |
| listeners.Invalidate(context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| EXPECT_EQ(0, event_filter.GetMatcherCountForEventForTesting(kEvent)); |
| } |
| |
| TEST_F(APIEventListenersTest, FilteredListenersMaxListenersTest) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| EventFilter event_filter; |
| FilteredEventListeners listeners(base::Bind(&DoNothingOnUpdate), kEvent, 1, |
| true, &event_filter); |
| |
| v8::Local<v8::Function> function_a = FunctionFromString(context, kFunction); |
| EXPECT_EQ(0u, listeners.GetNumListeners()); |
| |
| std::string error; |
| v8::Local<v8::Object> filter; |
| EXPECT_TRUE(listeners.AddListener(function_a, filter, context, &error)); |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| |
| v8::Local<v8::Function> function_b = FunctionFromString(context, kFunction); |
| EXPECT_FALSE(listeners.AddListener(function_b, filter, context, &error)); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_FALSE(listeners.HasListener(function_b)); |
| EXPECT_TRUE(listeners.HasListener(function_a)); |
| EXPECT_EQ(1u, listeners.GetNumListeners()); |
| } |
| |
| TEST_F(APIEventListenersTest, FilteredListenersLazyListeners) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| MockEventChangeHandler handler; |
| EventFilter event_filter; |
| FilteredEventListeners listeners( |
| handler.Get(), kEvent, binding::kNoListenerMax, false, &event_filter); |
| |
| v8::Local<v8::Function> listener = FunctionFromString(context, kFunction); |
| std::string error; |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::HAS_LISTENERS, |
| testing::NotNull(), false, context)); |
| listeners.AddListener(listener, v8::Local<v8::Object>(), context, &error); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| |
| EXPECT_CALL(handler, Run(binding::EventListenersChanged::NO_LISTENERS, |
| testing::NotNull(), false, context)); |
| listeners.RemoveListener(listener, context); |
| ::testing::Mock::VerifyAndClearExpectations(&handler); |
| } |
| |
| } // namespace extensions |