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.
5 #include "content/browser/service_worker/service_worker_storage.h"
9 #include "base/bind_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/files/file_util.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/task_runner_util.h"
16 #include "content/browser/service_worker/service_worker_context_core.h"
17 #include "content/browser/service_worker/service_worker_disk_cache.h"
18 #include "content/browser/service_worker/service_worker_info.h"
19 #include "content/browser/service_worker/service_worker_metrics.h"
20 #include "content/browser/service_worker/service_worker_registration.h"
21 #include "content/browser/service_worker/service_worker_utils.h"
22 #include "content/browser/service_worker/service_worker_version.h"
23 #include "content/common/service_worker/service_worker_types.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "net/base/completion_callback.h"
26 #include "net/base/io_buffer.h"
27 #include "net/base/net_errors.h"
28 #include "storage/browser/quota/quota_manager_proxy.h"
29 #include "storage/browser/quota/special_storage_policy.h"
35 void RunSoon(const tracked_objects::Location& from_here,
36 const base::Closure& closure) {
37 base::MessageLoop::current()->PostTask(from_here, closure);
41 const scoped_refptr<ServiceWorkerRegistration>& registration,
42 ServiceWorkerStatusCode status,
43 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
44 callback.Run(status, registration);
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));
55 const base::FilePath::CharType kDatabaseName[] =
56 FILE_PATH_LITERAL("Database");
57 const base::FilePath::CharType kDiskCacheName[] =
58 FILE_PATH_LITERAL("Cache");
60 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
61 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
63 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
64 ServiceWorkerDatabase::Status 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:
73 return SERVICE_WORKER_ERROR_FAILED;
77 class ResponseComparer : public base::RefCounted<ResponseComparer> {
80 base::WeakPtr<ServiceWorkerStorage> owner,
81 scoped_ptr<ServiceWorkerResponseReader> lhs,
82 scoped_ptr<ServiceWorkerResponseReader> rhs,
83 const ServiceWorkerStorage::CompareCallback& callback)
85 completion_callback_(callback),
86 lhs_reader_(lhs.release()),
87 rhs_reader_(rhs.release()),
95 friend class base::RefCounted<ResponseComparer>;
97 static const int kBufferSize = 16 * 1024;
99 ~ResponseComparer() {}
101 void OnReadInfoComplete(int result);
103 void OnReadDataComplete(int result);
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);
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();
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));
134 void ResponseComparer::OnReadInfoComplete(int result) {
135 if (completion_callback_.is_null() || !owner_)
138 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
139 completion_callback_.Reset();
142 if (++completion_count_ != 2)
145 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
146 completion_callback_.Run(SERVICE_WORKER_OK, false);
152 void ResponseComparer::ReadSomeData() {
153 completion_count_ = 0;
154 lhs_reader_->ReadData(
157 base::Bind(&ResponseComparer::OnReadDataComplete, this));
158 rhs_reader_->ReadData(
161 base::Bind(&ResponseComparer::OnReadDataComplete, this));
164 void ResponseComparer::OnReadDataComplete(int result) {
165 if (completion_callback_.is_null() || !owner_)
168 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
169 completion_callback_.Reset();
172 if (++completion_count_ != 2) {
173 previous_result_ = result;
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
180 if (result != previous_result_) {
181 completion_callback_.Run(SERVICE_WORKER_OK, false);
186 completion_callback_.Run(SERVICE_WORKER_OK, true);
191 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
192 if (compare_result != 0) {
193 completion_callback_.Run(SERVICE_WORKER_OK, false);
202 ServiceWorkerStorage::InitialData::InitialData()
203 : next_registration_id(kInvalidServiceWorkerRegistrationId),
204 next_version_id(kInvalidServiceWorkerVersionId),
205 next_resource_id(kInvalidServiceWorkerResourceId) {
208 ServiceWorkerStorage::InitialData::~InitialData() {
211 ServiceWorkerStorage::
212 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
213 : registration_id(kInvalidServiceWorkerRegistrationId) {
216 ServiceWorkerStorage::
217 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
220 ServiceWorkerStorage::~ServiceWorkerStorage() {
221 ClearSessionOnlyOrigins();
222 weak_factory_.InvalidateWeakPtrs();
223 database_task_manager_->GetTaskRunner()->DeleteSoon(FROM_HERE,
224 database_.release());
228 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
229 const base::FilePath& path,
230 base::WeakPtr<ServiceWorkerContextCore> context,
231 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
232 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
233 storage::QuotaManagerProxy* quota_manager_proxy,
234 storage::SpecialStoragePolicy* special_storage_policy) {
235 return make_scoped_ptr(new ServiceWorkerStorage(path,
237 database_task_manager.Pass(),
240 special_storage_policy));
244 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
245 base::WeakPtr<ServiceWorkerContextCore> context,
246 ServiceWorkerStorage* old_storage) {
247 return make_scoped_ptr(
248 new ServiceWorkerStorage(old_storage->path_,
250 old_storage->database_task_manager_->Clone(),
251 old_storage->disk_cache_thread_,
252 old_storage->quota_manager_proxy_.get(),
253 old_storage->special_storage_policy_.get()));
256 void ServiceWorkerStorage::FindRegistrationForDocument(
257 const GURL& document_url,
258 const FindRegistrationCallback& callback) {
259 DCHECK(!document_url.has_ref());
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);
267 TRACE_EVENT_INSTANT1(
269 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
270 TRACE_EVENT_SCOPE_THREAD,
271 "URL", document_url.spec());
274 DCHECK_EQ(INITIALIZED, state_);
276 // See if there are any stored registrations for the origin.
277 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
278 // Look for something currently being installed.
279 scoped_refptr<ServiceWorkerRegistration> installing_registration =
280 FindInstallingRegistrationForDocument(document_url);
281 ServiceWorkerStatusCode status = installing_registration.get() ?
282 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
283 TRACE_EVENT_INSTANT2(
285 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
286 TRACE_EVENT_SCOPE_THREAD,
287 "URL", document_url.spec(),
288 "Status", ServiceWorkerStatusToString(status));
289 CompleteFindNow(installing_registration,
295 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
297 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
298 TRACE_EVENT_ASYNC_BEGIN1(
300 "ServiceWorkerStorage::FindRegistrationForDocument",
302 "URL", document_url.spec());
303 database_task_manager_->GetTaskRunner()->PostTask(
306 &FindForDocumentInDB,
308 base::MessageLoopProxy::current(),
310 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
311 weak_factory_.GetWeakPtr(),
317 void ServiceWorkerStorage::FindRegistrationForPattern(
319 const FindRegistrationCallback& callback) {
320 if (!LazyInitialize(base::Bind(
321 &ServiceWorkerStorage::FindRegistrationForPattern,
322 weak_factory_.GetWeakPtr(), scope, callback))) {
323 if (state_ != INITIALIZING || !context_) {
324 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
325 SERVICE_WORKER_ERROR_FAILED, callback);
329 DCHECK_EQ(INITIALIZED, state_);
331 // See if there are any stored registrations for the origin.
332 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
333 // Look for something currently being installed.
334 scoped_refptr<ServiceWorkerRegistration> installing_registration =
335 FindInstallingRegistrationForPattern(scope);
336 CompleteFindSoon(FROM_HERE,
337 installing_registration,
338 installing_registration.get()
340 : SERVICE_WORKER_ERROR_NOT_FOUND,
345 database_task_manager_->GetTaskRunner()->PostTask(
350 base::MessageLoopProxy::current(),
352 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
353 weak_factory_.GetWeakPtr(), scope, callback)));
356 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
358 if (state_ != INITIALIZED || !context_)
360 for (RegistrationRefsById::const_iterator it =
361 uninstalling_registrations_.begin();
362 it != uninstalling_registrations_.end();
364 if (it->second->pattern() == scope) {
365 DCHECK(it->second->is_uninstalling());
366 return it->second.get();
372 void ServiceWorkerStorage::FindRegistrationForId(
373 int64 registration_id,
375 const FindRegistrationCallback& callback) {
376 if (!LazyInitialize(base::Bind(
377 &ServiceWorkerStorage::FindRegistrationForId,
378 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
379 if (state_ != INITIALIZING || !context_) {
380 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
381 SERVICE_WORKER_ERROR_FAILED, callback);
385 DCHECK_EQ(INITIALIZED, state_);
387 // See if there are any stored registrations for the origin.
388 if (!ContainsKey(registered_origins_, origin)) {
389 // Look for something currently being installed.
390 scoped_refptr<ServiceWorkerRegistration> installing_registration =
391 FindInstallingRegistrationForId(registration_id);
392 CompleteFindNow(installing_registration,
393 installing_registration.get()
395 : SERVICE_WORKER_ERROR_NOT_FOUND,
400 scoped_refptr<ServiceWorkerRegistration> registration =
401 context_->GetLiveRegistration(registration_id);
402 if (registration.get()) {
403 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
407 database_task_manager_->GetTaskRunner()->PostTask(
409 base::Bind(&FindForIdInDB,
411 base::MessageLoopProxy::current(),
412 registration_id, origin,
413 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
414 weak_factory_.GetWeakPtr(), callback)));
417 void ServiceWorkerStorage::GetAllRegistrations(
418 const GetAllRegistrationInfosCallback& callback) {
419 if (!LazyInitialize(base::Bind(
420 &ServiceWorkerStorage::GetAllRegistrations,
421 weak_factory_.GetWeakPtr(), callback))) {
422 if (state_ != INITIALIZING || !context_) {
423 RunSoon(FROM_HERE, base::Bind(
424 callback, std::vector<ServiceWorkerRegistrationInfo>()));
428 DCHECK_EQ(INITIALIZED, state_);
430 RegistrationList* registrations = new RegistrationList;
431 PostTaskAndReplyWithResult(
432 database_task_manager_->GetTaskRunner(),
434 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
435 base::Unretained(database_.get()),
436 base::Unretained(registrations)),
437 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
438 weak_factory_.GetWeakPtr(),
440 base::Owned(registrations)));
443 void ServiceWorkerStorage::StoreRegistration(
444 ServiceWorkerRegistration* registration,
445 ServiceWorkerVersion* version,
446 const StatusCallback& callback) {
447 DCHECK(registration);
450 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
451 if (IsDisabled() || !context_) {
452 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
456 ServiceWorkerDatabase::RegistrationData data;
457 data.registration_id = registration->id();
458 data.scope = registration->pattern();
459 data.script = version->script_url();
460 data.has_fetch_handler = true;
461 data.version_id = version->version_id();
462 data.last_update_check = registration->last_update_check();
463 data.is_active = (version == registration->active_version());
465 ResourceList resources;
466 version->script_cache_map()->GetResources(&resources);
468 uint64 resources_total_size_bytes = 0;
469 for (const auto& resource : resources) {
470 resources_total_size_bytes += resource.size_bytes;
472 data.resources_total_size_bytes = resources_total_size_bytes;
474 if (!has_checked_for_stale_resources_)
475 DeleteStaleResources();
477 database_task_manager_->GetTaskRunner()->PostTask(
479 base::Bind(&WriteRegistrationInDB,
481 base::MessageLoopProxy::current(),
484 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
485 weak_factory_.GetWeakPtr(),
489 registration->set_is_deleted(false);
492 void ServiceWorkerStorage::UpdateToActiveState(
493 ServiceWorkerRegistration* registration,
494 const StatusCallback& callback) {
495 DCHECK(registration);
497 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
498 if (IsDisabled() || !context_) {
499 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
503 PostTaskAndReplyWithResult(
504 database_task_manager_->GetTaskRunner(),
506 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
507 base::Unretained(database_.get()),
509 registration->pattern().GetOrigin()),
510 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
511 weak_factory_.GetWeakPtr(),
515 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
516 ServiceWorkerRegistration* registration) {
517 DCHECK(registration);
519 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
520 if (IsDisabled() || !context_)
523 database_task_manager_->GetTaskRunner()->PostTask(
526 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
527 base::Unretained(database_.get()),
529 registration->pattern().GetOrigin(),
530 registration->last_update_check()));
533 void ServiceWorkerStorage::DeleteRegistration(
534 int64 registration_id,
536 const StatusCallback& callback) {
537 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
538 if (IsDisabled() || !context_) {
539 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
543 if (!has_checked_for_stale_resources_)
544 DeleteStaleResources();
546 DidDeleteRegistrationParams params;
547 params.registration_id = registration_id;
548 params.origin = origin;
549 params.callback = callback;
551 database_task_manager_->GetTaskRunner()->PostTask(
553 base::Bind(&DeleteRegistrationFromDB,
555 base::MessageLoopProxy::current(),
556 registration_id, origin,
557 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
558 weak_factory_.GetWeakPtr(), params)));
560 // The registration should no longer be findable.
561 pending_deletions_.insert(registration_id);
562 ServiceWorkerRegistration* registration =
563 context_->GetLiveRegistration(registration_id);
565 registration->set_is_deleted(true);
568 scoped_ptr<ServiceWorkerResponseReader>
569 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
570 return make_scoped_ptr(
571 new ServiceWorkerResponseReader(response_id, disk_cache()));
574 scoped_ptr<ServiceWorkerResponseWriter>
575 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
576 return make_scoped_ptr(
577 new ServiceWorkerResponseWriter(response_id, disk_cache()));
580 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
581 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
582 DCHECK_EQ(INITIALIZED, state_);
584 if (!has_checked_for_stale_resources_)
585 DeleteStaleResources();
587 database_task_manager_->GetTaskRunner()->PostTask(
589 base::Bind(base::IgnoreResult(
590 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
591 base::Unretained(database_.get()),
592 std::set<int64>(&id, &id + 1)));
595 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
596 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
597 database_task_manager_->GetTaskRunner()->PostTask(
599 base::Bind(base::IgnoreResult(
600 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
601 base::Unretained(database_.get()),
602 std::set<int64>(&id, &id + 1)));
603 StartPurgingResources(std::vector<int64>(1, id));
606 void ServiceWorkerStorage::CompareScriptResources(
607 int64 lhs_id, int64 rhs_id,
608 const CompareCallback& callback) {
609 DCHECK(!callback.is_null());
610 scoped_refptr<ResponseComparer> comparer =
611 new ResponseComparer(weak_factory_.GetWeakPtr(),
612 CreateResponseReader(lhs_id),
613 CreateResponseReader(rhs_id),
615 comparer->Start(); // It deletes itself when done.
618 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
621 // Delete the database on the database thread.
622 PostTaskAndReplyWithResult(
623 database_task_manager_->GetTaskRunner(),
625 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
626 base::Unretained(database_.get())),
627 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
628 weak_factory_.GetWeakPtr(),
632 int64 ServiceWorkerStorage::NewRegistrationId() {
633 if (state_ == DISABLED)
634 return kInvalidServiceWorkerRegistrationId;
635 DCHECK_EQ(INITIALIZED, state_);
636 return next_registration_id_++;
639 int64 ServiceWorkerStorage::NewVersionId() {
640 if (state_ == DISABLED)
641 return kInvalidServiceWorkerVersionId;
642 DCHECK_EQ(INITIALIZED, state_);
643 return next_version_id_++;
646 int64 ServiceWorkerStorage::NewResourceId() {
647 if (state_ == DISABLED)
648 return kInvalidServiceWorkerResourceId;
649 DCHECK_EQ(INITIALIZED, state_);
650 return next_resource_id_++;
653 void ServiceWorkerStorage::NotifyInstallingRegistration(
654 ServiceWorkerRegistration* registration) {
655 DCHECK(installing_registrations_.find(registration->id()) ==
656 installing_registrations_.end());
657 installing_registrations_[registration->id()] = registration;
660 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
661 ServiceWorkerRegistration* registration,
662 ServiceWorkerVersion* version,
663 ServiceWorkerStatusCode status) {
664 installing_registrations_.erase(registration->id());
665 if (status != SERVICE_WORKER_OK && version) {
666 ResourceList resources;
667 version->script_cache_map()->GetResources(&resources);
670 for (size_t i = 0; i < resources.size(); ++i)
671 ids.insert(resources[i].resource_id);
673 database_task_manager_->GetTaskRunner()->PostTask(
675 base::Bind(base::IgnoreResult(
676 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
677 base::Unretained(database_.get()),
682 void ServiceWorkerStorage::NotifyUninstallingRegistration(
683 ServiceWorkerRegistration* registration) {
684 DCHECK(uninstalling_registrations_.find(registration->id()) ==
685 uninstalling_registrations_.end());
686 uninstalling_registrations_[registration->id()] = registration;
689 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
690 ServiceWorkerRegistration* registration) {
691 uninstalling_registrations_.erase(registration->id());
694 void ServiceWorkerStorage::Disable() {
697 disk_cache_->Disable();
700 bool ServiceWorkerStorage::IsDisabled() const {
701 return state_ == DISABLED;
704 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
705 if (!has_checked_for_stale_resources_)
706 DeleteStaleResources();
707 StartPurgingResources(resources);
710 ServiceWorkerStorage::ServiceWorkerStorage(
711 const base::FilePath& path,
712 base::WeakPtr<ServiceWorkerContextCore> context,
713 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
714 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
715 storage::QuotaManagerProxy* quota_manager_proxy,
716 storage::SpecialStoragePolicy* special_storage_policy)
717 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
718 next_version_id_(kInvalidServiceWorkerVersionId),
719 next_resource_id_(kInvalidServiceWorkerResourceId),
720 state_(UNINITIALIZED),
723 database_task_manager_(database_task_manager.Pass()),
724 disk_cache_thread_(disk_cache_thread),
725 quota_manager_proxy_(quota_manager_proxy),
726 special_storage_policy_(special_storage_policy),
727 is_purge_pending_(false),
728 has_checked_for_stale_resources_(false),
729 weak_factory_(this) {
730 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
733 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
735 return base::FilePath();
736 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
737 .Append(kDatabaseName);
740 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
742 return base::FilePath();
743 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
744 .Append(kDiskCacheName);
747 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
757 pending_tasks_.push_back(callback);
760 pending_tasks_.push_back(callback);
764 state_ = INITIALIZING;
765 database_task_manager_->GetTaskRunner()->PostTask(
767 base::Bind(&ReadInitialDataFromDB,
769 base::MessageLoopProxy::current(),
770 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
771 weak_factory_.GetWeakPtr())));
775 void ServiceWorkerStorage::DidReadInitialData(
777 ServiceWorkerDatabase::Status status) {
779 DCHECK_EQ(INITIALIZING, state_);
781 if (status == ServiceWorkerDatabase::STATUS_OK) {
782 next_registration_id_ = data->next_registration_id;
783 next_version_id_ = data->next_version_id;
784 next_resource_id_ = data->next_resource_id;
785 registered_origins_.swap(data->origins);
786 state_ = INITIALIZED;
788 DVLOG(2) << "Failed to initialize: "
789 << ServiceWorkerDatabase::StatusToString(status);
790 ScheduleDeleteAndStartOver();
793 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
794 it != pending_tasks_.end(); ++it) {
795 RunSoon(FROM_HERE, *it);
797 pending_tasks_.clear();
800 void ServiceWorkerStorage::DidFindRegistrationForDocument(
801 const GURL& document_url,
802 const FindRegistrationCallback& callback,
804 const ServiceWorkerDatabase::RegistrationData& data,
805 const ResourceList& resources,
806 ServiceWorkerDatabase::Status status) {
807 if (status == ServiceWorkerDatabase::STATUS_OK) {
808 ReturnFoundRegistration(callback, data, resources);
809 TRACE_EVENT_ASYNC_END1(
811 "ServiceWorkerStorage::FindRegistrationForDocument",
813 "Status", ServiceWorkerDatabase::StatusToString(status));
817 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
818 // Look for something currently being installed.
819 scoped_refptr<ServiceWorkerRegistration> installing_registration =
820 FindInstallingRegistrationForDocument(document_url);
821 ServiceWorkerStatusCode installing_status = installing_registration.get() ?
822 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
823 callback.Run(installing_status, installing_registration);
824 TRACE_EVENT_ASYNC_END2(
826 "ServiceWorkerStorage::FindRegistrationForDocument",
828 "Status", ServiceWorkerDatabase::StatusToString(status),
830 (installing_status == SERVICE_WORKER_OK) ?
831 "Installing registration is found" :
832 "Any registrations are not found");
836 ScheduleDeleteAndStartOver();
837 callback.Run(DatabaseStatusToStatusCode(status),
838 scoped_refptr<ServiceWorkerRegistration>());
839 TRACE_EVENT_ASYNC_END1(
841 "ServiceWorkerStorage::FindRegistrationForDocument",
843 "Status", ServiceWorkerDatabase::StatusToString(status));
846 void ServiceWorkerStorage::DidFindRegistrationForPattern(
848 const FindRegistrationCallback& callback,
849 const ServiceWorkerDatabase::RegistrationData& data,
850 const ResourceList& resources,
851 ServiceWorkerDatabase::Status status) {
852 if (status == ServiceWorkerDatabase::STATUS_OK) {
853 ReturnFoundRegistration(callback, data, resources);
857 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
858 scoped_refptr<ServiceWorkerRegistration> installing_registration =
859 FindInstallingRegistrationForPattern(scope);
860 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
861 : SERVICE_WORKER_ERROR_NOT_FOUND,
862 installing_registration);
866 ScheduleDeleteAndStartOver();
867 callback.Run(DatabaseStatusToStatusCode(status),
868 scoped_refptr<ServiceWorkerRegistration>());
871 void ServiceWorkerStorage::DidFindRegistrationForId(
872 const FindRegistrationCallback& callback,
873 const ServiceWorkerDatabase::RegistrationData& data,
874 const ResourceList& resources,
875 ServiceWorkerDatabase::Status status) {
876 if (status == ServiceWorkerDatabase::STATUS_OK) {
877 ReturnFoundRegistration(callback, data, resources);
881 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
882 // TODO(nhiroki): Find a registration in |installing_registrations_|.
883 callback.Run(DatabaseStatusToStatusCode(status),
884 scoped_refptr<ServiceWorkerRegistration>());
888 ScheduleDeleteAndStartOver();
889 callback.Run(DatabaseStatusToStatusCode(status),
890 scoped_refptr<ServiceWorkerRegistration>());
893 void ServiceWorkerStorage::ReturnFoundRegistration(
894 const FindRegistrationCallback& callback,
895 const ServiceWorkerDatabase::RegistrationData& data,
896 const ResourceList& resources) {
897 scoped_refptr<ServiceWorkerRegistration> registration =
898 GetOrCreateRegistration(data, resources);
899 if (registration->is_deleted()) {
900 // It's past the point of no return and no longer findable.
901 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
904 callback.Run(SERVICE_WORKER_OK, registration);
907 void ServiceWorkerStorage::DidGetAllRegistrations(
908 const GetAllRegistrationInfosCallback& callback,
909 RegistrationList* registrations,
910 ServiceWorkerDatabase::Status status) {
911 DCHECK(registrations);
912 if (status != ServiceWorkerDatabase::STATUS_OK &&
913 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
914 ScheduleDeleteAndStartOver();
915 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
919 // Add all stored registrations.
920 std::set<int64> pushed_registrations;
921 std::vector<ServiceWorkerRegistrationInfo> infos;
922 for (const auto& registration_data : *registrations) {
923 const bool inserted =
924 pushed_registrations.insert(registration_data.registration_id).second;
927 ServiceWorkerRegistration* registration =
928 context_->GetLiveRegistration(registration_data.registration_id);
930 infos.push_back(registration->GetInfo());
934 ServiceWorkerRegistrationInfo info;
935 info.pattern = registration_data.scope;
936 info.registration_id = registration_data.registration_id;
937 info.stored_version_size_bytes =
938 registration_data.resources_total_size_bytes;
939 if (ServiceWorkerVersion* version =
940 context_->GetLiveVersion(registration_data.version_id)) {
941 if (registration_data.is_active)
942 info.active_version = version->GetInfo();
944 info.waiting_version = version->GetInfo();
945 infos.push_back(info);
949 if (registration_data.is_active) {
950 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
951 info.active_version.version_id = registration_data.version_id;
953 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
954 info.waiting_version.version_id = registration_data.version_id;
956 infos.push_back(info);
959 // Add unstored registrations that are being installed.
960 for (RegistrationRefsById::const_iterator it =
961 installing_registrations_.begin();
962 it != installing_registrations_.end(); ++it) {
963 if (pushed_registrations.insert(it->first).second)
964 infos.push_back(it->second->GetInfo());
970 void ServiceWorkerStorage::DidStoreRegistration(
971 const StatusCallback& callback,
972 const ServiceWorkerDatabase::RegistrationData& new_version,
974 const ServiceWorkerDatabase::RegistrationData& deleted_version,
975 const std::vector<int64>& newly_purgeable_resources,
976 ServiceWorkerDatabase::Status status) {
977 if (status != ServiceWorkerDatabase::STATUS_OK) {
978 ScheduleDeleteAndStartOver();
979 callback.Run(DatabaseStatusToStatusCode(status));
982 registered_origins_.insert(origin);
984 scoped_refptr<ServiceWorkerRegistration> registration =
985 context_->GetLiveRegistration(new_version.registration_id);
986 registration->set_resources_total_size_bytes(
987 new_version.resources_total_size_bytes);
988 if (quota_manager_proxy_.get()) {
989 // Can be nullptr in tests.
990 quota_manager_proxy_->NotifyStorageModified(
991 storage::QuotaClient::kServiceWorker,
993 storage::StorageType::kStorageTypeTemporary,
994 new_version.resources_total_size_bytes -
995 deleted_version.resources_total_size_bytes);
998 callback.Run(SERVICE_WORKER_OK);
1000 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1001 StartPurgingResources(newly_purgeable_resources);
1004 void ServiceWorkerStorage::DidUpdateToActiveState(
1005 const StatusCallback& callback,
1006 ServiceWorkerDatabase::Status status) {
1007 if (status != ServiceWorkerDatabase::STATUS_OK &&
1008 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1009 ScheduleDeleteAndStartOver();
1011 callback.Run(DatabaseStatusToStatusCode(status));
1014 void ServiceWorkerStorage::DidDeleteRegistration(
1015 const DidDeleteRegistrationParams& params,
1016 bool origin_is_deletable,
1017 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1018 const std::vector<int64>& newly_purgeable_resources,
1019 ServiceWorkerDatabase::Status status) {
1020 pending_deletions_.erase(params.registration_id);
1021 if (status != ServiceWorkerDatabase::STATUS_OK) {
1022 ScheduleDeleteAndStartOver();
1023 params.callback.Run(DatabaseStatusToStatusCode(status));
1026 if (quota_manager_proxy_.get()) {
1027 // Can be nullptr in tests.
1028 quota_manager_proxy_->NotifyStorageModified(
1029 storage::QuotaClient::kServiceWorker,
1031 storage::StorageType::kStorageTypeTemporary,
1032 -deleted_version.resources_total_size_bytes);
1034 if (origin_is_deletable)
1035 registered_origins_.erase(params.origin);
1036 params.callback.Run(SERVICE_WORKER_OK);
1038 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1039 StartPurgingResources(newly_purgeable_resources);
1042 scoped_refptr<ServiceWorkerRegistration>
1043 ServiceWorkerStorage::GetOrCreateRegistration(
1044 const ServiceWorkerDatabase::RegistrationData& data,
1045 const ResourceList& resources) {
1046 scoped_refptr<ServiceWorkerRegistration> registration =
1047 context_->GetLiveRegistration(data.registration_id);
1048 if (registration.get())
1049 return registration;
1051 registration = new ServiceWorkerRegistration(
1052 data.scope, data.registration_id, context_);
1053 registration->set_resources_total_size_bytes(data.resources_total_size_bytes);
1054 registration->set_last_update_check(data.last_update_check);
1055 if (pending_deletions_.find(data.registration_id) !=
1056 pending_deletions_.end()) {
1057 registration->set_is_deleted(true);
1059 scoped_refptr<ServiceWorkerVersion> version =
1060 context_->GetLiveVersion(data.version_id);
1061 if (!version.get()) {
1062 version = new ServiceWorkerVersion(
1063 registration.get(), data.script, data.version_id, context_);
1064 version->SetStatus(data.is_active ?
1065 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1066 version->script_cache_map()->SetResources(resources);
1069 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1070 registration->SetActiveVersion(version.get());
1071 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1072 registration->SetWaitingVersion(version.get());
1076 return registration;
1079 ServiceWorkerRegistration*
1080 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1081 const GURL& document_url) {
1082 DCHECK(!document_url.has_ref());
1084 LongestScopeMatcher matcher(document_url);
1085 ServiceWorkerRegistration* match = NULL;
1087 // TODO(nhiroki): This searches over installing registrations linearly and it
1088 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1089 for (RegistrationRefsById::const_iterator it =
1090 installing_registrations_.begin();
1091 it != installing_registrations_.end(); ++it) {
1092 if (matcher.MatchLongest(it->second->pattern()))
1093 match = it->second.get();
1098 ServiceWorkerRegistration*
1099 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1100 const GURL& scope) {
1101 for (RegistrationRefsById::const_iterator it =
1102 installing_registrations_.begin();
1103 it != installing_registrations_.end(); ++it) {
1104 if (it->second->pattern() == scope)
1105 return it->second.get();
1110 ServiceWorkerRegistration*
1111 ServiceWorkerStorage::FindInstallingRegistrationForId(
1112 int64 registration_id) {
1113 RegistrationRefsById::const_iterator found =
1114 installing_registrations_.find(registration_id);
1115 if (found == installing_registrations_.end())
1117 return found->second.get();
1120 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1122 return disk_cache_.get();
1124 disk_cache_.reset(new ServiceWorkerDiskCache);
1126 base::FilePath path = GetDiskCachePath();
1128 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1129 net::CompletionCallback());
1130 DCHECK_EQ(net::OK, rv);
1131 return disk_cache_.get();
1134 int rv = disk_cache_->InitWithDiskBackend(
1139 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1140 weak_factory_.GetWeakPtr()));
1141 if (rv != net::ERR_IO_PENDING)
1142 OnDiskCacheInitialized(rv);
1144 return disk_cache_.get();
1147 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1148 if (rv != net::OK) {
1149 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1150 << net::ErrorToString(rv);
1151 ScheduleDeleteAndStartOver();
1153 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1156 void ServiceWorkerStorage::StartPurgingResources(
1157 const std::vector<int64>& ids) {
1158 DCHECK(has_checked_for_stale_resources_);
1159 for (size_t i = 0; i < ids.size(); ++i)
1160 purgeable_resource_ids_.push_back(ids[i]);
1161 ContinuePurgingResources();
1164 void ServiceWorkerStorage::StartPurgingResources(
1165 const ResourceList& resources) {
1166 DCHECK(has_checked_for_stale_resources_);
1167 for (size_t i = 0; i < resources.size(); ++i)
1168 purgeable_resource_ids_.push_back(resources[i].resource_id);
1169 ContinuePurgingResources();
1172 void ServiceWorkerStorage::ContinuePurgingResources() {
1173 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1176 // Do one at a time until we're done, use RunSoon to avoid recursion when
1177 // DoomEntry returns immediately.
1178 is_purge_pending_ = true;
1179 int64 id = purgeable_resource_ids_.front();
1180 purgeable_resource_ids_.pop_front();
1182 base::Bind(&ServiceWorkerStorage::PurgeResource,
1183 weak_factory_.GetWeakPtr(), id));
1186 void ServiceWorkerStorage::PurgeResource(int64 id) {
1187 DCHECK(is_purge_pending_);
1188 int rv = disk_cache()->DoomEntry(
1189 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1190 weak_factory_.GetWeakPtr(), id));
1191 if (rv != net::ERR_IO_PENDING)
1192 OnResourcePurged(id, rv);
1195 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1196 DCHECK(is_purge_pending_);
1197 is_purge_pending_ = false;
1199 database_task_manager_->GetTaskRunner()->PostTask(
1201 base::Bind(base::IgnoreResult(
1202 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1203 base::Unretained(database_.get()),
1204 std::set<int64>(&id, &id + 1)));
1206 ContinuePurgingResources();
1209 void ServiceWorkerStorage::DeleteStaleResources() {
1210 DCHECK(!has_checked_for_stale_resources_);
1211 has_checked_for_stale_resources_ = true;
1212 database_task_manager_->GetTaskRunner()->PostTask(
1214 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1216 base::MessageLoopProxy::current(),
1217 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1218 weak_factory_.GetWeakPtr())));
1221 void ServiceWorkerStorage::DidCollectStaleResources(
1222 const std::vector<int64>& stale_resource_ids,
1223 ServiceWorkerDatabase::Status status) {
1224 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1225 if (status != ServiceWorkerDatabase::STATUS_OK)
1227 StartPurgingResources(stale_resource_ids);
1230 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1231 // Can be null in tests.
1232 if (!special_storage_policy_.get())
1235 if (!special_storage_policy_->HasSessionOnlyOrigins())
1238 std::set<GURL> session_only_origins;
1239 for (const GURL& origin : registered_origins_) {
1240 if (special_storage_policy_->IsStorageSessionOnly(origin))
1241 session_only_origins.insert(origin);
1244 database_task_manager_->GetShutdownBlockingTaskRunner()->PostTask(
1246 base::Bind(&DeleteAllDataForOriginsFromDB,
1248 session_only_origins));
1251 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1252 ServiceWorkerDatabase* database,
1253 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1254 const GetResourcesCallback& callback) {
1255 std::set<int64> ids;
1256 ServiceWorkerDatabase::Status status =
1257 database->GetUncommittedResourceIds(&ids);
1258 if (status != ServiceWorkerDatabase::STATUS_OK) {
1259 original_task_runner->PostTask(
1262 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1266 status = database->PurgeUncommittedResourceIds(ids);
1267 if (status != ServiceWorkerDatabase::STATUS_OK) {
1268 original_task_runner->PostTask(
1271 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1276 status = database->GetPurgeableResourceIds(&ids);
1277 original_task_runner->PostTask(
1279 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1282 void ServiceWorkerStorage::ReadInitialDataFromDB(
1283 ServiceWorkerDatabase* database,
1284 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1285 const InitializeCallback& callback) {
1287 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1288 new ServiceWorkerStorage::InitialData());
1290 ServiceWorkerDatabase::Status status =
1291 database->GetNextAvailableIds(&data->next_registration_id,
1292 &data->next_version_id,
1293 &data->next_resource_id);
1294 if (status != ServiceWorkerDatabase::STATUS_OK) {
1295 original_task_runner->PostTask(
1296 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1300 status = database->GetOriginsWithRegistrations(&data->origins);
1301 original_task_runner->PostTask(
1302 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1305 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1306 ServiceWorkerDatabase* database,
1307 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1308 int64 registration_id,
1310 const DeleteRegistrationCallback& callback) {
1313 ServiceWorkerDatabase::RegistrationData deleted_version;
1314 std::vector<int64> newly_purgeable_resources;
1315 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1316 registration_id, origin, &deleted_version, &newly_purgeable_resources);
1317 if (status != ServiceWorkerDatabase::STATUS_OK) {
1318 original_task_runner->PostTask(
1321 callback, false, deleted_version, std::vector<int64>(), status));
1325 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1326 // unique origin list.
1327 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1328 status = database->GetRegistrationsForOrigin(origin, ®istrations);
1329 if (status != ServiceWorkerDatabase::STATUS_OK) {
1330 original_task_runner->PostTask(
1333 callback, false, deleted_version, std::vector<int64>(), status));
1337 bool deletable = registrations.empty();
1338 original_task_runner->PostTask(FROM_HERE,
1339 base::Bind(callback,
1342 newly_purgeable_resources,
1346 void ServiceWorkerStorage::WriteRegistrationInDB(
1347 ServiceWorkerDatabase* database,
1348 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1349 const ServiceWorkerDatabase::RegistrationData& data,
1350 const ResourceList& resources,
1351 const WriteRegistrationCallback& callback) {
1353 ServiceWorkerDatabase::RegistrationData deleted_version;
1354 std::vector<int64> newly_purgeable_resources;
1355 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1356 data, resources, &deleted_version, &newly_purgeable_resources);
1357 original_task_runner->PostTask(FROM_HERE,
1358 base::Bind(callback,
1359 data.script.GetOrigin(),
1361 newly_purgeable_resources,
1365 void ServiceWorkerStorage::FindForDocumentInDB(
1366 ServiceWorkerDatabase* database,
1367 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1368 const GURL& document_url,
1369 const FindInDBCallback& callback) {
1370 GURL origin = document_url.GetOrigin();
1371 RegistrationList registrations;
1372 ServiceWorkerDatabase::Status status =
1373 database->GetRegistrationsForOrigin(origin, ®istrations);
1374 if (status != ServiceWorkerDatabase::STATUS_OK) {
1375 original_task_runner->PostTask(
1377 base::Bind(callback,
1378 ServiceWorkerDatabase::RegistrationData(),
1384 ServiceWorkerDatabase::RegistrationData data;
1385 ResourceList resources;
1386 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1388 // Find one with a pattern match.
1389 LongestScopeMatcher matcher(document_url);
1390 int64 match = kInvalidServiceWorkerRegistrationId;
1391 for (size_t i = 0; i < registrations.size(); ++i) {
1392 if (matcher.MatchLongest(registrations[i].scope))
1393 match = registrations[i].registration_id;
1396 if (match != kInvalidServiceWorkerRegistrationId)
1397 status = database->ReadRegistration(match, origin, &data, &resources);
1399 original_task_runner->PostTask(
1401 base::Bind(callback, data, resources, status));
1404 void ServiceWorkerStorage::FindForPatternInDB(
1405 ServiceWorkerDatabase* database,
1406 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1408 const FindInDBCallback& callback) {
1409 GURL origin = scope.GetOrigin();
1410 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1411 ServiceWorkerDatabase::Status status =
1412 database->GetRegistrationsForOrigin(origin, ®istrations);
1413 if (status != ServiceWorkerDatabase::STATUS_OK) {
1414 original_task_runner->PostTask(
1416 base::Bind(callback,
1417 ServiceWorkerDatabase::RegistrationData(),
1423 // Find one with an exact matching scope.
1424 ServiceWorkerDatabase::RegistrationData data;
1425 ResourceList resources;
1426 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1427 for (RegistrationList::const_iterator it = registrations.begin();
1428 it != registrations.end(); ++it) {
1429 if (scope != it->scope)
1431 status = database->ReadRegistration(it->registration_id, origin,
1433 break; // We're done looping.
1436 original_task_runner->PostTask(
1438 base::Bind(callback, data, resources, status));
1441 void ServiceWorkerStorage::FindForIdInDB(
1442 ServiceWorkerDatabase* database,
1443 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1444 int64 registration_id,
1446 const FindInDBCallback& callback) {
1447 ServiceWorkerDatabase::RegistrationData data;
1448 ResourceList resources;
1449 ServiceWorkerDatabase::Status status =
1450 database->ReadRegistration(registration_id, origin, &data, &resources);
1451 original_task_runner->PostTask(
1452 FROM_HERE, base::Bind(callback, data, resources, status));
1455 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1456 ServiceWorkerDatabase* database,
1457 const std::set<GURL>& origins) {
1460 std::vector<int64> newly_purgeable_resources;
1461 database->DeleteAllDataForOrigins(origins, &newly_purgeable_resources);
1464 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1465 // is transient and it can get healed soon (e.g. IO error). To do that, the
1466 // database should not disable itself when an error occurs and the storage
1467 // controls it instead.
1468 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1469 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1471 if (state_ == DISABLED) {
1472 // Recovery process has already been scheduled.
1477 DVLOG(1) << "Schedule to delete the context and start over.";
1478 context_->ScheduleDeleteAndStartOver();
1481 void ServiceWorkerStorage::DidDeleteDatabase(
1482 const StatusCallback& callback,
1483 ServiceWorkerDatabase::Status status) {
1484 DCHECK_EQ(DISABLED, state_);
1485 if (status != ServiceWorkerDatabase::STATUS_OK) {
1486 // Give up the corruption recovery until the browser restarts.
1487 LOG(ERROR) << "Failed to delete the database: "
1488 << ServiceWorkerDatabase::StatusToString(status);
1489 callback.Run(DatabaseStatusToStatusCode(status));
1492 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1494 // Delete the disk cache on the cache thread.
1495 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1496 // Deleting the directory could take a long time and restart could be delayed.
1497 // We should probably rename the directory and delete it later.
1498 PostTaskAndReplyWithResult(
1499 database_task_manager_->GetTaskRunner(),
1501 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1502 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1503 weak_factory_.GetWeakPtr(),
1507 void ServiceWorkerStorage::DidDeleteDiskCache(
1508 const StatusCallback& callback, bool result) {
1509 DCHECK_EQ(DISABLED, state_);
1511 // Give up the corruption recovery until the browser restarts.
1512 LOG(ERROR) << "Failed to delete the diskcache.";
1513 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1516 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1517 callback.Run(SERVICE_WORKER_OK);
1520 } // namespace content