diff options
author | justinlin@chromium.org <justinlin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-06 07:21:08 +0000 |
---|---|---|
committer | justinlin@chromium.org <justinlin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-06 07:21:08 +0000 |
commit | 5556c5d23047abd3a19705103a1ec14b1026d92e (patch) | |
tree | 94349edac8fb53dd7aadacf74122f16cc9eb0a47 | |
parent | 9bbaad67d5fe08d314c9e9e3cdd88bb6ddabcd1c (diff) | |
download | chromium_src-5556c5d23047abd3a19705103a1ec14b1026d92e.zip chromium_src-5556c5d23047abd3a19705103a1ec14b1026d92e.tar.gz chromium_src-5556c5d23047abd3a19705103a1ec14b1026d92e.tar.bz2 |
Initial chrome.mdns API.
Currently just implements the API layer which allows adding a listener and defines a manifest key to specify mdns service types to watch.
BUG=280900
TBR=erg
Review URL: https://chromiumcodereview.appspot.com/23437015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221619 0039d316-1c4b-4281-b951-d872f2087c98
27 files changed, 591 insertions, 7 deletions
diff --git a/chrome/browser/extensions/api/mdns/OWNERS b/chrome/browser/extensions/api/mdns/OWNERS new file mode 100644 index 0000000..cb84d70 --- /dev/null +++ b/chrome/browser/extensions/api/mdns/OWNERS @@ -0,0 +1,2 @@ +justinlin@chromium.org +mfoltz@chromium.org diff --git a/chrome/browser/extensions/api/mdns/dns_sd_registry.cc b/chrome/browser/extensions/api/mdns/dns_sd_registry.cc new file mode 100644 index 0000000..ca9980b --- /dev/null +++ b/chrome/browser/extensions/api/mdns/dns_sd_registry.cc @@ -0,0 +1,28 @@ +// Copyright 2013 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 "chrome/browser/extensions/api/mdns/dns_sd_registry.h" + +namespace extensions { + +DnsSdRegistry::DnsSdRegistry() {} +DnsSdRegistry::~DnsSdRegistry() {} + +void DnsSdRegistry::AddObserver(DnsSdObserver* observer) { + observers_.AddObserver(observer); +} + +void DnsSdRegistry::RemoveObserver(DnsSdObserver* observer) { + observers_.RemoveObserver(observer); +} + +void DnsSdRegistry::RegisterDnsSdListener(std::string service_type) { + // TODO(mfoltz): Start DNS-SD service. +} + +void DnsSdRegistry::UnregisterDnsSdListener(std::string service_type) { + // TODO(mfoltz): Stop DNS-SD service. +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/mdns/dns_sd_registry.h b/chrome/browser/extensions/api/mdns/dns_sd_registry.h new file mode 100644 index 0000000..4deb759 --- /dev/null +++ b/chrome/browser/extensions/api/mdns/dns_sd_registry.h @@ -0,0 +1,47 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_REGISTRY_H_ +#define CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_REGISTRY_H_ + +#include <string> +#include <vector> + +#include "base/observer_list.h" + +namespace extensions { + +// Network Service Discovery registry class for keeping track of discovered +// network services. +class DnsSdRegistry { + public: + typedef std::vector<std::string> DnsSdServiceList; + + class DnsSdObserver { + public: + virtual void OnDnsSdEvent(const std::string& service_type, + const DnsSdServiceList& services) = 0; + + protected: + virtual ~DnsSdObserver() {} + }; + + explicit DnsSdRegistry(); + virtual ~DnsSdRegistry(); + + // Observer registration for parties interested in discovery events. + virtual void AddObserver(DnsSdObserver* observer); + virtual void RemoveObserver(DnsSdObserver* observer); + + // DNS-SD-related discovery functionality. + virtual void RegisterDnsSdListener(std::string service_type); + virtual void UnregisterDnsSdListener(std::string service_type); + + protected: + ObserverList<DnsSdObserver> observers_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_REGISTRY_H_ diff --git a/chrome/browser/extensions/api/mdns/mdns_api.cc b/chrome/browser/extensions/api/mdns/mdns_api.cc new file mode 100644 index 0000000..1f74d3a --- /dev/null +++ b/chrome/browser/extensions/api/mdns/mdns_api.cc @@ -0,0 +1,154 @@ +// Copyright 2013 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 "chrome/browser/extensions/api/mdns/mdns_api.h" + +#include <vector> + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/common/extensions/api/mdns.h" + +namespace extensions { + +namespace mdns = api::mdns; + +namespace { + +// Whitelisted mDNS service types. +const char kCastServiceType[] = "_googlecast._tcp.local"; +const char kTestServiceType[] = "_testing._tcp.local"; + +bool IsServiceTypeWhitelisted(const std::string& service_type) { + return service_type == kCastServiceType || + service_type == kTestServiceType; +} + +} // namespace + +MDnsAPI::MDnsAPI(Profile* profile) : profile_(profile) { + DCHECK(profile_); + ExtensionSystem::Get(profile)->event_router()->RegisterObserver( + this, mdns::OnServiceList::kEventName); +} + +MDnsAPI::~MDnsAPI() { + if (dns_sd_registry_.get()) { + dns_sd_registry_->RemoveObserver(this); + } +} + +// static +MDnsAPI* MDnsAPI::Get(Profile* profile) { + return ProfileKeyedAPIFactory<MDnsAPI>::GetForProfile(profile); +} + +static base::LazyInstance<ProfileKeyedAPIFactory<MDnsAPI> > g_factory = + LAZY_INSTANCE_INITIALIZER; + +// static +ProfileKeyedAPIFactory<MDnsAPI>* MDnsAPI::GetFactoryInstance() { + return &g_factory.Get(); +} + +void MDnsAPI::SetDnsSdRegistryForTesting( + scoped_ptr<DnsSdRegistry> dns_sd_registry) { + dns_sd_registry_ = dns_sd_registry.Pass(); +} + +DnsSdRegistry* MDnsAPI::dns_sd_registry() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!dns_sd_registry_.get()) { + dns_sd_registry_.reset(new extensions::DnsSdRegistry()); + dns_sd_registry_->AddObserver(this); + } + return dns_sd_registry_.get(); +} + +void MDnsAPI::OnListenerAdded(const EventListenerInfo& details) { + DCHECK(thread_checker_.CalledOnValidThread()); + UpdateMDnsListeners(details); +} + +void MDnsAPI::OnListenerRemoved(const EventListenerInfo& details) { + DCHECK(thread_checker_.CalledOnValidThread()); + UpdateMDnsListeners(details); +} + +void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) { + std::set<std::string> new_service_types; + + // Check all listeners for service type filers. + const EventListenerMap::ListenerList& listeners = + extensions::ExtensionSystem::Get(profile_)->event_router()-> + listeners().GetEventListenersByName(details.event_name); + for (EventListenerMap::ListenerList::const_iterator it = listeners.begin(); + it != listeners.end(); ++it) { + base::DictionaryValue* filter = ((*it)->filter.get()); + for (base::DictionaryValue::Iterator iter(*filter); + !iter.IsAtEnd(); iter.Advance()) { + } + + std::string filter_value; + filter->GetStringASCII(kEventFilterServiceTypeKey, &filter_value); + if (filter_value.empty()) + continue; + + new_service_types.insert(filter_value); + } + + // Find all the added and removed service types since last update. + std::set<std::string> added_service_types = + base::STLSetDifference<std::set<std::string> >( + service_types_, new_service_types); + std::set<std::string> removed_service_types = + base::STLSetDifference<std::set<std::string> >( + new_service_types, service_types_); + + // Update the registry. + DnsSdRegistry* registry = dns_sd_registry(); + for (std::set<std::string>::iterator it = added_service_types.begin(); + it != added_service_types.end(); ++it) { + if (IsServiceTypeWhitelisted(*it)) + registry->RegisterDnsSdListener(*it); + } + for (std::set<std::string>::iterator it = removed_service_types.begin(); + it != removed_service_types.end(); ++it) { + if (IsServiceTypeWhitelisted(*it)) + registry->UnregisterDnsSdListener(*it); + } + + service_types_ = new_service_types; +} + +void MDnsAPI::OnDnsSdEvent(const std::string& service_type, + const DnsSdRegistry::DnsSdServiceList& services) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (services.size() == 0) + return; + + std::vector<linked_ptr<mdns::MDnsService> > args; + for (DnsSdRegistry::DnsSdServiceList::const_iterator it = services.begin(); + it != services.end(); ++it) { + linked_ptr<mdns::MDnsService> mdns_service = + make_linked_ptr(new mdns::MDnsService); + // TODO(justinlin, mfoltz): Copy data from service list. + mdns_service->service_name = service_type; + args.push_back(mdns_service); + } + + scoped_ptr<base::ListValue> results = mdns::OnServiceList::Create(args); + scoped_ptr<Event> event( + new Event(mdns::OnServiceList::kEventName, results.Pass())); + event->restrict_to_profile = profile_; + event->filter_info.SetServiceType(service_type); + + // TODO(justinlin): To avoid having listeners without filters getting all + // events, modify API to have this event require filters. + extensions::ExtensionSystem::Get(profile_)->event_router()-> + BroadcastEvent(event.Pass()); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/mdns/mdns_api.h b/chrome/browser/extensions/api/mdns/mdns_api.h new file mode 100644 index 0000000..851e0b9 --- /dev/null +++ b/chrome/browser/extensions/api/mdns/mdns_api.h @@ -0,0 +1,80 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_MDNS_MDNS_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_MDNS_MDNS_API_H_ + +#include <set> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "chrome/browser/extensions/api/mdns/dns_sd_registry.h" +#include "chrome/browser/extensions/api/profile_keyed_api_factory.h" +#include "chrome/browser/extensions/event_router.h" + +namespace extensions { + +class DnsSdRegistry; + +// MDnsAPI is instantiated with the profile and will listen for extensions that +// register listeners for the chrome.mdns extension API. It will use a registry +// class to start the mDNS listener process (if necessary) and observe new +// service events to dispatch them to registered extensions. +class MDnsAPI : public ProfileKeyedAPI, + public EventRouter::Observer, + public DnsSdRegistry::DnsSdObserver { + public: + explicit MDnsAPI(Profile* profile); + virtual ~MDnsAPI(); + + static MDnsAPI* Get(Profile* profile); + + // ProfileKeyedAPI implementation. + static ProfileKeyedAPIFactory<MDnsAPI>* GetFactoryInstance(); + + // Used to mock out the DnsSdRegistry for testing. + void SetDnsSdRegistryForTesting(scoped_ptr<DnsSdRegistry> registry); + + protected: + // Retrieve an instance of the registry. Lazily created when needed. + virtual DnsSdRegistry* dns_sd_registry(); + + private: + friend class ProfileKeyedAPIFactory<MDnsAPI>; + + // EventRouter::Observer: + virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE; + virtual void OnListenerRemoved(const EventListenerInfo& details) OVERRIDE; + + // DnsSdRegistry::Observer + virtual void OnDnsSdEvent( + const std::string& service_type, + const DnsSdRegistry::DnsSdServiceList& services) OVERRIDE; + + // ProfileKeyedAPI implementation. + static const char* service_name() { + return "MDnsAPI"; + } + + static const bool kServiceIsCreatedWithBrowserContext = true; + static const bool kServiceIsNULLWhileTesting = true; + + // Update the current list of service types and update the registry. + void UpdateMDnsListeners(const EventListenerInfo& details); + + // Ensure methods are only called on UI thread. + base::ThreadChecker thread_checker_; + Profile* const profile_; + // Lazily created on first access and destroyed with this API class. + scoped_ptr<DnsSdRegistry> dns_sd_registry_; + // Current set of service types registered with the registry. + std::set<std::string> service_types_; + + DISALLOW_COPY_AND_ASSIGN(MDnsAPI); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_MDNS_MDNS_API_H_ diff --git a/chrome/browser/extensions/api/mdns/mdns_apitest.cc b/chrome/browser/extensions/api/mdns/mdns_apitest.cc new file mode 100644 index 0000000..46349dc --- /dev/null +++ b/chrome/browser/extensions/api/mdns/mdns_apitest.cc @@ -0,0 +1,120 @@ +// Copyright 2013 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 "base/command_line.h" +#include "chrome/browser/extensions/api/mdns/mdns_api.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/api/mdns.h" +#include "testing/gmock/include/gmock/gmock.h" + +using extensions::DnsSdRegistry; +using ::testing::A; +using ::testing::_; + +namespace api = extensions::api; + +namespace { + +class MockDnsSdRegistry : public DnsSdRegistry { + public: + explicit MockDnsSdRegistry(extensions::MDnsAPI* api) : api_(api) {} + virtual ~MockDnsSdRegistry() {} + + MOCK_METHOD1(AddObserver, void(DnsSdObserver* observer)); + MOCK_METHOD1(RemoveObserver, void(DnsSdObserver* observer)); + MOCK_METHOD1(RegisterDnsSdListener, void(std::string service_type)); + MOCK_METHOD1(UnregisterDnsSdListener, void(std::string service_type)); + + void DispatchMDnsEvent(const std::string& service_type, + const DnsSdServiceList& services) { + api_->OnDnsSdEvent(service_type, services); + } + + private: + extensions::DnsSdRegistry::DnsSdObserver* api_; +}; + +class MDnsAPITest : public ExtensionApiTest { + public: + MDnsAPITest() {} + + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + ExtensionApiTest::SetUpCommandLine(command_line); + command_line->AppendSwitchASCII( + switches::kWhitelistedExtensionID, "ddchlicdkolnonkihahngkmmmjnjlkkf"); + } + + void SetUpTestDnsSdRegistry() { + extensions::MDnsAPI* api = extensions::MDnsAPI::Get(profile()); + dns_sd_registry_ = new MockDnsSdRegistry(api); + // Transfers ownership of the registry instance. + api->SetDnsSdRegistryForTesting( + make_scoped_ptr<DnsSdRegistry>(dns_sd_registry_).Pass()); + } + + protected: + MockDnsSdRegistry* dns_sd_registry_; +}; + +} // namespace + +// Test loading extension, registering an MDNS listener and dispatching events. +IN_PROC_BROWSER_TEST_F(MDnsAPITest, RegisterListener) { + const std::string& service_type = "_googlecast._tcp.local"; + SetUpTestDnsSdRegistry(); + + EXPECT_CALL(*dns_sd_registry_, RegisterDnsSdListener(service_type)) + .Times(1); + EXPECT_CALL(*dns_sd_registry_, UnregisterDnsSdListener(service_type)) + .Times(1); + EXPECT_CALL(*dns_sd_registry_, + RemoveObserver(A<extensions::DnsSdRegistry::DnsSdObserver*>())) + .Times(1); + + EXPECT_TRUE(RunExtensionSubtest("mdns/api", "register_listener.html")) + << message_; + + ResultCatcher catcher; + // Dispatch 3 events, one of which should not be sent to the test extension. + std::vector<std::string> services; + services.push_back("test"); + + dns_sd_registry_->DispatchMDnsEvent(service_type, services); + dns_sd_registry_->DispatchMDnsEvent("_uninteresting._tcp.local", services); + dns_sd_registry_->DispatchMDnsEvent(service_type, services); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + +// Test loading extension and registering multiple listeners. +IN_PROC_BROWSER_TEST_F(MDnsAPITest, RegisterMultipleListeners) { + const std::string& service_type = "_googlecast._tcp.local"; + const std::string& test_service_type = "_testing._tcp.local"; + SetUpTestDnsSdRegistry(); + + EXPECT_CALL(*dns_sd_registry_, RegisterDnsSdListener(service_type)) + .Times(1); + EXPECT_CALL(*dns_sd_registry_, UnregisterDnsSdListener(service_type)) + .Times(1); + EXPECT_CALL(*dns_sd_registry_, RegisterDnsSdListener(test_service_type)) + .Times(1); + EXPECT_CALL(*dns_sd_registry_, UnregisterDnsSdListener(test_service_type)) + .Times(1); + EXPECT_CALL(*dns_sd_registry_, + RemoveObserver(A<extensions::DnsSdRegistry::DnsSdObserver*>())) + .Times(1); + + EXPECT_TRUE(RunExtensionSubtest("mdns/api", + "register_multiple_listeners.html")) + << message_; + + ResultCatcher catcher; + std::vector<std::string> services; + services.push_back("test"); + + dns_sd_registry_->DispatchMDnsEvent(service_type, services); + dns_sd_registry_->DispatchMDnsEvent(test_service_type, services); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 4052d82..3e0db36 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc @@ -37,6 +37,7 @@ #include "chrome/browser/extensions/api/input/input.h" #include "chrome/browser/extensions/api/location/location_manager.h" #include "chrome/browser/extensions/api/management/management_api.h" +#include "chrome/browser/extensions/api/mdns/mdns_api.h" #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h" #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" #include "chrome/browser/extensions/api/preference/chrome_direct_setting_api.h" @@ -239,6 +240,7 @@ EnsureBrowserContextKeyedServiceFactoriesBuilt() { #endif extensions::LocationManager::GetFactoryInstance(); extensions::ManagementAPI::GetFactoryInstance(); + extensions::MDnsAPI::GetFactoryInstance(); extensions::MediaGalleriesPrivateAPI::GetFactoryInstance(); #if defined(OS_CHROMEOS) extensions::MediaPlayerAPI::GetFactoryInstance(); diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 777ae89..add7b1d 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -329,6 +329,10 @@ 'browser/extensions/api/messaging/native_message_port.h', 'browser/extensions/api/metrics_private/metrics_private_api.cc', 'browser/extensions/api/metrics_private/metrics_private_api.h', + 'browser/extensions/api/mdns/dns_sd_registry.h', + 'browser/extensions/api/mdns/dns_sd_registry.cc', + 'browser/extensions/api/mdns/mdns_api.h', + 'browser/extensions/api/mdns/mdns_api.cc', 'browser/extensions/api/module/module.cc', 'browser/extensions/api/module/module.h', 'browser/extensions/api/music_manager_private/device_id.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 1d970f1..d0aee2c 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1344,6 +1344,7 @@ 'browser/extensions/api/management/management_api_browsertest.cc', 'browser/extensions/api/management/management_apitest.cc', 'browser/extensions/api/management/management_browsertest.cc', + 'browser/extensions/api/mdns/mdns_apitest.cc', 'browser/extensions/api/media_galleries/media_galleries_apitest.cc', 'browser/extensions/api/media_galleries_private/media_galleries_private_apitest.cc', 'browser/extensions/api/media_galleries_private/media_galleries_watch_apitest.cc', diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json index 1123226..094a8d6 100644 --- a/chrome/common/extensions/api/_api_features.json +++ b/chrome/common/extensions/api/_api_features.json @@ -351,6 +351,10 @@ "dependencies": ["permission:metricsPrivate"], "contexts": ["blessed_extension"] }, + "mdns": { + "dependencies": ["permission:mdns"], + "contexts": ["blessed_extension"] + }, "musicManagerPrivate": { "dependencies": ["permission:musicManagerPrivate"], "contexts": ["blessed_extension"] diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json index 651bb5b..f2bec5f 100644 --- a/chrome/common/extensions/api/_permission_features.json +++ b/chrome/common/extensions/api/_permission_features.json @@ -428,6 +428,18 @@ "eemlkeanncmjljgehlbplemhmdmalhdc" // CCD Release ] }, + "mdns": { + "channel": "stable", + "extension_types": ["extension"], + "whitelist": [ + "enhhojjnijigcajfphajepfemndkmdlo", // Dev + "pkedcjkdefgpdelpbcmbmeomcjbeemfm", // Trusted Tester + "fmfcbgogabcbclcofgocippekhfcmgfj", // Staging + "hfaagokkkhdbgiakmmlclaapfelnkoah", // Canary + "F155646B5D1CA545F7E1E4E20D573DFDD44C2540", // Trusted Tester (public) + "16CA7A47AAE4BE49B1E75A6B960C3875E945B264" // Release + ] + }, "musicManagerPrivate": { "channel": "stable", "extension_types": ["platform_app"], diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp index 211d594..aef722a 100644 --- a/chrome/common/extensions/api/api.gyp +++ b/chrome/common/extensions/api/api.gyp @@ -72,6 +72,7 @@ 'log_private.idl', 'management.json', 'manifest_types.json', + 'mdns.idl', 'media_galleries.idl', 'media_galleries_private.idl', 'media_player_private.json', diff --git a/chrome/common/extensions/api/mdns.idl b/chrome/common/extensions/api/mdns.idl new file mode 100644 index 0000000..b792327 --- /dev/null +++ b/chrome/common/extensions/api/mdns.idl @@ -0,0 +1,34 @@ +// Copyright 2013 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. + +// Use the <code>chrome.mdns</code> API to discover services over mDNS. +// This comprises a subset of the features of the NSD spec: +// http://www.w3.org/TR/discovery-api/ +namespace mdns { + + // Represents a mDNS/DNS-SD service. + dictionary MDnsService { + // The service name of an mDNS advertised service, + // <instance_name>.<service_type>. + DOMString serviceName; + + // The host:port pair of an mDNS advertised service. + DOMString serviceHostPort; + + // The IP address of an mDNS advertised service. + DOMString ipAddress; + + // Metadata for an mDNS advertised service. + DOMString[] serviceData; + }; + + interface Events { + // Event fired to inform clients of the current complete set of known + // available services. Clients should only need to store the list from the + // most recent event. The service types that the extension is interested in + // discovering should be declared as an array in the extensions manifest + // file with the 'mdns_service_types' key. + [supportsFilters=true] static void onServiceList(MDnsService[] services); + }; +}; diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h index 2b02412..ab50889 100644 --- a/chrome/common/extensions/permissions/api_permission.h +++ b/chrome/common/extensions/permissions/api_permission.h @@ -100,6 +100,7 @@ class APIPermission { kMediaGalleriesPrivate, kMediaPlayerPrivate, kMetricsPrivate, + kMDns, kMusicManagerPrivate, kNativeMessaging, kNetworkingPrivate, diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc index 16b4d73..0f85128 100644 --- a/chrome/common/extensions/permissions/chrome_api_permissions.cc +++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc @@ -187,6 +187,7 @@ std::vector<APIPermissionInfo*> ChromeAPIPermissions::GetAllPermissions() APIPermissionInfo::kFlagCannotBeOptional }, { APIPermission::kMetricsPrivate, "metricsPrivate", APIPermissionInfo::kFlagCannotBeOptional }, + { APIPermission::kMDns, "mdns", APIPermissionInfo::kFlagCannotBeOptional }, { APIPermission::kMusicManagerPrivate, "musicManagerPrivate", APIPermissionInfo::kFlagCannotBeOptional, IDS_EXTENSION_PROMPT_WARNING_MUSIC_MANAGER_PRIVATE, diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index 4e37958..a96e873 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -716,6 +716,7 @@ TEST(PermissionsTest, PermissionMessages) { skip.insert(APIPermission::kMediaGalleriesPrivate); skip.insert(APIPermission::kMediaPlayerPrivate); skip.insert(APIPermission::kMetricsPrivate); + skip.insert(APIPermission::kMDns); skip.insert(APIPermission::kPreferencesPrivate); skip.insert(APIPermission::kRecoveryPrivate); skip.insert(APIPermission::kRtcPrivate); diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 30cafb0..719187e 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -4,6 +4,9 @@ #include "chrome/renderer/extensions/event_bindings.h" +#include <map> +#include <set> +#include <string> #include <vector> #include "base/basictypes.h" @@ -21,7 +24,6 @@ #include "chrome/renderer/extensions/chrome_v8_context_set.h" #include "chrome/renderer/extensions/chrome_v8_extension.h" #include "chrome/renderer/extensions/dispatcher.h" -#include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_helper.h" #include "chrome/renderer/extensions/user_script_slave.h" #include "content/public/renderer/render_thread.h" @@ -293,6 +295,11 @@ class ExtensionImpl : public ChromeV8Extension { v8::Handle<v8::Value> instance_id_value(object->Get(instance_id)); info.SetInstanceID(instance_id_value->IntegerValue()); } + v8::Handle<v8::String> service_type(v8::String::New("serviceType")); + if (object->Has(service_type)) { + v8::Handle<v8::Value> service_type_value(object->Get(service_type)); + info.SetServiceType(*v8::String::AsciiValue(service_type_value)); + } return info; } diff --git a/chrome/renderer/resources/extensions/event.js b/chrome/renderer/resources/extensions/event.js index 13ad277..c6c5a0f 100644 --- a/chrome/renderer/resources/extensions/event.js +++ b/chrome/renderer/resources/extensions/event.js @@ -228,7 +228,7 @@ // Dispatches a named event with the given argument array. The args array is // the list of arguments that will be sent to the event callback. function dispatchEvent(name, args, filteringInfo) { - var listenerIDs = null; + var listenerIDs = []; if (filteringInfo) listenerIDs = eventNatives.MatchAgainstEventFilter(name, filteringInfo); @@ -262,7 +262,11 @@ if (!this.eventOptions_.supportsFilters) throw new Error("This event does not support filters."); if (filters.url && !(filters.url instanceof Array)) - throw new Error("filters.url should be an array"); + throw new Error("filters.url should be an array."); + if (filters.serviceType && + !(typeof filters.serviceType === 'string')) { + throw new Error("filters.serviceType should be a string.") + } } var listener = {callback: cb, filters: filters}; this.attach_(listener); diff --git a/chrome/test/data/extensions/api_test/mdns/api/manifest.json b/chrome/test/data/extensions/api_test/mdns/api/manifest.json new file mode 100644 index 0000000..96cfbed --- /dev/null +++ b/chrome/test/data/extensions/api_test/mdns/api/manifest.json @@ -0,0 +1,8 @@ + { + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8xv6iO+j4kzj1HiBL93+XVJH/CRyAQMUHS/Z0l8nCAzaAFkW/JsNwxJqQhrZspnxLqbQxNncXs6g6bsXAwKHiEs+LSs+bIv0Gc/2ycZdhXJ8GhEsSMakog5dpQd1681c2gLK/8CrAoewE/0GIKhaFcp7a2iZlGh4Am6fgMKy0iQIDAQAB", + "manifest_version": 2, + "name": "MDNS Extension", + "version": "1.0", + "description": "Test MDNS Extension", + "permissions": ["mdns"] +} diff --git a/chrome/test/data/extensions/api_test/mdns/api/register_listener.html b/chrome/test/data/extensions/api_test/mdns/api/register_listener.html new file mode 100644 index 0000000..9b54ce5 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mdns/api/register_listener.html @@ -0,0 +1 @@ +<script src="register_listener.js"></script> diff --git a/chrome/test/data/extensions/api_test/mdns/api/register_listener.js b/chrome/test/data/extensions/api_test/mdns/api/register_listener.js new file mode 100644 index 0000000..802a3ad --- /dev/null +++ b/chrome/test/data/extensions/api_test/mdns/api/register_listener.js @@ -0,0 +1,22 @@ +// Copyright 2013 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. + +onload = function() { + chrome.test.runTests([ + function registerListener() { + var numEvents = 0; + chrome.mdns.onServiceList.addListener(function(services) { + if (services[0].serviceName != '_googlecast._tcp.local') { + chrome.test.fail(); + return; + } else if (numEvents == 1) { + chrome.test.succeed(); + } else { + numEvents++; + } + }, {'serviceType': '_googlecast._tcp.local'}); + chrome.test.notifyPass(); + } + ]); +}; diff --git a/chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.html b/chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.html new file mode 100644 index 0000000..7490ea5 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.html @@ -0,0 +1 @@ +<script src="register_multiple_listeners.js"></script> diff --git a/chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.js b/chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.js new file mode 100644 index 0000000..0782153 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.js @@ -0,0 +1,23 @@ +// Copyright 2013 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. + +onload = function() { + chrome.test.runTests([ + function registerListeners() { + var numEvents = 0; + chrome.mdns.onServiceList.addListener(function(services) { + if (numEvents++ == 1) { + chrome.test.succeed(); + } + }, {'serviceType': '_googlecast._tcp.local'}); + + chrome.mdns.onServiceList.addListener(function(services) { + if (numEvents++ == 1) { + chrome.test.succeed(); + } + }, {'serviceType': '_testing._tcp.local'}); + chrome.test.notifyPass(); + } + ]); +}; diff --git a/extensions/common/event_filtering_info.cc b/extensions/common/event_filtering_info.cc index 29566e8..cc46f96 100644 --- a/extensions/common/event_filtering_info.cc +++ b/extensions/common/event_filtering_info.cc @@ -38,11 +38,15 @@ scoped_ptr<base::Value> EventFilteringInfo::AsValue() const { if (has_instance_id_) result->SetInteger("instanceId", instance_id_); + + if (!service_type_.empty()) + result->SetString("serviceType", service_type_); + return result.PassAs<base::Value>(); } bool EventFilteringInfo::IsEmpty() const { - return !has_url_; + return !has_url_ && service_type_.empty(); } } // namespace extensions diff --git a/extensions/common/event_filtering_info.h b/extensions/common/event_filtering_info.h index 86309ab..a358798 100644 --- a/extensions/common/event_filtering_info.h +++ b/extensions/common/event_filtering_info.h @@ -28,6 +28,9 @@ class EventFilteringInfo { ~EventFilteringInfo(); void SetURL(const GURL& url); void SetInstanceID(int instance_id); + void SetServiceType(const std::string& service_type) { + service_type_ = service_type; + } bool has_url() const { return has_url_; } const GURL& url() const { return url_; } @@ -35,12 +38,16 @@ class EventFilteringInfo { bool has_instance_id() const { return has_instance_id_; } int instance_id() const { return instance_id_; } + bool has_service_type() const { return !service_type_.empty(); } + const std::string& service_type() const { return service_type_; } + scoped_ptr<base::Value> AsValue() const; bool IsEmpty() const; private: bool has_url_; GURL url_; + std::string service_type_; bool has_instance_id_; int instance_id_; diff --git a/extensions/common/event_matcher.cc b/extensions/common/event_matcher.cc index ad76c89..8ae022a 100644 --- a/extensions/common/event_matcher.cc +++ b/extensions/common/event_matcher.cc @@ -12,6 +12,8 @@ const char kUrlFiltersKey[] = "url"; namespace extensions { +const char kEventFilterServiceTypeKey[] = "serviceType"; + EventMatcher::EventMatcher(scoped_ptr<base::DictionaryValue> filter, int routing_id) : filter_(filter.Pass()), @@ -23,10 +25,13 @@ EventMatcher::~EventMatcher() { bool EventMatcher::MatchNonURLCriteria( const EventFilteringInfo& event_info) const { - if (!event_info.has_instance_id()) - return true; + if (event_info.has_instance_id()) { + return event_info.instance_id() == GetInstanceID(); + } - return event_info.instance_id() == GetInstanceID(); + const std::string& service_type_filter = GetServiceTypeFilter(); + return service_type_filter.empty() || + service_type_filter == event_info.service_type(); } int EventMatcher::GetURLFilterCount() const { @@ -48,6 +53,12 @@ int EventMatcher::HasURLFilters() const { return GetURLFilterCount() != 0; } +std::string EventMatcher::GetServiceTypeFilter() const { + std::string service_type_filter; + filter_->GetStringASCII(kEventFilterServiceTypeKey, &service_type_filter); + return service_type_filter; +} + int EventMatcher::GetInstanceID() const { int instance_id = 0; filter_->GetInteger("instanceId", &instance_id); diff --git a/extensions/common/event_matcher.h b/extensions/common/event_matcher.h index 7843fce..bd0ce7b 100644 --- a/extensions/common/event_matcher.h +++ b/extensions/common/event_matcher.h @@ -12,6 +12,8 @@ namespace extensions { class EventFilteringInfo; +extern const char kEventFilterServiceTypeKey[]; + // Matches EventFilteringInfos against a set of criteria. This is intended to // be used by EventFilter which performs efficient URL matching across // potentially many EventMatchers itself. This is why this class only exposes @@ -29,6 +31,8 @@ class EventMatcher { int GetURLFilterCount() const; bool GetURLFilter(int i, base::DictionaryValue** url_filter_out); + std::string GetServiceTypeFilter() const; + int HasURLFilters() const; int GetInstanceID() const; |