summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/api
diff options
context:
space:
mode:
authorreddaly <reddaly@chromium.org>2015-03-27 16:18:25 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-27 23:19:03 +0000
commit68ea490084597d5d4640e782989c0a6a094dcd21 (patch)
tree4bc54f7d81b00de793c9a1ed804813d8f294558f /chrome/browser/extensions/api
parent5f681cc59082ad1e8a05f6921595661e6dc1bdf9 (diff)
downloadchromium_src-68ea490084597d5d4640e782989c0a6a094dcd21.zip
chromium_src-68ea490084597d5d4640e782989c0a6a094dcd21.tar.gz
chromium_src-68ea490084597d5d4640e782989c0a6a094dcd21.tar.bz2
Allows all packaged apps to use the chrome.mdns API, and removes whitelist that restricts the set of service type that can be discovered.
R=mkwst@chromium.org,mfoltz@chromium.org BUG=426500 Review URL: https://codereview.chromium.org/668983003 Cr-Commit-Position: refs/heads/master@{#322671}
Diffstat (limited to 'chrome/browser/extensions/api')
-rw-r--r--chrome/browser/extensions/api/mdns/mdns_api.cc40
-rw-r--r--chrome/browser/extensions/api/mdns/mdns_api_unittest.cc217
-rw-r--r--chrome/browser/extensions/api/mdns/mdns_apitest.cc2
3 files changed, 245 insertions, 14 deletions
diff --git a/chrome/browser/extensions/api/mdns/mdns_api.cc b/chrome/browser/extensions/api/mdns/mdns_api.cc
index 9bc7fb3..2a7c41d 100644
--- a/chrome/browser/extensions/api/mdns/mdns_api.cc
+++ b/chrome/browser/extensions/api/mdns/mdns_api.cc
@@ -9,6 +9,7 @@
#include "base/lazy_instance.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/common/extensions/api/mdns.h"
+#include "extensions/browser/extension_registry.h"
namespace extensions {
@@ -31,8 +32,9 @@ bool IsServiceTypeWhitelisted(const std::string& service_type) {
MDnsAPI::MDnsAPI(content::BrowserContext* context) : browser_context_(context) {
DCHECK(browser_context_);
- EventRouter::Get(context)
- ->RegisterObserver(this, mdns::OnServiceList::kEventName);
+ extensions::EventRouter* event_router = EventRouter::Get(context);
+ DCHECK(event_router);
+ event_router->RegisterObserver(this, mdns::OnServiceList::kEventName);
}
MDnsAPI::~MDnsAPI() {
@@ -57,6 +59,8 @@ BrowserContextKeyedAPIFactory<MDnsAPI>* MDnsAPI::GetFactoryInstance() {
void MDnsAPI::SetDnsSdRegistryForTesting(
scoped_ptr<DnsSdRegistry> dns_sd_registry) {
dns_sd_registry_ = dns_sd_registry.Pass();
+ if (dns_sd_registry_.get())
+ dns_sd_registry_.get()->AddObserver(this);
}
DnsSdRegistry* MDnsAPI::dns_sd_registry() {
@@ -81,7 +85,7 @@ void MDnsAPI::OnListenerRemoved(const EventListenerInfo& details) {
void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) {
std::set<std::string> new_service_types;
- // Check all listeners for service type filers.
+ // Check all listeners for service type filters.
const EventListenerMap::ListenerList& listeners =
extensions::EventRouter::Get(browser_context_)
->listeners()
@@ -94,6 +98,19 @@ void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) {
filter->GetStringASCII(kEventFilterServiceTypeKey, &filter_value);
if (filter_value.empty())
continue;
+
+ const Extension* extension = ExtensionRegistry::Get(browser_context_)->
+ enabled_extensions().GetByID((*it)->extension_id());
+ // Don't listen for services associated only with disabled extensions.
+ if (!extension)
+ continue;
+
+ // Platform apps may query for all services; other types of extensions are
+ // restricted to a whitelist.
+ if (!extension->is_platform_app() &&
+ !IsServiceTypeWhitelisted(filter_value))
+ continue;
+
new_service_types.insert(filter_value);
}
@@ -107,17 +124,12 @@ void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) {
// 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 (const auto& srv : added_service_types) {
+ registry->RegisterDnsSdListener(srv);
}
- for (std::set<std::string>::iterator it = removed_service_types.begin();
- it != removed_service_types.end(); ++it) {
- if (IsServiceTypeWhitelisted(*it))
- registry->UnregisterDnsSdListener(*it);
+ for (const auto& srv : removed_service_types) {
+ registry->UnregisterDnsSdListener(srv);
}
-
service_types_ = new_service_types;
}
@@ -143,10 +155,10 @@ void MDnsAPI::OnDnsSdEvent(const std::string& service_type,
event->restrict_to_browser_context = browser_context_;
event->filter_info.SetServiceType(service_type);
- VLOG(1) << "Broadcasting OnServiceList event: " << event.get();
-
// TODO(justinlin): To avoid having listeners without filters getting all
// events, modify API to have this event require filters.
+ // TODO(reddaly): If event isn't on whitelist, ensure it does not get
+ // broadcast to extensions.
extensions::EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass());
}
diff --git a/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc b/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc
new file mode 100644
index 0000000..ede2a2b
--- /dev/null
+++ b/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright 2015 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 "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/common/extensions/api/mdns.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "extensions/browser/extension_prefs_factory.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/manifest_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+KeyedService* MDnsAPITestingFactoryFunction(content::BrowserContext* context) {
+ return new MDnsAPI(context);
+}
+
+// For ExtensionService interface when it requires a path that is not used.
+base::FilePath bogus_file_pathname(const std::string& name) {
+ return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"))
+ .AppendASCII(name);
+}
+
+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 extensions::ExtensionServiceTestBase {
+ public:
+ void SetUp() override {
+ extensions::ExtensionServiceTestBase::SetUp();
+
+ // Set up browser_context().
+ InitializeEmptyExtensionService();
+
+ // A custom TestingFactoryFunction is required for an MDnsAPI to actually be
+ // constructed.
+ MDnsAPI::GetFactoryInstance()->SetTestingFactory(
+ browser_context(),
+ MDnsAPITestingFactoryFunction);
+
+ // Create an event router and associate it with the context.
+ extensions::EventRouter* event_router = new extensions::EventRouter(
+ browser_context(),
+ ExtensionPrefsFactory::GetInstance()->GetForBrowserContext(
+ browser_context()));
+ static_cast<TestExtensionSystem*>(
+ ExtensionSystem::Get(browser_context()))->SetEventRouter(
+ scoped_ptr<extensions::EventRouter>(event_router));
+
+ // Do some sanity checking
+ ASSERT_EQ(event_router, EventRouter::Get(browser_context()));
+ ASSERT_TRUE(MDnsAPI::Get(browser_context())); // constructs MDnsAPI
+
+ registry_ = new MockDnsSdRegistry(MDnsAPI::Get(browser_context()));
+ EXPECT_CALL(*dns_sd_registry(),
+ AddObserver(MDnsAPI::Get(browser_context())))
+ .Times(1);
+ MDnsAPI::Get(browser_context())->SetDnsSdRegistryForTesting(
+ scoped_ptr<DnsSdRegistry>(registry_));
+
+ render_process_host_.reset(
+ new content::MockRenderProcessHost(browser_context()));
+ }
+
+ void TearDown() override {
+ EXPECT_CALL(*dns_sd_registry(),
+ RemoveObserver(MDnsAPI::Get(browser_context())))
+ .Times(1);
+ render_process_host_.reset();
+ extensions::ExtensionServiceTestBase::TearDown();
+ MDnsAPI::GetFactoryInstance()->SetTestingFactory(
+ browser_context(),
+ nullptr);
+
+ registry_ = nullptr;
+ }
+
+ virtual MockDnsSdRegistry* dns_sd_registry() {
+ return registry_;
+ }
+
+ // Constructs an extension according to the parameters that matter most to
+ // MDnsAPI the local unit tests.
+ const scoped_refptr<extensions::Extension> CreateExtension(
+ std::string name,
+ bool is_platform_app,
+ std::string extension_id) {
+ base::DictionaryValue manifest;
+ manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
+ manifest.SetString(extensions::manifest_keys::kName, name);
+ if (is_platform_app) {
+ // Setting app.background.page = "background.html" is sufficient to make
+ // the extension type TYPE_PLATFORM_APP.
+ manifest.Set(extensions::manifest_keys::kPlatformAppBackgroundPage,
+ new base::StringValue("background.html"));
+ }
+
+ std::string error;
+ return extensions::Extension::Create(
+ bogus_file_pathname(name),
+ extensions::Manifest::INVALID_LOCATION,
+ manifest,
+ Extension::NO_FLAGS,
+ extension_id,
+ &error);
+ }
+
+ content::RenderProcessHost* render_process_host() const {
+ return render_process_host_.get();
+ }
+
+ private:
+ // The registry is owned by MDnsAPI, but MDnsAPI does not have an accessor
+ // for it, so use a private member.
+ MockDnsSdRegistry* registry_;
+
+ scoped_ptr<content::RenderProcessHost> render_process_host_;
+
+};
+
+TEST_F(MDnsAPITest, ExtensionRespectsWhitelist) {
+ const std::string ext_id("mbflcebpggnecokmikipoihdbecnjfoj");
+ scoped_refptr<extensions::Extension> extension =
+ CreateExtension("Dinosaur networker", false, ext_id);
+ ExtensionRegistry::Get(browser_context())->AddEnabled(extension);
+ ASSERT_EQ(Manifest::TYPE_EXTENSION, extension.get()->GetType());
+
+ // There is a whitelist of mdns service types extensions may access, which
+ // includes "_testing._tcp.local" and exludes "_trex._tcp.local"
+ {
+ base::DictionaryValue filter;
+ filter.SetString(kEventFilterServiceTypeKey, "_trex._tcp.local");
+
+ ASSERT_TRUE(dns_sd_registry());
+ // Test that the extension is able to listen to a non-whitelisted service
+ EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"))
+ .Times(0);
+ EventRouter::Get(browser_context())->AddFilteredEventListener(
+ api::mdns::OnServiceList::kEventName, render_process_host(), ext_id,
+ filter, false);
+
+ EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"))
+ .Times(0);
+ EventRouter::Get(browser_context())->RemoveFilteredEventListener(
+ api::mdns::OnServiceList::kEventName, render_process_host(), ext_id,
+ filter, false);
+ }
+ {
+ base::DictionaryValue filter;
+ filter.SetString(kEventFilterServiceTypeKey, "_testing._tcp.local");
+
+ ASSERT_TRUE(dns_sd_registry());
+ // Test that the extension is able to listen to a whitelisted service
+ EXPECT_CALL(*dns_sd_registry(),
+ RegisterDnsSdListener("_testing._tcp.local"));
+ EventRouter::Get(browser_context())->AddFilteredEventListener(
+ api::mdns::OnServiceList::kEventName, render_process_host(), ext_id,
+ filter, false);
+
+ EXPECT_CALL(*dns_sd_registry(),
+ UnregisterDnsSdListener("_testing._tcp.local"));
+ EventRouter::Get(browser_context())->RemoveFilteredEventListener(
+ api::mdns::OnServiceList::kEventName, render_process_host(),
+ ext_id, filter, false);
+ }
+}
+
+TEST_F(MDnsAPITest, PlatformAppsNotSubjectToWhitelist) {
+ const std::string ext_id("mbflcebpggnecokmikipoihdbecnjfoj");
+ scoped_refptr<extensions::Extension> extension =
+ CreateExtension("Dinosaur networker", true, ext_id);
+ ExtensionRegistry::Get(browser_context())->AddEnabled(extension);
+ ASSERT_TRUE(extension.get()->is_platform_app());
+
+ base::DictionaryValue filter;
+ filter.SetString(kEventFilterServiceTypeKey, "_trex._tcp.local");
+
+ ASSERT_TRUE(dns_sd_registry());
+ // Test that the extension is able to listen to a non-whitelisted service
+ EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"));
+ EventRouter::Get(browser_context())->AddFilteredEventListener(
+ api::mdns::OnServiceList::kEventName, render_process_host(), ext_id,
+ filter, false);
+
+ EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"));
+ EventRouter::Get(browser_context())->RemoveFilteredEventListener(
+ api::mdns::OnServiceList::kEventName, render_process_host(), ext_id,
+ filter, false);
+}
+
+} // empty namespace
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/mdns/mdns_apitest.cc b/chrome/browser/extensions/api/mdns/mdns_apitest.cc
index 5365518..61d10d3 100644
--- a/chrome/browser/extensions/api/mdns/mdns_apitest.cc
+++ b/chrome/browser/extensions/api/mdns/mdns_apitest.cc
@@ -52,6 +52,8 @@ class MDnsAPITest : public ExtensionApiTest {
void SetUpTestDnsSdRegistry() {
extensions::MDnsAPI* api = extensions::MDnsAPI::Get(profile());
dns_sd_registry_ = new MockDnsSdRegistry(api);
+ EXPECT_CALL(*dns_sd_registry_, AddObserver(api))
+ .Times(1);
// Transfers ownership of the registry instance.
api->SetDnsSdRegistryForTesting(
make_scoped_ptr<DnsSdRegistry>(dns_sd_registry_).Pass());