protocol_handler_registry.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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
27using content::BrowserThread;
28using content::ChildProcessSecurityPolicy;
29
30namespace {
31
32const 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.
46bool 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.
66class 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
113ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool)
114    : enabled_(true) {}
115ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {}
116
117bool 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
123void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault(
124    const std::string& scheme) {
125  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
126  default_handlers_.erase(scheme);
127}
128
129void 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|.
139net::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      "Protocol Handler Registry");
156}
157
158// JobInterceptorFactory -------------------------------------------------------
159
160// Instances of JobInterceptorFactory are produced for ownership by the IO
161// thread where it handler URL requests. We should never hold
162// any pointers on this class, only produce them in response to
163// requests via |ProtocolHandlerRegistry::CreateJobInterceptorFactory|.
164ProtocolHandlerRegistry::JobInterceptorFactory::JobInterceptorFactory(
165    IOThreadDelegate* io_thread_delegate)
166    : io_thread_delegate_(io_thread_delegate) {
167  DCHECK(io_thread_delegate_.get());
168  DetachFromThread();
169}
170
171ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() {
172}
173
174void ProtocolHandlerRegistry::JobInterceptorFactory::Chain(
175    scoped_ptr<net::URLRequestJobFactory> job_factory) {
176  job_factory_ = job_factory.Pass();
177}
178
179net::URLRequestJob*
180ProtocolHandlerRegistry::JobInterceptorFactory::
181MaybeCreateJobWithProtocolHandler(
182    const std::string& scheme,
183    net::URLRequest* request,
184    net::NetworkDelegate* network_delegate) const {
185  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
186  net::URLRequestJob* job = io_thread_delegate_->MaybeCreateJob(
187      request, network_delegate);
188  if (job)
189    return job;
190  return job_factory_->MaybeCreateJobWithProtocolHandler(
191      scheme, request, network_delegate);
192}
193
194bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol(
195    const std::string& scheme) const {
196  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
197  return io_thread_delegate_->IsHandledProtocol(scheme) ||
198      job_factory_->IsHandledProtocol(scheme);
199}
200
201bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledURL(
202    const GURL& url) const {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204  return (url.is_valid() &&
205      io_thread_delegate_->IsHandledProtocol(url.scheme())) ||
206      job_factory_->IsHandledURL(url);
207}
208
209bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget(
210    const GURL& location) const {
211  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
212  return job_factory_->IsSafeRedirectTarget(location);
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.get());
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      io_thread_delegate_(new IOThreadDelegate(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(&IOThreadDelegate::ClearDefault, io_thread_delegate_, 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 base::DictionaryValue*> registered_handlers =
449      GetHandlersFromPref(prefs::kRegisteredProtocolHandlers);
450  for (std::vector<const base::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 base::DictionaryValue*> ignored_handlers =
461    GetHandlersFromPref(prefs::kIgnoredProtocolHandlers);
462  for (std::vector<const base::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.get());
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(&IOThreadDelegate::ClearDefault, io_thread_delegate_,
633                     q->second.protocol()));
634
635      default_handlers_.erase(q);
636    }
637  }
638
639  if (!IsHandledProtocol(handler.protocol())) {
640    delegate_->DeregisterExternalHandler(handler.protocol());
641  }
642  Save();
643  NotifyChanged();
644}
645
646void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) {
647  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
648  ProtocolHandler current_default = GetHandlerFor(scheme);
649  if (!current_default.IsEmpty())
650    RemoveHandler(current_default);
651}
652
653const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor(
654    const std::string& scheme) const {
655  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
656  return LookupHandler(default_handlers_, scheme);
657}
658
659void ProtocolHandlerRegistry::Enable() {
660  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
661  if (enabled_) {
662    return;
663  }
664  enabled_ = true;
665  BrowserThread::PostTask(
666      BrowserThread::IO,
667      FROM_HERE,
668      base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_));
669
670  ProtocolHandlerMap::const_iterator p;
671  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
672    delegate_->RegisterExternalHandler(p->first);
673  }
674  Save();
675  NotifyChanged();
676}
677
678void ProtocolHandlerRegistry::Disable() {
679  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
680  if (!enabled_) {
681    return;
682  }
683  enabled_ = false;
684  BrowserThread::PostTask(
685      BrowserThread::IO,
686      FROM_HERE,
687      base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_));
688
689  ProtocolHandlerMap::const_iterator p;
690  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
691    delegate_->DeregisterExternalHandler(p->first);
692  }
693  Save();
694  NotifyChanged();
695}
696
697void ProtocolHandlerRegistry::Shutdown() {
698  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
699  delegate_.reset(NULL);
700  // We free these now in case there are any outstanding workers running. If
701  // we didn't free them they could respond to workers and try to update the
702  // protocol handler registry after it was deleted.
703  // Observers remove themselves from this list when they are deleted; so
704  // we delete the last item until none are left in the list.
705  while (!default_client_observers_.empty()) {
706    delete default_client_observers_.back();
707  }
708}
709
710// static
711void ProtocolHandlerRegistry::RegisterProfilePrefs(
712    user_prefs::PrefRegistrySyncable* registry) {
713  registry->RegisterListPref(prefs::kRegisteredProtocolHandlers,
714                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
715  registry->RegisterListPref(prefs::kIgnoredProtocolHandlers,
716                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
717  registry->RegisterBooleanPref(
718      prefs::kCustomHandlersEnabled,
719      true,
720      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
721}
722
723ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
724  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
725  DCHECK(default_client_observers_.empty());
726}
727
728void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
729  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
730  DCHECK(IsRegistered(handler));
731  ProtocolHandlerMultiMap::iterator p =
732      protocol_handlers_.find(handler.protocol());
733  ProtocolHandlerList& list = p->second;
734  list.erase(std::find(list.begin(), list.end(), handler));
735  list.insert(list.begin(), handler);
736}
737
738void ProtocolHandlerRegistry::Save() {
739  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
740  if (is_loading_) {
741    return;
742  }
743  scoped_ptr<base::Value> registered_protocol_handlers(
744      EncodeRegisteredHandlers());
745  scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
746  scoped_ptr<base::Value> enabled(base::Value::CreateBooleanValue(enabled_));
747  profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers,
748      *registered_protocol_handlers);
749  profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers,
750      *ignored_protocol_handlers);
751  profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled);
752}
753
754const ProtocolHandlerRegistry::ProtocolHandlerList*
755ProtocolHandlerRegistry::GetHandlerList(
756    const std::string& scheme) const {
757  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
758  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
759  if (p == protocol_handlers_.end()) {
760    return NULL;
761  }
762  return &p->second;
763}
764
765void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
766  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
767  ProtocolHandlerMap::const_iterator p = default_handlers_.find(
768      handler.protocol());
769  // If we're not loading, and we are setting a default for a new protocol,
770  // register with the OS.
771  if (!is_loading_ && p == default_handlers_.end())
772      delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
773  default_handlers_.erase(handler.protocol());
774  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
775  PromoteHandler(handler);
776  BrowserThread::PostTask(
777      BrowserThread::IO,
778      FROM_HERE,
779      base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
780}
781
782void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
783  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
784  ProtocolHandlerMultiMap::iterator p =
785      protocol_handlers_.find(handler.protocol());
786
787  if (p != protocol_handlers_.end()) {
788    p->second.push_back(handler);
789    return;
790  }
791
792  ProtocolHandlerList new_list;
793  new_list.push_back(handler);
794  protocol_handlers_[handler.protocol()] = new_list;
795}
796
797base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
798  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
799  base::ListValue* protocol_handlers = new base::ListValue();
800  for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin();
801       i != protocol_handlers_.end(); ++i) {
802    for (ProtocolHandlerList::iterator j = i->second.begin();
803         j != i->second.end(); ++j) {
804      base::DictionaryValue* encoded = j->Encode();
805      if (IsDefault(*j)) {
806        encoded->Set("default", base::Value::CreateBooleanValue(true));
807      }
808      protocol_handlers->Append(encoded);
809    }
810  }
811  return protocol_handlers;
812}
813
814base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
815  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
816  base::ListValue* handlers = new base::ListValue();
817  for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin();
818       i != ignored_protocol_handlers_.end(); ++i) {
819    handlers->Append(i->Encode());
820  }
821  return handlers;
822}
823
824void ProtocolHandlerRegistry::NotifyChanged() {
825  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
826  content::NotificationService::current()->Notify(
827      chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
828      content::Source<Profile>(profile_),
829      content::NotificationService::NoDetails());
830}
831
832void ProtocolHandlerRegistry::RegisterProtocolHandler(
833    const ProtocolHandler& handler) {
834  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
835  DCHECK(CanSchemeBeOverridden(handler.protocol()));
836  DCHECK(!handler.IsEmpty());
837  if (IsRegistered(handler)) {
838    return;
839  }
840  if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
841    delegate_->RegisterExternalHandler(handler.protocol());
842  InsertHandler(handler);
843}
844
845std::vector<const base::DictionaryValue*>
846ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
847  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
848  std::vector<const base::DictionaryValue*> result;
849  PrefService* prefs = profile_->GetPrefs();
850  if (!prefs->HasPrefPath(pref_name)) {
851    return result;
852  }
853
854  const base::ListValue* handlers = prefs->GetList(pref_name);
855  if (handlers) {
856    for (size_t i = 0; i < handlers->GetSize(); ++i) {
857      const base::DictionaryValue* dict;
858      if (!handlers->GetDictionary(i, &dict))
859        continue;
860      if (ProtocolHandler::IsValidDict(dict)) {
861        result.push_back(dict);
862      }
863    }
864  }
865  return result;
866}
867
868void ProtocolHandlerRegistry::IgnoreProtocolHandler(
869    const ProtocolHandler& handler) {
870  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
871  ignored_protocol_handlers_.push_back(handler);
872}
873
874void ProtocolHandlerRegistry::AddPredefinedHandler(
875    const ProtocolHandler& handler) {
876  DCHECK(!is_loaded_);  // Must be called prior InitProtocolSettings.
877  RegisterProtocolHandler(handler);
878  SetDefault(handler);
879}
880
881scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
882ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
883  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
884  // this is always created on the UI thread (in profile_io's
885  // InitializeOnUIThread. Any method calls must be done
886  // on the IO thread (this is checked).
887  return scoped_ptr<JobInterceptorFactory>(
888      new JobInterceptorFactory(io_thread_delegate_.get()));
889}
890