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/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"
32 void RunSoon(const tracked_objects::Location& from_here,
33 const base::Closure& closure) {
34 base::MessageLoop::current()->PostTask(from_here, closure);
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 kDatabaseName[] =
53 FILE_PATH_LITERAL("Database");
54 const base::FilePath::CharType kDiskCacheName[] =
55 FILE_PATH_LITERAL("Cache");
57 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
58 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
60 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
61 ServiceWorkerDatabase::Status status) {
63 case ServiceWorkerDatabase::STATUS_OK:
64 return SERVICE_WORKER_OK;
65 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
66 return SERVICE_WORKER_ERROR_NOT_FOUND;
67 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
70 return SERVICE_WORKER_ERROR_FAILED;
74 class ResponseComparer : public base::RefCounted<ResponseComparer> {
77 base::WeakPtr<ServiceWorkerStorage> owner,
78 scoped_ptr<ServiceWorkerResponseReader> lhs,
79 scoped_ptr<ServiceWorkerResponseReader> rhs,
80 const ServiceWorkerStorage::CompareCallback& callback)
82 completion_callback_(callback),
83 lhs_reader_(lhs.release()),
84 rhs_reader_(rhs.release()),
92 friend class base::RefCounted<ResponseComparer>;
94 static const int kBufferSize = 16 * 1024;
96 ~ResponseComparer() {}
98 void OnReadInfoComplete(int result);
100 void OnReadDataComplete(int result);
102 base::WeakPtr<ServiceWorkerStorage> owner_;
103 ServiceWorkerStorage::CompareCallback completion_callback_;
104 scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
105 scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
106 scoped_refptr<net::IOBuffer> lhs_buffer_;
107 scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
108 scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
109 scoped_refptr<net::IOBuffer> rhs_buffer_;
110 int completion_count_;
111 int previous_result_;
112 DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
115 void ResponseComparer::Start() {
116 lhs_buffer_ = new net::IOBuffer(kBufferSize);
117 lhs_info_ = new HttpResponseInfoIOBuffer();
118 rhs_buffer_ = new net::IOBuffer(kBufferSize);
119 rhs_info_ = new HttpResponseInfoIOBuffer();
124 void ResponseComparer::ReadInfos() {
125 lhs_reader_->ReadInfo(
127 base::Bind(&ResponseComparer::OnReadInfoComplete,
129 rhs_reader_->ReadInfo(
131 base::Bind(&ResponseComparer::OnReadInfoComplete,
135 void ResponseComparer::OnReadInfoComplete(int result) {
136 if (completion_callback_.is_null() || !owner_)
139 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
140 completion_callback_.Reset();
143 if (++completion_count_ != 2)
146 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
147 completion_callback_.Run(SERVICE_WORKER_OK, false);
153 void ResponseComparer::ReadSomeData() {
154 completion_count_ = 0;
155 lhs_reader_->ReadData(
158 base::Bind(&ResponseComparer::OnReadDataComplete, this));
159 rhs_reader_->ReadData(
162 base::Bind(&ResponseComparer::OnReadDataComplete, this));
165 void ResponseComparer::OnReadDataComplete(int result) {
166 if (completion_callback_.is_null() || !owner_)
169 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
170 completion_callback_.Reset();
173 if (++completion_count_ != 2) {
174 previous_result_ = result;
178 // TODO(michaeln): Probably shouldn't assume that the amounts read from
179 // each reader will always be the same. This would wrongly signal false
181 if (result != previous_result_) {
182 completion_callback_.Run(SERVICE_WORKER_OK, false);
187 completion_callback_.Run(SERVICE_WORKER_OK, true);
192 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
193 if (compare_result != 0) {
194 completion_callback_.Run(SERVICE_WORKER_OK, false);
203 ServiceWorkerStorage::InitialData::InitialData()
204 : next_registration_id(kInvalidServiceWorkerRegistrationId),
205 next_version_id(kInvalidServiceWorkerVersionId),
206 next_resource_id(kInvalidServiceWorkerResourceId) {
209 ServiceWorkerStorage::InitialData::~InitialData() {
212 ServiceWorkerStorage::
213 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
214 : registration_id(kInvalidServiceWorkerRegistrationId) {
217 ServiceWorkerStorage::
218 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
221 ServiceWorkerStorage::~ServiceWorkerStorage() {
222 weak_factory_.InvalidateWeakPtrs();
223 database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
227 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
228 const base::FilePath& path,
229 base::WeakPtr<ServiceWorkerContextCore> context,
230 base::SequencedTaskRunner* database_task_runner,
231 base::MessageLoopProxy* disk_cache_thread,
232 quota::QuotaManagerProxy* quota_manager_proxy) {
233 return make_scoped_ptr(
234 new ServiceWorkerStorage(path,
236 database_task_runner,
238 quota_manager_proxy));
242 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
243 base::WeakPtr<ServiceWorkerContextCore> context,
244 ServiceWorkerStorage* old_storage) {
245 return make_scoped_ptr(
246 new ServiceWorkerStorage(old_storage->path_,
248 old_storage->database_task_runner_,
249 old_storage->disk_cache_thread_,
250 old_storage->quota_manager_proxy_));
253 void ServiceWorkerStorage::FindRegistrationForDocument(
254 const GURL& document_url,
255 const FindRegistrationCallback& callback) {
256 DCHECK(!document_url.has_ref());
257 if (!LazyInitialize(base::Bind(
258 &ServiceWorkerStorage::FindRegistrationForDocument,
259 weak_factory_.GetWeakPtr(), document_url, callback))) {
260 if (state_ != INITIALIZING || !context_) {
261 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
262 SERVICE_WORKER_ERROR_FAILED, callback);
266 DCHECK_EQ(INITIALIZED, state_);
268 // See if there are any stored registrations for the origin.
269 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
270 // Look for something currently being installed.
271 scoped_refptr<ServiceWorkerRegistration> installing_registration =
272 FindInstallingRegistrationForDocument(document_url);
274 installing_registration,
275 installing_registration ?
276 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
281 database_task_runner_->PostTask(
284 &FindForDocumentInDB,
286 base::MessageLoopProxy::current(),
288 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
289 weak_factory_.GetWeakPtr(), document_url, callback)));
292 void ServiceWorkerStorage::FindRegistrationForPattern(
294 const FindRegistrationCallback& callback) {
295 if (!LazyInitialize(base::Bind(
296 &ServiceWorkerStorage::FindRegistrationForPattern,
297 weak_factory_.GetWeakPtr(), scope, callback))) {
298 if (state_ != INITIALIZING || !context_) {
299 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
300 SERVICE_WORKER_ERROR_FAILED, callback);
304 DCHECK_EQ(INITIALIZED, state_);
306 // See if there are any stored registrations for the origin.
307 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
308 // Look for something currently being installed.
309 scoped_refptr<ServiceWorkerRegistration> installing_registration =
310 FindInstallingRegistrationForPattern(scope);
312 FROM_HERE, installing_registration,
313 installing_registration ?
314 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
319 database_task_runner_->PostTask(
324 base::MessageLoopProxy::current(),
326 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
327 weak_factory_.GetWeakPtr(), scope, callback)));
330 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
332 if (state_ != INITIALIZED || !context_)
334 for (RegistrationRefsById::const_iterator it =
335 uninstalling_registrations_.begin();
336 it != uninstalling_registrations_.end();
338 if (it->second->pattern() == scope) {
339 DCHECK(it->second->is_uninstalling());
346 void ServiceWorkerStorage::FindRegistrationForId(
347 int64 registration_id,
349 const FindRegistrationCallback& callback) {
350 if (!LazyInitialize(base::Bind(
351 &ServiceWorkerStorage::FindRegistrationForId,
352 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
353 if (state_ != INITIALIZING || !context_) {
354 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
355 SERVICE_WORKER_ERROR_FAILED, callback);
359 DCHECK_EQ(INITIALIZED, state_);
361 // See if there are any stored registrations for the origin.
362 if (!ContainsKey(registered_origins_, origin)) {
363 // Look for something currently being installed.
364 scoped_refptr<ServiceWorkerRegistration> installing_registration =
365 FindInstallingRegistrationForId(registration_id);
367 installing_registration,
368 installing_registration ?
369 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
374 scoped_refptr<ServiceWorkerRegistration> registration =
375 context_->GetLiveRegistration(registration_id);
377 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
381 database_task_runner_->PostTask(
383 base::Bind(&FindForIdInDB,
385 base::MessageLoopProxy::current(),
386 registration_id, origin,
387 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
388 weak_factory_.GetWeakPtr(), callback)));
391 void ServiceWorkerStorage::GetAllRegistrations(
392 const GetAllRegistrationInfosCallback& callback) {
393 if (!LazyInitialize(base::Bind(
394 &ServiceWorkerStorage::GetAllRegistrations,
395 weak_factory_.GetWeakPtr(), callback))) {
396 if (state_ != INITIALIZING || !context_) {
397 RunSoon(FROM_HERE, base::Bind(
398 callback, std::vector<ServiceWorkerRegistrationInfo>()));
402 DCHECK_EQ(INITIALIZED, state_);
404 RegistrationList* registrations = new RegistrationList;
405 PostTaskAndReplyWithResult(
406 database_task_runner_,
408 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
409 base::Unretained(database_.get()),
410 base::Unretained(registrations)),
411 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
412 weak_factory_.GetWeakPtr(),
414 base::Owned(registrations)));
417 void ServiceWorkerStorage::StoreRegistration(
418 ServiceWorkerRegistration* registration,
419 ServiceWorkerVersion* version,
420 const StatusCallback& callback) {
421 DCHECK(registration);
424 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
425 if (IsDisabled() || !context_) {
426 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
430 ServiceWorkerDatabase::RegistrationData data;
431 data.registration_id = registration->id();
432 data.scope = registration->pattern();
433 data.script = registration->script_url();
434 data.has_fetch_handler = true;
435 data.version_id = version->version_id();
436 data.last_update_check = registration->last_update_check();
437 data.is_active = (version == registration->active_version());
439 ResourceList resources;
440 version->script_cache_map()->GetResources(&resources);
442 if (!has_checked_for_stale_resources_)
443 DeleteStaleResources();
445 database_task_runner_->PostTask(
447 base::Bind(&WriteRegistrationInDB,
449 base::MessageLoopProxy::current(),
451 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
452 weak_factory_.GetWeakPtr(),
455 registration->set_is_deleted(false);
458 void ServiceWorkerStorage::UpdateToActiveState(
459 ServiceWorkerRegistration* registration,
460 const StatusCallback& callback) {
461 DCHECK(registration);
463 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
464 if (IsDisabled() || !context_) {
465 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
469 PostTaskAndReplyWithResult(
470 database_task_runner_,
472 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
473 base::Unretained(database_.get()),
475 registration->script_url().GetOrigin()),
476 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
477 weak_factory_.GetWeakPtr(),
481 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
482 ServiceWorkerRegistration* registration) {
483 DCHECK(registration);
485 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
486 if (IsDisabled() || !context_)
489 database_task_runner_->PostTask(
492 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
493 base::Unretained(database_.get()),
495 registration->script_url().GetOrigin(),
496 registration->last_update_check()));
499 void ServiceWorkerStorage::DeleteRegistration(
500 int64 registration_id,
502 const StatusCallback& callback) {
503 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
504 if (IsDisabled() || !context_) {
505 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
509 if (!has_checked_for_stale_resources_)
510 DeleteStaleResources();
512 DidDeleteRegistrationParams params;
513 params.registration_id = registration_id;
514 params.origin = origin;
515 params.callback = callback;
517 database_task_runner_->PostTask(
519 base::Bind(&DeleteRegistrationFromDB,
521 base::MessageLoopProxy::current(),
522 registration_id, origin,
523 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
524 weak_factory_.GetWeakPtr(), params)));
526 // The registration should no longer be findable.
527 pending_deletions_.insert(registration_id);
528 ServiceWorkerRegistration* registration =
529 context_->GetLiveRegistration(registration_id);
531 registration->set_is_deleted(true);
534 scoped_ptr<ServiceWorkerResponseReader>
535 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
536 return make_scoped_ptr(
537 new ServiceWorkerResponseReader(response_id, disk_cache()));
540 scoped_ptr<ServiceWorkerResponseWriter>
541 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
542 return make_scoped_ptr(
543 new ServiceWorkerResponseWriter(response_id, disk_cache()));
546 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
547 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
548 DCHECK_EQ(INITIALIZED, state_);
550 if (!has_checked_for_stale_resources_)
551 DeleteStaleResources();
553 database_task_runner_->PostTask(
555 base::Bind(base::IgnoreResult(
556 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
557 base::Unretained(database_.get()),
558 std::set<int64>(&id, &id + 1)));
561 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
562 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
563 database_task_runner_->PostTask(
565 base::Bind(base::IgnoreResult(
566 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
567 base::Unretained(database_.get()),
568 std::set<int64>(&id, &id + 1)));
569 StartPurgingResources(std::vector<int64>(1, id));
572 void ServiceWorkerStorage::CompareScriptResources(
573 int64 lhs_id, int64 rhs_id,
574 const CompareCallback& callback) {
575 DCHECK(!callback.is_null());
576 scoped_refptr<ResponseComparer> comparer =
577 new ResponseComparer(weak_factory_.GetWeakPtr(),
578 CreateResponseReader(lhs_id),
579 CreateResponseReader(rhs_id),
581 comparer->Start(); // It deletes itself when done.
584 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
587 // Delete the database on the database thread.
588 PostTaskAndReplyWithResult(
589 database_task_runner_,
591 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
592 base::Unretained(database_.get())),
593 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
594 weak_factory_.GetWeakPtr(), callback));
597 int64 ServiceWorkerStorage::NewRegistrationId() {
598 if (state_ == DISABLED)
599 return kInvalidServiceWorkerRegistrationId;
600 DCHECK_EQ(INITIALIZED, state_);
601 return next_registration_id_++;
604 int64 ServiceWorkerStorage::NewVersionId() {
605 if (state_ == DISABLED)
606 return kInvalidServiceWorkerVersionId;
607 DCHECK_EQ(INITIALIZED, state_);
608 return next_version_id_++;
611 int64 ServiceWorkerStorage::NewResourceId() {
612 if (state_ == DISABLED)
613 return kInvalidServiceWorkerResourceId;
614 DCHECK_EQ(INITIALIZED, state_);
615 return next_resource_id_++;
618 void ServiceWorkerStorage::NotifyInstallingRegistration(
619 ServiceWorkerRegistration* registration) {
620 DCHECK(installing_registrations_.find(registration->id()) ==
621 installing_registrations_.end());
622 installing_registrations_[registration->id()] = registration;
625 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
626 ServiceWorkerRegistration* registration,
627 ServiceWorkerVersion* version,
628 ServiceWorkerStatusCode status) {
629 installing_registrations_.erase(registration->id());
630 if (status != SERVICE_WORKER_OK && version) {
631 ResourceList resources;
632 version->script_cache_map()->GetResources(&resources);
635 for (size_t i = 0; i < resources.size(); ++i)
636 ids.insert(resources[i].resource_id);
638 database_task_runner_->PostTask(
640 base::Bind(base::IgnoreResult(
641 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
642 base::Unretained(database_.get()),
647 void ServiceWorkerStorage::NotifyUninstallingRegistration(
648 ServiceWorkerRegistration* registration) {
649 DCHECK(uninstalling_registrations_.find(registration->id()) ==
650 uninstalling_registrations_.end());
651 uninstalling_registrations_[registration->id()] = registration;
654 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
655 ServiceWorkerRegistration* registration) {
656 uninstalling_registrations_.erase(registration->id());
659 void ServiceWorkerStorage::Disable() {
662 disk_cache_->Disable();
665 bool ServiceWorkerStorage::IsDisabled() const {
666 return state_ == DISABLED;
669 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
670 if (!has_checked_for_stale_resources_)
671 DeleteStaleResources();
672 StartPurgingResources(resources);
675 ServiceWorkerStorage::ServiceWorkerStorage(
676 const base::FilePath& path,
677 base::WeakPtr<ServiceWorkerContextCore> context,
678 base::SequencedTaskRunner* database_task_runner,
679 base::MessageLoopProxy* disk_cache_thread,
680 quota::QuotaManagerProxy* quota_manager_proxy)
681 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
682 next_version_id_(kInvalidServiceWorkerVersionId),
683 next_resource_id_(kInvalidServiceWorkerResourceId),
684 state_(UNINITIALIZED),
687 database_task_runner_(database_task_runner),
688 disk_cache_thread_(disk_cache_thread),
689 quota_manager_proxy_(quota_manager_proxy),
690 is_purge_pending_(false),
691 has_checked_for_stale_resources_(false),
692 weak_factory_(this) {
693 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
696 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
698 return base::FilePath();
699 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
700 .Append(kDatabaseName);
703 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
705 return base::FilePath();
706 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
707 .Append(kDiskCacheName);
710 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
720 pending_tasks_.push_back(callback);
723 pending_tasks_.push_back(callback);
727 state_ = INITIALIZING;
728 database_task_runner_->PostTask(
730 base::Bind(&ReadInitialDataFromDB,
732 base::MessageLoopProxy::current(),
733 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
734 weak_factory_.GetWeakPtr())));
738 void ServiceWorkerStorage::DidReadInitialData(
740 ServiceWorkerDatabase::Status status) {
742 DCHECK_EQ(INITIALIZING, state_);
744 if (status == ServiceWorkerDatabase::STATUS_OK) {
745 next_registration_id_ = data->next_registration_id;
746 next_version_id_ = data->next_version_id;
747 next_resource_id_ = data->next_resource_id;
748 registered_origins_.swap(data->origins);
749 state_ = INITIALIZED;
751 // TODO(nhiroki): Stringify |status| using StatusToString() defined in
752 // service_worker_database.cc.
753 DVLOG(2) << "Failed to initialize: " << status;
754 ScheduleDeleteAndStartOver();
757 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
758 it != pending_tasks_.end(); ++it) {
759 RunSoon(FROM_HERE, *it);
761 pending_tasks_.clear();
764 void ServiceWorkerStorage::DidFindRegistrationForDocument(
765 const GURL& document_url,
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);
775 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
776 // Look for something currently being installed.
777 scoped_refptr<ServiceWorkerRegistration> installing_registration =
778 FindInstallingRegistrationForDocument(document_url);
779 callback.Run(installing_registration ?
780 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
781 installing_registration);
785 ScheduleDeleteAndStartOver();
786 callback.Run(DatabaseStatusToStatusCode(status),
787 scoped_refptr<ServiceWorkerRegistration>());
790 void ServiceWorkerStorage::DidFindRegistrationForPattern(
792 const FindRegistrationCallback& callback,
793 const ServiceWorkerDatabase::RegistrationData& data,
794 const ResourceList& resources,
795 ServiceWorkerDatabase::Status status) {
796 if (status == ServiceWorkerDatabase::STATUS_OK) {
797 ReturnFoundRegistration(callback, data, resources);
801 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
802 scoped_refptr<ServiceWorkerRegistration> installing_registration =
803 FindInstallingRegistrationForPattern(scope);
804 callback.Run(installing_registration ?
805 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
806 installing_registration);
810 ScheduleDeleteAndStartOver();
811 callback.Run(DatabaseStatusToStatusCode(status),
812 scoped_refptr<ServiceWorkerRegistration>());
815 void ServiceWorkerStorage::DidFindRegistrationForId(
816 const FindRegistrationCallback& callback,
817 const ServiceWorkerDatabase::RegistrationData& data,
818 const ResourceList& resources,
819 ServiceWorkerDatabase::Status status) {
820 if (status == ServiceWorkerDatabase::STATUS_OK) {
821 ReturnFoundRegistration(callback, data, resources);
825 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
826 // TODO(nhiroki): Find a registration in |installing_registrations_|.
827 callback.Run(DatabaseStatusToStatusCode(status),
828 scoped_refptr<ServiceWorkerRegistration>());
832 ScheduleDeleteAndStartOver();
833 callback.Run(DatabaseStatusToStatusCode(status),
834 scoped_refptr<ServiceWorkerRegistration>());
837 void ServiceWorkerStorage::ReturnFoundRegistration(
838 const FindRegistrationCallback& callback,
839 const ServiceWorkerDatabase::RegistrationData& data,
840 const ResourceList& resources) {
841 scoped_refptr<ServiceWorkerRegistration> registration =
842 GetOrCreateRegistration(data, resources);
843 if (registration->is_deleted()) {
844 // It's past the point of no return and no longer findable.
845 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
848 callback.Run(SERVICE_WORKER_OK, registration);
851 void ServiceWorkerStorage::DidGetAllRegistrations(
852 const GetAllRegistrationInfosCallback& callback,
853 RegistrationList* registrations,
854 ServiceWorkerDatabase::Status status) {
855 DCHECK(registrations);
856 if (status != ServiceWorkerDatabase::STATUS_OK &&
857 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
858 ScheduleDeleteAndStartOver();
859 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
863 // Add all stored registrations.
864 std::set<int64> pushed_registrations;
865 std::vector<ServiceWorkerRegistrationInfo> infos;
866 for (RegistrationList::const_iterator it = registrations->begin();
867 it != registrations->end(); ++it) {
868 const bool inserted =
869 pushed_registrations.insert(it->registration_id).second;
872 ServiceWorkerRegistration* registration =
873 context_->GetLiveRegistration(it->registration_id);
875 infos.push_back(registration->GetInfo());
879 ServiceWorkerRegistrationInfo info;
880 info.pattern = it->scope;
881 info.script_url = it->script;
882 info.registration_id = it->registration_id;
883 if (ServiceWorkerVersion* version =
884 context_->GetLiveVersion(it->version_id)) {
886 info.active_version = version->GetInfo();
888 info.waiting_version = version->GetInfo();
889 infos.push_back(info);
894 info.active_version.is_null = false;
895 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
896 info.active_version.version_id = it->version_id;
898 info.waiting_version.is_null = false;
899 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
900 info.waiting_version.version_id = it->version_id;
902 infos.push_back(info);
905 // Add unstored registrations that are being installed.
906 for (RegistrationRefsById::const_iterator it =
907 installing_registrations_.begin();
908 it != installing_registrations_.end(); ++it) {
909 if (pushed_registrations.insert(it->first).second)
910 infos.push_back(it->second->GetInfo());
916 void ServiceWorkerStorage::DidStoreRegistration(
917 const StatusCallback& callback,
919 int64 deleted_version_id,
920 const std::vector<int64>& newly_purgeable_resources,
921 ServiceWorkerDatabase::Status status) {
922 if (status != ServiceWorkerDatabase::STATUS_OK) {
923 ScheduleDeleteAndStartOver();
924 callback.Run(DatabaseStatusToStatusCode(status));
927 registered_origins_.insert(origin);
928 callback.Run(SERVICE_WORKER_OK);
930 if (!context_ || !context_->GetLiveVersion(deleted_version_id))
931 StartPurgingResources(newly_purgeable_resources);
934 void ServiceWorkerStorage::DidUpdateToActiveState(
935 const StatusCallback& callback,
936 ServiceWorkerDatabase::Status status) {
937 if (status != ServiceWorkerDatabase::STATUS_OK &&
938 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
939 ScheduleDeleteAndStartOver();
941 callback.Run(DatabaseStatusToStatusCode(status));
944 void ServiceWorkerStorage::DidDeleteRegistration(
945 const DidDeleteRegistrationParams& params,
946 bool origin_is_deletable,
948 const std::vector<int64>& newly_purgeable_resources,
949 ServiceWorkerDatabase::Status status) {
950 pending_deletions_.erase(params.registration_id);
951 if (status != ServiceWorkerDatabase::STATUS_OK) {
952 ScheduleDeleteAndStartOver();
953 params.callback.Run(DatabaseStatusToStatusCode(status));
956 if (origin_is_deletable)
957 registered_origins_.erase(params.origin);
958 params.callback.Run(SERVICE_WORKER_OK);
960 if (!context_ || !context_->GetLiveVersion(version_id))
961 StartPurgingResources(newly_purgeable_resources);
964 scoped_refptr<ServiceWorkerRegistration>
965 ServiceWorkerStorage::GetOrCreateRegistration(
966 const ServiceWorkerDatabase::RegistrationData& data,
967 const ResourceList& resources) {
968 scoped_refptr<ServiceWorkerRegistration> registration =
969 context_->GetLiveRegistration(data.registration_id);
973 registration = new ServiceWorkerRegistration(
974 data.scope, data.script, data.registration_id, context_);
975 registration->set_last_update_check(data.last_update_check);
976 if (pending_deletions_.find(data.registration_id) !=
977 pending_deletions_.end()) {
978 registration->set_is_deleted(true);
980 scoped_refptr<ServiceWorkerVersion> version =
981 context_->GetLiveVersion(data.version_id);
983 version = new ServiceWorkerVersion(registration, data.version_id, context_);
984 version->SetStatus(data.is_active ?
985 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
986 version->script_cache_map()->SetResources(resources);
989 if (version->status() == ServiceWorkerVersion::ACTIVATED)
990 registration->SetActiveVersion(version);
991 else if (version->status() == ServiceWorkerVersion::INSTALLED)
992 registration->SetWaitingVersion(version);
999 ServiceWorkerRegistration*
1000 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1001 const GURL& document_url) {
1002 DCHECK(!document_url.has_ref());
1004 LongestScopeMatcher matcher(document_url);
1005 ServiceWorkerRegistration* match = NULL;
1007 // TODO(nhiroki): This searches over installing registrations linearly and it
1008 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1009 for (RegistrationRefsById::const_iterator it =
1010 installing_registrations_.begin();
1011 it != installing_registrations_.end(); ++it) {
1012 if (matcher.MatchLongest(it->second->pattern()))
1018 ServiceWorkerRegistration*
1019 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1020 const GURL& scope) {
1021 for (RegistrationRefsById::const_iterator it =
1022 installing_registrations_.begin();
1023 it != installing_registrations_.end(); ++it) {
1024 if (it->second->pattern() == scope)
1030 ServiceWorkerRegistration*
1031 ServiceWorkerStorage::FindInstallingRegistrationForId(
1032 int64 registration_id) {
1033 RegistrationRefsById::const_iterator found =
1034 installing_registrations_.find(registration_id);
1035 if (found == installing_registrations_.end())
1037 return found->second;
1040 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1042 return disk_cache_.get();
1044 disk_cache_.reset(new ServiceWorkerDiskCache);
1046 base::FilePath path = GetDiskCachePath();
1048 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1049 net::CompletionCallback());
1050 DCHECK_EQ(net::OK, rv);
1051 return disk_cache_.get();
1054 int rv = disk_cache_->InitWithDiskBackend(
1055 path, kMaxDiskCacheSize, false,
1056 disk_cache_thread_.get(),
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_)
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();
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(
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(
1132 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
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)
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(
1159 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1163 status = database->PurgeUncommittedResourceIds(ids);
1164 if (status != ServiceWorkerDatabase::STATUS_OK) {
1165 original_task_runner->PostTask(
1168 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1173 status = database->GetPurgeableResourceIds(&ids);
1174 original_task_runner->PostTask(
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) {
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));
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,
1207 const DeleteRegistrationCallback& callback) {
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,
1218 kInvalidServiceWorkerVersionId,
1219 std::vector<int64>(),
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, ®istrations);
1228 if (status != ServiceWorkerDatabase::STATUS_OK) {
1229 original_task_runner->PostTask(FROM_HERE,
1230 base::Bind(callback,
1232 kInvalidServiceWorkerVersionId,
1233 std::vector<int64>(),
1238 bool deletable = registrations.empty();
1239 original_task_runner->PostTask(
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) {
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(),
1260 newly_purgeable_resources,
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, ®istrations);
1273 if (status != ServiceWorkerDatabase::STATUS_OK) {
1274 original_task_runner->PostTask(
1276 base::Bind(callback,
1277 ServiceWorkerDatabase::RegistrationData(),
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(
1300 base::Bind(callback, data, resources, status));
1303 void ServiceWorkerStorage::FindForPatternInDB(
1304 ServiceWorkerDatabase* database,
1305 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1307 const FindInDBCallback& callback) {
1308 GURL origin = scope.GetOrigin();
1309 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1310 ServiceWorkerDatabase::Status status =
1311 database->GetRegistrationsForOrigin(origin, ®istrations);
1312 if (status != ServiceWorkerDatabase::STATUS_OK) {
1313 original_task_runner->PostTask(
1315 base::Bind(callback,
1316 ServiceWorkerDatabase::RegistrationData(),
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)
1330 status = database->ReadRegistration(it->registration_id, origin,
1332 break; // We're done looping.
1335 original_task_runner->PostTask(
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,
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.
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));
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_,
1388 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1389 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1390 weak_factory_.GetWeakPtr(), callback));
1393 void ServiceWorkerStorage::DidDeleteDiskCache(
1394 const StatusCallback& callback, bool result) {
1395 DCHECK_EQ(DISABLED, state_);
1397 // Give up the corruption recovery until the browser restarts.
1398 LOG(ERROR) << "Failed to delete the diskcache.";
1399 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1402 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1403 callback.Run(SERVICE_WORKER_OK);
1406 } // namespace content