- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / custom_handlers / protocol_handler_registry.cc
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.
4
5 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
6
7 #include <utility>
8
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"
26
27 using content::BrowserThread;
28 using content::ChildProcessSecurityPolicy;
29
30 namespace {
31
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);
37
38   if (p != handler_map.end())
39     return p->second;
40
41   return ProtocolHandler::EmptyProtocolHandler();
42 }
43
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
57 }
58
59 }  // namespace
60
61 // IOThreadDelegate ------------------------------------------------------------
62
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:
70
71   // Creates a new instance. If |enabled| is true the registry is considered
72   // enabled on the IO thread.
73   explicit IOThreadDelegate(bool enabled);
74
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;
78
79   // Clears the default for the provided protocol.
80   // Should be called only from the IO thread.
81   void ClearDefault(const std::string& scheme);
82
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);
86
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;
91
92   // Indicate that the registry has been enabled in the IO thread's
93   // copy of the data.
94   void Enable() { enabled_ = true; }
95
96   // Indicate that the registry has been disabled in the IO thread's copy of
97   // the data.
98   void Disable() { enabled_ = false; }
99
100  private:
101   friend class base::RefCountedThreadSafe<IOThreadDelegate>;
102   virtual ~IOThreadDelegate();
103
104   // Copy of protocol handlers use only on the IO thread.
105   ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_;
106
107   // Is the registry enabled on the IO thread.
108   bool enabled_;
109
110   DISALLOW_COPY_AND_ASSIGN(IOThreadDelegate);
111 };
112
113 ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool)
114     : enabled_(true) {}
115 ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {}
116
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();
121 }
122
123 void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault(
124     const std::string& scheme) {
125   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
126   default_handlers_.erase(scheme);
127 }
128
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));
134 }
135
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));
142
143   ProtocolHandler handler = LookupHandler(default_handlers_,
144                                           request->url().scheme());
145   if (handler.IsEmpty())
146     return NULL;
147
148   GURL translated_url(handler.TranslateUrl(request->url()));
149   if (!translated_url.is_valid())
150     return NULL;
151
152   return new net::URLRequestRedirectJob(
153       request, network_delegate, translated_url,
154       net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT);
155 }
156
157 // JobInterceptorFactory -------------------------------------------------------
158
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();
168 }
169
170 ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() {
171 }
172
173 void ProtocolHandlerRegistry::JobInterceptorFactory::Chain(
174     scoped_ptr<net::URLRequestJobFactory> job_factory) {
175   job_factory_ = job_factory.Pass();
176 }
177
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);
191 }
192
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);
198 }
199
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);
206 }
207
208 bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget(
209     const GURL& location) const {
210   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211   return job_factory_->IsSafeRedirectTarget(location);
212 }
213
214 // DefaultClientObserver ------------------------------------------------------
215
216 ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver(
217     ProtocolHandlerRegistry* registry)
218     : worker_(NULL),
219       registry_(registry) {
220   DCHECK(registry_);
221 }
222
223 ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() {
224   if (worker_)
225     worker_->ObserverDestroyed();
226
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);
231 }
232
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());
239     }
240   } else {
241     NOTREACHED();
242   }
243 }
244
245 bool ProtocolHandlerRegistry::DefaultClientObserver::
246     IsInteractiveSetDefaultPermitted() {
247   return true;
248 }
249
250 void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker(
251     ShellIntegration::DefaultProtocolClientWorker* worker) {
252   worker_ = worker;
253 }
254
255 bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() {
256   return true;
257 }
258
259 // Delegate --------------------------------------------------------------------
260
261 ProtocolHandlerRegistry::Delegate::~Delegate() {}
262
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);
269   }
270 }
271
272 void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler(
273     const std::string& protocol) {
274 }
275
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);
281 }
282
283 ShellIntegration::DefaultProtocolClientWorker*
284 ProtocolHandlerRegistry::Delegate::CreateShellWorker(
285     ShellIntegration::DefaultWebClientObserver* observer,
286     const std::string& protocol) {
287   return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol);
288 }
289
290 ProtocolHandlerRegistry::DefaultClientObserver*
291 ProtocolHandlerRegistry::Delegate::CreateShellObserver(
292     ProtocolHandlerRegistry* registry) {
293   return new DefaultClientObserver(registry);
294 }
295
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();
307 }
308
309 // ProtocolHandlerRegistry -----------------------------------------------------
310
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_)){
319 }
320
321 bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest(
322     const ProtocolHandler& handler) {
323   if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol()))
324     return true;
325
326   if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler))
327     return true;
328
329   if (AttemptReplace(handler))
330     return true;
331
332   return false;
333 }
334
335 void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler(
336     const ProtocolHandler& handler) {
337   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338   RegisterProtocolHandler(handler);
339   SetDefault(handler);
340   Save();
341   NotifyChanged();
342 }
343
344 void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
345     const ProtocolHandler& handler) {
346   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347   RegisterProtocolHandler(handler);
348   Save();
349   NotifyChanged();
350 }
351
352 void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
353     const ProtocolHandler& handler) {
354   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355   IgnoreProtocolHandler(handler);
356   Save();
357   NotifyChanged();
358 }
359
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);
370   }
371   if (make_new_handler_default) {
372     OnAcceptRegisterProtocolHandler(handler);
373   } else {
374     InsertHandler(handler);
375     NotifyChanged();
376   }
377   return true;
378 }
379
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);
391     }
392   }
393   return replaced_handlers;
394 }
395
396 void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) {
397   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
398
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();
406 }
407
408 bool ProtocolHandlerRegistry::IsDefault(
409     const ProtocolHandler& handler) const {
410   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411   return GetHandlerFor(handler.protocol()) == handler;
412 }
413
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
430 }
431
432 void ProtocolHandlerRegistry::InitProtocolSettings() {
433   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
434
435   // Any further default additions to the table will get rejected from now on.
436   is_loaded_ = true;
437   is_loading_ = true;
438
439   PrefService* prefs = profile_->GetPrefs();
440   if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) {
441     if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) {
442       Enable();
443     } else {
444       Disable();
445     }
446   }
447   std::vector<const DictionaryValue*> registered_handlers =
448       GetHandlersFromPref(prefs::kRegisteredProtocolHandlers);
449   for (std::vector<const 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);
457     }
458   }
459   std::vector<const DictionaryValue*> ignored_handlers =
460     GetHandlersFromPref(prefs::kIgnoredProtocolHandlers);
461   for (std::vector<const DictionaryValue*>::const_iterator p =
462        ignored_handlers.begin();
463        p != ignored_handlers.end(); ++p) {
464     IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p));
465   }
466   is_loading_ = false;
467
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();
480     }
481   }
482 }
483
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;
492
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;
498   }
499   return -1;
500 }
501
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();
509   }
510   return p->second;
511 }
512
513 ProtocolHandlerRegistry::ProtocolHandlerList
514 ProtocolHandlerRegistry::GetIgnoredHandlers() {
515   return ignored_protocol_handlers_;
516 }
517
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);
525   }
526 }
527
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);
537 }
538
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;
545   }
546   return std::find(handlers->begin(), handlers->end(), handler) !=
547       handlers->end();
548 }
549
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;
557     }
558   }
559   return false;
560 }
561
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;
568   }
569   ProtocolHandlerList::const_iterator i;
570   for (i = handlers->begin(); i != handlers->end(); ++i) {
571     if (handler.IsEquivalent(*i)) {
572       return true;
573     }
574   }
575   return false;
576 }
577
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;
586     }
587   }
588   return false;
589 }
590
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;
602   }
603   if (should_notify)
604     NotifyChanged();
605 }
606
607 bool ProtocolHandlerRegistry::IsHandledProtocol(
608     const std::string& scheme) const {
609   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
610   return enabled_ && !GetHandlerFor(scheme).IsEmpty();
611 }
612
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);
621   }
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()));
633
634       default_handlers_.erase(q);
635     }
636   }
637
638   if (!IsHandledProtocol(handler.protocol())) {
639     delegate_->DeregisterExternalHandler(handler.protocol());
640   }
641   Save();
642   NotifyChanged();
643 }
644
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);
650 }
651
652 const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
653     const std::string& scheme) const {
654   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655   return LookupHandler(default_handlers_, scheme);
656 }
657
658 void ProtocolHandlerRegistry::Enable() {
659   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
660   if (enabled_) {
661     return;
662   }
663   enabled_ = true;
664   BrowserThread::PostTask(
665       BrowserThread::IO,
666       FROM_HERE,
667       base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_));
668
669   ProtocolHandlerMap::const_iterator p;
670   for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
671     delegate_->RegisterExternalHandler(p->first);
672   }
673   Save();
674   NotifyChanged();
675 }
676
677 void ProtocolHandlerRegistry::Disable() {
678   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
679   if (!enabled_) {
680     return;
681   }
682   enabled_ = false;
683   BrowserThread::PostTask(
684       BrowserThread::IO,
685       FROM_HERE,
686       base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_));
687
688   ProtocolHandlerMap::const_iterator p;
689   for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
690     delegate_->DeregisterExternalHandler(p->first);
691   }
692   Save();
693   NotifyChanged();
694 }
695
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();
706   }
707 }
708
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);
720 }
721
722 ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
723   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
724   DCHECK(default_client_observers_.empty());
725 }
726
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);
735 }
736
737 void ProtocolHandlerRegistry::Save() {
738   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739   if (is_loading_) {
740     return;
741   }
742   scoped_ptr<Value> registered_protocol_handlers(EncodeRegisteredHandlers());
743   scoped_ptr<Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
744   scoped_ptr<Value> enabled(Value::CreateBooleanValue(enabled_));
745   profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers,
746       *registered_protocol_handlers);
747   profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers,
748       *ignored_protocol_handlers);
749   profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled);
750 }
751
752 const ProtocolHandlerRegistry::ProtocolHandlerList*
753 ProtocolHandlerRegistry::GetHandlerList(
754     const std::string& scheme) const {
755   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
756   ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
757   if (p == protocol_handlers_.end()) {
758     return NULL;
759   }
760   return &p->second;
761 }
762
763 void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
764   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
765   ProtocolHandlerMap::const_iterator p = default_handlers_.find(
766       handler.protocol());
767   // If we're not loading, and we are setting a default for a new protocol,
768   // register with the OS.
769   if (!is_loading_ && p == default_handlers_.end())
770       delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
771   default_handlers_.erase(handler.protocol());
772   default_handlers_.insert(std::make_pair(handler.protocol(), handler));
773   PromoteHandler(handler);
774   BrowserThread::PostTask(
775       BrowserThread::IO,
776       FROM_HERE,
777       base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
778 }
779
780 void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
781   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
782   ProtocolHandlerMultiMap::iterator p =
783       protocol_handlers_.find(handler.protocol());
784
785   if (p != protocol_handlers_.end()) {
786     p->second.push_back(handler);
787     return;
788   }
789
790   ProtocolHandlerList new_list;
791   new_list.push_back(handler);
792   protocol_handlers_[handler.protocol()] = new_list;
793 }
794
795 Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
796   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
797   ListValue* protocol_handlers = new ListValue();
798   for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin();
799        i != protocol_handlers_.end(); ++i) {
800     for (ProtocolHandlerList::iterator j = i->second.begin();
801          j != i->second.end(); ++j) {
802       DictionaryValue* encoded = j->Encode();
803       if (IsDefault(*j)) {
804         encoded->Set("default", Value::CreateBooleanValue(true));
805       }
806       protocol_handlers->Append(encoded);
807     }
808   }
809   return protocol_handlers;
810 }
811
812 Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
813   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
814   ListValue* handlers = new ListValue();
815   for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin();
816        i != ignored_protocol_handlers_.end(); ++i) {
817     handlers->Append(i->Encode());
818   }
819   return handlers;
820 }
821
822 void ProtocolHandlerRegistry::NotifyChanged() {
823   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
824   content::NotificationService::current()->Notify(
825       chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
826       content::Source<Profile>(profile_),
827       content::NotificationService::NoDetails());
828 }
829
830 void ProtocolHandlerRegistry::RegisterProtocolHandler(
831     const ProtocolHandler& handler) {
832   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
833   DCHECK(CanSchemeBeOverridden(handler.protocol()));
834   DCHECK(!handler.IsEmpty());
835   if (IsRegistered(handler)) {
836     return;
837   }
838   if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
839     delegate_->RegisterExternalHandler(handler.protocol());
840   InsertHandler(handler);
841 }
842
843 std::vector<const DictionaryValue*>
844 ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
845   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
846   std::vector<const DictionaryValue*> result;
847   PrefService* prefs = profile_->GetPrefs();
848   if (!prefs->HasPrefPath(pref_name)) {
849     return result;
850   }
851
852   const ListValue* handlers = prefs->GetList(pref_name);
853   if (handlers) {
854     for (size_t i = 0; i < handlers->GetSize(); ++i) {
855       const DictionaryValue* dict;
856       if (!handlers->GetDictionary(i, &dict))
857         continue;
858       if (ProtocolHandler::IsValidDict(dict)) {
859         result.push_back(dict);
860       }
861     }
862   }
863   return result;
864 }
865
866 void ProtocolHandlerRegistry::IgnoreProtocolHandler(
867     const ProtocolHandler& handler) {
868   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
869   ignored_protocol_handlers_.push_back(handler);
870 }
871
872 void ProtocolHandlerRegistry::AddPredefinedHandler(
873     const ProtocolHandler& handler) {
874   DCHECK(!is_loaded_);  // Must be called prior InitProtocolSettings.
875   RegisterProtocolHandler(handler);
876   SetDefault(handler);
877 }
878
879 scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
880 ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
881   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
882   // this is always created on the UI thread (in profile_io's
883   // InitializeOnUIThread. Any method calls must be done
884   // on the IO thread (this is checked).
885   return scoped_ptr<JobInterceptorFactory>(
886       new JobInterceptorFactory(io_thread_delegate_.get()));
887 }