| 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 |