blob: ede2a2b50d4a03491f54e9f4e8e34ff72d3e852d [file] [log] [blame]
// 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