Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / mdns / mdns_api.cc
blob477b11b5cb44879381c345206c838d823369b216
1 // Copyright 2013 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.
5 #include "chrome/browser/extensions/api/mdns/mdns_api.h"
7 #include <vector>
9 #include "base/lazy_instance.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "content/public/browser/render_frame_host.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "content/public/browser/web_contents.h"
15 #include "extensions/browser/extension_function.h"
16 #include "extensions/browser/extension_host.h"
17 #include "extensions/browser/extension_registry.h"
19 namespace extensions {
21 namespace mdns = api::mdns;
23 namespace {
25 // Whitelisted mDNS service types.
26 const char kCastServiceType[] = "_googlecast._tcp.local";
27 const char kPrivetServiceType[] = "_privet._tcp.local";
28 const char kTestServiceType[] = "_testing._tcp.local";
30 bool IsServiceTypeWhitelisted(const std::string& service_type) {
31 return service_type == kCastServiceType ||
32 service_type == kPrivetServiceType ||
33 service_type == kTestServiceType;
36 } // namespace
38 MDnsAPI::MDnsAPI(content::BrowserContext* context) : browser_context_(context) {
39 DCHECK(browser_context_);
40 extensions::EventRouter* event_router = EventRouter::Get(context);
41 DCHECK(event_router);
42 event_router->RegisterObserver(this, mdns::OnServiceList::kEventName);
45 MDnsAPI::~MDnsAPI() {
46 if (dns_sd_registry_.get()) {
47 dns_sd_registry_->RemoveObserver(this);
51 // static
52 MDnsAPI* MDnsAPI::Get(content::BrowserContext* context) {
53 return BrowserContextKeyedAPIFactory<MDnsAPI>::Get(context);
56 static base::LazyInstance<BrowserContextKeyedAPIFactory<MDnsAPI> > g_factory =
57 LAZY_INSTANCE_INITIALIZER;
59 // static
60 BrowserContextKeyedAPIFactory<MDnsAPI>* MDnsAPI::GetFactoryInstance() {
61 return g_factory.Pointer();
64 void MDnsAPI::SetDnsSdRegistryForTesting(
65 scoped_ptr<DnsSdRegistry> dns_sd_registry) {
66 dns_sd_registry_ = dns_sd_registry.Pass();
67 if (dns_sd_registry_.get())
68 dns_sd_registry_.get()->AddObserver(this);
71 void MDnsAPI::ForceDiscovery() {
72 DCHECK(thread_checker_.CalledOnValidThread());
73 DnsSdRegistry* registry = dns_sd_registry();
74 return registry->ForceDiscovery();
77 DnsSdRegistry* MDnsAPI::dns_sd_registry() {
78 DCHECK(thread_checker_.CalledOnValidThread());
79 if (!dns_sd_registry_.get()) {
80 dns_sd_registry_.reset(new extensions::DnsSdRegistry());
81 dns_sd_registry_->AddObserver(this);
83 return dns_sd_registry_.get();
86 void MDnsAPI::OnListenerAdded(const EventListenerInfo& details) {
87 DCHECK(thread_checker_.CalledOnValidThread());
88 UpdateMDnsListeners();
91 void MDnsAPI::OnListenerRemoved(const EventListenerInfo& details) {
92 DCHECK(thread_checker_.CalledOnValidThread());
93 UpdateMDnsListeners();
96 void MDnsAPI::UpdateMDnsListeners() {
97 std::set<std::string> new_service_types;
98 ServiceTypeCounts current_service_counts;
99 GetValidOnServiceListListeners(
100 "" /* service_type_filter - blank = all services */,
101 nullptr /* extension_ids */, &current_service_counts);
103 DnsSdRegistry* registry = dns_sd_registry();
105 // Check if the counts of per-service-type event handlers has changed since
106 // the previous invocation, and take appropriate action if a change was
107 // detected.
109 // mDNS registration is performed for difference(cur, previous).
110 // mDNS unregistration is performed for difference(previous, cur).
111 // The mDNS device list is refreshed if the listener count has grown for
112 // a service type in union(cur, previous).
113 ServiceTypeCounts::iterator i_cur = current_service_counts.begin();
114 ServiceTypeCounts::iterator i_prev = prev_service_counts_.begin();
115 while (i_cur != current_service_counts.end() ||
116 i_prev != prev_service_counts_.end()) {
117 if (i_prev == prev_service_counts_.end() ||
118 (i_cur != current_service_counts.end() &&
119 i_cur->first < i_prev->first)) {
120 DVLOG(2) << "Registering listener for mDNS service " << i_cur->first;
121 registry->RegisterDnsSdListener(i_cur->first);
122 i_cur++;
123 } else if (i_cur == current_service_counts.end() ||
124 (i_prev != prev_service_counts_.end() &&
125 i_prev->first < i_cur->first)) {
126 DVLOG(2) << "Unregistering listener for mDNS service " << i_prev->first;
127 registry->UnregisterDnsSdListener(i_prev->first);
128 i_prev++;
129 } else {
130 if (i_cur->second > i_prev->second) {
131 DVLOG(2) << "Additional listeners added for mDNS service "
132 << i_cur->first;
133 registry->Publish(i_cur->first);
135 ++i_cur;
136 ++i_prev;
139 prev_service_counts_.swap(current_service_counts);
142 void MDnsAPI::OnDnsSdEvent(const std::string& service_type,
143 const DnsSdRegistry::DnsSdServiceList& services) {
144 DCHECK(thread_checker_.CalledOnValidThread());
146 std::vector<linked_ptr<mdns::MDnsService> > args;
147 for (DnsSdRegistry::DnsSdServiceList::const_iterator it = services.begin();
148 it != services.end(); ++it) {
149 if (static_cast<int>(args.size()) ==
150 api::mdns::MAX_SERVICE_INSTANCES_PER_EVENT) {
151 // TODO(reddaly): This is not the most meaningful way of notifying the
152 // application that something bad happened. It will go to the user's
153 // console (which most users don't look at)and the developer will be none
154 // the wiser. Instead, changing the event to pass the number of
155 // discovered instances would allow the caller to know when the list is
156 // truncated and tell the user something meaningful in the extension/app.
157 WriteToConsole(service_type,
158 content::CONSOLE_MESSAGE_LEVEL_WARNING,
159 base::StringPrintf(
160 "Truncating number of service instances in "
161 "onServiceList to maximum allowed: %d",
162 api::mdns::MAX_SERVICE_INSTANCES_PER_EVENT));
163 break;
165 linked_ptr<mdns::MDnsService> mdns_service =
166 make_linked_ptr(new mdns::MDnsService);
167 mdns_service->service_name = (*it).service_name;
168 mdns_service->service_host_port = (*it).service_host_port;
169 mdns_service->ip_address = (*it).ip_address;
170 mdns_service->service_data = (*it).service_data;
171 args.push_back(mdns_service);
174 scoped_ptr<base::ListValue> results = mdns::OnServiceList::Create(args);
175 scoped_ptr<Event> event(new Event(events::MDNS_ON_SERVICE_LIST,
176 mdns::OnServiceList::kEventName,
177 results.Pass()));
178 event->restrict_to_browser_context = browser_context_;
179 event->filter_info.SetServiceType(service_type);
181 // TODO(justinlin): To avoid having listeners without filters getting all
182 // events, modify API to have this event require filters.
183 // TODO(reddaly): If event isn't on whitelist, ensure it does not get
184 // broadcast to extensions.
185 extensions::EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass());
188 const extensions::EventListenerMap::ListenerList& MDnsAPI::GetEventListeners() {
189 return extensions::EventRouter::Get(browser_context_)
190 ->listeners()
191 .GetEventListenersByName(mdns::OnServiceList::kEventName);
194 bool MDnsAPI::IsMDnsAllowed(const std::string& extension_id,
195 const std::string& service_type) const {
196 const extensions::Extension* extension =
197 ExtensionRegistry::Get(browser_context_)
198 ->enabled_extensions()
199 .GetByID(extension_id);
200 return (extension && (extension->is_platform_app() ||
201 IsServiceTypeWhitelisted(service_type)));
204 void MDnsAPI::GetValidOnServiceListListeners(
205 const std::string& service_type_filter,
206 std::set<std::string>* extension_ids,
207 ServiceTypeCounts* service_type_counts) {
208 for (const auto& listener : GetEventListeners()) {
209 base::DictionaryValue* filter = listener->filter();
211 std::string service_type;
212 filter->GetStringASCII(kEventFilterServiceTypeKey, &service_type);
213 if (service_type.empty())
214 continue;
216 // Match service type when filter isn't ""
217 if (!service_type_filter.empty() && service_type_filter != service_type)
218 continue;
220 // Don't listen for services associated only with disabled extensions
221 // or non-whitelisted, non-platform-app extensions.
222 if (!IsMDnsAllowed(listener->extension_id(), service_type))
223 continue;
225 if (extension_ids)
226 extension_ids->insert(listener->extension_id());
227 if (service_type_counts) {
228 (*service_type_counts)[service_type]++;
233 void MDnsAPI::WriteToConsole(const std::string& service_type,
234 content::ConsoleMessageLevel level,
235 const std::string& message) {
236 // Get all the extensions with an onServiceList listener for a particular
237 // service type.
238 std::set<std::string> extension_ids;
239 ServiceTypeCounts counts;
240 GetValidOnServiceListListeners(service_type, &extension_ids,
241 nullptr /* service_type_counts */);
243 std::string logged_message(std::string("[chrome.mdns] ") + message);
245 // Log to the consoles of the background pages for those extensions.
246 // TODO(devlin): It's a little weird to log to the background pages,
247 // especially when it might be dormant. We should probably just log to a place
248 // like the ErrorConsole instead.
249 for (const std::string& extension_id : extension_ids) {
250 extensions::ExtensionHost* host =
251 extensions::ProcessManager::Get(browser_context_)
252 ->GetBackgroundHostForExtension(extension_id);
253 content::RenderFrameHost* rfh =
254 host ? host->host_contents()->GetMainFrame() : nullptr;
255 if (rfh)
256 rfh->AddMessageToConsole(level, logged_message);
260 MdnsForceDiscoveryFunction::MdnsForceDiscoveryFunction() {
263 MdnsForceDiscoveryFunction::~MdnsForceDiscoveryFunction() {
266 AsyncApiFunction::ResponseAction MdnsForceDiscoveryFunction::Run() {
267 MDnsAPI* api = MDnsAPI::Get(browser_context());
268 if (!api) {
269 return RespondNow(Error("Unknown error."));
271 api->ForceDiscovery();
272 return RespondNow(NoArguments());
275 } // namespace extensions