Re-land: C++ readability review
[chromium-blink-merge.git] / chrome / browser / custom_handlers / protocol_handler_registry.cc
blob99bfd7cf21ad588f51eb47b560f3d8130fd36044
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(BrowserThread::CurrentlyOn(BrowserThread::IO));
119 return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty();
122 void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault(
123 const std::string& scheme) {
124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
125 default_handlers_.erase(scheme);
128 void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault(
129 const ProtocolHandler& handler) {
130 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 RegisterProtocolHandler(handler, USER);
355 SetDefault(handler);
356 Save();
357 NotifyChanged();
360 void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
361 const ProtocolHandler& handler) {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363 RegisterProtocolHandler(handler, USER);
364 Save();
365 NotifyChanged();
368 void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
369 const ProtocolHandler& handler) {
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
371 IgnoreProtocolHandler(handler, USER);
372 Save();
373 NotifyChanged();
376 bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) {
377 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::UI));
624 return enabled_ && !GetHandlerFor(scheme).IsEmpty();
627 void ProtocolHandlerRegistry::RemoveHandler(
628 const ProtocolHandler& handler) {
629 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::UI));
673 return LookupHandler(default_handlers_, scheme);
676 void ProtocolHandlerRegistry::Enable() {
677 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
732 registry->RegisterListPref(prefs::kIgnoredProtocolHandlers,
733 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
734 registry->RegisterListPref(prefs::kPolicyRegisteredProtocolHandlers,
735 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
736 registry->RegisterListPref(prefs::kPolicyIgnoredProtocolHandlers,
737 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
738 registry->RegisterBooleanPref(
739 prefs::kCustomHandlersEnabled,
740 true,
741 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
744 ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
745 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
746 DCHECK(default_client_observers_.empty());
749 void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
750 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
751 DCHECK(IsRegistered(handler));
752 ProtocolHandlerMultiMap::iterator p =
753 protocol_handlers_.find(handler.protocol());
754 ProtocolHandlerList& list = p->second;
755 list.erase(std::find(list.begin(), list.end(), handler));
756 list.insert(list.begin(), handler);
759 void ProtocolHandlerRegistry::Save() {
760 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
761 if (is_loading_) {
762 return;
764 scoped_ptr<base::Value> registered_protocol_handlers(
765 EncodeRegisteredHandlers());
766 scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
767 PrefService* prefs = user_prefs::UserPrefs::Get(context_);
769 prefs->Set(prefs::kRegisteredProtocolHandlers,
770 *registered_protocol_handlers);
771 prefs->Set(prefs::kIgnoredProtocolHandlers,
772 *ignored_protocol_handlers);
773 prefs->SetBoolean(prefs::kCustomHandlersEnabled, enabled_);
776 const ProtocolHandlerRegistry::ProtocolHandlerList*
777 ProtocolHandlerRegistry::GetHandlerList(
778 const std::string& scheme) const {
779 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
780 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
781 if (p == protocol_handlers_.end()) {
782 return NULL;
784 return &p->second;
787 void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
788 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
789 ProtocolHandlerMap::const_iterator p = default_handlers_.find(
790 handler.protocol());
791 // If we're not loading, and we are setting a default for a new protocol,
792 // register with the OS.
793 if (!is_loading_ && p == default_handlers_.end())
794 delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
795 default_handlers_.erase(handler.protocol());
796 default_handlers_.insert(std::make_pair(handler.protocol(), handler));
797 PromoteHandler(handler);
798 BrowserThread::PostTask(
799 BrowserThread::IO,
800 FROM_HERE,
801 base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
804 void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
805 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
806 ProtocolHandlerMultiMap::iterator p =
807 protocol_handlers_.find(handler.protocol());
809 if (p != protocol_handlers_.end()) {
810 p->second.push_back(handler);
811 return;
814 ProtocolHandlerList new_list;
815 new_list.push_back(handler);
816 protocol_handlers_[handler.protocol()] = new_list;
819 base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
820 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
821 base::ListValue* protocol_handlers = new base::ListValue();
822 for (ProtocolHandlerMultiMap::iterator i = user_protocol_handlers_.begin();
823 i != user_protocol_handlers_.end();
824 ++i) {
825 for (ProtocolHandlerList::iterator j = i->second.begin();
826 j != i->second.end(); ++j) {
827 base::DictionaryValue* encoded = j->Encode();
828 if (IsDefault(*j)) {
829 encoded->Set("default", new base::FundamentalValue(true));
831 protocol_handlers->Append(encoded);
834 return protocol_handlers;
837 base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
838 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
839 base::ListValue* handlers = new base::ListValue();
840 for (ProtocolHandlerList::iterator i =
841 user_ignored_protocol_handlers_.begin();
842 i != user_ignored_protocol_handlers_.end();
843 ++i) {
844 handlers->Append(i->Encode());
846 return handlers;
849 void ProtocolHandlerRegistry::NotifyChanged() {
850 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
851 content::NotificationService::current()->Notify(
852 chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
853 content::Source<content::BrowserContext>(context_),
854 content::NotificationService::NoDetails());
857 void ProtocolHandlerRegistry::RegisterProtocolHandler(
858 const ProtocolHandler& handler,
859 const HandlerSource source) {
860 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
861 DCHECK(CanSchemeBeOverridden(handler.protocol()));
862 DCHECK(!handler.IsEmpty());
863 ProtocolHandlerMultiMap& map =
864 (source == POLICY) ? policy_protocol_handlers_ : user_protocol_handlers_;
865 ProtocolHandlerList& list = map[handler.protocol()];
866 if (!HandlerExists(handler, list))
867 list.push_back(handler);
868 if (IsRegistered(handler)) {
869 return;
871 if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
872 delegate_->RegisterExternalHandler(handler.protocol());
873 InsertHandler(handler);
876 std::vector<const base::DictionaryValue*>
877 ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
879 std::vector<const base::DictionaryValue*> result;
880 PrefService* prefs = user_prefs::UserPrefs::Get(context_);
881 if (!prefs->HasPrefPath(pref_name)) {
882 return result;
885 const base::ListValue* handlers = prefs->GetList(pref_name);
886 if (handlers) {
887 for (size_t i = 0; i < handlers->GetSize(); ++i) {
888 const base::DictionaryValue* dict;
889 if (!handlers->GetDictionary(i, &dict))
890 continue;
891 if (ProtocolHandler::IsValidDict(dict)) {
892 result.push_back(dict);
896 return result;
899 void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref(
900 const char* pref_name,
901 const HandlerSource source) {
902 std::vector<const base::DictionaryValue*> registered_handlers =
903 GetHandlersFromPref(pref_name);
904 for (std::vector<const base::DictionaryValue*>::const_iterator p =
905 registered_handlers.begin();
906 p != registered_handlers.end();
907 ++p) {
908 ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
909 RegisterProtocolHandler(handler, source);
910 bool is_default = false;
911 if ((*p)->GetBoolean("default", &is_default) && is_default) {
912 SetDefault(handler);
917 void ProtocolHandlerRegistry::IgnoreProtocolHandler(
918 const ProtocolHandler& handler,
919 const HandlerSource source) {
920 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
921 ProtocolHandlerList& list = (source == POLICY)
922 ? policy_ignored_protocol_handlers_
923 : user_ignored_protocol_handlers_;
924 if (!HandlerExists(handler, list))
925 list.push_back(handler);
926 if (HandlerExists(handler, ignored_protocol_handlers_))
927 return;
928 ignored_protocol_handlers_.push_back(handler);
931 void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref(
932 const char* pref_name,
933 const HandlerSource source) {
934 std::vector<const base::DictionaryValue*> ignored_handlers =
935 GetHandlersFromPref(pref_name);
936 for (std::vector<const base::DictionaryValue*>::const_iterator p =
937 ignored_handlers.begin();
938 p != ignored_handlers.end();
939 ++p) {
940 IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p), source);
944 bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
945 ProtocolHandlerMultiMap* map) {
946 return HandlerExists(handler, (*map)[handler.protocol()]);
949 bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
950 const ProtocolHandlerList& list) {
951 return std::find(list.begin(), list.end(), handler) != list.end();
954 void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
955 ProtocolHandlerMultiMap* map) {
956 EraseHandler(handler, &(*map)[handler.protocol()]);
959 void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
960 ProtocolHandlerList* list) {
961 list->erase(std::find(list->begin(), list->end(), handler));
964 void ProtocolHandlerRegistry::AddPredefinedHandler(
965 const ProtocolHandler& handler) {
966 DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings.
967 RegisterProtocolHandler(handler, USER);
968 SetDefault(handler);
971 scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
972 ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
973 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
974 // this is always created on the UI thread (in profile_io's
975 // InitializeOnUIThread. Any method calls must be done
976 // on the IO thread (this is checked).
977 return scoped_ptr<JobInterceptorFactory>(
978 new JobInterceptorFactory(io_thread_delegate_.get()));