// 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 <string> #include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_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: // Provides notification of when the OS level user agent settings // are changed. class DefaultClientObserver : public ShellIntegration::DefaultWebClientObserver { public: explicit DefaultClientObserver(ProtocolHandlerRegistry* registry); virtual ~DefaultClientObserver(); // Get response from the worker regarding whether Chrome is the default // handler for the protocol. virtual void SetDefaultWebClientUIState( ShellIntegration::DefaultWebClientUIState state) OVERRIDE; virtual bool IsInteractiveSetDefaultPermitted() OVERRIDE; // Give the observer a handle to the worker, so we can find out the protocol // when we're called and also tell the worker if we get deleted. void SetWorker(ShellIntegration::DefaultProtocolClientWorker* worker); protected: ShellIntegration::DefaultProtocolClientWorker* worker_; private: virtual bool IsOwnedByWorker() OVERRIDE; // This is a raw pointer, not reference counted, intentionally. In general // subclasses of DefaultWebClientObserver are not able to be refcounted // e.g. the browser options page ProtocolHandlerRegistry* registry_; DISALLOW_COPY_AND_ASSIGN(DefaultClientObserver); }; // |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 ShellIntegration::DefaultProtocolClientWorker* CreateShellWorker( ShellIntegration::DefaultWebClientObserver* observer, const std::string& protocol); virtual DefaultClientObserver* CreateShellObserver( ProtocolHandlerRegistry* registry); virtual void RegisterWithOSAsDefaultClient( 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); virtual ~JobInterceptorFactory(); // |job_factory| is set as the URLRequestJobFactory where requests are // forwarded if JobInterceptorFactory decides to pass on them. void Chain(scoped_ptr<net::URLRequestJobFactory> job_factory); // URLRequestJobFactory implementation. virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler( const std::string& scheme, net::URLRequest* request, net::NetworkDelegate* network_delegate) const OVERRIDE; virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE; virtual bool IsHandledURL(const GURL& url) const OVERRIDE; virtual 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_|. scoped_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; typedef std::vector<DefaultClientObserver*> DefaultClientObserverList; // Creates a new instance. Assumes ownership of |delegate|. ProtocolHandlerRegistry(Profile* profile, Delegate* delegate); virtual ~ProtocolHandlerRegistry(); // Returns a net::URLRequestJobFactory suitable for use on the IO thread, but // is initialized on the UI thread. scoped_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 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. virtual 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); 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); // 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); // Map from protocols (strings) to protocol handlers. ProtocolHandlerMultiMap protocol_handlers_; // Protocol handlers that the user has told us to ignore. ProtocolHandlerList ignored_protocol_handlers_; // Protocol handlers that are the defaults for a given protocol. ProtocolHandlerMap default_handlers_; // The Profile that owns this ProtocolHandlerRegistry. Profile* profile_; // The Delegate that registers / deregisters external handlers on our behalf. scoped_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_; DefaultClientObserverList default_client_observers_; DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistry); }; #endif // CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_H_