1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/geofencing/geofencing_manager.h"
9 #include "base/callback.h"
10 #include "content/browser/geofencing/geofencing_service.h"
11 #include "content/browser/geofencing/mock_geofencing_service.h"
12 #include "content/browser/service_worker/service_worker_context_wrapper.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h"
17 #include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
22 struct GeofencingManager::Registration
{
23 Registration(int64 service_worker_registration_id
,
24 const GURL
& service_worker_origin
,
25 const std::string
& region_id
,
26 const blink::WebCircularGeofencingRegion
& region
,
27 const StatusCallback
& callback
,
28 int64 geofencing_registration_id
);
29 int64 service_worker_registration_id
;
30 GURL service_worker_origin
;
31 std::string region_id
;
32 blink::WebCircularGeofencingRegion region
;
34 // Registration ID as returned by the |GeofencingService|.
35 int64 geofencing_registration_id
;
37 // Callback to call when registration is completed. This field is reset when
38 // registration is complete.
39 StatusCallback registration_callback
;
41 // Returns true if registration has been completed, and thus should be
42 // included in calls to GetRegisteredRegions.
43 bool is_active() const { return registration_callback
.is_null(); }
46 GeofencingManager::Registration::Registration(
47 int64 service_worker_registration_id
,
48 const GURL
& service_worker_origin
,
49 const std::string
& region_id
,
50 const blink::WebCircularGeofencingRegion
& region
,
51 const GeofencingManager::StatusCallback
& callback
,
52 int64 geofencing_registration_id
)
53 : service_worker_registration_id(service_worker_registration_id
),
54 service_worker_origin(service_worker_origin
),
57 geofencing_registration_id(geofencing_registration_id
),
58 registration_callback(callback
) {
61 GeofencingManager::GeofencingManager(
62 const scoped_refptr
<ServiceWorkerContextWrapper
>& service_worker_context
)
63 : service_(nullptr), service_worker_context_(service_worker_context
) {
64 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
67 GeofencingManager::~GeofencingManager() {
70 void GeofencingManager::Init() {
71 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
72 BrowserThread::PostTask(BrowserThread::IO
,
74 base::Bind(&GeofencingManager::InitOnIO
, this));
77 void GeofencingManager::Shutdown() {
78 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
79 BrowserThread::PostTask(BrowserThread::IO
,
81 base::Bind(&GeofencingManager::ShutdownOnIO
, this));
84 void GeofencingManager::InitOnIO() {
85 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
86 service_worker_context_
->AddObserver(this);
88 service_
= GeofencingServiceImpl::GetInstance();
91 void GeofencingManager::ShutdownOnIO() {
92 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
93 service_worker_context_
->RemoveObserver(this);
94 // Clean up all registrations with the |GeofencingService|.
95 // TODO(mek): This will need to change to support geofence registrations that
96 // outlive the browser, although removing the references to this
97 // |GeofencingManager| from the |GeofencingService| will still be needed.
98 for (const auto& registration
: registrations_by_id_
)
99 service_
->UnregisterRegion(registration
.first
);
102 void GeofencingManager::RegisterRegion(
103 int64 service_worker_registration_id
,
104 const std::string
& region_id
,
105 const blink::WebCircularGeofencingRegion
& region
,
106 const StatusCallback
& callback
) {
107 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
109 // TODO(mek): Validate region_id and region.
111 // Look up service worker.
112 ServiceWorkerRegistration
* service_worker_registration
=
113 service_worker_context_
->context()->GetLiveRegistration(
114 service_worker_registration_id
);
115 if (!service_worker_registration
) {
116 callback
.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER
);
120 GURL service_worker_origin
=
121 service_worker_registration
->pattern().GetOrigin();
123 if (!service_
->IsServiceAvailable()) {
124 callback
.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE
);
128 if (FindRegistration(service_worker_registration_id
, region_id
)) {
129 // Already registered, return an error.
130 // TODO(mek): Use a more specific error code.
131 callback
.Run(GEOFENCING_STATUS_ERROR
);
135 AddRegistration(service_worker_registration_id
, service_worker_origin
,
136 region_id
, region
, callback
,
137 service_
->RegisterRegion(region
, this));
140 void GeofencingManager::UnregisterRegion(int64 service_worker_registration_id
,
141 const std::string
& region_id
,
142 const StatusCallback
& callback
) {
143 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
145 // TODO(mek): Validate region_id.
147 // Look up service worker.
148 ServiceWorkerRegistration
* service_worker_registration
=
149 service_worker_context_
->context()->GetLiveRegistration(
150 service_worker_registration_id
);
151 if (!service_worker_registration
) {
152 callback
.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER
);
156 if (!service_
->IsServiceAvailable()) {
157 callback
.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE
);
161 Registration
* registration
=
162 FindRegistration(service_worker_registration_id
, region_id
);
164 // Not registered, return an error.
165 callback
.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED
);
169 if (!registration
->is_active()) {
170 // Started registration, but not completed yet, error.
171 callback
.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED
);
175 service_
->UnregisterRegion(registration
->geofencing_registration_id
);
176 ClearRegistration(registration
);
177 callback
.Run(GEOFENCING_STATUS_OK
);
180 GeofencingStatus
GeofencingManager::GetRegisteredRegions(
181 int64 service_worker_registration_id
,
182 std::map
<std::string
, blink::WebCircularGeofencingRegion
>* result
) {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
186 // Look up service worker.
187 ServiceWorkerRegistration
* service_worker_registration
=
188 service_worker_context_
->context()->GetLiveRegistration(
189 service_worker_registration_id
);
190 if (!service_worker_registration
) {
191 return GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER
;
194 if (!service_
->IsServiceAvailable()) {
195 return GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE
;
198 // Populate result, filtering out inactive registrations.
200 ServiceWorkerRegistrationsMap::iterator registrations
=
201 registrations_
.find(service_worker_registration_id
);
202 if (registrations
== registrations_
.end())
203 return GEOFENCING_STATUS_OK
;
204 for (const auto& registration
: registrations
->second
) {
205 if (registration
.second
.is_active())
206 (*result
)[registration
.first
] = registration
.second
.region
;
208 return GEOFENCING_STATUS_OK
;
211 void GeofencingManager::SetMockProvider(GeofencingMockState mock_state
) {
212 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
214 // TODO(mek): It would be nice if enabling the mock geofencing service
215 // wouldn't completely delete all existing registrations but instead just
216 // somehow keep them around but inactive.
217 // For now mocking is only used in layout tests, so just clearing all
218 // registrations works good enough.
219 for (const auto& registration
: registrations_by_id_
)
220 service_
->UnregisterRegion(registration
.first
);
221 registrations_by_id_
.clear();
222 registrations_
.clear();
224 // Then set or reset the mock service for the service worker.
225 if (mock_state
== GeofencingMockState::NONE
) {
226 service_
= GeofencingServiceImpl::GetInstance();
227 mock_service_
.reset();
229 mock_service_
.reset(new MockGeofencingService(
230 mock_state
!= GeofencingMockState::SERVICE_UNAVAILABLE
));
231 service_
= mock_service_
.get();
235 void GeofencingManager::SetMockPosition(double latitude
, double longitude
) {
236 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
239 mock_service_
->SetMockPosition(latitude
, longitude
);
242 void GeofencingManager::OnRegistrationDeleted(
243 int64 service_worker_registration_id
,
244 const GURL
& pattern
) {
245 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
246 CleanUpForServiceWorker(service_worker_registration_id
);
249 void GeofencingManager::RegistrationFinished(int64 geofencing_registration_id
,
250 GeofencingStatus status
) {
251 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
252 Registration
* registration
= FindRegistrationById(geofencing_registration_id
);
253 DCHECK(registration
);
254 DCHECK(!registration
->is_active());
255 registration
->registration_callback
.Run(status
);
256 registration
->registration_callback
.Reset();
258 // If the registration wasn't succesful, remove it from our storage.
259 if (status
!= GEOFENCING_STATUS_OK
)
260 ClearRegistration(registration
);
263 void GeofencingManager::RegionEntered(int64 geofencing_registration_id
) {
264 DispatchGeofencingEvent(blink::WebGeofencingEventTypeEnter
,
265 geofencing_registration_id
);
268 void GeofencingManager::RegionExited(int64 geofencing_registration_id
) {
269 DispatchGeofencingEvent(blink::WebGeofencingEventTypeLeave
,
270 geofencing_registration_id
);
273 GeofencingManager::Registration
* GeofencingManager::FindRegistration(
274 int64 service_worker_registration_id
,
275 const std::string
& region_id
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
277 ServiceWorkerRegistrationsMap::iterator registrations_iterator
=
278 registrations_
.find(service_worker_registration_id
);
279 if (registrations_iterator
== registrations_
.end())
281 RegionIdRegistrationMap::iterator registration
=
282 registrations_iterator
->second
.find(region_id
);
283 if (registration
== registrations_iterator
->second
.end())
285 return ®istration
->second
;
288 GeofencingManager::Registration
* GeofencingManager::FindRegistrationById(
289 int64 geofencing_registration_id
) {
290 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
291 RegistrationIdRegistrationMap::iterator registration_iterator
=
292 registrations_by_id_
.find(geofencing_registration_id
);
293 if (registration_iterator
== registrations_by_id_
.end())
295 return ®istration_iterator
->second
->second
;
298 GeofencingManager::Registration
& GeofencingManager::AddRegistration(
299 int64 service_worker_registration_id
,
300 const GURL
& service_worker_origin
,
301 const std::string
& region_id
,
302 const blink::WebCircularGeofencingRegion
& region
,
303 const StatusCallback
& callback
,
304 int64 geofencing_registration_id
) {
305 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
306 DCHECK(!FindRegistration(service_worker_registration_id
, region_id
));
307 RegionIdRegistrationMap::iterator registration
=
308 registrations_
[service_worker_registration_id
]
309 .insert(std::make_pair(region_id
,
310 Registration(service_worker_registration_id
,
311 service_worker_origin
,
315 geofencing_registration_id
)))
317 registrations_by_id_
[geofencing_registration_id
] = registration
;
318 return registration
->second
;
321 void GeofencingManager::ClearRegistration(Registration
* registration
) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
323 registrations_by_id_
.erase(registration
->geofencing_registration_id
);
324 ServiceWorkerRegistrationsMap::iterator registrations_iterator
=
325 registrations_
.find(registration
->service_worker_registration_id
);
326 DCHECK(registrations_iterator
!= registrations_
.end());
327 registrations_iterator
->second
.erase(registration
->region_id
);
328 if (registrations_iterator
->second
.empty())
329 registrations_
.erase(registrations_iterator
);
332 void GeofencingManager::CleanUpForServiceWorker(
333 int64 service_worker_registration_id
) {
334 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
335 ServiceWorkerRegistrationsMap::iterator registrations_iterator
=
336 registrations_
.find(service_worker_registration_id
);
337 if (registrations_iterator
== registrations_
.end())
340 for (const auto& registration
: registrations_iterator
->second
) {
341 int geofencing_registration_id
=
342 registration
.second
.geofencing_registration_id
;
343 service_
->UnregisterRegion(geofencing_registration_id
);
344 registrations_by_id_
.erase(geofencing_registration_id
);
346 registrations_
.erase(service_worker_registration_id
);
349 void GeofencingManager::DispatchGeofencingEvent(
350 blink::WebGeofencingEventType event_type
,
351 int64 geofencing_registration_id
) {
352 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
353 Registration
* registration
= FindRegistrationById(geofencing_registration_id
);
355 registration
->service_worker_registration_id
==
356 kInvalidServiceWorkerRegistrationId
) {
357 // TODO(mek): Log/track these failures.
361 service_worker_context_
->context()->storage()->FindRegistrationForId(
362 registration
->service_worker_registration_id
,
363 registration
->service_worker_origin
,
364 base::Bind(&GeofencingManager::DeliverGeofencingEvent
,
367 geofencing_registration_id
));
370 void GeofencingManager::DeliverGeofencingEvent(
371 blink::WebGeofencingEventType event_type
,
372 int64 geofencing_registration_id
,
373 ServiceWorkerStatusCode service_worker_status
,
374 const scoped_refptr
<ServiceWorkerRegistration
>&
375 service_worker_registration
) {
376 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
377 Registration
* registration
= FindRegistrationById(geofencing_registration_id
);
379 // Geofence got unregistered in the meantime, no longer need to deliver
384 if (service_worker_status
!= SERVICE_WORKER_OK
) {
385 // TODO(mek): SW no longer exists, somehow handle this.
389 ServiceWorkerVersion
* active_version
=
390 service_worker_registration
->active_version();
391 if (!active_version
) {
392 // TODO(mek): No active version, potentially because one is still being
393 // installed. Handle this somehow.
397 // Hold on to the service worker registration in the callback to keep it alive
398 // until the callback dies. Otherwise the registration could be released when
399 // this method returns - before the event is delivered to the service worker.
400 base::Callback
<void(ServiceWorkerStatusCode
)> dispatch_event_callback
=
401 base::Bind(&GeofencingManager::DeliverGeofencingEventEnd
,
403 service_worker_registration
);
404 active_version
->DispatchGeofencingEvent(dispatch_event_callback
,
406 registration
->region_id
,
407 registration
->region
);
410 void GeofencingManager::DeliverGeofencingEventEnd(
411 const scoped_refptr
<ServiceWorkerRegistration
>& service_worker_registration
,
412 ServiceWorkerStatusCode service_worker_status
) {
413 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
414 // TODO(mek): log/check result.
417 } // namespace content