OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/geofencing/geofencing_manager.h" | |
6 | |
7 #include <algorithm> | |
8 | |
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/common/geofencing_messages.h" | |
14 #include "content/common/service_worker/service_worker_messages.h" | |
15 #include "content/public/browser/browser_context.h" | |
16 #include "content/public/browser/browser_thread.h" | |
17 #include "content/public/browser/storage_partition.h" | |
18 #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" | |
19 #include "third_party/WebKit/public/platform/WebGeofencingEventType.h" | |
20 #include "url/gurl.h" | |
21 | |
22 namespace content { | |
23 | |
24 struct GeofencingManager::Registration { | |
25 Registration(int64_t service_worker_registration_id, | |
26 const GURL& service_worker_origin, | |
27 const std::string& region_id, | |
28 const blink::WebCircularGeofencingRegion& region, | |
29 const StatusCallback& callback, | |
30 int64_t geofencing_registration_id); | |
31 int64_t service_worker_registration_id; | |
32 GURL service_worker_origin; | |
33 std::string region_id; | |
34 blink::WebCircularGeofencingRegion region; | |
35 | |
36 // Registration ID as returned by the |GeofencingService|. | |
37 int64_t geofencing_registration_id; | |
38 | |
39 // Callback to call when registration is completed. This field is reset when | |
40 // registration is complete. | |
41 StatusCallback registration_callback; | |
42 | |
43 // Returns true if registration has been completed, and thus should be | |
44 // included in calls to GetRegisteredRegions. | |
45 bool is_active() const { return registration_callback.is_null(); } | |
46 }; | |
47 | |
48 GeofencingManager::Registration::Registration( | |
49 int64_t service_worker_registration_id, | |
50 const GURL& service_worker_origin, | |
51 const std::string& region_id, | |
52 const blink::WebCircularGeofencingRegion& region, | |
53 const GeofencingManager::StatusCallback& callback, | |
54 int64_t geofencing_registration_id) | |
55 : service_worker_registration_id(service_worker_registration_id), | |
56 service_worker_origin(service_worker_origin), | |
57 region_id(region_id), | |
58 region(region), | |
59 geofencing_registration_id(geofencing_registration_id), | |
60 registration_callback(callback) {} | |
61 | |
62 GeofencingManager::GeofencingManager( | |
63 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) | |
64 : service_(nullptr), service_worker_context_(service_worker_context) { | |
65 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
66 } | |
67 | |
68 GeofencingManager::~GeofencingManager() { | |
69 } | |
70 | |
71 void GeofencingManager::Init() { | |
72 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
73 BrowserThread::PostTask(BrowserThread::IO, | |
74 FROM_HERE, | |
75 base::Bind(&GeofencingManager::InitOnIO, this)); | |
76 } | |
77 | |
78 void GeofencingManager::Shutdown() { | |
79 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
80 BrowserThread::PostTask(BrowserThread::IO, | |
81 FROM_HERE, | |
82 base::Bind(&GeofencingManager::ShutdownOnIO, this)); | |
83 } | |
84 | |
85 void GeofencingManager::InitOnIO() { | |
86 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
87 service_worker_context_->AddObserver(this); | |
88 if (!service_) | |
89 service_ = GeofencingServiceImpl::GetInstance(); | |
90 } | |
91 | |
92 void GeofencingManager::ShutdownOnIO() { | |
93 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
94 service_worker_context_->RemoveObserver(this); | |
95 // Clean up all registrations with the |GeofencingService|. | |
96 // TODO(mek): This will need to change to support geofence registrations that | |
97 // outlive the browser, although removing the references to this | |
98 // |GeofencingManager| from the |GeofencingService| will still be needed. | |
99 for (const auto& registration : registrations_by_id_) | |
100 service_->UnregisterRegion(registration.first); | |
101 } | |
102 | |
103 void GeofencingManager::RegisterRegion( | |
104 int64_t service_worker_registration_id, | |
105 const std::string& region_id, | |
106 const blink::WebCircularGeofencingRegion& region, | |
107 const StatusCallback& callback) { | |
108 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
109 | |
110 // TODO(mek): Validate region_id and region. | |
111 | |
112 // Look up service worker. | |
113 ServiceWorkerRegistration* service_worker_registration = | |
114 service_worker_context_->GetLiveRegistration( | |
115 service_worker_registration_id); | |
116 if (!service_worker_registration) { | |
117 callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER); | |
118 return; | |
119 } | |
120 | |
121 GURL service_worker_origin = | |
122 service_worker_registration->pattern().GetOrigin(); | |
123 | |
124 if (!service_->IsServiceAvailable()) { | |
125 callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE); | |
126 return; | |
127 } | |
128 | |
129 if (FindRegistration(service_worker_registration_id, region_id)) { | |
130 // Already registered, return an error. | |
131 // TODO(mek): Use a more specific error code. | |
132 callback.Run(GEOFENCING_STATUS_ERROR); | |
133 return; | |
134 } | |
135 | |
136 AddRegistration(service_worker_registration_id, service_worker_origin, | |
137 region_id, region, callback, | |
138 service_->RegisterRegion(region, this)); | |
139 } | |
140 | |
141 void GeofencingManager::UnregisterRegion(int64_t service_worker_registration_id, | |
142 const std::string& region_id, | |
143 const StatusCallback& callback) { | |
144 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
145 | |
146 // TODO(mek): Validate region_id. | |
147 | |
148 // Look up service worker. | |
149 ServiceWorkerRegistration* service_worker_registration = | |
150 service_worker_context_->GetLiveRegistration( | |
151 service_worker_registration_id); | |
152 if (!service_worker_registration) { | |
153 callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER); | |
154 return; | |
155 } | |
156 | |
157 if (!service_->IsServiceAvailable()) { | |
158 callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE); | |
159 return; | |
160 } | |
161 | |
162 Registration* registration = | |
163 FindRegistration(service_worker_registration_id, region_id); | |
164 if (!registration) { | |
165 // Not registered, return an error. | |
166 callback.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED); | |
167 return; | |
168 } | |
169 | |
170 if (!registration->is_active()) { | |
171 // Started registration, but not completed yet, error. | |
172 callback.Run(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED); | |
173 return; | |
174 } | |
175 | |
176 service_->UnregisterRegion(registration->geofencing_registration_id); | |
177 ClearRegistration(registration); | |
178 callback.Run(GEOFENCING_STATUS_OK); | |
179 } | |
180 | |
181 GeofencingStatus GeofencingManager::GetRegisteredRegions( | |
182 int64_t service_worker_registration_id, | |
183 std::map<std::string, blink::WebCircularGeofencingRegion>* result) { | |
184 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
185 CHECK(result); | |
186 | |
187 // Look up service worker. | |
188 ServiceWorkerRegistration* service_worker_registration = | |
189 service_worker_context_->GetLiveRegistration( | |
190 service_worker_registration_id); | |
191 if (!service_worker_registration) { | |
192 return GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER; | |
193 } | |
194 | |
195 if (!service_->IsServiceAvailable()) { | |
196 return GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE; | |
197 } | |
198 | |
199 // Populate result, filtering out inactive registrations. | |
200 result->clear(); | |
201 ServiceWorkerRegistrationsMap::iterator registrations = | |
202 registrations_.find(service_worker_registration_id); | |
203 if (registrations == registrations_.end()) | |
204 return GEOFENCING_STATUS_OK; | |
205 for (const auto& registration : registrations->second) { | |
206 if (registration.second.is_active()) | |
207 (*result)[registration.first] = registration.second.region; | |
208 } | |
209 return GEOFENCING_STATUS_OK; | |
210 } | |
211 | |
212 void GeofencingManager::SetMockProvider(GeofencingMockState mock_state) { | |
213 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
214 | |
215 // TODO(mek): It would be nice if enabling the mock geofencing service | |
216 // wouldn't completely delete all existing registrations but instead just | |
217 // somehow keep them around but inactive. | |
218 // For now mocking is only used in layout tests, so just clearing all | |
219 // registrations works good enough. | |
220 for (const auto& registration : registrations_by_id_) | |
221 service_->UnregisterRegion(registration.first); | |
222 registrations_by_id_.clear(); | |
223 registrations_.clear(); | |
224 | |
225 // Then set or reset the mock service for the service worker. | |
226 if (mock_state == GeofencingMockState::NONE) { | |
227 service_ = GeofencingServiceImpl::GetInstance(); | |
228 mock_service_.reset(); | |
229 } else { | |
230 mock_service_.reset(new MockGeofencingService( | |
231 mock_state != GeofencingMockState::SERVICE_UNAVAILABLE)); | |
232 service_ = mock_service_.get(); | |
233 } | |
234 } | |
235 | |
236 void GeofencingManager::SetMockPosition(double latitude, double longitude) { | |
237 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
238 if (!mock_service_) | |
239 return; | |
240 mock_service_->SetMockPosition(latitude, longitude); | |
241 } | |
242 | |
243 void GeofencingManager::OnRegistrationDeleted( | |
244 int64_t service_worker_registration_id, | |
245 const GURL& pattern) { | |
246 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
247 CleanUpForServiceWorker(service_worker_registration_id); | |
248 } | |
249 | |
250 void GeofencingManager::RegistrationFinished(int64_t geofencing_registration_id, | |
251 GeofencingStatus status) { | |
252 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
253 Registration* registration = FindRegistrationById(geofencing_registration_id); | |
254 DCHECK(registration); | |
255 DCHECK(!registration->is_active()); | |
256 registration->registration_callback.Run(status); | |
257 registration->registration_callback.Reset(); | |
258 | |
259 // If the registration wasn't succesful, remove it from our storage. | |
260 if (status != GEOFENCING_STATUS_OK) | |
261 ClearRegistration(registration); | |
262 } | |
263 | |
264 void GeofencingManager::RegionEntered(int64_t geofencing_registration_id) { | |
265 DispatchGeofencingEvent(blink::WebGeofencingEventTypeEnter, | |
266 geofencing_registration_id); | |
267 } | |
268 | |
269 void GeofencingManager::RegionExited(int64_t geofencing_registration_id) { | |
270 DispatchGeofencingEvent(blink::WebGeofencingEventTypeLeave, | |
271 geofencing_registration_id); | |
272 } | |
273 | |
274 GeofencingManager::Registration* GeofencingManager::FindRegistration( | |
275 int64_t service_worker_registration_id, | |
276 const std::string& region_id) { | |
277 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
278 ServiceWorkerRegistrationsMap::iterator registrations_iterator = | |
279 registrations_.find(service_worker_registration_id); | |
280 if (registrations_iterator == registrations_.end()) | |
281 return nullptr; | |
282 RegionIdRegistrationMap::iterator registration = | |
283 registrations_iterator->second.find(region_id); | |
284 if (registration == registrations_iterator->second.end()) | |
285 return nullptr; | |
286 return ®istration->second; | |
287 } | |
288 | |
289 GeofencingManager::Registration* GeofencingManager::FindRegistrationById( | |
290 int64_t geofencing_registration_id) { | |
291 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
292 RegistrationIdRegistrationMap::iterator registration_iterator = | |
293 registrations_by_id_.find(geofencing_registration_id); | |
294 if (registration_iterator == registrations_by_id_.end()) | |
295 return nullptr; | |
296 return ®istration_iterator->second->second; | |
297 } | |
298 | |
299 GeofencingManager::Registration& GeofencingManager::AddRegistration( | |
300 int64_t service_worker_registration_id, | |
301 const GURL& service_worker_origin, | |
302 const std::string& region_id, | |
303 const blink::WebCircularGeofencingRegion& region, | |
304 const StatusCallback& callback, | |
305 int64_t geofencing_registration_id) { | |
306 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
307 DCHECK(!FindRegistration(service_worker_registration_id, region_id)); | |
308 RegionIdRegistrationMap::iterator registration = | |
309 registrations_[service_worker_registration_id] | |
310 .insert(std::make_pair(region_id, | |
311 Registration(service_worker_registration_id, | |
312 service_worker_origin, | |
313 region_id, | |
314 region, | |
315 callback, | |
316 geofencing_registration_id))) | |
317 .first; | |
318 registrations_by_id_[geofencing_registration_id] = registration; | |
319 return registration->second; | |
320 } | |
321 | |
322 void GeofencingManager::ClearRegistration(Registration* registration) { | |
323 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
324 registrations_by_id_.erase(registration->geofencing_registration_id); | |
325 ServiceWorkerRegistrationsMap::iterator registrations_iterator = | |
326 registrations_.find(registration->service_worker_registration_id); | |
327 DCHECK(registrations_iterator != registrations_.end()); | |
328 registrations_iterator->second.erase(registration->region_id); | |
329 if (registrations_iterator->second.empty()) | |
330 registrations_.erase(registrations_iterator); | |
331 } | |
332 | |
333 void GeofencingManager::CleanUpForServiceWorker( | |
334 int64_t service_worker_registration_id) { | |
335 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
336 ServiceWorkerRegistrationsMap::iterator registrations_iterator = | |
337 registrations_.find(service_worker_registration_id); | |
338 if (registrations_iterator == registrations_.end()) | |
339 return; | |
340 | |
341 for (const auto& registration : registrations_iterator->second) { | |
342 int geofencing_registration_id = | |
343 registration.second.geofencing_registration_id; | |
344 service_->UnregisterRegion(geofencing_registration_id); | |
345 registrations_by_id_.erase(geofencing_registration_id); | |
346 } | |
347 registrations_.erase(service_worker_registration_id); | |
348 } | |
349 | |
350 void GeofencingManager::DispatchGeofencingEvent( | |
351 blink::WebGeofencingEventType event_type, | |
352 int64_t geofencing_registration_id) { | |
353 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
354 Registration* registration = FindRegistrationById(geofencing_registration_id); | |
355 if (!registration || | |
356 registration->service_worker_registration_id == | |
357 kInvalidServiceWorkerRegistrationId) { | |
358 // TODO(mek): Log/track these failures. | |
359 return; | |
360 } | |
361 | |
362 service_worker_context_->FindReadyRegistrationForId( | |
363 registration->service_worker_registration_id, | |
364 registration->service_worker_origin, | |
365 base::Bind(&GeofencingManager::DeliverGeofencingEvent, | |
366 this, | |
367 event_type, | |
368 geofencing_registration_id)); | |
369 } | |
370 | |
371 void GeofencingManager::DeliverGeofencingEvent( | |
372 blink::WebGeofencingEventType event_type, | |
373 int64_t geofencing_registration_id, | |
374 ServiceWorkerStatusCode service_worker_status, | |
375 const scoped_refptr<ServiceWorkerRegistration>& | |
376 service_worker_registration) { | |
377 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
378 Registration* registration = FindRegistrationById(geofencing_registration_id); | |
379 if (!registration) { | |
380 // Geofence got unregistered in the meantime, no longer need to deliver | |
381 // event. | |
382 return; | |
383 } | |
384 | |
385 if (service_worker_status != SERVICE_WORKER_OK) { | |
386 // TODO(mek): SW no longer exists, somehow handle this. | |
387 return; | |
388 } | |
389 | |
390 ServiceWorkerVersion* active_version = | |
391 service_worker_registration->active_version(); | |
392 DCHECK(active_version); | |
393 | |
394 // Hold on to the service worker registration in the callback to keep it alive | |
395 // until the callback dies. Otherwise the registration could be released when | |
396 // this method returns - before the event is delivered to the service worker. | |
397 active_version->RunAfterStartWorker( | |
398 ServiceWorkerMetrics::EventType::GEOFENCING, | |
399 base::Bind(&GeofencingManager::DeliverEventToRunningWorker, this, | |
400 service_worker_registration, event_type, | |
401 registration->region_id, registration->region, | |
402 make_scoped_refptr(active_version)), | |
403 base::Bind(&GeofencingManager::OnEventError, this)); | |
404 } | |
405 | |
406 void GeofencingManager::DeliverEventToRunningWorker( | |
407 const scoped_refptr<ServiceWorkerRegistration>& service_worker_registration, | |
408 blink::WebGeofencingEventType event_type, | |
409 const std::string& region_id, | |
410 const blink::WebCircularGeofencingRegion& region, | |
411 const scoped_refptr<ServiceWorkerVersion>& worker) { | |
412 int request_id = | |
413 worker->StartRequest(ServiceWorkerMetrics::EventType::GEOFENCING, | |
414 base::Bind(&GeofencingManager::OnEventError, this)); | |
415 | |
416 worker->DispatchEvent<ServiceWorkerHostMsg_GeofencingEventFinished>( | |
417 request_id, ServiceWorkerMsg_GeofencingEvent(request_id, event_type, | |
418 region_id, region), | |
419 base::Bind(&GeofencingManager::OnEventResponse, this, worker, | |
420 service_worker_registration)); | |
421 } | |
422 | |
423 void GeofencingManager::OnEventResponse( | |
424 const scoped_refptr<ServiceWorkerVersion>& worker, | |
425 const scoped_refptr<ServiceWorkerRegistration>& registration, | |
426 int request_id, | |
427 blink::WebServiceWorkerEventResult result) { | |
428 bool finish_result = worker->FinishRequest( | |
429 request_id, result == blink::WebServiceWorkerEventResultCompleted); | |
430 DCHECK(finish_result) | |
431 << "No messages should be passed to handler if request had " | |
432 "already finished"; | |
433 // TODO(mek): log/check result. | |
434 } | |
435 | |
436 void GeofencingManager::OnEventError( | |
437 ServiceWorkerStatusCode service_worker_status) { | |
438 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
439 // TODO(mek): log/check errors. | |
440 } | |
441 | |
442 } // namespace content | |
OLD | NEW |