Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / custom_handlers / protocol_handler_registry.cc
blob43b1b20694a49f4e421c05ec630f16e1f5ad832d
1 // Copyright (c) 2012 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/custom_handlers/protocol_handler_registry.h"
7 #include <utility>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/prefs/pref_service.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/custom_handlers/register_protocol_handler_infobar_delegate.h"
15 #include "chrome/browser/profiles/profile_io_data.h"
16 #include "chrome/common/custom_handlers/protocol_handler.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "components/pref_registry/pref_registry_syncable.h"
20 #include "components/user_prefs/user_prefs.h"
21 #include "content/public/browser/child_process_security_policy.h"
22 #include "net/base/network_delegate.h"
23 #include "net/url_request/url_request_redirect_job.h"
24 #include "ui/base/l10n/l10n_util.h"
26 using content::BrowserThread;
27 using content::ChildProcessSecurityPolicy;
29 namespace {
31 const ProtocolHandler& LookupHandler(
32 const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map,
33 const std::string& scheme) {
34 ProtocolHandlerRegistry::ProtocolHandlerMap::const_iterator p =
35 handler_map.find(scheme);
37 if (p != handler_map.end())
38 return p->second;
40 return ProtocolHandler::EmptyProtocolHandler();
43 // If true default protocol handlers will be removed if the OS level
44 // registration for a protocol is no longer Chrome.
45 bool ShouldRemoveHandlersNotInOS() {
46 #if defined(OS_LINUX)
47 // We don't do this on Linux as the OS registration there is not reliable,
48 // and Chrome OS doesn't have any notion of OS registration.
49 // TODO(benwells): When Linux support is more reliable remove this
50 // difference (http://crbug.com/88255).
51 return false;
52 #else
53 return ShellIntegration::CanSetAsDefaultProtocolClient() !=
54 ShellIntegration::SET_DEFAULT_NOT_ALLOWED;
55 #endif
58 } // namespace
60 // IOThreadDelegate ------------------------------------------------------------
62 // IOThreadDelegate is an IO thread specific object. Access to the class should
63 // all be done via the IO thread. The registry living on the UI thread makes
64 // a best effort to update the IO object after local updates are completed.
65 class ProtocolHandlerRegistry::IOThreadDelegate
66 : public base::RefCountedThreadSafe<
67 ProtocolHandlerRegistry::IOThreadDelegate> {
68 public:
70 // Creates a new instance. If |enabled| is true the registry is considered
71 // enabled on the IO thread.
72 explicit IOThreadDelegate(bool enabled);
74 // Returns true if the protocol has a default protocol handler.
75 // Should be called only from the IO thread.
76 bool IsHandledProtocol(const std::string& scheme) const;
78 // Clears the default for the provided protocol.
79 // Should be called only from the IO thread.
80 void ClearDefault(const std::string& scheme);
82 // Makes this ProtocolHandler the default handler for its protocol.
83 // Should be called only from the IO thread.
84 void SetDefault(const ProtocolHandler& handler);
86 // Creates a URL request job for the given request if there is a matching
87 // protocol handler, returns NULL otherwise.
88 net::URLRequestJob* MaybeCreateJob(
89 net::URLRequest* request, net::NetworkDelegate* network_delegate) const;
91 // Indicate that the registry has been enabled in the IO thread's
92 // copy of the data.
93 void Enable() { enabled_ = true; }
95 // Indicate that the registry has been disabled in the IO thread's copy of
96 // the data.
97 void Disable() { enabled_ = false; }
99 private:
100 friend class base::RefCountedThreadSafe<IOThreadDelegate>;
101 virtual ~IOThreadDelegate();
103 // Copy of protocol handlers use only on the IO thread.
104 ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_;
106 // Is the registry enabled on the IO thread.
107 bool enabled_;
109 DISALLOW_COPY_AND_ASSIGN(IOThreadDelegate);
112 ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool)
113 : enabled_(true) {}
114 ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {}
116 bool ProtocolHandlerRegistry::IOThreadDelegate::IsHandledProtocol(
117 const std::string& scheme) const {
118 DCHECK_CURRENTLY_ON(BrowserThread::IO);
119 return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty();
122 void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault(
123 const std::string& scheme) {
124 DCHECK_CURRENTLY_ON(BrowserThread::IO);
125 default_handlers_.erase(scheme);
128 void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault(
129 const ProtocolHandler& handler) {
130 DCHECK_CURRENTLY_ON(BrowserThread::IO);
131 ClearDefault(handler.protocol());
132 default_handlers_.insert(std::make_pair(handler.protocol(), handler));
135 // Create a new job for the supplied |URLRequest| if a default handler
136 // is registered and the associated handler is able to interpret
137 // the url from |request|.
138 net::URLRequestJob* ProtocolHandlerRegistry::IOThreadDelegate::MaybeCreateJob(
139 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO);
142 ProtocolHandler handler = LookupHandler(default_handlers_,
143 request->url().scheme());
144 if (handler.IsEmpty())
145 return NULL;
147 GURL translated_url(handler.TranslateUrl(request->url()));
148 if (!translated_url.is_valid())
149 return NULL;
151 return new net::URLRequestRedirectJob(
152 request, network_delegate, translated_url,
153 net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
154 "Protocol Handler Registry");
157 // JobInterceptorFactory -------------------------------------------------------
159 // Instances of JobInterceptorFactory are produced for ownership by the IO
160 // thread where it handler URL requests. We should never hold
161 // any pointers on this class, only produce them in response to
162 // requests via |ProtocolHandlerRegistry::CreateJobInterceptorFactory|.
163 ProtocolHandlerRegistry::JobInterceptorFactory::JobInterceptorFactory(
164 IOThreadDelegate* io_thread_delegate)
165 : io_thread_delegate_(io_thread_delegate) {
166 DCHECK(io_thread_delegate_.get());
167 DetachFromThread();
170 ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() {
173 void ProtocolHandlerRegistry::JobInterceptorFactory::Chain(
174 scoped_ptr<net::URLRequestJobFactory> job_factory) {
175 job_factory_ = job_factory.Pass();
178 net::URLRequestJob*
179 ProtocolHandlerRegistry::JobInterceptorFactory::
180 MaybeCreateJobWithProtocolHandler(
181 const std::string& scheme,
182 net::URLRequest* request,
183 net::NetworkDelegate* network_delegate) const {
184 DCHECK_CURRENTLY_ON(BrowserThread::IO);
185 net::URLRequestJob* job = io_thread_delegate_->MaybeCreateJob(
186 request, network_delegate);
187 if (job)
188 return job;
189 return job_factory_->MaybeCreateJobWithProtocolHandler(
190 scheme, request, network_delegate);
193 net::URLRequestJob*
194 ProtocolHandlerRegistry::JobInterceptorFactory::MaybeInterceptRedirect(
195 net::URLRequest* request,
196 net::NetworkDelegate* network_delegate,
197 const GURL& location) const {
198 return job_factory_->MaybeInterceptRedirect(
199 request, network_delegate, location);
202 net::URLRequestJob*
203 ProtocolHandlerRegistry::JobInterceptorFactory::MaybeInterceptResponse(
204 net::URLRequest* request,
205 net::NetworkDelegate* network_delegate) const {
206 return job_factory_->MaybeInterceptResponse(request, network_delegate);
209 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol(
210 const std::string& scheme) const {
211 DCHECK_CURRENTLY_ON(BrowserThread::IO);
212 return io_thread_delegate_->IsHandledProtocol(scheme) ||
213 job_factory_->IsHandledProtocol(scheme);
216 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledURL(
217 const GURL& url) const {
218 DCHECK_CURRENTLY_ON(BrowserThread::IO);
219 return (url.is_valid() &&
220 io_thread_delegate_->IsHandledProtocol(url.scheme())) ||
221 job_factory_->IsHandledURL(url);
224 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget(
225 const GURL& location) const {
226 DCHECK_CURRENTLY_ON(BrowserThread::IO);
227 return job_factory_->IsSafeRedirectTarget(location);
230 // DefaultClientObserver ------------------------------------------------------
232 ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver(
233 ProtocolHandlerRegistry* registry)
234 : worker_(NULL),
235 registry_(registry) {
236 DCHECK(registry_);
239 ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() {
240 if (worker_)
241 worker_->ObserverDestroyed();
243 DefaultClientObserverList::iterator iter = std::find(
244 registry_->default_client_observers_.begin(),
245 registry_->default_client_observers_.end(), this);
246 registry_->default_client_observers_.erase(iter);
249 void ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState(
250 ShellIntegration::DefaultWebClientUIState state) {
251 if (worker_) {
252 if (ShouldRemoveHandlersNotInOS() &&
253 (state == ShellIntegration::STATE_NOT_DEFAULT)) {
254 registry_->ClearDefault(worker_->protocol());
256 } else {
257 NOTREACHED();
261 bool ProtocolHandlerRegistry::DefaultClientObserver::
262 IsInteractiveSetDefaultPermitted() {
263 return true;
266 void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker(
267 ShellIntegration::DefaultProtocolClientWorker* worker) {
268 worker_ = worker;
271 bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() {
272 return true;
275 // Delegate --------------------------------------------------------------------
277 ProtocolHandlerRegistry::Delegate::~Delegate() {}
279 void ProtocolHandlerRegistry::Delegate::RegisterExternalHandler(
280 const std::string& protocol) {
281 ChildProcessSecurityPolicy* policy =
282 ChildProcessSecurityPolicy::GetInstance();
283 if (!policy->IsWebSafeScheme(protocol)) {
284 policy->RegisterWebSafeScheme(protocol);
288 void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler(
289 const std::string& protocol) {
292 bool ProtocolHandlerRegistry::Delegate::IsExternalHandlerRegistered(
293 const std::string& protocol) {
294 // NOTE(koz): This function is safe to call from any thread, despite living
295 // in ProfileIOData.
296 return ProfileIOData::IsHandledProtocol(protocol);
299 ShellIntegration::DefaultProtocolClientWorker*
300 ProtocolHandlerRegistry::Delegate::CreateShellWorker(
301 ShellIntegration::DefaultWebClientObserver* observer,
302 const std::string& protocol) {
303 return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol);
306 ProtocolHandlerRegistry::DefaultClientObserver*
307 ProtocolHandlerRegistry::Delegate::CreateShellObserver(
308 ProtocolHandlerRegistry* registry) {
309 return new DefaultClientObserver(registry);
312 void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient(
313 const std::string& protocol, ProtocolHandlerRegistry* registry) {
314 DefaultClientObserver* observer = CreateShellObserver(registry);
315 // The worker pointer is reference counted. While it is running the
316 // message loops of the FILE and UI thread will hold references to it
317 // and it will be automatically freed once all its tasks have finished.
318 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
319 worker = CreateShellWorker(observer, protocol);
320 observer->SetWorker(worker.get());
321 registry->default_client_observers_.push_back(observer);
322 worker->StartSetAsDefault();
325 // ProtocolHandlerRegistry -----------------------------------------------------
327 ProtocolHandlerRegistry::ProtocolHandlerRegistry(
328 content::BrowserContext* context, Delegate* delegate)
329 : context_(context),
330 delegate_(delegate),
331 enabled_(true),
332 is_loading_(false),
333 is_loaded_(false),
334 io_thread_delegate_(new IOThreadDelegate(enabled_)){
337 bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest(
338 const ProtocolHandler& handler) {
339 if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol()))
340 return true;
342 if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler))
343 return true;
345 if (AttemptReplace(handler))
346 return true;
348 return false;
351 void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler(
352 const ProtocolHandler& handler) {
353 DCHECK_CURRENTLY_ON(BrowserThread::UI);
354 RegisterProtocolHandler(handler, USER);
355 SetDefault(handler);
356 Save();
357 NotifyChanged();
360 void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
361 const ProtocolHandler& handler) {
362 DCHECK_CURRENTLY_ON(BrowserThread::UI);
363 RegisterProtocolHandler(handler, USER);
364 Save();
365 NotifyChanged();
368 void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
369 const ProtocolHandler& handler) {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 IgnoreProtocolHandler(handler, USER);
372 Save();
373 NotifyChanged();
376 bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) {
377 DCHECK_CURRENTLY_ON(BrowserThread::UI);
378 ProtocolHandler old_default = GetHandlerFor(handler.protocol());
379 bool make_new_handler_default = handler.IsSameOrigin(old_default);
380 ProtocolHandlerList to_replace(GetReplacedHandlers(handler));
381 if (to_replace.empty())
382 return false;
383 for (ProtocolHandlerList::iterator p = to_replace.begin();
384 p != to_replace.end(); ++p) {
385 RemoveHandler(*p);
387 if (make_new_handler_default) {
388 OnAcceptRegisterProtocolHandler(handler);
389 } else {
390 InsertHandler(handler);
391 NotifyChanged();
393 return true;
396 ProtocolHandlerRegistry::ProtocolHandlerList
397 ProtocolHandlerRegistry::GetReplacedHandlers(
398 const ProtocolHandler& handler) const {
399 ProtocolHandlerList replaced_handlers;
400 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
401 if (!handlers)
402 return replaced_handlers;
403 for (ProtocolHandlerList::const_iterator p = handlers->begin();
404 p != handlers->end(); p++) {
405 if (handler.IsSameOrigin(*p)) {
406 replaced_handlers.push_back(*p);
409 return replaced_handlers;
412 void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) {
413 DCHECK_CURRENTLY_ON(BrowserThread::UI);
415 default_handlers_.erase(scheme);
416 BrowserThread::PostTask(
417 BrowserThread::IO,
418 FROM_HERE,
419 base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_, scheme));
420 Save();
421 NotifyChanged();
424 bool ProtocolHandlerRegistry::IsDefault(
425 const ProtocolHandler& handler) const {
426 DCHECK_CURRENTLY_ON(BrowserThread::UI);
427 return GetHandlerFor(handler.protocol()) == handler;
430 void ProtocolHandlerRegistry::InstallDefaultsForChromeOS() {
431 #if defined(OS_CHROMEOS)
432 // Only chromeos has default protocol handlers at this point.
433 AddPredefinedHandler(
434 ProtocolHandler::CreateProtocolHandler(
435 "mailto",
436 GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_MAILTO_HANDLER_URL))));
437 AddPredefinedHandler(
438 ProtocolHandler::CreateProtocolHandler(
439 "webcal",
440 GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL))));
441 #else
442 NOTREACHED(); // this method should only ever be called in chromeos.
443 #endif
446 void ProtocolHandlerRegistry::InitProtocolSettings() {
447 DCHECK_CURRENTLY_ON(BrowserThread::UI);
449 // Any further default additions to the table will get rejected from now on.
450 is_loaded_ = true;
451 is_loading_ = true;
453 PrefService* prefs = user_prefs::UserPrefs::Get(context_);
454 if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) {
455 if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) {
456 Enable();
457 } else {
458 Disable();
462 RegisterProtocolHandlersFromPref(prefs::kPolicyRegisteredProtocolHandlers,
463 POLICY);
464 RegisterProtocolHandlersFromPref(prefs::kRegisteredProtocolHandlers, USER);
465 IgnoreProtocolHandlersFromPref(prefs::kPolicyIgnoredProtocolHandlers, POLICY);
466 IgnoreProtocolHandlersFromPref(prefs::kIgnoredProtocolHandlers, USER);
468 is_loading_ = false;
470 // For each default protocol handler, check that we are still registered
471 // with the OS as the default application.
472 if (ShouldRemoveHandlersNotInOS()) {
473 for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
474 p != default_handlers_.end(); ++p) {
475 ProtocolHandler handler = p->second;
476 DefaultClientObserver* observer = delegate_->CreateShellObserver(this);
477 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
478 worker = delegate_->CreateShellWorker(observer, handler.protocol());
479 observer->SetWorker(worker.get());
480 default_client_observers_.push_back(observer);
481 worker->StartCheckIsDefault();
486 int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const {
487 DCHECK_CURRENTLY_ON(BrowserThread::UI);
488 const ProtocolHandler& handler = GetHandlerFor(scheme);
489 if (handler.IsEmpty())
490 return -1;
491 const ProtocolHandlerList* handlers = GetHandlerList(scheme);
492 if (!handlers)
493 return -1;
495 ProtocolHandlerList::const_iterator p;
496 int i;
497 for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) {
498 if (*p == handler)
499 return i;
501 return -1;
504 ProtocolHandlerRegistry::ProtocolHandlerList
505 ProtocolHandlerRegistry::GetHandlersFor(
506 const std::string& scheme) const {
507 DCHECK_CURRENTLY_ON(BrowserThread::UI);
508 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
509 if (p == protocol_handlers_.end()) {
510 return ProtocolHandlerList();
512 return p->second;
515 ProtocolHandlerRegistry::ProtocolHandlerList
516 ProtocolHandlerRegistry::GetIgnoredHandlers() {
517 return ignored_protocol_handlers_;
520 void ProtocolHandlerRegistry::GetRegisteredProtocols(
521 std::vector<std::string>* output) const {
522 DCHECK_CURRENTLY_ON(BrowserThread::UI);
523 ProtocolHandlerMultiMap::const_iterator p;
524 for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
525 if (!p->second.empty())
526 output->push_back(p->first);
530 bool ProtocolHandlerRegistry::CanSchemeBeOverridden(
531 const std::string& scheme) const {
532 DCHECK_CURRENTLY_ON(BrowserThread::UI);
533 const ProtocolHandlerList* handlers = GetHandlerList(scheme);
534 // If we already have a handler for this scheme, we can add more.
535 if (handlers != NULL && !handlers->empty())
536 return true;
537 // Don't override a scheme if it already has an external handler.
538 return !delegate_->IsExternalHandlerRegistered(scheme);
541 bool ProtocolHandlerRegistry::IsRegistered(
542 const ProtocolHandler& handler) const {
543 DCHECK_CURRENTLY_ON(BrowserThread::UI);
544 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
545 if (!handlers) {
546 return false;
548 return std::find(handlers->begin(), handlers->end(), handler) !=
549 handlers->end();
552 bool ProtocolHandlerRegistry::IsRegisteredByUser(
553 const ProtocolHandler& handler) {
554 return HandlerExists(handler, &user_protocol_handlers_);
557 bool ProtocolHandlerRegistry::HasPolicyRegisteredHandler(
558 const std::string& scheme) {
559 return (policy_protocol_handlers_.find(scheme) !=
560 policy_protocol_handlers_.end());
563 bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
564 DCHECK_CURRENTLY_ON(BrowserThread::UI);
565 ProtocolHandlerList::const_iterator i;
566 for (i = ignored_protocol_handlers_.begin();
567 i != ignored_protocol_handlers_.end(); ++i) {
568 if (*i == handler) {
569 return true;
572 return false;
575 bool ProtocolHandlerRegistry::HasRegisteredEquivalent(
576 const ProtocolHandler& handler) const {
577 DCHECK_CURRENTLY_ON(BrowserThread::UI);
578 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
579 if (!handlers) {
580 return false;
582 ProtocolHandlerList::const_iterator i;
583 for (i = handlers->begin(); i != handlers->end(); ++i) {
584 if (handler.IsEquivalent(*i)) {
585 return true;
588 return false;
591 bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
592 const ProtocolHandler& handler) const {
593 DCHECK_CURRENTLY_ON(BrowserThread::UI);
594 ProtocolHandlerList::const_iterator i;
595 for (i = ignored_protocol_handlers_.begin();
596 i != ignored_protocol_handlers_.end(); ++i) {
597 if (handler.IsEquivalent(*i)) {
598 return true;
601 return false;
604 void ProtocolHandlerRegistry::RemoveIgnoredHandler(
605 const ProtocolHandler& handler) {
606 DCHECK_CURRENTLY_ON(BrowserThread::UI);
607 bool should_notify = false;
608 if (HandlerExists(handler, ignored_protocol_handlers_) &&
609 HandlerExists(handler, user_ignored_protocol_handlers_)) {
610 EraseHandler(handler, &user_ignored_protocol_handlers_);
611 Save();
612 if (!HandlerExists(handler, policy_ignored_protocol_handlers_)) {
613 EraseHandler(handler, &ignored_protocol_handlers_);
614 should_notify = true;
617 if (should_notify)
618 NotifyChanged();
621 bool ProtocolHandlerRegistry::IsHandledProtocol(
622 const std::string& scheme) const {
623 DCHECK_CURRENTLY_ON(BrowserThread::UI);
624 return enabled_ && !GetHandlerFor(scheme).IsEmpty();
627 void ProtocolHandlerRegistry::RemoveHandler(
628 const ProtocolHandler& handler) {
629 DCHECK_CURRENTLY_ON(BrowserThread::UI);
630 ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
631 bool erase_success = false;
632 if (HandlerExists(handler, handlers) &&
633 HandlerExists(handler, &user_protocol_handlers_)) {
634 EraseHandler(handler, &user_protocol_handlers_);
635 erase_success = true;
636 if (!HandlerExists(handler, &policy_protocol_handlers_))
637 EraseHandler(handler, &protocol_handlers_);
639 ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol());
640 if (erase_success && q != default_handlers_.end() && q->second == handler) {
641 // Make the new top handler in the list the default.
642 if (!handlers.empty()) {
643 // NOTE We pass a copy because SetDefault() modifies handlers.
644 SetDefault(ProtocolHandler(handlers[0]));
645 } else {
646 BrowserThread::PostTask(
647 BrowserThread::IO, FROM_HERE,
648 base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_,
649 q->second.protocol()));
651 default_handlers_.erase(q);
655 if (erase_success && !IsHandledProtocol(handler.protocol())) {
656 delegate_->DeregisterExternalHandler(handler.protocol());
658 Save();
659 if (erase_success)
660 NotifyChanged();
663 void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) {
664 DCHECK_CURRENTLY_ON(BrowserThread::UI);
665 ProtocolHandler current_default = GetHandlerFor(scheme);
666 if (!current_default.IsEmpty())
667 RemoveHandler(current_default);
670 const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
671 const std::string& scheme) const {
672 DCHECK_CURRENTLY_ON(BrowserThread::UI);
673 return LookupHandler(default_handlers_, scheme);
676 void ProtocolHandlerRegistry::Enable() {
677 DCHECK_CURRENTLY_ON(BrowserThread::UI);
678 if (enabled_) {
679 return;
681 enabled_ = true;
682 BrowserThread::PostTask(
683 BrowserThread::IO,
684 FROM_HERE,
685 base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_));
687 ProtocolHandlerMap::const_iterator p;
688 for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
689 delegate_->RegisterExternalHandler(p->first);
691 Save();
692 NotifyChanged();
695 void ProtocolHandlerRegistry::Disable() {
696 DCHECK_CURRENTLY_ON(BrowserThread::UI);
697 if (!enabled_) {
698 return;
700 enabled_ = false;
701 BrowserThread::PostTask(
702 BrowserThread::IO,
703 FROM_HERE,
704 base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_));
706 ProtocolHandlerMap::const_iterator p;
707 for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
708 delegate_->DeregisterExternalHandler(p->first);
710 Save();
711 NotifyChanged();
714 void ProtocolHandlerRegistry::Shutdown() {
715 DCHECK_CURRENTLY_ON(BrowserThread::UI);
716 delegate_.reset(NULL);
717 // We free these now in case there are any outstanding workers running. If
718 // we didn't free them they could respond to workers and try to update the
719 // protocol handler registry after it was deleted.
720 // Observers remove themselves from this list when they are deleted; so
721 // we delete the last item until none are left in the list.
722 while (!default_client_observers_.empty()) {
723 delete default_client_observers_.back();
727 // static
728 void ProtocolHandlerRegistry::RegisterProfilePrefs(
729 user_prefs::PrefRegistrySyncable* registry) {
730 registry->RegisterListPref(prefs::kRegisteredProtocolHandlers);
731 registry->RegisterListPref(prefs::kIgnoredProtocolHandlers);
732 registry->RegisterListPref(prefs::kPolicyRegisteredProtocolHandlers);
733 registry->RegisterListPref(prefs::kPolicyIgnoredProtocolHandlers);
734 registry->RegisterBooleanPref(prefs::kCustomHandlersEnabled, true);
737 ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
738 DCHECK_CURRENTLY_ON(BrowserThread::UI);
739 DCHECK(default_client_observers_.empty());
742 void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
743 DCHECK_CURRENTLY_ON(BrowserThread::UI);
744 DCHECK(IsRegistered(handler));
745 ProtocolHandlerMultiMap::iterator p =
746 protocol_handlers_.find(handler.protocol());
747 ProtocolHandlerList& list = p->second;
748 list.erase(std::find(list.begin(), list.end(), handler));
749 list.insert(list.begin(), handler);
752 void ProtocolHandlerRegistry::Save() {
753 DCHECK_CURRENTLY_ON(BrowserThread::UI);
754 if (is_loading_) {
755 return;
757 scoped_ptr<base::Value> registered_protocol_handlers(
758 EncodeRegisteredHandlers());
759 scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
760 PrefService* prefs = user_prefs::UserPrefs::Get(context_);
762 prefs->Set(prefs::kRegisteredProtocolHandlers,
763 *registered_protocol_handlers);
764 prefs->Set(prefs::kIgnoredProtocolHandlers,
765 *ignored_protocol_handlers);
766 prefs->SetBoolean(prefs::kCustomHandlersEnabled, enabled_);
769 const ProtocolHandlerRegistry::ProtocolHandlerList*
770 ProtocolHandlerRegistry::GetHandlerList(
771 const std::string& scheme) const {
772 DCHECK_CURRENTLY_ON(BrowserThread::UI);
773 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
774 if (p == protocol_handlers_.end()) {
775 return NULL;
777 return &p->second;
780 void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
781 DCHECK_CURRENTLY_ON(BrowserThread::UI);
782 ProtocolHandlerMap::const_iterator p = default_handlers_.find(
783 handler.protocol());
784 // If we're not loading, and we are setting a default for a new protocol,
785 // register with the OS.
786 if (!is_loading_ && p == default_handlers_.end())
787 delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
788 default_handlers_.erase(handler.protocol());
789 default_handlers_.insert(std::make_pair(handler.protocol(), handler));
790 PromoteHandler(handler);
791 BrowserThread::PostTask(
792 BrowserThread::IO,
793 FROM_HERE,
794 base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
797 void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
798 DCHECK_CURRENTLY_ON(BrowserThread::UI);
799 ProtocolHandlerMultiMap::iterator p =
800 protocol_handlers_.find(handler.protocol());
802 if (p != protocol_handlers_.end()) {
803 p->second.push_back(handler);
804 return;
807 ProtocolHandlerList new_list;
808 new_list.push_back(handler);
809 protocol_handlers_[handler.protocol()] = new_list;
812 base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
813 DCHECK_CURRENTLY_ON(BrowserThread::UI);
814 base::ListValue* protocol_handlers = new base::ListValue();
815 for (ProtocolHandlerMultiMap::iterator i = user_protocol_handlers_.begin();
816 i != user_protocol_handlers_.end();
817 ++i) {
818 for (ProtocolHandlerList::iterator j = i->second.begin();
819 j != i->second.end(); ++j) {
820 base::DictionaryValue* encoded = j->Encode();
821 if (IsDefault(*j)) {
822 encoded->Set("default", new base::FundamentalValue(true));
824 protocol_handlers->Append(encoded);
827 return protocol_handlers;
830 base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
831 DCHECK_CURRENTLY_ON(BrowserThread::UI);
832 base::ListValue* handlers = new base::ListValue();
833 for (ProtocolHandlerList::iterator i =
834 user_ignored_protocol_handlers_.begin();
835 i != user_ignored_protocol_handlers_.end();
836 ++i) {
837 handlers->Append(i->Encode());
839 return handlers;
842 void ProtocolHandlerRegistry::NotifyChanged() {
843 DCHECK_CURRENTLY_ON(BrowserThread::UI);
844 content::NotificationService::current()->Notify(
845 chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
846 content::Source<content::BrowserContext>(context_),
847 content::NotificationService::NoDetails());
850 void ProtocolHandlerRegistry::RegisterProtocolHandler(
851 const ProtocolHandler& handler,
852 const HandlerSource source) {
853 DCHECK_CURRENTLY_ON(BrowserThread::UI);
854 DCHECK(CanSchemeBeOverridden(handler.protocol()));
855 DCHECK(!handler.IsEmpty());
856 ProtocolHandlerMultiMap& map =
857 (source == POLICY) ? policy_protocol_handlers_ : user_protocol_handlers_;
858 ProtocolHandlerList& list = map[handler.protocol()];
859 if (!HandlerExists(handler, list))
860 list.push_back(handler);
861 if (IsRegistered(handler)) {
862 return;
864 if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
865 delegate_->RegisterExternalHandler(handler.protocol());
866 InsertHandler(handler);
869 std::vector<const base::DictionaryValue*>
870 ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
871 DCHECK_CURRENTLY_ON(BrowserThread::UI);
872 std::vector<const base::DictionaryValue*> result;
873 PrefService* prefs = user_prefs::UserPrefs::Get(context_);
874 if (!prefs->HasPrefPath(pref_name)) {
875 return result;
878 const base::ListValue* handlers = prefs->GetList(pref_name);
879 if (handlers) {
880 for (size_t i = 0; i < handlers->GetSize(); ++i) {
881 const base::DictionaryValue* dict;
882 if (!handlers->GetDictionary(i, &dict))
883 continue;
884 if (ProtocolHandler::IsValidDict(dict)) {
885 result.push_back(dict);
889 return result;
892 void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref(
893 const char* pref_name,
894 const HandlerSource source) {
895 std::vector<const base::DictionaryValue*> registered_handlers =
896 GetHandlersFromPref(pref_name);
897 for (std::vector<const base::DictionaryValue*>::const_iterator p =
898 registered_handlers.begin();
899 p != registered_handlers.end();
900 ++p) {
901 ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
902 RegisterProtocolHandler(handler, source);
903 bool is_default = false;
904 if ((*p)->GetBoolean("default", &is_default) && is_default) {
905 SetDefault(handler);
910 void ProtocolHandlerRegistry::IgnoreProtocolHandler(
911 const ProtocolHandler& handler,
912 const HandlerSource source) {
913 DCHECK_CURRENTLY_ON(BrowserThread::UI);
914 ProtocolHandlerList& list = (source == POLICY)
915 ? policy_ignored_protocol_handlers_
916 : user_ignored_protocol_handlers_;
917 if (!HandlerExists(handler, list))
918 list.push_back(handler);
919 if (HandlerExists(handler, ignored_protocol_handlers_))
920 return;
921 ignored_protocol_handlers_.push_back(handler);
924 void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref(
925 const char* pref_name,
926 const HandlerSource source) {
927 std::vector<const base::DictionaryValue*> ignored_handlers =
928 GetHandlersFromPref(pref_name);
929 for (std::vector<const base::DictionaryValue*>::const_iterator p =
930 ignored_handlers.begin();
931 p != ignored_handlers.end();
932 ++p) {
933 IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p), source);
937 bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
938 ProtocolHandlerMultiMap* map) {
939 return HandlerExists(handler, (*map)[handler.protocol()]);
942 bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
943 const ProtocolHandlerList& list) {
944 return std::find(list.begin(), list.end(), handler) != list.end();
947 void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
948 ProtocolHandlerMultiMap* map) {
949 EraseHandler(handler, &(*map)[handler.protocol()]);
952 void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
953 ProtocolHandlerList* list) {
954 list->erase(std::find(list->begin(), list->end(), handler));
957 void ProtocolHandlerRegistry::AddPredefinedHandler(
958 const ProtocolHandler& handler) {
959 DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings.
960 RegisterProtocolHandler(handler, USER);
961 SetDefault(handler);
964 scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
965 ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
966 DCHECK_CURRENTLY_ON(BrowserThread::UI);
967 // this is always created on the UI thread (in profile_io's
968 // InitializeOnUIThread. Any method calls must be done
969 // on the IO thread (this is checked).
970 return scoped_ptr<JobInterceptorFactory>(
971 new JobInterceptorFactory(io_thread_delegate_.get()));