summaryrefslogtreecommitdiffstats
path: root/chrome/browser/custom_handlers
diff options
context:
space:
mode:
authorsmckay@chromium.org <smckay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-20 01:52:02 +0000
committersmckay@chromium.org <smckay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-20 01:52:02 +0000
commit26effd10fe11d820b3040e72d85e00dd3bd524d5 (patch)
tree604505f94bf6cd372a8fc63a7337c9435bcd3b58 /chrome/browser/custom_handlers
parent0397dea43ea8a08e496971c80795970db5ad0644 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/custom_handlers/protocol_handler_registry.cc273
-rw-r--r--chrome/browser/custom_handlers/protocol_handler_registry.h66
-rw-r--r--chrome/browser/custom_handlers/protocol_handler_registry_factory.cc61
-rw-r--r--chrome/browser/custom_handlers/protocol_handler_registry_factory.h47
-rw-r--r--chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc229
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());