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 9 #include "base/bind_helpers.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/sequenced_task_runner.h" 12 #include "base/task_runner_util.h" 13 #include "content/browser/service_worker/service_worker_context_core.h" 14 #include "content/browser/service_worker/service_worker_disk_cache.h" 15 #include "content/browser/service_worker/service_worker_histograms.h" 16 #include "content/browser/service_worker/service_worker_info.h" 17 #include "content/browser/service_worker/service_worker_registration.h" 18 #include "content/browser/service_worker/service_worker_utils.h" 19 #include "content/browser/service_worker/service_worker_version.h" 20 #include "content/common/service_worker/service_worker_types.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "net/base/net_errors.h" 23 #include "webkit/browser/quota/quota_manager_proxy.h" 24 25 namespace content { 26 27 namespace { 28 29 void RunSoon(const tracked_objects::Location& from_here, 30 const base::Closure& closure) { 31 base::MessageLoop::current()->PostTask(from_here, closure); 32 } 33 34 void CompleteFindNow( 35 const scoped_refptr<ServiceWorkerRegistration>& registration, 36 ServiceWorkerStatusCode status, 37 const ServiceWorkerStorage::FindRegistrationCallback& callback) { 38 callback.Run(status, registration); 39 } 40 41 void CompleteFindSoon( 42 const tracked_objects::Location& from_here, 43 const scoped_refptr<ServiceWorkerRegistration>& registration, 44 ServiceWorkerStatusCode status, 45 const ServiceWorkerStorage::FindRegistrationCallback& callback) { 46 RunSoon(from_here, base::Bind(callback, status, registration)); 47 } 48 49 const base::FilePath::CharType kServiceWorkerDirectory[] = 50 FILE_PATH_LITERAL("Service Worker"); 51 const base::FilePath::CharType kDatabaseName[] = 52 FILE_PATH_LITERAL("Database"); 53 const base::FilePath::CharType kDiskCacheName[] = 54 FILE_PATH_LITERAL("Cache"); 55 56 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024; 57 const int kMaxDiskCacheSize = 250 * 1024 * 1024; 58 59 void EmptyCompletionCallback(int) {} 60 61 ServiceWorkerStatusCode DatabaseStatusToStatusCode( 62 ServiceWorkerDatabase::Status status) { 63 switch (status) { 64 case ServiceWorkerDatabase::STATUS_OK: 65 return SERVICE_WORKER_OK; 66 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND: 67 return SERVICE_WORKER_ERROR_NOT_FOUND; 68 case ServiceWorkerDatabase::STATUS_ERROR_MAX: 69 NOTREACHED(); 70 default: 71 return SERVICE_WORKER_ERROR_FAILED; 72 } 73 } 74 75 } // namespace 76 77 ServiceWorkerStorage::InitialData::InitialData() 78 : next_registration_id(kInvalidServiceWorkerRegistrationId), 79 next_version_id(kInvalidServiceWorkerVersionId), 80 next_resource_id(kInvalidServiceWorkerResourceId) { 81 } 82 83 ServiceWorkerStorage::InitialData::~InitialData() { 84 } 85 86 ServiceWorkerStorage::ServiceWorkerStorage( 87 const base::FilePath& path, 88 base::WeakPtr<ServiceWorkerContextCore> context, 89 base::SequencedTaskRunner* database_task_runner, 90 base::MessageLoopProxy* disk_cache_thread, 91 quota::QuotaManagerProxy* quota_manager_proxy) 92 : next_registration_id_(kInvalidServiceWorkerRegistrationId), 93 next_version_id_(kInvalidServiceWorkerVersionId), 94 next_resource_id_(kInvalidServiceWorkerResourceId), 95 state_(UNINITIALIZED), 96 context_(context), 97 database_task_runner_(database_task_runner), 98 disk_cache_thread_(disk_cache_thread), 99 quota_manager_proxy_(quota_manager_proxy), 100 is_purge_pending_(false), 101 weak_factory_(this) { 102 if (!path.empty()) 103 path_ = path.Append(kServiceWorkerDirectory); 104 database_.reset(new ServiceWorkerDatabase(GetDatabasePath())); 105 } 106 107 ServiceWorkerStorage::~ServiceWorkerStorage() { 108 weak_factory_.InvalidateWeakPtrs(); 109 database_task_runner_->DeleteSoon(FROM_HERE, database_.release()); 110 } 111 112 void ServiceWorkerStorage::FindRegistrationForDocument( 113 const GURL& document_url, 114 const FindRegistrationCallback& callback) { 115 DCHECK(!document_url.has_ref()); 116 if (!LazyInitialize(base::Bind( 117 &ServiceWorkerStorage::FindRegistrationForDocument, 118 weak_factory_.GetWeakPtr(), document_url, callback))) { 119 if (state_ != INITIALIZING || !context_) { 120 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(), 121 SERVICE_WORKER_ERROR_FAILED, callback); 122 } 123 return; 124 } 125 DCHECK_EQ(INITIALIZED, state_); 126 127 // See if there are any stored registrations for the origin. 128 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) { 129 // Look for something currently being installed. 130 scoped_refptr<ServiceWorkerRegistration> installing_registration = 131 FindInstallingRegistrationForDocument(document_url); 132 CompleteFindNow( 133 installing_registration, 134 installing_registration ? 135 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND, 136 callback); 137 return; 138 } 139 140 database_task_runner_->PostTask( 141 FROM_HERE, 142 base::Bind( 143 &FindForDocumentInDB, 144 database_.get(), 145 base::MessageLoopProxy::current(), 146 document_url, 147 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument, 148 weak_factory_.GetWeakPtr(), document_url, callback))); 149 } 150 151 void ServiceWorkerStorage::FindRegistrationForPattern( 152 const GURL& scope, 153 const FindRegistrationCallback& callback) { 154 if (!LazyInitialize(base::Bind( 155 &ServiceWorkerStorage::FindRegistrationForPattern, 156 weak_factory_.GetWeakPtr(), scope, callback))) { 157 if (state_ != INITIALIZING || !context_) { 158 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(), 159 SERVICE_WORKER_ERROR_FAILED, callback); 160 } 161 return; 162 } 163 DCHECK_EQ(INITIALIZED, state_); 164 165 // See if there are any stored registrations for the origin. 166 if (!ContainsKey(registered_origins_, scope.GetOrigin())) { 167 // Look for something currently being installed. 168 scoped_refptr<ServiceWorkerRegistration> installing_registration = 169 FindInstallingRegistrationForPattern(scope); 170 CompleteFindSoon( 171 FROM_HERE, installing_registration, 172 installing_registration ? 173 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND, 174 callback); 175 return; 176 } 177 178 database_task_runner_->PostTask( 179 FROM_HERE, 180 base::Bind( 181 &FindForPatternInDB, 182 database_.get(), 183 base::MessageLoopProxy::current(), 184 scope, 185 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern, 186 weak_factory_.GetWeakPtr(), scope, callback))); 187 } 188 189 void ServiceWorkerStorage::FindRegistrationForId( 190 int64 registration_id, 191 const GURL& origin, 192 const FindRegistrationCallback& callback) { 193 if (!LazyInitialize(base::Bind( 194 &ServiceWorkerStorage::FindRegistrationForId, 195 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) { 196 if (state_ != INITIALIZING || !context_) { 197 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(), 198 SERVICE_WORKER_ERROR_FAILED, callback); 199 } 200 return; 201 } 202 DCHECK_EQ(INITIALIZED, state_); 203 204 // See if there are any stored registrations for the origin. 205 if (!ContainsKey(registered_origins_, origin)) { 206 // Look for something currently being installed. 207 scoped_refptr<ServiceWorkerRegistration> installing_registration = 208 FindInstallingRegistrationForId(registration_id); 209 CompleteFindNow( 210 installing_registration, 211 installing_registration ? 212 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND, 213 callback); 214 return; 215 } 216 217 scoped_refptr<ServiceWorkerRegistration> registration = 218 context_->GetLiveRegistration(registration_id); 219 if (registration) { 220 CompleteFindNow(registration, SERVICE_WORKER_OK, callback); 221 return; 222 } 223 224 database_task_runner_->PostTask( 225 FROM_HERE, 226 base::Bind(&FindForIdInDB, 227 database_.get(), 228 base::MessageLoopProxy::current(), 229 registration_id, origin, 230 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId, 231 weak_factory_.GetWeakPtr(), callback))); 232 } 233 234 void ServiceWorkerStorage::GetAllRegistrations( 235 const GetAllRegistrationInfosCallback& callback) { 236 if (!LazyInitialize(base::Bind( 237 &ServiceWorkerStorage::GetAllRegistrations, 238 weak_factory_.GetWeakPtr(), callback))) { 239 if (state_ != INITIALIZING || !context_) { 240 RunSoon(FROM_HERE, base::Bind( 241 callback, std::vector<ServiceWorkerRegistrationInfo>())); 242 } 243 return; 244 } 245 DCHECK_EQ(INITIALIZED, state_); 246 247 RegistrationList* registrations = new RegistrationList; 248 PostTaskAndReplyWithResult( 249 database_task_runner_, 250 FROM_HERE, 251 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations, 252 base::Unretained(database_.get()), 253 base::Unretained(registrations)), 254 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations, 255 weak_factory_.GetWeakPtr(), 256 callback, 257 base::Owned(registrations))); 258 } 259 260 void ServiceWorkerStorage::StoreRegistration( 261 ServiceWorkerRegistration* registration, 262 ServiceWorkerVersion* version, 263 const StatusCallback& callback) { 264 DCHECK(registration); 265 DCHECK(version); 266 267 DCHECK(state_ == INITIALIZED || state_ == DISABLED); 268 if (state_ != INITIALIZED || !context_) { 269 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 270 return; 271 } 272 273 ServiceWorkerDatabase::RegistrationData data; 274 data.registration_id = registration->id(); 275 data.scope = registration->pattern(); 276 data.script = registration->script_url(); 277 data.has_fetch_handler = true; 278 data.version_id = version->version_id(); 279 data.last_update_check = base::Time::Now(); 280 data.is_active = false; // initially stored in the waiting state 281 282 ResourceList resources; 283 version->script_cache_map()->GetResources(&resources); 284 285 database_task_runner_->PostTask( 286 FROM_HERE, 287 base::Bind(&WriteRegistrationInDB, 288 database_.get(), 289 base::MessageLoopProxy::current(), 290 data, resources, 291 base::Bind(&ServiceWorkerStorage::DidStoreRegistration, 292 weak_factory_.GetWeakPtr(), 293 callback))); 294 } 295 296 void ServiceWorkerStorage::UpdateToActiveState( 297 ServiceWorkerRegistration* registration, 298 const StatusCallback& callback) { 299 DCHECK(registration); 300 301 DCHECK(state_ == INITIALIZED || state_ == DISABLED); 302 if (state_ != INITIALIZED || !context_) { 303 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 304 return; 305 } 306 307 PostTaskAndReplyWithResult( 308 database_task_runner_, 309 FROM_HERE, 310 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive, 311 base::Unretained(database_.get()), 312 registration->id(), 313 registration->script_url().GetOrigin()), 314 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState, 315 weak_factory_.GetWeakPtr(), 316 callback)); 317 } 318 319 void ServiceWorkerStorage::DeleteRegistration( 320 int64 registration_id, 321 const GURL& origin, 322 const StatusCallback& callback) { 323 DCHECK(state_ == INITIALIZED || state_ == DISABLED); 324 if (state_ != INITIALIZED || !context_) { 325 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 326 return; 327 } 328 329 database_task_runner_->PostTask( 330 FROM_HERE, 331 base::Bind(&DeleteRegistrationFromDB, 332 database_.get(), 333 base::MessageLoopProxy::current(), 334 registration_id, origin, 335 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration, 336 weak_factory_.GetWeakPtr(), origin, callback))); 337 338 // TODO(michaeln): Either its instance should also be 339 // removed from liveregistrations map or the live object 340 // should marked as deleted in some way and not 'findable' 341 // thereafter. 342 } 343 344 scoped_ptr<ServiceWorkerResponseReader> 345 ServiceWorkerStorage::CreateResponseReader(int64 response_id) { 346 return make_scoped_ptr( 347 new ServiceWorkerResponseReader(response_id, disk_cache())); 348 } 349 350 scoped_ptr<ServiceWorkerResponseWriter> 351 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) { 352 return make_scoped_ptr( 353 new ServiceWorkerResponseWriter(response_id, disk_cache())); 354 } 355 356 void ServiceWorkerStorage::StoreUncommittedReponseId(int64 id) { 357 DCHECK_NE(kInvalidServiceWorkerResponseId, id); 358 database_task_runner_->PostTask( 359 FROM_HERE, 360 base::Bind(base::IgnoreResult( 361 &ServiceWorkerDatabase::WriteUncommittedResourceIds), 362 base::Unretained(database_.get()), 363 std::set<int64>(&id, &id + 1))); 364 } 365 366 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) { 367 DCHECK_NE(kInvalidServiceWorkerResponseId, id); 368 database_task_runner_->PostTask( 369 FROM_HERE, 370 base::Bind(base::IgnoreResult( 371 &ServiceWorkerDatabase::PurgeUncommittedResourceIds), 372 base::Unretained(database_.get()), 373 std::set<int64>(&id, &id + 1))); 374 StartPurgingResources(std::vector<int64>(1, id)); 375 } 376 377 int64 ServiceWorkerStorage::NewRegistrationId() { 378 if (state_ == DISABLED) 379 return kInvalidServiceWorkerRegistrationId; 380 DCHECK_EQ(INITIALIZED, state_); 381 return next_registration_id_++; 382 } 383 384 int64 ServiceWorkerStorage::NewVersionId() { 385 if (state_ == DISABLED) 386 return kInvalidServiceWorkerVersionId; 387 DCHECK_EQ(INITIALIZED, state_); 388 return next_version_id_++; 389 } 390 391 int64 ServiceWorkerStorage::NewResourceId() { 392 if (state_ == DISABLED) 393 return kInvalidServiceWorkerResourceId; 394 DCHECK_EQ(INITIALIZED, state_); 395 return next_resource_id_++; 396 } 397 398 void ServiceWorkerStorage::NotifyInstallingRegistration( 399 ServiceWorkerRegistration* registration) { 400 installing_registrations_[registration->id()] = registration; 401 } 402 403 void ServiceWorkerStorage::NotifyDoneInstallingRegistration( 404 ServiceWorkerRegistration* registration, 405 ServiceWorkerVersion* version, 406 ServiceWorkerStatusCode status) { 407 installing_registrations_.erase(registration->id()); 408 if (status != SERVICE_WORKER_OK && version) { 409 ResourceList resources; 410 version->script_cache_map()->GetResources(&resources); 411 412 std::set<int64> ids; 413 for (size_t i = 0; i < resources.size(); ++i) 414 ids.insert(resources[i].resource_id); 415 416 database_task_runner_->PostTask( 417 FROM_HERE, 418 base::Bind(base::IgnoreResult( 419 &ServiceWorkerDatabase::PurgeUncommittedResourceIds), 420 base::Unretained(database_.get()), 421 ids)); 422 423 StartPurgingResources(resources); 424 } 425 } 426 427 base::FilePath ServiceWorkerStorage::GetDatabasePath() { 428 if (path_.empty()) 429 return base::FilePath(); 430 return path_.Append(kDatabaseName); 431 } 432 433 base::FilePath ServiceWorkerStorage::GetDiskCachePath() { 434 if (path_.empty()) 435 return base::FilePath(); 436 return path_.Append(kDiskCacheName); 437 } 438 439 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) { 440 if (!context_) 441 return false; 442 443 switch (state_) { 444 case INITIALIZED: 445 return true; 446 case DISABLED: 447 return false; 448 case INITIALIZING: 449 pending_tasks_.push_back(callback); 450 return false; 451 case UNINITIALIZED: 452 pending_tasks_.push_back(callback); 453 // Fall-through. 454 } 455 456 state_ = INITIALIZING; 457 database_task_runner_->PostTask( 458 FROM_HERE, 459 base::Bind(&ReadInitialDataFromDB, 460 database_.get(), 461 base::MessageLoopProxy::current(), 462 base::Bind(&ServiceWorkerStorage::DidReadInitialData, 463 weak_factory_.GetWeakPtr()))); 464 return false; 465 } 466 467 void ServiceWorkerStorage::DidReadInitialData( 468 InitialData* data, 469 ServiceWorkerDatabase::Status status) { 470 DCHECK(data); 471 DCHECK_EQ(INITIALIZING, state_); 472 473 if (status == ServiceWorkerDatabase::STATUS_OK) { 474 next_registration_id_ = data->next_registration_id; 475 next_version_id_ = data->next_version_id; 476 next_resource_id_ = data->next_resource_id; 477 registered_origins_.swap(data->origins); 478 state_ = INITIALIZED; 479 } else { 480 // TODO(nhiroki): If status==STATUS_ERROR_CORRUPTED, do corruption recovery 481 // (http://crbug.com/371675). 482 DLOG(WARNING) << "Failed to initialize: " << status; 483 state_ = DISABLED; 484 } 485 486 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin(); 487 it != pending_tasks_.end(); ++it) { 488 RunSoon(FROM_HERE, *it); 489 } 490 pending_tasks_.clear(); 491 } 492 493 void ServiceWorkerStorage::DidFindRegistrationForDocument( 494 const GURL& document_url, 495 const FindRegistrationCallback& callback, 496 const ServiceWorkerDatabase::RegistrationData& data, 497 const ResourceList& resources, 498 ServiceWorkerDatabase::Status status) { 499 if (status == ServiceWorkerDatabase::STATUS_OK) { 500 callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources)); 501 return; 502 } 503 504 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 505 // Look for something currently being installed. 506 scoped_refptr<ServiceWorkerRegistration> installing_registration = 507 FindInstallingRegistrationForDocument(document_url); 508 callback.Run(installing_registration ? 509 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND, 510 installing_registration); 511 return; 512 } 513 514 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 515 callback.Run(DatabaseStatusToStatusCode(status), 516 scoped_refptr<ServiceWorkerRegistration>()); 517 } 518 519 void ServiceWorkerStorage::DidFindRegistrationForPattern( 520 const GURL& scope, 521 const FindRegistrationCallback& callback, 522 const ServiceWorkerDatabase::RegistrationData& data, 523 const ResourceList& resources, 524 ServiceWorkerDatabase::Status status) { 525 if (status == ServiceWorkerDatabase::STATUS_OK) { 526 callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources)); 527 return; 528 } 529 530 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 531 scoped_refptr<ServiceWorkerRegistration> installing_registration = 532 FindInstallingRegistrationForPattern(scope); 533 callback.Run(installing_registration ? 534 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND, 535 installing_registration); 536 return; 537 } 538 539 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 540 callback.Run(DatabaseStatusToStatusCode(status), 541 scoped_refptr<ServiceWorkerRegistration>()); 542 } 543 544 void ServiceWorkerStorage::DidFindRegistrationForId( 545 const FindRegistrationCallback& callback, 546 const ServiceWorkerDatabase::RegistrationData& data, 547 const ResourceList& resources, 548 ServiceWorkerDatabase::Status status) { 549 if (status == ServiceWorkerDatabase::STATUS_OK) { 550 callback.Run(SERVICE_WORKER_OK, 551 GetOrCreateRegistration(data, resources)); 552 return; 553 } 554 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 555 callback.Run(DatabaseStatusToStatusCode(status), 556 scoped_refptr<ServiceWorkerRegistration>()); 557 } 558 559 void ServiceWorkerStorage::DidGetAllRegistrations( 560 const GetAllRegistrationInfosCallback& callback, 561 RegistrationList* registrations, 562 ServiceWorkerDatabase::Status status) { 563 DCHECK(registrations); 564 if (status != ServiceWorkerDatabase::STATUS_OK) { 565 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 566 callback.Run(std::vector<ServiceWorkerRegistrationInfo>()); 567 return; 568 } 569 570 // Add all stored registrations. 571 std::set<int64> pushed_registrations; 572 std::vector<ServiceWorkerRegistrationInfo> infos; 573 for (RegistrationList::const_iterator it = registrations->begin(); 574 it != registrations->end(); ++it) { 575 const bool inserted = 576 pushed_registrations.insert(it->registration_id).second; 577 DCHECK(inserted); 578 579 ServiceWorkerRegistration* registration = 580 context_->GetLiveRegistration(it->registration_id); 581 if (registration) { 582 infos.push_back(registration->GetInfo()); 583 continue; 584 } 585 586 ServiceWorkerRegistrationInfo info; 587 info.pattern = it->scope; 588 info.script_url = it->script; 589 info.registration_id = it->registration_id; 590 if (ServiceWorkerVersion* version = 591 context_->GetLiveVersion(it->version_id)) { 592 if (it->is_active) 593 info.active_version = version->GetInfo(); 594 else 595 info.waiting_version = version->GetInfo(); 596 infos.push_back(info); 597 continue; 598 } 599 600 if (it->is_active) { 601 info.active_version.is_null = false; 602 info.active_version.status = ServiceWorkerVersion::ACTIVE; 603 info.active_version.version_id = it->version_id; 604 } else { 605 info.waiting_version.is_null = false; 606 info.waiting_version.status = ServiceWorkerVersion::INSTALLED; 607 info.waiting_version.version_id = it->version_id; 608 } 609 infos.push_back(info); 610 } 611 612 // Add unstored registrations that are being installed. 613 for (RegistrationRefsById::const_iterator it = 614 installing_registrations_.begin(); 615 it != installing_registrations_.end(); ++it) { 616 if (pushed_registrations.insert(it->first).second) 617 infos.push_back(it->second->GetInfo()); 618 } 619 620 callback.Run(infos); 621 } 622 623 void ServiceWorkerStorage::DidStoreRegistration( 624 const StatusCallback& callback, 625 const GURL& origin, 626 const std::vector<int64>& newly_purgeable_resources, 627 ServiceWorkerDatabase::Status status) { 628 if (status != ServiceWorkerDatabase::STATUS_OK) { 629 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 630 callback.Run(DatabaseStatusToStatusCode(status)); 631 return; 632 } 633 registered_origins_.insert(origin); 634 callback.Run(SERVICE_WORKER_OK); 635 StartPurgingResources(newly_purgeable_resources); 636 } 637 638 void ServiceWorkerStorage::DidUpdateToActiveState( 639 const StatusCallback& callback, 640 ServiceWorkerDatabase::Status status) { 641 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 642 callback.Run(DatabaseStatusToStatusCode(status)); 643 } 644 645 void ServiceWorkerStorage::DidDeleteRegistration( 646 const GURL& origin, 647 const StatusCallback& callback, 648 bool origin_is_deletable, 649 const std::vector<int64>& newly_purgeable_resources, 650 ServiceWorkerDatabase::Status status) { 651 if (status != ServiceWorkerDatabase::STATUS_OK) { 652 // TODO(nhiroki): Handle database error (http://crbug.com/371675). 653 callback.Run(DatabaseStatusToStatusCode(status)); 654 return; 655 } 656 if (origin_is_deletable) 657 registered_origins_.erase(origin); 658 callback.Run(SERVICE_WORKER_OK); 659 StartPurgingResources(newly_purgeable_resources); 660 } 661 662 scoped_refptr<ServiceWorkerRegistration> 663 ServiceWorkerStorage::GetOrCreateRegistration( 664 const ServiceWorkerDatabase::RegistrationData& data, 665 const ResourceList& resources) { 666 scoped_refptr<ServiceWorkerRegistration> registration = 667 context_->GetLiveRegistration(data.registration_id); 668 if (registration) 669 return registration; 670 671 registration = new ServiceWorkerRegistration( 672 data.scope, data.script, data.registration_id, context_); 673 scoped_refptr<ServiceWorkerVersion> version = 674 context_->GetLiveVersion(data.version_id); 675 if (!version) { 676 version = new ServiceWorkerVersion(registration, data.version_id, context_); 677 version->SetStatus(data.is_active ? 678 ServiceWorkerVersion::ACTIVE : ServiceWorkerVersion::INSTALLED); 679 version->script_cache_map()->SetResources(resources); 680 } 681 682 if (version->status() == ServiceWorkerVersion::ACTIVE) 683 registration->set_active_version(version); 684 else if (version->status() == ServiceWorkerVersion::INSTALLED) 685 registration->set_waiting_version(version); 686 else 687 NOTREACHED(); 688 // TODO(michaeln): Hmmm, what if DeleteReg was invoked after 689 // the Find result we're returning here? NOTREACHED condition? 690 return registration; 691 } 692 693 ServiceWorkerRegistration* 694 ServiceWorkerStorage::FindInstallingRegistrationForDocument( 695 const GURL& document_url) { 696 DCHECK(!document_url.has_ref()); 697 698 LongestScopeMatcher matcher(document_url); 699 ServiceWorkerRegistration* match = NULL; 700 701 // TODO(nhiroki): This searches over installing registrations linearly and it 702 // couldn't be scalable. Maybe the regs should be partitioned by origin. 703 for (RegistrationRefsById::const_iterator it = 704 installing_registrations_.begin(); 705 it != installing_registrations_.end(); ++it) { 706 if (matcher.MatchLongest(it->second->pattern())) 707 match = it->second; 708 } 709 return match; 710 } 711 712 ServiceWorkerRegistration* 713 ServiceWorkerStorage::FindInstallingRegistrationForPattern( 714 const GURL& scope) { 715 for (RegistrationRefsById::const_iterator it = 716 installing_registrations_.begin(); 717 it != installing_registrations_.end(); ++it) { 718 if (it->second->pattern() == scope) 719 return it->second; 720 } 721 return NULL; 722 } 723 724 ServiceWorkerRegistration* 725 ServiceWorkerStorage::FindInstallingRegistrationForId( 726 int64 registration_id) { 727 RegistrationRefsById::const_iterator found = 728 installing_registrations_.find(registration_id); 729 if (found == installing_registrations_.end()) 730 return NULL; 731 return found->second; 732 } 733 734 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() { 735 if (disk_cache_) 736 return disk_cache_.get(); 737 738 disk_cache_.reset(new ServiceWorkerDiskCache); 739 740 base::FilePath path = GetDiskCachePath(); 741 if (path.empty()) { 742 int rv = disk_cache_->InitWithMemBackend( 743 kMaxMemDiskCacheSize, 744 base::Bind(&EmptyCompletionCallback)); 745 DCHECK_EQ(net::OK, rv); 746 return disk_cache_.get(); 747 } 748 749 int rv = disk_cache_->InitWithDiskBackend( 750 path, kMaxDiskCacheSize, false, 751 disk_cache_thread_.get(), 752 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized, 753 weak_factory_.GetWeakPtr())); 754 if (rv != net::ERR_IO_PENDING) 755 OnDiskCacheInitialized(rv); 756 757 return disk_cache_.get(); 758 } 759 760 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) { 761 if (rv != net::OK) { 762 LOG(ERROR) << "Failed to open the serviceworker diskcache: " 763 << net::ErrorToString(rv); 764 // TODO(michaeln): DeleteAndStartOver() 765 disk_cache_->Disable(); 766 state_ = DISABLED; 767 } 768 ServiceWorkerHistograms::CountInitDiskCacheResult(rv == net::OK); 769 } 770 771 void ServiceWorkerStorage::StartPurgingResources( 772 const std::vector<int64>& ids) { 773 for (size_t i = 0; i < ids.size(); ++i) 774 purgeable_reource_ids_.push_back(ids[i]); 775 ContinuePurgingResources(); 776 } 777 778 void ServiceWorkerStorage::StartPurgingResources( 779 const ResourceList& resources) { 780 for (size_t i = 0; i < resources.size(); ++i) 781 purgeable_reource_ids_.push_back(resources[i].resource_id); 782 ContinuePurgingResources(); 783 } 784 785 void ServiceWorkerStorage::ContinuePurgingResources() { 786 if (purgeable_reource_ids_.empty() || is_purge_pending_) 787 return; 788 789 // Do one at a time until we're done, use RunSoon to avoid recursion when 790 // DoomEntry returns immediately. 791 is_purge_pending_ = true; 792 int64 id = purgeable_reource_ids_.front(); 793 purgeable_reource_ids_.pop_front(); 794 RunSoon(FROM_HERE, 795 base::Bind(&ServiceWorkerStorage::PurgeResource, 796 weak_factory_.GetWeakPtr(), id)); 797 } 798 799 void ServiceWorkerStorage::PurgeResource(int64 id) { 800 DCHECK(is_purge_pending_); 801 int rv = disk_cache()->DoomEntry( 802 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged, 803 weak_factory_.GetWeakPtr(), id)); 804 if (rv != net::ERR_IO_PENDING) 805 OnResourcePurged(id, rv); 806 } 807 808 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) { 809 DCHECK(is_purge_pending_); 810 is_purge_pending_ = false; 811 812 database_task_runner_->PostTask( 813 FROM_HERE, 814 base::Bind(base::IgnoreResult( 815 &ServiceWorkerDatabase::ClearPurgeableResourceIds), 816 base::Unretained(database_.get()), 817 std::set<int64>(&id, &id + 1))); 818 819 ContinuePurgingResources(); 820 } 821 822 void ServiceWorkerStorage::ReadInitialDataFromDB( 823 ServiceWorkerDatabase* database, 824 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 825 const InitializeCallback& callback) { 826 DCHECK(database); 827 scoped_ptr<ServiceWorkerStorage::InitialData> data( 828 new ServiceWorkerStorage::InitialData()); 829 830 ServiceWorkerDatabase::Status status = 831 database->GetNextAvailableIds(&data->next_registration_id, 832 &data->next_version_id, 833 &data->next_resource_id); 834 if (status != ServiceWorkerDatabase::STATUS_OK) { 835 original_task_runner->PostTask( 836 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status)); 837 return; 838 } 839 840 status = database->GetOriginsWithRegistrations(&data->origins); 841 original_task_runner->PostTask( 842 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status)); 843 } 844 845 void ServiceWorkerStorage::DeleteRegistrationFromDB( 846 ServiceWorkerDatabase* database, 847 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 848 int64 registration_id, 849 const GURL& origin, 850 const DeleteRegistrationCallback& callback) { 851 DCHECK(database); 852 853 std::vector<int64> newly_purgeable_resources; 854 ServiceWorkerDatabase::Status status = 855 database->DeleteRegistration(registration_id, origin, 856 &newly_purgeable_resources); 857 if (status != ServiceWorkerDatabase::STATUS_OK) { 858 original_task_runner->PostTask( 859 FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status)); 860 return; 861 } 862 863 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the 864 // unique origin list. 865 std::vector<ServiceWorkerDatabase::RegistrationData> registrations; 866 status = database->GetRegistrationsForOrigin(origin, ®istrations); 867 if (status != ServiceWorkerDatabase::STATUS_OK) { 868 original_task_runner->PostTask( 869 FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status)); 870 return; 871 } 872 873 bool deletable = registrations.empty(); 874 original_task_runner->PostTask( 875 FROM_HERE, base::Bind(callback, deletable, 876 newly_purgeable_resources, status)); 877 } 878 879 void ServiceWorkerStorage::WriteRegistrationInDB( 880 ServiceWorkerDatabase* database, 881 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 882 const ServiceWorkerDatabase::RegistrationData& data, 883 const ResourceList& resources, 884 const WriteRegistrationCallback& callback) { 885 DCHECK(database); 886 std::vector<int64> newly_purgeable_resources; 887 ServiceWorkerDatabase::Status status = 888 database->WriteRegistration(data, resources, &newly_purgeable_resources); 889 original_task_runner->PostTask( 890 FROM_HERE, 891 base::Bind(callback, data.script.GetOrigin(), 892 newly_purgeable_resources, status)); 893 } 894 895 void ServiceWorkerStorage::FindForDocumentInDB( 896 ServiceWorkerDatabase* database, 897 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 898 const GURL& document_url, 899 const FindInDBCallback& callback) { 900 GURL origin = document_url.GetOrigin(); 901 RegistrationList registrations; 902 ServiceWorkerDatabase::Status status = 903 database->GetRegistrationsForOrigin(origin, ®istrations); 904 if (status != ServiceWorkerDatabase::STATUS_OK) { 905 original_task_runner->PostTask( 906 FROM_HERE, 907 base::Bind(callback, 908 ServiceWorkerDatabase::RegistrationData(), 909 ResourceList(), 910 status)); 911 return; 912 } 913 914 ServiceWorkerDatabase::RegistrationData data; 915 ResourceList resources; 916 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; 917 918 // Find one with a pattern match. 919 LongestScopeMatcher matcher(document_url); 920 int64 match = kInvalidServiceWorkerRegistrationId; 921 for (size_t i = 0; i < registrations.size(); ++i) { 922 if (matcher.MatchLongest(registrations[i].scope)) 923 match = registrations[i].registration_id; 924 } 925 926 if (match != kInvalidServiceWorkerRegistrationId) 927 status = database->ReadRegistration(match, origin, &data, &resources); 928 929 original_task_runner->PostTask( 930 FROM_HERE, 931 base::Bind(callback, data, resources, status)); 932 } 933 934 void ServiceWorkerStorage::FindForPatternInDB( 935 ServiceWorkerDatabase* database, 936 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 937 const GURL& scope, 938 const FindInDBCallback& callback) { 939 GURL origin = scope.GetOrigin(); 940 std::vector<ServiceWorkerDatabase::RegistrationData> registrations; 941 ServiceWorkerDatabase::Status status = 942 database->GetRegistrationsForOrigin(origin, ®istrations); 943 if (status != ServiceWorkerDatabase::STATUS_OK) { 944 original_task_runner->PostTask( 945 FROM_HERE, 946 base::Bind(callback, 947 ServiceWorkerDatabase::RegistrationData(), 948 ResourceList(), 949 status)); 950 return; 951 } 952 953 // Find one with an exact matching scope. 954 ServiceWorkerDatabase::RegistrationData data; 955 ResourceList resources; 956 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; 957 for (RegistrationList::const_iterator it = registrations.begin(); 958 it != registrations.end(); ++it) { 959 if (scope != it->scope) 960 continue; 961 status = database->ReadRegistration(it->registration_id, origin, 962 &data, &resources); 963 break; // We're done looping. 964 } 965 966 original_task_runner->PostTask( 967 FROM_HERE, 968 base::Bind(callback, data, resources, status)); 969 } 970 971 void ServiceWorkerStorage::FindForIdInDB( 972 ServiceWorkerDatabase* database, 973 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 974 int64 registration_id, 975 const GURL& origin, 976 const FindInDBCallback& callback) { 977 ServiceWorkerDatabase::RegistrationData data; 978 ResourceList resources; 979 ServiceWorkerDatabase::Status status = 980 database->ReadRegistration(registration_id, origin, &data, &resources); 981 original_task_runner->PostTask( 982 FROM_HERE, base::Bind(callback, data, resources, status)); 983 } 984 985 } // namespace content 986