service_worker_storage.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1// Copyright 2013 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/service_worker/service_worker_storage.h" 6 7#include <string> 8#include "base/message_loop/message_loop.h" 9#include "content/browser/service_worker/service_worker_context_core.h" 10#include "content/browser/service_worker/service_worker_info.h" 11#include "content/browser/service_worker/service_worker_registration.h" 12#include "content/browser/service_worker/service_worker_utils.h" 13#include "content/browser/service_worker/service_worker_version.h" 14#include "content/public/browser/browser_thread.h" 15#include "webkit/browser/quota/quota_manager_proxy.h" 16 17namespace content { 18 19namespace { 20 21void RunSoon(const base::Closure& closure) { 22 base::MessageLoop::current()->PostTask(FROM_HERE, closure); 23} 24 25void CompleteFindNow( 26 const scoped_refptr<ServiceWorkerRegistration>& registration, 27 ServiceWorkerStatusCode status, 28 const ServiceWorkerStorage::FindRegistrationCallback& callback) { 29 callback.Run(status, registration); 30} 31 32void CompleteFindSoon( 33 const scoped_refptr<ServiceWorkerRegistration>& registration, 34 ServiceWorkerStatusCode status, 35 const ServiceWorkerStorage::FindRegistrationCallback& callback) { 36 RunSoon(base::Bind(callback, status, registration)); 37} 38 39const base::FilePath::CharType kServiceWorkerDirectory[] = 40 FILE_PATH_LITERAL("ServiceWorker"); 41 42} // namespace 43 44ServiceWorkerStorage::ServiceWorkerStorage( 45 const base::FilePath& path, 46 base::WeakPtr<ServiceWorkerContextCore> context, 47 quota::QuotaManagerProxy* quota_manager_proxy) 48 : last_registration_id_(0), 49 last_version_id_(0), 50 last_resource_id_(0), 51 simulated_lazy_initted_(false), 52 context_(context), 53 quota_manager_proxy_(quota_manager_proxy) { 54 if (!path.empty()) 55 path_ = path.Append(kServiceWorkerDirectory); 56} 57 58ServiceWorkerStorage::~ServiceWorkerStorage() { 59} 60 61void ServiceWorkerStorage::FindRegistrationForPattern( 62 const GURL& scope, 63 const FindRegistrationCallback& callback) { 64 simulated_lazy_initted_ = true; 65 scoped_refptr<ServiceWorkerRegistration> null_registration; 66 if (!context_) { 67 CompleteFindSoon(null_registration, SERVICE_WORKER_ERROR_FAILED, callback); 68 return; 69 } 70 71 scoped_refptr<ServiceWorkerRegistration> installing_registration = 72 FindInstallingRegistrationForPattern(scope); 73 if (installing_registration) { 74 CompleteFindSoon(installing_registration, SERVICE_WORKER_OK, callback); 75 return; 76 } 77 78 // See if there are any registrations for the origin. 79 OriginRegistrationsMap::const_iterator 80 found = stored_registrations_.find(scope.GetOrigin()); 81 if (found == stored_registrations_.end()) { 82 CompleteFindSoon(null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, 83 callback); 84 return; 85 } 86 87 // Find one with a matching scope. 88 for (RegistrationsMap::const_iterator it = found->second.begin(); 89 it != found->second.end(); ++it) { 90 if (scope == it->second.scope) { 91 const ServiceWorkerDatabase::RegistrationData* data = &(it->second); 92 scoped_refptr<ServiceWorkerRegistration> registration = 93 context_->GetLiveRegistration(data->registration_id); 94 if (registration) { 95 CompleteFindSoon(registration, SERVICE_WORKER_OK, callback); 96 return; 97 } 98 99 registration = CreateRegistration(data); 100 CompleteFindSoon(registration, SERVICE_WORKER_OK, callback); 101 return; 102 } 103 } 104 105 CompleteFindSoon(null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, callback); 106} 107 108void ServiceWorkerStorage::FindRegistrationForDocument( 109 const GURL& document_url, 110 const FindRegistrationCallback& callback) { 111 simulated_lazy_initted_ = true; 112 scoped_refptr<ServiceWorkerRegistration> null_registration; 113 if (!context_) { 114 CompleteFindNow(null_registration, SERVICE_WORKER_ERROR_FAILED, callback); 115 return; 116 } 117 118 // See if there are any registrations for the origin. 119 OriginRegistrationsMap::const_iterator 120 found = stored_registrations_.find(document_url.GetOrigin()); 121 if (found == stored_registrations_.end()) { 122 // Look for something currently being installed. 123 scoped_refptr<ServiceWorkerRegistration> installing_registration = 124 FindInstallingRegistrationForDocument(document_url); 125 if (installing_registration) { 126 CompleteFindNow(installing_registration, SERVICE_WORKER_OK, callback); 127 return; 128 } 129 130 // Return syncly to simulate this class having an in memory map of 131 // origins with registrations. 132 CompleteFindNow(null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, 133 callback); 134 return; 135 } 136 137 // Find one with a pattern match. 138 for (RegistrationsMap::const_iterator it = found->second.begin(); 139 it != found->second.end(); ++it) { 140 // TODO(michaeln): if there are multiple matches the one with 141 // the longest scope should win. 142 if (ServiceWorkerUtils::ScopeMatches(it->second.scope, document_url)) { 143 const ServiceWorkerDatabase::RegistrationData* data = &(it->second); 144 145 // If its in the live map, return syncly to simulate this class having 146 // iterated over the values in that map instead of reading the db. 147 scoped_refptr<ServiceWorkerRegistration> registration = 148 context_->GetLiveRegistration(data->registration_id); 149 if (registration) { 150 CompleteFindNow(registration, SERVICE_WORKER_OK, callback); 151 return; 152 } 153 154 // If we have to create a new instance, return it asyncly to simulate 155 // having had to retreive the RegistrationData from the db. 156 registration = CreateRegistration(data); 157 CompleteFindSoon(registration, SERVICE_WORKER_OK, callback); 158 return; 159 } 160 } 161 162 // Look for something currently being installed. 163 // TODO(michaeln): Should be mixed in with the stored registrations 164 // for this test. 165 scoped_refptr<ServiceWorkerRegistration> installing_registration = 166 FindInstallingRegistrationForDocument(document_url); 167 if (installing_registration) { 168 CompleteFindSoon(installing_registration, SERVICE_WORKER_OK, callback); 169 return; 170 } 171 172 // Return asyncly to simulate having had to look in the db since this 173 // origin does have some registations. 174 CompleteFindSoon(installing_registration, SERVICE_WORKER_OK, callback); 175} 176 177void ServiceWorkerStorage::FindRegistrationForId( 178 int64 registration_id, 179 const FindRegistrationCallback& callback) { 180 simulated_lazy_initted_ = true; 181 scoped_refptr<ServiceWorkerRegistration> null_registration; 182 if (!context_) { 183 CompleteFindNow(null_registration, SERVICE_WORKER_ERROR_FAILED, callback); 184 return; 185 } 186 scoped_refptr<ServiceWorkerRegistration> installing_registration = 187 FindInstallingRegistrationForId(registration_id); 188 if (installing_registration) { 189 CompleteFindNow(installing_registration, SERVICE_WORKER_OK, callback); 190 return; 191 } 192 RegistrationPtrMap::const_iterator found = 193 registrations_by_id_.find(registration_id); 194 if (found == registrations_by_id_.end()) { 195 CompleteFindNow(null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, 196 callback); 197 return; 198 } 199 scoped_refptr<ServiceWorkerRegistration> registration = 200 context_->GetLiveRegistration(registration_id); 201 if (registration) { 202 CompleteFindNow(registration, SERVICE_WORKER_OK, callback); 203 return; 204 } 205 registration = CreateRegistration(found->second); 206 CompleteFindSoon(registration, SERVICE_WORKER_OK, callback); 207} 208 209void ServiceWorkerStorage::GetAllRegistrations( 210 const GetAllRegistrationInfosCallback& callback) { 211 simulated_lazy_initted_ = true; 212 std::vector<ServiceWorkerRegistrationInfo> registrations; 213 if (!context_) { 214 RunSoon(base::Bind(callback, registrations)); 215 return; 216 } 217 218 // Add all stored registrations. 219 for (RegistrationPtrMap::const_iterator it = registrations_by_id_.begin(); 220 it != registrations_by_id_.end(); ++it) { 221 ServiceWorkerRegistration* registration = 222 context_->GetLiveRegistration(it->first); 223 if (registration) { 224 registrations.push_back(registration->GetInfo()); 225 continue; 226 } 227 ServiceWorkerRegistrationInfo info; 228 info.pattern = it->second->scope; 229 info.script_url = it->second->script; 230 info.active_version.is_null = false; 231 if (it->second->is_active) 232 info.active_version.status = ServiceWorkerVersion::ACTIVE; 233 else 234 info.active_version.status = ServiceWorkerVersion::INSTALLED; 235 registrations.push_back(info); 236 } 237 238 // Add unstored registrations that are being installed. 239 for (RegistrationRefsById::const_iterator it = 240 installing_registrations_.begin(); 241 it != installing_registrations_.end(); ++it) { 242 if (registrations_by_id_.find(it->first) == registrations_by_id_.end()) 243 registrations.push_back(it->second->GetInfo()); 244 } 245 246 RunSoon(base::Bind(callback, registrations)); 247} 248 249void ServiceWorkerStorage::StoreRegistration( 250 ServiceWorkerRegistration* registration, 251 ServiceWorkerVersion* version, 252 const StatusCallback& callback) { 253 DCHECK(registration); 254 DCHECK(version); 255 DCHECK(simulated_lazy_initted_); 256 if (!context_) { 257 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 258 return; 259 } 260 261 // Keep a database struct in the storage map. 262 RegistrationsMap& storage_map = 263 stored_registrations_[registration->script_url().GetOrigin()]; 264 ServiceWorkerDatabase::RegistrationData& data = 265 storage_map[registration->id()]; 266 data.registration_id = registration->id(); 267 data.scope = registration->pattern(); 268 data.script = registration->script_url(); 269 data.has_fetch_handler = true; 270 data.version_id = version->version_id(); 271 data.last_update_check = base::Time::Now(); 272 data.is_active = false; // initially stored in the waiting state 273 274 // Keep a seperate map of ptrs keyed by id only. 275 registrations_by_id_[registration->id()] = &storage_map[registration->id()]; 276 277 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 278} 279 280 void ServiceWorkerStorage::UpdateToActiveState( 281 ServiceWorkerRegistration* registration, 282 const StatusCallback& callback) { 283 DCHECK(simulated_lazy_initted_); 284 if (!context_) { 285 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 286 return; 287 } 288 289 RegistrationPtrMap::const_iterator 290 found = registrations_by_id_.find(registration->id()); 291 if (found == registrations_by_id_.end()) { 292 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_NOT_FOUND)); 293 return; 294 } 295 DCHECK(!found->second->is_active); 296 found->second->is_active = true; 297 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 298} 299 300void ServiceWorkerStorage::DeleteRegistration( 301 int64 registration_id, 302 const StatusCallback& callback) { 303 DCHECK(simulated_lazy_initted_); 304 RegistrationPtrMap::iterator 305 found = registrations_by_id_.find(registration_id); 306 if (found == registrations_by_id_.end()) { 307 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_NOT_FOUND)); 308 return; 309 } 310 311 GURL origin = found->second->script.GetOrigin(); 312 stored_registrations_[origin].erase(registration_id); 313 if (stored_registrations_[origin].empty()) 314 stored_registrations_.erase(origin); 315 316 registrations_by_id_.erase(found); 317 318 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 319 // TODO(michaeln): Either its instance should also be 320 // removed from liveregistrations map or the live object 321 // should marked as deleted in some way and not 'findable' 322 // thereafter. 323} 324 325int64 ServiceWorkerStorage::NewRegistrationId() { 326 DCHECK(simulated_lazy_initted_); 327 return ++last_registration_id_; 328} 329 330int64 ServiceWorkerStorage::NewVersionId() { 331 DCHECK(simulated_lazy_initted_); 332 return ++last_version_id_; 333} 334 335int64 ServiceWorkerStorage::NewResourceId() { 336 DCHECK(simulated_lazy_initted_); 337 return ++last_resource_id_; 338} 339 340void ServiceWorkerStorage::NotifyInstallingRegistration( 341 ServiceWorkerRegistration* registration) { 342 installing_registrations_[registration->id()] = registration; 343} 344 345void ServiceWorkerStorage::NotifyDoneInstallingRegistration( 346 ServiceWorkerRegistration* registration) { 347 installing_registrations_.erase(registration->id()); 348} 349 350scoped_refptr<ServiceWorkerRegistration> 351ServiceWorkerStorage::CreateRegistration( 352 const ServiceWorkerDatabase::RegistrationData* data) { 353 scoped_refptr<ServiceWorkerRegistration> registration( 354 new ServiceWorkerRegistration( 355 data->scope, data->script, data->registration_id, context_)); 356 357 scoped_refptr<ServiceWorkerVersion> version = 358 context_->GetLiveVersion(data->version_id); 359 if (!version) { 360 version = new ServiceWorkerVersion( 361 registration, data->version_id, context_); 362 version->SetStatus(data->GetVersionStatus()); 363 } 364 365 if (version->status() == ServiceWorkerVersion::ACTIVE) 366 registration->set_active_version(version); 367 else if (version->status() == ServiceWorkerVersion::INSTALLED) 368 registration->set_pending_version(version); 369 else 370 NOTREACHED(); 371 // TODO(michaeln): Hmmm, what if DeleteReg was invoked after 372 // the Find result we're returning here? NOTREACHED condition? 373 374 return registration; 375} 376 377ServiceWorkerRegistration* 378ServiceWorkerStorage::FindInstallingRegistrationForDocument( 379 const GURL& document_url) { 380 // TODO(michaeln): if there are multiple matches the one with 381 // the longest scope should win, and these should on equal footing 382 // with the stored registrations in FindRegistrationForDocument(). 383 for (RegistrationRefsById::const_iterator it = 384 installing_registrations_.begin(); 385 it != installing_registrations_.end(); ++it) { 386 if (ServiceWorkerUtils::ScopeMatches( 387 it->second->pattern(), document_url)) { 388 return it->second; 389 } 390 } 391 return NULL; 392} 393 394ServiceWorkerRegistration* 395ServiceWorkerStorage::FindInstallingRegistrationForPattern( 396 const GURL& scope) { 397 for (RegistrationRefsById::const_iterator it = 398 installing_registrations_.begin(); 399 it != installing_registrations_.end(); ++it) { 400 if (it->second->pattern() == scope) 401 return it->second; 402 } 403 return NULL; 404} 405 406ServiceWorkerRegistration* 407ServiceWorkerStorage::FindInstallingRegistrationForId( 408 int64 registration_id) { 409 RegistrationRefsById::const_iterator found = 410 installing_registrations_.find(registration_id); 411 if (found == installing_registrations_.end()) 412 return NULL; 413 return found->second; 414} 415 416} // namespace content 417