diff options
author | Jüri Valdmann <juri.valdmann@qt.io> | 2018-01-23 10:33:55 +0100 |
---|---|---|
committer | Jüri Valdmann <juri.valdmann@qt.io> | 2018-01-29 08:22:41 +0000 |
commit | 3541bcae07fcea351a103d0ccbdef7526683de66 (patch) | |
tree | b01353007520024bdcf13cbed392533efdb8a6b1 | |
parent | 37034957c104b571a100130ffe7fcd19340ced51 (diff) | |
download | qtwebengine-chromium-3541bcae07fcea351a103d0ccbdef7526683de66.tar.gz |
Import ProtocolHandlerRegistry and related classes
Import additional files from Chromium 63.0.3239.117.
Task-number: QTBUG-62783
Change-Id: I5ddf38675807d189dd056a79004e2d80abb7fe54
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
6 files changed, 1576 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())); +} diff --git a/chromium/chrome/browser/custom_handlers/protocol_handler_registry.h b/chromium/chrome/browser/custom_handlers/protocol_handler_registry.h new file mode 100755 index 00000000000..92608305bab --- /dev/null +++ b/chromium/chrome/browser/custom_handlers/protocol_handler_registry.h @@ -0,0 +1,370 @@ +// 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. + +#ifndef CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_H_ +#define CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner_helpers.h" +#include "base/values.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/shell_integration.h" +#include "chrome/common/custom_handlers/protocol_handler.h" +#include "components/keyed_service/core/keyed_service.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" +#include "net/url_request/url_request_job_factory.h" + +namespace user_prefs { +class PrefRegistrySyncable; +} + +// This is where handlers for protocols registered with +// navigator.registerProtocolHandler() are registered. Each Profile owns an +// instance of this class, which is initialized on browser start through +// Profile::InitRegisteredProtocolHandlers(), and they should be the only +// instances of this class. +class ProtocolHandlerRegistry : public KeyedService { + public: + enum HandlerSource { + USER, // The handler was installed by user + POLICY, // The handler was installed by policy + }; + + // |Delegate| provides an interface for interacting asynchronously + // with the underlying OS for the purposes of registering Chrome + // as the default handler for specific protocols. + class Delegate { + public: + virtual ~Delegate(); + virtual void RegisterExternalHandler(const std::string& protocol); + virtual void DeregisterExternalHandler(const std::string& protocol); + virtual bool IsExternalHandlerRegistered(const std::string& protocol); + virtual void RegisterWithOSAsDefaultClient( + const std::string& protocol, + ProtocolHandlerRegistry* registry); + virtual void CheckDefaultClientWithOS(const std::string& protocol, + ProtocolHandlerRegistry* registry); + }; + + // Forward declaration of the internal implementation class. + class IOThreadDelegate; + + // JobInterceptorFactory intercepts URLRequestJob creation for URLRequests the + // ProtocolHandlerRegistry is registered to handle. When no handler is + // registered, the URLRequest is passed along to the chained + // URLRequestJobFactory (set with |JobInterceptorFactory::Chain|). + // JobInterceptorFactory's are created via + // |ProtocolHandlerRegistry::CreateJobInterceptorFactory|. + class JobInterceptorFactory : public net::URLRequestJobFactory { + public: + // |io_thread_delegate| is used to perform actual job creation work. + explicit JobInterceptorFactory(IOThreadDelegate* io_thread_delegate); + ~JobInterceptorFactory() override; + + // |job_factory| is set as the URLRequestJobFactory where requests are + // forwarded if JobInterceptorFactory decides to pass on them. + void Chain(std::unique_ptr<net::URLRequestJobFactory> job_factory); + + // URLRequestJobFactory implementation. + net::URLRequestJob* MaybeCreateJobWithProtocolHandler( + const std::string& scheme, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override; + + net::URLRequestJob* MaybeInterceptRedirect( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const GURL& location) const override; + + net::URLRequestJob* MaybeInterceptResponse( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override; + + bool IsHandledProtocol(const std::string& scheme) const override; + bool IsSafeRedirectTarget(const GURL& location) const override; + + private: + // When JobInterceptorFactory decides to pass on particular requests, + // they're forwarded to the chained URLRequestJobFactory, |job_factory_|. + std::unique_ptr<URLRequestJobFactory> job_factory_; + // |io_thread_delegate_| performs the actual job creation decisions by + // mirroring the ProtocolHandlerRegistry on the IO thread. + scoped_refptr<IOThreadDelegate> io_thread_delegate_; + + DISALLOW_COPY_AND_ASSIGN(JobInterceptorFactory); + }; + + typedef std::map<std::string, ProtocolHandler> ProtocolHandlerMap; + typedef std::vector<ProtocolHandler> ProtocolHandlerList; + typedef std::map<std::string, ProtocolHandlerList> ProtocolHandlerMultiMap; + + // Creates a new instance. Assumes ownership of |delegate|. + ProtocolHandlerRegistry(content::BrowserContext* context, Delegate* delegate); + ~ProtocolHandlerRegistry() override; + + // Returns a net::URLRequestJobFactory suitable for use on the IO thread, but + // is initialized on the UI thread. + std::unique_ptr<JobInterceptorFactory> CreateJobInterceptorFactory(); + + // Called when a site tries to register as a protocol handler. If the request + // can be handled silently by the registry - either to ignore the request + // or to update an existing handler - the request will succeed. If this + // function returns false the user needs to be prompted for confirmation. + bool SilentlyHandleRegisterHandlerRequest(const ProtocolHandler& handler); + + // Called when the user accepts the registration of a given protocol handler. + void OnAcceptRegisterProtocolHandler(const ProtocolHandler& handler); + + // Called when the user denies the registration of a given protocol handler. + void OnDenyRegisterProtocolHandler(const ProtocolHandler& handler); + + // Called when the user indicates that they don't want to be asked about the + // given protocol handler again. + void OnIgnoreRegisterProtocolHandler(const ProtocolHandler& handler); + + // Removes all handlers that have the same origin and protocol as the given + // one and installs the given handler. Returns true if any protocol handlers + // were replaced. + bool AttemptReplace(const ProtocolHandler& handler); + + // Returns a list of protocol handlers that can be replaced by the given + // handler. + ProtocolHandlerList GetReplacedHandlers(const ProtocolHandler& handler) const; + + // Clears the default for the provided protocol. + void ClearDefault(const std::string& scheme); + + // Returns true if this handler is the default handler for its protocol. + bool IsDefault(const ProtocolHandler& handler) const; + + // Initializes default protocol settings and loads them from prefs. + // This method must be called to complete initialization of the + // registry after creation, and prior to use. + void InitProtocolSettings(); + + // Returns the offset in the list of handlers for a protocol of the default + // handler for that protocol. + int GetHandlerIndex(const std::string& scheme) const; + + // Get the list of protocol handlers for the given scheme. + ProtocolHandlerList GetHandlersFor(const std::string& scheme) const; + + // Get the list of ignored protocol handlers. + ProtocolHandlerList GetIgnoredHandlers(); + + // Yields a list of the protocols that have handlers registered in this + // registry. + void GetRegisteredProtocols(std::vector<std::string>* output) const; + + // Returns true if we allow websites to register handlers for the given + // scheme. + bool CanSchemeBeOverridden(const std::string& scheme) const; + + // Returns true if an identical protocol handler has already been registered. + bool IsRegistered(const ProtocolHandler& handler) const; + + // Returns true if an identical protocol handler has already been registered + // by the user. + bool IsRegisteredByUser(const ProtocolHandler& handler); + + // Returns true if the scheme has at least one handler that is registered by + // policy. + bool HasPolicyRegisteredHandler(const std::string& scheme); + + // Returns true if an identical protocol handler is being ignored. + bool IsIgnored(const ProtocolHandler& handler) const; + + // Returns true if an equivalent protocol handler has already been registered. + bool HasRegisteredEquivalent(const ProtocolHandler& handler) const; + + // Returns true if an equivalent protocol handler is being ignored. + bool HasIgnoredEquivalent(const ProtocolHandler& handler) const; + + // Causes the given protocol handler to not be ignored anymore. + void RemoveIgnoredHandler(const ProtocolHandler& handler); + + // Returns true if the protocol has a default protocol handler. + bool IsHandledProtocol(const std::string& scheme) const; + + // Removes the given protocol handler from the registry. + void RemoveHandler(const ProtocolHandler& handler); + + // Remove the default handler for the given protocol. + void RemoveDefaultHandler(const std::string& scheme); + + // Returns the default handler for this protocol, or an empty handler if none + // exists. + const ProtocolHandler& GetHandlerFor(const std::string& scheme) const; + + // Puts this registry in the enabled state - registered protocol handlers + // will handle requests. + void Enable(); + + // Puts this registry in the disabled state - registered protocol handlers + // will not handle requests. + void Disable(); + + // This is called by the UI thread when the system is shutting down. This + // does finalization which must be done on the UI thread. + void Shutdown() override; + + // Registers the preferences that we store registered protocol handlers in. + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + bool enabled() const { return enabled_; } + + // Add a predefined protocol handler. This has to be called before the first + // load command was issued, otherwise the command will be ignored. + void AddPredefinedHandler(const ProtocolHandler& handler); + + // Gets the callback for DefaultProtocolClientWorker. Allows the Delegate to + // create the worker on behalf of ProtocolHandlerRegistry. + shell_integration::DefaultWebClientWorkerCallback GetDefaultWebClientCallback( + const std::string& protocol); + + private: + friend class base::DeleteHelper<ProtocolHandlerRegistry>; + friend struct content::BrowserThread::DeleteOnThread< + content::BrowserThread::IO>; + + // for access to InstallDefaultsForChromeOS + friend class ProtocolHandlerRegistryFactory; + + friend class ProtocolHandlerRegistryTest; + friend class RegisterProtocolHandlerBrowserTest; + + // Puts the given handler at the top of the list of handlers for its + // protocol. + void PromoteHandler(const ProtocolHandler& handler); + + // Saves a user's registered protocol handlers. + void Save(); + + // Returns a pointer to the list of handlers registered for the given scheme, + // or NULL if there are none. + const ProtocolHandlerList* GetHandlerList(const std::string& scheme) const; + + // Install default protocol handlers for chromeos which must be done + // prior to calling InitProtocolSettings. + void InstallDefaultsForChromeOS(); + + // Makes this ProtocolHandler the default handler for its protocol. + void SetDefault(const ProtocolHandler& handler); + + // Insert the given ProtocolHandler into the registry. + void InsertHandler(const ProtocolHandler& handler); + + // Returns a JSON list of protocol handlers. The caller is responsible for + // deleting this Value. + base::Value* EncodeRegisteredHandlers(); + + // Returns a JSON list of ignored protocol handlers. The caller is + // responsible for deleting this Value. + base::Value* EncodeIgnoredHandlers(); + + // Sends a notification of the given type to the NotificationService. + void NotifyChanged(); + + // Registers a new protocol handler. + void RegisterProtocolHandler(const ProtocolHandler& handler, + const HandlerSource source); + + // Registers protocol handlers from the preference. + void RegisterProtocolHandlersFromPref(const char* pref_name, + const HandlerSource source); + + // Get the DictionaryValues stored under the given pref name that are valid + // ProtocolHandler values. + std::vector<const base::DictionaryValue*> GetHandlersFromPref( + const char* pref_name) const; + + // Ignores future requests to register the given protocol handler. + void IgnoreProtocolHandler(const ProtocolHandler& handler, + const HandlerSource source); + + // Ignores protocol handlers from the preference. + void IgnoreProtocolHandlersFromPref(const char* pref_name, + const HandlerSource source); + + // Verifies if the handler exists in the map. + bool HandlerExists(const ProtocolHandler& handler, + ProtocolHandlerMultiMap* map); + + // Verifies if the handler exists in the list. + bool HandlerExists(const ProtocolHandler& handler, + const ProtocolHandlerList& list); + + // Erases the handler that is guaranteed to exist from the map. + void EraseHandler(const ProtocolHandler& handler, + ProtocolHandlerMultiMap* map); + + // Erases the handler that is guaranteed to exist from the list. + void EraseHandler(const ProtocolHandler& handler, ProtocolHandlerList* list); + + // Called with the default state when the default protocol client worker is + // done. + void OnSetAsDefaultProtocolClientFinished( + const std::string& protocol, + shell_integration::DefaultWebClientState state); + + // Map from protocols (strings) to protocol handlers. + ProtocolHandlerMultiMap protocol_handlers_; + + // Protocol handlers that the user has told us to ignore. + ProtocolHandlerList ignored_protocol_handlers_; + + // These maps track the source of protocol handler registrations for the + // purposes of disallowing the removal of handlers that are registered by + // policy. Every entry in protocol_handlers_ should exist in at least one of + // the user or policy maps. + ProtocolHandlerMultiMap user_protocol_handlers_; + ProtocolHandlerMultiMap policy_protocol_handlers_; + + // These lists track the source of protocol handlers that were ignored, for + // the purposes of disallowing the removal of handlers that are ignored by + // policy. Every entry in ignored_protocol_handlers_ should exist in at least + // one of the user or policy lists. + ProtocolHandlerList user_ignored_protocol_handlers_; + ProtocolHandlerList policy_ignored_protocol_handlers_; + + // Protocol handlers that are the defaults for a given protocol. + ProtocolHandlerMap default_handlers_; + + // The browser context that owns this ProtocolHandlerRegistry. + content::BrowserContext* context_; + + // The Delegate that registers / deregisters external handlers on our behalf. + std::unique_ptr<Delegate> delegate_; + + // If false then registered protocol handlers will not be used to handle + // requests. + bool enabled_; + + // Whether or not we are loading. + bool is_loading_; + + // When the table gets loaded this flag will be set and any further calls to + // AddPredefinedHandler will be rejected. + bool is_loaded_; + + // Copy of registry data for use on the IO thread. Changes to the registry + // are posted to the IO thread where updates are applied to this object. + scoped_refptr<IOThreadDelegate> io_thread_delegate_; + + // Makes it possible to invalidate the callback for the + // DefaultProtocolClientWorker. + base::WeakPtrFactory<ProtocolHandlerRegistry> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistry); +}; +#endif // CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_H_ diff --git a/chromium/chrome/browser/custom_handlers/protocol_handler_registry_factory.cc b/chromium/chrome/browser/custom_handlers/protocol_handler_registry_factory.cc new file mode 100755 index 00000000000..4f725d2c254 --- /dev/null +++ b/chromium/chrome/browser/custom_handlers/protocol_handler_registry_factory.cc @@ -0,0 +1,70 @@ +// 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_factory.h" + +#include "base/memory/singleton.h" +#include "build/build_config.h" +#include "chrome/browser/custom_handlers/protocol_handler_registry.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +// static +ProtocolHandlerRegistryFactory* ProtocolHandlerRegistryFactory::GetInstance() { + return base::Singleton<ProtocolHandlerRegistryFactory>::get(); +} + +// static +ProtocolHandlerRegistry* ProtocolHandlerRegistryFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<ProtocolHandlerRegistry*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +ProtocolHandlerRegistryFactory::ProtocolHandlerRegistryFactory() + : BrowserContextKeyedServiceFactory( + "ProtocolHandlerRegistry", + BrowserContextDependencyManager::GetInstance()) { +} + +ProtocolHandlerRegistryFactory::~ProtocolHandlerRegistryFactory() { +} + +// Will be created when initializing profile_io_data, so we might +// as well have the framework create this along with other +// PKSs to preserve orderly civic conduct :) +bool +ProtocolHandlerRegistryFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +// Allows the produced registry to be used in incognito mode. +content::BrowserContext* ProtocolHandlerRegistryFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +// Do not create this service for tests. MANY tests will fail +// due to the threading requirements of this service. ALSO, +// not creating this increases test isolation (which is GOOD!) +bool ProtocolHandlerRegistryFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +KeyedService* ProtocolHandlerRegistryFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + ProtocolHandlerRegistry* registry = new ProtocolHandlerRegistry( + context, new ProtocolHandlerRegistry::Delegate()); + +#if defined(OS_CHROMEOS) + // If installing defaults, they must be installed prior calling + // InitProtocolSettings + registry->InstallDefaultsForChromeOS(); +#endif + + // Must be called as a part of the creation process. + registry->InitProtocolSettings(); + + return registry; +} diff --git a/chromium/chrome/browser/custom_handlers/protocol_handler_registry_factory.h b/chromium/chrome/browser/custom_handlers/protocol_handler_registry_factory.h new file mode 100755 index 00000000000..ad03addb325 --- /dev/null +++ b/chromium/chrome/browser/custom_handlers/protocol_handler_registry_factory.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_FACTORY_H_ +#define CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; +class ProtocolHandlerRegistry; + +namespace base { +template <typename T> struct DefaultSingletonTraits; +} + +// Singleton that owns all ProtocolHandlerRegistrys and associates them with +// Profiles. Listens for the Profile's destruction notification and cleans up +// the associated ProtocolHandlerRegistry. +class ProtocolHandlerRegistryFactory + : public BrowserContextKeyedServiceFactory { + public: + // Returns the singleton instance of the ProtocolHandlerRegistryFactory. + static ProtocolHandlerRegistryFactory* GetInstance(); + + // Returns the ProtocolHandlerRegistry that provides intent registration for + // |context|. Ownership stays with this factory object. + static ProtocolHandlerRegistry* GetForBrowserContext( + content::BrowserContext* context); + + protected: + // BrowserContextKeyedServiceFactory implementation. + bool ServiceIsCreatedWithBrowserContext() const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsNULLWhileTesting() const override; + + private: + friend struct base::DefaultSingletonTraits<ProtocolHandlerRegistryFactory>; + + ProtocolHandlerRegistryFactory(); + ~ProtocolHandlerRegistryFactory() override; + + // BrowserContextKeyedServiceFactory implementation. + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + + DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistryFactory); +}; + +#endif // CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_FACTORY_H_ diff --git a/chromium/chrome/common/custom_handlers/protocol_handler.cc b/chromium/chrome/common/custom_handlers/protocol_handler.cc new file mode 100755 index 00000000000..85749b3ff2d --- /dev/null +++ b/chromium/chrome/common/custom_handlers/protocol_handler.cc @@ -0,0 +1,87 @@ +// 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/common/custom_handlers/protocol_handler.h" + +#include "base/memory/ptr_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "net/base/escape.h" + + +ProtocolHandler::ProtocolHandler(const std::string& protocol, + const GURL& url) + : protocol_(protocol), + url_(url) { +} + +ProtocolHandler ProtocolHandler::CreateProtocolHandler( + const std::string& protocol, + const GURL& url) { + std::string lower_protocol = base::ToLowerASCII(protocol); + return ProtocolHandler(lower_protocol, url); +} + +ProtocolHandler::ProtocolHandler() { +} + +bool ProtocolHandler::IsValidDict(const base::DictionaryValue* value) { + // Note that "title" parameter is ignored. + return value->HasKey("protocol") && value->HasKey("url"); +} + +bool ProtocolHandler::IsSameOrigin( + const ProtocolHandler& handler) const { + return handler.url().GetOrigin() == url_.GetOrigin(); +} + +const ProtocolHandler& ProtocolHandler::EmptyProtocolHandler() { + static const ProtocolHandler* const kEmpty = new ProtocolHandler(); + return *kEmpty; +} + +ProtocolHandler ProtocolHandler::CreateProtocolHandler( + const base::DictionaryValue* value) { + if (!IsValidDict(value)) { + return EmptyProtocolHandler(); + } + std::string protocol, url; + value->GetString("protocol", &protocol); + value->GetString("url", &url); + return ProtocolHandler::CreateProtocolHandler(protocol, GURL(url)); +} + +GURL ProtocolHandler::TranslateUrl(const GURL& url) const { + std::string translatedUrlSpec(url_.spec()); + base::ReplaceSubstringsAfterOffset(&translatedUrlSpec, 0, "%s", + net::EscapeQueryParamValue(url.spec(), true)); + return GURL(translatedUrlSpec); +} + +std::unique_ptr<base::DictionaryValue> ProtocolHandler::Encode() const { + auto d = base::MakeUnique<base::DictionaryValue>(); + d->SetString("protocol", protocol_); + d->SetString("url", url_.spec()); + return d; +} + +#if !defined(NDEBUG) +std::string ProtocolHandler::ToString() const { + return "{ protocol=" + protocol_ + + ", url=" + url_.spec() + + " }"; +} +#endif + +bool ProtocolHandler::operator==(const ProtocolHandler& other) const { + return protocol_ == other.protocol_ && url_ == other.url_; +} + +bool ProtocolHandler::IsEquivalent(const ProtocolHandler& other) const { + return protocol_ == other.protocol_ && url_ == other.url_; +} + +bool ProtocolHandler::operator<(const ProtocolHandler& other) const { + return url_ < other.url_; +} diff --git a/chromium/chrome/common/custom_handlers/protocol_handler.h b/chromium/chrome/common/custom_handlers/protocol_handler.h new file mode 100755 index 00000000000..69c195c7c56 --- /dev/null +++ b/chromium/chrome/common/custom_handlers/protocol_handler.h @@ -0,0 +1,73 @@ +// 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. + +#ifndef CHROME_COMMON_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_ +#define CHROME_COMMON_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_ + +#include <memory> +#include <string> + +#include "base/values.h" +#include "url/gurl.h" + +// A single tuple of (protocol, url) that indicates how URLs of the +// given protocol should be rewritten to be handled. + +class ProtocolHandler { + public: + static ProtocolHandler CreateProtocolHandler(const std::string& protocol, + const GURL& url); + + // Creates a ProtocolHandler with fields from the dictionary. Returns an + // empty ProtocolHandler if the input is invalid. + static ProtocolHandler CreateProtocolHandler( + const base::DictionaryValue* value); + + // Returns true if the dictionary value has all the necessary fields to + // define a ProtocolHandler. + static bool IsValidDict(const base::DictionaryValue* value); + + // Returns true if this handler's url has the same origin as the given one. + bool IsSameOrigin(const ProtocolHandler& handler) const; + + // Canonical empty ProtocolHandler. + static const ProtocolHandler& EmptyProtocolHandler(); + + // Interpolates the given URL into the URL template of this handler. + GURL TranslateUrl(const GURL& url) const; + + // Returns true if the handlers are considered equivalent when determining + // if both handlers can be registered, or if a handler has previously been + // ignored. + bool IsEquivalent(const ProtocolHandler& other) const; + + // Encodes this protocol handler as a DictionaryValue. + std::unique_ptr<base::DictionaryValue> Encode() const; + + const std::string& protocol() const { return protocol_; } + const GURL& url() const { return url_;} + + bool IsEmpty() const { + return protocol_.empty(); + } + +#if !defined(NDEBUG) + // Returns a string representation suitable for use in debugging. + std::string ToString() const; +#endif + + + bool operator==(const ProtocolHandler& other) const; + bool operator<(const ProtocolHandler& other) const; + + private: + ProtocolHandler(const std::string& protocol, + const GURL& url); + ProtocolHandler(); + + std::string protocol_; + GURL url_; +}; + +#endif // CHROME_COMMON_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_ |