Apply _RELATIVE relocations ahead of others.
[chromium-blink-merge.git] / content / browser / geofencing / geofencing_manager.cc
blob6899c73067a2c4be63108f24e00cf05413ac0a0c
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"
7 #include <algorithm>
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"
17 #include "url/gurl.h"
19 namespace content {
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),
54 region_id(region_id),
55 region(region),
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,
72 FROM_HERE,
73 base::Bind(&GeofencingManager::InitOnIO, this));
76 void GeofencingManager::Shutdown() {
77 DCHECK_CURRENTLY_ON(BrowserThread::UI);
78 BrowserThread::PostTask(BrowserThread::IO,
79 FROM_HERE,
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
109 // be set.
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);
117 return;
120 service_worker_origin = service_worker_registration->pattern().GetOrigin();
123 if (!service_->IsServiceAvailable()) {
124 callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE);
125 return;
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);
132 return;
135 AddRegistration(service_worker_registration_id,
136 service_worker_origin,
137 region_id,
138 region,
139 callback,
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
151 // be set.
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);
158 return;
162 if (!service_->IsServiceAvailable()) {
163 callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE);
164 return;
167 Registration* registration =
168 FindRegistration(service_worker_registration_id, region_id);
169 if (!registration) {
170 // Not registered, return an error.
171 callback.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED);
172 return;
175 if (!registration->is_active()) {
176 // Started registration, but not completed yet, error.
177 callback.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED);
178 return;
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);
190 CHECK(result);
192 // Look up service worker. In unit tests |service_worker_context_| might not
193 // be set.
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.
208 result->clear();
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())
251 return nullptr;
252 RegionIdRegistrationMap::iterator registration =
253 registrations_iterator->second.find(region_id);
254 if (registration == registrations_iterator->second.end())
255 return nullptr;
256 return &registration->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())
265 return nullptr;
266 return &registration_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,
283 region_id,
284 region,
285 callback,
286 geofencing_registration_id)))
287 .first;
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);
308 if (!registration ||
309 registration->service_worker_registration_id ==
310 kInvalidServiceWorkerRegistrationId) {
311 // TODO(mek): Log/track these failures.
312 return;
315 service_worker_context_->context()->storage()->FindRegistrationForId(
316 registration->service_worker_registration_id,
317 registration->service_worker_origin,
318 base::Bind(&GeofencingManager::DeliverGeofencingEvent,
319 this,
320 event_type,
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);
332 if (!registration) {
333 // Geofence got unregistered in the meantime, no longer need to deliver
334 // event.
335 return;
338 if (service_worker_status != SERVICE_WORKER_OK) {
339 // TODO(mek): SW no longer exists, somehow handle this.
340 return;
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.
348 return;
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,
356 this,
357 service_worker_registration);
358 active_version->DispatchGeofencingEvent(dispatch_event_callback,
359 event_type,
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