diff options
author | smckay@chromium.org <smckay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-20 01:52:02 +0000 |
---|---|---|
committer | smckay@chromium.org <smckay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-20 01:52:02 +0000 |
commit | 26effd10fe11d820b3040e72d85e00dd3bd524d5 (patch) | |
tree | 604505f94bf6cd372a8fc63a7337c9435bcd3b58 /chrome/browser/custom_handlers | |
parent | 0397dea43ea8a08e496971c80795970db5ad0644 (diff) | |
download | chromium_src-26effd10fe11d820b3040e72d85e00dd3bd524d5.zip chromium_src-26effd10fe11d820b3040e72d85e00dd3bd524d5.tar.gz chromium_src-26effd10fe11d820b3040e72d85e00dd3bd524d5.tar.bz2 |
Convert ProtocolHandlerRegistry to be a ProfileKeyedService.
BUG=129200
TEST=protocol_handler_registry_browsertest.cc,protocol_handler_registry_unittest.cc
willchan@ -> profile_io changes
+bauerb -> content_settings changes
Review URL: https://chromiumcodereview.appspot.com/10546083
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147597 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/custom_handlers')
5 files changed, 478 insertions, 198 deletions
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chrome/browser/custom_handlers/protocol_handler_registry.cc index 5a6dfae..712f791 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry.cc +++ b/chrome/browser/custom_handlers/protocol_handler_registry.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/custom_handlers/register_protocol_handler_infobar_delegate.h" #include "chrome/browser/net/chrome_url_request_context.h" @@ -21,14 +22,30 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/notification_service.h" +#include "grit/generated_resources.h" #include "net/base/network_delegate.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.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() { @@ -44,17 +61,165 @@ bool ShouldRemoveHandlersNotInOS() { #endif } -} // namespace +void InstallDefaultProtocolHandlers(ProtocolHandlerRegistry* registry) { + // Only chromeos has default protocol handlers at this point. + #if defined(OS_CHROMEOS) + registry->AddPredefinedHandler( + ProtocolHandler::CreateProtocolHandler( + "mailto", + GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_MAILTO_HANDLER_URL)), + l10n_util::GetStringUTF16(IDS_GOOGLE_MAILTO_HANDLER_NAME))); + registry->AddPredefinedHandler( + ProtocolHandler::CreateProtocolHandler( + "webcal", + GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL)), + l10n_util::GetStringUTF16(IDS_GOOGLE_WEBCAL_HANDLER_NAME))); + #endif +} -static 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; +} // namespace + +// Core ------------------------------------------------------------------------ + +// Core 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::Core + : public base::RefCountedThreadSafe<ProtocolHandlerRegistry::Core> { + public: + + // Creates a new instance. If |enabled| is true the registry is considered + // enabled on the IO thread. + explicit Core(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) 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<Core>; + virtual ~Core(); + + // 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(Core); +}; + +ProtocolHandlerRegistry::Core::Core(bool) : enabled_(true) {} +ProtocolHandlerRegistry::Core::~Core() {} + +bool ProtocolHandlerRegistry::Core::IsHandledProtocol( + const std::string& scheme) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty(); +} + +void ProtocolHandlerRegistry::Core::ClearDefault(const std::string& scheme) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + default_handlers_.erase(scheme); +} + +void ProtocolHandlerRegistry::Core::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::Core::MaybeCreateJob( + net::URLRequest* request) 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, translated_url); +} + +// URLInterceptor ------------------------------------------------------------ + +// Instances of this class 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::CreateURLInterceptor|. +class ProtocolHandlerRegistry::URLInterceptor + : public net::URLRequestJobFactory::Interceptor { + public: + explicit URLInterceptor(Core* core); + virtual ~URLInterceptor(); + + virtual net::URLRequestJob* MaybeIntercept( + net::URLRequest* request) const OVERRIDE; + + virtual bool WillHandleProtocol(const std::string& protocol) const OVERRIDE; + + virtual net::URLRequestJob* MaybeInterceptRedirect( + const GURL& url, net::URLRequest* request) const OVERRIDE { + return NULL; } - return ProtocolHandler::EmptyProtocolHandler(); + + virtual net::URLRequestJob* MaybeInterceptResponse( + net::URLRequest* request) const OVERRIDE { + return NULL; + } + + private: + scoped_refptr<Core> core_; + DISALLOW_COPY_AND_ASSIGN(URLInterceptor); +}; + +ProtocolHandlerRegistry::URLInterceptor::URLInterceptor(Core* core) + : core_(core) { + DCHECK(core_); +} + +ProtocolHandlerRegistry::URLInterceptor::~URLInterceptor() { +} + +net::URLRequestJob* ProtocolHandlerRegistry::URLInterceptor::MaybeIntercept( + net::URLRequest* request) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + return core_->MaybeCreateJob(request); +} + +bool ProtocolHandlerRegistry::URLInterceptor::WillHandleProtocol( + const std::string& protocol) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + return core_->IsHandledProtocol(protocol); } // DefaultClientObserver ------------------------------------------------------ @@ -76,8 +241,7 @@ ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() { registry_->default_client_observers_.erase(iter); } -void -ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState( +void ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState( ShellIntegration::DefaultWebClientUIState state) { if (worker_) { if (ShouldRemoveHandlersNotInOS() && @@ -151,9 +315,9 @@ ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile, : profile_(profile), delegate_(delegate), enabled_(true), - enabled_io_(enabled_), is_loading_(false), - is_loaded_(false) { + is_loaded_(false), + core_(new Core(enabled_)){ } bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest( @@ -233,11 +397,12 @@ ProtocolHandlerRegistry::GetReplacedHandlers( void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + default_handlers_.erase(scheme); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&ProtocolHandlerRegistry::ClearDefaultIO, this, scheme)); + base::Bind(&Core::ClearDefault, core_, scheme)); Save(); NotifyChanged(); } @@ -248,19 +413,23 @@ bool ProtocolHandlerRegistry::IsDefault( return GetHandlerFor(handler.protocol()) == handler; } -void ProtocolHandlerRegistry::Load() { +void ProtocolHandlerRegistry::InitProtocolSettings() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Default protocol handlers must be installed first. + InstallDefaultProtocolHandlers(this); + // Any further default additions to the table will get rejected from now on. is_loaded_ = true; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); is_loading_ = true; + PrefService* prefs = profile_->GetPrefs(); if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) { - enabled_ = prefs->GetBoolean(prefs::kCustomHandlersEnabled); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(enabled_ ? &ProtocolHandlerRegistry::EnableIO : - &ProtocolHandlerRegistry::DisableIO, this)); + if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) { + Enable(); + } else { + Disable(); + } } std::vector<const DictionaryValue*> registered_handlers = GetHandlersFromPref(prefs::kRegisteredProtocolHandlers); @@ -428,12 +597,6 @@ bool ProtocolHandlerRegistry::IsHandledProtocol( return enabled_ && !GetHandlerFor(scheme).IsEmpty(); } -bool ProtocolHandlerRegistry::IsHandledProtocolIO( - const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - return enabled_io_ && !LookupHandler(default_handlers_io_, scheme).IsEmpty(); -} - void ProtocolHandlerRegistry::RemoveHandler( const ProtocolHandler& handler) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -452,8 +615,8 @@ void ProtocolHandlerRegistry::RemoveHandler( } else { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&ProtocolHandlerRegistry::ClearDefaultIO, this, - q->second.protocol())); + base::Bind(&Core::ClearDefault, core_, q->second.protocol())); + default_handlers_.erase(q); } } @@ -478,21 +641,6 @@ const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor( return LookupHandler(default_handlers_, scheme); } -net::URLRequestJob* ProtocolHandlerRegistry::MaybeCreateJob( - net::URLRequest* request) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ProtocolHandler handler = LookupHandler(default_handlers_io_, - 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, translated_url); -} - void ProtocolHandlerRegistry::Enable() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (enabled_) { @@ -502,7 +650,8 @@ void ProtocolHandlerRegistry::Enable() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&ProtocolHandlerRegistry::EnableIO, this)); + base::Bind(&Core::Enable, core_)); + ProtocolHandlerMap::const_iterator p; for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { delegate_->RegisterExternalHandler(p->first); @@ -520,7 +669,8 @@ void ProtocolHandlerRegistry::Disable() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&ProtocolHandlerRegistry::DisableIO, this)); + base::Bind(&Core::Disable, core_)); + ProtocolHandlerMap::const_iterator p; for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { delegate_->DeregisterExternalHandler(p->first); @@ -529,7 +679,7 @@ void ProtocolHandlerRegistry::Disable() { NotifyChanged(); } -void ProtocolHandlerRegistry::Finalize() { +void ProtocolHandlerRegistry::Shutdown() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); delegate_.reset(NULL); // We free these now in case there are any outstanding workers running. If @@ -553,7 +703,7 @@ void ProtocolHandlerRegistry::RegisterPrefs(PrefService* pref_service) { } ProtocolHandlerRegistry::~ProtocolHandlerRegistry() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(default_client_observers_.empty()); } @@ -567,17 +717,6 @@ void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) { list.insert(list.begin(), handler); } -void ProtocolHandlerRegistry::ClearDefaultIO(const std::string& scheme) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - default_handlers_io_.erase(scheme); -} - -void ProtocolHandlerRegistry::SetDefaultIO(const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ClearDefaultIO(handler.protocol()); - default_handlers_io_.insert(std::make_pair(handler.protocol(), handler)); -} - void ProtocolHandlerRegistry::Save() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (is_loading_) { @@ -618,7 +757,7 @@ void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&ProtocolHandlerRegistry::SetDefaultIO, this, handler)); + base::Bind(&Core::SetDefault, core_, handler)); } void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) { @@ -715,10 +854,16 @@ void ProtocolHandlerRegistry::IgnoreProtocolHandler( void ProtocolHandlerRegistry::AddPredefinedHandler( const ProtocolHandler& handler) { - // If called after the load command was issued this function will fail. - DCHECK(!is_loaded_); + DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings. RegisterProtocolHandler(handler); SetDefault(handler); } - +net::URLRequestJobFactory::Interceptor* + ProtocolHandlerRegistry::CreateURLInterceptor() { + 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 new URLInterceptor(core_); +} diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.h b/chrome/browser/custom_handlers/protocol_handler_registry.h index a74a765..82f608db 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry.h +++ b/chrome/browser/custom_handlers/protocol_handler_registry.h @@ -20,17 +20,18 @@ #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" // 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 ProfileKeyedService { -class ProtocolHandlerRegistry - : public base::RefCountedThreadSafe< - ProtocolHandlerRegistry, content::BrowserThread::DeleteOnIOThread> { public: + // Provides notification of when the OS level user agent settings + // are changed. class DefaultClientObserver : public ShellIntegration::DefaultWebClientObserver { public: @@ -59,9 +60,9 @@ class ProtocolHandlerRegistry DISALLOW_COPY_AND_ASSIGN(DefaultClientObserver); }; - // TODO(koz): Refactor this to eliminate the unnecessary virtuals. All that - // should be needed is a way to ensure that the list of websafe protocols is - // updated. + // |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(); @@ -83,7 +84,14 @@ class ProtocolHandlerRegistry typedef std::map<std::string, ProtocolHandlerList> ProtocolHandlerMultiMap; typedef std::vector<DefaultClientObserver*> DefaultClientObserverList; + // Creates a new instance. Assumes ownership of |delegate|. ProtocolHandlerRegistry(Profile* profile, Delegate* delegate); + virtual ~ProtocolHandlerRegistry(); + + // Returns a net::URLRequestJobFactory::Interceptor suitable + // for use on the IO thread, but is initialized on the UI thread. + // Callers assume responsibility for deleting this object. + net::URLRequestJobFactory::Interceptor* CreateURLInterceptor(); // 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 @@ -116,8 +124,10 @@ class ProtocolHandlerRegistry // Returns true if this handler is the default handler for its protocol. bool IsDefault(const ProtocolHandler& handler) const; - // Loads a user's registered protocol handlers. - void Load(); + // 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. @@ -155,10 +165,6 @@ class ProtocolHandlerRegistry // Returns true if the protocol has a default protocol handler. bool IsHandledProtocol(const std::string& scheme) const; - // Returns true if the protocol has a default protocol handler. - // Should be called only from the IO thread. - bool IsHandledProtocolIO(const std::string& scheme) const; - // Removes the given protocol handler from the registry. void RemoveHandler(const ProtocolHandler& handler); @@ -169,10 +175,6 @@ class ProtocolHandlerRegistry // exists. const ProtocolHandler& GetHandlerFor(const std::string& scheme) const; - // 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) const; - // Puts this registry in the enabled state - registered protocol handlers // will handle requests. void Enable(); @@ -183,7 +185,7 @@ class ProtocolHandlerRegistry // 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 Finalize(); + virtual void Shutdown() OVERRIDE; // Registers the preferences that we store registered protocol handlers in. static void RegisterPrefs(PrefService* prefService); @@ -201,28 +203,14 @@ class ProtocolHandlerRegistry friend class ProtocolHandlerRegistryTest; friend class RegisterProtocolHandlerBrowserTest; - ~ProtocolHandlerRegistry(); + // Forward declaration of the internal implementation classes. + class Core; + class URLInterceptor; // Puts the given handler at the top of the list of handlers for its // protocol. void PromoteHandler(const ProtocolHandler& handler); - // Clears the default for the provided protocol. - // Should be called only from the IO thread. - void ClearDefaultIO(const std::string& scheme); - - // Makes this ProtocolHandler the default handler for its protocol. - // Should be called only from the IO thread. - void SetDefaultIO(const ProtocolHandler& handler); - - // Indicate that the registry has been enabled in the IO thread's copy of the - // data. - void EnableIO() { enabled_io_ = true; } - - // Indicate that the registry has been disabled in the IO thread's copy of - // the data. - void DisableIO() { enabled_io_ = false; } - // Saves a user's registered protocol handlers. void Save(); @@ -277,9 +265,6 @@ class ProtocolHandlerRegistry // requests. bool enabled_; - // Copy of enabled_ that is only accessed on the IO thread. - bool enabled_io_; - // Whether or not we are loading. bool is_loading_; @@ -287,10 +272,11 @@ class ProtocolHandlerRegistry // AddPredefinedHandler will be rejected. bool is_loaded_; - DefaultClientObserverList default_client_observers_; + // 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<Core> core_; - // Copy of default_handlers_ that is only accessed on the IO thread. - ProtocolHandlerMap default_handlers_io_; + DefaultClientObserverList default_client_observers_; DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistry); }; diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_factory.cc b/chrome/browser/custom_handlers/protocol_handler_registry_factory.cc new file mode 100644 index 0000000..63a3548 --- /dev/null +++ b/chrome/browser/custom_handlers/protocol_handler_registry_factory.cc @@ -0,0 +1,61 @@ +// 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 "chrome/browser/custom_handlers/protocol_handler_registry.h" +#include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_dependency_manager.h" + +// static +ProtocolHandlerRegistryFactory* ProtocolHandlerRegistryFactory::GetInstance() { + return Singleton<ProtocolHandlerRegistryFactory>::get(); +} + +// static +ProtocolHandlerRegistry* ProtocolHandlerRegistryFactory::GetForProfile( + Profile* profile) { + return static_cast<ProtocolHandlerRegistry*>( + GetInstance()->GetServiceForProfile(profile, true)); +} + +ProtocolHandlerRegistryFactory::ProtocolHandlerRegistryFactory() + : ProfileKeyedServiceFactory("ProtocolHandlerRegistry", + ProfileDependencyManager::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::ServiceIsCreatedWithProfile() { + return true; +} + +// Allows the produced registry to be used in incognito mode. +bool ProtocolHandlerRegistryFactory::ServiceRedirectedInIncognito() { + return true; +} + +// 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() { + return true; +} + +ProfileKeyedService* ProtocolHandlerRegistryFactory::BuildServiceInstanceFor( + Profile* profile) const { + ProtocolHandlerRegistry* registry = new ProtocolHandlerRegistry( + profile, new ProtocolHandlerRegistry::Delegate()); + + // Must be called as a part of the creation process. + registry->InitProtocolSettings(); + + return registry; +} diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_factory.h b/chrome/browser/custom_handlers/protocol_handler_registry_factory.h new file mode 100644 index 0000000..343303f --- /dev/null +++ b/chrome/browser/custom_handlers/protocol_handler_registry_factory.h @@ -0,0 +1,47 @@ +// 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/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +class Profile; +class ProtocolHandlerRegistry; +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 ProfileKeyedServiceFactory { + public: + // Returns the singleton instance of the ProtocolHandlerRegistryFactory. + static ProtocolHandlerRegistryFactory* GetInstance(); + + // Returns the ProtocolHandlerRegistry that provides intent registration for + // |profile|. Ownership stays with this factory object. + static ProtocolHandlerRegistry* GetForProfile(Profile* profile); + + protected: + // ProfileKeyedServiceFactory implementation. + virtual bool ServiceIsCreatedWithProfile() OVERRIDE; + virtual bool ServiceRedirectedInIncognito() OVERRIDE; + virtual bool ServiceIsNULLWhileTesting() OVERRIDE; + + private: + friend struct DefaultSingletonTraits<ProtocolHandlerRegistryFactory>; + + ProtocolHandlerRegistryFactory(); + virtual ~ProtocolHandlerRegistryFactory(); + + // ProfileKeyedServiceFactory implementation. + virtual ProfileKeyedService* BuildServiceInstanceFor( + Profile* profile) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistryFactory); +}; + +#endif // CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_FACTORY_H_ diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc index 1330661..029e99d1 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc +++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" +#include "base/synchronization/waitable_event.h" #include "base/utf_string_conversions.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/custom_handlers/protocol_handler.h" @@ -21,11 +22,56 @@ #include "content/public/test/test_renderer_host.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "testing/gtest/include/gtest/gtest.h" using content::BrowserThread; namespace { +void AssertInterceptedIO( + const GURL& url, + net::URLRequestJobFactory::Interceptor* interceptor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + net::URLRequestContext context; + net::URLRequest request(url, NULL, &context); + scoped_refptr<net::URLRequestJob> job = interceptor->MaybeIntercept(&request); + ASSERT_TRUE(job.get() != NULL); +} + +void AssertIntercepted( + const GURL& url, + net::URLRequestJobFactory::Interceptor* interceptor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(AssertInterceptedIO, + url, + base::Unretained(interceptor))); + MessageLoop::current()->RunAllPending(); +} + +void AssertWillHandleIO( + const std::string& scheme, + bool expected, + net::URLRequestJobFactory::Interceptor* interceptor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ASSERT_EQ(expected, interceptor->WillHandleProtocol(scheme)); +} + +void AssertWillHandle( + const std::string& scheme, + bool expected, + net::URLRequestJobFactory::Interceptor* interceptor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(AssertWillHandleIO, + scheme, + expected, + base::Unretained(interceptor))); + MessageLoop::current()->RunAllPending(); +} + class FakeDelegate : public ProtocolHandlerRegistry::Delegate { public: FakeDelegate() : force_os_failure_(false) {} @@ -174,7 +220,7 @@ class QueryProtocolHandlerOnChange public: QueryProtocolHandlerOnChange(Profile* profile, ProtocolHandlerRegistry* registry) - : registry_(registry), + : local_registry_(registry), called_(false), notification_registrar_() { notification_registrar_.Add(this, @@ -186,26 +232,53 @@ class QueryProtocolHandlerOnChange const content::NotificationSource& source, const content::NotificationDetails& details) { std::vector<std::string> output; - registry_->GetRegisteredProtocols(&output); + local_registry_->GetRegisteredProtocols(&output); called_ = true; } - ProtocolHandlerRegistry* registry_; + ProtocolHandlerRegistry* local_registry_; bool called_; content::NotificationRegistrar notification_registrar_; }; +// URLRequest DCHECKS that the current MessageLoop is IO. It does this because +// it can't check the thread id (since net can't depend on content.) We want +// to harness our tests so all threads use the same loop allowing us to +// guarantee all messages are processed.) By overriding the IsType method +// we basically ignore the supplied message loop type, and instead infer +// our type based on the current thread. GO DEPENDENCY INJECTION! +class TestMessageLoop : public MessageLoop { + public: + TestMessageLoop() : MessageLoop(MessageLoop::TYPE_DEFAULT) {} + ~TestMessageLoop() {} + virtual bool IsType(MessageLoop::Type type) const OVERRIDE { + switch (type) { + case MessageLoop::TYPE_UI: + return BrowserThread::CurrentlyOn(BrowserThread::UI); + case MessageLoop::TYPE_IO: + return BrowserThread::CurrentlyOn(BrowserThread::IO); + case MessageLoop::TYPE_DEFAULT: + return !BrowserThread::CurrentlyOn(BrowserThread::UI) && + !BrowserThread::CurrentlyOn(BrowserThread::IO); + } + return false; + } +}; + } // namespace class ProtocolHandlerRegistryTest : public testing::Test { protected: ProtocolHandlerRegistryTest() - : test_protocol_handler_(CreateProtocolHandler("test", "test")) {} + : ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_), + io_thread_(BrowserThread::IO, &loop_), + test_protocol_handler_(CreateProtocolHandler("test", "test")) {} FakeDelegate* delegate() const { return delegate_; } + ProtocolHandlerRegistry* registry() { return registry_.get(); } TestingProfile* profile() const { return profile_.get(); } PrefService* pref_service() const { return profile_->GetPrefs(); } - ProtocolHandlerRegistry* registry() const { return registry_.get(); } const ProtocolHandler& test_protocol_handler() const { return test_protocol_handler_; } @@ -223,68 +296,48 @@ class ProtocolHandlerRegistryTest : public testing::Test { name); } - void ReloadProtocolHandlerRegistry() { - delegate_ = new FakeDelegate(); - registry_->Finalize(); - registry_ = NULL; - registry_ = new ProtocolHandlerRegistry(profile(), delegate()); - registry_->Load(); + void RecreateRegistry(bool initialize) { + TeadDownRegistry(); + SetUpRegistry(initialize); } - void ReloadProtocolHandlerRegistryAndInstallDefaultHandler() { + // Returns a new registry, initializing it if |initialize| is true. + // Caller assumes ownership for the object + void SetUpRegistry(bool initialize) { delegate_ = new FakeDelegate(); - registry_->Finalize(); - registry_ = NULL; - registry_ = new ProtocolHandlerRegistry(profile(), delegate()); - registry_->AddPredefinedHandler(CreateProtocolHandler( - "test", GURL("http://test.com/%s"), "Test")); - registry_->Load(); + registry_.reset(new ProtocolHandlerRegistry(profile(), delegate())); + if (initialize) registry_->InitProtocolSettings(); } - virtual void SetUp() { - ui_message_loop_.reset(new MessageLoopForUI()); - ui_thread_.reset(new content::TestBrowserThread(BrowserThread::UI, - MessageLoop::current())); - io_thread_.reset(new content::TestBrowserThread(BrowserThread::IO)); - io_thread_->StartIOThread(); - - file_thread_.reset(new content::TestBrowserThread(BrowserThread::FILE)); - file_thread_->Start(); + void TeadDownRegistry() { + registry_->Shutdown(); + registry_.reset(); + // Registry owns the delegate_ it handles deletion of that object. + } + virtual void SetUp() { profile_.reset(new TestingProfile()); profile_->SetPrefService(new TestingPrefService()); - delegate_ = new FakeDelegate(); - registry_ = new ProtocolHandlerRegistry(profile(), delegate()); - registry_->Load(); + SetUpRegistry(true); test_protocol_handler_ = CreateProtocolHandler("test", GURL("http://test.com/%s"), "Test"); - ProtocolHandlerRegistry::RegisterPrefs(pref_service()); } virtual void TearDown() { - registry_->Finalize(); - registry_ = NULL; - io_thread_->Stop(); - io_thread_.reset(NULL); - file_thread_->Stop(); - file_thread_.reset(NULL); - ui_thread_.reset(NULL); - ui_message_loop_.reset(NULL); + TeadDownRegistry(); } - bool enabled_io() { - return registry()->enabled_io_; - } + TestMessageLoop loop_; - scoped_ptr<MessageLoopForUI> ui_message_loop_; - scoped_ptr<content::TestBrowserThread> ui_thread_; - scoped_ptr<content::TestBrowserThread> io_thread_; - scoped_ptr<content::TestBrowserThread> file_thread_; + private: + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; + content::TestBrowserThread io_thread_; - FakeDelegate* delegate_; scoped_ptr<TestingProfile> profile_; - scoped_refptr<ProtocolHandlerRegistry> registry_; + FakeDelegate* delegate_; // Registry assumes ownership of delegate_. + scoped_ptr<ProtocolHandlerRegistry> registry_; ProtocolHandler test_protocol_handler_; }; @@ -383,7 +436,7 @@ TEST_F(ProtocolHandlerRegistryTest, SaveAndLoad) { ASSERT_TRUE(registry()->IsHandledProtocol("test")); ASSERT_TRUE(registry()->IsIgnored(stuff_protocol_handler)); delegate()->Reset(); - ReloadProtocolHandlerRegistry(); + RecreateRegistry(true); ASSERT_TRUE(registry()->IsHandledProtocol("test")); ASSERT_TRUE(registry()->IsIgnored(stuff_protocol_handler)); } @@ -469,14 +522,14 @@ TEST_F(ProtocolHandlerRegistryTest, TestDefaultSaveLoad) { registry()->OnAcceptRegisterProtocolHandler(ph2); registry()->Disable(); - ReloadProtocolHandlerRegistry(); + RecreateRegistry(true); ASSERT_FALSE(registry()->enabled()); registry()->Enable(); ASSERT_FALSE(registry()->IsDefault(ph1)); ASSERT_TRUE(registry()->IsDefault(ph2)); - ReloadProtocolHandlerRegistry(); + RecreateRegistry(true); ASSERT_TRUE(registry()->enabled()); } @@ -636,6 +689,10 @@ TEST_F(ProtocolHandlerRegistryTest, TestDisablePreventsHandling) { ASSERT_FALSE(registry()->IsHandledProtocol("test")); } +// TODO(smckay): This is much more appropriately an integration +// test. Make that so, then update the +// ShellIntegretion{Delegate,Observer,Worker} test classes we use to fully +// isolate this test from the FILE thread. TEST_F(ProtocolHandlerRegistryTest, TestOSRegistration) { ProtocolHandler ph_do1 = CreateProtocolHandler("do", "test1"); ProtocolHandler ph_do2 = CreateProtocolHandler("do", "test2"); @@ -646,7 +703,7 @@ TEST_F(ProtocolHandlerRegistryTest, TestOSRegistration) { registry()->OnAcceptRegisterProtocolHandler(ph_do1); registry()->OnDenyRegisterProtocolHandler(ph_dont); - MessageLoop::current()->Run(); + MessageLoop::current()->Run(); // FILE thread needs to run. ASSERT_TRUE(delegate()->IsFakeRegisteredWithOS("do")); ASSERT_FALSE(delegate()->IsFakeRegisteredWithOS("dont")); @@ -664,6 +721,10 @@ TEST_F(ProtocolHandlerRegistryTest, TestOSRegistration) { #define MAYBE_TestOSRegistrationFailure TestOSRegistrationFailure #endif +// TODO(smckay): This is much more appropriately an integration +// test. Make that so, then update the +// ShellIntegretion{Delegate,Observer,Worker} test classes we use to fully +// isolate this test from the FILE thread. TEST_F(ProtocolHandlerRegistryTest, MAYBE_TestOSRegistrationFailure) { ProtocolHandler ph_do = CreateProtocolHandler("do", "test1"); ProtocolHandler ph_dont = CreateProtocolHandler("dont", "test"); @@ -672,40 +733,24 @@ TEST_F(ProtocolHandlerRegistryTest, MAYBE_TestOSRegistrationFailure) { ASSERT_FALSE(registry()->IsHandledProtocol("dont")); registry()->OnAcceptRegisterProtocolHandler(ph_do); - MessageLoop::current()->Run(); + MessageLoop::current()->Run(); // FILE thread needs to run. delegate()->set_force_os_failure(true); registry()->OnAcceptRegisterProtocolHandler(ph_dont); - MessageLoop::current()->Run(); + MessageLoop::current()->Run(); // FILE thread needs to run. ASSERT_TRUE(registry()->IsHandledProtocol("do")); ASSERT_EQ(static_cast<size_t>(1), registry()->GetHandlersFor("do").size()); ASSERT_FALSE(registry()->IsHandledProtocol("dont")); ASSERT_EQ(static_cast<size_t>(1), registry()->GetHandlersFor("dont").size()); } -static void MakeRequest(const GURL& url, ProtocolHandlerRegistry* registry) { - net::URLRequestContext context; - net::URLRequest request(url, NULL, &context); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - MessageLoop::QuitClosure()); - scoped_refptr<net::URLRequestJob> job(registry->MaybeCreateJob(&request)); - ASSERT_TRUE(job.get() != NULL); -} - TEST_F(ProtocolHandlerRegistryTest, TestMaybeCreateTaskWorksFromIOThread) { ProtocolHandler ph1 = CreateProtocolHandler("mailto", "test1"); registry()->OnAcceptRegisterProtocolHandler(ph1); GURL url("mailto:someone@something.com"); - scoped_refptr<ProtocolHandlerRegistry> r(registry()); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(MakeRequest, url, r)); - MessageLoop::current()->Run(); -} + net::URLRequestJobFactory::Interceptor* interceptor = + registry()->CreateURLInterceptor(); -static void CheckIsHandled(const std::string& scheme, bool expected, - ProtocolHandlerRegistry* registry) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - MessageLoop::QuitClosure()); - ASSERT_EQ(expected, registry->IsHandledProtocolIO(scheme)); + AssertIntercepted(url, interceptor); } TEST_F(ProtocolHandlerRegistryTest, @@ -713,11 +758,10 @@ TEST_F(ProtocolHandlerRegistryTest, std::string scheme("mailto"); ProtocolHandler ph1 = CreateProtocolHandler(scheme, "test1"); registry()->OnAcceptRegisterProtocolHandler(ph1); - scoped_refptr<ProtocolHandlerRegistry> r(registry()); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(CheckIsHandled, scheme, true, r)); + net::URLRequestJobFactory::Interceptor* interceptor = + registry()->CreateURLInterceptor(); + + AssertWillHandle(scheme, true, interceptor); } TEST_F(ProtocolHandlerRegistryTest, TestRemovingDefaultFallsBackToOldDefault) { @@ -761,26 +805,20 @@ TEST_F(ProtocolHandlerRegistryTest, MAYBE_TestClearDefaultGetsPropagatedToIO) { ProtocolHandler ph1 = CreateProtocolHandler(scheme, "test1"); registry()->OnAcceptRegisterProtocolHandler(ph1); registry()->ClearDefault(scheme); - scoped_refptr<ProtocolHandlerRegistry> r(registry()); + net::URLRequestJobFactory::Interceptor* interceptor = + registry()->CreateURLInterceptor(); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(CheckIsHandled, scheme, false, r)); -} - -static void QuitUILoop() { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - MessageLoop::QuitClosure()); + AssertWillHandle(scheme, false, interceptor); } TEST_F(ProtocolHandlerRegistryTest, TestLoadEnabledGetsPropogatedToIO) { + std::string mailto("mailto"); + ProtocolHandler ph1 = CreateProtocolHandler(mailto, "MailtoHandler"); + registry()->OnAcceptRegisterProtocolHandler(ph1); + + AssertWillHandle(mailto, true, registry()->CreateURLInterceptor()); registry()->Disable(); - ReloadProtocolHandlerRegistry(); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(QuitUILoop)); - MessageLoop::current()->Run(); - ASSERT_FALSE(enabled_io()); + AssertWillHandle(mailto, false, registry()->CreateURLInterceptor()); } TEST_F(ProtocolHandlerRegistryTest, TestReplaceHandler) { @@ -844,7 +882,10 @@ TEST_F(ProtocolHandlerRegistryTest, TestIsSameOrigin) { } TEST_F(ProtocolHandlerRegistryTest, MAYBE_TestInstallDefaultHandler) { - ReloadProtocolHandlerRegistryAndInstallDefaultHandler(); + RecreateRegistry(false); + registry()->AddPredefinedHandler(CreateProtocolHandler( + "test", GURL("http://test.com/%s"), "Test")); + registry()->InitProtocolSettings(); std::vector<std::string> protocols; registry()->GetRegisteredProtocols(&protocols); ASSERT_EQ(static_cast<size_t>(1), protocols.size()); |