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