| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/custom_handlers/protocol_handler_registry.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/prefs/pref_service.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/custom_handlers/register_protocol_handler_infobar_delegate.h" |
| #include "chrome/browser/net/chrome_url_request_context.h" |
| #include "chrome/browser/profiles/profile_io_data.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/custom_handlers/protocol_handler.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "content/public/browser/child_process_security_policy.h" |
| #include "grit/generated_resources.h" |
| #include "net/base/network_delegate.h" |
| #include "net/url_request/url_request_redirect_job.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| using content::BrowserThread; |
| using content::ChildProcessSecurityPolicy; |
| |
| namespace { |
| |
| const ProtocolHandler& LookupHandler( |
| const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map, |
| const std::string& scheme) { |
| ProtocolHandlerRegistry::ProtocolHandlerMap::const_iterator p = |
| handler_map.find(scheme); |
| |
| if (p != handler_map.end()) |
| return p->second; |
| |
| return ProtocolHandler::EmptyProtocolHandler(); |
| } |
| |
| // If true default protocol handlers will be removed if the OS level |
| // registration for a protocol is no longer Chrome. |
| bool ShouldRemoveHandlersNotInOS() { |
| #if defined(OS_LINUX) |
| // We don't do this on Linux as the OS registration there is not reliable, |
| // and Chrome OS doesn't have any notion of OS registration. |
| // TODO(benwells): When Linux support is more reliable remove this |
| // difference (http://crbug.com/88255). |
| return false; |
| #else |
| return ShellIntegration::CanSetAsDefaultProtocolClient() != |
| ShellIntegration::SET_DEFAULT_NOT_ALLOWED; |
| #endif |
| } |
| |
| } // namespace |
| |
| // IOThreadDelegate ------------------------------------------------------------ |
| |
| // IOThreadDelegate is an IO thread specific object. Access to the class should |
| // all be done via the IO thread. The registry living on the UI thread makes |
| // a best effort to update the IO object after local updates are completed. |
| class ProtocolHandlerRegistry::IOThreadDelegate |
| : public base::RefCountedThreadSafe< |
| ProtocolHandlerRegistry::IOThreadDelegate> { |
| public: |
| |
| // Creates a new instance. If |enabled| is true the registry is considered |
| // enabled on the IO thread. |
| explicit IOThreadDelegate(bool enabled); |
| |
| // Returns true if the protocol has a default protocol handler. |
| // Should be called only from the IO thread. |
| bool IsHandledProtocol(const std::string& scheme) const; |
| |
| // Clears the default for the provided protocol. |
| // Should be called only from the IO thread. |
| void ClearDefault(const std::string& scheme); |
| |
| // Makes this ProtocolHandler the default handler for its protocol. |
| // Should be called only from the IO thread. |
| void SetDefault(const ProtocolHandler& handler); |
| |
| // Creates a URL request job for the given request if there is a matching |
| // protocol handler, returns NULL otherwise. |
| net::URLRequestJob* MaybeCreateJob( |
| net::URLRequest* request, net::NetworkDelegate* network_delegate) const; |
| |
| // Indicate that the registry has been enabled in the IO thread's |
| // copy of the data. |
| void Enable() { enabled_ = true; } |
| |
| // Indicate that the registry has been disabled in the IO thread's copy of |
| // the data. |
| void Disable() { enabled_ = false; } |
| |
| private: |
| friend class base::RefCountedThreadSafe<IOThreadDelegate>; |
| virtual ~IOThreadDelegate(); |
| |
| // Copy of protocol handlers use only on the IO thread. |
| ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_; |
| |
| // Is the registry enabled on the IO thread. |
| bool enabled_; |
| |
| DISALLOW_COPY_AND_ASSIGN(IOThreadDelegate); |
| }; |
| |
| ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool) |
| : enabled_(true) {} |
| ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {} |
| |
| bool ProtocolHandlerRegistry::IOThreadDelegate::IsHandledProtocol( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty(); |
| } |
| |
| void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault( |
| const std::string& scheme) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| default_handlers_.erase(scheme); |
| } |
| |
| void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault( |
| const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| ClearDefault(handler.protocol()); |
| default_handlers_.insert(std::make_pair(handler.protocol(), handler)); |
| } |
| |
| // Create a new job for the supplied |URLRequest| if a default handler |
| // is registered and the associated handler is able to interpret |
| // the url from |request|. |
| net::URLRequestJob* ProtocolHandlerRegistry::IOThreadDelegate::MaybeCreateJob( |
| net::URLRequest* request, net::NetworkDelegate* network_delegate) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| ProtocolHandler handler = LookupHandler(default_handlers_, |
| request->url().scheme()); |
| if (handler.IsEmpty()) |
| return NULL; |
| |
| GURL translated_url(handler.TranslateUrl(request->url())); |
| if (!translated_url.is_valid()) |
| return NULL; |
| |
| return new net::URLRequestRedirectJob( |
| request, network_delegate, translated_url, |
| net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, |
| "Protocol Handler Registry"); |
| } |
| |
| // JobInterceptorFactory ------------------------------------------------------- |
| |
| // Instances of JobInterceptorFactory are produced for ownership by the IO |
| // thread where it handler URL requests. We should never hold |
| // any pointers on this class, only produce them in response to |
| // requests via |ProtocolHandlerRegistry::CreateJobInterceptorFactory|. |
| ProtocolHandlerRegistry::JobInterceptorFactory::JobInterceptorFactory( |
| IOThreadDelegate* io_thread_delegate) |
| : io_thread_delegate_(io_thread_delegate) { |
| DCHECK(io_thread_delegate_.get()); |
| DetachFromThread(); |
| } |
| |
| ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() { |
| } |
| |
| void ProtocolHandlerRegistry::JobInterceptorFactory::Chain( |
| scoped_ptr<net::URLRequestJobFactory> job_factory) { |
| job_factory_ = job_factory.Pass(); |
| } |
| |
| net::URLRequestJob* |
| ProtocolHandlerRegistry::JobInterceptorFactory:: |
| MaybeCreateJobWithProtocolHandler( |
| const std::string& scheme, |
| net::URLRequest* request, |
| net::NetworkDelegate* network_delegate) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| net::URLRequestJob* job = io_thread_delegate_->MaybeCreateJob( |
| request, network_delegate); |
| if (job) |
| return job; |
| return job_factory_->MaybeCreateJobWithProtocolHandler( |
| scheme, request, network_delegate); |
| } |
| |
| bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| return io_thread_delegate_->IsHandledProtocol(scheme) || |
| job_factory_->IsHandledProtocol(scheme); |
| } |
| |
| bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledURL( |
| const GURL& url) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| return (url.is_valid() && |
| io_thread_delegate_->IsHandledProtocol(url.scheme())) || |
| job_factory_->IsHandledURL(url); |
| } |
| |
| bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget( |
| const GURL& location) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| return job_factory_->IsSafeRedirectTarget(location); |
| } |
| |
| // DefaultClientObserver ------------------------------------------------------ |
| |
| ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver( |
| ProtocolHandlerRegistry* registry) |
| : worker_(NULL), |
| registry_(registry) { |
| DCHECK(registry_); |
| } |
| |
| ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() { |
| if (worker_) |
| worker_->ObserverDestroyed(); |
| |
| DefaultClientObserverList::iterator iter = std::find( |
| registry_->default_client_observers_.begin(), |
| registry_->default_client_observers_.end(), this); |
| registry_->default_client_observers_.erase(iter); |
| } |
| |
| void ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState( |
| ShellIntegration::DefaultWebClientUIState state) { |
| if (worker_) { |
| if (ShouldRemoveHandlersNotInOS() && |
| (state == ShellIntegration::STATE_NOT_DEFAULT)) { |
| registry_->ClearDefault(worker_->protocol()); |
| } |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| bool ProtocolHandlerRegistry::DefaultClientObserver:: |
| IsInteractiveSetDefaultPermitted() { |
| return true; |
| } |
| |
| void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker( |
| ShellIntegration::DefaultProtocolClientWorker* worker) { |
| worker_ = worker; |
| } |
| |
| bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() { |
| return true; |
| } |
| |
| // Delegate -------------------------------------------------------------------- |
| |
| ProtocolHandlerRegistry::Delegate::~Delegate() {} |
| |
| void ProtocolHandlerRegistry::Delegate::RegisterExternalHandler( |
| const std::string& protocol) { |
| ChildProcessSecurityPolicy* policy = |
| ChildProcessSecurityPolicy::GetInstance(); |
| if (!policy->IsWebSafeScheme(protocol)) { |
| policy->RegisterWebSafeScheme(protocol); |
| } |
| } |
| |
| void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler( |
| const std::string& protocol) { |
| } |
| |
| bool ProtocolHandlerRegistry::Delegate::IsExternalHandlerRegistered( |
| const std::string& protocol) { |
| // NOTE(koz): This function is safe to call from any thread, despite living |
| // in ProfileIOData. |
| return ProfileIOData::IsHandledProtocol(protocol); |
| } |
| |
| ShellIntegration::DefaultProtocolClientWorker* |
| ProtocolHandlerRegistry::Delegate::CreateShellWorker( |
| ShellIntegration::DefaultWebClientObserver* observer, |
| const std::string& protocol) { |
| return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol); |
| } |
| |
| ProtocolHandlerRegistry::DefaultClientObserver* |
| ProtocolHandlerRegistry::Delegate::CreateShellObserver( |
| ProtocolHandlerRegistry* registry) { |
| return new DefaultClientObserver(registry); |
| } |
| |
| void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient( |
| const std::string& protocol, ProtocolHandlerRegistry* registry) { |
| DefaultClientObserver* observer = CreateShellObserver(registry); |
| // The worker pointer is reference counted. While it is running the |
| // message loops of the FILE and UI thread will hold references to it |
| // and it will be automatically freed once all its tasks have finished. |
| scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker; |
| worker = CreateShellWorker(observer, protocol); |
| observer->SetWorker(worker.get()); |
| registry->default_client_observers_.push_back(observer); |
| worker->StartSetAsDefault(); |
| } |
| |
| // ProtocolHandlerRegistry ----------------------------------------------------- |
| |
| ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile, |
| Delegate* delegate) |
| : profile_(profile), |
| delegate_(delegate), |
| enabled_(true), |
| is_loading_(false), |
| is_loaded_(false), |
| io_thread_delegate_(new IOThreadDelegate(enabled_)){ |
| } |
| |
| bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest( |
| const ProtocolHandler& handler) { |
| if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol())) |
| return true; |
| |
| if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler)) |
| return true; |
| |
| if (AttemptReplace(handler)) |
| return true; |
| |
| return false; |
| } |
| |
| void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler( |
| const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| RegisterProtocolHandler(handler, USER); |
| SetDefault(handler); |
| Save(); |
| NotifyChanged(); |
| } |
| |
| void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler( |
| const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| RegisterProtocolHandler(handler, USER); |
| Save(); |
| NotifyChanged(); |
| } |
| |
| void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler( |
| const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| IgnoreProtocolHandler(handler, USER); |
| Save(); |
| NotifyChanged(); |
| } |
| |
| bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandler old_default = GetHandlerFor(handler.protocol()); |
| bool make_new_handler_default = handler.IsSameOrigin(old_default); |
| ProtocolHandlerList to_replace(GetReplacedHandlers(handler)); |
| if (to_replace.empty()) |
| return false; |
| for (ProtocolHandlerList::iterator p = to_replace.begin(); |
| p != to_replace.end(); ++p) { |
| RemoveHandler(*p); |
| } |
| if (make_new_handler_default) { |
| OnAcceptRegisterProtocolHandler(handler); |
| } else { |
| InsertHandler(handler); |
| NotifyChanged(); |
| } |
| return true; |
| } |
| |
| ProtocolHandlerRegistry::ProtocolHandlerList |
| ProtocolHandlerRegistry::GetReplacedHandlers( |
| const ProtocolHandler& handler) const { |
| ProtocolHandlerList replaced_handlers; |
| const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); |
| if (!handlers) |
| return replaced_handlers; |
| for (ProtocolHandlerList::const_iterator p = handlers->begin(); |
| p != handlers->end(); p++) { |
| if (handler.IsSameOrigin(*p)) { |
| replaced_handlers.push_back(*p); |
| } |
| } |
| return replaced_handlers; |
| } |
| |
| void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| default_handlers_.erase(scheme); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_, scheme)); |
| Save(); |
| NotifyChanged(); |
| } |
| |
| bool ProtocolHandlerRegistry::IsDefault( |
| const ProtocolHandler& handler) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| return GetHandlerFor(handler.protocol()) == handler; |
| } |
| |
| void ProtocolHandlerRegistry::InstallDefaultsForChromeOS() { |
| #if defined(OS_CHROMEOS) |
| // Only chromeos has default protocol handlers at this point. |
| AddPredefinedHandler( |
| ProtocolHandler::CreateProtocolHandler( |
| "mailto", |
| GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_MAILTO_HANDLER_URL)))); |
| AddPredefinedHandler( |
| ProtocolHandler::CreateProtocolHandler( |
| "webcal", |
| GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL)))); |
| #else |
| NOTREACHED(); // this method should only ever be called in chromeos. |
| #endif |
| } |
| |
| void ProtocolHandlerRegistry::InitProtocolSettings() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| // Any further default additions to the table will get rejected from now on. |
| is_loaded_ = true; |
| is_loading_ = true; |
| |
| PrefService* prefs = profile_->GetPrefs(); |
| if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) { |
| if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) { |
| Enable(); |
| } else { |
| Disable(); |
| } |
| } |
| |
| RegisterProtocolHandlersFromPref(prefs::kPolicyRegisteredProtocolHandlers, |
| POLICY); |
| RegisterProtocolHandlersFromPref(prefs::kRegisteredProtocolHandlers, USER); |
| IgnoreProtocolHandlersFromPref(prefs::kPolicyIgnoredProtocolHandlers, POLICY); |
| IgnoreProtocolHandlersFromPref(prefs::kIgnoredProtocolHandlers, USER); |
| |
| is_loading_ = false; |
| |
| // For each default protocol handler, check that we are still registered |
| // with the OS as the default application. |
| if (ShouldRemoveHandlersNotInOS()) { |
| for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin(); |
| p != default_handlers_.end(); ++p) { |
| ProtocolHandler handler = p->second; |
| DefaultClientObserver* observer = delegate_->CreateShellObserver(this); |
| scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker; |
| worker = delegate_->CreateShellWorker(observer, handler.protocol()); |
| observer->SetWorker(worker.get()); |
| default_client_observers_.push_back(observer); |
| worker->StartCheckIsDefault(); |
| } |
| } |
| } |
| |
| int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| const ProtocolHandler& handler = GetHandlerFor(scheme); |
| if (handler.IsEmpty()) |
| return -1; |
| const ProtocolHandlerList* handlers = GetHandlerList(scheme); |
| if (!handlers) |
| return -1; |
| |
| ProtocolHandlerList::const_iterator p; |
| int i; |
| for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) { |
| if (*p == handler) |
| return i; |
| } |
| return -1; |
| } |
| |
| ProtocolHandlerRegistry::ProtocolHandlerList |
| ProtocolHandlerRegistry::GetHandlersFor( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme); |
| if (p == protocol_handlers_.end()) { |
| return ProtocolHandlerList(); |
| } |
| return p->second; |
| } |
| |
| ProtocolHandlerRegistry::ProtocolHandlerList |
| ProtocolHandlerRegistry::GetIgnoredHandlers() { |
| return ignored_protocol_handlers_; |
| } |
| |
| void ProtocolHandlerRegistry::GetRegisteredProtocols( |
| std::vector<std::string>* output) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerMultiMap::const_iterator p; |
| for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) { |
| if (!p->second.empty()) |
| output->push_back(p->first); |
| } |
| } |
| |
| bool ProtocolHandlerRegistry::CanSchemeBeOverridden( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| const ProtocolHandlerList* handlers = GetHandlerList(scheme); |
| // If we already have a handler for this scheme, we can add more. |
| if (handlers != NULL && !handlers->empty()) |
| return true; |
| // Don't override a scheme if it already has an external handler. |
| return !delegate_->IsExternalHandlerRegistered(scheme); |
| } |
| |
| bool ProtocolHandlerRegistry::IsRegistered( |
| const ProtocolHandler& handler) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); |
| if (!handlers) { |
| return false; |
| } |
| return std::find(handlers->begin(), handlers->end(), handler) != |
| handlers->end(); |
| } |
| |
| bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerList::const_iterator i; |
| for (i = ignored_protocol_handlers_.begin(); |
| i != ignored_protocol_handlers_.end(); ++i) { |
| if (*i == handler) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool ProtocolHandlerRegistry::HasRegisteredEquivalent( |
| const ProtocolHandler& handler) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); |
| if (!handlers) { |
| return false; |
| } |
| ProtocolHandlerList::const_iterator i; |
| for (i = handlers->begin(); i != handlers->end(); ++i) { |
| if (handler.IsEquivalent(*i)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool ProtocolHandlerRegistry::HasIgnoredEquivalent( |
| const ProtocolHandler& handler) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerList::const_iterator i; |
| for (i = ignored_protocol_handlers_.begin(); |
| i != ignored_protocol_handlers_.end(); ++i) { |
| if (handler.IsEquivalent(*i)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void ProtocolHandlerRegistry::RemoveIgnoredHandler( |
| const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| bool should_notify = false; |
| if (HandlerExists(handler, ignored_protocol_handlers_) && |
| HandlerExists(handler, user_ignored_protocol_handlers_)) { |
| EraseHandler(handler, &user_ignored_protocol_handlers_); |
| Save(); |
| if (!HandlerExists(handler, policy_ignored_protocol_handlers_)) { |
| EraseHandler(handler, &ignored_protocol_handlers_); |
| should_notify = true; |
| } |
| } |
| if (should_notify) |
| NotifyChanged(); |
| } |
| |
| bool ProtocolHandlerRegistry::IsHandledProtocol( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| return enabled_ && !GetHandlerFor(scheme).IsEmpty(); |
| } |
| |
| void ProtocolHandlerRegistry::RemoveHandler( |
| const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()]; |
| bool erase_success = false; |
| if (HandlerExists(handler, handlers) && |
| HandlerExists(handler, &user_protocol_handlers_)) { |
| EraseHandler(handler, &user_protocol_handlers_); |
| if (!HandlerExists(handler, &policy_protocol_handlers_)) { |
| erase_success = true; |
| EraseHandler(handler, &protocol_handlers_); |
| } |
| } |
| ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol()); |
| if (erase_success && q != default_handlers_.end() && q->second == handler) { |
| // Make the new top handler in the list the default. |
| if (!handlers.empty()) { |
| // NOTE We pass a copy because SetDefault() modifies handlers. |
| SetDefault(ProtocolHandler(handlers[0])); |
| } else { |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_, |
| q->second.protocol())); |
| |
| default_handlers_.erase(q); |
| } |
| } |
| |
| if (erase_success && !IsHandledProtocol(handler.protocol())) { |
| delegate_->DeregisterExternalHandler(handler.protocol()); |
| } |
| Save(); |
| if (erase_success) |
| NotifyChanged(); |
| } |
| |
| void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandler current_default = GetHandlerFor(scheme); |
| if (!current_default.IsEmpty()) |
| RemoveHandler(current_default); |
| } |
| |
| const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| return LookupHandler(default_handlers_, scheme); |
| } |
| |
| void ProtocolHandlerRegistry::Enable() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (enabled_) { |
| return; |
| } |
| enabled_ = true; |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_)); |
| |
| ProtocolHandlerMap::const_iterator p; |
| for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { |
| delegate_->RegisterExternalHandler(p->first); |
| } |
| Save(); |
| NotifyChanged(); |
| } |
| |
| void ProtocolHandlerRegistry::Disable() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (!enabled_) { |
| return; |
| } |
| enabled_ = false; |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_)); |
| |
| ProtocolHandlerMap::const_iterator p; |
| for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { |
| delegate_->DeregisterExternalHandler(p->first); |
| } |
| Save(); |
| NotifyChanged(); |
| } |
| |
| void ProtocolHandlerRegistry::Shutdown() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| delegate_.reset(NULL); |
| // We free these now in case there are any outstanding workers running. If |
| // we didn't free them they could respond to workers and try to update the |
| // protocol handler registry after it was deleted. |
| // Observers remove themselves from this list when they are deleted; so |
| // we delete the last item until none are left in the list. |
| while (!default_client_observers_.empty()) { |
| delete default_client_observers_.back(); |
| } |
| } |
| |
| // static |
| void ProtocolHandlerRegistry::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterListPref(prefs::kRegisteredProtocolHandlers, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| registry->RegisterListPref(prefs::kIgnoredProtocolHandlers, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| registry->RegisterListPref(prefs::kPolicyRegisteredProtocolHandlers, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| registry->RegisterListPref(prefs::kPolicyIgnoredProtocolHandlers, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kCustomHandlersEnabled, |
| true, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| } |
| |
| ProtocolHandlerRegistry::~ProtocolHandlerRegistry() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(default_client_observers_.empty()); |
| } |
| |
| void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(IsRegistered(handler)); |
| ProtocolHandlerMultiMap::iterator p = |
| protocol_handlers_.find(handler.protocol()); |
| ProtocolHandlerList& list = p->second; |
| list.erase(std::find(list.begin(), list.end(), handler)); |
| list.insert(list.begin(), handler); |
| } |
| |
| void ProtocolHandlerRegistry::Save() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (is_loading_) { |
| return; |
| } |
| scoped_ptr<base::Value> registered_protocol_handlers( |
| EncodeRegisteredHandlers()); |
| scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers()); |
| scoped_ptr<base::Value> enabled(base::Value::CreateBooleanValue(enabled_)); |
| profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers, |
| *registered_protocol_handlers); |
| profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers, |
| *ignored_protocol_handlers); |
| profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled); |
| } |
| |
| const ProtocolHandlerRegistry::ProtocolHandlerList* |
| ProtocolHandlerRegistry::GetHandlerList( |
| const std::string& scheme) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme); |
| if (p == protocol_handlers_.end()) { |
| return NULL; |
| } |
| return &p->second; |
| } |
| |
| void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerMap::const_iterator p = default_handlers_.find( |
| handler.protocol()); |
| // If we're not loading, and we are setting a default for a new protocol, |
| // register with the OS. |
| if (!is_loading_ && p == default_handlers_.end()) |
| delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this); |
| default_handlers_.erase(handler.protocol()); |
| default_handlers_.insert(std::make_pair(handler.protocol(), handler)); |
| PromoteHandler(handler); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler)); |
| } |
| |
| void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerMultiMap::iterator p = |
| protocol_handlers_.find(handler.protocol()); |
| |
| if (p != protocol_handlers_.end()) { |
| p->second.push_back(handler); |
| return; |
| } |
| |
| ProtocolHandlerList new_list; |
| new_list.push_back(handler); |
| protocol_handlers_[handler.protocol()] = new_list; |
| } |
| |
| base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| base::ListValue* protocol_handlers = new base::ListValue(); |
| for (ProtocolHandlerMultiMap::iterator i = user_protocol_handlers_.begin(); |
| i != user_protocol_handlers_.end(); |
| ++i) { |
| for (ProtocolHandlerList::iterator j = i->second.begin(); |
| j != i->second.end(); ++j) { |
| base::DictionaryValue* encoded = j->Encode(); |
| if (IsDefault(*j)) { |
| encoded->Set("default", base::Value::CreateBooleanValue(true)); |
| } |
| protocol_handlers->Append(encoded); |
| } |
| } |
| return protocol_handlers; |
| } |
| |
| base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| base::ListValue* handlers = new base::ListValue(); |
| for (ProtocolHandlerList::iterator i = |
| user_ignored_protocol_handlers_.begin(); |
| i != user_ignored_protocol_handlers_.end(); |
| ++i) { |
| handlers->Append(i->Encode()); |
| } |
| return handlers; |
| } |
| |
| void ProtocolHandlerRegistry::NotifyChanged() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, |
| content::Source<Profile>(profile_), |
| content::NotificationService::NoDetails()); |
| } |
| |
| void ProtocolHandlerRegistry::RegisterProtocolHandler( |
| const ProtocolHandler& handler, |
| const HandlerSource source) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(CanSchemeBeOverridden(handler.protocol())); |
| DCHECK(!handler.IsEmpty()); |
| ProtocolHandlerMultiMap& map = |
| (source == POLICY) ? policy_protocol_handlers_ : user_protocol_handlers_; |
| ProtocolHandlerList& list = map[handler.protocol()]; |
| if (!HandlerExists(handler, list)) |
| list.push_back(handler); |
| if (IsRegistered(handler)) { |
| return; |
| } |
| if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol())) |
| delegate_->RegisterExternalHandler(handler.protocol()); |
| InsertHandler(handler); |
| } |
| |
| std::vector<const base::DictionaryValue*> |
| ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| std::vector<const base::DictionaryValue*> result; |
| PrefService* prefs = profile_->GetPrefs(); |
| if (!prefs->HasPrefPath(pref_name)) { |
| return result; |
| } |
| |
| const base::ListValue* handlers = prefs->GetList(pref_name); |
| if (handlers) { |
| for (size_t i = 0; i < handlers->GetSize(); ++i) { |
| const base::DictionaryValue* dict; |
| if (!handlers->GetDictionary(i, &dict)) |
| continue; |
| if (ProtocolHandler::IsValidDict(dict)) { |
| result.push_back(dict); |
| } |
| } |
| } |
| return result; |
| } |
| |
| void ProtocolHandlerRegistry::RegisterProtocolHandlersFromPref( |
| const char* pref_name, |
| const HandlerSource source) { |
| std::vector<const base::DictionaryValue*> registered_handlers = |
| GetHandlersFromPref(pref_name); |
| for (std::vector<const base::DictionaryValue*>::const_iterator p = |
| registered_handlers.begin(); |
| p != registered_handlers.end(); |
| ++p) { |
| ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p); |
| RegisterProtocolHandler(handler, source); |
| bool is_default = false; |
| if ((*p)->GetBoolean("default", &is_default) && is_default) { |
| SetDefault(handler); |
| } |
| } |
| } |
| |
| void ProtocolHandlerRegistry::IgnoreProtocolHandler( |
| const ProtocolHandler& handler, |
| const HandlerSource source) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ProtocolHandlerList& list = (source == POLICY) |
| ? policy_ignored_protocol_handlers_ |
| : user_ignored_protocol_handlers_; |
| if (!HandlerExists(handler, list)) |
| list.push_back(handler); |
| if (HandlerExists(handler, ignored_protocol_handlers_)) |
| return; |
| ignored_protocol_handlers_.push_back(handler); |
| } |
| |
| void ProtocolHandlerRegistry::IgnoreProtocolHandlersFromPref( |
| const char* pref_name, |
| const HandlerSource source) { |
| std::vector<const base::DictionaryValue*> ignored_handlers = |
| GetHandlersFromPref(pref_name); |
| for (std::vector<const base::DictionaryValue*>::const_iterator p = |
| ignored_handlers.begin(); |
| p != ignored_handlers.end(); |
| ++p) { |
| IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p), source); |
| } |
| } |
| |
| bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler, |
| ProtocolHandlerMultiMap* map) { |
| return HandlerExists(handler, (*map)[handler.protocol()]); |
| } |
| |
| bool ProtocolHandlerRegistry::HandlerExists(const ProtocolHandler& handler, |
| const ProtocolHandlerList& list) { |
| return std::find(list.begin(), list.end(), handler) != list.end(); |
| } |
| |
| void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler, |
| ProtocolHandlerMultiMap* map) { |
| EraseHandler(handler, &(*map)[handler.protocol()]); |
| } |
| |
| void ProtocolHandlerRegistry::EraseHandler(const ProtocolHandler& handler, |
| ProtocolHandlerList* list) { |
| list->erase(std::find(list->begin(), list->end(), handler)); |
| } |
| |
| void ProtocolHandlerRegistry::AddPredefinedHandler( |
| const ProtocolHandler& handler) { |
| DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings. |
| RegisterProtocolHandler(handler, USER); |
| SetDefault(handler); |
| } |
| |
| scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory> |
| ProtocolHandlerRegistry::CreateJobInterceptorFactory() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| // this is always created on the UI thread (in profile_io's |
| // InitializeOnUIThread. Any method calls must be done |
| // on the IO thread (this is checked). |
| return scoped_ptr<JobInterceptorFactory>( |
| new JobInterceptorFactory(io_thread_delegate_.get())); |
| } |