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/debug/trace_event.h" 11 #include "base/files/file_util.h" 12 #include "base/hash.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/sequenced_task_runner.h" 15 #include "base/single_thread_task_runner.h" 16 #include "base/task_runner_util.h" 17 #include "content/browser/service_worker/service_worker_context_core.h" 18 #include "content/browser/service_worker/service_worker_disk_cache.h" 19 #include "content/browser/service_worker/service_worker_info.h" 20 #include "content/browser/service_worker/service_worker_metrics.h" 21 #include "content/browser/service_worker/service_worker_registration.h" 22 #include "content/browser/service_worker/service_worker_utils.h" 23 #include "content/browser/service_worker/service_worker_version.h" 24 #include "content/common/service_worker/service_worker_types.h" 25 #include "content/public/browser/browser_thread.h" 26 #include "net/base/completion_callback.h" 27 #include "net/base/io_buffer.h" 28 #include "net/base/net_errors.h" 29 #include "storage/browser/quota/quota_manager_proxy.h" 30 31 namespace content { 32 33 namespace { 34 35 void RunSoon(const tracked_objects::Location& from_here, 36 const base::Closure& closure) { 37 base::MessageLoop::current()->PostTask(from_here, closure); 38 } 39 40 void CompleteFindNow( 41 const scoped_refptr<ServiceWorkerRegistration>& registration, 42 ServiceWorkerStatusCode status, 43 const ServiceWorkerStorage::FindRegistrationCallback& callback) { 44 callback.Run(status, registration); 45 } 46 47 void CompleteFindSoon( 48 const tracked_objects::Location& from_here, 49 const scoped_refptr<ServiceWorkerRegistration>& registration, 50 ServiceWorkerStatusCode status, 51 const ServiceWorkerStorage::FindRegistrationCallback& callback) { 52 RunSoon(from_here, base::Bind(callback, status, registration)); 53 } 54 55 const base::FilePath::CharType kDatabaseName[] = 56 FILE_PATH_LITERAL("Database"); 57 const base::FilePath::CharType kDiskCacheName[] = 58 FILE_PATH_LITERAL("Cache"); 59 60 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024; 61 const int kMaxDiskCacheSize = 250 * 1024 * 1024; 62 63 ServiceWorkerStatusCode DatabaseStatusToStatusCode( 64 ServiceWorkerDatabase::Status status) { 65 switch (status) { 66 case ServiceWorkerDatabase::STATUS_OK: 67 return SERVICE_WORKER_OK; 68 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND: 69 return SERVICE_WORKER_ERROR_NOT_FOUND; 70 case ServiceWorkerDatabase::STATUS_ERROR_MAX: 71 NOTREACHED(); 72 default: 73 return SERVICE_WORKER_ERROR_FAILED; 74 } 75 } 76 77 class ResponseComparer : public base::RefCounted<ResponseComparer> { 78 public: 79 ResponseComparer( 80 base::WeakPtr<ServiceWorkerStorage> owner, 81 scoped_ptr<ServiceWorkerResponseReader> lhs, 82 scoped_ptr<ServiceWorkerResponseReader> rhs, 83 const ServiceWorkerStorage::CompareCallback& callback) 84 : owner_(owner), 85 completion_callback_(callback), 86 lhs_reader_(lhs.release()), 87 rhs_reader_(rhs.release()), 88 completion_count_(0), 89 previous_result_(0) { 90 } 91 92 void Start(); 93 94 private: 95 friend class base::RefCounted<ResponseComparer>; 96 97 static const int kBufferSize = 16 * 1024; 98 99 ~ResponseComparer() {} 100 void ReadInfos(); 101 void OnReadInfoComplete(int result); 102 void ReadSomeData(); 103 void OnReadDataComplete(int result); 104 105 base::WeakPtr<ServiceWorkerStorage> owner_; 106 ServiceWorkerStorage::CompareCallback completion_callback_; 107 scoped_ptr<ServiceWorkerResponseReader> lhs_reader_; 108 scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_; 109 scoped_refptr<net::IOBuffer> lhs_buffer_; 110 scoped_ptr<ServiceWorkerResponseReader> rhs_reader_; 111 scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_; 112 scoped_refptr<net::IOBuffer> rhs_buffer_; 113 int completion_count_; 114 int previous_result_; 115 DISALLOW_COPY_AND_ASSIGN(ResponseComparer); 116 }; 117 118 void ResponseComparer::Start() { 119 lhs_buffer_ = new net::IOBuffer(kBufferSize); 120 lhs_info_ = new HttpResponseInfoIOBuffer(); 121 rhs_buffer_ = new net::IOBuffer(kBufferSize); 122 rhs_info_ = new HttpResponseInfoIOBuffer(); 123 124 ReadInfos(); 125 } 126 127 void ResponseComparer::ReadInfos() { 128 lhs_reader_->ReadInfo( 129 lhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this)); 130 rhs_reader_->ReadInfo( 131 rhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this)); 132 } 133 134 void ResponseComparer::OnReadInfoComplete(int result) { 135 if (completion_callback_.is_null() || !owner_) 136 return; 137 if (result < 0) { 138 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false); 139 completion_callback_.Reset(); 140 return; 141 } 142 if (++completion_count_ != 2) 143 return; 144 145 if (lhs_info_->response_data_size != rhs_info_->response_data_size) { 146 completion_callback_.Run(SERVICE_WORKER_OK, false); 147 return; 148 } 149 ReadSomeData(); 150 } 151 152 void ResponseComparer::ReadSomeData() { 153 completion_count_ = 0; 154 lhs_reader_->ReadData( 155 lhs_buffer_.get(), 156 kBufferSize, 157 base::Bind(&ResponseComparer::OnReadDataComplete, this)); 158 rhs_reader_->ReadData( 159 rhs_buffer_.get(), 160 kBufferSize, 161 base::Bind(&ResponseComparer::OnReadDataComplete, this)); 162 } 163 164 void ResponseComparer::OnReadDataComplete(int result) { 165 if (completion_callback_.is_null() || !owner_) 166 return; 167 if (result < 0) { 168 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false); 169 completion_callback_.Reset(); 170 return; 171 } 172 if (++completion_count_ != 2) { 173 previous_result_ = result; 174 return; 175 } 176 177 // TODO(michaeln): Probably shouldn't assume that the amounts read from 178 // each reader will always be the same. This would wrongly signal false 179 // in that case. 180 if (result != previous_result_) { 181 completion_callback_.Run(SERVICE_WORKER_OK, false); 182 return; 183 } 184 185 if (result == 0) { 186 completion_callback_.Run(SERVICE_WORKER_OK, true); 187 return; 188 } 189 190 int compare_result = 191 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result); 192 if (compare_result != 0) { 193 completion_callback_.Run(SERVICE_WORKER_OK, false); 194 return; 195 } 196 197 ReadSomeData(); 198 } 199 200 } // namespace 201 202 ServiceWorkerStorage::InitialData::InitialData() 203 : next_registration_id(kInvalidServiceWorkerRegistrationId), 204 next_version_id(kInvalidServiceWorkerVersionId), 205 next_resource_id(kInvalidServiceWorkerResourceId) { 206 } 207 208 ServiceWorkerStorage::InitialData::~InitialData() { 209 } 210 211 ServiceWorkerStorage:: 212 DidDeleteRegistrationParams::DidDeleteRegistrationParams() 213 : registration_id(kInvalidServiceWorkerRegistrationId) { 214 } 215 216 ServiceWorkerStorage:: 217 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() { 218 } 219 220 ServiceWorkerStorage::~ServiceWorkerStorage() { 221 weak_factory_.InvalidateWeakPtrs(); 222 database_task_runner_->DeleteSoon(FROM_HERE, database_.release()); 223 } 224 225 // static 226 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create( 227 const base::FilePath& path, 228 base::WeakPtr<ServiceWorkerContextCore> context, 229 const scoped_refptr<base::SequencedTaskRunner>& database_task_runner, 230 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread, 231 storage::QuotaManagerProxy* quota_manager_proxy) { 232 return make_scoped_ptr(new ServiceWorkerStorage(path, 233 context, 234 database_task_runner, 235 disk_cache_thread, 236 quota_manager_proxy)); 237 } 238 239 // static 240 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create( 241 base::WeakPtr<ServiceWorkerContextCore> context, 242 ServiceWorkerStorage* old_storage) { 243 return make_scoped_ptr( 244 new ServiceWorkerStorage(old_storage->path_, 245 context, 246 old_storage->database_task_runner_, 247 old_storage->disk_cache_thread_, 248 old_storage->quota_manager_proxy_.get())); 249 } 250 251 void ServiceWorkerStorage::FindRegistrationForDocument( 252 const GURL& document_url, 253 const FindRegistrationCallback& callback) { 254 DCHECK(!document_url.has_ref()); 255 TRACE_EVENT_ASYNC_BEGIN1( 256 "ServiceWorker", 257 "ServiceWorkerStorage::FindRegistrationForDocument", 258 base::Hash(document_url.spec()), 259 "URL", document_url.spec()); 260 if (!LazyInitialize(base::Bind( 261 &ServiceWorkerStorage::FindRegistrationForDocument, 262 weak_factory_.GetWeakPtr(), document_url, callback))) { 263 if (state_ != INITIALIZING || !context_) { 264 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(), 265 SERVICE_WORKER_ERROR_FAILED, callback); 266 } 267 return; 268 } 269 DCHECK_EQ(INITIALIZED, state_); 270 271 // See if there are any stored registrations for the origin. 272 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) { 273 // Look for something currently being installed. 274 scoped_refptr<ServiceWorkerRegistration> installing_registration = 275 FindInstallingRegistrationForDocument(document_url); 276 CompleteFindNow(installing_registration, 277 installing_registration.get() 278 ? SERVICE_WORKER_OK 279 : SERVICE_WORKER_ERROR_NOT_FOUND, 280 callback); 281 return; 282 } 283 284 database_task_runner_->PostTask( 285 FROM_HERE, 286 base::Bind( 287 &FindForDocumentInDB, 288 database_.get(), 289 base::MessageLoopProxy::current(), 290 document_url, 291 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument, 292 weak_factory_.GetWeakPtr(), document_url, callback))); 293 } 294 295 void ServiceWorkerStorage::FindRegistrationForPattern( 296 const GURL& scope, 297 const FindRegistrationCallback& callback) { 298 if (!LazyInitialize(base::Bind( 299 &ServiceWorkerStorage::FindRegistrationForPattern, 300 weak_factory_.GetWeakPtr(), scope, callback))) { 301 if (state_ != INITIALIZING || !context_) { 302 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(), 303 SERVICE_WORKER_ERROR_FAILED, callback); 304 } 305 return; 306 } 307 DCHECK_EQ(INITIALIZED, state_); 308 309 // See if there are any stored registrations for the origin. 310 if (!ContainsKey(registered_origins_, scope.GetOrigin())) { 311 // Look for something currently being installed. 312 scoped_refptr<ServiceWorkerRegistration> installing_registration = 313 FindInstallingRegistrationForPattern(scope); 314 CompleteFindSoon(FROM_HERE, 315 installing_registration, 316 installing_registration.get() 317 ? SERVICE_WORKER_OK 318 : SERVICE_WORKER_ERROR_NOT_FOUND, 319 callback); 320 return; 321 } 322 323 database_task_runner_->PostTask( 324 FROM_HERE, 325 base::Bind( 326 &FindForPatternInDB, 327 database_.get(), 328 base::MessageLoopProxy::current(), 329 scope, 330 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern, 331 weak_factory_.GetWeakPtr(), scope, callback))); 332 } 333 334 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration( 335 const GURL& scope) { 336 if (state_ != INITIALIZED || !context_) 337 return NULL; 338 for (RegistrationRefsById::const_iterator it = 339 uninstalling_registrations_.begin(); 340 it != uninstalling_registrations_.end(); 341 ++it) { 342 if (it->second->pattern() == scope) { 343 DCHECK(it->second->is_uninstalling()); 344 return it->second.get(); 345 } 346 } 347 return NULL; 348 } 349 350 void ServiceWorkerStorage::FindRegistrationForId( 351 int64 registration_id, 352 const GURL& origin, 353 const FindRegistrationCallback& callback) { 354 if (!LazyInitialize(base::Bind( 355 &ServiceWorkerStorage::FindRegistrationForId, 356 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) { 357 if (state_ != INITIALIZING || !context_) { 358 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(), 359 SERVICE_WORKER_ERROR_FAILED, callback); 360 } 361 return; 362 } 363 DCHECK_EQ(INITIALIZED, state_); 364 365 // See if there are any stored registrations for the origin. 366 if (!ContainsKey(registered_origins_, origin)) { 367 // Look for something currently being installed. 368 scoped_refptr<ServiceWorkerRegistration> installing_registration = 369 FindInstallingRegistrationForId(registration_id); 370 CompleteFindNow(installing_registration, 371 installing_registration.get() 372 ? SERVICE_WORKER_OK 373 : SERVICE_WORKER_ERROR_NOT_FOUND, 374 callback); 375 return; 376 } 377 378 scoped_refptr<ServiceWorkerRegistration> registration = 379 context_->GetLiveRegistration(registration_id); 380 if (registration.get()) { 381 CompleteFindNow(registration, SERVICE_WORKER_OK, callback); 382 return; 383 } 384 385 database_task_runner_->PostTask( 386 FROM_HERE, 387 base::Bind(&FindForIdInDB, 388 database_.get(), 389 base::MessageLoopProxy::current(), 390 registration_id, origin, 391 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId, 392 weak_factory_.GetWeakPtr(), callback))); 393 } 394 395 void ServiceWorkerStorage::GetAllRegistrations( 396 const GetAllRegistrationInfosCallback& callback) { 397 if (!LazyInitialize(base::Bind( 398 &ServiceWorkerStorage::GetAllRegistrations, 399 weak_factory_.GetWeakPtr(), callback))) { 400 if (state_ != INITIALIZING || !context_) { 401 RunSoon(FROM_HERE, base::Bind( 402 callback, std::vector<ServiceWorkerRegistrationInfo>())); 403 } 404 return; 405 } 406 DCHECK_EQ(INITIALIZED, state_); 407 408 RegistrationList* registrations = new RegistrationList; 409 PostTaskAndReplyWithResult( 410 database_task_runner_.get(), 411 FROM_HERE, 412 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations, 413 base::Unretained(database_.get()), 414 base::Unretained(registrations)), 415 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations, 416 weak_factory_.GetWeakPtr(), 417 callback, 418 base::Owned(registrations))); 419 } 420 421 void ServiceWorkerStorage::StoreRegistration( 422 ServiceWorkerRegistration* registration, 423 ServiceWorkerVersion* version, 424 const StatusCallback& callback) { 425 DCHECK(registration); 426 DCHECK(version); 427 428 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_; 429 if (IsDisabled() || !context_) { 430 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 431 return; 432 } 433 434 ServiceWorkerDatabase::RegistrationData data; 435 data.registration_id = registration->id(); 436 data.scope = registration->pattern(); 437 data.script = version->script_url(); 438 data.has_fetch_handler = true; 439 data.version_id = version->version_id(); 440 data.last_update_check = registration->last_update_check(); 441 data.is_active = (version == registration->active_version()); 442 443 ResourceList resources; 444 version->script_cache_map()->GetResources(&resources); 445 446 if (!has_checked_for_stale_resources_) 447 DeleteStaleResources(); 448 449 database_task_runner_->PostTask( 450 FROM_HERE, 451 base::Bind(&WriteRegistrationInDB, 452 database_.get(), 453 base::MessageLoopProxy::current(), 454 data, resources, 455 base::Bind(&ServiceWorkerStorage::DidStoreRegistration, 456 weak_factory_.GetWeakPtr(), 457 callback))); 458 459 registration->set_is_deleted(false); 460 } 461 462 void ServiceWorkerStorage::UpdateToActiveState( 463 ServiceWorkerRegistration* registration, 464 const StatusCallback& callback) { 465 DCHECK(registration); 466 467 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_; 468 if (IsDisabled() || !context_) { 469 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 470 return; 471 } 472 473 PostTaskAndReplyWithResult( 474 database_task_runner_.get(), 475 FROM_HERE, 476 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive, 477 base::Unretained(database_.get()), 478 registration->id(), 479 registration->pattern().GetOrigin()), 480 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState, 481 weak_factory_.GetWeakPtr(), 482 callback)); 483 } 484 485 void ServiceWorkerStorage::UpdateLastUpdateCheckTime( 486 ServiceWorkerRegistration* registration) { 487 DCHECK(registration); 488 489 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_; 490 if (IsDisabled() || !context_) 491 return; 492 493 database_task_runner_->PostTask( 494 FROM_HERE, 495 base::Bind( 496 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime), 497 base::Unretained(database_.get()), 498 registration->id(), 499 registration->pattern().GetOrigin(), 500 registration->last_update_check())); 501 } 502 503 void ServiceWorkerStorage::DeleteRegistration( 504 int64 registration_id, 505 const GURL& origin, 506 const StatusCallback& callback) { 507 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_; 508 if (IsDisabled() || !context_) { 509 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); 510 return; 511 } 512 513 if (!has_checked_for_stale_resources_) 514 DeleteStaleResources(); 515 516 DidDeleteRegistrationParams params; 517 params.registration_id = registration_id; 518 params.origin = origin; 519 params.callback = callback; 520 521 database_task_runner_->PostTask( 522 FROM_HERE, 523 base::Bind(&DeleteRegistrationFromDB, 524 database_.get(), 525 base::MessageLoopProxy::current(), 526 registration_id, origin, 527 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration, 528 weak_factory_.GetWeakPtr(), params))); 529 530 // The registration should no longer be findable. 531 pending_deletions_.insert(registration_id); 532 ServiceWorkerRegistration* registration = 533 context_->GetLiveRegistration(registration_id); 534 if (registration) 535 registration->set_is_deleted(true); 536 } 537 538 scoped_ptr<ServiceWorkerResponseReader> 539 ServiceWorkerStorage::CreateResponseReader(int64 response_id) { 540 return make_scoped_ptr( 541 new ServiceWorkerResponseReader(response_id, disk_cache())); 542 } 543 544 scoped_ptr<ServiceWorkerResponseWriter> 545 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) { 546 return make_scoped_ptr( 547 new ServiceWorkerResponseWriter(response_id, disk_cache())); 548 } 549 550 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) { 551 DCHECK_NE(kInvalidServiceWorkerResponseId, id); 552 DCHECK_EQ(INITIALIZED, state_); 553 554 if (!has_checked_for_stale_resources_) 555 DeleteStaleResources(); 556 557 database_task_runner_->PostTask( 558 FROM_HERE, 559 base::Bind(base::IgnoreResult( 560 &ServiceWorkerDatabase::WriteUncommittedResourceIds), 561 base::Unretained(database_.get()), 562 std::set<int64>(&id, &id + 1))); 563 } 564 565 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) { 566 DCHECK_NE(kInvalidServiceWorkerResponseId, id); 567 database_task_runner_->PostTask( 568 FROM_HERE, 569 base::Bind(base::IgnoreResult( 570 &ServiceWorkerDatabase::PurgeUncommittedResourceIds), 571 base::Unretained(database_.get()), 572 std::set<int64>(&id, &id + 1))); 573 StartPurgingResources(std::vector<int64>(1, id)); 574 } 575 576 void ServiceWorkerStorage::CompareScriptResources( 577 int64 lhs_id, int64 rhs_id, 578 const CompareCallback& callback) { 579 DCHECK(!callback.is_null()); 580 scoped_refptr<ResponseComparer> comparer = 581 new ResponseComparer(weak_factory_.GetWeakPtr(), 582 CreateResponseReader(lhs_id), 583 CreateResponseReader(rhs_id), 584 callback); 585 comparer->Start(); // It deletes itself when done. 586 } 587 588 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) { 589 Disable(); 590 591 // Delete the database on the database thread. 592 PostTaskAndReplyWithResult( 593 database_task_runner_.get(), 594 FROM_HERE, 595 base::Bind(&ServiceWorkerDatabase::DestroyDatabase, 596 base::Unretained(database_.get())), 597 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase, 598 weak_factory_.GetWeakPtr(), 599 callback)); 600 } 601 602 int64 ServiceWorkerStorage::NewRegistrationId() { 603 if (state_ == DISABLED) 604 return kInvalidServiceWorkerRegistrationId; 605 DCHECK_EQ(INITIALIZED, state_); 606 return next_registration_id_++; 607 } 608 609 int64 ServiceWorkerStorage::NewVersionId() { 610 if (state_ == DISABLED) 611 return kInvalidServiceWorkerVersionId; 612 DCHECK_EQ(INITIALIZED, state_); 613 return next_version_id_++; 614 } 615 616 int64 ServiceWorkerStorage::NewResourceId() { 617 if (state_ == DISABLED) 618 return kInvalidServiceWorkerResourceId; 619 DCHECK_EQ(INITIALIZED, state_); 620 return next_resource_id_++; 621 } 622 623 void ServiceWorkerStorage::NotifyInstallingRegistration( 624 ServiceWorkerRegistration* registration) { 625 DCHECK(installing_registrations_.find(registration->id()) == 626 installing_registrations_.end()); 627 installing_registrations_[registration->id()] = registration; 628 } 629 630 void ServiceWorkerStorage::NotifyDoneInstallingRegistration( 631 ServiceWorkerRegistration* registration, 632 ServiceWorkerVersion* version, 633 ServiceWorkerStatusCode status) { 634 installing_registrations_.erase(registration->id()); 635 if (status != SERVICE_WORKER_OK && version) { 636 ResourceList resources; 637 version->script_cache_map()->GetResources(&resources); 638 639 std::set<int64> ids; 640 for (size_t i = 0; i < resources.size(); ++i) 641 ids.insert(resources[i].resource_id); 642 643 database_task_runner_->PostTask( 644 FROM_HERE, 645 base::Bind(base::IgnoreResult( 646 &ServiceWorkerDatabase::PurgeUncommittedResourceIds), 647 base::Unretained(database_.get()), 648 ids)); 649 } 650 } 651 652 void ServiceWorkerStorage::NotifyUninstallingRegistration( 653 ServiceWorkerRegistration* registration) { 654 DCHECK(uninstalling_registrations_.find(registration->id()) == 655 uninstalling_registrations_.end()); 656 uninstalling_registrations_[registration->id()] = registration; 657 } 658 659 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration( 660 ServiceWorkerRegistration* registration) { 661 uninstalling_registrations_.erase(registration->id()); 662 } 663 664 void ServiceWorkerStorage::Disable() { 665 state_ = DISABLED; 666 if (disk_cache_) 667 disk_cache_->Disable(); 668 } 669 670 bool ServiceWorkerStorage::IsDisabled() const { 671 return state_ == DISABLED; 672 } 673 674 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) { 675 if (!has_checked_for_stale_resources_) 676 DeleteStaleResources(); 677 StartPurgingResources(resources); 678 } 679 680 ServiceWorkerStorage::ServiceWorkerStorage( 681 const base::FilePath& path, 682 base::WeakPtr<ServiceWorkerContextCore> context, 683 const scoped_refptr<base::SequencedTaskRunner>& database_task_runner, 684 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread, 685 storage::QuotaManagerProxy* quota_manager_proxy) 686 : next_registration_id_(kInvalidServiceWorkerRegistrationId), 687 next_version_id_(kInvalidServiceWorkerVersionId), 688 next_resource_id_(kInvalidServiceWorkerResourceId), 689 state_(UNINITIALIZED), 690 path_(path), 691 context_(context), 692 database_task_runner_(database_task_runner), 693 disk_cache_thread_(disk_cache_thread), 694 quota_manager_proxy_(quota_manager_proxy), 695 is_purge_pending_(false), 696 has_checked_for_stale_resources_(false), 697 weak_factory_(this) { 698 database_.reset(new ServiceWorkerDatabase(GetDatabasePath())); 699 } 700 701 base::FilePath ServiceWorkerStorage::GetDatabasePath() { 702 if (path_.empty()) 703 return base::FilePath(); 704 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) 705 .Append(kDatabaseName); 706 } 707 708 base::FilePath ServiceWorkerStorage::GetDiskCachePath() { 709 if (path_.empty()) 710 return base::FilePath(); 711 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) 712 .Append(kDiskCacheName); 713 } 714 715 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) { 716 if (!context_) 717 return false; 718 719 switch (state_) { 720 case INITIALIZED: 721 return true; 722 case DISABLED: 723 return false; 724 case INITIALIZING: 725 pending_tasks_.push_back(callback); 726 return false; 727 case UNINITIALIZED: 728 pending_tasks_.push_back(callback); 729 // Fall-through. 730 } 731 732 state_ = INITIALIZING; 733 database_task_runner_->PostTask( 734 FROM_HERE, 735 base::Bind(&ReadInitialDataFromDB, 736 database_.get(), 737 base::MessageLoopProxy::current(), 738 base::Bind(&ServiceWorkerStorage::DidReadInitialData, 739 weak_factory_.GetWeakPtr()))); 740 return false; 741 } 742 743 void ServiceWorkerStorage::DidReadInitialData( 744 InitialData* data, 745 ServiceWorkerDatabase::Status status) { 746 DCHECK(data); 747 DCHECK_EQ(INITIALIZING, state_); 748 749 if (status == ServiceWorkerDatabase::STATUS_OK) { 750 next_registration_id_ = data->next_registration_id; 751 next_version_id_ = data->next_version_id; 752 next_resource_id_ = data->next_resource_id; 753 registered_origins_.swap(data->origins); 754 state_ = INITIALIZED; 755 } else { 756 // TODO(nhiroki): Stringify |status| using StatusToString() defined in 757 // service_worker_database.cc. 758 DVLOG(2) << "Failed to initialize: " << status; 759 ScheduleDeleteAndStartOver(); 760 } 761 762 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin(); 763 it != pending_tasks_.end(); ++it) { 764 RunSoon(FROM_HERE, *it); 765 } 766 pending_tasks_.clear(); 767 } 768 769 void ServiceWorkerStorage::DidFindRegistrationForDocument( 770 const GURL& document_url, 771 const FindRegistrationCallback& callback, 772 const ServiceWorkerDatabase::RegistrationData& data, 773 const ResourceList& resources, 774 ServiceWorkerDatabase::Status status) { 775 if (status == ServiceWorkerDatabase::STATUS_OK) { 776 ReturnFoundRegistration(callback, data, resources); 777 TRACE_EVENT_ASYNC_END1( 778 "ServiceWorker", 779 "ServiceWorkerStorage::FindRegistrationForDocument", 780 base::Hash(document_url.spec()), 781 "Status", "OK"); 782 return; 783 } 784 785 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 786 // Look for something currently being installed. 787 scoped_refptr<ServiceWorkerRegistration> installing_registration = 788 FindInstallingRegistrationForDocument(document_url); 789 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK 790 : SERVICE_WORKER_ERROR_NOT_FOUND, 791 installing_registration); 792 TRACE_EVENT_ASYNC_END1( 793 "ServiceWorker", 794 "ServiceWorkerStorage::FindRegistrationForDocument", 795 base::Hash(document_url.spec()), 796 "Status", status); 797 return; 798 } 799 800 ScheduleDeleteAndStartOver(); 801 callback.Run(DatabaseStatusToStatusCode(status), 802 scoped_refptr<ServiceWorkerRegistration>()); 803 TRACE_EVENT_ASYNC_END1( 804 "ServiceWorker", 805 "ServiceWorkerStorage::FindRegistrationForDocument", 806 base::Hash(document_url.spec()), 807 "Status", status); 808 } 809 810 void ServiceWorkerStorage::DidFindRegistrationForPattern( 811 const GURL& scope, 812 const FindRegistrationCallback& callback, 813 const ServiceWorkerDatabase::RegistrationData& data, 814 const ResourceList& resources, 815 ServiceWorkerDatabase::Status status) { 816 if (status == ServiceWorkerDatabase::STATUS_OK) { 817 ReturnFoundRegistration(callback, data, resources); 818 return; 819 } 820 821 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 822 scoped_refptr<ServiceWorkerRegistration> installing_registration = 823 FindInstallingRegistrationForPattern(scope); 824 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK 825 : SERVICE_WORKER_ERROR_NOT_FOUND, 826 installing_registration); 827 return; 828 } 829 830 ScheduleDeleteAndStartOver(); 831 callback.Run(DatabaseStatusToStatusCode(status), 832 scoped_refptr<ServiceWorkerRegistration>()); 833 } 834 835 void ServiceWorkerStorage::DidFindRegistrationForId( 836 const FindRegistrationCallback& callback, 837 const ServiceWorkerDatabase::RegistrationData& data, 838 const ResourceList& resources, 839 ServiceWorkerDatabase::Status status) { 840 if (status == ServiceWorkerDatabase::STATUS_OK) { 841 ReturnFoundRegistration(callback, data, resources); 842 return; 843 } 844 845 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 846 // TODO(nhiroki): Find a registration in |installing_registrations_|. 847 callback.Run(DatabaseStatusToStatusCode(status), 848 scoped_refptr<ServiceWorkerRegistration>()); 849 return; 850 } 851 852 ScheduleDeleteAndStartOver(); 853 callback.Run(DatabaseStatusToStatusCode(status), 854 scoped_refptr<ServiceWorkerRegistration>()); 855 } 856 857 void ServiceWorkerStorage::ReturnFoundRegistration( 858 const FindRegistrationCallback& callback, 859 const ServiceWorkerDatabase::RegistrationData& data, 860 const ResourceList& resources) { 861 scoped_refptr<ServiceWorkerRegistration> registration = 862 GetOrCreateRegistration(data, resources); 863 if (registration->is_deleted()) { 864 // It's past the point of no return and no longer findable. 865 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL); 866 return; 867 } 868 callback.Run(SERVICE_WORKER_OK, registration); 869 } 870 871 void ServiceWorkerStorage::DidGetAllRegistrations( 872 const GetAllRegistrationInfosCallback& callback, 873 RegistrationList* registrations, 874 ServiceWorkerDatabase::Status status) { 875 DCHECK(registrations); 876 if (status != ServiceWorkerDatabase::STATUS_OK && 877 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 878 ScheduleDeleteAndStartOver(); 879 callback.Run(std::vector<ServiceWorkerRegistrationInfo>()); 880 return; 881 } 882 883 // Add all stored registrations. 884 std::set<int64> pushed_registrations; 885 std::vector<ServiceWorkerRegistrationInfo> infos; 886 for (RegistrationList::const_iterator it = registrations->begin(); 887 it != registrations->end(); ++it) { 888 const bool inserted = 889 pushed_registrations.insert(it->registration_id).second; 890 DCHECK(inserted); 891 892 ServiceWorkerRegistration* registration = 893 context_->GetLiveRegistration(it->registration_id); 894 if (registration) { 895 infos.push_back(registration->GetInfo()); 896 continue; 897 } 898 899 ServiceWorkerRegistrationInfo info; 900 info.pattern = it->scope; 901 info.registration_id = it->registration_id; 902 if (ServiceWorkerVersion* version = 903 context_->GetLiveVersion(it->version_id)) { 904 if (it->is_active) 905 info.active_version = version->GetInfo(); 906 else 907 info.waiting_version = version->GetInfo(); 908 infos.push_back(info); 909 continue; 910 } 911 912 if (it->is_active) { 913 info.active_version.is_null = false; 914 info.active_version.status = ServiceWorkerVersion::ACTIVATED; 915 info.active_version.version_id = it->version_id; 916 } else { 917 info.waiting_version.is_null = false; 918 info.waiting_version.status = ServiceWorkerVersion::INSTALLED; 919 info.waiting_version.version_id = it->version_id; 920 } 921 infos.push_back(info); 922 } 923 924 // Add unstored registrations that are being installed. 925 for (RegistrationRefsById::const_iterator it = 926 installing_registrations_.begin(); 927 it != installing_registrations_.end(); ++it) { 928 if (pushed_registrations.insert(it->first).second) 929 infos.push_back(it->second->GetInfo()); 930 } 931 932 callback.Run(infos); 933 } 934 935 void ServiceWorkerStorage::DidStoreRegistration( 936 const StatusCallback& callback, 937 const GURL& origin, 938 int64 deleted_version_id, 939 const std::vector<int64>& newly_purgeable_resources, 940 ServiceWorkerDatabase::Status status) { 941 if (status != ServiceWorkerDatabase::STATUS_OK) { 942 ScheduleDeleteAndStartOver(); 943 callback.Run(DatabaseStatusToStatusCode(status)); 944 return; 945 } 946 registered_origins_.insert(origin); 947 callback.Run(SERVICE_WORKER_OK); 948 949 if (!context_ || !context_->GetLiveVersion(deleted_version_id)) 950 StartPurgingResources(newly_purgeable_resources); 951 } 952 953 void ServiceWorkerStorage::DidUpdateToActiveState( 954 const StatusCallback& callback, 955 ServiceWorkerDatabase::Status status) { 956 if (status != ServiceWorkerDatabase::STATUS_OK && 957 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) { 958 ScheduleDeleteAndStartOver(); 959 } 960 callback.Run(DatabaseStatusToStatusCode(status)); 961 } 962 963 void ServiceWorkerStorage::DidDeleteRegistration( 964 const DidDeleteRegistrationParams& params, 965 bool origin_is_deletable, 966 int64 version_id, 967 const std::vector<int64>& newly_purgeable_resources, 968 ServiceWorkerDatabase::Status status) { 969 pending_deletions_.erase(params.registration_id); 970 if (status != ServiceWorkerDatabase::STATUS_OK) { 971 ScheduleDeleteAndStartOver(); 972 params.callback.Run(DatabaseStatusToStatusCode(status)); 973 return; 974 } 975 if (origin_is_deletable) 976 registered_origins_.erase(params.origin); 977 params.callback.Run(SERVICE_WORKER_OK); 978 979 if (!context_ || !context_->GetLiveVersion(version_id)) 980 StartPurgingResources(newly_purgeable_resources); 981 } 982 983 scoped_refptr<ServiceWorkerRegistration> 984 ServiceWorkerStorage::GetOrCreateRegistration( 985 const ServiceWorkerDatabase::RegistrationData& data, 986 const ResourceList& resources) { 987 scoped_refptr<ServiceWorkerRegistration> registration = 988 context_->GetLiveRegistration(data.registration_id); 989 if (registration.get()) 990 return registration; 991 992 registration = new ServiceWorkerRegistration( 993 data.scope, data.registration_id, context_); 994 registration->set_last_update_check(data.last_update_check); 995 if (pending_deletions_.find(data.registration_id) != 996 pending_deletions_.end()) { 997 registration->set_is_deleted(true); 998 } 999 scoped_refptr<ServiceWorkerVersion> version = 1000 context_->GetLiveVersion(data.version_id); 1001 if (!version.get()) { 1002 version = new ServiceWorkerVersion( 1003 registration.get(), data.script, data.version_id, context_); 1004 version->SetStatus(data.is_active ? 1005 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED); 1006 version->script_cache_map()->SetResources(resources); 1007 } 1008 1009 if (version->status() == ServiceWorkerVersion::ACTIVATED) 1010 registration->SetActiveVersion(version.get()); 1011 else if (version->status() == ServiceWorkerVersion::INSTALLED) 1012 registration->SetWaitingVersion(version.get()); 1013 else 1014 NOTREACHED(); 1015 1016 return registration; 1017 } 1018 1019 ServiceWorkerRegistration* 1020 ServiceWorkerStorage::FindInstallingRegistrationForDocument( 1021 const GURL& document_url) { 1022 DCHECK(!document_url.has_ref()); 1023 1024 LongestScopeMatcher matcher(document_url); 1025 ServiceWorkerRegistration* match = NULL; 1026 1027 // TODO(nhiroki): This searches over installing registrations linearly and it 1028 // couldn't be scalable. Maybe the regs should be partitioned by origin. 1029 for (RegistrationRefsById::const_iterator it = 1030 installing_registrations_.begin(); 1031 it != installing_registrations_.end(); ++it) { 1032 if (matcher.MatchLongest(it->second->pattern())) 1033 match = it->second.get(); 1034 } 1035 return match; 1036 } 1037 1038 ServiceWorkerRegistration* 1039 ServiceWorkerStorage::FindInstallingRegistrationForPattern( 1040 const GURL& scope) { 1041 for (RegistrationRefsById::const_iterator it = 1042 installing_registrations_.begin(); 1043 it != installing_registrations_.end(); ++it) { 1044 if (it->second->pattern() == scope) 1045 return it->second.get(); 1046 } 1047 return NULL; 1048 } 1049 1050 ServiceWorkerRegistration* 1051 ServiceWorkerStorage::FindInstallingRegistrationForId( 1052 int64 registration_id) { 1053 RegistrationRefsById::const_iterator found = 1054 installing_registrations_.find(registration_id); 1055 if (found == installing_registrations_.end()) 1056 return NULL; 1057 return found->second.get(); 1058 } 1059 1060 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() { 1061 if (disk_cache_) 1062 return disk_cache_.get(); 1063 1064 disk_cache_.reset(new ServiceWorkerDiskCache); 1065 1066 base::FilePath path = GetDiskCachePath(); 1067 if (path.empty()) { 1068 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize, 1069 net::CompletionCallback()); 1070 DCHECK_EQ(net::OK, rv); 1071 return disk_cache_.get(); 1072 } 1073 1074 int rv = disk_cache_->InitWithDiskBackend( 1075 path, 1076 kMaxDiskCacheSize, 1077 false, 1078 disk_cache_thread_, 1079 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized, 1080 weak_factory_.GetWeakPtr())); 1081 if (rv != net::ERR_IO_PENDING) 1082 OnDiskCacheInitialized(rv); 1083 1084 return disk_cache_.get(); 1085 } 1086 1087 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) { 1088 if (rv != net::OK) { 1089 LOG(ERROR) << "Failed to open the serviceworker diskcache: " 1090 << net::ErrorToString(rv); 1091 ScheduleDeleteAndStartOver(); 1092 } 1093 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK); 1094 } 1095 1096 void ServiceWorkerStorage::StartPurgingResources( 1097 const std::vector<int64>& ids) { 1098 DCHECK(has_checked_for_stale_resources_); 1099 for (size_t i = 0; i < ids.size(); ++i) 1100 purgeable_resource_ids_.push_back(ids[i]); 1101 ContinuePurgingResources(); 1102 } 1103 1104 void ServiceWorkerStorage::StartPurgingResources( 1105 const ResourceList& resources) { 1106 DCHECK(has_checked_for_stale_resources_); 1107 for (size_t i = 0; i < resources.size(); ++i) 1108 purgeable_resource_ids_.push_back(resources[i].resource_id); 1109 ContinuePurgingResources(); 1110 } 1111 1112 void ServiceWorkerStorage::ContinuePurgingResources() { 1113 if (purgeable_resource_ids_.empty() || is_purge_pending_) 1114 return; 1115 1116 // Do one at a time until we're done, use RunSoon to avoid recursion when 1117 // DoomEntry returns immediately. 1118 is_purge_pending_ = true; 1119 int64 id = purgeable_resource_ids_.front(); 1120 purgeable_resource_ids_.pop_front(); 1121 RunSoon(FROM_HERE, 1122 base::Bind(&ServiceWorkerStorage::PurgeResource, 1123 weak_factory_.GetWeakPtr(), id)); 1124 } 1125 1126 void ServiceWorkerStorage::PurgeResource(int64 id) { 1127 DCHECK(is_purge_pending_); 1128 int rv = disk_cache()->DoomEntry( 1129 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged, 1130 weak_factory_.GetWeakPtr(), id)); 1131 if (rv != net::ERR_IO_PENDING) 1132 OnResourcePurged(id, rv); 1133 } 1134 1135 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) { 1136 DCHECK(is_purge_pending_); 1137 is_purge_pending_ = false; 1138 1139 database_task_runner_->PostTask( 1140 FROM_HERE, 1141 base::Bind(base::IgnoreResult( 1142 &ServiceWorkerDatabase::ClearPurgeableResourceIds), 1143 base::Unretained(database_.get()), 1144 std::set<int64>(&id, &id + 1))); 1145 1146 ContinuePurgingResources(); 1147 } 1148 1149 void ServiceWorkerStorage::DeleteStaleResources() { 1150 DCHECK(!has_checked_for_stale_resources_); 1151 has_checked_for_stale_resources_ = true; 1152 database_task_runner_->PostTask( 1153 FROM_HERE, 1154 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB, 1155 database_.get(), 1156 base::MessageLoopProxy::current(), 1157 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources, 1158 weak_factory_.GetWeakPtr()))); 1159 } 1160 1161 void ServiceWorkerStorage::DidCollectStaleResources( 1162 const std::vector<int64>& stale_resource_ids, 1163 ServiceWorkerDatabase::Status status) { 1164 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status); 1165 if (status != ServiceWorkerDatabase::STATUS_OK) 1166 return; 1167 StartPurgingResources(stale_resource_ids); 1168 } 1169 1170 void ServiceWorkerStorage::CollectStaleResourcesFromDB( 1171 ServiceWorkerDatabase* database, 1172 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1173 const GetResourcesCallback& callback) { 1174 std::set<int64> ids; 1175 ServiceWorkerDatabase::Status status = 1176 database->GetUncommittedResourceIds(&ids); 1177 if (status != ServiceWorkerDatabase::STATUS_OK) { 1178 original_task_runner->PostTask( 1179 FROM_HERE, 1180 base::Bind( 1181 callback, std::vector<int64>(ids.begin(), ids.end()), status)); 1182 return; 1183 } 1184 1185 status = database->PurgeUncommittedResourceIds(ids); 1186 if (status != ServiceWorkerDatabase::STATUS_OK) { 1187 original_task_runner->PostTask( 1188 FROM_HERE, 1189 base::Bind( 1190 callback, std::vector<int64>(ids.begin(), ids.end()), status)); 1191 return; 1192 } 1193 1194 ids.clear(); 1195 status = database->GetPurgeableResourceIds(&ids); 1196 original_task_runner->PostTask( 1197 FROM_HERE, 1198 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status)); 1199 } 1200 1201 void ServiceWorkerStorage::ReadInitialDataFromDB( 1202 ServiceWorkerDatabase* database, 1203 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1204 const InitializeCallback& callback) { 1205 DCHECK(database); 1206 scoped_ptr<ServiceWorkerStorage::InitialData> data( 1207 new ServiceWorkerStorage::InitialData()); 1208 1209 ServiceWorkerDatabase::Status status = 1210 database->GetNextAvailableIds(&data->next_registration_id, 1211 &data->next_version_id, 1212 &data->next_resource_id); 1213 if (status != ServiceWorkerDatabase::STATUS_OK) { 1214 original_task_runner->PostTask( 1215 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status)); 1216 return; 1217 } 1218 1219 status = database->GetOriginsWithRegistrations(&data->origins); 1220 original_task_runner->PostTask( 1221 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status)); 1222 } 1223 1224 void ServiceWorkerStorage::DeleteRegistrationFromDB( 1225 ServiceWorkerDatabase* database, 1226 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1227 int64 registration_id, 1228 const GURL& origin, 1229 const DeleteRegistrationCallback& callback) { 1230 DCHECK(database); 1231 1232 int64 version_id = kInvalidServiceWorkerVersionId; 1233 std::vector<int64> newly_purgeable_resources; 1234 ServiceWorkerDatabase::Status status = database->DeleteRegistration( 1235 registration_id, origin, &version_id, &newly_purgeable_resources); 1236 if (status != ServiceWorkerDatabase::STATUS_OK) { 1237 original_task_runner->PostTask(FROM_HERE, 1238 base::Bind(callback, 1239 false, 1240 kInvalidServiceWorkerVersionId, 1241 std::vector<int64>(), 1242 status)); 1243 return; 1244 } 1245 1246 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the 1247 // unique origin list. 1248 std::vector<ServiceWorkerDatabase::RegistrationData> registrations; 1249 status = database->GetRegistrationsForOrigin(origin, ®istrations); 1250 if (status != ServiceWorkerDatabase::STATUS_OK) { 1251 original_task_runner->PostTask(FROM_HERE, 1252 base::Bind(callback, 1253 false, 1254 kInvalidServiceWorkerVersionId, 1255 std::vector<int64>(), 1256 status)); 1257 return; 1258 } 1259 1260 bool deletable = registrations.empty(); 1261 original_task_runner->PostTask( 1262 FROM_HERE, 1263 base::Bind( 1264 callback, deletable, version_id, newly_purgeable_resources, status)); 1265 } 1266 1267 void ServiceWorkerStorage::WriteRegistrationInDB( 1268 ServiceWorkerDatabase* database, 1269 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1270 const ServiceWorkerDatabase::RegistrationData& data, 1271 const ResourceList& resources, 1272 const WriteRegistrationCallback& callback) { 1273 DCHECK(database); 1274 int64 deleted_version_id = kInvalidServiceWorkerVersionId; 1275 std::vector<int64> newly_purgeable_resources; 1276 ServiceWorkerDatabase::Status status = database->WriteRegistration( 1277 data, resources, &deleted_version_id, &newly_purgeable_resources); 1278 original_task_runner->PostTask(FROM_HERE, 1279 base::Bind(callback, 1280 data.script.GetOrigin(), 1281 deleted_version_id, 1282 newly_purgeable_resources, 1283 status)); 1284 } 1285 1286 void ServiceWorkerStorage::FindForDocumentInDB( 1287 ServiceWorkerDatabase* database, 1288 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1289 const GURL& document_url, 1290 const FindInDBCallback& callback) { 1291 GURL origin = document_url.GetOrigin(); 1292 RegistrationList registrations; 1293 ServiceWorkerDatabase::Status status = 1294 database->GetRegistrationsForOrigin(origin, ®istrations); 1295 if (status != ServiceWorkerDatabase::STATUS_OK) { 1296 original_task_runner->PostTask( 1297 FROM_HERE, 1298 base::Bind(callback, 1299 ServiceWorkerDatabase::RegistrationData(), 1300 ResourceList(), 1301 status)); 1302 return; 1303 } 1304 1305 ServiceWorkerDatabase::RegistrationData data; 1306 ResourceList resources; 1307 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; 1308 1309 // Find one with a pattern match. 1310 LongestScopeMatcher matcher(document_url); 1311 int64 match = kInvalidServiceWorkerRegistrationId; 1312 for (size_t i = 0; i < registrations.size(); ++i) { 1313 if (matcher.MatchLongest(registrations[i].scope)) 1314 match = registrations[i].registration_id; 1315 } 1316 1317 if (match != kInvalidServiceWorkerRegistrationId) 1318 status = database->ReadRegistration(match, origin, &data, &resources); 1319 1320 original_task_runner->PostTask( 1321 FROM_HERE, 1322 base::Bind(callback, data, resources, status)); 1323 } 1324 1325 void ServiceWorkerStorage::FindForPatternInDB( 1326 ServiceWorkerDatabase* database, 1327 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1328 const GURL& scope, 1329 const FindInDBCallback& callback) { 1330 GURL origin = scope.GetOrigin(); 1331 std::vector<ServiceWorkerDatabase::RegistrationData> registrations; 1332 ServiceWorkerDatabase::Status status = 1333 database->GetRegistrationsForOrigin(origin, ®istrations); 1334 if (status != ServiceWorkerDatabase::STATUS_OK) { 1335 original_task_runner->PostTask( 1336 FROM_HERE, 1337 base::Bind(callback, 1338 ServiceWorkerDatabase::RegistrationData(), 1339 ResourceList(), 1340 status)); 1341 return; 1342 } 1343 1344 // Find one with an exact matching scope. 1345 ServiceWorkerDatabase::RegistrationData data; 1346 ResourceList resources; 1347 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; 1348 for (RegistrationList::const_iterator it = registrations.begin(); 1349 it != registrations.end(); ++it) { 1350 if (scope != it->scope) 1351 continue; 1352 status = database->ReadRegistration(it->registration_id, origin, 1353 &data, &resources); 1354 break; // We're done looping. 1355 } 1356 1357 original_task_runner->PostTask( 1358 FROM_HERE, 1359 base::Bind(callback, data, resources, status)); 1360 } 1361 1362 void ServiceWorkerStorage::FindForIdInDB( 1363 ServiceWorkerDatabase* database, 1364 scoped_refptr<base::SequencedTaskRunner> original_task_runner, 1365 int64 registration_id, 1366 const GURL& origin, 1367 const FindInDBCallback& callback) { 1368 ServiceWorkerDatabase::RegistrationData data; 1369 ResourceList resources; 1370 ServiceWorkerDatabase::Status status = 1371 database->ReadRegistration(registration_id, origin, &data, &resources); 1372 original_task_runner->PostTask( 1373 FROM_HERE, base::Bind(callback, data, resources, status)); 1374 } 1375 1376 // TODO(nhiroki): The corruption recovery should not be scheduled if the error 1377 // is transient and it can get healed soon (e.g. IO error). To do that, the 1378 // database should not disable itself when an error occurs and the storage 1379 // controls it instead. 1380 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() { 1381 if (state_ == DISABLED) { 1382 // Recovery process has already been scheduled. 1383 return; 1384 } 1385 Disable(); 1386 1387 DVLOG(1) << "Schedule to delete the context and start over."; 1388 context_->ScheduleDeleteAndStartOver(); 1389 } 1390 1391 void ServiceWorkerStorage::DidDeleteDatabase( 1392 const StatusCallback& callback, 1393 ServiceWorkerDatabase::Status status) { 1394 DCHECK_EQ(DISABLED, state_); 1395 if (status != ServiceWorkerDatabase::STATUS_OK) { 1396 // Give up the corruption recovery until the browser restarts. 1397 LOG(ERROR) << "Failed to delete the database: " << status; 1398 callback.Run(DatabaseStatusToStatusCode(status)); 1399 return; 1400 } 1401 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully."; 1402 1403 // Delete the disk cache on the cache thread. 1404 // TODO(nhiroki): What if there is a bunch of files in the cache directory? 1405 // Deleting the directory could take a long time and restart could be delayed. 1406 // We should probably rename the directory and delete it later. 1407 PostTaskAndReplyWithResult( 1408 database_task_runner_.get(), 1409 FROM_HERE, 1410 base::Bind(&base::DeleteFile, GetDiskCachePath(), true), 1411 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache, 1412 weak_factory_.GetWeakPtr(), 1413 callback)); 1414 } 1415 1416 void ServiceWorkerStorage::DidDeleteDiskCache( 1417 const StatusCallback& callback, bool result) { 1418 DCHECK_EQ(DISABLED, state_); 1419 if (!result) { 1420 // Give up the corruption recovery until the browser restarts. 1421 LOG(ERROR) << "Failed to delete the diskcache."; 1422 callback.Run(SERVICE_WORKER_ERROR_FAILED); 1423 return; 1424 } 1425 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully."; 1426 callback.Run(SERVICE_WORKER_OK); 1427 } 1428 1429 } // namespace content 1430