diff options
Diffstat (limited to 'chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc')
-rwxr-xr-x | chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc new file mode 100755 index 00000000000..608da379d81 --- /dev/null +++ b/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc @@ -0,0 +1,923 @@ +// 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 <stddef.h> + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/stl_util.h" +#include "build/build_config.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/profiles/profile_io_data.h" +#include "chrome/common/custom_handlers/protocol_handler.h" +#include "chrome/common/pref_names.h" +#include "chrome/grit/generated_resources.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_service.h" +#include "components/user_prefs/user_prefs.h" +#include "content/public/browser/child_process_security_policy.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 shell_integration::GetDefaultWebClientSetPermission() != + shell_integration::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_CURRENTLY_ON(BrowserThread::IO); + return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty(); +} + +void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault( + const std::string& scheme) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + default_handlers_.erase(scheme); +} + +void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault( + const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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()); + DETACH_FROM_THREAD(thread_checker_); +} + +ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() { +} + +void ProtocolHandlerRegistry::JobInterceptorFactory::Chain( + std::unique_ptr<net::URLRequestJobFactory> job_factory) { + job_factory_ = std::move(job_factory); +} + +net::URLRequestJob* +ProtocolHandlerRegistry::JobInterceptorFactory:: +MaybeCreateJobWithProtocolHandler( + const std::string& scheme, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + net::URLRequestJob* job = io_thread_delegate_->MaybeCreateJob( + request, network_delegate); + if (job) + return job; + return job_factory_->MaybeCreateJobWithProtocolHandler( + scheme, request, network_delegate); +} + +net::URLRequestJob* +ProtocolHandlerRegistry::JobInterceptorFactory::MaybeInterceptRedirect( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const GURL& location) const { + return job_factory_->MaybeInterceptRedirect( + request, network_delegate, location); +} + +net::URLRequestJob* +ProtocolHandlerRegistry::JobInterceptorFactory::MaybeInterceptResponse( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + return job_factory_->MaybeInterceptResponse(request, network_delegate); +} + +bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol( + const std::string& scheme) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return io_thread_delegate_->IsHandledProtocol(scheme) || + job_factory_->IsHandledProtocol(scheme); +} + +bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget( + const GURL& location) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return job_factory_->IsSafeRedirectTarget(location); +} + +// 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); +} + +void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient( + const std::string& protocol, ProtocolHandlerRegistry* registry) { + // The worker pointer is reference counted. While it is running, the + // sequence it runs on will hold references it will be automatically freed + // once all its tasks have finished. + base::MakeRefCounted<shell_integration::DefaultProtocolClientWorker>( + registry->GetDefaultWebClientCallback(protocol), protocol) + ->StartSetAsDefault(); +} + +void ProtocolHandlerRegistry::Delegate::CheckDefaultClientWithOS( + const std::string& protocol, + ProtocolHandlerRegistry* registry) { + // The worker pointer is reference counted. While it is running, the + // sequence it runs on will hold references it will be automatically freed + // once all its tasks have finished. + base::MakeRefCounted<shell_integration::DefaultProtocolClientWorker>( + registry->GetDefaultWebClientCallback(protocol), protocol) + ->StartCheckIsDefault(); +} + +// ProtocolHandlerRegistry ----------------------------------------------------- + +ProtocolHandlerRegistry::ProtocolHandlerRegistry( + content::BrowserContext* context, + Delegate* delegate) + : context_(context), + delegate_(delegate), + enabled_(true), + is_loading_(false), + is_loaded_(false), + io_thread_delegate_(new IOThreadDelegate(enabled_)), + weak_ptr_factory_(this) {} + +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_CURRENTLY_ON(BrowserThread::UI); + RegisterProtocolHandler(handler, USER); + SetDefault(handler); + Save(); + NotifyChanged(); +} + +void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler( + const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + RegisterProtocolHandler(handler, USER); + Save(); + NotifyChanged(); +} + +void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler( + const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + IgnoreProtocolHandler(handler, USER); + Save(); + NotifyChanged(); +} + +bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + + default_handlers_.erase(scheme); + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::BindOnce(&IOThreadDelegate::ClearDefault, + io_thread_delegate_, scheme)); + Save(); + NotifyChanged(); +} + +bool ProtocolHandlerRegistry::IsDefault( + const ProtocolHandler& handler) const { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + + // Any further default additions to the table will get rejected from now on. + is_loaded_ = true; + is_loading_ = true; + + PrefService* prefs = user_prefs::UserPrefs::Get(context_); + 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) { + delegate_->CheckDefaultClientWithOS(p->second.protocol(), this); + } + } +} + +int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); + if (!handlers) { + return false; + } + return base::ContainsValue(*handlers, handler); +} + +bool ProtocolHandlerRegistry::IsRegisteredByUser( + const ProtocolHandler& handler) { + return HandlerExists(handler, &user_protocol_handlers_); +} + +bool ProtocolHandlerRegistry::HasPolicyRegisteredHandler( + const std::string& scheme) { + return (policy_protocol_handlers_.find(scheme) != + policy_protocol_handlers_.end()); +} + +bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + return enabled_ && !GetHandlerFor(scheme).IsEmpty(); +} + +void ProtocolHandlerRegistry::RemoveHandler( + const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(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_); + erase_success = true; + if (!HandlerExists(handler, &policy_protocol_handlers_)) + 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::BindOnce(&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_CURRENTLY_ON(BrowserThread::UI); + ProtocolHandler current_default = GetHandlerFor(scheme); + if (!current_default.IsEmpty()) + RemoveHandler(current_default); +} + +const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor( + const std::string& scheme) const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return LookupHandler(default_handlers_, scheme); +} + +void ProtocolHandlerRegistry::Enable() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (enabled_) { + return; + } + enabled_ = true; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&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_CURRENTLY_ON(BrowserThread::UI); + if (!enabled_) { + return; + } + enabled_ = false; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&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_CURRENTLY_ON(BrowserThread::UI); + delegate_.reset(NULL); + + weak_ptr_factory_.InvalidateWeakPtrs(); +} + +// static +void ProtocolHandlerRegistry::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterListPref(prefs::kRegisteredProtocolHandlers); + registry->RegisterListPref(prefs::kIgnoredProtocolHandlers); + registry->RegisterListPref(prefs::kPolicyRegisteredProtocolHandlers); + registry->RegisterListPref(prefs::kPolicyIgnoredProtocolHandlers); + registry->RegisterBooleanPref(prefs::kCustomHandlersEnabled, true); +} + +ProtocolHandlerRegistry::~ProtocolHandlerRegistry() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + if (is_loading_) { + return; + } + std::unique_ptr<base::Value> registered_protocol_handlers( + EncodeRegisteredHandlers()); + std::unique_ptr<base::Value> ignored_protocol_handlers( + EncodeIgnoredHandlers()); + PrefService* prefs = user_prefs::UserPrefs::Get(context_); + + prefs->Set(prefs::kRegisteredProtocolHandlers, + *registered_protocol_handlers); + prefs->Set(prefs::kIgnoredProtocolHandlers, + *ignored_protocol_handlers); + prefs->SetBoolean(prefs::kCustomHandlersEnabled, enabled_); +} + +const ProtocolHandlerRegistry::ProtocolHandlerList* +ProtocolHandlerRegistry::GetHandlerList( + const std::string& scheme) const { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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::BindOnce(&IOThreadDelegate::SetDefault, + io_thread_delegate_, handler)); +} + +void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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) { + std::unique_ptr<base::DictionaryValue> encoded = j->Encode(); + if (IsDefault(*j)) { + encoded->Set("default", base::MakeUnique<base::Value>(true)); + } + protocol_handlers->Append(std::move(encoded)); + } + } + return protocol_handlers; +} + +base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, + content::Source<content::BrowserContext>(context_), + content::NotificationService::NoDetails()); +} + +void ProtocolHandlerRegistry::RegisterProtocolHandler( + const ProtocolHandler& handler, + const HandlerSource source) { + DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); + std::vector<const base::DictionaryValue*> result; + PrefService* prefs = user_prefs::UserPrefs::Get(context_); + 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_CURRENTLY_ON(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 base::ContainsValue(list, handler); +} + +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::OnSetAsDefaultProtocolClientFinished( + const std::string& protocol, + shell_integration::DefaultWebClientState state) { + // Clear if the default protocol client isn't this installation. + if (ShouldRemoveHandlersNotInOS() && + (state == shell_integration::NOT_DEFAULT || + state == shell_integration::OTHER_MODE_IS_DEFAULT)) { + ClearDefault(protocol); + } +} + +void ProtocolHandlerRegistry::AddPredefinedHandler( + const ProtocolHandler& handler) { + DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings. + RegisterProtocolHandler(handler, USER); + SetDefault(handler); +} + +shell_integration::DefaultWebClientWorkerCallback +ProtocolHandlerRegistry::GetDefaultWebClientCallback( + const std::string& protocol) { + return base::Bind( + &ProtocolHandlerRegistry::OnSetAsDefaultProtocolClientFinished, + weak_ptr_factory_.GetWeakPtr(), protocol); +} + +std::unique_ptr<ProtocolHandlerRegistry::JobInterceptorFactory> +ProtocolHandlerRegistry::CreateJobInterceptorFactory() { + DCHECK_CURRENTLY_ON(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 std::unique_ptr<JobInterceptorFactory>( + new JobInterceptorFactory(io_thread_delegate_.get())); +} |