diff options
author | mek <mek@chromium.org> | 2014-10-23 15:06:09 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-23 22:06:35 +0000 |
commit | 0c55c7f631795d3f76b38bc7f6b54f5be7da8f22 (patch) | |
tree | 5c6b4c75cbea82cdd5b38b61b87a62a6b1fc858c /content/browser/geofencing | |
parent | 4087adc5677bb8dd7f414c0afafc8f4215ce0077 (diff) | |
download | chromium_src-0c55c7f631795d3f76b38bc7f6b54f5be7da8f22.zip chromium_src-0c55c7f631795d3f76b38bc7f6b54f5be7da8f22.tar.gz chromium_src-0c55c7f631795d3f76b38bc7f6b54f5be7da8f22.tar.bz2 |
Refactor GeofencingManager to have one instance per StoragePartition.
Add a new GeofencingService class for the few global tasks GeofencingManager
used to do.
BUG=383125
Review URL: https://codereview.chromium.org/645763003
Cr-Commit-Position: refs/heads/master@{#300960}
Diffstat (limited to 'content/browser/geofencing')
-rw-r--r-- | content/browser/geofencing/geofencing_dispatcher_host.cc | 24 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_dispatcher_host.h | 6 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_manager.cc | 295 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_manager.h | 115 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_manager_unittest.cc | 319 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_provider.h | 23 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_registration_delegate.h | 29 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_service.cc | 199 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_service.h | 105 | ||||
-rw-r--r-- | content/browser/geofencing/geofencing_service_unittest.cc | 191 |
10 files changed, 915 insertions, 391 deletions
diff --git a/content/browser/geofencing/geofencing_dispatcher_host.cc b/content/browser/geofencing/geofencing_dispatcher_host.cc index 077485b..f2fe54a 100644 --- a/content/browser/geofencing/geofencing_dispatcher_host.cc +++ b/content/browser/geofencing/geofencing_dispatcher_host.cc @@ -7,16 +7,15 @@ #include "content/browser/geofencing/geofencing_manager.h" #include "content/common/geofencing_messages.h" #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" -#include "url/gurl.h" namespace content { static const int kMaxRegionIdLength = 200; GeofencingDispatcherHost::GeofencingDispatcherHost( - BrowserContext* browser_context) + GeofencingManager* geofencing_manager) : BrowserMessageFilter(GeofencingMsgStart), - browser_context_(browser_context), + manager_(geofencing_manager), weak_factory_(this) { } @@ -47,10 +46,8 @@ void GeofencingDispatcherHost::OnRegisterRegion( return; } // TODO(mek): Actually pass service worker information to manager. - GeofencingManager::GetInstance()->RegisterRegion( - browser_context_, - 0, /* service_worker_registration_id */ - GURL(), /* service_worker_origin */ + manager_->RegisterRegion( + 0, /* service_worker_registration_id */ region_id, region, base::Bind(&GeofencingDispatcherHost::RegisterRegionCompleted, @@ -70,10 +67,8 @@ void GeofencingDispatcherHost::OnUnregisterRegion( return; } // TODO(mek): Actually pass service worker information to manager. - GeofencingManager::GetInstance()->UnregisterRegion( - browser_context_, - 0, /* service_worker_registration_id */ - GURL(), /* service_worker_origin */ + manager_->UnregisterRegion( + 0, /* service_worker_registration_id */ region_id, base::Bind(&GeofencingDispatcherHost::UnregisterRegionCompleted, weak_factory_.GetWeakPtr(), @@ -86,11 +81,8 @@ void GeofencingDispatcherHost::OnGetRegisteredRegions(int thread_id, GeofencingRegistrations result; // TODO(mek): Actually pass service worker information to manager. GeofencingStatus status = - GeofencingManager::GetInstance()->GetRegisteredRegions( - browser_context_, - 0, /* service_worker_registration_id */ - GURL(), /* service_worker_origin */ - &result); + manager_->GetRegisteredRegions(0, /* service_worker_registration_id */ + &result); Send(new GeofencingMsg_GetRegisteredRegionsComplete( thread_id, request_id, status, result)); } diff --git a/content/browser/geofencing/geofencing_dispatcher_host.h b/content/browser/geofencing/geofencing_dispatcher_host.h index 86858b8..16bc2b4 100644 --- a/content/browser/geofencing/geofencing_dispatcher_host.h +++ b/content/browser/geofencing/geofencing_dispatcher_host.h @@ -14,11 +14,11 @@ struct WebCircularGeofencingRegion; namespace content { -class BrowserContext; +class GeofencingManager; class GeofencingDispatcherHost : public BrowserMessageFilter { public: - explicit GeofencingDispatcherHost(BrowserContext* browser_context); + explicit GeofencingDispatcherHost(GeofencingManager* geofencing_manager); private: ~GeofencingDispatcherHost() override; @@ -42,7 +42,7 @@ class GeofencingDispatcherHost : public BrowserMessageFilter { int request_id, GeofencingStatus result); - BrowserContext* browser_context_; + scoped_refptr<GeofencingManager> manager_; base::WeakPtrFactory<GeofencingDispatcherHost> weak_factory_; DISALLOW_COPY_AND_ASSIGN(GeofencingDispatcherHost); diff --git a/content/browser/geofencing/geofencing_manager.cc b/content/browser/geofencing/geofencing_manager.cc index 94b0f25..33afae5 100644 --- a/content/browser/geofencing/geofencing_manager.cc +++ b/content/browser/geofencing/geofencing_manager.cc @@ -7,97 +7,90 @@ #include <algorithm> #include "base/callback.h" -#include "base/memory/singleton.h" -#include "content/browser/geofencing/geofencing_provider.h" +#include "content/browser/geofencing/geofencing_service.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/public/browser/browser_thread.h" #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" -#include "url/gurl.h" namespace content { -struct GeofencingManager::RegistrationKey { - RegistrationKey(BrowserContext* browser_context, - int64 service_worker_registration_id, - const GURL& service_worker_origin, - const std::string& region_id); - - BrowserContext* browser_context; +struct GeofencingManager::Registration { + Registration(int64 service_worker_registration_id, + const std::string& region_id, + const blink::WebCircularGeofencingRegion& region, + const StatusCallback& callback, + int64 geofencing_registration_id); int64 service_worker_registration_id; - GURL service_worker_origin; - std::string region_id; -}; - -GeofencingManager::RegistrationKey::RegistrationKey( - BrowserContext* browser_context, - int64 service_worker_registration_id, - const GURL& service_worker_origin, - const std::string& region_id) - : browser_context(browser_context), - service_worker_registration_id(service_worker_registration_id), - service_worker_origin(service_worker_origin), - region_id(region_id) { -} - -struct GeofencingManager::Registration { - Registration(); - Registration(const RegistrationKey& key, - const blink::WebCircularGeofencingRegion& region); - - RegistrationKey key; blink::WebCircularGeofencingRegion region; - // Registration id as returned by the GeofencingProvider, set to -1 if not - // currently registered with the provider. - int registration_id; + // Registration ID as returned by the |GeofencingService|. + int64 geofencing_registration_id; + + // Callback to call when registration is completed. This field is reset when + // registration is complete. + StatusCallback registration_callback; - // Flag to indicate if this registration has completed, and thus should be + // Returns true if registration has been completed, and thus should be // included in calls to GetRegisteredRegions. - bool is_active; + bool is_active() const { return registration_callback.is_null(); } }; -GeofencingManager::Registration::Registration() : key(nullptr, -1, GURL(), "") { +GeofencingManager::Registration::Registration( + int64 service_worker_registration_id, + const std::string& region_id, + const blink::WebCircularGeofencingRegion& region, + const GeofencingManager::StatusCallback& callback, + int64 geofencing_registration_id) + : service_worker_registration_id(service_worker_registration_id), + region_id(region_id), + region(region), + geofencing_registration_id(geofencing_registration_id), + registration_callback(callback) { } -GeofencingManager::Registration::Registration( - const RegistrationKey& key, - const blink::WebCircularGeofencingRegion& region) - : key(key), region(region), registration_id(-1), is_active(false) { +GeofencingManager::GeofencingManager( + const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) + : service_(nullptr), service_worker_context_(service_worker_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); } -class GeofencingManager::RegistrationMatches { - public: - RegistrationMatches(const RegistrationKey& key) : key_(key) {} - - bool operator()(const Registration& registration) { - return registration.key.browser_context == key_.browser_context && - registration.key.service_worker_registration_id == - key_.service_worker_registration_id && - registration.key.service_worker_origin == - key_.service_worker_origin && - registration.key.region_id == key_.region_id; - } +GeofencingManager::~GeofencingManager() { +} - private: - const RegistrationKey& key_; -}; +void GeofencingManager::Init() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&GeofencingManager::InitOnIO, this)); +} -GeofencingManager::GeofencingManager() { +void GeofencingManager::Shutdown() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&GeofencingManager::ShutdownOnIO, this)); } -GeofencingManager::~GeofencingManager() { +void GeofencingManager::InitOnIO() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + service_ = GeofencingServiceImpl::GetInstance(); } -GeofencingManager* GeofencingManager::GetInstance() { +void GeofencingManager::ShutdownOnIO() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - return Singleton<GeofencingManager>::get(); + // Clean up all registrations with the |GeofencingService|. + // TODO(mek): This will need to change to support geofence registrations that + // outlive the browser, although removing the references to this + // |GeofencingManager| from the |GeofencingService| will still be needed. + for (const auto& registration : registrations_by_id_) { + service_->UnregisterRegion(registration.first); + } } void GeofencingManager::RegisterRegion( - BrowserContext* browser_context, int64 service_worker_registration_id, - const GURL& service_worker_origin, const std::string& region_id, const blink::WebCircularGeofencingRegion& region, const StatusCallback& callback) { @@ -105,148 +98,148 @@ void GeofencingManager::RegisterRegion( // TODO(mek): Validate region_id and region. - if (!provider_.get()) { - callback.Run(GeofencingStatus:: - GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE); + if (!service_->IsServiceAvailable()) { + callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE); return; } - RegistrationKey key(browser_context, - service_worker_registration_id, - service_worker_origin, - region_id); - if (FindRegistration(key)) { + if (FindRegistration(service_worker_registration_id, region_id)) { // Already registered, return an error. - callback.Run(GeofencingStatus::GEOFENCING_STATUS_ERROR); + // TODO(mek): Use a more specific error code. + callback.Run(GEOFENCING_STATUS_ERROR); return; } - // Add registration, but don't mark it as active yet. This prevents duplicate - // registrations. - AddRegistration(key, region); - - // Register with provider. - provider_->RegisterRegion( - region, - base::Bind(&GeofencingManager::RegisterRegionCompleted, - base::Unretained(this), - callback, - key)); + AddRegistration(service_worker_registration_id, + region_id, + region, + callback, + service_->RegisterRegion(region, this)); } -void GeofencingManager::UnregisterRegion(BrowserContext* browser_context, - int64 service_worker_registration_id, - const GURL& service_worker_origin, +void GeofencingManager::UnregisterRegion(int64 service_worker_registration_id, const std::string& region_id, const StatusCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // TODO(mek): Validate region_id. - if (!provider_.get()) { - callback.Run(GeofencingStatus:: - GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE); + if (!service_->IsServiceAvailable()) { + callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE); return; } - RegistrationKey key(browser_context, - service_worker_registration_id, - service_worker_origin, - region_id); - Registration* registration = FindRegistration(key); + Registration* registration = + FindRegistration(service_worker_registration_id, region_id); if (!registration) { - // Not registered, return an error/ - callback.Run(GeofencingStatus::GEOFENCING_STATUS_ERROR); + // Not registered, return an error. + callback.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED); return; } - if (!registration->is_active) { + if (!registration->is_active()) { // Started registration, but not completed yet, error. - callback.Run(GeofencingStatus::GEOFENCING_STATUS_ERROR); + callback.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED); return; } - if (registration->registration_id != -1) { - provider_->UnregisterRegion(registration->registration_id); - } - ClearRegistration(key); - callback.Run(GeofencingStatus::GEOFENCING_STATUS_OK); + service_->UnregisterRegion(registration->geofencing_registration_id); + ClearRegistration(registration); + callback.Run(GEOFENCING_STATUS_OK); } GeofencingStatus GeofencingManager::GetRegisteredRegions( - BrowserContext* browser_context, int64 service_worker_registration_id, - const GURL& service_worker_origin, std::map<std::string, blink::WebCircularGeofencingRegion>* result) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CHECK(result); - if (!provider_.get()) { - return GeofencingStatus:: - GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE; + if (!service_->IsServiceAvailable()) { + return GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE; } // Populate result, filtering out inactive registrations. result->clear(); - for (const auto& registration : registrations_) { - if (registration.key.browser_context == browser_context && - registration.key.service_worker_registration_id == - service_worker_registration_id && - registration.key.service_worker_origin == service_worker_origin && - registration.is_active) { - (*result)[registration.key.region_id] = registration.region; - } + ServiceWorkerRegistrationsMap::iterator registrations = + registrations_.find(service_worker_registration_id); + if (registrations == registrations_.end()) + return GEOFENCING_STATUS_OK; + for (const auto& registration : registrations->second) { + if (registration.second.is_active()) + (*result)[registration.first] = registration.second.region; } - return GeofencingStatus::GEOFENCING_STATUS_OK; + return GEOFENCING_STATUS_OK; } -void GeofencingManager::RegisterRegionCompleted(const StatusCallback& callback, - const RegistrationKey& key, - GeofencingStatus status, - int registration_id) { +void GeofencingManager::RegistrationFinished(int64 geofencing_registration_id, + GeofencingStatus status) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (status != GEOFENCING_STATUS_OK) { - ClearRegistration(key); - callback.Run(status); - return; - } - - Registration* registration = FindRegistration(key); + Registration* registration = FindRegistrationById(geofencing_registration_id); DCHECK(registration); - registration->registration_id = registration_id; - registration->is_active = true; - callback.Run(GeofencingStatus::GEOFENCING_STATUS_OK); -} + DCHECK(!registration->is_active()); + registration->registration_callback.Run(status); + registration->registration_callback.Reset(); -void GeofencingManager::SetProviderForTests( - scoped_ptr<GeofencingProvider> provider) { - DCHECK(!provider_.get()); - provider_ = provider.Pass(); + // If the registration wasn't succesful, remove it from our storage. + if (status != GEOFENCING_STATUS_OK) + ClearRegistration(registration); } GeofencingManager::Registration* GeofencingManager::FindRegistration( - const RegistrationKey& key) { - std::vector<Registration>::iterator it = std::find_if( - registrations_.begin(), registrations_.end(), RegistrationMatches(key)); - if (it == registrations_.end()) + int64 service_worker_registration_id, + const std::string& region_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ServiceWorkerRegistrationsMap::iterator registrations_iterator = + registrations_.find(service_worker_registration_id); + if (registrations_iterator == registrations_.end()) + return nullptr; + RegionIdRegistrationMap::iterator registration = + registrations_iterator->second.find(region_id); + if (registration == registrations_iterator->second.end()) return nullptr; - return &*it; + return ®istration->second; +} + +GeofencingManager::Registration* GeofencingManager::FindRegistrationById( + int64 geofencing_registration_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + RegistrationIdRegistrationMap::iterator registration_iterator = + registrations_by_id_.find(geofencing_registration_id); + if (registration_iterator == registrations_by_id_.end()) + return nullptr; + return ®istration_iterator->second->second; } GeofencingManager::Registration& GeofencingManager::AddRegistration( - const RegistrationKey& key, - const blink::WebCircularGeofencingRegion& region) { - DCHECK(!FindRegistration(key)); - registrations_.push_back(Registration(key, region)); - return registrations_.back(); + int64 service_worker_registration_id, + const std::string& region_id, + const blink::WebCircularGeofencingRegion& region, + const StatusCallback& callback, + int64 geofencing_registration_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!FindRegistration(service_worker_registration_id, region_id)); + RegionIdRegistrationMap::iterator registration = + registrations_[service_worker_registration_id] + .insert(std::make_pair(region_id, + Registration(service_worker_registration_id, + region_id, + region, + callback, + geofencing_registration_id))) + .first; + registrations_by_id_[geofencing_registration_id] = registration; + return registration->second; } -void GeofencingManager::ClearRegistration(const RegistrationKey& key) { - std::vector<Registration>::iterator it = std::find_if( - registrations_.begin(), registrations_.end(), RegistrationMatches(key)); - if (it == registrations_.end()) - return; - registrations_.erase(it); +void GeofencingManager::ClearRegistration(Registration* registration) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + registrations_by_id_.erase(registration->geofencing_registration_id); + ServiceWorkerRegistrationsMap::iterator registrations_iterator = + registrations_.find(registration->service_worker_registration_id); + DCHECK(registrations_iterator != registrations_.end()); + registrations_iterator->second.erase(registration->region_id); + if (registrations_iterator->second.empty()) + registrations_.erase(registrations_iterator); } } // namespace content diff --git a/content/browser/geofencing/geofencing_manager.h b/content/browser/geofencing/geofencing_manager.h index cae5edd..d5842b3 100644 --- a/content/browser/geofencing/geofencing_manager.h +++ b/content/browser/geofencing/geofencing_manager.h @@ -11,7 +11,9 @@ #include "base/callback_forward.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "content/browser/geofencing/geofencing_registration_delegate.h" #include "content/common/content_export.h" #include "content/common/geofencing_status.h" @@ -25,34 +27,30 @@ struct WebCircularGeofencingRegion; namespace content { -class BrowserContext; -class GeofencingProvider; +class GeofencingService; +class ServiceWorkerContextWrapper; -// This is the main API to the geofencing subsystem. The application will hold -// a single instance of this class. +// This is the main API to the geofencing subsystem. There is one instance of +// this class per storage partition. // This class is responsible for keeping track of which geofences are currently // registered by websites/workers, persisting this list of registrations and -// registering a subset of these active registrations with the underlying -// platform specific |GeofencingProvider| instance. -// This class lives on the IO thread, and all public methods of it should only -// ever be called from that same thread. -// FIXME: Does it make more sense for this to live on the UI thread instead of -// the IO thread? +// registering them with the global |GeofencingService|. +// This class is created on the UI thread, but all its methods should only be +// called from the IO thread. // TODO(mek): Implement some kind of persistence of registrations. -// TODO(mek): Limit the number of geofences that are registered with the -// underlying GeofencingProvider. -class CONTENT_EXPORT GeofencingManager { +class CONTENT_EXPORT GeofencingManager + : NON_EXPORTED_BASE(public GeofencingRegistrationDelegate), + public base::RefCountedThreadSafe<GeofencingManager> { public: typedef base::Callback<void(GeofencingStatus)> StatusCallback; - typedef base::Callback<void( - GeofencingStatus, - const std::map<std::string, blink::WebCircularGeofencingRegion>&)> - RegistrationsCallback; - // Gets a pointer to the singleton instance of the geofencing manager. This - // must only be called on the IO thread so that the GeofencingManager is - // always instantiated on the same thread. Ownership is NOT returned. - static GeofencingManager* GetInstance(); + explicit GeofencingManager( + const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context); + + // Init and Shutdown are for use on the UI thread when the storagepartition is + // being setup and torn down. + void Init(); + void Shutdown(); // Initiates registration of a new geofence. StatusCallback is called when // registration has completed or failed (which could possibly be before @@ -61,9 +59,7 @@ class CONTENT_EXPORT GeofencingManager { // (or in progress of being registered) region will fail. // TODO(mek): Behavior when using an already used ID might need to be revised // depending on what the actual spec ends up saying about this. - void RegisterRegion(BrowserContext* browser_context, - int64 service_worker_registration_id, - const GURL& service_worker_origin, + void RegisterRegion(int64 service_worker_registration_id, const std::string& region_id, const blink::WebCircularGeofencingRegion& region, const StatusCallback& callback); @@ -74,9 +70,7 @@ class CONTENT_EXPORT GeofencingManager { // (RegisterRegion hasn't called its callback yet) will fail. // TODO(mek): Maybe better behavior would be to allow unregistering still // in-progress registrations. - void UnregisterRegion(BrowserContext* browser_context, - int64 service_worker_registration_id, - const GURL& service_worker_origin, + void UnregisterRegion(int64 service_worker_registration_id, const std::string& region_id, const StatusCallback& callback); @@ -87,51 +81,64 @@ class CONTENT_EXPORT GeofencingManager { // has been called already (so it doesn't include still in progress // registrations). GeofencingStatus GetRegisteredRegions( - BrowserContext* browser_context, int64 service_worker_registration_id, - const GURL& service_worker_origin, - std::map<std::string, blink::WebCircularGeofencingRegion>* regions); + std::map<std::string, blink::WebCircularGeofencingRegion>* result); - void SetProviderForTests(scoped_ptr<GeofencingProvider> provider); + void SetServiceForTesting(GeofencingService* service) { + service_ = service; + } protected: - friend struct DefaultSingletonTraits<GeofencingManager>; - friend class GeofencingManagerTest; - GeofencingManager(); - virtual ~GeofencingManager(); + friend class base::RefCountedThreadSafe<GeofencingManager>; + ~GeofencingManager() override; private: // Internal bookkeeping associated with each registered geofence. - struct RegistrationKey; struct Registration; - class RegistrationMatches; - // Called by GeofencingProvider when the platform specific provider completes - // registration of a geofence. - void RegisterRegionCompleted(const StatusCallback& callback, - const RegistrationKey& key, - GeofencingStatus status, - int registration_id); + void InitOnIO(); + void ShutdownOnIO(); + + // GeofencingRegistrationDelegate implementation. + void RegistrationFinished(int64 geofencing_registration_id, + GeofencingStatus status) override; // Looks up a particular geofence registration. Returns nullptr if no // registration with the given IDs exists. - Registration* FindRegistration(const RegistrationKey& key); + Registration* FindRegistration(int64 service_worker_registration_id, + const std::string& region_id); + + // Looks up a particular geofence registration. Returns nullptr if no + // registration with the given ID exists. + Registration* FindRegistrationById(int64 geofencing_registration_id); // Registers a new registration, returning a reference to the newly inserted // object. Assumes no registration with the same IDs currently exists. Registration& AddRegistration( - const RegistrationKey& key, - const blink::WebCircularGeofencingRegion& region); + int64 service_worker_registration_id, + const std::string& region_id, + const blink::WebCircularGeofencingRegion& region, + const StatusCallback& callback, + int64 geofencing_registration_id); // Clears a registration. - void ClearRegistration(const RegistrationKey& key); - - // List of all currently registered geofences. - // TODO(mek): Better way of storing these that allows more efficient lookup - // and deletion. - std::vector<Registration> registrations_; - - scoped_ptr<GeofencingProvider> provider_; + void ClearRegistration(Registration* registration); + + // Map of all registered regions for a particular service worker registration. + typedef std::map<std::string, Registration> RegionIdRegistrationMap; + // Map of service worker registration id to the regions registered by that + // service worker. + typedef std::map<int64, RegionIdRegistrationMap> + ServiceWorkerRegistrationsMap; + ServiceWorkerRegistrationsMap registrations_; + + // Map of all registered regions by geofencing_registration_id. + typedef std::map<int64, RegionIdRegistrationMap::iterator> + RegistrationIdRegistrationMap; + RegistrationIdRegistrationMap registrations_by_id_; + + GeofencingService* service_; + scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_; DISALLOW_COPY_AND_ASSIGN(GeofencingManager); }; diff --git a/content/browser/geofencing/geofencing_manager_unittest.cc b/content/browser/geofencing/geofencing_manager_unittest.cc index 78828ac..6970b71 100644 --- a/content/browser/geofencing/geofencing_manager_unittest.cc +++ b/content/browser/geofencing/geofencing_manager_unittest.cc @@ -5,14 +5,13 @@ #include "base/callback.h" #include "base/message_loop/message_loop.h" #include "content/browser/geofencing/geofencing_manager.h" -#include "content/browser/geofencing/geofencing_provider.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/test/test_browser_thread.h" +#include "content/browser/geofencing/geofencing_service.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" -#include "url/gurl.h" using blink::WebCircularGeofencingRegion; typedef std::map<std::string, WebCircularGeofencingRegion> RegionMap; @@ -22,6 +21,8 @@ namespace { static const char* kTestRegionId = "region-id"; static const int64 kTestServiceWorkerRegistrationId = 123; static const int64 kTestServiceWorkerRegistrationId2 = 456; +static const int64 kTestGeofencingRegistrationId = 42; +static const int64 kTestGeofencingRegistrationId2 = 43; bool RegionsMatch(const WebCircularGeofencingRegion& expected, const WebCircularGeofencingRegion& arg) { @@ -33,20 +34,21 @@ bool RegionsMatch(const WebCircularGeofencingRegion& expected, namespace content { -class TestGeofencingProvider : public GeofencingProvider { +class TestGeofencingService : public GeofencingService { public: + MOCK_METHOD0(IsServiceAvailable, bool()); MOCK_METHOD2(RegisterRegion, - void(const WebCircularGeofencingRegion& region, - const RegisterCallback& callback)); - MOCK_METHOD1(UnregisterRegion, void(int registration_id)); + int64(const WebCircularGeofencingRegion& region, + GeofencingRegistrationDelegate* delegate)); + MOCK_METHOD1(UnregisterRegion, void(int64 geofencing_registration_id)); }; -ACTION_P2(CallRegisterCallback, status, id) { - arg1.Run(status, id); +ACTION_P(SaveDelegate, delegate) { + *delegate = arg1; } -ACTION_P(SaveRegisterCallback, callback) { - *callback = arg1; +ACTION_P(QuitRunner, runner) { + runner->Quit(); } MATCHER_P(WebCircularGeofencingRegionEq, expected, "") { @@ -78,25 +80,30 @@ class StatusCatcher { class GeofencingManagerTest : public testing::Test { public: - GeofencingManagerTest() - : message_loop_(), - io_thread_(BrowserThread::IO, &message_loop_), - provider_(0), - manager_(0), - test_origin_("https://example.com/") { + GeofencingManagerTest() : service_(nullptr) { test_region_.latitude = 37.421999; test_region_.longitude = -122.084015; test_region_.radius = 100; expected_regions_[kTestRegionId] = test_region_; } - virtual void SetUp() { manager_ = new GeofencingManager(); } + virtual void SetUp() { + service_ = new TestGeofencingService(); + ON_CALL(*service_, IsServiceAvailable()) + .WillByDefault(testing::Return(false)); + manager_ = new GeofencingManager(nullptr /* ServiceWorkerContextWrapper */); + manager_->SetServiceForTesting(service_); + } - virtual void TearDown() { delete manager_; } + virtual void TearDown() { + manager_ = nullptr; + delete service_; + service_ = nullptr; + } - void SetProviderForTests() { - provider_ = new TestGeofencingProvider(); - manager_->SetProviderForTests(scoped_ptr<GeofencingProvider>(provider_)); + void SetHasProviderForTests() { + ON_CALL(*service_, IsServiceAvailable()) + .WillByDefault(testing::Return(true)); } GeofencingStatus RegisterRegionSync( @@ -105,48 +112,46 @@ class GeofencingManagerTest : public testing::Test { const WebCircularGeofencingRegion& region) { StatusCatcher result; manager_->RegisterRegion( - nullptr, /* browser_context */ service_worker_registration_id, - test_origin_, id, region, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); return result.Wait(); } - GeofencingStatus RegisterRegionSyncWithProviderResult( + GeofencingStatus RegisterRegionSyncWithServiceResult( int64 service_worker_registration_id, const std::string& id, const WebCircularGeofencingRegion& region, - GeofencingStatus provider_status, - int provider_result) { + GeofencingStatus service_status, + int64 geofencing_registration_id) { StatusCatcher result; + GeofencingRegistrationDelegate* delegate = 0; EXPECT_CALL( - *provider_, + *service_, RegisterRegion(WebCircularGeofencingRegionEq(region), testing::_)) - .WillOnce(CallRegisterCallback(provider_status, provider_result)); + .WillOnce(testing::DoAll(SaveDelegate(&delegate), + testing::Return(geofencing_registration_id))); manager_->RegisterRegion( - nullptr, /* browser_context */ service_worker_registration_id, - test_origin_, id, region, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); + CHECK(delegate); + delegate->RegistrationFinished(geofencing_registration_id, service_status); return result.Wait(); } GeofencingStatus UnregisterRegionSync(int64 service_worker_registration_id, const std::string& id, - bool should_call_provider, - int provider_id = 0) { + bool should_call_service, + int64 geofencing_registration_id = 0) { StatusCatcher result; - if (should_call_provider) { - EXPECT_CALL(*provider_, UnregisterRegion(provider_id)); + if (should_call_service) { + EXPECT_CALL(*service_, UnregisterRegion(geofencing_registration_id)); } manager_->UnregisterRegion( - nullptr, /* browser_context */ service_worker_registration_id, - test_origin_, id, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); return result.Wait(); @@ -155,10 +160,8 @@ class GeofencingManagerTest : public testing::Test { void VerifyRegions(int64 service_worker_registration_id, const RegionMap& expected_regions) { RegionMap regions; - EXPECT_EQ(GeofencingStatus::GEOFENCING_STATUS_OK, - manager_->GetRegisteredRegions(nullptr, /* browser_context */ - service_worker_registration_id, - test_origin_, + EXPECT_EQ(GEOFENCING_STATUS_OK, + manager_->GetRegisteredRegions(service_worker_registration_id, ®ions)); EXPECT_EQ(expected_regions.size(), regions.size()); for (RegionMap::const_iterator it = expected_regions.begin(); @@ -170,236 +173,242 @@ class GeofencingManagerTest : public testing::Test { } protected: - base::MessageLoop message_loop_; - TestBrowserThread io_thread_; - TestGeofencingProvider* provider_; - GeofencingManager* manager_; + TestBrowserThreadBundle threads_; + TestGeofencingService* service_; + scoped_refptr<GeofencingManager> manager_; WebCircularGeofencingRegion test_region_; RegionMap expected_regions_; - GURL test_origin_; }; -TEST_F(GeofencingManagerTest, RegisterRegion_NoProvider) { - EXPECT_EQ(GeofencingStatus:: - GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, +TEST_F(GeofencingManagerTest, RegisterRegion_NoService) { + EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, RegisterRegionSync( kTestServiceWorkerRegistrationId, kTestRegionId, test_region_)); } -TEST_F(GeofencingManagerTest, UnregisterRegion_NoProvider) { - EXPECT_EQ(GeofencingStatus:: - GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, +TEST_F(GeofencingManagerTest, UnregisterRegion_NoService) { + EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, UnregisterRegionSync( kTestServiceWorkerRegistrationId, kTestRegionId, false)); } -TEST_F(GeofencingManagerTest, GetRegisteredRegions_NoProvider) { +TEST_F(GeofencingManagerTest, GetRegisteredRegions_NoService) { RegionMap regions; - EXPECT_EQ(GeofencingStatus:: - GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, - manager_->GetRegisteredRegions(nullptr, /* browser_context */ - kTestServiceWorkerRegistrationId, - test_origin_, + EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, + manager_->GetRegisteredRegions(kTestServiceWorkerRegistrationId, ®ions)); EXPECT_TRUE(regions.empty()); } -TEST_F(GeofencingManagerTest, RegisterRegion_FailsInProvider) { - SetProviderForTests(); +TEST_F(GeofencingManagerTest, RegisterRegion_FailsInService) { + SetHasProviderForTests(); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_ERROR, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_ERROR, - -1)); + GEOFENCING_STATUS_ERROR, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_ERROR, + -1)); } -TEST_F(GeofencingManagerTest, RegisterRegion_SucceedsInProvider) { - SetProviderForTests(); +TEST_F(GeofencingManagerTest, RegisterRegion_SucceedsInService) { + SetHasProviderForTests(); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - 0)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId)); VerifyRegions(kTestServiceWorkerRegistrationId, expected_regions_); } TEST_F(GeofencingManagerTest, RegisterRegion_AlreadyRegistered) { - SetProviderForTests(); + SetHasProviderForTests(); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - 0)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId)); VerifyRegions(kTestServiceWorkerRegistrationId, expected_regions_); WebCircularGeofencingRegion region2; region2.latitude = 43.2; region2.longitude = 1.45; region2.radius = 8.5; - EXPECT_EQ(GeofencingStatus::GEOFENCING_STATUS_ERROR, + EXPECT_EQ(GEOFENCING_STATUS_ERROR, RegisterRegionSync( kTestServiceWorkerRegistrationId, kTestRegionId, region2)); VerifyRegions(kTestServiceWorkerRegistrationId, expected_regions_); } TEST_F(GeofencingManagerTest, UnregisterRegion_NotRegistered) { - SetProviderForTests(); - EXPECT_EQ(GeofencingStatus::GEOFENCING_STATUS_ERROR, + SetHasProviderForTests(); + EXPECT_EQ(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED, UnregisterRegionSync( kTestServiceWorkerRegistrationId, kTestRegionId, false)); } TEST_F(GeofencingManagerTest, UnregisterRegion_Success) { - SetProviderForTests(); - int provider_id = 123; - - EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - provider_id)); + SetHasProviderForTests(); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - UnregisterRegionSync( - kTestServiceWorkerRegistrationId, kTestRegionId, true, provider_id)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId)); + + EXPECT_EQ(GEOFENCING_STATUS_OK, + UnregisterRegionSync(kTestServiceWorkerRegistrationId, + kTestRegionId, + true, + kTestGeofencingRegistrationId)); VerifyRegions(kTestServiceWorkerRegistrationId, RegionMap()); } TEST_F(GeofencingManagerTest, GetRegisteredRegions_RegistrationInProgress) { - SetProviderForTests(); + SetHasProviderForTests(); StatusCatcher result; - GeofencingProvider::RegisterCallback callback; + GeofencingRegistrationDelegate* delegate = nullptr; EXPECT_CALL( - *provider_, + *service_, RegisterRegion(WebCircularGeofencingRegionEq(test_region_), testing::_)) - .WillOnce(SaveRegisterCallback(&callback)); + .WillOnce(testing::DoAll(SaveDelegate(&delegate), + testing::Return(kTestGeofencingRegistrationId))); manager_->RegisterRegion( - nullptr, /* browser_context */ kTestServiceWorkerRegistrationId, - test_origin_, kTestRegionId, test_region_, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); // At this point the manager should have tried registering the region with - // the provider, resulting in |callback| being set. Until the callback is + // the service, resulting in |delegate| being set. Until the callback is // called the registration is not complete though. - EXPECT_FALSE(callback.is_null()); + EXPECT_NE(delegate, nullptr); VerifyRegions(kTestServiceWorkerRegistrationId, RegionMap()); // Now call the callback, and verify the registration completed succesfully. - callback.Run(GEOFENCING_STATUS_OK, 123); - EXPECT_EQ(GeofencingStatus::GEOFENCING_STATUS_OK, result.Wait()); + delegate->RegistrationFinished(kTestGeofencingRegistrationId, + GEOFENCING_STATUS_OK); + EXPECT_EQ(GEOFENCING_STATUS_OK, result.Wait()); VerifyRegions(kTestServiceWorkerRegistrationId, expected_regions_); } TEST_F(GeofencingManagerTest, UnregisterRegion_RegistrationInProgress) { - SetProviderForTests(); + SetHasProviderForTests(); StatusCatcher result; - GeofencingProvider::RegisterCallback callback; + GeofencingRegistrationDelegate* delegate = nullptr; EXPECT_CALL( - *provider_, + *service_, RegisterRegion(WebCircularGeofencingRegionEq(test_region_), testing::_)) - .WillOnce(SaveRegisterCallback(&callback)); + .WillOnce(testing::DoAll(SaveDelegate(&delegate), + testing::Return(kTestGeofencingRegistrationId))); manager_->RegisterRegion( - nullptr, /* browser_context */ kTestServiceWorkerRegistrationId, - test_origin_, kTestRegionId, test_region_, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); // At this point the manager should have tried registering the region with - // the provider, resulting in |callback| being set. Until the callback is + // the service, resulting in |delegate| being set. Until the callback is // called the registration is not complete though. - EXPECT_FALSE(callback.is_null()); + EXPECT_NE(delegate, nullptr); - EXPECT_EQ(GeofencingStatus::GEOFENCING_STATUS_ERROR, + EXPECT_EQ(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED, UnregisterRegionSync( kTestServiceWorkerRegistrationId, kTestRegionId, false)); } TEST_F(GeofencingManagerTest, GetRegisteredRegions_NoRegions) { - SetProviderForTests(); + SetHasProviderForTests(); VerifyRegions(kTestServiceWorkerRegistrationId, RegionMap()); } TEST_F(GeofencingManagerTest, RegisterRegion_SeparateServiceWorkers) { - SetProviderForTests(); - int provider_id1 = 12; - int provider_id2 = 34; + SetHasProviderForTests(); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - provider_id1)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId)); VerifyRegions(kTestServiceWorkerRegistrationId, expected_regions_); VerifyRegions(kTestServiceWorkerRegistrationId2, RegionMap()); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId2, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - provider_id2)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId2, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId2)); VerifyRegions(kTestServiceWorkerRegistrationId, expected_regions_); VerifyRegions(kTestServiceWorkerRegistrationId2, expected_regions_); } TEST_F(GeofencingManagerTest, UnregisterRegion_SeparateServiceWorkers) { - SetProviderForTests(); - int provider_id1 = 12; - int provider_id2 = 34; + SetHasProviderForTests(); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - provider_id1)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId)); EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - RegisterRegionSyncWithProviderResult(kTestServiceWorkerRegistrationId2, - kTestRegionId, - test_region_, - GEOFENCING_STATUS_OK, - provider_id2)); - - EXPECT_EQ( - GeofencingStatus::GEOFENCING_STATUS_OK, - UnregisterRegionSync( - kTestServiceWorkerRegistrationId, kTestRegionId, true, provider_id1)); + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId2, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId2)); + + EXPECT_EQ(GEOFENCING_STATUS_OK, + UnregisterRegionSync(kTestServiceWorkerRegistrationId, + kTestRegionId, + true, + kTestGeofencingRegistrationId)); VerifyRegions(kTestServiceWorkerRegistrationId, RegionMap()); VerifyRegions(kTestServiceWorkerRegistrationId2, expected_regions_); - EXPECT_EQ(GeofencingStatus::GEOFENCING_STATUS_OK, + EXPECT_EQ(GEOFENCING_STATUS_OK, UnregisterRegionSync(kTestServiceWorkerRegistrationId2, kTestRegionId, true, - provider_id2)); + kTestGeofencingRegistrationId2)); VerifyRegions(kTestServiceWorkerRegistrationId, RegionMap()); VerifyRegions(kTestServiceWorkerRegistrationId2, RegionMap()); } +TEST_F(GeofencingManagerTest, ShutdownCleansRegistrations) { + SetHasProviderForTests(); + scoped_refptr<MessageLoopRunner> runner(new MessageLoopRunner()); + EXPECT_EQ( + GEOFENCING_STATUS_OK, + RegisterRegionSyncWithServiceResult(kTestServiceWorkerRegistrationId, + kTestRegionId, + test_region_, + GEOFENCING_STATUS_OK, + kTestGeofencingRegistrationId)); + + EXPECT_CALL(*service_, UnregisterRegion(kTestGeofencingRegistrationId)) + .WillOnce(QuitRunner(runner)); + manager_->Shutdown(); + runner->Run(); +} + } // namespace content diff --git a/content/browser/geofencing/geofencing_provider.h b/content/browser/geofencing/geofencing_provider.h index ab5faa0..dec96b5 100644 --- a/content/browser/geofencing/geofencing_provider.h +++ b/content/browser/geofencing/geofencing_provider.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_GEOFENCING_GEOFENCING_PROVIDER_H_ #define CONTENT_BROWSER_GEOFENCING_GEOFENCING_PROVIDER_H_ +#include "base/basictypes.h" #include "base/callback_forward.h" #include "content/common/geofencing_status.h" @@ -16,28 +17,26 @@ namespace content { class GeofencingProvider { public: - // Callback that gets called on completion of registering a new region. The - // status indicates success or failure, and in case of success, an id to use - // to later unregister the region is passed as |registration_id|. If - // registration failed, providers should set |registration_id| to -1. - typedef base::Callback<void(GeofencingStatus, int registration_id)> - RegisterCallback; + typedef base::Callback<void(GeofencingStatus)> StatusCallback; virtual ~GeofencingProvider() {} - // Called by |GeofencingManager| to register a new fence. GeofencingManager is + // Called by |GeofencingService| to register a new fence. GeofencingService is // responsible for handling things like duplicate regions, so platform // specific implementations shouldn't have to worry about things like that. - // Also GeofencingManager should be making sure the total number of geofences + // Also GeofencingService should be making sure the total number of geofences // that is registered with the platform specific provider does not exceed the // number of regions supported by the platform, although that isn't // implemented yet. - virtual void RegisterRegion(const blink::WebCircularGeofencingRegion& region, - const RegisterCallback& callback) = 0; + // Implementations of RegisterRegion must asynchronously call the |callback| + // to indicate success or failure. + virtual void RegisterRegion(int64 geofencing_registration_id, + const blink::WebCircularGeofencingRegion& region, + const StatusCallback& callback) = 0; - // Called by |GeofencingManager| to unregister an existing registration. Will + // Called by |GeofencingService| to unregister an existing registration. Will // only be called once for each registration. - virtual void UnregisterRegion(int registration_id) = 0; + virtual void UnregisterRegion(int64 geofencing_registration_id) = 0; }; } // namespace content diff --git a/content/browser/geofencing/geofencing_registration_delegate.h b/content/browser/geofencing/geofencing_registration_delegate.h new file mode 100644 index 0000000..ba7836c --- /dev/null +++ b/content/browser/geofencing/geofencing_registration_delegate.h @@ -0,0 +1,29 @@ +// Copyright 2014 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 CONTENT_BROWSER_GEOFENCING_GEOFENCING_REGISTRATION_DELEGATE_H_ +#define CONTENT_BROWSER_GEOFENCING_GEOFENCING_REGISTRATION_DELEGATE_H_ + +#include "base/basictypes.h" +#include "content/common/geofencing_status.h" + +namespace content { + +// |GeofencingService| has an instance of this class associated with each +// geofence registration, and uses it to inform about events related to the +// registration, such as the geofence finishing being registered. +// These methods will always be called on the IO thread. +// TODO(mek): Add methods for geofence enter/leave events. +class GeofencingRegistrationDelegate { + public: + virtual void RegistrationFinished(int64 geofencing_registration_id, + GeofencingStatus status) = 0; + + protected: + virtual ~GeofencingRegistrationDelegate() {} +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GEOFENCING_GEOFENCING_REGISTRATION_DELEGATE_H_ diff --git a/content/browser/geofencing/geofencing_service.cc b/content/browser/geofencing/geofencing_service.cc new file mode 100644 index 0000000..11df952 --- /dev/null +++ b/content/browser/geofencing/geofencing_service.cc @@ -0,0 +1,199 @@ +// Copyright 2014 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 "content/browser/geofencing/geofencing_service.h" + +#include "base/memory/singleton.h" +#include "base/message_loop/message_loop.h" +#include "content/browser/geofencing/geofencing_provider.h" +#include "content/browser/geofencing/geofencing_registration_delegate.h" +#include "content/public/browser/browser_thread.h" +#include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" + +namespace content { + +namespace { + +void RunSoon(const base::Closure& callback) { + if (!callback.is_null()) + base::MessageLoop::current()->PostTask(FROM_HERE, callback); +} + +} // namespace + +struct GeofencingServiceImpl::Registration { + Registration(); + Registration(const blink::WebCircularGeofencingRegion& region, + int64 geofencing_registration_id, + GeofencingRegistrationDelegate* delegate); + + blink::WebCircularGeofencingRegion region; + int64 geofencing_registration_id; + GeofencingRegistrationDelegate* delegate; + + enum RegistrationState { + // In progress of being registered with provider. + STATE_REGISTERING, + // Currently registered with provider. + STATE_REGISTERED, + // In progress of being registered with provider, but should be unregistered + // and deleted. + STATE_SHOULD_UNREGISTER_AND_DELETE, + // Not currently registered with provider, but still an active registration. + STATE_UNREGISTERED + }; + RegistrationState state; +}; + +GeofencingServiceImpl::Registration::Registration() + : geofencing_registration_id(-1), + delegate(nullptr), + state(STATE_UNREGISTERED) { +} + +GeofencingServiceImpl::Registration::Registration( + const blink::WebCircularGeofencingRegion& region, + int64 geofencing_registration_id, + GeofencingRegistrationDelegate* delegate) + : region(region), + geofencing_registration_id(geofencing_registration_id), + delegate(delegate), + state(STATE_REGISTERING) { +} + +GeofencingServiceImpl::GeofencingServiceImpl() : next_registration_id_(0) { +} + +GeofencingServiceImpl::~GeofencingServiceImpl() { +} + +GeofencingServiceImpl* GeofencingServiceImpl::GetInstance() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return Singleton<GeofencingServiceImpl>::get(); +} + +bool GeofencingServiceImpl::IsServiceAvailable() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return EnsureProvider(); +} + +int64 GeofencingServiceImpl::RegisterRegion( + const blink::WebCircularGeofencingRegion& region, + GeofencingRegistrationDelegate* delegate) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + int64 geofencing_registration_id = GetNextId(); + registrations_[geofencing_registration_id] = + Registration(region, geofencing_registration_id, delegate); + + if (!EnsureProvider()) { + RunSoon( + base::Bind(&GeofencingServiceImpl::NotifyRegistrationFinished, + base::Unretained(this), + geofencing_registration_id, + GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE)); + return geofencing_registration_id; + } + + provider_->RegisterRegion( + geofencing_registration_id, + region, + base::Bind(&GeofencingServiceImpl::NotifyRegistrationFinished, + base::Unretained(this), + geofencing_registration_id)); + return geofencing_registration_id; +} + +void GeofencingServiceImpl::UnregisterRegion(int64 geofencing_registration_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + RegistrationsMap::iterator registration_iterator = + registrations_.find(geofencing_registration_id); + DCHECK(registration_iterator != registrations_.end()); + + if (!EnsureProvider()) + return; + + switch (registration_iterator->second.state) { + case Registration::STATE_REGISTERED: + provider_->UnregisterRegion(geofencing_registration_id); + // fallthru + case Registration::STATE_UNREGISTERED: + registrations_.erase(registration_iterator); + break; + case Registration::STATE_REGISTERING: + // Update state, NotifyRegistrationFinished will take care of actually + // unregistering. + registration_iterator->second.state = + Registration::STATE_SHOULD_UNREGISTER_AND_DELETE; + break; + case Registration::STATE_SHOULD_UNREGISTER_AND_DELETE: + // Should not happen. + NOTREACHED(); + break; + } +} + +void GeofencingServiceImpl::SetProviderForTesting( + scoped_ptr<GeofencingProvider> provider) { + DCHECK(!provider_.get()); + provider_ = provider.Pass(); +} + +int GeofencingServiceImpl::RegistrationCountForTesting() { + return registrations_.size(); +} + +bool GeofencingServiceImpl::EnsureProvider() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!provider_) { + // TODO(mek): Create platform specific provider. + return false; + } + return true; +} + +int64 GeofencingServiceImpl::GetNextId() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + return next_registration_id_++; +} + +void GeofencingServiceImpl::NotifyRegistrationFinished( + int64 geofencing_registration_id, + GeofencingStatus status) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + RegistrationsMap::iterator registration_iterator = + registrations_.find(geofencing_registration_id); + DCHECK(registration_iterator != registrations_.end()); + DCHECK(registration_iterator->second.state == + Registration::STATE_REGISTERING || + registration_iterator->second.state == + Registration::STATE_SHOULD_UNREGISTER_AND_DELETE); + + if (registration_iterator->second.state == + Registration::STATE_SHOULD_UNREGISTER_AND_DELETE) { + // Don't call delegate, but unregister with provider if registration was + // succesfull. + if (status == GEOFENCING_STATUS_OK) { + provider_->UnregisterRegion(geofencing_registration_id); + } + registrations_.erase(registration_iterator); + return; + } + + // Normal case, mark as registered and call delegate. + registration_iterator->second.state = Registration::STATE_REGISTERED; + registration_iterator->second.delegate->RegistrationFinished( + geofencing_registration_id, status); + + if (status != GEOFENCING_STATUS_OK) { + // Registration failed, remove from our book-keeping. + registrations_.erase(registration_iterator); + } +} + +} // namespace content diff --git a/content/browser/geofencing/geofencing_service.h b/content/browser/geofencing/geofencing_service.h new file mode 100644 index 0000000..dc3d832 --- /dev/null +++ b/content/browser/geofencing/geofencing_service.h @@ -0,0 +1,105 @@ +// Copyright 2014 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 CONTENT_BROWSER_GEOFENCING_GEOFENCING_SERVICE_H_ +#define CONTENT_BROWSER_GEOFENCING_GEOFENCING_SERVICE_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/common/geofencing_status.h" + +template <typename T> +struct DefaultSingletonTraits; + +namespace blink { +struct WebCircularGeofencingRegion; +}; + +namespace content { + +class GeofencingProvider; +class GeofencingRegistrationDelegate; + +// This interface exists primarily to facilitate testing of classes that depend +// on GeofencingService. It defines the interface exposed by +// |GeofencingService|. +class GeofencingService { + public: + virtual ~GeofencingService() {} + + // Returns if a geofencing service is available. + virtual bool IsServiceAvailable() = 0; + + // Register a region. This returns a unique registration ID, and + // asynchronously calls the |delegate|s RegistrationFinished method to + // inform the delegate of the result of the attempt to register the region. + // This does not transfer ownership of the |delegate|. Callers have to ensure + // that the delegate remains valid as long as the region is registered. + virtual int64 RegisterRegion(const blink::WebCircularGeofencingRegion& region, + GeofencingRegistrationDelegate* delegate) = 0; + + // Unregister a region. This is assumed to always succeed. It is safe to call + // this even if a registration is still in progress. + virtual void UnregisterRegion(int64 geofencing_registration_id) = 0; +}; + +// This class combines all the geofence registrations from the various per +// storage partition |GeofencingManager| instances, and registers a subset +// of those fences with an underlying platform specific |GeofencingProvider|. +// TODO(mek): Limit the number of geofences that are registered with the +// underlying GeofencingProvider. +class CONTENT_EXPORT GeofencingServiceImpl + : NON_EXPORTED_BASE(public GeofencingService) { + public: + // Gets a pointer to the singleton instance of the geofencing service. This + // must only be called on the IO thread so that the GeofencingService is + // always instantiated on the same thread. Ownership is NOT returned. + static GeofencingServiceImpl* GetInstance(); + + // GeofencingServiceInterface implementation. + bool IsServiceAvailable() override; + int64 RegisterRegion( + const blink::WebCircularGeofencingRegion& region, + GeofencingRegistrationDelegate* delegate) override; + void UnregisterRegion(int64 geofencing_registration_id) override; + + protected: + friend class GeofencingServiceTest; + friend struct DefaultSingletonTraits<GeofencingServiceImpl>; + GeofencingServiceImpl(); + ~GeofencingServiceImpl() override; + + void SetProviderForTesting(scoped_ptr<GeofencingProvider> provider); + int RegistrationCountForTesting(); + + private: + struct Registration; + typedef std::map<int64, Registration> RegistrationsMap; + + // This method checks if a |GeofencingProvider| exists, creates a new one if + // not, and finally returns false if it can't create a provider for the + // current platform. + bool EnsureProvider(); + + // Returns a new unique ID to use for the next geofence registration. + int64 GetNextId(); + + // Notifies the correct delegate that registration has completed for a + // specific geofence registration. + void NotifyRegistrationFinished(int64 geofencing_registration_id, + GeofencingStatus status); + + int64 next_registration_id_; + RegistrationsMap registrations_; + scoped_ptr<GeofencingProvider> provider_; + + DISALLOW_COPY_AND_ASSIGN(GeofencingServiceImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GEOFENCING_GEOFENCING_SERVICE_H_ diff --git a/content/browser/geofencing/geofencing_service_unittest.cc b/content/browser/geofencing/geofencing_service_unittest.cc new file mode 100644 index 0000000..329fcaf --- /dev/null +++ b/content/browser/geofencing/geofencing_service_unittest.cc @@ -0,0 +1,191 @@ +// Copyright 2014 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 "content/browser/geofencing/geofencing_provider.h" +#include "content/browser/geofencing/geofencing_registration_delegate.h" +#include "content/browser/geofencing/geofencing_service.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" + +using blink::WebCircularGeofencingRegion; + +namespace { + +bool RegionsMatch(const WebCircularGeofencingRegion& expected, + const WebCircularGeofencingRegion& arg) { + return testing::Matches(expected.latitude)(arg.latitude) && + testing::Matches(expected.longitude)(arg.longitude) && + testing::Matches(expected.radius)(arg.radius); +} + +} // namespace + +namespace content { + +class MockGeofencingRegistrationDelegate + : public GeofencingRegistrationDelegate { + public: + MOCK_METHOD2(RegistrationFinished, + void(int64 geofencing_registration_id, GeofencingStatus status)); +}; + +class MockGeofencingProvider : public GeofencingProvider { + public: + MOCK_METHOD3(RegisterRegion, + void(int64 geofencing_registration_id, + const blink::WebCircularGeofencingRegion& region, + const StatusCallback& callback)); + MOCK_METHOD1(UnregisterRegion, void(int64 geofencing_registration_id)); +}; + +ACTION_P(QuitRunner, runner) { + runner->Quit(); +} + +ACTION_P(SaveRegistrationId, geofencing_registration_id) { + *geofencing_registration_id = arg0; +} + +ACTION_P(SaveStatusCallback, callback) { + *callback = arg2; +} + +MATCHER_P(WebCircularGeofencingRegionEq, expected, "") { + return RegionsMatch(expected, arg); +} + +class GeofencingServiceTest : public testing::Test { + public: + GeofencingServiceTest() : service_(nullptr) { + test_region_.latitude = 37.421999; + test_region_.longitude = -122.084015; + test_region_.radius = 100; + } + + virtual void SetUp() { service_ = new GeofencingServiceImpl(); } + + virtual void TearDown() { delete service_; } + + void SetProviderForTests() { + provider_ = new MockGeofencingProvider(); + service_->SetProviderForTesting(make_scoped_ptr(provider_)); + } + + int RegistrationCount() { return service_->RegistrationCountForTesting(); } + + int64 RegisterRegionSync(const WebCircularGeofencingRegion& region, + GeofencingStatus provider_status) { + scoped_refptr<MessageLoopRunner> runner(new MessageLoopRunner()); + + // The registration ID that is passed to the provider. + int64 provider_registration_id = -1; + // The callback that is passed to the provider. + GeofencingProvider::StatusCallback callback; + + EXPECT_CALL( + *provider_, + RegisterRegion( + testing::_, WebCircularGeofencingRegionEq(region), testing::_)) + .WillOnce(testing::DoAll(SaveRegistrationId(&provider_registration_id), + SaveStatusCallback(&callback))); + + int64 geofencing_registration_id = + service_->RegisterRegion(region, &delegate_); + + // Service should have synchronously called the provider. + CHECK(!callback.is_null()); + CHECK(provider_registration_id == geofencing_registration_id); + + // Finish up registration by calling the callback and waiting for the + // delegate to be called. + EXPECT_CALL( + delegate_, + RegistrationFinished(geofencing_registration_id, provider_status)) + .WillOnce(QuitRunner(runner)); + callback.Run(provider_status); + runner->Run(); + return geofencing_registration_id; + } + + protected: + TestBrowserThreadBundle threads_; + GeofencingServiceImpl* service_; + MockGeofencingProvider* provider_; + MockGeofencingRegistrationDelegate delegate_; + + WebCircularGeofencingRegion test_region_; +}; + +TEST_F(GeofencingServiceTest, RegisterRegion_NoProvider) { + scoped_refptr<MessageLoopRunner> runner(new MessageLoopRunner()); + int64 geofencing_registration_id = + service_->RegisterRegion(test_region_, &delegate_); + EXPECT_CALL(delegate_, + RegistrationFinished( + geofencing_registration_id, + GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE)) + .WillOnce(QuitRunner(runner)); + runner->Run(); + EXPECT_EQ(0, RegistrationCount()); +} + +TEST_F(GeofencingServiceTest, RegisterRegion_FailsInProvider) { + SetProviderForTests(); + RegisterRegionSync(test_region_, GEOFENCING_STATUS_ERROR); + EXPECT_EQ(0, RegistrationCount()); +} + +TEST_F(GeofencingServiceTest, RegisterRegion_SucceedsInProvider) { + SetProviderForTests(); + RegisterRegionSync(test_region_, GEOFENCING_STATUS_OK); + EXPECT_EQ(1, RegistrationCount()); +} + +TEST_F(GeofencingServiceTest, UnregisterRegion_AfterRegistration) { + SetProviderForTests(); + int geofencing_registration_id = + RegisterRegionSync(test_region_, GEOFENCING_STATUS_OK); + EXPECT_EQ(1, RegistrationCount()); + + EXPECT_CALL(*provider_, UnregisterRegion(geofencing_registration_id)); + service_->UnregisterRegion(geofencing_registration_id); + EXPECT_EQ(0, RegistrationCount()); +} + +TEST_F(GeofencingServiceTest, UnregisterRegion_DuringSuccesfullRegistration) { + SetProviderForTests(); + scoped_refptr<MessageLoopRunner> runner(new MessageLoopRunner()); + + // The callback that is passed to the provider. + GeofencingProvider::StatusCallback callback; + + EXPECT_CALL( + *provider_, + RegisterRegion( + testing::_, WebCircularGeofencingRegionEq(test_region_), testing::_)) + .WillOnce(SaveStatusCallback(&callback)); + + int64 geofencing_registration_id = + service_->RegisterRegion(test_region_, &delegate_); + + // Service should have synchronously called the provider. + CHECK(!callback.is_null()); + + // Call unregister before registration is finished. + service_->UnregisterRegion(geofencing_registration_id); + + // Finish up registration by calling the callback and waiting for the + // provider to be called. The delegate should not be called in this case. + EXPECT_CALL(delegate_, RegistrationFinished(testing::_, testing::_)).Times(0); + EXPECT_CALL(*provider_, UnregisterRegion(geofencing_registration_id)) + .WillOnce(QuitRunner(runner)); + callback.Run(GEOFENCING_STATUS_OK); + runner->Run(); + EXPECT_EQ(0, RegistrationCount()); +} + +} // namespace content |