summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjustinlin@chromium.org <justinlin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-06 07:21:08 +0000
committerjustinlin@chromium.org <justinlin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-06 07:21:08 +0000
commit5556c5d23047abd3a19705103a1ec14b1026d92e (patch)
tree94349edac8fb53dd7aadacf74122f16cc9eb0a47
parent9bbaad67d5fe08d314c9e9e3cdd88bb6ddabcd1c (diff)
downloadchromium_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
-rw-r--r--chrome/browser/extensions/api/mdns/OWNERS2
-rw-r--r--chrome/browser/extensions/api/mdns/dns_sd_registry.cc28
-rw-r--r--chrome/browser/extensions/api/mdns/dns_sd_registry.h47
-rw-r--r--chrome/browser/extensions/api/mdns/mdns_api.cc154
-rw-r--r--chrome/browser/extensions/api/mdns/mdns_api.h80
-rw-r--r--chrome/browser/extensions/api/mdns/mdns_apitest.cc120
-rw-r--r--chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc2
-rw-r--r--chrome/chrome_browser_extensions.gypi4
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/extensions/api/_api_features.json4
-rw-r--r--chrome/common/extensions/api/_permission_features.json12
-rw-r--r--chrome/common/extensions/api/api.gyp1
-rw-r--r--chrome/common/extensions/api/mdns.idl34
-rw-r--r--chrome/common/extensions/permissions/api_permission.h1
-rw-r--r--chrome/common/extensions/permissions/chrome_api_permissions.cc1
-rw-r--r--chrome/common/extensions/permissions/permission_set_unittest.cc1
-rw-r--r--chrome/renderer/extensions/event_bindings.cc9
-rw-r--r--chrome/renderer/resources/extensions/event.js8
-rw-r--r--chrome/test/data/extensions/api_test/mdns/api/manifest.json8
-rw-r--r--chrome/test/data/extensions/api_test/mdns/api/register_listener.html1
-rw-r--r--chrome/test/data/extensions/api_test/mdns/api/register_listener.js22
-rw-r--r--chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.html1
-rw-r--r--chrome/test/data/extensions/api_test/mdns/api/register_multiple_listeners.js23
-rw-r--r--extensions/common/event_filtering_info.cc6
-rw-r--r--extensions/common/event_filtering_info.h7
-rw-r--r--extensions/common/event_matcher.cc17
-rw-r--r--extensions/common/event_matcher.h4
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;