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/service_worker/service_worker_context_wrapper.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/storage_partition.h"
15 #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h"
16 #include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
21 struct GeofencingManager::Registration
{
22 Registration(int64 service_worker_registration_id
,
23 const GURL
& service_worker_origin
,
24 const std::string
& region_id
,
25 const blink::WebCircularGeofencingRegion
& region
,
26 const StatusCallback
& callback
,
27 int64 geofencing_registration_id
);
28 int64 service_worker_registration_id
;
29 GURL service_worker_origin
;
30 std::string region_id
;
31 blink::WebCircularGeofencingRegion region
;
33 // Registration ID as returned by the |GeofencingService|.
34 int64 geofencing_registration_id
;
36 // Callback to call when registration is completed. This field is reset when
37 // registration is complete.
38 StatusCallback registration_callback
;
40 // Returns true if registration has been completed, and thus should be
41 // included in calls to GetRegisteredRegions.
42 bool is_active() const { return registration_callback
.is_null(); }
45 GeofencingManager::Registration::Registration(
46 int64 service_worker_registration_id
,
47 const GURL
& service_worker_origin
,
48 const std::string
& region_id
,
49 const blink::WebCircularGeofencingRegion
& region
,
50 const GeofencingManager::StatusCallback
& callback
,
51 int64 geofencing_registration_id
)
52 : service_worker_registration_id(service_worker_registration_id
),
53 service_worker_origin(service_worker_origin
),
56 geofencing_registration_id(geofencing_registration_id
),
57 registration_callback(callback
) {
60 GeofencingManager::GeofencingManager(
61 const scoped_refptr
<ServiceWorkerContextWrapper
>& service_worker_context
)
62 : service_(nullptr), service_worker_context_(service_worker_context
) {
63 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
66 GeofencingManager::~GeofencingManager() {
69 void GeofencingManager::Init() {
70 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
71 BrowserThread::PostTask(BrowserThread::IO
,
73 base::Bind(&GeofencingManager::InitOnIO
, this));
76 void GeofencingManager::Shutdown() {
77 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
78 BrowserThread::PostTask(BrowserThread::IO
,
80 base::Bind(&GeofencingManager::ShutdownOnIO
, this));
83 void GeofencingManager::InitOnIO() {
84 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
85 service_
= GeofencingServiceImpl::GetInstance();
88 void GeofencingManager::ShutdownOnIO() {
89 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
90 // Clean up all registrations with the |GeofencingService|.
91 // TODO(mek): This will need to change to support geofence registrations that
92 // outlive the browser, although removing the references to this
93 // |GeofencingManager| from the |GeofencingService| will still be needed.
94 for (const auto& registration
: registrations_by_id_
) {
95 service_
->UnregisterRegion(registration
.first
);
99 void GeofencingManager::RegisterRegion(
100 int64 service_worker_registration_id
,
101 const std::string
& region_id
,
102 const blink::WebCircularGeofencingRegion
& region
,
103 const StatusCallback
& callback
) {
104 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
106 // TODO(mek): Validate region_id and region.
108 // Look up service worker. In unit tests |service_worker_context_| might not
110 GURL service_worker_origin
;
111 if (service_worker_context_
.get()) {
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 service_worker_origin
= 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
,
136 service_worker_origin
,
140 service_
->RegisterRegion(region
, this));
143 void GeofencingManager::UnregisterRegion(int64 service_worker_registration_id
,
144 const std::string
& region_id
,
145 const StatusCallback
& callback
) {
146 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
148 // TODO(mek): Validate region_id.
150 // Look up service worker. In unit tests |service_worker_context_| might not
152 if (service_worker_context_
.get()) {
153 ServiceWorkerRegistration
* service_worker_registration
=
154 service_worker_context_
->context()->GetLiveRegistration(
155 service_worker_registration_id
);
156 if (!service_worker_registration
) {
157 callback
.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER
);
162 if (!service_
->IsServiceAvailable()) {
163 callback
.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE
);
167 Registration
* registration
=
168 FindRegistration(service_worker_registration_id
, region_id
);
170 // Not registered, return an error.
171 callback
.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED
);
175 if (!registration
->is_active()) {
176 // Started registration, but not completed yet, error.
177 callback
.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED
);
181 service_
->UnregisterRegion(registration
->geofencing_registration_id
);
182 ClearRegistration(registration
);
183 callback
.Run(GEOFENCING_STATUS_OK
);
186 GeofencingStatus
GeofencingManager::GetRegisteredRegions(
187 int64 service_worker_registration_id
,
188 std::map
<std::string
, blink::WebCircularGeofencingRegion
>* result
) {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
192 // Look up service worker. In unit tests |service_worker_context_| might not
194 if (service_worker_context_
.get()) {
195 ServiceWorkerRegistration
* service_worker_registration
=
196 service_worker_context_
->context()->GetLiveRegistration(
197 service_worker_registration_id
);
198 if (!service_worker_registration
) {
199 return GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER
;
203 if (!service_
->IsServiceAvailable()) {
204 return GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE
;
207 // Populate result, filtering out inactive registrations.
209 ServiceWorkerRegistrationsMap::iterator registrations
=
210 registrations_
.find(service_worker_registration_id
);
211 if (registrations
== registrations_
.end())
212 return GEOFENCING_STATUS_OK
;
213 for (const auto& registration
: registrations
->second
) {
214 if (registration
.second
.is_active())
215 (*result
)[registration
.first
] = registration
.second
.region
;
217 return GEOFENCING_STATUS_OK
;
220 void GeofencingManager::RegistrationFinished(int64 geofencing_registration_id
,
221 GeofencingStatus status
) {
222 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
223 Registration
* registration
= FindRegistrationById(geofencing_registration_id
);
224 DCHECK(registration
);
225 DCHECK(!registration
->is_active());
226 registration
->registration_callback
.Run(status
);
227 registration
->registration_callback
.Reset();
229 // If the registration wasn't succesful, remove it from our storage.
230 if (status
!= GEOFENCING_STATUS_OK
)
231 ClearRegistration(registration
);
234 void GeofencingManager::RegionEntered(int64 geofencing_registration_id
) {
235 DispatchGeofencingEvent(blink::WebGeofencingEventTypeEnter
,
236 geofencing_registration_id
);
239 void GeofencingManager::RegionExited(int64 geofencing_registration_id
) {
240 DispatchGeofencingEvent(blink::WebGeofencingEventTypeLeave
,
241 geofencing_registration_id
);
244 GeofencingManager::Registration
* GeofencingManager::FindRegistration(
245 int64 service_worker_registration_id
,
246 const std::string
& region_id
) {
247 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
248 ServiceWorkerRegistrationsMap::iterator registrations_iterator
=
249 registrations_
.find(service_worker_registration_id
);
250 if (registrations_iterator
== registrations_
.end())
252 RegionIdRegistrationMap::iterator registration
=
253 registrations_iterator
->second
.find(region_id
);
254 if (registration
== registrations_iterator
->second
.end())
256 return ®istration
->second
;
259 GeofencingManager::Registration
* GeofencingManager::FindRegistrationById(
260 int64 geofencing_registration_id
) {
261 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
262 RegistrationIdRegistrationMap::iterator registration_iterator
=
263 registrations_by_id_
.find(geofencing_registration_id
);
264 if (registration_iterator
== registrations_by_id_
.end())
266 return ®istration_iterator
->second
->second
;
269 GeofencingManager::Registration
& GeofencingManager::AddRegistration(
270 int64 service_worker_registration_id
,
271 const GURL
& service_worker_origin
,
272 const std::string
& region_id
,
273 const blink::WebCircularGeofencingRegion
& region
,
274 const StatusCallback
& callback
,
275 int64 geofencing_registration_id
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
277 DCHECK(!FindRegistration(service_worker_registration_id
, region_id
));
278 RegionIdRegistrationMap::iterator registration
=
279 registrations_
[service_worker_registration_id
]
280 .insert(std::make_pair(region_id
,
281 Registration(service_worker_registration_id
,
282 service_worker_origin
,
286 geofencing_registration_id
)))
288 registrations_by_id_
[geofencing_registration_id
] = registration
;
289 return registration
->second
;
292 void GeofencingManager::ClearRegistration(Registration
* registration
) {
293 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
294 registrations_by_id_
.erase(registration
->geofencing_registration_id
);
295 ServiceWorkerRegistrationsMap::iterator registrations_iterator
=
296 registrations_
.find(registration
->service_worker_registration_id
);
297 DCHECK(registrations_iterator
!= registrations_
.end());
298 registrations_iterator
->second
.erase(registration
->region_id
);
299 if (registrations_iterator
->second
.empty())
300 registrations_
.erase(registrations_iterator
);
303 void GeofencingManager::DispatchGeofencingEvent(
304 blink::WebGeofencingEventType event_type
,
305 int64 geofencing_registration_id
) {
306 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
307 Registration
* registration
= FindRegistrationById(geofencing_registration_id
);
309 registration
->service_worker_registration_id
==
310 kInvalidServiceWorkerRegistrationId
) {
311 // TODO(mek): Log/track these failures.
315 service_worker_context_
->context()->storage()->FindRegistrationForId(
316 registration
->service_worker_registration_id
,
317 registration
->service_worker_origin
,
318 base::Bind(&GeofencingManager::DeliverGeofencingEvent
,
321 geofencing_registration_id
));
324 void GeofencingManager::DeliverGeofencingEvent(
325 blink::WebGeofencingEventType event_type
,
326 int64 geofencing_registration_id
,
327 ServiceWorkerStatusCode service_worker_status
,
328 const scoped_refptr
<ServiceWorkerRegistration
>&
329 service_worker_registration
) {
330 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
331 Registration
* registration
= FindRegistrationById(geofencing_registration_id
);
333 // Geofence got unregistered in the meantime, no longer need to deliver
338 if (service_worker_status
!= SERVICE_WORKER_OK
) {
339 // TODO(mek): SW no longer exists, somehow handle this.
343 ServiceWorkerVersion
* active_version
=
344 service_worker_registration
->active_version();
345 if (!active_version
) {
346 // TODO(mek): No active version, potentially because one is still being
347 // installed. Handle this somehow.
351 // Hold on to the service worker registration in the callback to keep it alive
352 // until the callback dies. Otherwise the registration could be released when
353 // this method returns - before the event is delivered to the service worker.
354 base::Callback
<void(ServiceWorkerStatusCode
)> dispatch_event_callback
=
355 base::Bind(&GeofencingManager::DeliverGeofencingEventEnd
,
357 service_worker_registration
);
358 active_version
->DispatchGeofencingEvent(dispatch_event_callback
,
360 registration
->region_id
,
361 registration
->region
);
364 void GeofencingManager::DeliverGeofencingEventEnd(
365 const scoped_refptr
<ServiceWorkerRegistration
>& service_worker_registration
,
366 ServiceWorkerStatusCode service_worker_status
) {
367 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
368 // TODO(mek): log/check result.
371 } // namespace content