1 // Copyright 2015 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.
7 #include "base/memory/linked_ptr.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "chrome/browser/extensions/api/mdns/mdns_api.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_service_test_base.h"
12 #include "chrome/browser/extensions/test_extension_system.h"
13 #include "chrome/common/extensions/api/mdns.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/test/mock_render_process_host.h"
16 #include "extensions/browser/event_listener_map.h"
17 #include "extensions/browser/event_router_factory.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/common/manifest_constants.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
25 using testing::Return
;
26 using testing::ReturnRef
;
28 namespace extensions
{
31 const char kExtId
[] = "mbflcebpggnecokmikipoihdbecnjfoj";
32 const char kService1
[] = "service1";
33 const char kService2
[] = "service2";
35 // Registers a new EventListener for |service_types| in |listener_list|.
36 void AddEventListener(
37 const std::string
& extension_id
,
38 const std::string
& service_type
,
39 extensions::EventListenerMap::ListenerList
* listener_list
) {
40 scoped_ptr
<base::DictionaryValue
> filter(new base::DictionaryValue
);
41 filter
->SetString(kEventFilterServiceTypeKey
, service_type
);
42 listener_list
->push_back(make_linked_ptr(
43 EventListener::ForExtension(kEventFilterServiceTypeKey
, extension_id
,
44 nullptr, filter
.Pass()).release()));
47 class NullDelegate
: public EventListenerMap::Delegate
{
49 void OnListenerAdded(const EventListener
* listener
) override
{}
50 void OnListenerRemoved(const EventListener
* listener
) override
{}
53 // Testing subclass of MDnsAPI which replaces calls to core browser components
54 // that we don't want to have to instantiate or mock for these tests.
55 class MockedMDnsAPI
: public MDnsAPI
{
57 explicit MockedMDnsAPI(content::BrowserContext
* context
) : MDnsAPI(context
) {}
60 MOCK_CONST_METHOD2(IsMDnsAllowed
,
61 bool(const std::string
& extension_id
,
62 const std::string
& service_type
));
64 MOCK_METHOD0(GetEventListeners
,
65 const extensions::EventListenerMap::ListenerList
&());
68 scoped_ptr
<KeyedService
> MockedMDnsAPITestingFactoryFunction(
69 content::BrowserContext
* context
) {
70 return make_scoped_ptr(new MockedMDnsAPI(context
));
73 scoped_ptr
<KeyedService
> MDnsAPITestingFactoryFunction(
74 content::BrowserContext
* context
) {
75 return make_scoped_ptr(new MDnsAPI(context
));
78 scoped_ptr
<KeyedService
> BuildEventRouter(content::BrowserContext
* context
) {
79 return make_scoped_ptr(
80 new extensions::EventRouter(context
, ExtensionPrefs::Get(context
)));
83 // For ExtensionService interface when it requires a path that is not used.
84 base::FilePath
bogus_file_pathname(const std::string
& name
) {
85 return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"))
89 class MockDnsSdRegistry
: public DnsSdRegistry
{
91 explicit MockDnsSdRegistry(extensions::MDnsAPI
* api
) : api_(api
) {}
92 virtual ~MockDnsSdRegistry() {}
94 MOCK_METHOD1(AddObserver
, void(DnsSdObserver
* observer
));
95 MOCK_METHOD1(RemoveObserver
, void(DnsSdObserver
* observer
));
96 MOCK_METHOD1(RegisterDnsSdListener
, void(const std::string
& service_type
));
97 MOCK_METHOD1(UnregisterDnsSdListener
, void(const std::string
& service_type
));
98 MOCK_METHOD1(Publish
, void(const std::string
&));
99 MOCK_METHOD0(ForceDiscovery
, void(void));
101 void DispatchMDnsEvent(const std::string
& service_type
,
102 const DnsSdServiceList
& services
) {
103 api_
->OnDnsSdEvent(service_type
, services
);
107 extensions::DnsSdRegistry::DnsSdObserver
* api_
;
110 class MockEventRouter
: public EventRouter
{
112 explicit MockEventRouter(content::BrowserContext
* browser_context
,
113 ExtensionPrefs
* extension_prefs
)
114 : EventRouter(browser_context
, extension_prefs
) {}
115 virtual ~MockEventRouter() {}
117 virtual void BroadcastEvent(scoped_ptr
<Event
> event
) {
118 BroadcastEventPtr(event
.get());
120 MOCK_METHOD1(BroadcastEventPtr
, void(Event
* event
));
123 scoped_ptr
<KeyedService
> MockEventRouterFactoryFunction(
124 content::BrowserContext
* context
) {
125 return make_scoped_ptr(
126 new MockEventRouter(context
, ExtensionPrefs::Get(context
)));
129 class EventServiceListSizeMatcher
130 : public testing::MatcherInterface
<const Event
&> {
132 explicit EventServiceListSizeMatcher(size_t expected_size
)
133 : expected_size_(expected_size
) {}
135 virtual bool MatchAndExplain(const Event
& e
,
136 testing::MatchResultListener
* listener
) const {
138 *listener
<< "event.event_arg is null when it shouldn't be";
141 if (e
.event_args
->GetSize() != 1) {
142 *listener
<< "event.event_arg.GetSize() should be 1 but is "
143 << e
.event_args
->GetSize();
146 const base::ListValue
* services
= nullptr;
148 const base::Value
* out
;
149 e
.event_args
->Get(0, &out
);
150 services
= static_cast<const base::ListValue
*>(out
);
153 *listener
<< "event's service list argument is not a ListValue";
156 *listener
<< "number of services is " << services
->GetSize();
157 return static_cast<testing::Matcher
<size_t>>(testing::Eq(expected_size_
))
158 .MatchAndExplain(services
->GetSize(), listener
);
161 virtual void DescribeTo(::std::ostream
* os
) const {
162 *os
<< "is an onServiceList event where the number of services is "
166 virtual void DescribeNegationTo(::std::ostream
* os
) const {
167 *os
<< "isn't an onServiceList event where the number of services is "
172 size_t expected_size_
;
175 inline testing::Matcher
<const Event
&> EventServiceListSize(
176 size_t expected_size
) {
177 return testing::MakeMatcher(new EventServiceListSizeMatcher(expected_size
));
182 class MDnsAPITest
: public extensions::ExtensionServiceTestBase
{
184 void SetUp() override
{
185 extensions::ExtensionServiceTestBase::SetUp();
187 // Set up browser_context().
188 InitializeEmptyExtensionService();
190 // A custom TestingFactoryFunction is required for an MDnsAPI to actually be
192 MDnsAPI::GetFactoryInstance()->SetTestingFactory(browser_context(),
195 EventRouterFactory::GetInstance()->SetTestingFactory(browser_context(),
198 // Do some sanity checking
199 ASSERT_TRUE(MDnsAPI::Get(browser_context())); // constructs MDnsAPI
200 ASSERT_TRUE(EventRouter::Get(browser_context())); // constructs EventRouter
202 registry_
= new MockDnsSdRegistry(MDnsAPI::Get(browser_context()));
203 EXPECT_CALL(*dns_sd_registry(),
204 AddObserver(MDnsAPI::Get(browser_context())))
206 MDnsAPI::Get(browser_context())->SetDnsSdRegistryForTesting(
207 scoped_ptr
<DnsSdRegistry
>(registry_
));
209 render_process_host_
.reset(
210 new content::MockRenderProcessHost(browser_context()));
213 // Returns the mDNS API factory function (mock vs. real) to use for the test.
214 virtual BrowserContextKeyedServiceFactory::TestingFactoryFunction
216 return MDnsAPITestingFactoryFunction
;
219 void TearDown() override
{
220 EXPECT_CALL(*dns_sd_registry(),
221 RemoveObserver(MDnsAPI::Get(browser_context())))
223 render_process_host_
.reset();
224 extensions::ExtensionServiceTestBase::TearDown();
227 virtual MockDnsSdRegistry
* dns_sd_registry() {
231 // Constructs an extension according to the parameters that matter most to
232 // MDnsAPI the local unit tests.
233 const scoped_refptr
<extensions::Extension
> CreateExtension(
235 bool is_platform_app
,
236 std::string extension_id
) {
237 base::DictionaryValue manifest
;
238 manifest
.SetString(extensions::manifest_keys::kVersion
, "1.0.0.0");
239 manifest
.SetString(extensions::manifest_keys::kName
, name
);
240 if (is_platform_app
) {
241 // Setting app.background.page = "background.html" is sufficient to make
242 // the extension type TYPE_PLATFORM_APP.
243 manifest
.Set(extensions::manifest_keys::kPlatformAppBackgroundPage
,
244 new base::StringValue("background.html"));
248 return extensions::Extension::Create(
249 bogus_file_pathname(name
),
250 extensions::Manifest::INVALID_LOCATION
,
257 content::RenderProcessHost
* render_process_host() const {
258 return render_process_host_
.get();
262 // The registry is owned by MDnsAPI, but MDnsAPI does not have an accessor
263 // for it, so use a private member.
264 MockDnsSdRegistry
* registry_
;
266 scoped_ptr
<content::RenderProcessHost
> render_process_host_
;
269 class MDnsAPIMaxServicesTest
: public MDnsAPITest
{
271 MockEventRouter
* event_router() {
272 return static_cast<MockEventRouter
*>(EventRouter::Get(browser_context()));
276 class MDnsAPIDiscoveryTest
: public MDnsAPITest
{
278 BrowserContextKeyedServiceFactory::TestingFactoryFunction
GetMDnsFactory()
280 return MockedMDnsAPITestingFactoryFunction
;
283 void SetUp() override
{
284 MDnsAPITest::SetUp();
285 mdns_api_
= static_cast<MockedMDnsAPI
*>(MDnsAPI::Get(browser_context()));
286 EXPECT_CALL(*mdns_api_
, IsMDnsAllowed(_
, _
)).WillRepeatedly(Return(true));
290 MockedMDnsAPI
* mdns_api_
;
293 TEST_F(MDnsAPIDiscoveryTest
, ServiceListenersAddedAndRemoved
) {
294 EventRouterFactory::GetInstance()->SetTestingFactory(
295 browser_context(), &MockEventRouterFactoryFunction
);
296 extensions::EventListenerMap::ListenerList listeners
;
298 extensions::EventListenerInfo
listener_info(
299 kEventFilterServiceTypeKey
, kExtId
, GURL(), browser_context());
301 EXPECT_CALL(*mdns_api_
, GetEventListeners())
302 .WillRepeatedly(ReturnRef(listeners
));
304 // Listener #1 added with kService1.
305 AddEventListener(kExtId
, kService1
, &listeners
);
306 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener(kService1
));
307 mdns_api_
->OnListenerAdded(listener_info
);
309 // Listener #2 added with kService2.
310 AddEventListener(kExtId
, kService2
, &listeners
);
311 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener(kService2
));
312 mdns_api_
->OnListenerAdded(listener_info
);
315 mdns_api_
->OnListenerAdded(listener_info
);
317 // Listener #3 added with kService2. Should trigger a refresh of kService2.
318 AddEventListener(kExtId
, kService2
, &listeners
);
319 EXPECT_CALL(*dns_sd_registry(), Publish(kService2
));
320 mdns_api_
->OnListenerAdded(listener_info
);
322 // Listener #3 removed, should be a no-op since there is still a live
323 // listener for kService2.
324 listeners
.pop_back();
325 mdns_api_
->OnListenerRemoved(listener_info
);
327 // Listener #2 removed, should unregister kService2.
328 listeners
.pop_back();
329 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener(kService2
));
330 mdns_api_
->OnListenerRemoved(listener_info
);
332 // Listener #1 removed, should unregister kService1.
333 listeners
.pop_back();
334 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener(kService1
));
335 mdns_api_
->OnListenerRemoved(listener_info
);
338 mdns_api_
->OnListenerAdded(listener_info
);
340 // Listener #4 added with kService1.
341 AddEventListener(kExtId
, kService1
, &listeners
);
342 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener(kService1
));
343 mdns_api_
->OnListenerAdded(listener_info
);
346 TEST_F(MDnsAPIMaxServicesTest
, OnServiceListDoesNotExceedLimit
) {
347 EventRouterFactory::GetInstance()->SetTestingFactory(
348 browser_context(), &MockEventRouterFactoryFunction
);
350 // This check should change when the [value=64] changes in the IDL file.
351 EXPECT_EQ(64, api::mdns::MAX_SERVICE_INSTANCES_PER_EVENT
);
353 // Dispatch an mDNS event with more service instances than the max, and ensure
354 // that the list is truncated by inspecting the argument to MockEventRouter's
355 // BroadcastEvent method.
356 DnsSdRegistry::DnsSdServiceList services
;
357 for (int i
= 0; i
< api::mdns::MAX_SERVICE_INSTANCES_PER_EVENT
+ 10; ++i
) {
358 services
.push_back(DnsSdService());
360 EXPECT_CALL(*event_router(), BroadcastEventPtr(testing::Pointee(
361 EventServiceListSize(size_t(64))))).Times(1);
362 dns_sd_registry()->DispatchMDnsEvent("_testing._tcp.local", services
);
365 TEST_F(MDnsAPITest
, ExtensionRespectsWhitelist
) {
366 scoped_refptr
<extensions::Extension
> extension
=
367 CreateExtension("Dinosaur networker", false, kExtId
);
368 ExtensionRegistry::Get(browser_context())->AddEnabled(extension
);
369 ASSERT_EQ(Manifest::TYPE_EXTENSION
, extension
.get()->GetType());
371 // There is a whitelist of mdns service types extensions may access, which
372 // includes "_testing._tcp.local" and exludes "_trex._tcp.local"
374 base::DictionaryValue filter
;
375 filter
.SetString(kEventFilterServiceTypeKey
, "_trex._tcp.local");
377 ASSERT_TRUE(dns_sd_registry());
378 // Test that the extension is able to listen to a non-whitelisted service
379 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"))
381 EventRouter::Get(browser_context())
382 ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName
,
383 render_process_host(), kExtId
, filter
,
386 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"))
388 EventRouter::Get(browser_context())
389 ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName
,
390 render_process_host(), kExtId
, filter
,
394 base::DictionaryValue filter
;
395 filter
.SetString(kEventFilterServiceTypeKey
, "_testing._tcp.local");
397 ASSERT_TRUE(dns_sd_registry());
398 // Test that the extension is able to listen to a whitelisted service
399 EXPECT_CALL(*dns_sd_registry(),
400 RegisterDnsSdListener("_testing._tcp.local"));
401 EventRouter::Get(browser_context())
402 ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName
,
403 render_process_host(), kExtId
, filter
,
406 EXPECT_CALL(*dns_sd_registry(),
407 UnregisterDnsSdListener("_testing._tcp.local"));
408 EventRouter::Get(browser_context())
409 ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName
,
410 render_process_host(), kExtId
, filter
,
415 TEST_F(MDnsAPITest
, PlatformAppsNotSubjectToWhitelist
) {
416 scoped_refptr
<extensions::Extension
> extension
=
417 CreateExtension("Dinosaur networker", true, kExtId
);
418 ExtensionRegistry::Get(browser_context())->AddEnabled(extension
);
419 ASSERT_TRUE(extension
.get()->is_platform_app());
421 base::DictionaryValue filter
;
422 filter
.SetString(kEventFilterServiceTypeKey
, "_trex._tcp.local");
424 ASSERT_TRUE(dns_sd_registry());
425 // Test that the extension is able to listen to a non-whitelisted service
426 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"));
427 EventRouter::Get(browser_context())
428 ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName
,
429 render_process_host(), kExtId
, filter
, false);
431 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"));
432 EventRouter::Get(browser_context())
433 ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName
,
434 render_process_host(), kExtId
, filter
,
438 } // namespace extensions