Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / custom_handlers / protocol_handler_registry.cc
blobdbeda49a088579a2bc60211604999f0137ddae93
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/net/chrome_url_request_context.h"
16 #include "chrome/browser/profiles/profile_io_data.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/custom_handlers/protocol_handler.h"
19 #include "chrome/common/pref_names.h"
20 #include "components/user_prefs/pref_registry_syncable.h"
21 #include "content/public/browser/child_process_security_policy.h"
22 #include "grit/generated_resources.h"
23 #include "net/base/network_delegate.h"
24 #include "net/url_request/url_request_redirect_job.h"
25 #include "ui/base/l10n/l10n_util.h"
27 using content::BrowserThread;
28 using content::ChildProcessSecurityPolicy;
30 namespace {
32 const ProtocolHandler& LookupHandler(
33 const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map,
34 const std::string& scheme) {
35 ProtocolHandlerRegistry::ProtocolHandlerMap::const_iterator p =
36 handler_map.find(scheme);
38 if (p != handler_map.end())
39 return p->second;
41 return ProtocolHandler::EmptyProtocolHandler();
44 // If true default protocol handlers will be removed if the OS level
45 // registration for a protocol is no longer Chrome.
46 bool ShouldRemoveHandlersNotInOS() {
47 #if defined(OS_LINUX)
48 // We don't do this on Linux as the OS registration there is not reliable,
49 // and Chrome OS doesn't have any notion of OS registration.
50 // TODO(benwells): When Linux support is more reliable remove this
51 // difference (http://crbug.com/88255).
52 return false;
53 #else
54 return ShellIntegration::CanSetAsDefaultProtocolClient() !=
55 ShellIntegration::SET_DEFAULT_NOT_ALLOWED;
56 #endif
59 } // namespace
61 // IOThreadDelegate ------------------------------------------------------------
63 // IOThreadDelegate is an IO thread specific object. Access to the class should
64 // all be done via the IO thread. The registry living on the UI thread makes
65 // a best effort to update the IO object after local updates are completed.
66 class ProtocolHandlerRegistry::IOThreadDelegate
67 : public base::RefCountedThreadSafe<
68 ProtocolHandlerRegistry::IOThreadDelegate> {
69 public:
71 // Creates a new instance. If |enabled| is true the registry is considered
72 // enabled on the IO thread.
73 explicit IOThreadDelegate(bool enabled);
75 // Returns true if the protocol has a default protocol handler.
76 // Should be called only from the IO thread.
77 bool IsHandledProtocol(const std::string& scheme) const;
79 // Clears the default for the provided protocol.
80 // Should be called only from the IO thread.
81 void ClearDefault(const std::string& scheme);
83 // Makes this ProtocolHandler the default handler for its protocol.
84 // Should be called only from the IO thread.
85 void SetDefault(const ProtocolHandler& handler);
87 // Creates a URL request job for the given request if there is a matching
88 // protocol handler, returns NULL otherwise.
89 net::URLRequestJob* MaybeCreateJob(
90 net::URLRequest* request, net::NetworkDelegate* network_delegate) const;
92 // Indicate that the registry has been enabled in the IO thread's
93 // copy of the data.
94 void Enable() { enabled_ = true; }
96 // Indicate that the registry has been disabled in the IO thread's copy of
97 // the data.
98 void Disable() { enabled_ = false; }
100 private:
101 friend class base::RefCountedThreadSafe<IOThreadDelegate>;
102 virtual ~IOThreadDelegate();
104 // Copy of protocol handlers use only on the IO thread.
105 ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_;
107 // Is the registry enabled on the IO thread.
108 bool enabled_;
110 DISALLOW_COPY_AND_ASSIGN(IOThreadDelegate);
113 ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool)
114 : enabled_(true) {}
115 ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {}
117 bool ProtocolHandlerRegistry::IOThreadDelegate::IsHandledProtocol(
118 const std::string& scheme) const {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120 return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty();
123 void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault(
124 const std::string& scheme) {
125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
126 default_handlers_.erase(scheme);
129 void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault(
130 const ProtocolHandler& handler) {
131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
132 ClearDefault(handler.protocol());
133 default_handlers_.insert(std::make_pair(handler.protocol(), handler));
136 // Create a new job for the supplied |URLRequest| if a default handler
137 // is registered and the associated handler is able to interpret
138 // the url from |request|.
139 net::URLRequestJob* ProtocolHandlerRegistry::IOThreadDelegate::MaybeCreateJob(
140 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143 ProtocolHandler handler = LookupHandler(default_handlers_,
144 request->url().scheme());
145 if (handler.IsEmpty())
146 return NULL;
148 GURL translated_url(handler.TranslateUrl(request->url()));
149 if (!translated_url.is_valid())
150 return NULL;
152 return new net::URLRequestRedirectJob(
153 request, network_delegate, translated_url,
154 net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT);
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 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol(
194 const std::string& scheme) const {
195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
196 return io_thread_delegate_->IsHandledProtocol(scheme) ||
197 job_factory_->IsHandledProtocol(scheme);
200 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledURL(
201 const GURL& url) const {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
203 return (url.is_valid() &&
204 io_thread_delegate_->IsHandledProtocol(url.scheme())) ||
205 job_factory_->IsHandledURL(url);
208 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget(
209 const GURL& location) const {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211 return job_factory_->IsSafeRedirectTarget(location);
214 // DefaultClientObserver ------------------------------------------------------
216 ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver(
217 ProtocolHandlerRegistry* registry)
218 : worker_(NULL),
219 registry_(registry) {
220 DCHECK(registry_);
223 ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() {
224 if (worker_)
225 worker_->ObserverDestroyed();
227 DefaultClientObserverList::iterator iter = std::find(
228 registry_->default_client_observers_.begin(),
229 registry_->default_client_observers_.end(), this);
230 registry_->default_client_observers_.erase(iter);
233 void ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState(
234 ShellIntegration::DefaultWebClientUIState state) {
235 if (worker_) {
236 if (ShouldRemoveHandlersNotInOS() &&
237 (state == ShellIntegration::STATE_NOT_DEFAULT)) {
238 registry_->ClearDefault(worker_->protocol());
240 } else {
241 NOTREACHED();
245 bool ProtocolHandlerRegistry::DefaultClientObserver::
246 IsInteractiveSetDefaultPermitted() {
247 return true;
250 void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker(
251 ShellIntegration::DefaultProtocolClientWorker* worker) {
252 worker_ = worker;
255 bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() {
256 return true;
259 // Delegate --------------------------------------------------------------------
261 ProtocolHandlerRegistry::Delegate::~Delegate() {}
263 void ProtocolHandlerRegistry::Delegate::RegisterExternalHandler(
264 const std::string& protocol) {
265 ChildProcessSecurityPolicy* policy =
266 ChildProcessSecurityPolicy::GetInstance();
267 if (!policy->IsWebSafeScheme(protocol)) {
268 policy->RegisterWebSafeScheme(protocol);
272 void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler(
273 const std::string& protocol) {
276 bool ProtocolHandlerRegistry::Delegate::IsExternalHandlerRegistered(
277 const std::string& protocol) {
278 // NOTE(koz): This function is safe to call from any thread, despite living
279 // in ProfileIOData.
280 return ProfileIOData::IsHandledProtocol(protocol);
283 ShellIntegration::DefaultProtocolClientWorker*
284 ProtocolHandlerRegistry::Delegate::CreateShellWorker(
285 ShellIntegration::DefaultWebClientObserver* observer,
286 const std::string& protocol) {
287 return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol);
290 ProtocolHandlerRegistry::DefaultClientObserver*
291 ProtocolHandlerRegistry::Delegate::CreateShellObserver(
292 ProtocolHandlerRegistry* registry) {
293 return new DefaultClientObserver(registry);
296 void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient(
297 const std::string& protocol, ProtocolHandlerRegistry* registry) {
298 DefaultClientObserver* observer = CreateShellObserver(registry);
299 // The worker pointer is reference counted. While it is running the
300 // message loops of the FILE and UI thread will hold references to it
301 // and it will be automatically freed once all its tasks have finished.
302 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
303 worker = CreateShellWorker(observer, protocol);
304 observer->SetWorker(worker.get());
305 registry->default_client_observers_.push_back(observer);
306 worker->StartSetAsDefault();
309 // ProtocolHandlerRegistry -----------------------------------------------------
311 ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile,
312 Delegate* delegate)
313 : profile_(profile),
314 delegate_(delegate),
315 enabled_(true),
316 is_loading_(false),
317 is_loaded_(false),
318 io_thread_delegate_(new IOThreadDelegate(enabled_)){
321 bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest(
322 const ProtocolHandler& handler) {
323 if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol()))
324 return true;
326 if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler))
327 return true;
329 if (AttemptReplace(handler))
330 return true;
332 return false;
335 void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler(
336 const ProtocolHandler& handler) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338 RegisterProtocolHandler(handler);
339 SetDefault(handler);
340 Save();
341 NotifyChanged();
344 void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
345 const ProtocolHandler& handler) {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347 RegisterProtocolHandler(handler);
348 Save();
349 NotifyChanged();
352 void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
353 const ProtocolHandler& handler) {
354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355 IgnoreProtocolHandler(handler);
356 Save();
357 NotifyChanged();
360 bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) {
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
362 ProtocolHandler old_default = GetHandlerFor(handler.protocol());
363 bool make_new_handler_default = handler.IsSameOrigin(old_default);
364 ProtocolHandlerList to_replace(GetReplacedHandlers(handler));
365 if (to_replace.empty())
366 return false;
367 for (ProtocolHandlerList::iterator p = to_replace.begin();
368 p != to_replace.end(); ++p) {
369 RemoveHandler(*p);
371 if (make_new_handler_default) {
372 OnAcceptRegisterProtocolHandler(handler);
373 } else {
374 InsertHandler(handler);
375 NotifyChanged();
377 return true;
380 ProtocolHandlerRegistry::ProtocolHandlerList
381 ProtocolHandlerRegistry::GetReplacedHandlers(
382 const ProtocolHandler& handler) const {
383 ProtocolHandlerList replaced_handlers;
384 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
385 if (!handlers)
386 return replaced_handlers;
387 for (ProtocolHandlerList::const_iterator p = handlers->begin();
388 p != handlers->end(); p++) {
389 if (handler.IsSameOrigin(*p)) {
390 replaced_handlers.push_back(*p);
393 return replaced_handlers;
396 void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399 default_handlers_.erase(scheme);
400 BrowserThread::PostTask(
401 BrowserThread::IO,
402 FROM_HERE,
403 base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_, scheme));
404 Save();
405 NotifyChanged();
408 bool ProtocolHandlerRegistry::IsDefault(
409 const ProtocolHandler& handler) const {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411 return GetHandlerFor(handler.protocol()) == handler;
414 void ProtocolHandlerRegistry::InstallDefaultsForChromeOS() {
415 #if defined(OS_CHROMEOS)
416 // Only chromeos has default protocol handlers at this point.
417 AddPredefinedHandler(
418 ProtocolHandler::CreateProtocolHandler(
419 "mailto",
420 GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_MAILTO_HANDLER_URL)),
421 l10n_util::GetStringUTF16(IDS_GOOGLE_MAILTO_HANDLER_NAME)));
422 AddPredefinedHandler(
423 ProtocolHandler::CreateProtocolHandler(
424 "webcal",
425 GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL)),
426 l10n_util::GetStringUTF16(IDS_GOOGLE_WEBCAL_HANDLER_NAME)));
427 #else
428 NOTREACHED(); // this method should only ever be called in chromeos.
429 #endif
432 void ProtocolHandlerRegistry::InitProtocolSettings() {
433 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
435 // Any further default additions to the table will get rejected from now on.
436 is_loaded_ = true;
437 is_loading_ = true;
439 PrefService* prefs = profile_->GetPrefs();
440 if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) {
441 if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) {
442 Enable();
443 } else {
444 Disable();
447 std::vector<const base::DictionaryValue*> registered_handlers =
448 GetHandlersFromPref(prefs::kRegisteredProtocolHandlers);
449 for (std::vector<const base::DictionaryValue*>::const_iterator p =
450 registered_handlers.begin();
451 p != registered_handlers.end(); ++p) {
452 ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
453 RegisterProtocolHandler(handler);
454 bool is_default = false;
455 if ((*p)->GetBoolean("default", &is_default) && is_default) {
456 SetDefault(handler);
459 std::vector<const base::DictionaryValue*> ignored_handlers =
460 GetHandlersFromPref(prefs::kIgnoredProtocolHandlers);
461 for (std::vector<const base::DictionaryValue*>::const_iterator p =
462 ignored_handlers.begin();
463 p != ignored_handlers.end(); ++p) {
464 IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p));
466 is_loading_ = false;
468 // For each default protocol handler, check that we are still registered
469 // with the OS as the default application.
470 if (ShouldRemoveHandlersNotInOS()) {
471 for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
472 p != default_handlers_.end(); ++p) {
473 ProtocolHandler handler = p->second;
474 DefaultClientObserver* observer = delegate_->CreateShellObserver(this);
475 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
476 worker = delegate_->CreateShellWorker(observer, handler.protocol());
477 observer->SetWorker(worker.get());
478 default_client_observers_.push_back(observer);
479 worker->StartCheckIsDefault();
484 int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const {
485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
486 const ProtocolHandler& handler = GetHandlerFor(scheme);
487 if (handler.IsEmpty())
488 return -1;
489 const ProtocolHandlerList* handlers = GetHandlerList(scheme);
490 if (!handlers)
491 return -1;
493 ProtocolHandlerList::const_iterator p;
494 int i;
495 for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) {
496 if (*p == handler)
497 return i;
499 return -1;
502 ProtocolHandlerRegistry::ProtocolHandlerList
503 ProtocolHandlerRegistry::GetHandlersFor(
504 const std::string& scheme) const {
505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
506 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
507 if (p == protocol_handlers_.end()) {
508 return ProtocolHandlerList();
510 return p->second;
513 ProtocolHandlerRegistry::ProtocolHandlerList
514 ProtocolHandlerRegistry::GetIgnoredHandlers() {
515 return ignored_protocol_handlers_;
518 void ProtocolHandlerRegistry::GetRegisteredProtocols(
519 std::vector<std::string>* output) const {
520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
521 ProtocolHandlerMultiMap::const_iterator p;
522 for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
523 if (!p->second.empty())
524 output->push_back(p->first);
528 bool ProtocolHandlerRegistry::CanSchemeBeOverridden(
529 const std::string& scheme) const {
530 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
531 const ProtocolHandlerList* handlers = GetHandlerList(scheme);
532 // If we already have a handler for this scheme, we can add more.
533 if (handlers != NULL && !handlers->empty())
534 return true;
535 // Don't override a scheme if it already has an external handler.
536 return !delegate_->IsExternalHandlerRegistered(scheme);
539 bool ProtocolHandlerRegistry::IsRegistered(
540 const ProtocolHandler& handler) const {
541 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
542 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
543 if (!handlers) {
544 return false;
546 return std::find(handlers->begin(), handlers->end(), handler) !=
547 handlers->end();
550 bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
552 ProtocolHandlerList::const_iterator i;
553 for (i = ignored_protocol_handlers_.begin();
554 i != ignored_protocol_handlers_.end(); ++i) {
555 if (*i == handler) {
556 return true;
559 return false;
562 bool ProtocolHandlerRegistry::HasRegisteredEquivalent(
563 const ProtocolHandler& handler) const {
564 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
566 if (!handlers) {
567 return false;
569 ProtocolHandlerList::const_iterator i;
570 for (i = handlers->begin(); i != handlers->end(); ++i) {
571 if (handler.IsEquivalent(*i)) {
572 return true;
575 return false;
578 bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
579 const ProtocolHandler& handler) const {
580 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
581 ProtocolHandlerList::const_iterator i;
582 for (i = ignored_protocol_handlers_.begin();
583 i != ignored_protocol_handlers_.end(); ++i) {
584 if (handler.IsEquivalent(*i)) {
585 return true;
588 return false;
591 void ProtocolHandlerRegistry::RemoveIgnoredHandler(
592 const ProtocolHandler& handler) {
593 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
594 bool should_notify = false;
595 ProtocolHandlerList::iterator p = std::find(
596 ignored_protocol_handlers_.begin(), ignored_protocol_handlers_.end(),
597 handler);
598 if (p != ignored_protocol_handlers_.end()) {
599 ignored_protocol_handlers_.erase(p);
600 Save();
601 should_notify = true;
603 if (should_notify)
604 NotifyChanged();
607 bool ProtocolHandlerRegistry::IsHandledProtocol(
608 const std::string& scheme) const {
609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
610 return enabled_ && !GetHandlerFor(scheme).IsEmpty();
613 void ProtocolHandlerRegistry::RemoveHandler(
614 const ProtocolHandler& handler) {
615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
616 ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
617 ProtocolHandlerList::iterator p =
618 std::find(handlers.begin(), handlers.end(), handler);
619 if (p != handlers.end()) {
620 handlers.erase(p);
622 ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol());
623 if (q != default_handlers_.end() && q->second == handler) {
624 // Make the new top handler in the list the default.
625 if (!handlers.empty()) {
626 // NOTE We pass a copy because SetDefault() modifies handlers.
627 SetDefault(ProtocolHandler(handlers[0]));
628 } else {
629 BrowserThread::PostTask(
630 BrowserThread::IO, FROM_HERE,
631 base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_,
632 q->second.protocol()));
634 default_handlers_.erase(q);
638 if (!IsHandledProtocol(handler.protocol())) {
639 delegate_->DeregisterExternalHandler(handler.protocol());
641 Save();
642 NotifyChanged();
645 void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) {
646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
647 ProtocolHandler current_default = GetHandlerFor(scheme);
648 if (!current_default.IsEmpty())
649 RemoveHandler(current_default);
652 const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
653 const std::string& scheme) const {
654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655 return LookupHandler(default_handlers_, scheme);
658 void ProtocolHandlerRegistry::Enable() {
659 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
660 if (enabled_) {
661 return;
663 enabled_ = true;
664 BrowserThread::PostTask(
665 BrowserThread::IO,
666 FROM_HERE,
667 base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_));
669 ProtocolHandlerMap::const_iterator p;
670 for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
671 delegate_->RegisterExternalHandler(p->first);
673 Save();
674 NotifyChanged();
677 void ProtocolHandlerRegistry::Disable() {
678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
679 if (!enabled_) {
680 return;
682 enabled_ = false;
683 BrowserThread::PostTask(
684 BrowserThread::IO,
685 FROM_HERE,
686 base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_));
688 ProtocolHandlerMap::const_iterator p;
689 for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
690 delegate_->DeregisterExternalHandler(p->first);
692 Save();
693 NotifyChanged();
696 void ProtocolHandlerRegistry::Shutdown() {
697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
698 delegate_.reset(NULL);
699 // We free these now in case there are any outstanding workers running. If
700 // we didn't free them they could respond to workers and try to update the
701 // protocol handler registry after it was deleted.
702 // Observers remove themselves from this list when they are deleted; so
703 // we delete the last item until none are left in the list.
704 while (!default_client_observers_.empty()) {
705 delete default_client_observers_.back();
709 // static
710 void ProtocolHandlerRegistry::RegisterProfilePrefs(
711 user_prefs::PrefRegistrySyncable* registry) {
712 registry->RegisterListPref(prefs::kRegisteredProtocolHandlers,
713 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
714 registry->RegisterListPref(prefs::kIgnoredProtocolHandlers,
715 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
716 registry->RegisterBooleanPref(
717 prefs::kCustomHandlersEnabled,
718 true,
719 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
722 ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
723 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
724 DCHECK(default_client_observers_.empty());
727 void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
729 DCHECK(IsRegistered(handler));
730 ProtocolHandlerMultiMap::iterator p =
731 protocol_handlers_.find(handler.protocol());
732 ProtocolHandlerList& list = p->second;
733 list.erase(std::find(list.begin(), list.end(), handler));
734 list.insert(list.begin(), handler);
737 void ProtocolHandlerRegistry::Save() {
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739 if (is_loading_) {
740 return;
742 scoped_ptr<base::Value> registered_protocol_handlers(
743 EncodeRegisteredHandlers());
744 scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
745 scoped_ptr<base::Value> enabled(base::Value::CreateBooleanValue(enabled_));
746 profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers,
747 *registered_protocol_handlers);
748 profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers,
749 *ignored_protocol_handlers);
750 profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled);
753 const ProtocolHandlerRegistry::ProtocolHandlerList*
754 ProtocolHandlerRegistry::GetHandlerList(
755 const std::string& scheme) const {
756 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
757 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
758 if (p == protocol_handlers_.end()) {
759 return NULL;
761 return &p->second;
764 void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
765 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
766 ProtocolHandlerMap::const_iterator p = default_handlers_.find(
767 handler.protocol());
768 // If we're not loading, and we are setting a default for a new protocol,
769 // register with the OS.
770 if (!is_loading_ && p == default_handlers_.end())
771 delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
772 default_handlers_.erase(handler.protocol());
773 default_handlers_.insert(std::make_pair(handler.protocol(), handler));
774 PromoteHandler(handler);
775 BrowserThread::PostTask(
776 BrowserThread::IO,
777 FROM_HERE,
778 base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
781 void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
782 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
783 ProtocolHandlerMultiMap::iterator p =
784 protocol_handlers_.find(handler.protocol());
786 if (p != protocol_handlers_.end()) {
787 p->second.push_back(handler);
788 return;
791 ProtocolHandlerList new_list;
792 new_list.push_back(handler);
793 protocol_handlers_[handler.protocol()] = new_list;
796 base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
797 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
798 base::ListValue* protocol_handlers = new base::ListValue();
799 for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin();
800 i != protocol_handlers_.end(); ++i) {
801 for (ProtocolHandlerList::iterator j = i->second.begin();
802 j != i->second.end(); ++j) {
803 base::DictionaryValue* encoded = j->Encode();
804 if (IsDefault(*j)) {
805 encoded->Set("default", base::Value::CreateBooleanValue(true));
807 protocol_handlers->Append(encoded);
810 return protocol_handlers;
813 base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
814 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
815 base::ListValue* handlers = new base::ListValue();
816 for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin();
817 i != ignored_protocol_handlers_.end(); ++i) {
818 handlers->Append(i->Encode());
820 return handlers;
823 void ProtocolHandlerRegistry::NotifyChanged() {
824 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
825 content::NotificationService::current()->Notify(
826 chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
827 content::Source<Profile>(profile_),
828 content::NotificationService::NoDetails());
831 void ProtocolHandlerRegistry::RegisterProtocolHandler(
832 const ProtocolHandler& handler) {
833 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
834 DCHECK(CanSchemeBeOverridden(handler.protocol()));
835 DCHECK(!handler.IsEmpty());
836 if (IsRegistered(handler)) {
837 return;
839 if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
840 delegate_->RegisterExternalHandler(handler.protocol());
841 InsertHandler(handler);
844 std::vector<const base::DictionaryValue*>
845 ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
846 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
847 std::vector<const base::DictionaryValue*> result;
848 PrefService* prefs = profile_->GetPrefs();
849 if (!prefs->HasPrefPath(pref_name)) {
850 return result;
853 const base::ListValue* handlers = prefs->GetList(pref_name);
854 if (handlers) {
855 for (size_t i = 0; i < handlers->GetSize(); ++i) {
856 const base::DictionaryValue* dict;
857 if (!handlers->GetDictionary(i, &dict))
858 continue;
859 if (ProtocolHandler::IsValidDict(dict)) {
860 result.push_back(dict);
864 return result;
867 void ProtocolHandlerRegistry::IgnoreProtocolHandler(
868 const ProtocolHandler& handler) {
869 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
870 ignored_protocol_handlers_.push_back(handler);
873 void ProtocolHandlerRegistry::AddPredefinedHandler(
874 const ProtocolHandler& handler) {
875 DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings.
876 RegisterProtocolHandler(handler);
877 SetDefault(handler);
880 scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
881 ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
882 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
883 // this is always created on the UI thread (in profile_io's
884 // InitializeOnUIThread. Any method calls must be done
885 // on the IO thread (this is checked).
886 return scoped_ptr<JobInterceptorFactory>(
887 new JobInterceptorFactory(io_thread_delegate_.get()));