The serviceworker update algo has to wait until documents close prior to performing...
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blob83e8c798fb3f5ea20465a51e8f1c23d631662876
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"
7 #include <string>
9 #include "base/bind_helpers.h"
10 #include "base/file_util.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/task_runner_util.h"
14 #include "content/browser/service_worker/service_worker_context_core.h"
15 #include "content/browser/service_worker/service_worker_disk_cache.h"
16 #include "content/browser/service_worker/service_worker_info.h"
17 #include "content/browser/service_worker/service_worker_metrics.h"
18 #include "content/browser/service_worker/service_worker_registration.h"
19 #include "content/browser/service_worker/service_worker_utils.h"
20 #include "content/browser/service_worker/service_worker_version.h"
21 #include "content/common/service_worker/service_worker_types.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "net/base/completion_callback.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/net_errors.h"
26 #include "webkit/browser/quota/quota_manager_proxy.h"
28 namespace content {
30 namespace {
32 void RunSoon(const tracked_objects::Location& from_here,
33 const base::Closure& closure) {
34 base::MessageLoop::current()->PostTask(from_here, closure);
37 void CompleteFindNow(
38 const scoped_refptr<ServiceWorkerRegistration>& registration,
39 ServiceWorkerStatusCode status,
40 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
41 callback.Run(status, registration);
44 void CompleteFindSoon(
45 const tracked_objects::Location& from_here,
46 const scoped_refptr<ServiceWorkerRegistration>& registration,
47 ServiceWorkerStatusCode status,
48 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
49 RunSoon(from_here, base::Bind(callback, status, registration));
52 const base::FilePath::CharType kServiceWorkerDirectory[] =
53 FILE_PATH_LITERAL("Service Worker");
54 const base::FilePath::CharType kDatabaseName[] =
55 FILE_PATH_LITERAL("Database");
56 const base::FilePath::CharType kDiskCacheName[] =
57 FILE_PATH_LITERAL("Cache");
59 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
60 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
62 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
63 ServiceWorkerDatabase::Status status) {
64 switch (status) {
65 case ServiceWorkerDatabase::STATUS_OK:
66 return SERVICE_WORKER_OK;
67 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
68 return SERVICE_WORKER_ERROR_NOT_FOUND;
69 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
70 NOTREACHED();
71 default:
72 return SERVICE_WORKER_ERROR_FAILED;
76 class ResponseComparer : public base::RefCounted<ResponseComparer> {
77 public:
78 ResponseComparer(
79 base::WeakPtr<ServiceWorkerStorage> owner,
80 scoped_ptr<ServiceWorkerResponseReader> lhs,
81 scoped_ptr<ServiceWorkerResponseReader> rhs,
82 const ServiceWorkerStorage::CompareCallback& callback)
83 : owner_(owner),
84 completion_callback_(callback),
85 lhs_reader_(lhs.release()),
86 rhs_reader_(rhs.release()),
87 completion_count_(0),
88 previous_result_(0) {
91 void Start();
93 private:
94 friend class base::RefCounted<ResponseComparer>;
96 static const int kBufferSize = 16 * 1024;
98 ~ResponseComparer() {}
99 void ReadInfos();
100 void OnReadInfoComplete(int result);
101 void ReadSomeData();
102 void OnReadDataComplete(int result);
104 base::WeakPtr<ServiceWorkerStorage> owner_;
105 ServiceWorkerStorage::CompareCallback completion_callback_;
106 scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
107 scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
108 scoped_refptr<net::IOBuffer> lhs_buffer_;
109 scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
110 scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
111 scoped_refptr<net::IOBuffer> rhs_buffer_;
112 int completion_count_;
113 int previous_result_;
114 DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
117 void ResponseComparer::Start() {
118 lhs_buffer_ = new net::IOBuffer(kBufferSize);
119 lhs_info_ = new HttpResponseInfoIOBuffer();
120 rhs_buffer_ = new net::IOBuffer(kBufferSize);
121 rhs_info_ = new HttpResponseInfoIOBuffer();
123 ReadInfos();
126 void ResponseComparer::ReadInfos() {
127 lhs_reader_->ReadInfo(
128 lhs_info_,
129 base::Bind(&ResponseComparer::OnReadInfoComplete,
130 this));
131 rhs_reader_->ReadInfo(
132 rhs_info_,
133 base::Bind(&ResponseComparer::OnReadInfoComplete,
134 this));
137 void ResponseComparer::OnReadInfoComplete(int result) {
138 if (completion_callback_.is_null() || !owner_)
139 return;
140 if (result < 0) {
141 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
142 completion_callback_.Reset();
143 return;
145 if (++completion_count_ != 2)
146 return;
148 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
149 completion_callback_.Run(SERVICE_WORKER_OK, false);
150 return;
152 ReadSomeData();
155 void ResponseComparer::ReadSomeData() {
156 completion_count_ = 0;
157 lhs_reader_->ReadData(
158 lhs_buffer_,
159 kBufferSize,
160 base::Bind(&ResponseComparer::OnReadDataComplete, this));
161 rhs_reader_->ReadData(
162 rhs_buffer_,
163 kBufferSize,
164 base::Bind(&ResponseComparer::OnReadDataComplete, this));
167 void ResponseComparer::OnReadDataComplete(int result) {
168 if (completion_callback_.is_null() || !owner_)
169 return;
170 if (result < 0) {
171 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
172 completion_callback_.Reset();
173 return;
175 if (++completion_count_ != 2) {
176 previous_result_ = result;
177 return;
180 // TODO(michaeln): Probably shouldn't assume that the amounts read from
181 // each reader will always be the same. This would wrongly signal false
182 // in that case.
183 if (result != previous_result_) {
184 completion_callback_.Run(SERVICE_WORKER_OK, false);
185 return;
188 if (result == 0) {
189 completion_callback_.Run(SERVICE_WORKER_OK, true);
190 return;
193 int compare_result =
194 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
195 if (compare_result != 0) {
196 completion_callback_.Run(SERVICE_WORKER_OK, false);
197 return;
200 ReadSomeData();
203 } // namespace
205 ServiceWorkerStorage::InitialData::InitialData()
206 : next_registration_id(kInvalidServiceWorkerRegistrationId),
207 next_version_id(kInvalidServiceWorkerVersionId),
208 next_resource_id(kInvalidServiceWorkerResourceId) {
211 ServiceWorkerStorage::InitialData::~InitialData() {
214 ServiceWorkerStorage::
215 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
216 : registration_id(kInvalidServiceWorkerRegistrationId) {
219 ServiceWorkerStorage::
220 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
223 ServiceWorkerStorage::~ServiceWorkerStorage() {
224 weak_factory_.InvalidateWeakPtrs();
225 database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
228 // static
229 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
230 const base::FilePath& path,
231 base::WeakPtr<ServiceWorkerContextCore> context,
232 base::SequencedTaskRunner* database_task_runner,
233 base::MessageLoopProxy* disk_cache_thread,
234 quota::QuotaManagerProxy* quota_manager_proxy) {
235 return make_scoped_ptr(
236 new ServiceWorkerStorage(path,
237 context,
238 database_task_runner,
239 disk_cache_thread,
240 quota_manager_proxy));
243 // static
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_,
249 context,
250 old_storage->database_task_runner_,
251 old_storage->disk_cache_thread_,
252 old_storage->quota_manager_proxy_));
255 void ServiceWorkerStorage::FindRegistrationForDocument(
256 const GURL& document_url,
257 const FindRegistrationCallback& callback) {
258 DCHECK(!document_url.has_ref());
259 if (!LazyInitialize(base::Bind(
260 &ServiceWorkerStorage::FindRegistrationForDocument,
261 weak_factory_.GetWeakPtr(), document_url, callback))) {
262 if (state_ != INITIALIZING || !context_) {
263 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
264 SERVICE_WORKER_ERROR_FAILED, callback);
266 return;
268 DCHECK_EQ(INITIALIZED, state_);
270 // See if there are any stored registrations for the origin.
271 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
272 // Look for something currently being installed.
273 scoped_refptr<ServiceWorkerRegistration> installing_registration =
274 FindInstallingRegistrationForDocument(document_url);
275 CompleteFindNow(
276 installing_registration,
277 installing_registration ?
278 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
279 callback);
280 return;
283 database_task_runner_->PostTask(
284 FROM_HERE,
285 base::Bind(
286 &FindForDocumentInDB,
287 database_.get(),
288 base::MessageLoopProxy::current(),
289 document_url,
290 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
291 weak_factory_.GetWeakPtr(), document_url, callback)));
294 void ServiceWorkerStorage::FindRegistrationForPattern(
295 const GURL& scope,
296 const FindRegistrationCallback& callback) {
297 if (!LazyInitialize(base::Bind(
298 &ServiceWorkerStorage::FindRegistrationForPattern,
299 weak_factory_.GetWeakPtr(), scope, callback))) {
300 if (state_ != INITIALIZING || !context_) {
301 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
302 SERVICE_WORKER_ERROR_FAILED, callback);
304 return;
306 DCHECK_EQ(INITIALIZED, state_);
308 // See if there are any stored registrations for the origin.
309 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
310 // Look for something currently being installed.
311 scoped_refptr<ServiceWorkerRegistration> installing_registration =
312 FindInstallingRegistrationForPattern(scope);
313 CompleteFindSoon(
314 FROM_HERE, installing_registration,
315 installing_registration ?
316 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
317 callback);
318 return;
321 database_task_runner_->PostTask(
322 FROM_HERE,
323 base::Bind(
324 &FindForPatternInDB,
325 database_.get(),
326 base::MessageLoopProxy::current(),
327 scope,
328 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
329 weak_factory_.GetWeakPtr(), scope, callback)));
332 void ServiceWorkerStorage::FindRegistrationForId(
333 int64 registration_id,
334 const GURL& origin,
335 const FindRegistrationCallback& callback) {
336 if (!LazyInitialize(base::Bind(
337 &ServiceWorkerStorage::FindRegistrationForId,
338 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
339 if (state_ != INITIALIZING || !context_) {
340 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
341 SERVICE_WORKER_ERROR_FAILED, callback);
343 return;
345 DCHECK_EQ(INITIALIZED, state_);
347 // See if there are any stored registrations for the origin.
348 if (!ContainsKey(registered_origins_, origin)) {
349 // Look for something currently being installed.
350 scoped_refptr<ServiceWorkerRegistration> installing_registration =
351 FindInstallingRegistrationForId(registration_id);
352 CompleteFindNow(
353 installing_registration,
354 installing_registration ?
355 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
356 callback);
357 return;
360 scoped_refptr<ServiceWorkerRegistration> registration =
361 context_->GetLiveRegistration(registration_id);
362 if (registration) {
363 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
364 return;
367 database_task_runner_->PostTask(
368 FROM_HERE,
369 base::Bind(&FindForIdInDB,
370 database_.get(),
371 base::MessageLoopProxy::current(),
372 registration_id, origin,
373 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
374 weak_factory_.GetWeakPtr(), callback)));
377 void ServiceWorkerStorage::GetAllRegistrations(
378 const GetAllRegistrationInfosCallback& callback) {
379 if (!LazyInitialize(base::Bind(
380 &ServiceWorkerStorage::GetAllRegistrations,
381 weak_factory_.GetWeakPtr(), callback))) {
382 if (state_ != INITIALIZING || !context_) {
383 RunSoon(FROM_HERE, base::Bind(
384 callback, std::vector<ServiceWorkerRegistrationInfo>()));
386 return;
388 DCHECK_EQ(INITIALIZED, state_);
390 RegistrationList* registrations = new RegistrationList;
391 PostTaskAndReplyWithResult(
392 database_task_runner_,
393 FROM_HERE,
394 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
395 base::Unretained(database_.get()),
396 base::Unretained(registrations)),
397 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
398 weak_factory_.GetWeakPtr(),
399 callback,
400 base::Owned(registrations)));
403 void ServiceWorkerStorage::StoreRegistration(
404 ServiceWorkerRegistration* registration,
405 ServiceWorkerVersion* version,
406 const StatusCallback& callback) {
407 DCHECK(registration);
408 DCHECK(version);
410 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
411 if (IsDisabled() || !context_) {
412 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
413 return;
416 ServiceWorkerDatabase::RegistrationData data;
417 data.registration_id = registration->id();
418 data.scope = registration->pattern();
419 data.script = registration->script_url();
420 data.has_fetch_handler = true;
421 data.version_id = version->version_id();
422 data.last_update_check = base::Time::Now();
423 data.is_active = false; // initially stored in the waiting state
425 ResourceList resources;
426 version->script_cache_map()->GetResources(&resources);
428 if (!has_checked_for_stale_resources_)
429 DeleteStaleResources();
431 database_task_runner_->PostTask(
432 FROM_HERE,
433 base::Bind(&WriteRegistrationInDB,
434 database_.get(),
435 base::MessageLoopProxy::current(),
436 data, resources,
437 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
438 weak_factory_.GetWeakPtr(),
439 callback)));
442 void ServiceWorkerStorage::UpdateToActiveState(
443 ServiceWorkerRegistration* registration,
444 const StatusCallback& callback) {
445 DCHECK(registration);
447 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
448 if (IsDisabled() || !context_) {
449 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
450 return;
453 PostTaskAndReplyWithResult(
454 database_task_runner_,
455 FROM_HERE,
456 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
457 base::Unretained(database_.get()),
458 registration->id(),
459 registration->script_url().GetOrigin()),
460 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
461 weak_factory_.GetWeakPtr(),
462 callback));
465 void ServiceWorkerStorage::DeleteRegistration(
466 int64 registration_id,
467 const GURL& origin,
468 const StatusCallback& callback) {
469 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
470 if (IsDisabled() || !context_) {
471 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
472 return;
475 if (!has_checked_for_stale_resources_)
476 DeleteStaleResources();
478 DidDeleteRegistrationParams params;
479 params.registration_id = registration_id;
480 params.origin = origin;
481 params.callback = callback;
483 database_task_runner_->PostTask(
484 FROM_HERE,
485 base::Bind(&DeleteRegistrationFromDB,
486 database_.get(),
487 base::MessageLoopProxy::current(),
488 registration_id, origin,
489 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
490 weak_factory_.GetWeakPtr(), params)));
492 // The registration should no longer be findable.
493 pending_deletions_.insert(registration_id);
494 ServiceWorkerRegistration* registration =
495 context_->GetLiveRegistration(registration_id);
496 if (registration)
497 registration->set_is_deleted();
500 scoped_ptr<ServiceWorkerResponseReader>
501 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
502 return make_scoped_ptr(
503 new ServiceWorkerResponseReader(response_id, disk_cache()));
506 scoped_ptr<ServiceWorkerResponseWriter>
507 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
508 return make_scoped_ptr(
509 new ServiceWorkerResponseWriter(response_id, disk_cache()));
512 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
513 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
514 DCHECK_EQ(INITIALIZED, state_);
516 if (!has_checked_for_stale_resources_)
517 DeleteStaleResources();
519 database_task_runner_->PostTask(
520 FROM_HERE,
521 base::Bind(base::IgnoreResult(
522 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
523 base::Unretained(database_.get()),
524 std::set<int64>(&id, &id + 1)));
527 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
528 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
529 database_task_runner_->PostTask(
530 FROM_HERE,
531 base::Bind(base::IgnoreResult(
532 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
533 base::Unretained(database_.get()),
534 std::set<int64>(&id, &id + 1)));
535 StartPurgingResources(std::vector<int64>(1, id));
538 void ServiceWorkerStorage::CompareScriptResources(
539 int64 lhs_id, int64 rhs_id,
540 const CompareCallback& callback) {
541 DCHECK(!callback.is_null());
542 scoped_refptr<ResponseComparer> comparer =
543 new ResponseComparer(weak_factory_.GetWeakPtr(),
544 CreateResponseReader(lhs_id),
545 CreateResponseReader(rhs_id),
546 callback);
547 comparer->Start(); // It deletes itself when done.
550 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
551 Disable();
553 // Delete the database on the database thread.
554 PostTaskAndReplyWithResult(
555 database_task_runner_,
556 FROM_HERE,
557 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
558 base::Unretained(database_.get())),
559 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
560 weak_factory_.GetWeakPtr(), callback));
563 int64 ServiceWorkerStorage::NewRegistrationId() {
564 if (state_ == DISABLED)
565 return kInvalidServiceWorkerRegistrationId;
566 DCHECK_EQ(INITIALIZED, state_);
567 return next_registration_id_++;
570 int64 ServiceWorkerStorage::NewVersionId() {
571 if (state_ == DISABLED)
572 return kInvalidServiceWorkerVersionId;
573 DCHECK_EQ(INITIALIZED, state_);
574 return next_version_id_++;
577 int64 ServiceWorkerStorage::NewResourceId() {
578 if (state_ == DISABLED)
579 return kInvalidServiceWorkerResourceId;
580 DCHECK_EQ(INITIALIZED, state_);
581 return next_resource_id_++;
584 void ServiceWorkerStorage::NotifyInstallingRegistration(
585 ServiceWorkerRegistration* registration) {
586 installing_registrations_[registration->id()] = registration;
589 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
590 ServiceWorkerRegistration* registration,
591 ServiceWorkerVersion* version,
592 ServiceWorkerStatusCode status) {
593 installing_registrations_.erase(registration->id());
594 if (status != SERVICE_WORKER_OK && version) {
595 ResourceList resources;
596 version->script_cache_map()->GetResources(&resources);
598 std::set<int64> ids;
599 for (size_t i = 0; i < resources.size(); ++i)
600 ids.insert(resources[i].resource_id);
602 database_task_runner_->PostTask(
603 FROM_HERE,
604 base::Bind(base::IgnoreResult(
605 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
606 base::Unretained(database_.get()),
607 ids));
611 void ServiceWorkerStorage::Disable() {
612 state_ = DISABLED;
613 if (disk_cache_)
614 disk_cache_->Disable();
617 bool ServiceWorkerStorage::IsDisabled() const {
618 return state_ == DISABLED;
621 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
622 if (!has_checked_for_stale_resources_)
623 DeleteStaleResources();
624 StartPurgingResources(resources);
627 ServiceWorkerStorage::ServiceWorkerStorage(
628 const base::FilePath& path,
629 base::WeakPtr<ServiceWorkerContextCore> context,
630 base::SequencedTaskRunner* database_task_runner,
631 base::MessageLoopProxy* disk_cache_thread,
632 quota::QuotaManagerProxy* quota_manager_proxy)
633 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
634 next_version_id_(kInvalidServiceWorkerVersionId),
635 next_resource_id_(kInvalidServiceWorkerResourceId),
636 state_(UNINITIALIZED),
637 path_(path),
638 context_(context),
639 database_task_runner_(database_task_runner),
640 disk_cache_thread_(disk_cache_thread),
641 quota_manager_proxy_(quota_manager_proxy),
642 is_purge_pending_(false),
643 has_checked_for_stale_resources_(false),
644 weak_factory_(this) {
645 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
648 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
649 if (path_.empty())
650 return base::FilePath();
651 return path_.Append(kServiceWorkerDirectory).Append(kDatabaseName);
654 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
655 if (path_.empty())
656 return base::FilePath();
657 return path_.Append(kServiceWorkerDirectory).Append(kDiskCacheName);
660 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
661 if (!context_)
662 return false;
664 switch (state_) {
665 case INITIALIZED:
666 return true;
667 case DISABLED:
668 return false;
669 case INITIALIZING:
670 pending_tasks_.push_back(callback);
671 return false;
672 case UNINITIALIZED:
673 pending_tasks_.push_back(callback);
674 // Fall-through.
677 state_ = INITIALIZING;
678 database_task_runner_->PostTask(
679 FROM_HERE,
680 base::Bind(&ReadInitialDataFromDB,
681 database_.get(),
682 base::MessageLoopProxy::current(),
683 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
684 weak_factory_.GetWeakPtr())));
685 return false;
688 void ServiceWorkerStorage::DidReadInitialData(
689 InitialData* data,
690 ServiceWorkerDatabase::Status status) {
691 DCHECK(data);
692 DCHECK_EQ(INITIALIZING, state_);
694 if (status == ServiceWorkerDatabase::STATUS_OK) {
695 next_registration_id_ = data->next_registration_id;
696 next_version_id_ = data->next_version_id;
697 next_resource_id_ = data->next_resource_id;
698 registered_origins_.swap(data->origins);
699 state_ = INITIALIZED;
700 } else {
701 // TODO(nhiroki): Stringify |status| using StatusToString() defined in
702 // service_worker_database.cc.
703 DVLOG(2) << "Failed to initialize: " << status;
704 ScheduleDeleteAndStartOver();
707 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
708 it != pending_tasks_.end(); ++it) {
709 RunSoon(FROM_HERE, *it);
711 pending_tasks_.clear();
714 void ServiceWorkerStorage::DidFindRegistrationForDocument(
715 const GURL& document_url,
716 const FindRegistrationCallback& callback,
717 const ServiceWorkerDatabase::RegistrationData& data,
718 const ResourceList& resources,
719 ServiceWorkerDatabase::Status status) {
720 if (status == ServiceWorkerDatabase::STATUS_OK) {
721 ReturnFoundRegistration(callback, data, resources);
722 return;
725 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
726 // Look for something currently being installed.
727 scoped_refptr<ServiceWorkerRegistration> installing_registration =
728 FindInstallingRegistrationForDocument(document_url);
729 callback.Run(installing_registration ?
730 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
731 installing_registration);
732 return;
735 ScheduleDeleteAndStartOver();
736 callback.Run(DatabaseStatusToStatusCode(status),
737 scoped_refptr<ServiceWorkerRegistration>());
740 void ServiceWorkerStorage::DidFindRegistrationForPattern(
741 const GURL& scope,
742 const FindRegistrationCallback& callback,
743 const ServiceWorkerDatabase::RegistrationData& data,
744 const ResourceList& resources,
745 ServiceWorkerDatabase::Status status) {
746 if (status == ServiceWorkerDatabase::STATUS_OK) {
747 ReturnFoundRegistration(callback, data, resources);
748 return;
751 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
752 scoped_refptr<ServiceWorkerRegistration> installing_registration =
753 FindInstallingRegistrationForPattern(scope);
754 callback.Run(installing_registration ?
755 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
756 installing_registration);
757 return;
760 ScheduleDeleteAndStartOver();
761 callback.Run(DatabaseStatusToStatusCode(status),
762 scoped_refptr<ServiceWorkerRegistration>());
765 void ServiceWorkerStorage::DidFindRegistrationForId(
766 const FindRegistrationCallback& callback,
767 const ServiceWorkerDatabase::RegistrationData& data,
768 const ResourceList& resources,
769 ServiceWorkerDatabase::Status status) {
770 if (status == ServiceWorkerDatabase::STATUS_OK) {
771 ReturnFoundRegistration(callback, data, resources);
772 return;
775 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
776 // TODO(nhiroki): Find a registration in |installing_registrations_|.
777 callback.Run(DatabaseStatusToStatusCode(status),
778 scoped_refptr<ServiceWorkerRegistration>());
779 return;
782 ScheduleDeleteAndStartOver();
783 callback.Run(DatabaseStatusToStatusCode(status),
784 scoped_refptr<ServiceWorkerRegistration>());
787 void ServiceWorkerStorage::ReturnFoundRegistration(
788 const FindRegistrationCallback& callback,
789 const ServiceWorkerDatabase::RegistrationData& data,
790 const ResourceList& resources) {
791 scoped_refptr<ServiceWorkerRegistration> registration =
792 GetOrCreateRegistration(data, resources);
793 if (registration->is_deleted()) {
794 // It's past the point of no return and no longer findable.
795 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
796 return;
798 callback.Run(SERVICE_WORKER_OK, registration);
801 void ServiceWorkerStorage::DidGetAllRegistrations(
802 const GetAllRegistrationInfosCallback& callback,
803 RegistrationList* registrations,
804 ServiceWorkerDatabase::Status status) {
805 DCHECK(registrations);
806 if (status != ServiceWorkerDatabase::STATUS_OK &&
807 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
808 ScheduleDeleteAndStartOver();
809 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
810 return;
813 // Add all stored registrations.
814 std::set<int64> pushed_registrations;
815 std::vector<ServiceWorkerRegistrationInfo> infos;
816 for (RegistrationList::const_iterator it = registrations->begin();
817 it != registrations->end(); ++it) {
818 const bool inserted =
819 pushed_registrations.insert(it->registration_id).second;
820 DCHECK(inserted);
822 ServiceWorkerRegistration* registration =
823 context_->GetLiveRegistration(it->registration_id);
824 if (registration) {
825 infos.push_back(registration->GetInfo());
826 continue;
829 ServiceWorkerRegistrationInfo info;
830 info.pattern = it->scope;
831 info.script_url = it->script;
832 info.registration_id = it->registration_id;
833 if (ServiceWorkerVersion* version =
834 context_->GetLiveVersion(it->version_id)) {
835 if (it->is_active)
836 info.active_version = version->GetInfo();
837 else
838 info.waiting_version = version->GetInfo();
839 infos.push_back(info);
840 continue;
843 if (it->is_active) {
844 info.active_version.is_null = false;
845 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
846 info.active_version.version_id = it->version_id;
847 } else {
848 info.waiting_version.is_null = false;
849 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
850 info.waiting_version.version_id = it->version_id;
852 infos.push_back(info);
855 // Add unstored registrations that are being installed.
856 for (RegistrationRefsById::const_iterator it =
857 installing_registrations_.begin();
858 it != installing_registrations_.end(); ++it) {
859 if (pushed_registrations.insert(it->first).second)
860 infos.push_back(it->second->GetInfo());
863 callback.Run(infos);
866 void ServiceWorkerStorage::DidStoreRegistration(
867 const StatusCallback& callback,
868 const GURL& origin,
869 int64 deleted_version_id,
870 const std::vector<int64>& newly_purgeable_resources,
871 ServiceWorkerDatabase::Status status) {
872 if (status != ServiceWorkerDatabase::STATUS_OK) {
873 ScheduleDeleteAndStartOver();
874 callback.Run(DatabaseStatusToStatusCode(status));
875 return;
877 registered_origins_.insert(origin);
878 callback.Run(SERVICE_WORKER_OK);
880 if (!context_ || !context_->GetLiveVersion(deleted_version_id))
881 StartPurgingResources(newly_purgeable_resources);
884 void ServiceWorkerStorage::DidUpdateToActiveState(
885 const StatusCallback& callback,
886 ServiceWorkerDatabase::Status status) {
887 if (status != ServiceWorkerDatabase::STATUS_OK &&
888 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
889 ScheduleDeleteAndStartOver();
891 callback.Run(DatabaseStatusToStatusCode(status));
894 void ServiceWorkerStorage::DidDeleteRegistration(
895 const DidDeleteRegistrationParams& params,
896 bool origin_is_deletable,
897 int64 version_id,
898 const std::vector<int64>& newly_purgeable_resources,
899 ServiceWorkerDatabase::Status status) {
900 pending_deletions_.erase(params.registration_id);
901 if (status != ServiceWorkerDatabase::STATUS_OK) {
902 ScheduleDeleteAndStartOver();
903 params.callback.Run(DatabaseStatusToStatusCode(status));
904 return;
906 if (origin_is_deletable)
907 registered_origins_.erase(params.origin);
908 params.callback.Run(SERVICE_WORKER_OK);
910 if (!context_ || !context_->GetLiveVersion(version_id))
911 StartPurgingResources(newly_purgeable_resources);
914 scoped_refptr<ServiceWorkerRegistration>
915 ServiceWorkerStorage::GetOrCreateRegistration(
916 const ServiceWorkerDatabase::RegistrationData& data,
917 const ResourceList& resources) {
918 scoped_refptr<ServiceWorkerRegistration> registration =
919 context_->GetLiveRegistration(data.registration_id);
920 if (registration)
921 return registration;
923 registration = new ServiceWorkerRegistration(
924 data.scope, data.script, data.registration_id, context_);
925 if (pending_deletions_.find(data.registration_id) !=
926 pending_deletions_.end()) {
927 registration->set_is_deleted();
929 scoped_refptr<ServiceWorkerVersion> version =
930 context_->GetLiveVersion(data.version_id);
931 if (!version) {
932 version = new ServiceWorkerVersion(registration, data.version_id, context_);
933 version->SetStatus(data.is_active ?
934 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
935 version->script_cache_map()->SetResources(resources);
938 if (version->status() == ServiceWorkerVersion::ACTIVATED)
939 registration->SetActiveVersion(version);
940 else if (version->status() == ServiceWorkerVersion::INSTALLED)
941 registration->SetWaitingVersion(version);
942 else
943 NOTREACHED();
945 return registration;
948 ServiceWorkerRegistration*
949 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
950 const GURL& document_url) {
951 DCHECK(!document_url.has_ref());
953 LongestScopeMatcher matcher(document_url);
954 ServiceWorkerRegistration* match = NULL;
956 // TODO(nhiroki): This searches over installing registrations linearly and it
957 // couldn't be scalable. Maybe the regs should be partitioned by origin.
958 for (RegistrationRefsById::const_iterator it =
959 installing_registrations_.begin();
960 it != installing_registrations_.end(); ++it) {
961 if (matcher.MatchLongest(it->second->pattern()))
962 match = it->second;
964 return match;
967 ServiceWorkerRegistration*
968 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
969 const GURL& scope) {
970 for (RegistrationRefsById::const_iterator it =
971 installing_registrations_.begin();
972 it != installing_registrations_.end(); ++it) {
973 if (it->second->pattern() == scope)
974 return it->second;
976 return NULL;
979 ServiceWorkerRegistration*
980 ServiceWorkerStorage::FindInstallingRegistrationForId(
981 int64 registration_id) {
982 RegistrationRefsById::const_iterator found =
983 installing_registrations_.find(registration_id);
984 if (found == installing_registrations_.end())
985 return NULL;
986 return found->second;
989 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
990 if (disk_cache_)
991 return disk_cache_.get();
993 disk_cache_.reset(new ServiceWorkerDiskCache);
995 base::FilePath path = GetDiskCachePath();
996 if (path.empty()) {
997 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
998 net::CompletionCallback());
999 DCHECK_EQ(net::OK, rv);
1000 return disk_cache_.get();
1003 int rv = disk_cache_->InitWithDiskBackend(
1004 path, kMaxDiskCacheSize, false,
1005 disk_cache_thread_.get(),
1006 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1007 weak_factory_.GetWeakPtr()));
1008 if (rv != net::ERR_IO_PENDING)
1009 OnDiskCacheInitialized(rv);
1011 return disk_cache_.get();
1014 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1015 if (rv != net::OK) {
1016 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1017 << net::ErrorToString(rv);
1018 ScheduleDeleteAndStartOver();
1020 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1023 void ServiceWorkerStorage::StartPurgingResources(
1024 const std::vector<int64>& ids) {
1025 DCHECK(has_checked_for_stale_resources_);
1026 for (size_t i = 0; i < ids.size(); ++i)
1027 purgeable_resource_ids_.push_back(ids[i]);
1028 ContinuePurgingResources();
1031 void ServiceWorkerStorage::StartPurgingResources(
1032 const ResourceList& resources) {
1033 DCHECK(has_checked_for_stale_resources_);
1034 for (size_t i = 0; i < resources.size(); ++i)
1035 purgeable_resource_ids_.push_back(resources[i].resource_id);
1036 ContinuePurgingResources();
1039 void ServiceWorkerStorage::ContinuePurgingResources() {
1040 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1041 return;
1043 // Do one at a time until we're done, use RunSoon to avoid recursion when
1044 // DoomEntry returns immediately.
1045 is_purge_pending_ = true;
1046 int64 id = purgeable_resource_ids_.front();
1047 purgeable_resource_ids_.pop_front();
1048 RunSoon(FROM_HERE,
1049 base::Bind(&ServiceWorkerStorage::PurgeResource,
1050 weak_factory_.GetWeakPtr(), id));
1053 void ServiceWorkerStorage::PurgeResource(int64 id) {
1054 DCHECK(is_purge_pending_);
1055 int rv = disk_cache()->DoomEntry(
1056 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1057 weak_factory_.GetWeakPtr(), id));
1058 if (rv != net::ERR_IO_PENDING)
1059 OnResourcePurged(id, rv);
1062 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1063 DCHECK(is_purge_pending_);
1064 is_purge_pending_ = false;
1066 database_task_runner_->PostTask(
1067 FROM_HERE,
1068 base::Bind(base::IgnoreResult(
1069 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1070 base::Unretained(database_.get()),
1071 std::set<int64>(&id, &id + 1)));
1073 ContinuePurgingResources();
1076 void ServiceWorkerStorage::DeleteStaleResources() {
1077 DCHECK(!has_checked_for_stale_resources_);
1078 has_checked_for_stale_resources_ = true;
1079 database_task_runner_->PostTask(
1080 FROM_HERE,
1081 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1082 database_.get(),
1083 base::MessageLoopProxy::current(),
1084 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1085 weak_factory_.GetWeakPtr())));
1088 void ServiceWorkerStorage::DidCollectStaleResources(
1089 const std::vector<int64>& stale_resource_ids,
1090 ServiceWorkerDatabase::Status status) {
1091 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1092 if (status != ServiceWorkerDatabase::STATUS_OK)
1093 return;
1094 StartPurgingResources(stale_resource_ids);
1097 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1098 ServiceWorkerDatabase* database,
1099 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1100 const GetResourcesCallback& callback) {
1101 std::set<int64> ids;
1102 ServiceWorkerDatabase::Status status =
1103 database->GetUncommittedResourceIds(&ids);
1104 if (status != ServiceWorkerDatabase::STATUS_OK) {
1105 original_task_runner->PostTask(
1106 FROM_HERE,
1107 base::Bind(
1108 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1109 return;
1112 status = database->PurgeUncommittedResourceIds(ids);
1113 if (status != ServiceWorkerDatabase::STATUS_OK) {
1114 original_task_runner->PostTask(
1115 FROM_HERE,
1116 base::Bind(
1117 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1118 return;
1121 ids.clear();
1122 status = database->GetPurgeableResourceIds(&ids);
1123 original_task_runner->PostTask(
1124 FROM_HERE,
1125 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1128 void ServiceWorkerStorage::ReadInitialDataFromDB(
1129 ServiceWorkerDatabase* database,
1130 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1131 const InitializeCallback& callback) {
1132 DCHECK(database);
1133 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1134 new ServiceWorkerStorage::InitialData());
1136 ServiceWorkerDatabase::Status status =
1137 database->GetNextAvailableIds(&data->next_registration_id,
1138 &data->next_version_id,
1139 &data->next_resource_id);
1140 if (status != ServiceWorkerDatabase::STATUS_OK) {
1141 original_task_runner->PostTask(
1142 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1143 return;
1146 status = database->GetOriginsWithRegistrations(&data->origins);
1147 original_task_runner->PostTask(
1148 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1151 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1152 ServiceWorkerDatabase* database,
1153 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1154 int64 registration_id,
1155 const GURL& origin,
1156 const DeleteRegistrationCallback& callback) {
1157 DCHECK(database);
1159 int64 version_id = kInvalidServiceWorkerVersionId;
1160 std::vector<int64> newly_purgeable_resources;
1161 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1162 registration_id, origin, &version_id, &newly_purgeable_resources);
1163 if (status != ServiceWorkerDatabase::STATUS_OK) {
1164 original_task_runner->PostTask(FROM_HERE,
1165 base::Bind(callback,
1166 false,
1167 kInvalidServiceWorkerVersionId,
1168 std::vector<int64>(),
1169 status));
1170 return;
1173 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1174 // unique origin list.
1175 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1176 status = database->GetRegistrationsForOrigin(origin, &registrations);
1177 if (status != ServiceWorkerDatabase::STATUS_OK) {
1178 original_task_runner->PostTask(FROM_HERE,
1179 base::Bind(callback,
1180 false,
1181 kInvalidServiceWorkerVersionId,
1182 std::vector<int64>(),
1183 status));
1184 return;
1187 bool deletable = registrations.empty();
1188 original_task_runner->PostTask(
1189 FROM_HERE,
1190 base::Bind(
1191 callback, deletable, version_id, newly_purgeable_resources, status));
1194 void ServiceWorkerStorage::WriteRegistrationInDB(
1195 ServiceWorkerDatabase* database,
1196 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1197 const ServiceWorkerDatabase::RegistrationData& data,
1198 const ResourceList& resources,
1199 const WriteRegistrationCallback& callback) {
1200 DCHECK(database);
1201 int64 deleted_version_id = kInvalidServiceWorkerVersionId;
1202 std::vector<int64> newly_purgeable_resources;
1203 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1204 data, resources, &deleted_version_id, &newly_purgeable_resources);
1205 original_task_runner->PostTask(FROM_HERE,
1206 base::Bind(callback,
1207 data.script.GetOrigin(),
1208 deleted_version_id,
1209 newly_purgeable_resources,
1210 status));
1213 void ServiceWorkerStorage::FindForDocumentInDB(
1214 ServiceWorkerDatabase* database,
1215 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1216 const GURL& document_url,
1217 const FindInDBCallback& callback) {
1218 GURL origin = document_url.GetOrigin();
1219 RegistrationList registrations;
1220 ServiceWorkerDatabase::Status status =
1221 database->GetRegistrationsForOrigin(origin, &registrations);
1222 if (status != ServiceWorkerDatabase::STATUS_OK) {
1223 original_task_runner->PostTask(
1224 FROM_HERE,
1225 base::Bind(callback,
1226 ServiceWorkerDatabase::RegistrationData(),
1227 ResourceList(),
1228 status));
1229 return;
1232 ServiceWorkerDatabase::RegistrationData data;
1233 ResourceList resources;
1234 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1236 // Find one with a pattern match.
1237 LongestScopeMatcher matcher(document_url);
1238 int64 match = kInvalidServiceWorkerRegistrationId;
1239 for (size_t i = 0; i < registrations.size(); ++i) {
1240 if (matcher.MatchLongest(registrations[i].scope))
1241 match = registrations[i].registration_id;
1244 if (match != kInvalidServiceWorkerRegistrationId)
1245 status = database->ReadRegistration(match, origin, &data, &resources);
1247 original_task_runner->PostTask(
1248 FROM_HERE,
1249 base::Bind(callback, data, resources, status));
1252 void ServiceWorkerStorage::FindForPatternInDB(
1253 ServiceWorkerDatabase* database,
1254 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1255 const GURL& scope,
1256 const FindInDBCallback& callback) {
1257 GURL origin = scope.GetOrigin();
1258 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1259 ServiceWorkerDatabase::Status status =
1260 database->GetRegistrationsForOrigin(origin, &registrations);
1261 if (status != ServiceWorkerDatabase::STATUS_OK) {
1262 original_task_runner->PostTask(
1263 FROM_HERE,
1264 base::Bind(callback,
1265 ServiceWorkerDatabase::RegistrationData(),
1266 ResourceList(),
1267 status));
1268 return;
1271 // Find one with an exact matching scope.
1272 ServiceWorkerDatabase::RegistrationData data;
1273 ResourceList resources;
1274 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1275 for (RegistrationList::const_iterator it = registrations.begin();
1276 it != registrations.end(); ++it) {
1277 if (scope != it->scope)
1278 continue;
1279 status = database->ReadRegistration(it->registration_id, origin,
1280 &data, &resources);
1281 break; // We're done looping.
1284 original_task_runner->PostTask(
1285 FROM_HERE,
1286 base::Bind(callback, data, resources, status));
1289 void ServiceWorkerStorage::FindForIdInDB(
1290 ServiceWorkerDatabase* database,
1291 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1292 int64 registration_id,
1293 const GURL& origin,
1294 const FindInDBCallback& callback) {
1295 ServiceWorkerDatabase::RegistrationData data;
1296 ResourceList resources;
1297 ServiceWorkerDatabase::Status status =
1298 database->ReadRegistration(registration_id, origin, &data, &resources);
1299 original_task_runner->PostTask(
1300 FROM_HERE, base::Bind(callback, data, resources, status));
1303 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1304 // is transient and it can get healed soon (e.g. IO error). To do that, the
1305 // database should not disable itself when an error occurs and the storage
1306 // controls it instead.
1307 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1308 if (state_ == DISABLED) {
1309 // Recovery process has already been scheduled.
1310 return;
1312 Disable();
1314 DVLOG(1) << "Schedule to delete the context and start over.";
1315 context_->ScheduleDeleteAndStartOver();
1318 void ServiceWorkerStorage::DidDeleteDatabase(
1319 const StatusCallback& callback,
1320 ServiceWorkerDatabase::Status status) {
1321 DCHECK_EQ(DISABLED, state_);
1322 if (status != ServiceWorkerDatabase::STATUS_OK) {
1323 // Give up the corruption recovery until the browser restarts.
1324 LOG(ERROR) << "Failed to delete the database: " << status;
1325 callback.Run(DatabaseStatusToStatusCode(status));
1326 return;
1328 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1330 // Delete the disk cache on the cache thread.
1331 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1332 // Deleting the directory could take a long time and restart could be delayed.
1333 // We should probably rename the directory and delete it later.
1334 PostTaskAndReplyWithResult(
1335 database_task_runner_,
1336 FROM_HERE,
1337 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1338 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1339 weak_factory_.GetWeakPtr(), callback));
1342 void ServiceWorkerStorage::DidDeleteDiskCache(
1343 const StatusCallback& callback, bool result) {
1344 DCHECK_EQ(DISABLED, state_);
1345 if (!result) {
1346 // Give up the corruption recovery until the browser restarts.
1347 LOG(ERROR) << "Failed to delete the diskcache.";
1348 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1349 return;
1351 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1352 callback.Run(SERVICE_WORKER_OK);
1355 } // namespace content