protocol_handler_registry.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/memory/ref_counted.h"
13#include "base/memory/scoped_ptr.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/prefs/pref_service.h"
17#include "chrome/browser/profiles/profile_io_data.h"
18#include "chrome/common/chrome_notification_types.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/custom_handlers/protocol_handler.h"
21#include "chrome/common/pref_names.h"
22#include "content/public/browser/browser_thread.h"
23#include "content/public/browser/child_process_security_policy.h"
24#include "content/public/browser/notification_service.h"
25#include "grit/generated_resources.h"
26#include "net/base/network_delegate.h"
27#include "net/url_request/url_request.h"
28#include "net/url_request/url_request_job.h"
29#include "net/url_request/url_request_redirect_job.h"
30#include "ui/base/l10n/l10n_util.h"
31
32using content::BrowserThread;
33using content::ChildProcessSecurityPolicy;
34
35namespace {
36
37const ProtocolHandler& LookupHandler(
38    const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map,
39    const std::string& scheme) {
40  ProtocolHandlerRegistry::ProtocolHandlerMap::const_iterator p =
41      handler_map.find(scheme);
42
43  if (p != handler_map.end())
44    return p->second;
45
46  return ProtocolHandler::EmptyProtocolHandler();
47}
48
49// If true default protocol handlers will be removed if the OS level
50// registration for a protocol is no longer Chrome.
51bool ShouldRemoveHandlersNotInOS() {
52#if defined(OS_LINUX)
53  // We don't do this on Linux as the OS registration there is not reliable,
54  // and Chrome OS doesn't have any notion of OS registration.
55  // TODO(benwells): When Linux support is more reliable remove this
56  // difference (http://crbug.com/88255).
57  return false;
58#else
59  return ShellIntegration::CanSetAsDefaultProtocolClient() !=
60      ShellIntegration::SET_DEFAULT_NOT_ALLOWED;
61#endif
62}
63
64}  // namespace
65
66// Core ------------------------------------------------------------------------
67
68// Core is an IO thread specific object. Access to the class should all
69// be done via the IO thread. The registry living on the UI thread makes
70// a best effort to update the IO object after local updates are completed.
71class ProtocolHandlerRegistry::Core
72    : public base::RefCountedThreadSafe<ProtocolHandlerRegistry::Core> {
73 public:
74
75  // Creates a new instance. If |enabled| is true the registry is considered
76  // enabled on the IO thread.
77  explicit Core(bool enabled);
78
79  // Returns true if the protocol has a default protocol handler.
80  // Should be called only from the IO thread.
81  bool IsHandledProtocol(const std::string& scheme) const;
82
83  // Clears the default for the provided protocol.
84  // Should be called only from the IO thread.
85  void ClearDefault(const std::string& scheme);
86
87  // Makes this ProtocolHandler the default handler for its protocol.
88  // Should be called only from the IO thread.
89  void SetDefault(const ProtocolHandler& handler);
90
91  // Creates a URL request job for the given request if there is a matching
92  // protocol handler, returns NULL otherwise.
93  net::URLRequestJob* MaybeCreateJob(
94      net::URLRequest* request, net::NetworkDelegate* network_delegate) const;
95
96  // Indicate that the registry has been enabled in the IO thread's
97  // copy of the data.
98  void Enable() { enabled_ = true; }
99
100  // Indicate that the registry has been disabled in the IO thread's copy of
101  // the data.
102  void Disable() { enabled_ = false; }
103
104 private:
105  friend class base::RefCountedThreadSafe<Core>;
106  virtual ~Core();
107
108  // Copy of protocol handlers use only on the IO thread.
109  ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_;
110
111  // Is the registry enabled on the IO thread.
112  bool enabled_;
113
114  DISALLOW_COPY_AND_ASSIGN(Core);
115};
116
117ProtocolHandlerRegistry::Core::Core(bool) : enabled_(true) {}
118ProtocolHandlerRegistry::Core::~Core() {}
119
120bool ProtocolHandlerRegistry::Core::IsHandledProtocol(
121    const std::string& scheme) const {
122  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
123  return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty();
124}
125
126void ProtocolHandlerRegistry::Core::ClearDefault(const std::string& scheme) {
127  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
128  default_handlers_.erase(scheme);
129}
130
131void ProtocolHandlerRegistry::Core::SetDefault(const ProtocolHandler& handler) {
132  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
133  ClearDefault(handler.protocol());
134  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
135}
136
137// Create a new job for the supplied |URLRequest| if a default handler
138// is registered and the associated handler is able to interpret
139// the url from |request|.
140net::URLRequestJob* ProtocolHandlerRegistry::Core::MaybeCreateJob(
141    net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143
144  ProtocolHandler handler = LookupHandler(default_handlers_,
145                                          request->url().scheme());
146  if (handler.IsEmpty())
147    return NULL;
148
149  GURL translated_url(handler.TranslateUrl(request->url()));
150  if (!translated_url.is_valid())
151    return NULL;
152
153  return new net::URLRequestRedirectJob(
154      request, network_delegate, translated_url);
155}
156
157// URLInterceptor ------------------------------------------------------------
158
159// Instances of this class 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::CreateURLInterceptor|.
163class ProtocolHandlerRegistry::URLInterceptor
164    : public net::URLRequestJobFactory::Interceptor {
165 public:
166  explicit URLInterceptor(Core* core);
167  virtual ~URLInterceptor();
168
169  virtual net::URLRequestJob* MaybeIntercept(
170      net::URLRequest* request,
171      net::NetworkDelegate* network_delegate) const OVERRIDE;
172
173  virtual bool WillHandleProtocol(const std::string& protocol) const OVERRIDE;
174
175  virtual net::URLRequestJob* MaybeInterceptRedirect(
176      const GURL& url,
177      net::URLRequest* request,
178      net::NetworkDelegate* network_delegate) const OVERRIDE {
179    return NULL;
180  }
181
182  virtual net::URLRequestJob* MaybeInterceptResponse(
183      net::URLRequest* request,
184      net::NetworkDelegate* network_delegate) const OVERRIDE {
185    return NULL;
186  }
187
188 private:
189  scoped_refptr<Core> core_;
190  DISALLOW_COPY_AND_ASSIGN(URLInterceptor);
191};
192
193ProtocolHandlerRegistry::URLInterceptor::URLInterceptor(Core* core)
194    : core_(core) {
195  DCHECK(core_);
196}
197
198ProtocolHandlerRegistry::URLInterceptor::~URLInterceptor() {
199}
200
201net::URLRequestJob* ProtocolHandlerRegistry::URLInterceptor::MaybeIntercept(
202    net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204
205  return core_->MaybeCreateJob(request, network_delegate);
206}
207
208bool ProtocolHandlerRegistry::URLInterceptor::WillHandleProtocol(
209    const std::string& protocol) const {
210  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211
212  return core_->IsHandledProtocol(protocol);
213}
214
215// DefaultClientObserver ------------------------------------------------------
216
217ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver(
218    ProtocolHandlerRegistry* registry)
219    : worker_(NULL),
220      registry_(registry) {
221  DCHECK(registry_);
222}
223
224ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() {
225  if (worker_)
226    worker_->ObserverDestroyed();
227
228  DefaultClientObserverList::iterator iter = std::find(
229      registry_->default_client_observers_.begin(),
230      registry_->default_client_observers_.end(), this);
231  registry_->default_client_observers_.erase(iter);
232}
233
234void ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState(
235    ShellIntegration::DefaultWebClientUIState state) {
236  if (worker_) {
237    if (ShouldRemoveHandlersNotInOS() &&
238        (state == ShellIntegration::STATE_NOT_DEFAULT)) {
239      registry_->ClearDefault(worker_->protocol());
240    }
241  } else {
242    NOTREACHED();
243  }
244}
245
246bool ProtocolHandlerRegistry::DefaultClientObserver::
247    IsInteractiveSetDefaultPermitted() {
248  return true;
249}
250
251void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker(
252    ShellIntegration::DefaultProtocolClientWorker* worker) {
253  worker_ = worker;
254}
255
256bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() {
257  return true;
258}
259
260// Delegate --------------------------------------------------------------------
261
262ProtocolHandlerRegistry::Delegate::~Delegate() {}
263
264void ProtocolHandlerRegistry::Delegate::RegisterExternalHandler(
265    const std::string& protocol) {
266  ChildProcessSecurityPolicy* policy =
267    ChildProcessSecurityPolicy::GetInstance();
268  if (!policy->IsWebSafeScheme(protocol)) {
269    policy->RegisterWebSafeScheme(protocol);
270  }
271}
272
273void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler(
274    const std::string& protocol) {
275}
276
277bool ProtocolHandlerRegistry::Delegate::IsExternalHandlerRegistered(
278    const std::string& protocol) {
279  // NOTE(koz): This function is safe to call from any thread, despite living
280  // in ProfileIOData.
281  return ProfileIOData::IsHandledProtocol(protocol);
282}
283
284ShellIntegration::DefaultProtocolClientWorker*
285ProtocolHandlerRegistry::Delegate::CreateShellWorker(
286    ShellIntegration::DefaultWebClientObserver* observer,
287    const std::string& protocol) {
288  return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol);
289}
290
291ProtocolHandlerRegistry::DefaultClientObserver*
292ProtocolHandlerRegistry::Delegate::CreateShellObserver(
293    ProtocolHandlerRegistry* registry) {
294  return new DefaultClientObserver(registry);
295}
296
297void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient(
298    const std::string& protocol, ProtocolHandlerRegistry* registry) {
299  DefaultClientObserver* observer = CreateShellObserver(registry);
300  // The worker pointer is reference counted. While it is running the
301  // message loops of the FILE and UI thread will hold references to it
302  // and it will be automatically freed once all its tasks have finished.
303  scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
304  worker = CreateShellWorker(observer, protocol);
305  observer->SetWorker(worker);
306  registry->default_client_observers_.push_back(observer);
307  worker->StartSetAsDefault();
308}
309
310// ProtocolHandlerRegistry -----------------------------------------------------
311
312ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile,
313    Delegate* delegate)
314    : profile_(profile),
315      delegate_(delegate),
316      enabled_(true),
317      is_loading_(false),
318      is_loaded_(false),
319      core_(new Core(enabled_)){
320}
321
322bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest(
323    const ProtocolHandler& handler) {
324  if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol()))
325    return true;
326
327  if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler))
328    return true;
329
330  if (AttemptReplace(handler))
331    return true;
332
333  return false;
334}
335
336void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler(
337    const ProtocolHandler& handler) {
338  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339  RegisterProtocolHandler(handler);
340  SetDefault(handler);
341  Save();
342  NotifyChanged();
343}
344
345void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler(
346    const ProtocolHandler& handler) {
347  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
348  RegisterProtocolHandler(handler);
349  Save();
350  NotifyChanged();
351}
352
353void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
354    const ProtocolHandler& handler) {
355  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
356  IgnoreProtocolHandler(handler);
357  Save();
358  NotifyChanged();
359}
360
361bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) {
362  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363  ProtocolHandler old_default = GetHandlerFor(handler.protocol());
364  bool make_new_handler_default = handler.IsSameOrigin(old_default);
365  ProtocolHandlerList to_replace(GetReplacedHandlers(handler));
366  if (to_replace.empty())
367    return false;
368  for (ProtocolHandlerList::iterator p = to_replace.begin();
369       p != to_replace.end(); ++p) {
370    RemoveHandler(*p);
371  }
372  if (make_new_handler_default) {
373    OnAcceptRegisterProtocolHandler(handler);
374  } else {
375    InsertHandler(handler);
376    NotifyChanged();
377  }
378  return true;
379}
380
381ProtocolHandlerRegistry::ProtocolHandlerList
382ProtocolHandlerRegistry::GetReplacedHandlers(
383    const ProtocolHandler& handler) const {
384  ProtocolHandlerList replaced_handlers;
385  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
386  if (!handlers)
387    return replaced_handlers;
388  for (ProtocolHandlerList::const_iterator p = handlers->begin();
389       p != handlers->end(); p++) {
390    if (handler.IsSameOrigin(*p)) {
391      replaced_handlers.push_back(*p);
392    }
393  }
394  return replaced_handlers;
395}
396
397void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) {
398  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399
400  default_handlers_.erase(scheme);
401  BrowserThread::PostTask(
402      BrowserThread::IO,
403      FROM_HERE,
404      base::Bind(&Core::ClearDefault, core_, scheme));
405  Save();
406  NotifyChanged();
407}
408
409bool ProtocolHandlerRegistry::IsDefault(
410    const ProtocolHandler& handler) const {
411  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
412  return GetHandlerFor(handler.protocol()) == handler;
413}
414
415void ProtocolHandlerRegistry::InstallDefaultsForChromeOS() {
416#if defined(OS_CHROMEOS)
417  // Only chromeos has default protocol handlers at this point.
418  AddPredefinedHandler(
419      ProtocolHandler::CreateProtocolHandler(
420          "mailto",
421          GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_MAILTO_HANDLER_URL)),
422          l10n_util::GetStringUTF16(IDS_GOOGLE_MAILTO_HANDLER_NAME)));
423  AddPredefinedHandler(
424      ProtocolHandler::CreateProtocolHandler(
425          "webcal",
426          GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL)),
427          l10n_util::GetStringUTF16(IDS_GOOGLE_WEBCAL_HANDLER_NAME)));
428#else
429  NOTREACHED();  // this method should only ever be called in chromeos.
430#endif
431}
432
433void ProtocolHandlerRegistry::InitProtocolSettings() {
434  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
435
436  // Any further default additions to the table will get rejected from now on.
437  is_loaded_ = true;
438  is_loading_ = true;
439
440  PrefService* prefs = profile_->GetPrefs();
441  if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) {
442    if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) {
443      Enable();
444    } else {
445      Disable();
446    }
447  }
448  std::vector<const DictionaryValue*> registered_handlers =
449      GetHandlersFromPref(prefs::kRegisteredProtocolHandlers);
450  for (std::vector<const DictionaryValue*>::const_iterator p =
451       registered_handlers.begin();
452       p != registered_handlers.end(); ++p) {
453    ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
454    RegisterProtocolHandler(handler);
455    bool is_default = false;
456    if ((*p)->GetBoolean("default", &is_default) && is_default) {
457      SetDefault(handler);
458    }
459  }
460  std::vector<const DictionaryValue*> ignored_handlers =
461    GetHandlersFromPref(prefs::kIgnoredProtocolHandlers);
462  for (std::vector<const DictionaryValue*>::const_iterator p =
463       ignored_handlers.begin();
464       p != ignored_handlers.end(); ++p) {
465    IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p));
466  }
467  is_loading_ = false;
468
469  // For each default protocol handler, check that we are still registered
470  // with the OS as the default application.
471  if (ShouldRemoveHandlersNotInOS()) {
472    for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
473         p != default_handlers_.end(); ++p) {
474      ProtocolHandler handler = p->second;
475      DefaultClientObserver* observer = delegate_->CreateShellObserver(this);
476      scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
477      worker = delegate_->CreateShellWorker(observer, handler.protocol());
478      observer->SetWorker(worker);
479      default_client_observers_.push_back(observer);
480      worker->StartCheckIsDefault();
481    }
482  }
483}
484
485int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const {
486  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
487  const ProtocolHandler& handler = GetHandlerFor(scheme);
488  if (handler.IsEmpty())
489    return -1;
490  const ProtocolHandlerList* handlers = GetHandlerList(scheme);
491  if (!handlers)
492    return -1;
493
494  ProtocolHandlerList::const_iterator p;
495  int i;
496  for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) {
497    if (*p == handler)
498      return i;
499  }
500  return -1;
501}
502
503ProtocolHandlerRegistry::ProtocolHandlerList
504ProtocolHandlerRegistry::GetHandlersFor(
505    const std::string& scheme) const {
506  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
507  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
508  if (p == protocol_handlers_.end()) {
509    return ProtocolHandlerList();
510  }
511  return p->second;
512}
513
514ProtocolHandlerRegistry::ProtocolHandlerList
515ProtocolHandlerRegistry::GetIgnoredHandlers() {
516  return ignored_protocol_handlers_;
517}
518
519void ProtocolHandlerRegistry::GetRegisteredProtocols(
520    std::vector<std::string>* output) const {
521  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
522  ProtocolHandlerMultiMap::const_iterator p;
523  for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
524    if (!p->second.empty())
525      output->push_back(p->first);
526  }
527}
528
529bool ProtocolHandlerRegistry::CanSchemeBeOverridden(
530    const std::string& scheme) const {
531  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
532  const ProtocolHandlerList* handlers = GetHandlerList(scheme);
533  // If we already have a handler for this scheme, we can add more.
534  if (handlers != NULL && !handlers->empty())
535    return true;
536  // Don't override a scheme if it already has an external handler.
537  return !delegate_->IsExternalHandlerRegistered(scheme);
538}
539
540bool ProtocolHandlerRegistry::IsRegistered(
541    const ProtocolHandler& handler) const {
542  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
544  if (!handlers) {
545    return false;
546  }
547  return std::find(handlers->begin(), handlers->end(), handler) !=
548      handlers->end();
549}
550
551bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
552  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
553  ProtocolHandlerList::const_iterator i;
554  for (i = ignored_protocol_handlers_.begin();
555       i != ignored_protocol_handlers_.end(); ++i) {
556    if (*i == handler) {
557      return true;
558    }
559  }
560  return false;
561}
562
563bool ProtocolHandlerRegistry::HasRegisteredEquivalent(
564    const ProtocolHandler& handler) const {
565  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
567  if (!handlers) {
568    return false;
569  }
570  ProtocolHandlerList::const_iterator i;
571  for (i = handlers->begin(); i != handlers->end(); ++i) {
572    if (handler.IsEquivalent(*i)) {
573      return true;
574    }
575  }
576  return false;
577}
578
579bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
580    const ProtocolHandler& handler) const {
581  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
582  ProtocolHandlerList::const_iterator i;
583  for (i = ignored_protocol_handlers_.begin();
584       i != ignored_protocol_handlers_.end(); ++i) {
585    if (handler.IsEquivalent(*i)) {
586      return true;
587    }
588  }
589  return false;
590}
591
592void ProtocolHandlerRegistry::RemoveIgnoredHandler(
593    const ProtocolHandler& handler) {
594  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
595  bool should_notify = false;
596  ProtocolHandlerList::iterator p = std::find(
597      ignored_protocol_handlers_.begin(), ignored_protocol_handlers_.end(),
598      handler);
599  if (p != ignored_protocol_handlers_.end()) {
600    ignored_protocol_handlers_.erase(p);
601    Save();
602    should_notify = true;
603  }
604  if (should_notify)
605    NotifyChanged();
606}
607
608bool ProtocolHandlerRegistry::IsHandledProtocol(
609    const std::string& scheme) const {
610  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
611  return enabled_ && !GetHandlerFor(scheme).IsEmpty();
612}
613
614void ProtocolHandlerRegistry::RemoveHandler(
615    const ProtocolHandler& handler) {
616  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
617  ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
618  ProtocolHandlerList::iterator p =
619      std::find(handlers.begin(), handlers.end(), handler);
620  if (p != handlers.end()) {
621    handlers.erase(p);
622  }
623  ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol());
624  if (q != default_handlers_.end() && q->second == handler) {
625    // Make the new top handler in the list the default.
626    if (!handlers.empty()) {
627      // NOTE We pass a copy because SetDefault() modifies handlers.
628      SetDefault(ProtocolHandler(handlers[0]));
629    } else {
630      BrowserThread::PostTask(
631          BrowserThread::IO, FROM_HERE,
632          base::Bind(&Core::ClearDefault, core_, 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
645void 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
652const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
653    const std::string& scheme) const {
654  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655  return LookupHandler(default_handlers_, scheme);
656}
657
658void 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(&Core::Enable, core_));
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
677void 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(&Core::Disable, core_));
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
696void 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
710void ProtocolHandlerRegistry::RegisterPrefs(PrefService* pref_service) {
711  pref_service->RegisterListPref(prefs::kRegisteredProtocolHandlers,
712                                 PrefService::UNSYNCABLE_PREF);
713  pref_service->RegisterListPref(prefs::kIgnoredProtocolHandlers,
714                                 PrefService::UNSYNCABLE_PREF);
715  pref_service->RegisterBooleanPref(prefs::kCustomHandlersEnabled, true,
716                                    PrefService::UNSYNCABLE_PREF);
717}
718
719ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
720  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
721  DCHECK(default_client_observers_.empty());
722}
723
724void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
725  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
726  DCHECK(IsRegistered(handler));
727  ProtocolHandlerMultiMap::iterator p =
728      protocol_handlers_.find(handler.protocol());
729  ProtocolHandlerList& list = p->second;
730  list.erase(std::find(list.begin(), list.end(), handler));
731  list.insert(list.begin(), handler);
732}
733
734void ProtocolHandlerRegistry::Save() {
735  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
736  if (is_loading_) {
737    return;
738  }
739  scoped_ptr<Value> registered_protocol_handlers(EncodeRegisteredHandlers());
740  scoped_ptr<Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
741  scoped_ptr<Value> enabled(Value::CreateBooleanValue(enabled_));
742  profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers,
743      *registered_protocol_handlers);
744  profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers,
745      *ignored_protocol_handlers);
746  profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled);
747}
748
749const ProtocolHandlerRegistry::ProtocolHandlerList*
750ProtocolHandlerRegistry::GetHandlerList(
751    const std::string& scheme) const {
752  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
753  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
754  if (p == protocol_handlers_.end()) {
755    return NULL;
756  }
757  return &p->second;
758}
759
760void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
761  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
762  ProtocolHandlerMap::const_iterator p = default_handlers_.find(
763      handler.protocol());
764  // If we're not loading, and we are setting a default for a new protocol,
765  // register with the OS.
766  if (!is_loading_ && p == default_handlers_.end())
767      delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
768  default_handlers_.erase(handler.protocol());
769  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
770  PromoteHandler(handler);
771  BrowserThread::PostTask(
772      BrowserThread::IO,
773      FROM_HERE,
774      base::Bind(&Core::SetDefault, core_, handler));
775}
776
777void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
778  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
779  ProtocolHandlerMultiMap::iterator p =
780      protocol_handlers_.find(handler.protocol());
781
782  if (p != protocol_handlers_.end()) {
783    p->second.push_back(handler);
784    return;
785  }
786
787  ProtocolHandlerList new_list;
788  new_list.push_back(handler);
789  protocol_handlers_[handler.protocol()] = new_list;
790}
791
792Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
793  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
794  ListValue* protocol_handlers = new ListValue();
795  for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin();
796       i != protocol_handlers_.end(); ++i) {
797    for (ProtocolHandlerList::iterator j = i->second.begin();
798         j != i->second.end(); ++j) {
799      DictionaryValue* encoded = j->Encode();
800      if (IsDefault(*j)) {
801        encoded->Set("default", Value::CreateBooleanValue(true));
802      }
803      protocol_handlers->Append(encoded);
804    }
805  }
806  return protocol_handlers;
807}
808
809Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
810  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
811  ListValue* handlers = new ListValue();
812  for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin();
813       i != ignored_protocol_handlers_.end(); ++i) {
814    handlers->Append(i->Encode());
815  }
816  return handlers;
817}
818
819void ProtocolHandlerRegistry::NotifyChanged() {
820  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
821  content::NotificationService::current()->Notify(
822      chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
823      content::Source<Profile>(profile_),
824      content::NotificationService::NoDetails());
825}
826
827void ProtocolHandlerRegistry::RegisterProtocolHandler(
828    const ProtocolHandler& handler) {
829  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
830  DCHECK(CanSchemeBeOverridden(handler.protocol()));
831  DCHECK(!handler.IsEmpty());
832  if (IsRegistered(handler)) {
833    return;
834  }
835  if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
836    delegate_->RegisterExternalHandler(handler.protocol());
837  InsertHandler(handler);
838}
839
840std::vector<const DictionaryValue*>
841ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
842  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
843  std::vector<const DictionaryValue*> result;
844  PrefService* prefs = profile_->GetPrefs();
845  if (!prefs->HasPrefPath(pref_name)) {
846    return result;
847  }
848
849  const ListValue* handlers = prefs->GetList(pref_name);
850  if (handlers) {
851    for (size_t i = 0; i < handlers->GetSize(); ++i) {
852      const DictionaryValue* dict;
853      if (!handlers->GetDictionary(i, &dict))
854        continue;
855      if (ProtocolHandler::IsValidDict(dict)) {
856        result.push_back(dict);
857      }
858    }
859  }
860  return result;
861}
862
863void ProtocolHandlerRegistry::IgnoreProtocolHandler(
864    const ProtocolHandler& handler) {
865  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
866  ignored_protocol_handlers_.push_back(handler);
867}
868
869void ProtocolHandlerRegistry::AddPredefinedHandler(
870    const ProtocolHandler& handler) {
871  DCHECK(!is_loaded_);  // Must be called prior InitProtocolSettings.
872  RegisterProtocolHandler(handler);
873  SetDefault(handler);
874}
875
876net::URLRequestJobFactory::Interceptor*
877    ProtocolHandlerRegistry::CreateURLInterceptor() {
878  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
879  // this is always created on the UI thread (in profile_io's
880  // InitializeOnUIThread. Any method calls must be done
881  // on the IO thread (this is checked).
882  return new URLInterceptor(core_);
883}
884