diff options
author | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-17 05:10:30 +0000 |
---|---|---|
committer | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-17 05:10:30 +0000 |
commit | 83aa77e873d1dfde716c0083f6f5704999c93748 (patch) | |
tree | 0523199cb0032c5b48e2ca82aa243d1b65ce1fc9 /chrome/browser/chromeos | |
parent | af3b0898b4adf7961c5af8c6e1ef4909672c8488 (diff) | |
download | chromium_src-83aa77e873d1dfde716c0083f6f5704999c93748.zip chromium_src-83aa77e873d1dfde716c0083f6f5704999c93748.tar.gz chromium_src-83aa77e873d1dfde716c0083f6f5704999c93748.tar.bz2 |
Rework LibcrosService using our D-Bus library.
Also introduce DBusThreadManager, that manages the D-Bus thread, and
D-Bus clients using the thread.
cros_dbus_service.cc and proxy_resolution_service_provider.cc are based on
libcros_service_library.cc. The basic logic
is kept as before. The major changes are:
- Get rid of all libcros function calls (ex. StartLibCrosService).
- Export "ResolveProxy" D-Bus method from Chrome, instead of libcros.
- CrosDBusService is now managed by DBusThreadManager, instead of CrosLibrary.
- Reduce use of nested class per the C++ style guide.
- Now unit tested: libcros_service_unittest.cc
BUG=chromium-os:18904
TEST=on the device, change the proxy config to use http://proxyconfig.corp.google.com/wpad.dat, run /opt/google/chrome/chromeos/libcros_service_tester (installed by USE=install_tests gmerge libcros), and confirm the libcros service works as before.
Review URL: http://codereview.chromium.org/7819012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101640 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
14 files changed, 1103 insertions, 358 deletions
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index dfabefc..0f0a530 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc @@ -11,6 +11,7 @@ #include "base/message_loop.h" #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/dbus/dbus_thread_manager.h" #include "chrome/browser/chromeos/net/cros_network_change_notifier_factory.h" #include "chrome/browser/chromeos/sensors_source_chromeos.h" #include "chrome/browser/defaults.h" @@ -67,7 +68,10 @@ ChromeBrowserMainPartsChromeos::ChromeBrowserMainPartsChromeos( const MainFunctionParams& parameters) : ChromeBrowserMainPartsGtk(parameters) { } + ChromeBrowserMainPartsChromeos::~ChromeBrowserMainPartsChromeos() { + chromeos::DBusThreadManager::Shutdown(); + if (!parameters().ui_task && chromeos::CrosLibrary::Get()) chromeos::CrosLibrary::Shutdown(); @@ -116,4 +120,8 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopStart() { message_loop->PostTask(FROM_HERE, base::Bind(&DoDeferredSensorsInit, sensors_source_)); } + + // Initialize DBusThreadManager for the browser. This must be done after + // the main message loop is started, as it uses the message loop. + chromeos::DBusThreadManager::Initialize(); } diff --git a/chrome/browser/chromeos/cros/cros_library.cc b/chrome/browser/chromeos/cros/cros_library.cc index e3dc355..a3a748d 100644 --- a/chrome/browser/chromeos/cros/cros_library.cc +++ b/chrome/browser/chromeos/cros/cros_library.cc @@ -8,7 +8,6 @@ #include "chrome/browser/chromeos/cros/burn_library.h" #include "chrome/browser/chromeos/cros/cert_library.h" #include "chrome/browser/chromeos/cros/cryptohome_library.h" -#include "chrome/browser/chromeos/cros/libcros_service_library.h" #include "chrome/browser/chromeos/cros/library_loader.h" #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/cros/mount_library.h" @@ -82,7 +81,6 @@ DEFINE_GET_LIBRARY_METHOD(Brightness, brightness); DEFINE_GET_LIBRARY_METHOD(Burn, burn); DEFINE_GET_LIBRARY_METHOD(Cert, cert); DEFINE_GET_LIBRARY_METHOD(Cryptohome, crypto); -DEFINE_GET_LIBRARY_METHOD(LibCrosService, libcros_service); DEFINE_GET_LIBRARY_METHOD(Login, login); DEFINE_GET_LIBRARY_METHOD(Mount, mount); DEFINE_GET_LIBRARY_METHOD(Network, network); @@ -132,7 +130,6 @@ DEFINE_SET_LIBRARY_METHOD(Brightness, brightness); DEFINE_SET_LIBRARY_METHOD(Cert, cert); DEFINE_SET_LIBRARY_METHOD(Burn, burn); DEFINE_SET_LIBRARY_METHOD(Cryptohome, crypto); -DEFINE_SET_LIBRARY_METHOD(LibCrosService, libcros_service); DEFINE_SET_LIBRARY_METHOD(Login, login); DEFINE_SET_LIBRARY_METHOD(Mount, mount); DEFINE_SET_LIBRARY_METHOD(Network, network); diff --git a/chrome/browser/chromeos/cros/cros_library.h b/chrome/browser/chromeos/cros/cros_library.h index 5683e5e1..bc16426 100644 --- a/chrome/browser/chromeos/cros/cros_library.h +++ b/chrome/browser/chromeos/cros/cros_library.h @@ -20,7 +20,6 @@ class BrightnessLibrary; class BurnLibrary; class CertLibrary; class CryptohomeLibrary; -class LibCrosServiceLibrary; class LibraryLoader; class LoginLibrary; class MountLibrary; @@ -52,7 +51,6 @@ class CrosLibrary { void SetCertLibrary(CertLibrary* library, bool own); void SetBurnLibrary(BurnLibrary* library, bool own); void SetCryptohomeLibrary(CryptohomeLibrary* library, bool own); - void SetLibCrosServiceLibrary(LibCrosServiceLibrary* library, bool own); void SetLoginLibrary(LoginLibrary* library, bool own); void SetMountLibrary(MountLibrary* library, bool own); void SetNetworkLibrary(NetworkLibrary* library, bool own); @@ -82,7 +80,6 @@ class CrosLibrary { BurnLibrary* GetBurnLibrary(); CertLibrary* GetCertLibrary(); CryptohomeLibrary* GetCryptohomeLibrary(); - LibCrosServiceLibrary* GetLibCrosServiceLibrary(); LoginLibrary* GetLoginLibrary(); MountLibrary* GetMountLibrary(); NetworkLibrary* GetNetworkLibrary(); @@ -157,7 +154,6 @@ class CrosLibrary { Library<BurnLibrary> burn_lib_; Library<CertLibrary> cert_lib_; Library<CryptohomeLibrary> crypto_lib_; - Library<LibCrosServiceLibrary> libcros_service_lib_; Library<LoginLibrary> login_lib_; Library<MountLibrary> mount_lib_; Library<NetworkLibrary> network_lib_; diff --git a/chrome/browser/chromeos/cros/libcros_service_library.cc b/chrome/browser/chromeos/cros/libcros_service_library.cc deleted file mode 100644 index cd4d578d..0000000 --- a/chrome/browser/chromeos/cros/libcros_service_library.cc +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright (c) 2011 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/chromeos/cros/libcros_service_library.h" - -#include "base/synchronization/lock.h" -#include "chrome/browser/chromeos/cros/cros_library.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "content/browser/browser_thread.h" -#include "cros/chromeos_libcros_service.h" -#include "net/base/net_errors.h" -#include "net/proxy/proxy_service.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" - -namespace chromeos { - -class LibCrosServiceLibraryImpl : public LibCrosServiceLibrary { - public: - // Base class for all services of LibCrosService. - // Each subclass should declare DISABLE_RUNNABLE_METHOD_REFCOUNT to disable - // refcounting, which is okay since the subclass's object will exist as a - // scoped_ptr in Singleton LibCrosServiceLibraryImpl, guaranteeing that its - // lifetime is longer than that of any message loop. - class ServicingLibrary { - public: - explicit ServicingLibrary(LibCrosServiceConnection service_connection); - virtual ~ServicingLibrary(); - - // Clears service_connection_ (which is stored as weak pointer) so that it - // can't be used anymore. - virtual void ClearServiceConnection(); - - protected: - LibCrosServiceConnection service_connection_; // Weak pointer. - // Lock for data members to synchronize access on multiple threads. - base::Lock data_lock_; - - private: - DISALLOW_COPY_AND_ASSIGN(ServicingLibrary); - }; - - // Library that provides network proxy service for LibCrosService. - // For now, it only processes proxy resolution requests for ChromeOS clients. - class NetworkProxyLibrary : public ServicingLibrary { - public: - explicit NetworkProxyLibrary(LibCrosServiceConnection connection); - virtual ~NetworkProxyLibrary(); - - private: - // Data being used in one proxy resolution. - class Request { - public: - explicit Request(const std::string& source_url); - virtual ~Request() {} - - // Callback on IO thread for when net::ProxyService::ResolveProxy - // completes, synchronously or asynchronously. - void OnCompletion(int result); - net::CompletionCallbackImpl<Request> completion_callback_; - - std::string source_url_; // URL being resolved. - int result_; // Result of proxy resolution. - net::ProxyInfo proxy_info_; // ProxyInfo resolved for source_url_. - std::string error_; // Error from proxy resolution. - Task* notify_task_; // Task to notify of resolution result. - - private: - DISALLOW_COPY_AND_ASSIGN(Request); - }; - - // Static callback passed to LibCrosService to be invoked when ChromeOS - // clients send network proxy resolution requests to the service running in - // chrome executable. Called on UI thread from dbus request. - static void ResolveProxyHandler(void* object, const char* source_url); - - void ResolveProxy(const std::string& source_url, - scoped_refptr<net::URLRequestContextGetter> getter); - - // Wrapper on UI thread to call LibCrosService::NotifyNetworkProxyResolved. - void NotifyProxyResolved(Request* request); - - std::vector<Request*> all_requests_; - - DISALLOW_COPY_AND_ASSIGN(NetworkProxyLibrary); - }; - - LibCrosServiceLibraryImpl(); - virtual ~LibCrosServiceLibraryImpl(); - - // LibCrosServiceLibrary implementation. - - // Starts LibCrosService running on dbus if not already started. - virtual void StartService(); - - private: - // Connection to LibCrosService. - LibCrosServiceConnection service_connection_; - - // Libraries that form LibCrosService. - scoped_ptr<NetworkProxyLibrary> network_proxy_lib_; - - DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryImpl); -}; - -//---------------- LibCrosServiceLibraryImpl: public --------------------------- - -LibCrosServiceLibraryImpl::LibCrosServiceLibraryImpl() - : service_connection_(NULL) { - if (!CrosLibrary::Get()->EnsureLoaded()) { - LOG(ERROR) << "Cros library has not been loaded."; - } -} - -LibCrosServiceLibraryImpl::~LibCrosServiceLibraryImpl() { - if (service_connection_) { - // Clear service connections in servicing libraries which held the former - // as weak pointers. - if (network_proxy_lib_.get()) - network_proxy_lib_->ClearServiceConnection(); - - StopLibCrosService(service_connection_); - VLOG(1) << "LibCrosService stopped."; - service_connection_ = NULL; - } -} - -// Called on UI thread to start service for LibCrosService. -void LibCrosServiceLibraryImpl::StartService() { - // Make sure we're running on UI thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &LibCrosServiceLibraryImpl::StartService)); - return; - } - if (service_connection_) // Service has already been started. - return; - // Starts LibCrosService; the returned connection is used for future - // interactions with the service. - service_connection_ = StartLibCrosService(); - if (!service_connection_) { - LOG(WARNING) << "Error starting LibCrosService"; - return; - } - network_proxy_lib_.reset(new NetworkProxyLibrary(service_connection_)); - VLOG(1) << "LibCrosService started."; -} - -//------------- LibCrosServiceLibraryImpl::ServicingLibrary: public ------------ - -LibCrosServiceLibraryImpl::ServicingLibrary::ServicingLibrary( - LibCrosServiceConnection connection) - : service_connection_(connection) { -} - -LibCrosServiceLibraryImpl::ServicingLibrary::~ServicingLibrary() { - ClearServiceConnection(); -} - -void LibCrosServiceLibraryImpl::ServicingLibrary::ClearServiceConnection() { - base::AutoLock lock(data_lock_); - service_connection_ = NULL; -} - -//----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: public ----------- - -LibCrosServiceLibraryImpl::NetworkProxyLibrary::NetworkProxyLibrary( - LibCrosServiceConnection connection) - : ServicingLibrary(connection) { - // Register callback for LibCrosService::ResolveNetworkProxy. - SetNetworkProxyResolver(&ResolveProxyHandler, this, service_connection_); -} - -LibCrosServiceLibraryImpl::NetworkProxyLibrary::~NetworkProxyLibrary() { - base::AutoLock lock(data_lock_); - while (!all_requests_.empty()) { - LOG(WARNING) << "Pending request for " << all_requests_.back()->source_url_; - delete all_requests_.back(); - all_requests_.pop_back(); - } -} - -//----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: private ---------- - -// Static, called on UI thread from LibCrosService::ResolveProxy via dbus. -void LibCrosServiceLibraryImpl::NetworkProxyLibrary::ResolveProxyHandler( - void* object, const char* source_url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - NetworkProxyLibrary* lib = static_cast<NetworkProxyLibrary*>(object); - // parameters of RunnbaleMethod will be freed when this function returns, so - // make a copy of them. - std::string url(source_url); - scoped_refptr<net::URLRequestContextGetter> getter = - ProfileManager::GetDefaultProfile()->GetRequestContext(); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - NewRunnableMethod(lib, &NetworkProxyLibrary::ResolveProxy, url, getter)); -} - -// Called on IO thread as task posted from ResolveProxyHandler on UI thread. -void LibCrosServiceLibraryImpl::NetworkProxyLibrary::ResolveProxy( - const std::string& source_url, - scoped_refptr<net::URLRequestContextGetter> getter) { - // Make sure we're running on IO thread. - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - // Create a request slot for this proxy resolution request. - Request* request = new Request(source_url); - request->notify_task_ = NewRunnableMethod(this, - &NetworkProxyLibrary::NotifyProxyResolved, request); - { - base::AutoLock lock(data_lock_); - // Queue request slot. - all_requests_.push_back(request); - if (!service_connection_) // Make sure |service_connection_| is valid. - request->error_ = "LibCrosService not started"; - } - - // Retrieve ProxyService from profile's request context. - net::ProxyService* proxy_service = NULL; - if (request->error_.empty()) { // No error yet, proceed. - if (getter) - proxy_service = getter->GetURLRequestContext()->proxy_service(); - if (!proxy_service) // Make sure proxy_service is valid. - request->error_ = "No proxy service in chrome"; - } - - if (!request->error_.empty()) { // Error string was just set. - LOG(ERROR) << request->error_; - request->result_ = net::OK; // Set to OK since error string is set. - } else { - VLOG(1) << "Starting networy proxy resolution for " << request->source_url_; - request->result_ = proxy_service->ResolveProxy( - GURL(request->source_url_), &request->proxy_info_, - &request->completion_callback_, NULL, net::BoundNetLog()); - } - if (request->result_ != net::ERR_IO_PENDING) { - VLOG(1) << "Network proxy resolution completed synchronously."; - request->OnCompletion(request->result_); - } -} - -// Called on UI thread as task posted from Request::OnCompletion on IO thread. -void LibCrosServiceLibraryImpl::NetworkProxyLibrary::NotifyProxyResolved( - Request* request) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - base::AutoLock lock(data_lock_); - if (service_connection_) { - if (!NotifyNetworkProxyResolved(request->source_url_.c_str(), - request->proxy_info_.ToPacString().c_str(), - request->error_.c_str(), - service_connection_)) { - LOG(ERROR) << "LibCrosService has error with NotifyNetworkProxyResolved"; - } else { - VLOG(1) << "LibCrosService has notified proxy resoloution for " - << request->source_url_; - } - } - std::vector<Request*>::iterator iter = - std::find(all_requests_.begin(), all_requests_.end(), request); - if (iter == all_requests_.end()) { - LOG(ERROR) << "can't find request slot(" << request->source_url_ - << ") in proxy-resolution queue"; - } else { - all_requests_.erase(iter); - } - delete request; -} - -//---------- LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request ----------- - -LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request::Request( - const std::string& source_url) - : ALLOW_THIS_IN_INITIALIZER_LIST( - completion_callback_(this, &Request::OnCompletion)), - source_url_(source_url), - result_(net::ERR_FAILED), - notify_task_(NULL) { -} - -// Called on IO thread when net::ProxyService::ResolveProxy has completed. -void LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request::OnCompletion( - int result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - result_ = result; - if (result_ != net::OK) - error_ = net::ErrorToString(result_); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, notify_task_); - notify_task_ = NULL; -} - -//--------------------- LibCrosServiceLibraryStubImpl -------------------------- - -class LibCrosServiceLibraryStubImpl : public LibCrosServiceLibrary { - public: - LibCrosServiceLibraryStubImpl() {} - virtual ~LibCrosServiceLibraryStubImpl() {} - - // LibCrosServiceLibrary overrides. - virtual void StartService() {} - - DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryStubImpl); -}; - -//--------------------------- LibCrosServiceLibrary ---------------------------- - -// Static. -LibCrosServiceLibrary* LibCrosServiceLibrary::GetImpl(bool stub) { - if (stub) - return new LibCrosServiceLibraryStubImpl(); - return new LibCrosServiceLibraryImpl(); -} - -} // namespace chromeos - -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run, so are all its -// scoped_ptred class members. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::LibCrosServiceLibraryImpl); -DISABLE_RUNNABLE_METHOD_REFCOUNT( - chromeos::LibCrosServiceLibraryImpl::NetworkProxyLibrary); diff --git a/chrome/browser/chromeos/cros/libcros_service_library.h b/chrome/browser/chromeos/cros/libcros_service_library.h deleted file mode 100644 index b409f29..0000000 --- a/chrome/browser/chromeos/cros/libcros_service_library.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2010 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_CHROMEOS_CROS_LIBCROS_SERVICE_LIBRARY_H_ -#define CHROME_BROWSER_CHROMEOS_CROS_LIBCROS_SERVICE_LIBRARY_H_ -#pragma once - -namespace net{ -class ProxyService; -}; - -namespace chromeos { - -class LibCrosServiceLibrary { - public: - virtual ~LibCrosServiceLibrary() {} - - // Starts dbus service for LibCrosService. - virtual void StartService() = 0; - - // Factory function, creates a new instance and returns ownership. - // For normal usage, access the singleton via CrosLibrary::Get(). - static LibCrosServiceLibrary* GetImpl(bool stub); -}; - -} // namespace chromeos - -#endif // CHROME_BROWSER_CHROMEOS_CROS_LIBCROS_SERVICE_LIBRARY_H_ diff --git a/chrome/browser/chromeos/dbus/OWNERS b/chrome/browser/chromeos/dbus/OWNERS new file mode 100644 index 0000000..453c641 --- /dev/null +++ b/chrome/browser/chromeos/dbus/OWNERS @@ -0,0 +1,4 @@ +set noparent +satorux@chromium.org +stevenjb@chromium.org + diff --git a/chrome/browser/chromeos/dbus/cros_dbus_service.cc b/chrome/browser/chromeos/dbus/cros_dbus_service.cc new file mode 100644 index 0000000..52cfec4 --- /dev/null +++ b/chrome/browser/chromeos/dbus/cros_dbus_service.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2011 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/chromeos/dbus/cros_dbus_service.h" + +#include "base/stl_util.h" +#include "base/threading/platform_thread.h" +#include "chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h" +#include "content/browser/browser_thread.h" +#include "dbus/bus.h" +#include "dbus/exported_object.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +CrosDBusService::CrosDBusService(dbus::Bus* bus) + : service_started_(false), + origin_thread_id_(base::PlatformThread::CurrentId()), + bus_(bus) { +} + +CrosDBusService::~CrosDBusService() { +} + +void CrosDBusService::Start() { + // Make sure we're running on the origin thread (i.e. the UI thread in + // production). + DCHECK(OnOriginThread()); + + // Return if the service has been already started. + if (service_started_) + return; + + exported_object_ = bus_->GetExportedObject( + kLibCrosServiceName, + kLibCrosServicePath); + + for (size_t i = 0; i < service_providers_.size(); ++i) + service_providers_[i]->Start(exported_object_); + + service_started_ = true; + + VLOG(1) << "CrosDBusService started."; +} + +CrosDBusService* CrosDBusService::Get(dbus::Bus* bus) { + CrosDBusService* service = new CrosDBusService(bus); + service->RegisterServiceProvider(ProxyResolutionServiceProvider::Get()); + return service; +} + +CrosDBusService* CrosDBusService::GetForTesting( + dbus::Bus* bus, + ServiceProviderInterface* proxy_resolution_service) { + CrosDBusService* service = new CrosDBusService(bus); + service->RegisterServiceProvider(proxy_resolution_service); + return service; +} + +void CrosDBusService::RegisterServiceProvider( + ServiceProviderInterface* provider) { + service_providers_.push_back(provider); +} + +bool CrosDBusService::OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/dbus/cros_dbus_service.h b/chrome/browser/chromeos/dbus/cros_dbus_service.h new file mode 100644 index 0000000..4ab24c5 --- /dev/null +++ b/chrome/browser/chromeos/dbus/cros_dbus_service.h @@ -0,0 +1,94 @@ +// Copyright (c) 2011 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_CHROMEOS_DBUS_CROS_DBUS_SERVICE_H_ +#define CHROME_BROWSER_CHROMEOS_DBUS_CROS_DBUS_SERVICE_H_ +#pragma once + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/platform_thread.h" + +namespace dbus { +class Bus; +class ExportedObject; +} + +namespace chromeos { + +// CrosDBusService is used to run a D-Bus service inside Chrome for Chrome +// OS. The service will be registered as follows: +// +// Service name: org.chromium.LibCrosService (kLibCrosServiceName) +// Object path: chromium/LibCrosService (kLibCrosServicePath) +// +// For historical reasons, the rather irrelevant name "LibCrosService" is +// used in the D-Bus constants such as the service name. +// +// CrosDBusService exports D-Bus methods through service provider classes +// that implement CrosDBusService::ServiceProviderInterface. +// +class ProxyResolutionService; + +class CrosDBusService { + public: + // CrosDBusService consists of service providers that implement this + // interface. + // + // ServiceProviderInterface is a ref counted object, to ensure that + // |this| of the object is alive when callbacks referencing |this| are + // called. + class ServiceProviderInterface + : public base::RefCountedThreadSafe<ServiceProviderInterface> { + public: + virtual ~ServiceProviderInterface() {} + + // Starts the service provider. |exported_object| is used to export + // D-Bus methods. + virtual void Start( + scoped_refptr<dbus::ExportedObject> exported_object) = 0; + + private: + friend class base::RefCountedThreadSafe<ServiceProviderInterface>; + }; + + virtual ~CrosDBusService(); + + // Starts the D-Bus service. + virtual void Start(); + + // Gets the instance. + static CrosDBusService* Get(dbus::Bus* bus); + + private: + explicit CrosDBusService(dbus::Bus* bus); + + // Gets the instance for testing. Takes the ownership of + // |proxy_resolution_service|. + friend class CrosDBusServiceTest; + static CrosDBusService* GetForTesting( + dbus::Bus* bus, + ServiceProviderInterface* proxy_resolution_service); + + // Registers a service provider. This must be done before Start(). + // |provider| will be owned by CrosDBusService. + void RegisterServiceProvider(ServiceProviderInterface* provider); + + // Returns true if the current thread is on the origin thread. + bool OnOriginThread(); + + bool service_started_; + base::PlatformThreadId origin_thread_id_; + dbus::Bus* bus_; + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Service providers that form CrosDBusService. + std::vector<scoped_refptr<ServiceProviderInterface> > service_providers_; +}; + +} // namespace + +#endif // CHROME_BROWSER_CHROMEOS_DBUS_CROS_DBUS_SERVICE_H_ diff --git a/chrome/browser/chromeos/dbus/cros_dbus_service_unittest.cc b/chrome/browser/chromeos/dbus/cros_dbus_service_unittest.cc new file mode 100644 index 0000000..70e2ece --- /dev/null +++ b/chrome/browser/chromeos/dbus/cros_dbus_service_unittest.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2011 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/chromeos/dbus/cros_dbus_service.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "dbus/message.h" +#include "dbus/mock_bus.h" +#include "dbus/mock_exported_object.h" +#include "dbus/mock_object_proxy.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using ::testing::Eq; +using ::testing::Invoke; +using ::testing::Return; + +namespace chromeos { + +class MockProxyResolutionService + : public CrosDBusService::ServiceProviderInterface { + public: + MOCK_METHOD1(Start, void(scoped_refptr<dbus::ExportedObject> + exported_object)); +}; + +class CrosDBusServiceTest : public testing::Test { + public: + CrosDBusServiceTest() { + } + + // Creates an instance of CrosDBusService with mocks injected. + virtual void SetUp() { + // Create a mock bus. + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + mock_bus_ = new dbus::MockBus(options); + + // ShutdownAndBlock() will be called in TearDown(). + EXPECT_CALL(*mock_bus_, ShutdownAndBlock()).WillOnce(Return()); + + // Create a mock exported object that behaves as + // org.chromium.CrosDBusService. + mock_exported_object_ = + new dbus::MockExportedObject(mock_bus_.get(), + kLibCrosServiceName, + kLibCrosServicePath); + + // |mock_bus_|'s GetExportedObject() will return mock_exported_object_| + // for the given service name and the object path. + EXPECT_CALL(*mock_bus_, GetExportedObject( + kLibCrosServiceName, kLibCrosServicePath)) + .WillOnce(Return(mock_exported_object_.get())); + + // Create a mock proxy resolution service. + MockProxyResolutionService* mock_proxy_resolution_service_provider = + new MockProxyResolutionService; + + // Start() will be called with |mock_exported_object_|. + EXPECT_CALL(*mock_proxy_resolution_service_provider, + Start(Eq(mock_exported_object_))).WillOnce(Return()); + + // Create the cros service with the mocks injected. + cros_dbus_service_.reset( + CrosDBusService::GetForTesting( + mock_bus_, + mock_proxy_resolution_service_provider)); + } + + virtual void TearDown() { + // Shutdown the bus. + mock_bus_->ShutdownAndBlock(); + } + + protected: + scoped_refptr<dbus::MockBus> mock_bus_; + scoped_refptr<dbus::MockExportedObject> mock_exported_object_; + scoped_ptr<CrosDBusService> cros_dbus_service_; +}; + +TEST_F(CrosDBusServiceTest, Start) { + // Simply start the service and see if mock expectations are met: + // - The service object is exported by GetExportedObject() + // - The proxy resolution service is started. + cros_dbus_service_->Start(); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/dbus/dbus_thread_manager.cc b/chrome/browser/chromeos/dbus/dbus_thread_manager.cc new file mode 100644 index 0000000..5f4232c --- /dev/null +++ b/chrome/browser/chromeos/dbus/dbus_thread_manager.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2011 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/chromeos/dbus/dbus_thread_manager.h" + +#include "base/threading/thread.h" +#include "chrome/browser/chromeos/dbus/cros_dbus_service.h" +#include "dbus/bus.h" + +namespace chromeos { + +static DBusThreadManager* g_dbus_thread_manager = NULL; + +DBusThreadManager::DBusThreadManager() { + // Create the D-Bus thread. + base::Thread::Options thread_options; + thread_options.message_loop_type = MessageLoop::TYPE_IO; + dbus_thread_.reset(new base::Thread("D-Bus thread")); + dbus_thread_->StartWithOptions(thread_options); + + // Create the connection to the system bus. + dbus::Bus::Options system_bus_options; + system_bus_options.bus_type = dbus::Bus::SYSTEM; + system_bus_options.connection_type = dbus::Bus::PRIVATE; + system_bus_options.dbus_thread_message_loop_proxy = + dbus_thread_->message_loop_proxy(); + system_bus_ = new dbus::Bus(system_bus_options); + + // Create and start the cros D-Bus service. + cros_dbus_service_ = CrosDBusService::Get(system_bus_.get()); + cros_dbus_service_->Start(); +} + +DBusThreadManager::~DBusThreadManager() { + // Shut down the bus. During the browser shutdown, it's ok to shut down + // the bus synchronously. + system_bus_->ShutdownOnDBusThreadAndBlock(); + + // Stop the D-Bus thread. + dbus_thread_->Stop(); + + // D-Bus clients should be deleted after the D-Bus thread is stopped. + // See "CALLBACKS IN D-BUS CLIENTS" in the header file for why. + delete cros_dbus_service_; +} + +void DBusThreadManager::Initialize() { + CHECK(!g_dbus_thread_manager); + g_dbus_thread_manager = new DBusThreadManager; + VLOG(1) << "DBusThreadManager initialized"; +} + +void DBusThreadManager::Shutdown() { + if (!g_dbus_thread_manager) { + // This can happen in tests. + LOG(WARNING) << "DBusThreadManager::Shutdown() called with NULL manager"; + return; + } + delete g_dbus_thread_manager; + g_dbus_thread_manager = NULL; + VLOG(1) << "DBusThreadManager Shutdown completed"; +} + +DBusThreadManager* DBusThreadManager::Get() { + CHECK(g_dbus_thread_manager) + << "DBusThreadManager::Get() called before Initialize()"; + return g_dbus_thread_manager; +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/dbus/dbus_thread_manager.h b/chrome/browser/chromeos/dbus/dbus_thread_manager.h new file mode 100644 index 0000000..c4da3b6 --- /dev/null +++ b/chrome/browser/chromeos/dbus/dbus_thread_manager.h @@ -0,0 +1,71 @@ +// Copyright (c) 2011 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_CHROMEOS_DBUS_DBUS_THREAD_MANAGER_H_ +#define CHROME_BROWSER_CHROMEOS_DBUS_DBUS_THREAD_MANAGER_H_ +#pragma once + +#include "base/memory/scoped_ptr.h" +#include "base/memory/ref_counted.h" + +namespace base { +class Thread; +}; + +namespace dbus { +class Bus; +}; + +namespace chromeos { + +class CrosDBusService; + +// DBusThreadManager manages the D-Bus thread, the thread dedicated to +// handling asynchronous D-Bus operations. +// +// This class also manages D-Bus connections and D-Bus clients, which +// depend on the D-Bus thread to ensure the right order of shutdowns for +// the D-Bus thread, the D-Bus connections, and the D-Bus clients. +// +// CALLBACKS IN D-BUS CLIENTS: +// +// D-Bus clients managed by DBusThreadManager are guaranteed to be deleted +// after the D-Bus thread so the clients don't need to worry if new +// incoming messages arrive from the D-Bus thread during shutdown of the +// clients. However, the UI message loop may still be running during the +// shutdown, hence the D-Bus clients should inherit +// base::RefCountedThreadSafe if they export methods or call methods, to +// ensure that callbacks can reference |this| safely on the UI thread +// during the shutdown. +// +class DBusThreadManager { + public: + // Sets the global instance. Must be called before any calls to Get(). + // We explicitly initialize and shut down the global object, rather than + // making it a Singleton, to ensure clean startup and shutdown. + static void Initialize(); + + // Destroys the global instance. + static void Shutdown(); + + // Gets the global instance. Initialize() must be called first. + static DBusThreadManager* Get(); + + // Returns the connection to the system bus. + dbus::Bus* system_bus() { return system_bus_.get(); } + + private: + DBusThreadManager(); + virtual ~DBusThreadManager(); + + scoped_ptr<base::Thread> dbus_thread_; + scoped_refptr<dbus::Bus> system_bus_; + CrosDBusService* cros_dbus_service_; + + DISALLOW_COPY_AND_ASSIGN(DBusThreadManager); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_DBUS_DBUS_THREAD_MANAGER_H_ diff --git a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc new file mode 100644 index 0000000..bf0f31b --- /dev/null +++ b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.cc @@ -0,0 +1,265 @@ +// Copyright (c) 2011 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/chromeos/dbus/proxy_resolution_service_provider.h" + +#include "base/bind.h" +#include "base/threading/platform_thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/exported_object.h" +#include "net/base/net_errors.h" +#include "net/proxy/proxy_service.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +// The ProxyResolverInterface implementation used in production. +class ProxyResolverImpl : public ProxyResolverInterface { + public: + // Data being used in one proxy resolution. + class Request { + public: + explicit Request(const std::string& source_url) + : ALLOW_THIS_IN_INITIALIZER_LIST( + completion_callback_(this, &Request::OnCompletion)), + source_url_(source_url), + result_(net::ERR_FAILED) { + } + + virtual ~Request() {} + + // Callback on IO thread for when net::ProxyService::ResolveProxy + // completes, synchronously or asynchronously. + void OnCompletion(int result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + result_ = result; + // Generate the error message if the error message is not yet set, + // and there was an error. + if (error_.empty() && result_ != net::OK) + error_ = net::ErrorToString(result_); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, notify_task_); + } + + net::CompletionCallbackImpl<Request> completion_callback_; + + std::string source_url_; // URL being resolved. + int result_; // Result of proxy resolution. + net::ProxyInfo proxy_info_; // ProxyInfo resolved for source_url_. + std::string error_; // Error from proxy resolution. + base::Closure notify_task_; // Task to notify of resolution result. + + private: + DISALLOW_COPY_AND_ASSIGN(Request); + }; + + ProxyResolverImpl() + : origin_thread_id_(base::PlatformThread::CurrentId()) { + } + + virtual ~ProxyResolverImpl() { + base::AutoLock lock(data_lock_); + while (!all_requests_.empty()) { + LOG(WARNING) << "Pending request for " + << all_requests_.back()->source_url_; + delete all_requests_.back(); + all_requests_.pop_back(); + } + } + + // ProxyResolverInterface override. + virtual void ResolveProxy( + const std::string& source_url, + const std::string& signal_interface, + const std::string& signal_name, + scoped_refptr<dbus::ExportedObject> exported_object) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ProxyResolverImpl::ResolveProxyInternal, + this, + source_url, + signal_interface, + signal_name, + exported_object)); + } + + private: + // Helper function for ResolveProxy(). + void ResolveProxyInternal( + const std::string& source_url, + const std::string& signal_interface, + const std::string& signal_name, + scoped_refptr<dbus::ExportedObject> exported_object) { + // Make sure we're running on IO thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Create a request slot for this proxy resolution request. + Request* request = new Request(source_url); + request->notify_task_ = base::Bind( + &ProxyResolverImpl::NotifyProxyResolved, + this, + signal_interface, + signal_name, + exported_object, + request); + // Queue request slot. + { + base::AutoLock lock(data_lock_); + all_requests_.push_back(request); + } + + // Check if we have the URLRequestContextGetter. + scoped_refptr<net::URLRequestContextGetter> getter = + ProfileManager::GetDefaultProfile()->GetRequestContext(); + if (!getter) { + request->error_ = "No URLRequestContextGetter"; + request->OnCompletion(net::ERR_UNEXPECTED); + return; + } + + // Retrieve ProxyService from profile's request context. + net::ProxyService* proxy_service = + getter->GetURLRequestContext()->proxy_service(); + if (!proxy_service) { + request->error_ = "No proxy service in chrome"; + request->OnCompletion(net::ERR_UNEXPECTED); + return; + } + + VLOG(1) << "Starting network proxy resolution for " + << request->source_url_; + request->result_ = proxy_service->ResolveProxy( + GURL(request->source_url_), &request->proxy_info_, + &request->completion_callback_, NULL, net::BoundNetLog()); + if (request->result_ != net::ERR_IO_PENDING) { + VLOG(1) << "Network proxy resolution completed synchronously."; + request->OnCompletion(request->result_); + } + } + + // Called on UI thread as task posted from Request::OnCompletion on IO + // thread. + void NotifyProxyResolved( + const std::string& signal_interface, + const std::string& signal_name, + scoped_refptr<dbus::ExportedObject> exported_object, + Request* request) { + DCHECK(OnOriginThread()); + + // Send a signal to the client. + dbus::Signal signal(signal_interface, signal_name); + dbus::MessageWriter writer(&signal); + writer.AppendString(request->source_url_); + writer.AppendString(request->proxy_info_.ToPacString()); + writer.AppendString(request->error_); + exported_object->SendSignal(&signal); + VLOG(1) << "Sending signal: " << signal.ToString(); + + base::AutoLock lock(data_lock_); + std::vector<Request*>::iterator iter = + std::find(all_requests_.begin(), all_requests_.end(), request); + if (iter == all_requests_.end()) { + LOG(ERROR) << "can't find request slot(" << request->source_url_ + << ") in proxy-resolution queue"; + } else { + all_requests_.erase(iter); + } + delete request; + } + + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + base::PlatformThreadId origin_thread_id_; + // Lock for data members to synchronize access on multiple threads. + base::Lock data_lock_; + std::vector<Request*> all_requests_; + + DISALLOW_COPY_AND_ASSIGN(ProxyResolverImpl); +}; + +ProxyResolutionServiceProvider::ProxyResolutionServiceProvider( + ProxyResolverInterface* resolver) + : resolver_(resolver), + origin_thread_id_(base::PlatformThread::CurrentId()) { +} + +ProxyResolutionServiceProvider::~ProxyResolutionServiceProvider() { +} + +void ProxyResolutionServiceProvider::Start( + scoped_refptr<dbus::ExportedObject> exported_object) { + DCHECK(OnOriginThread()); + exported_object_ = exported_object; + VLOG(1) << "ProxyResolutionServiceProvider started"; + exported_object_->ExportMethod( + kLibCrosServiceInterface, + kResolveNetworkProxy, + base::Bind(&ProxyResolutionServiceProvider::ResolveProxyHandler, + this), + base::Bind(&ProxyResolutionServiceProvider::OnExported, + this)); +} + +void ProxyResolutionServiceProvider::OnExported( + const std::string& interface_name, + const std::string& method_name, + bool success) { + if (!success) { + LOG(ERROR) << "Failed to export " << interface_name << "." + << method_name; + } + VLOG(1) << "Method exported: " << interface_name << "." << method_name; +} + +bool ProxyResolutionServiceProvider::OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; +} + +dbus::Response* ProxyResolutionServiceProvider::ResolveProxyHandler( + dbus::MethodCall* method_call) { + DCHECK(OnOriginThread()); + VLOG(1) << "Handing method call: " << method_call->ToString(); + // The method call should contain the three string parameters. + dbus::MessageReader reader(method_call); + std::string source_url; + std::string signal_interface; + std::string signal_name; + if (!reader.PopString(&source_url) || + !reader.PopString(&signal_interface) || + !reader.PopString(&signal_name)) { + LOG(ERROR) << "Unexpected method call: " << method_call->ToString(); + return NULL; + } + + resolver_->ResolveProxy(source_url, + signal_interface, + signal_name, + exported_object_); + + // Return an empty response for now. We'll send a signal once the + // network proxy resolution is completed. + dbus::Response* response = dbus::Response::FromMethodCall(method_call); + return response; +} + +ProxyResolutionServiceProvider* ProxyResolutionServiceProvider::Get() { + return new ProxyResolutionServiceProvider(new ProxyResolverImpl); +} + +ProxyResolutionServiceProvider* ProxyResolutionServiceProvider::GetForTesting( + ProxyResolverInterface* resolver) { + return new ProxyResolutionServiceProvider(resolver); +} + +ProxyResolverInterface::~ProxyResolverInterface() { +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h new file mode 100644 index 0000000..2bb644e --- /dev/null +++ b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h @@ -0,0 +1,117 @@ +// Copyright (c) 2011 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_CHROMEOS_DBUS_PROXY_RESOLUTION_SERVICE_PROVIDER_H_ +#define CHROME_BROWSER_CHROMEOS_DBUS_PROXY_RESOLUTION_SERVICE_PROVIDER_H_ +#pragma once + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +#include "base/threading/platform_thread.h" +#include "chrome/browser/chromeos/dbus/cros_dbus_service.h" + +namespace dbus { +class ExportedObject; +class MethodCall; +class Response; +} + +namespace chromeos { + +class ProxyResolverInterface; + +// This class provides proxy resolution service for CrosDBusService. +// It processes proxy resolution requests for ChromeOS clients. +// +// The following methods are exported. +// +// Interface: org.chromium.LibCrosServiceInterface (kLibCrosServiceInterface) +// Method: ResolveNetworkProxy (kResolveNetworkProxy) +// Parameters: string:source_url +// string:signal_interface +// string:signal_name +// +// Resolves the proxy for |source_url|. Returns the result +// as a D-Bus signal sent to |signal_interface| and |signal_name|. +// +// The returned signal will contain the three values: +// - string:source_url - requested source URL. +// - string:proxy_info - proxy info for the source URL in PAC format +// like "PROXY cache.example.com:12345" +// - string:error_message - error message. Empty if successful. +// +class ProxyResolutionServiceProvider + : public CrosDBusService::ServiceProviderInterface { + public: + virtual ~ProxyResolutionServiceProvider(); + + // CrosDBusService::ServiceProviderInterface override. + virtual void Start(scoped_refptr<dbus::ExportedObject> exported_object); + + // Gets the instance. + static ProxyResolutionServiceProvider* Get(); + + private: + explicit ProxyResolutionServiceProvider(ProxyResolverInterface *resovler); + + // Gets the instance for testing. Takes the ownership of |resovler| + friend class ProxyResolutionServiceProviderTest; + static ProxyResolutionServiceProvider* GetForTesting( + ProxyResolverInterface* resolver); + + // Called from ExportedObject, when ResolveProxyHandler() is exported as + // a D-Bus method, or failed to be exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success); + + // Callback to be invoked when ChromeOS clients send network proxy + // resolution requests to the service running in chrome executable. + // Called on UI thread from dbus request. + dbus::Response* ResolveProxyHandler(dbus::MethodCall* method_call); + + // Returns true if the current thread is on the origin thread. + bool OnOriginThread(); + + scoped_refptr<dbus::ExportedObject> exported_object_; + scoped_refptr<ProxyResolverInterface> resolver_; + base::PlatformThreadId origin_thread_id_; + + DISALLOW_COPY_AND_ASSIGN(ProxyResolutionServiceProvider); +}; + +// The interface is defined so we can mock out the proxy resolver +// implementation. +// +// ProxyResolverInterface is a ref counted object, to ensure that |this| +// of the object is alive when callbacks referencing |this| are called. +class ProxyResolverInterface + : public base::RefCountedThreadSafe<ProxyResolverInterface> { + public: + virtual ~ProxyResolverInterface(); + + // Resolves the proxy for the given URL. Returns the result as a + // signal sent to |signal_interface| and + // |signal_name|. |exported_object| will be used to send the + // signal. The signal contains the three string members: + // + // - source url: the requested source URL. + // - proxy info: proxy info for the source URL in PAC format. + // - error message: empty if the proxy resolution was successful. + virtual void ResolveProxy( + const std::string& source_url, + const std::string& signal_interface, + const std::string& signal_name, + scoped_refptr<dbus::ExportedObject> exported_object) = 0; + + private: + friend class base::RefCountedThreadSafe<ProxyResolverInterface>; +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_DBUS_PROXY_RESOLUTION_SERVICE_PROVIDER_H_ diff --git a/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc new file mode 100644 index 0000000..50978a1 --- /dev/null +++ b/chrome/browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc @@ -0,0 +1,311 @@ +// Copyright (c) 2011 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. +// +// This test is relatively complicated. Here's the summary of what it does: +// +// - Set up mock D-Bus related objects to mock out D-Bus calls. +// - Set up a mock proxy resolver to mock out the proxy resolution. +// - Create ProxyResolutionServiceProvider by injecting the mocks +// - Start the service provider. +// - Request ProxyResolutionServiceProvider to resolve proxy for kSourceURL. +// - ProxyResolutionServiceProvider will return the result as a signal. +// - Confirm that we receive the signal and check the contents of the signal. + +#include "chrome/browser/chromeos/dbus/proxy_resolution_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "dbus/message.h" +#include "dbus/mock_bus.h" +#include "dbus/mock_exported_object.h" +#include "dbus/mock_object_proxy.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::Unused; + +namespace chromeos { + +// We want to know about the proxy info for the URL. +const char kSourceURL[] = "http://www.gmail.com/"; + +// ProxyResolutionServiceProvider will return the proxy info as a D-Bus +// signal, to the following signal interface and the signal name. +const char kReturnSignalInterface[] = "org.chromium.TestInterface"; +const char kReturnSignalName[] = "TestSignal"; + +// The returned proxy info. +const char kReturnProxyInfo[] = "PROXY cache.example.com:12345"; + +// The error message is empty if proxy resolution is successful. +const char kReturnEmptyErrorMessage[] = ""; + +// Mock for ProxyResolverInterface. We'll inject this to +// ProxyResolutionServiceProvider to mock out the proxy resolution. +class MockProxyResolver : public ProxyResolverInterface { + public: + MOCK_METHOD4(ResolveProxy, + void(const std::string& source_url, + const std::string& signal_interface, + const std::string& signal_name, + scoped_refptr<dbus::ExportedObject> exported_object)); +}; + +class ProxyResolutionServiceProviderTest : public testing::Test { + public: + ProxyResolutionServiceProviderTest() + : signal_received_successfully_(false), + mock_resolver_(NULL) { + } + + virtual void SetUp() { + // Create a mock bus. + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + mock_bus_ = new dbus::MockBus(options); + + // ShutdownAndBlock() will be called in TearDown(). + EXPECT_CALL(*mock_bus_, ShutdownAndBlock()).WillOnce(Return()); + + // Create a mock exported object that behaves as + // org.chromium.CrosDBusService. + mock_exported_object_ = + new dbus::MockExportedObject(mock_bus_.get(), + kLibCrosServiceName, + kLibCrosServicePath); + + // |mock_exported_object_|'s ExportMethod() will use + // |MockExportedObject(). + EXPECT_CALL( + *mock_exported_object_, + ExportMethod(_, _, _, _)).WillOnce( + Invoke(this, + &ProxyResolutionServiceProviderTest::MockExportMethod)); + // |mock_exported_object_|'s SendSignal() will use + // |MockSendSignal(). + EXPECT_CALL( + *mock_exported_object_, + SendSignal(_)).WillOnce( + Invoke(this, + &ProxyResolutionServiceProviderTest::MockSendSignal)); + + // Create a mock object proxy, with which we call a method of + // |mock_exported_object_|. + mock_object_proxy_ = + new dbus::MockObjectProxy(mock_bus_.get(), + kLibCrosServiceName, + kLibCrosServicePath); + // |mock_object_proxy_|'s CallMethodAndBlock() will use + // CreateResponse() to return responses. + EXPECT_CALL(*mock_object_proxy_, + CallMethodAndBlock(_, _)) + .WillOnce(Invoke( + this, + &ProxyResolutionServiceProviderTest::CreateResponse)); + // |mock_object_proxy_|'s ConnectToSignal will use + // MockConnectToSignal(). + EXPECT_CALL(*mock_object_proxy_, + ConnectToSignal(kReturnSignalInterface, + kReturnSignalName, + _, _)) + .WillOnce(Invoke( + this, + &ProxyResolutionServiceProviderTest::MockConnectToSignal)); + + // Create a mock proxy resolver. Will be owned by + // |proxy_resolution_service|. + mock_resolver_ = new MockProxyResolver; + // |mock_resolver_|'s ResolveProxy() will use MockResolveProxy(). + EXPECT_CALL(*mock_resolver_, ResolveProxy(kSourceURL, + kReturnSignalInterface, + kReturnSignalName, + _)) + .WillOnce(Invoke( + this, + &ProxyResolutionServiceProviderTest::MockResolveProxy)); + + // Create the proxy resolution service with the mock bus and the mock + // resolver injected. + proxy_resolution_service_ = + ProxyResolutionServiceProvider::GetForTesting(mock_resolver_); + + // Finally, start the service. + proxy_resolution_service_->Start(mock_exported_object_); + } + + virtual void TearDown() { + mock_bus_->ShutdownAndBlock(); + } + + // Called when a signal is received. + void OnSignalReceived(dbus::Signal* signal) { + ASSERT_EQ(kReturnSignalInterface, signal->GetInterface()); + ASSERT_EQ(kReturnSignalName, signal->GetMember()); + + std::string source_url; + std::string proxy_info; + std::string error_message; + + // The signal should contain three strings. + dbus::MessageReader reader(signal); + ASSERT_TRUE(reader.PopString(&source_url)); + ASSERT_TRUE(reader.PopString(&proxy_info)); + ASSERT_TRUE(reader.PopString(&error_message)); + + // Check the signal conetnts. + EXPECT_EQ(kSourceURL, source_url); + EXPECT_EQ(kReturnProxyInfo, proxy_info); + EXPECT_EQ(kReturnEmptyErrorMessage, error_message); + + // Mark that the signal is received successfully. + signal_received_successfully_ = true; + } + + // Called when connected to a signal. + void OnConnectedToSignal(const std::string& signal_interface, + const std::string& signal_name, + bool success){ + ASSERT_EQ(kReturnSignalInterface, signal_interface); + ASSERT_EQ(kReturnSignalName, signal_name); + ASSERT_TRUE(success); + } + + protected: + bool signal_received_successfully_; + MockProxyResolver* mock_resolver_; + scoped_refptr<dbus::MockBus> mock_bus_; + scoped_refptr<dbus::MockExportedObject> mock_exported_object_; + scoped_refptr<dbus::MockObjectProxy> mock_object_proxy_; + scoped_refptr<ProxyResolutionServiceProvider> proxy_resolution_service_; + dbus::ExportedObject::MethodCallCallback resolve_network_proxy_; + dbus::ObjectProxy::SignalCallback on_signal_callback_; + + private: + // Behaves as |mock_exported_object_|'s ExportMethod(). + void MockExportMethod( + const std::string& interface_name, + const std::string& method_name, + dbus::ExportedObject::MethodCallCallback method_callback, + dbus::ExportedObject::OnExportedCallback on_exported_callback) { + if (interface_name == kLibCrosServiceInterface, + method_name == kResolveNetworkProxy) { + // Tell the call back that the method is exported successfully. + on_exported_callback.Run(interface_name, method_name, true); + // Capture the callback, so we can run this at a later time. + resolve_network_proxy_ = method_callback; + return; + } + + LOG(ERROR) << "Unexpected method exported: " << interface_name + << method_name; + } + + // Behaves as |mock_exported_object_|'s SendSignal(). + void MockSendSignal(dbus::Signal* signal) { + ASSERT_EQ(kReturnSignalInterface, signal->GetInterface()); + ASSERT_EQ(kReturnSignalName, signal->GetMember()); + + // Run the callback captured in MockConnectToSignal(). This will call + // OnSignalReceived(). + on_signal_callback_.Run(signal); + } + + // Behaves as |mock_resolver_|'s ResolveProxy(). + void MockResolveProxy(const std::string& source_url, + const std::string& signal_interface, + const std::string& signal_name, + scoped_refptr<dbus::ExportedObject> exported_object) { + if (source_url == kSourceURL) { + dbus::Signal signal(signal_interface, + signal_name); + dbus::MessageWriter writer(&signal); + writer.AppendString(kSourceURL); + writer.AppendString(kReturnProxyInfo); + writer.AppendString(kReturnEmptyErrorMessage); + // Send the signal back to the requested signal interface and the + // signal name. + exported_object->SendSignal(&signal); + return; + } + + LOG(ERROR) << "Unexpected source URL: " << source_url; + } + + // Creates a response for |mock_object_proxy_|. + dbus::Response* CreateResponse( + dbus::MethodCall* method_call, + Unused) { + if (method_call->GetInterface() == + kLibCrosServiceInterface && + method_call->GetMember() == kResolveNetworkProxy) { + // Set the serial number to non-zero, so + // dbus_message_new_method_return() won't emit a warning. + method_call->SetSerial(1); + // Run the callback captured in MockExportMethod(). This will send + // a signal, which will be received by |on_signal_callback_|. + dbus::Response* response = resolve_network_proxy_.Run(method_call); + return response; + } + + LOG(ERROR) << "Unexpected method call: " << method_call->ToString(); + return NULL; + } + + // Behaves as |mock_object_proxy_|'s ConnectToSignal(). + void MockConnectToSignal( + const std::string& interface_name, + const std::string& signal_name, + dbus::ObjectProxy::SignalCallback signal_callback, + dbus::ObjectProxy::OnConnectedCallback connected_callback) { + // Tell the callback that the object proxy is connected to the signal. + connected_callback.Run(interface_name, signal_name, true); + // Capture the callback, so we can run this at a later time. + on_signal_callback_ = signal_callback; + } +}; + +TEST_F(ProxyResolutionServiceProviderTest, ResolveProxy) { + // Connect to the signal that will be sent to kReturnSignalInterface and + // kReturnSignalName. ResolveNetworkProxy() will send the result as a + // signal. OnSignalReceived() will be called upon the delivery. + mock_object_proxy_->ConnectToSignal( + kReturnSignalInterface, + kReturnSignalName, + base::Bind(&ProxyResolutionServiceProviderTest::OnSignalReceived, + base::Unretained(this)), + base::Bind(&ProxyResolutionServiceProviderTest::OnConnectedToSignal, + base::Unretained(this))); + // The signal is not yet received. + ASSERT_FALSE(signal_received_successfully_); + + // Create a method call to resolve proxy config for kSourceURL. + dbus::MethodCall method_call(kLibCrosServiceInterface, + kResolveNetworkProxy); + dbus::MessageWriter writer(&method_call); + writer.AppendString(kSourceURL); + writer.AppendString(kReturnSignalInterface); + writer.AppendString(kReturnSignalName); + + // Call the ResolveNetworkProxy method. + scoped_ptr<dbus::Response> response( + mock_object_proxy_->CallMethodAndBlock( + &method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + + // An empty response should be returned. + ASSERT_TRUE(response.get()); + dbus::MessageReader reader(response.get()); + ASSERT_FALSE(reader.HasMoreData()); + + // Confirm that the signal is received successfully. + // The contents of the signal are checked in OnSignalReceived(). + ASSERT_TRUE(signal_received_successfully_); +} + +} // namespace chromeos |