protocol_handler_registry.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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/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"
25
26using content::BrowserThread;
27using content::ChildProcessSecurityPolicy;
28
29namespace {
30
31const ProtocolHandler& LookupHandler(
32    const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map,
33    const std::string& scheme) {
34  ProtocolHandlerRegistry::ProtocolHandlerMap::const_iterator p =
35      handler_map.find(scheme);
36
37  if (p != handler_map.end())
38    return p->second;
39
40  return ProtocolHandler::EmptyProtocolHandler();
41}
42
43// If true default protocol handlers will be removed if the OS level
44// registration for a protocol is no longer Chrome.
45bool 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
56}
57
58}  // namespace
59
60// IOThreadDelegate ------------------------------------------------------------
61
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.
65class ProtocolHandlerRegistry::IOThreadDelegate
66    : public base::RefCountedThreadSafe<
67          ProtocolHandlerRegistry::IOThreadDelegate> {
68 public:
69
70  // Creates a new instance. If |enabled| is true the registry is considered
71  // enabled on the IO thread.
72  explicit IOThreadDelegate(bool enabled);
73
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;
77
78  // Clears the default for the provided protocol.
79  // Should be called only from the IO thread.
80  void ClearDefault(const std::string& scheme);
81
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);
85
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;
90
91  // Indicate that the registry has been enabled in the IO thread's
92  // copy of the data.
93  void Enable() { enabled_ = true; }
94
95  // Indicate that the registry has been disabled in the IO thread's copy of
96  // the data.
97  void Disable() { enabled_ = false; }
98
99 private:
100  friend class base::RefCountedThreadSafe<IOThreadDelegate>;
101  virtual ~IOThreadDelegate();
102
103  // Copy of protocol handlers use only on the IO thread.
104  ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_;
105
106  // Is the registry enabled on the IO thread.
107  bool enabled_;
108
109  DISALLOW_COPY_AND_ASSIGN(IOThreadDelegate);
110};
111
112ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool)
113    : enabled_(true) {}
114ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {}
115
116bool ProtocolHandlerRegistry::IOThreadDelegate::IsHandledProtocol(
117    const std::string& scheme) const {
118  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
119  return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty();
120}
121
122void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault(
123    const std::string& scheme) {
124  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
125  default_handlers_.erase(scheme);
126}
127
128void 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));
133}
134
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|.
138net::URLRequestJob* ProtocolHandlerRegistry::IOThreadDelegate::MaybeCreateJob(
139    net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
140  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
141
142  ProtocolHandler handler = LookupHandler(default_handlers_,
143                                          request->url().scheme());
144  if (handler.IsEmpty())
145    return NULL;
146
147  GURL translated_url(handler.TranslateUrl(request->url()));
148  if (!translated_url.is_valid())
149    return NULL;
150
151  return new net::URLRequestRedirectJob(
152      request, network_delegate, translated_url,
153      net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
154      "Protocol Handler Registry");
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|.
163ProtocolHandlerRegistry::JobInterceptorFactory::JobInterceptorFactory(
164    IOThreadDelegate* io_thread_delegate)
165    : io_thread_delegate_(io_thread_delegate) {
166  DCHECK(io_thread_delegate_.get());
167  DetachFromThread();
168}
169
170ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() {
171}
172
173void ProtocolHandlerRegistry::JobInterceptorFactory::Chain(
174    scoped_ptr<net::URLRequestJobFactory> job_factory) {
175  job_factory_ = job_factory.Pass();
176}
177
178net::URLRequestJob*
179ProtocolHandlerRegistry::JobInterceptorFactory::
180MaybeCreateJobWithProtocolHandler(
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
193bool 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
200bool 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
208bool 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
216ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver(
217    ProtocolHandlerRegistry* registry)
218    : worker_(NULL),
219      registry_(registry) {
220  DCHECK(registry_);
221}
222
223ProtocolHandlerRegistry::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
233void 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
245bool ProtocolHandlerRegistry::DefaultClientObserver::
246    IsInteractiveSetDefaultPermitted() {
247  return true;
248}
249
250void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker(
251    ShellIntegration::DefaultProtocolClientWorker* worker) {
252  worker_ = worker;
253}
254
255bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() {
256  return true;
257}
258
259// Delegate --------------------------------------------------------------------
260
261ProtocolHandlerRegistry::Delegate::~Delegate() {}
262
263void 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
272void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler(
273    const std::string& protocol) {
274}
275
276bool 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
283ShellIntegration::DefaultProtocolClientWorker*
284ProtocolHandlerRegistry::Delegate::CreateShellWorker(
285    ShellIntegration::DefaultWebClientObserver* observer,
286    const std::string& protocol) {
287  return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol);
288}
289
290ProtocolHandlerRegistry::DefaultClientObserver*
291ProtocolHandlerRegistry::Delegate::CreateShellObserver(
292    ProtocolHandlerRegistry* registry) {
293  return new DefaultClientObserver(registry);
294}
295
296void 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
311ProtocolHandlerRegistry::ProtocolHandlerRegistry(
312    content::BrowserContext* context, Delegate* delegate)
313    : context_(context),
314      delegate_(delegate),
315      enabled_(true),
316      is_loading_(false),
317      is_loaded_(false),
318      io_thread_delegate_(new IOThreadDelegate(enabled_)){
319}
320
321bool 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
335void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler(
336    const ProtocolHandler& handler) {
337  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338  RegisterProtocolHandler(handler, USER);
339  SetDefault(handler);
340  Save();
341  NotifyChanged();
342}
343
344void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
345    const ProtocolHandler& handler) {
346  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347  RegisterProtocolHandler(handler, USER);
348  Save();
349  NotifyChanged();
350}
351
352void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
353    const ProtocolHandler& handler) {
354  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355  IgnoreProtocolHandler(handler, USER);
356  Save();
357  NotifyChanged();
358}
359
360bool 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
380ProtocolHandlerRegistry::ProtocolHandlerList
381ProtocolHandlerRegistry::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
396void 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
408bool ProtocolHandlerRegistry::IsDefault(
409    const ProtocolHandler& handler) const {
410  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411  return GetHandlerFor(handler.protocol()) == handler;
412}
413
414void 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  AddPredefinedHandler(
422      ProtocolHandler::CreateProtocolHandler(
423          "webcal",
424          GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL))));
425#else
426  NOTREACHED();  // this method should only ever be called in chromeos.
427#endif
428}
429
430void ProtocolHandlerRegistry::InitProtocolSettings() {
431  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432
433  // Any further default additions to the table will get rejected from now on.
434  is_loaded_ = true;
435  is_loading_ = true;
436
437  PrefService* prefs = user_prefs::UserPrefs::Get(context_);
438  if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) {
439    if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) {
440      Enable();
441    } else {
442      Disable();
443    }
444  }
445
446  RegisterProtocolHandlersFromPref(prefs::kPolicyRegisteredProtocolHandlers,
447                                   POLICY);
448  RegisterProtocolHandlersFromPref(prefs::kRegisteredProtocolHandlers, USER);
449  IgnoreProtocolHandlersFromPref(prefs::kPolicyIgnoredProtocolHandlers, POLICY);
450  IgnoreProtocolHandlersFromPref(prefs::kIgnoredProtocolHandlers, USER);
451
452  is_loading_ = false;
453
454  // For each default protocol handler, check that we are still registered
455  // with the OS as the default application.
456  if (ShouldRemoveHandlersNotInOS()) {
457    for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
458         p != default_handlers_.end(); ++p) {
459      ProtocolHandler handler = p->second;
460      DefaultClientObserver* observer = delegate_->CreateShellObserver(this);
461      scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
462      worker = delegate_->CreateShellWorker(observer, handler.protocol());
463      observer->SetWorker(worker.get());
464      default_client_observers_.push_back(observer);
465      worker->StartCheckIsDefault();
466    }
467  }
468}
469
470int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const {
471  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
472  const ProtocolHandler& handler = GetHandlerFor(scheme);
473  if (handler.IsEmpty())
474    return -1;
475  const ProtocolHandlerList* handlers = GetHandlerList(scheme);
476  if (!handlers)
477    return -1;
478
479  ProtocolHandlerList::const_iterator p;
480  int i;
481  for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) {
482    if (*p == handler)
483      return i;
484  }
485  return -1;
486}
487
488ProtocolHandlerRegistry::ProtocolHandlerList
489ProtocolHandlerRegistry::GetHandlersFor(
490    const std::string& scheme) const {
491  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
492  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
493  if (p == protocol_handlers_.end()) {
494    return ProtocolHandlerList();
495  }
496  return p->second;
497}
498
499ProtocolHandlerRegistry::ProtocolHandlerList
500ProtocolHandlerRegistry::GetIgnoredHandlers() {
501  return ignored_protocol_handlers_;
502}
503
504void ProtocolHandlerRegistry::GetRegisteredProtocols(
505    std::vector<std::string>* output) const {
506  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
507  ProtocolHandlerMultiMap::const_iterator p;
508  for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
509    if (!p->second.empty())
510      output->push_back(p->first);
511  }
512}
513
514bool ProtocolHandlerRegistry::CanSchemeBeOverridden(
515    const std::string& scheme) const {
516  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
517  const ProtocolHandlerList* handlers = GetHandlerList(scheme);
518  // If we already have a handler for this scheme, we can add more.
519  if (handlers != NULL && !handlers->empty())
520    return true;
521  // Don't override a scheme if it already has an external handler.
522  return !delegate_->IsExternalHandlerRegistered(scheme);
523}
524
525bool ProtocolHandlerRegistry::IsRegistered(
526    const ProtocolHandler& handler) const {
527  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
528  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
529  if (!handlers) {
530    return false;
531  }
532  return std::find(handlers->begin(), handlers->end(), handler) !=
533      handlers->end();
534}
535
536bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
537  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
538  ProtocolHandlerList::const_iterator i;
539  for (i = ignored_protocol_handlers_.begin();
540       i != ignored_protocol_handlers_.end(); ++i) {
541    if (*i == handler) {
542      return true;
543    }
544  }
545  return false;
546}
547
548bool ProtocolHandlerRegistry::HasRegisteredEquivalent(
549    const ProtocolHandler& handler) const {
550  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
551  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
552  if (!handlers) {
553    return false;
554  }
555  ProtocolHandlerList::const_iterator i;
556  for (i = handlers->begin(); i != handlers->end(); ++i) {
557    if (handler.IsEquivalent(*i)) {
558      return true;
559    }
560  }
561  return false;
562}
563
564bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
565    const ProtocolHandler& handler) const {
566  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
567  ProtocolHandlerList::const_iterator i;
568  for (i = ignored_protocol_handlers_.begin();
569       i != ignored_protocol_handlers_.end(); ++i) {
570    if (handler.IsEquivalent(*i)) {
571      return true;
572    }
573  }
574  return false;
575}
576
577void ProtocolHandlerRegistry::RemoveIgnoredHandler(
578    const ProtocolHandler& handler) {
579  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580  bool should_notify = false;
581  if (HandlerExists(handler, ignored_protocol_handlers_) &&
582      HandlerExists(handler, user_ignored_protocol_handlers_)) {
583    EraseHandler(handler, &user_ignored_protocol_handlers_);
584    Save();
585    if (!HandlerExists(handler, policy_ignored_protocol_handlers_)) {
586      EraseHandler(handler, &ignored_protocol_handlers_);
587      should_notify = true;
588    }
589  }
590  if (should_notify)
591    NotifyChanged();
592}
593
594bool ProtocolHandlerRegistry::IsHandledProtocol(
595    const std::string& scheme) const {
596  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
597  return enabled_ && !GetHandlerFor(scheme).IsEmpty();
598}
599
600void ProtocolHandlerRegistry::RemoveHandler(
601    const ProtocolHandler& handler) {
602  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
603  ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
604  bool erase_success = false;
605  if (HandlerExists(handler, handlers) &&
606      HandlerExists(handler, &user_protocol_handlers_)) {
607    EraseHandler(handler, &user_protocol_handlers_);
608    if (!HandlerExists(handler, &policy_protocol_handlers_)) {
609      erase_success = true;
610      EraseHandler(handler, &protocol_handlers_);
611    }
612  }
613  ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol());
614  if (erase_success && q != default_handlers_.end() && q->second == handler) {
615    // Make the new top handler in the list the default.
616    if (!handlers.empty()) {
617      // NOTE We pass a copy because SetDefault() modifies handlers.
618      SetDefault(ProtocolHandler(handlers[0]));
619    } else {
620      BrowserThread::PostTask(
621          BrowserThread::IO, FROM_HERE,
622          base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_,
623                     q->second.protocol()));
624
625      default_handlers_.erase(q);
626    }
627  }
628
629  if (erase_success && !IsHandledProtocol(handler.protocol())) {
630    delegate_->DeregisterExternalHandler(handler.protocol());
631  }
632  Save();
633  if (erase_success)
634    NotifyChanged();
635}
636
637void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) {
638  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
639  ProtocolHandler current_default = GetHandlerFor(scheme);
640  if (!current_default.IsEmpty())
641    RemoveHandler(current_default);
642}
643
644const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
645    const std::string& scheme) const {
646  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
647  return LookupHandler(default_handlers_, scheme);
648}
649
650void ProtocolHandlerRegistry::Enable() {
651  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
652  if (enabled_) {
653    return;
654  }
655  enabled_ = true;
656  BrowserThread::PostTask(
657      BrowserThread::IO,
658      FROM_HERE,
659      base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_));
660
661  ProtocolHandlerMap::const_iterator p;
662  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
663    delegate_->RegisterExternalHandler(p->first);
664  }
665  Save();
666  NotifyChanged();
667}
668
669void ProtocolHandlerRegistry::Disable() {
670  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
671  if (!enabled_) {
672    return;
673  }
674  enabled_ = false;
675  BrowserThread::PostTask(
676      BrowserThread::IO,
677      FROM_HERE,
678      base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_));
679
680  ProtocolHandlerMap::const_iterator p;
681  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
682    delegate_->DeregisterExternalHandler(p->first);
683  }
684  Save();
685  NotifyChanged();
686}
687
688void ProtocolHandlerRegistry::Shutdown() {
689  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
690  delegate_.reset(NULL);
691  // We free these now in case there are any outstanding workers running. If
692  // we didn't free them they could respond to workers and try to update the
693  // protocol handler registry after it was deleted.
694  // Observers remove themselves from this list when they are deleted; so
695  // we delete the last item until none are left in the list.
696  while (!default_client_observers_.empty()) {
697    delete default_client_observers_.back();
698  }
699}
700
701// static
702void ProtocolHandlerRegistry::RegisterProfilePrefs(
703    user_prefs::PrefRegistrySyncable* registry) {
704  registry->RegisterListPref(prefs::kRegisteredProtocolHandlers,
705                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
706  registry->RegisterListPref(prefs::kIgnoredProtocolHandlers,
707                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
708  registry->RegisterListPref(prefs::kPolicyRegisteredProtocolHandlers,
709                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
710  registry->RegisterListPref(prefs::kPolicyIgnoredProtocolHandlers,
711                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
712  registry->RegisterBooleanPref(
713      prefs::kCustomHandlersEnabled,
714      true,
715      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
716}
717
718ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
719  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
720  DCHECK(default_client_observers_.empty());
721}
722
723void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
724  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
725  DCHECK(IsRegistered(handler));
726  ProtocolHandlerMultiMap::iterator p =
727      protocol_handlers_.find(handler.protocol());
728  ProtocolHandlerList& list = p->second;
729  list.erase(std::find(list.begin(), list.end(), handler));
730  list.insert(list.begin(), handler);
731}
732
733void ProtocolHandlerRegistry::Save() {
734  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
735  if (is_loading_) {
736    return;
737  }
738  scoped_ptr<base::Value> registered_protocol_handlers(
739      EncodeRegisteredHandlers());
740  scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
741  PrefService* prefs = user_prefs::UserPrefs::Get(context_);
742
743  prefs->Set(prefs::kRegisteredProtocolHandlers,
744      *registered_protocol_handlers);
745  prefs->Set(prefs::kIgnoredProtocolHandlers,
746      *ignored_protocol_handlers);
747  prefs->SetBoolean(prefs::kCustomHandlersEnabled, enabled_);
748}
749
750const ProtocolHandlerRegistry::ProtocolHandlerList*
751ProtocolHandlerRegistry::GetHandlerList(
752    const std::string& scheme) const {
753  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
754  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
755  if (p == protocol_handlers_.end()) {
756    return NULL;
757  }
758  return &p->second;
759}
760
761void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
762  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
763  ProtocolHandlerMap::const_iterator p = default_handlers_.find(
764      handler.protocol());
765  // If we're not loading, and we are setting a default for a new protocol,
766  // register with the OS.
767  if (!is_loading_ && p == default_handlers_.end())
768      delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
769  default_handlers_.erase(handler.protocol());
770  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
771  PromoteHandler(handler);
772  BrowserThread::PostTask(
773      BrowserThread::IO,
774      FROM_HERE,
775      base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
776}
777
778void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
779  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
780  ProtocolHandlerMultiMap::iterator p =
781      protocol_handlers_.find(handler.protocol());
782
783  if (p != protocol_handlers_.end()) {
784    p->second.push_back(handler);
785    return;
786  }
787
788  ProtocolHandlerList new_list;
789  new_list.push_back(handler);
790  protocol_handlers_[handler.protocol()] = new_list;
791}
792
793base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
794  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
795  base::ListValue* protocol_handlers = new base::ListValue();
796  for (ProtocolHandlerMultiMap::iterator i = user_protocol_handlers_.begin();
797       i != user_protocol_handlers_.end();
798       ++i) {
799    for (ProtocolHandlerList::iterator j = i->second.begin();
800         j != i->second.end(); ++j) {
801      base::DictionaryValue* encoded = j->Encode();
802      if (IsDefault(*j)) {
803        encoded->Set("default", new base::FundamentalValue(true));
804      }
805      protocol_handlers->Append(encoded);
806    }
807  }
808  return protocol_handlers;
809}
810
811base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
812  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
813  base::ListValue* handlers = new base::ListValue();
814  for (ProtocolHandlerList::iterator i =
815           user_ignored_protocol_handlers_.begin();
816       i != user_ignored_protocol_handlers_.end();
817       ++i) {
818    handlers->Append(i->Encode());
819  }
820  return handlers;
821}
822
823void ProtocolHandlerRegistry::NotifyChanged() {
824  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
825  content::NotificationService::current()->Notify(
826      chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
827      content::Source<content::BrowserContext>(context_),
828      content::NotificationService::NoDetails());
829}
830
831void ProtocolHandlerRegistry::RegisterProtocolHandler(
832    const ProtocolHandler& handler,
833    const HandlerSource source) {
834  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
835  DCHECK(CanSchemeBeOverridden(handler.protocol()));
836  DCHECK(!handler.IsEmpty());
837  ProtocolHandlerMultiMap& map =
838      (source == POLICY) ? policy_protocol_handlers_ : user_protocol_handlers_;
839  ProtocolHandlerList& list = map[handler.protocol()];
840  if (!HandlerExists(handler, list))
841    list.push_back(handler);
842  if (IsRegistered(handler)) {
843    return;
844  }
845  if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
846    delegate_->RegisterExternalHandler(handler.protocol());
847  InsertHandler(handler);
848}
849
850std::vector<const base::DictionaryValue*>
851ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
852  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
853  std::vector<const base::DictionaryValue*> result;
854  PrefService* prefs = user_prefs::UserPrefs::Get(context_);
855  if (!prefs->HasPrefPath(pref_name)) {
856    return result;
857  }
858
859  const base::ListValue* handlers = prefs->GetList(pref_name);
860  if (handlers) {
861    for (size_t i = 0; i < handlers->GetSize(); ++i) {
862      const base::DictionaryValue* dict;
863      if (!handlers->GetDictionary(i, &dict))
864        continue;
865      if (ProtocolHandler::IsValidDict(dict)) {
866        result.push_back(dict);
867      }
868    }
869  }
870  return result;
871}
872
873void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref(
874    const char* pref_name,
875    const HandlerSource source) {
876  std::vector<const base::DictionaryValue*> registered_handlers =
877      GetHandlersFromPref(pref_name);
878  for (std::vector<const base::DictionaryValue*>::const_iterator p =
879           registered_handlers.begin();
880       p != registered_handlers.end();
881       ++p) {
882    ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
883    RegisterProtocolHandler(handler, source);
884    bool is_default = false;
885    if ((*p)->GetBoolean("default", &is_default) && is_default) {
886      SetDefault(handler);
887    }
888  }
889}
890
891void ProtocolHandlerRegistry::IgnoreProtocolHandler(
892    const ProtocolHandler& handler,
893    const HandlerSource source) {
894  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
895  ProtocolHandlerList& list = (source == POLICY)
896                                  ? policy_ignored_protocol_handlers_
897                                  : user_ignored_protocol_handlers_;
898  if (!HandlerExists(handler, list))
899    list.push_back(handler);
900  if (HandlerExists(handler, ignored_protocol_handlers_))
901    return;
902  ignored_protocol_handlers_.push_back(handler);
903}
904
905void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref(
906    const char* pref_name,
907    const HandlerSource source) {
908  std::vector<const base::DictionaryValue*> ignored_handlers =
909      GetHandlersFromPref(pref_name);
910  for (std::vector<const base::DictionaryValue*>::const_iterator p =
911           ignored_handlers.begin();
912       p != ignored_handlers.end();
913       ++p) {
914    IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p), source);
915  }
916}
917
918bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
919                                            ProtocolHandlerMultiMap* map) {
920  return HandlerExists(handler, (*map)[handler.protocol()]);
921}
922
923bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler,
924                                            const ProtocolHandlerList& list) {
925  return std::find(list.begin(), list.end(), handler) != list.end();
926}
927
928void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
929                                           ProtocolHandlerMultiMap* map) {
930  EraseHandler(handler, &(*map)[handler.protocol()]);
931}
932
933void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler,
934                                           ProtocolHandlerList* list) {
935  list->erase(std::find(list->begin(), list->end(), handler));
936}
937
938void ProtocolHandlerRegistry::AddPredefinedHandler(
939    const ProtocolHandler& handler) {
940  DCHECK(!is_loaded_);  // Must be called prior InitProtocolSettings.
941  RegisterProtocolHandler(handler, USER);
942  SetDefault(handler);
943}
944
945scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
946ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
947  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
948  // this is always created on the UI thread (in profile_io's
949  // InitializeOnUIThread. Any method calls must be done
950  // on the IO thread (this is checked).
951  return scoped_ptr<JobInterceptorFactory>(
952      new JobInterceptorFactory(io_thread_delegate_.get()));
953}
954