protocol_handler_registry.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/custom_handlers/register_protocol_handler_infobar_delegate.h"
14#include "chrome/browser/net/chrome_url_request_context.h"
15#include "chrome/browser/profiles/profile_io_data.h"
16#include "chrome/common/chrome_notification_types.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}
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_);
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);
305  registry->default_client_observers_.push_back(observer);
306  worker->StartSetAsDefault();
307}
308
309// ProtocolHandlerRegistry -----------------------------------------------------
310
311ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile,
312    Delegate* delegate)
313    : profile_(profile),
314      delegate_(delegate),
315      enabled_(true),
316      is_loading_(false),
317      is_loaded_(false),
318      io_thread_delegate_(new IOThreadDelegate(enabled_)){
319}
320
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);
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);
348  Save();
349  NotifyChanged();
350}
351
352void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler(
353    const ProtocolHandler& handler) {
354  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355  IgnoreProtocolHandler(handler);
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          l10n_util::GetStringUTF16(IDS_GOOGLE_MAILTO_HANDLER_NAME)));
422  AddPredefinedHandler(
423      ProtocolHandler::CreateProtocolHandler(
424          "webcal",
425          GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL)),
426          l10n_util::GetStringUTF16(IDS_GOOGLE_WEBCAL_HANDLER_NAME)));
427#else
428  NOTREACHED();  // this method should only ever be called in chromeos.
429#endif
430}
431
432void ProtocolHandlerRegistry::InitProtocolSettings() {
433  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
434
435  // Any further default additions to the table will get rejected from now on.
436  is_loaded_ = true;
437  is_loading_ = true;
438
439  PrefService* prefs = profile_->GetPrefs();
440  if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) {
441    if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) {
442      Enable();
443    } else {
444      Disable();
445    }
446  }
447  std::vector<const DictionaryValue*> registered_handlers =
448      GetHandlersFromPref(prefs::kRegisteredProtocolHandlers);
449  for (std::vector<const DictionaryValue*>::const_iterator p =
450       registered_handlers.begin();
451       p != registered_handlers.end(); ++p) {
452    ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
453    RegisterProtocolHandler(handler);
454    bool is_default = false;
455    if ((*p)->GetBoolean("default", &is_default) && is_default) {
456      SetDefault(handler);
457    }
458  }
459  std::vector<const DictionaryValue*> ignored_handlers =
460    GetHandlersFromPref(prefs::kIgnoredProtocolHandlers);
461  for (std::vector<const DictionaryValue*>::const_iterator p =
462       ignored_handlers.begin();
463       p != ignored_handlers.end(); ++p) {
464    IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p));
465  }
466  is_loading_ = false;
467
468  // For each default protocol handler, check that we are still registered
469  // with the OS as the default application.
470  if (ShouldRemoveHandlersNotInOS()) {
471    for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
472         p != default_handlers_.end(); ++p) {
473      ProtocolHandler handler = p->second;
474      DefaultClientObserver* observer = delegate_->CreateShellObserver(this);
475      scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker;
476      worker = delegate_->CreateShellWorker(observer, handler.protocol());
477      observer->SetWorker(worker);
478      default_client_observers_.push_back(observer);
479      worker->StartCheckIsDefault();
480    }
481  }
482}
483
484int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const {
485  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
486  const ProtocolHandler& handler = GetHandlerFor(scheme);
487  if (handler.IsEmpty())
488    return -1;
489  const ProtocolHandlerList* handlers = GetHandlerList(scheme);
490  if (!handlers)
491    return -1;
492
493  ProtocolHandlerList::const_iterator p;
494  int i;
495  for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) {
496    if (*p == handler)
497      return i;
498  }
499  return -1;
500}
501
502ProtocolHandlerRegistry::ProtocolHandlerList
503ProtocolHandlerRegistry::GetHandlersFor(
504    const std::string& scheme) const {
505  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
506  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
507  if (p == protocol_handlers_.end()) {
508    return ProtocolHandlerList();
509  }
510  return p->second;
511}
512
513ProtocolHandlerRegistry::ProtocolHandlerList
514ProtocolHandlerRegistry::GetIgnoredHandlers() {
515  return ignored_protocol_handlers_;
516}
517
518void ProtocolHandlerRegistry::GetRegisteredProtocols(
519    std::vector<std::string>* output) const {
520  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
521  ProtocolHandlerMultiMap::const_iterator p;
522  for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
523    if (!p->second.empty())
524      output->push_back(p->first);
525  }
526}
527
528bool ProtocolHandlerRegistry::CanSchemeBeOverridden(
529    const std::string& scheme) const {
530  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
531  const ProtocolHandlerList* handlers = GetHandlerList(scheme);
532  // If we already have a handler for this scheme, we can add more.
533  if (handlers != NULL && !handlers->empty())
534    return true;
535  // Don't override a scheme if it already has an external handler.
536  return !delegate_->IsExternalHandlerRegistered(scheme);
537}
538
539bool ProtocolHandlerRegistry::IsRegistered(
540    const ProtocolHandler& handler) const {
541  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
542  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
543  if (!handlers) {
544    return false;
545  }
546  return std::find(handlers->begin(), handlers->end(), handler) !=
547      handlers->end();
548}
549
550bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
551  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
552  ProtocolHandlerList::const_iterator i;
553  for (i = ignored_protocol_handlers_.begin();
554       i != ignored_protocol_handlers_.end(); ++i) {
555    if (*i == handler) {
556      return true;
557    }
558  }
559  return false;
560}
561
562bool ProtocolHandlerRegistry::HasRegisteredEquivalent(
563    const ProtocolHandler& handler) const {
564  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565  const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol());
566  if (!handlers) {
567    return false;
568  }
569  ProtocolHandlerList::const_iterator i;
570  for (i = handlers->begin(); i != handlers->end(); ++i) {
571    if (handler.IsEquivalent(*i)) {
572      return true;
573    }
574  }
575  return false;
576}
577
578bool ProtocolHandlerRegistry::HasIgnoredEquivalent(
579    const ProtocolHandler& handler) const {
580  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
581  ProtocolHandlerList::const_iterator i;
582  for (i = ignored_protocol_handlers_.begin();
583       i != ignored_protocol_handlers_.end(); ++i) {
584    if (handler.IsEquivalent(*i)) {
585      return true;
586    }
587  }
588  return false;
589}
590
591void ProtocolHandlerRegistry::RemoveIgnoredHandler(
592    const ProtocolHandler& handler) {
593  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
594  bool should_notify = false;
595  ProtocolHandlerList::iterator p = std::find(
596      ignored_protocol_handlers_.begin(), ignored_protocol_handlers_.end(),
597      handler);
598  if (p != ignored_protocol_handlers_.end()) {
599    ignored_protocol_handlers_.erase(p);
600    Save();
601    should_notify = true;
602  }
603  if (should_notify)
604    NotifyChanged();
605}
606
607bool ProtocolHandlerRegistry::IsHandledProtocol(
608    const std::string& scheme) const {
609  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
610  return enabled_ && !GetHandlerFor(scheme).IsEmpty();
611}
612
613void ProtocolHandlerRegistry::RemoveHandler(
614    const ProtocolHandler& handler) {
615  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
616  ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
617  ProtocolHandlerList::iterator p =
618      std::find(handlers.begin(), handlers.end(), handler);
619  if (p != handlers.end()) {
620    handlers.erase(p);
621  }
622  ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol());
623  if (q != default_handlers_.end() && q->second == handler) {
624    // Make the new top handler in the list the default.
625    if (!handlers.empty()) {
626      // NOTE We pass a copy because SetDefault() modifies handlers.
627      SetDefault(ProtocolHandler(handlers[0]));
628    } else {
629      BrowserThread::PostTask(
630          BrowserThread::IO, FROM_HERE,
631          base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_,
632                     q->second.protocol()));
633
634      default_handlers_.erase(q);
635    }
636  }
637
638  if (!IsHandledProtocol(handler.protocol())) {
639    delegate_->DeregisterExternalHandler(handler.protocol());
640  }
641  Save();
642  NotifyChanged();
643}
644
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(&IOThreadDelegate::Enable, io_thread_delegate_));
668
669  ProtocolHandlerMap::const_iterator p;
670  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
671    delegate_->RegisterExternalHandler(p->first);
672  }
673  Save();
674  NotifyChanged();
675}
676
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(&IOThreadDelegate::Disable, io_thread_delegate_));
687
688  ProtocolHandlerMap::const_iterator p;
689  for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) {
690    delegate_->DeregisterExternalHandler(p->first);
691  }
692  Save();
693  NotifyChanged();
694}
695
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::RegisterUserPrefs(
711    user_prefs::PrefRegistrySyncable* registry) {
712  registry->RegisterListPref(prefs::kRegisteredProtocolHandlers,
713                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
714  registry->RegisterListPref(prefs::kIgnoredProtocolHandlers,
715                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
716  registry->RegisterBooleanPref(
717      prefs::kCustomHandlersEnabled,
718      true,
719      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
720}
721
722ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
723  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
724  DCHECK(default_client_observers_.empty());
725}
726
727void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) {
728  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
729  DCHECK(IsRegistered(handler));
730  ProtocolHandlerMultiMap::iterator p =
731      protocol_handlers_.find(handler.protocol());
732  ProtocolHandlerList& list = p->second;
733  list.erase(std::find(list.begin(), list.end(), handler));
734  list.insert(list.begin(), handler);
735}
736
737void ProtocolHandlerRegistry::Save() {
738  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739  if (is_loading_) {
740    return;
741  }
742  scoped_ptr<Value> registered_protocol_handlers(EncodeRegisteredHandlers());
743  scoped_ptr<Value> ignored_protocol_handlers(EncodeIgnoredHandlers());
744  scoped_ptr<Value> enabled(Value::CreateBooleanValue(enabled_));
745  profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers,
746      *registered_protocol_handlers);
747  profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers,
748      *ignored_protocol_handlers);
749  profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled);
750}
751
752const ProtocolHandlerRegistry::ProtocolHandlerList*
753ProtocolHandlerRegistry::GetHandlerList(
754    const std::string& scheme) const {
755  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
756  ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme);
757  if (p == protocol_handlers_.end()) {
758    return NULL;
759  }
760  return &p->second;
761}
762
763void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
764  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
765  ProtocolHandlerMap::const_iterator p = default_handlers_.find(
766      handler.protocol());
767  // If we're not loading, and we are setting a default for a new protocol,
768  // register with the OS.
769  if (!is_loading_ && p == default_handlers_.end())
770      delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
771  default_handlers_.erase(handler.protocol());
772  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
773  PromoteHandler(handler);
774  BrowserThread::PostTask(
775      BrowserThread::IO,
776      FROM_HERE,
777      base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler));
778}
779
780void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) {
781  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
782  ProtocolHandlerMultiMap::iterator p =
783      protocol_handlers_.find(handler.protocol());
784
785  if (p != protocol_handlers_.end()) {
786    p->second.push_back(handler);
787    return;
788  }
789
790  ProtocolHandlerList new_list;
791  new_list.push_back(handler);
792  protocol_handlers_[handler.protocol()] = new_list;
793}
794
795Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
796  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
797  ListValue* protocol_handlers = new ListValue();
798  for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin();
799       i != protocol_handlers_.end(); ++i) {
800    for (ProtocolHandlerList::iterator j = i->second.begin();
801         j != i->second.end(); ++j) {
802      DictionaryValue* encoded = j->Encode();
803      if (IsDefault(*j)) {
804        encoded->Set("default", Value::CreateBooleanValue(true));
805      }
806      protocol_handlers->Append(encoded);
807    }
808  }
809  return protocol_handlers;
810}
811
812Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() {
813  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
814  ListValue* handlers = new ListValue();
815  for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin();
816       i != ignored_protocol_handlers_.end(); ++i) {
817    handlers->Append(i->Encode());
818  }
819  return handlers;
820}
821
822void ProtocolHandlerRegistry::NotifyChanged() {
823  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
824  content::NotificationService::current()->Notify(
825      chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
826      content::Source<Profile>(profile_),
827      content::NotificationService::NoDetails());
828}
829
830void ProtocolHandlerRegistry::RegisterProtocolHandler(
831    const ProtocolHandler& handler) {
832  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
833  DCHECK(CanSchemeBeOverridden(handler.protocol()));
834  DCHECK(!handler.IsEmpty());
835  if (IsRegistered(handler)) {
836    return;
837  }
838  if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol()))
839    delegate_->RegisterExternalHandler(handler.protocol());
840  InsertHandler(handler);
841}
842
843std::vector<const DictionaryValue*>
844ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
845  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
846  std::vector<const DictionaryValue*> result;
847  PrefService* prefs = profile_->GetPrefs();
848  if (!prefs->HasPrefPath(pref_name)) {
849    return result;
850  }
851
852  const ListValue* handlers = prefs->GetList(pref_name);
853  if (handlers) {
854    for (size_t i = 0; i < handlers->GetSize(); ++i) {
855      const DictionaryValue* dict;
856      if (!handlers->GetDictionary(i, &dict))
857        continue;
858      if (ProtocolHandler::IsValidDict(dict)) {
859        result.push_back(dict);
860      }
861    }
862  }
863  return result;
864}
865
866void ProtocolHandlerRegistry::IgnoreProtocolHandler(
867    const ProtocolHandler& handler) {
868  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
869  ignored_protocol_handlers_.push_back(handler);
870}
871
872void ProtocolHandlerRegistry::AddPredefinedHandler(
873    const ProtocolHandler& handler) {
874  DCHECK(!is_loaded_);  // Must be called prior InitProtocolSettings.
875  RegisterProtocolHandler(handler);
876  SetDefault(handler);
877}
878
879scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory>
880ProtocolHandlerRegistry::CreateJobInterceptorFactory() {
881  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
882  // this is always created on the UI thread (in profile_io's
883  // InitializeOnUIThread. Any method calls must be done
884  // on the IO thread (this is checked).
885  return scoped_ptr<JobInterceptorFactory>(new JobInterceptorFactory(
886      io_thread_delegate_));
887}
888