Rename isSystemLocationEnabled to isLocationEnabled, as per internal review (185995).
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blob038afbeaebe2e879bb19a50300dea3f6561a5ce3
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 "base/bind_helpers.h"
8 #include "base/debug/trace_event.h"
9 #include "base/files/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/single_thread_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 "storage/browser/quota/quota_manager_proxy.h"
27 #include "storage/browser/quota/special_storage_policy.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 ClearSessionOnlyOrigins();
220 weak_factory_.InvalidateWeakPtrs();
221 database_task_manager_->GetTaskRunner()->DeleteSoon(FROM_HERE,
222 database_.release());
225 // static
226 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
227 const base::FilePath& path,
228 base::WeakPtr<ServiceWorkerContextCore> context,
229 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
230 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
231 storage::QuotaManagerProxy* quota_manager_proxy,
232 storage::SpecialStoragePolicy* special_storage_policy) {
233 return make_scoped_ptr(new ServiceWorkerStorage(path,
234 context,
235 database_task_manager.Pass(),
236 disk_cache_thread,
237 quota_manager_proxy,
238 special_storage_policy));
241 // static
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_,
247 context,
248 old_storage->database_task_manager_->Clone(),
249 old_storage->disk_cache_thread_,
250 old_storage->quota_manager_proxy_.get(),
251 old_storage->special_storage_policy_.get()));
254 void ServiceWorkerStorage::FindRegistrationForDocument(
255 const GURL& document_url,
256 const FindRegistrationCallback& callback) {
257 DCHECK(!document_url.has_ref());
258 if (!LazyInitialize(base::Bind(
259 &ServiceWorkerStorage::FindRegistrationForDocument,
260 weak_factory_.GetWeakPtr(), document_url, callback))) {
261 if (state_ != INITIALIZING || !context_) {
262 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
263 SERVICE_WORKER_ERROR_FAILED, callback);
265 TRACE_EVENT_INSTANT1(
266 "ServiceWorker",
267 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
268 TRACE_EVENT_SCOPE_THREAD,
269 "URL", document_url.spec());
270 return;
272 DCHECK_EQ(INITIALIZED, state_);
274 // See if there are any stored registrations for the origin.
275 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
276 // Look for something currently being installed.
277 scoped_refptr<ServiceWorkerRegistration> installing_registration =
278 FindInstallingRegistrationForDocument(document_url);
279 ServiceWorkerStatusCode status = installing_registration.get() ?
280 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
281 TRACE_EVENT_INSTANT2(
282 "ServiceWorker",
283 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
284 TRACE_EVENT_SCOPE_THREAD,
285 "URL", document_url.spec(),
286 "Status", ServiceWorkerStatusToString(status));
287 CompleteFindNow(installing_registration,
288 status,
289 callback);
290 return;
293 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
294 // callback id.
295 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
296 TRACE_EVENT_ASYNC_BEGIN1(
297 "ServiceWorker",
298 "ServiceWorkerStorage::FindRegistrationForDocument",
299 callback_id,
300 "URL", document_url.spec());
301 database_task_manager_->GetTaskRunner()->PostTask(
302 FROM_HERE,
303 base::Bind(
304 &FindForDocumentInDB,
305 database_.get(),
306 base::MessageLoopProxy::current(),
307 document_url,
308 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
309 weak_factory_.GetWeakPtr(),
310 document_url,
311 callback,
312 callback_id)));
315 void ServiceWorkerStorage::FindRegistrationForPattern(
316 const GURL& scope,
317 const FindRegistrationCallback& callback) {
318 if (!LazyInitialize(base::Bind(
319 &ServiceWorkerStorage::FindRegistrationForPattern,
320 weak_factory_.GetWeakPtr(), scope, callback))) {
321 if (state_ != INITIALIZING || !context_) {
322 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
323 SERVICE_WORKER_ERROR_FAILED, callback);
325 return;
327 DCHECK_EQ(INITIALIZED, state_);
329 // See if there are any stored registrations for the origin.
330 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
331 // Look for something currently being installed.
332 scoped_refptr<ServiceWorkerRegistration> installing_registration =
333 FindInstallingRegistrationForPattern(scope);
334 CompleteFindSoon(FROM_HERE,
335 installing_registration,
336 installing_registration.get()
337 ? SERVICE_WORKER_OK
338 : SERVICE_WORKER_ERROR_NOT_FOUND,
339 callback);
340 return;
343 database_task_manager_->GetTaskRunner()->PostTask(
344 FROM_HERE,
345 base::Bind(
346 &FindForPatternInDB,
347 database_.get(),
348 base::MessageLoopProxy::current(),
349 scope,
350 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
351 weak_factory_.GetWeakPtr(), scope, callback)));
354 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
355 const GURL& scope) {
356 if (state_ != INITIALIZED || !context_)
357 return NULL;
358 for (RegistrationRefsById::const_iterator it =
359 uninstalling_registrations_.begin();
360 it != uninstalling_registrations_.end();
361 ++it) {
362 if (it->second->pattern() == scope) {
363 DCHECK(it->second->is_uninstalling());
364 return it->second.get();
367 return NULL;
370 void ServiceWorkerStorage::FindRegistrationForId(
371 int64 registration_id,
372 const GURL& origin,
373 const FindRegistrationCallback& callback) {
374 if (!LazyInitialize(base::Bind(
375 &ServiceWorkerStorage::FindRegistrationForId,
376 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
377 if (state_ != INITIALIZING || !context_) {
378 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
379 SERVICE_WORKER_ERROR_FAILED, callback);
381 return;
383 DCHECK_EQ(INITIALIZED, state_);
385 // See if there are any stored registrations for the origin.
386 if (!ContainsKey(registered_origins_, origin)) {
387 // Look for something currently being installed.
388 scoped_refptr<ServiceWorkerRegistration> installing_registration =
389 FindInstallingRegistrationForId(registration_id);
390 CompleteFindNow(installing_registration,
391 installing_registration.get()
392 ? SERVICE_WORKER_OK
393 : SERVICE_WORKER_ERROR_NOT_FOUND,
394 callback);
395 return;
398 scoped_refptr<ServiceWorkerRegistration> registration =
399 context_->GetLiveRegistration(registration_id);
400 if (registration.get()) {
401 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
402 return;
405 database_task_manager_->GetTaskRunner()->PostTask(
406 FROM_HERE,
407 base::Bind(&FindForIdInDB,
408 database_.get(),
409 base::MessageLoopProxy::current(),
410 registration_id, origin,
411 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
412 weak_factory_.GetWeakPtr(), callback)));
415 void ServiceWorkerStorage::GetAllRegistrations(
416 const GetAllRegistrationInfosCallback& callback) {
417 if (!LazyInitialize(base::Bind(
418 &ServiceWorkerStorage::GetAllRegistrations,
419 weak_factory_.GetWeakPtr(), callback))) {
420 if (state_ != INITIALIZING || !context_) {
421 RunSoon(FROM_HERE, base::Bind(
422 callback, std::vector<ServiceWorkerRegistrationInfo>()));
424 return;
426 DCHECK_EQ(INITIALIZED, state_);
428 RegistrationList* registrations = new RegistrationList;
429 PostTaskAndReplyWithResult(
430 database_task_manager_->GetTaskRunner(),
431 FROM_HERE,
432 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
433 base::Unretained(database_.get()),
434 base::Unretained(registrations)),
435 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
436 weak_factory_.GetWeakPtr(),
437 callback,
438 base::Owned(registrations)));
441 void ServiceWorkerStorage::StoreRegistration(
442 ServiceWorkerRegistration* registration,
443 ServiceWorkerVersion* version,
444 const StatusCallback& callback) {
445 DCHECK(registration);
446 DCHECK(version);
448 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
449 if (IsDisabled() || !context_) {
450 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
451 return;
454 ServiceWorkerDatabase::RegistrationData data;
455 data.registration_id = registration->id();
456 data.scope = registration->pattern();
457 data.script = version->script_url();
458 data.has_fetch_handler = true;
459 data.version_id = version->version_id();
460 data.last_update_check = registration->last_update_check();
461 data.is_active = (version == registration->active_version());
463 ResourceList resources;
464 version->script_cache_map()->GetResources(&resources);
466 uint64 resources_total_size_bytes = 0;
467 for (const auto& resource : resources) {
468 resources_total_size_bytes += resource.size_bytes;
470 data.resources_total_size_bytes = resources_total_size_bytes;
472 if (!has_checked_for_stale_resources_)
473 DeleteStaleResources();
475 database_task_manager_->GetTaskRunner()->PostTask(
476 FROM_HERE,
477 base::Bind(&WriteRegistrationInDB,
478 database_.get(),
479 base::MessageLoopProxy::current(),
480 data,
481 resources,
482 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
483 weak_factory_.GetWeakPtr(),
484 callback,
485 data)));
487 registration->set_is_deleted(false);
490 void ServiceWorkerStorage::UpdateToActiveState(
491 ServiceWorkerRegistration* registration,
492 const StatusCallback& callback) {
493 DCHECK(registration);
495 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
496 if (IsDisabled() || !context_) {
497 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
498 return;
501 PostTaskAndReplyWithResult(
502 database_task_manager_->GetTaskRunner(),
503 FROM_HERE,
504 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
505 base::Unretained(database_.get()),
506 registration->id(),
507 registration->pattern().GetOrigin()),
508 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
509 weak_factory_.GetWeakPtr(),
510 callback));
513 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
514 ServiceWorkerRegistration* registration) {
515 DCHECK(registration);
517 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
518 if (IsDisabled() || !context_)
519 return;
521 database_task_manager_->GetTaskRunner()->PostTask(
522 FROM_HERE,
523 base::Bind(
524 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
525 base::Unretained(database_.get()),
526 registration->id(),
527 registration->pattern().GetOrigin(),
528 registration->last_update_check()));
531 void ServiceWorkerStorage::DeleteRegistration(
532 int64 registration_id,
533 const GURL& origin,
534 const StatusCallback& callback) {
535 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
536 if (IsDisabled() || !context_) {
537 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
538 return;
541 if (!has_checked_for_stale_resources_)
542 DeleteStaleResources();
544 DidDeleteRegistrationParams params;
545 params.registration_id = registration_id;
546 params.origin = origin;
547 params.callback = callback;
549 database_task_manager_->GetTaskRunner()->PostTask(
550 FROM_HERE,
551 base::Bind(&DeleteRegistrationFromDB,
552 database_.get(),
553 base::MessageLoopProxy::current(),
554 registration_id, origin,
555 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
556 weak_factory_.GetWeakPtr(), params)));
558 // The registration should no longer be findable.
559 pending_deletions_.insert(registration_id);
560 ServiceWorkerRegistration* registration =
561 context_->GetLiveRegistration(registration_id);
562 if (registration)
563 registration->set_is_deleted(true);
566 scoped_ptr<ServiceWorkerResponseReader>
567 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
568 return make_scoped_ptr(
569 new ServiceWorkerResponseReader(response_id, disk_cache()));
572 scoped_ptr<ServiceWorkerResponseWriter>
573 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
574 return make_scoped_ptr(
575 new ServiceWorkerResponseWriter(response_id, disk_cache()));
578 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
579 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
580 DCHECK_EQ(INITIALIZED, state_);
582 if (!has_checked_for_stale_resources_)
583 DeleteStaleResources();
585 database_task_manager_->GetTaskRunner()->PostTask(
586 FROM_HERE,
587 base::Bind(base::IgnoreResult(
588 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
589 base::Unretained(database_.get()),
590 std::set<int64>(&id, &id + 1)));
593 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
594 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
595 database_task_manager_->GetTaskRunner()->PostTask(
596 FROM_HERE,
597 base::Bind(base::IgnoreResult(
598 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
599 base::Unretained(database_.get()),
600 std::set<int64>(&id, &id + 1)));
601 StartPurgingResources(std::vector<int64>(1, id));
604 void ServiceWorkerStorage::CompareScriptResources(
605 int64 lhs_id, int64 rhs_id,
606 const CompareCallback& callback) {
607 DCHECK(!callback.is_null());
608 scoped_refptr<ResponseComparer> comparer =
609 new ResponseComparer(weak_factory_.GetWeakPtr(),
610 CreateResponseReader(lhs_id),
611 CreateResponseReader(rhs_id),
612 callback);
613 comparer->Start(); // It deletes itself when done.
616 void ServiceWorkerStorage::StoreUserData(
617 int64 registration_id,
618 const GURL& origin,
619 const std::string& key,
620 const std::string& data,
621 const StatusCallback& callback) {
622 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
623 if (IsDisabled() || !context_) {
624 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
625 return;
628 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
629 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
630 return;
633 PostTaskAndReplyWithResult(
634 database_task_manager_->GetTaskRunner(),
635 FROM_HERE,
636 base::Bind(&ServiceWorkerDatabase::WriteUserData,
637 base::Unretained(database_.get()),
638 registration_id, origin, key, data),
639 base::Bind(&ServiceWorkerStorage::DidStoreUserData,
640 weak_factory_.GetWeakPtr(),
641 callback));
644 void ServiceWorkerStorage::GetUserData(
645 int64 registration_id,
646 const std::string& key,
647 const GetUserDataCallback& callback) {
648 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
649 if (IsDisabled() || !context_) {
650 RunSoon(FROM_HERE,
651 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
652 return;
655 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
656 RunSoon(FROM_HERE,
657 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
658 return;
661 database_task_manager_->GetTaskRunner()->PostTask(
662 FROM_HERE,
663 base::Bind(&ServiceWorkerStorage::GetUserDataInDB,
664 database_.get(),
665 base::MessageLoopProxy::current(),
666 registration_id,
667 key,
668 base::Bind(&ServiceWorkerStorage::DidGetUserData,
669 weak_factory_.GetWeakPtr(), callback)));
672 void ServiceWorkerStorage::ClearUserData(
673 int64 registration_id,
674 const std::string& key,
675 const StatusCallback& callback) {
676 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
677 if (IsDisabled() || !context_) {
678 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
679 return;
682 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
683 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
684 return;
687 PostTaskAndReplyWithResult(
688 database_task_manager_->GetTaskRunner(),
689 FROM_HERE,
690 base::Bind(&ServiceWorkerDatabase::DeleteUserData,
691 base::Unretained(database_.get()),
692 registration_id, key),
693 base::Bind(&ServiceWorkerStorage::DidDeleteUserData,
694 weak_factory_.GetWeakPtr(),
695 callback));
698 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
699 Disable();
701 // Delete the database on the database thread.
702 PostTaskAndReplyWithResult(
703 database_task_manager_->GetTaskRunner(),
704 FROM_HERE,
705 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
706 base::Unretained(database_.get())),
707 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
708 weak_factory_.GetWeakPtr(),
709 callback));
712 int64 ServiceWorkerStorage::NewRegistrationId() {
713 if (state_ == DISABLED)
714 return kInvalidServiceWorkerRegistrationId;
715 DCHECK_EQ(INITIALIZED, state_);
716 return next_registration_id_++;
719 int64 ServiceWorkerStorage::NewVersionId() {
720 if (state_ == DISABLED)
721 return kInvalidServiceWorkerVersionId;
722 DCHECK_EQ(INITIALIZED, state_);
723 return next_version_id_++;
726 int64 ServiceWorkerStorage::NewResourceId() {
727 if (state_ == DISABLED)
728 return kInvalidServiceWorkerResourceId;
729 DCHECK_EQ(INITIALIZED, state_);
730 return next_resource_id_++;
733 void ServiceWorkerStorage::NotifyInstallingRegistration(
734 ServiceWorkerRegistration* registration) {
735 DCHECK(installing_registrations_.find(registration->id()) ==
736 installing_registrations_.end());
737 installing_registrations_[registration->id()] = registration;
740 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
741 ServiceWorkerRegistration* registration,
742 ServiceWorkerVersion* version,
743 ServiceWorkerStatusCode status) {
744 installing_registrations_.erase(registration->id());
745 if (status != SERVICE_WORKER_OK && version) {
746 ResourceList resources;
747 version->script_cache_map()->GetResources(&resources);
749 std::set<int64> ids;
750 for (size_t i = 0; i < resources.size(); ++i)
751 ids.insert(resources[i].resource_id);
753 database_task_manager_->GetTaskRunner()->PostTask(
754 FROM_HERE,
755 base::Bind(base::IgnoreResult(
756 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
757 base::Unretained(database_.get()),
758 ids));
762 void ServiceWorkerStorage::NotifyUninstallingRegistration(
763 ServiceWorkerRegistration* registration) {
764 DCHECK(uninstalling_registrations_.find(registration->id()) ==
765 uninstalling_registrations_.end());
766 uninstalling_registrations_[registration->id()] = registration;
769 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
770 ServiceWorkerRegistration* registration) {
771 uninstalling_registrations_.erase(registration->id());
774 void ServiceWorkerStorage::Disable() {
775 state_ = DISABLED;
776 if (disk_cache_)
777 disk_cache_->Disable();
780 bool ServiceWorkerStorage::IsDisabled() const {
781 return state_ == DISABLED;
784 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
785 if (!has_checked_for_stale_resources_)
786 DeleteStaleResources();
787 StartPurgingResources(resources);
790 ServiceWorkerStorage::ServiceWorkerStorage(
791 const base::FilePath& path,
792 base::WeakPtr<ServiceWorkerContextCore> context,
793 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
794 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
795 storage::QuotaManagerProxy* quota_manager_proxy,
796 storage::SpecialStoragePolicy* special_storage_policy)
797 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
798 next_version_id_(kInvalidServiceWorkerVersionId),
799 next_resource_id_(kInvalidServiceWorkerResourceId),
800 state_(UNINITIALIZED),
801 path_(path),
802 context_(context),
803 database_task_manager_(database_task_manager.Pass()),
804 disk_cache_thread_(disk_cache_thread),
805 quota_manager_proxy_(quota_manager_proxy),
806 special_storage_policy_(special_storage_policy),
807 is_purge_pending_(false),
808 has_checked_for_stale_resources_(false),
809 weak_factory_(this) {
810 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
813 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
814 if (path_.empty())
815 return base::FilePath();
816 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
817 .Append(kDatabaseName);
820 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
821 if (path_.empty())
822 return base::FilePath();
823 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
824 .Append(kDiskCacheName);
827 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
828 if (!context_)
829 return false;
831 switch (state_) {
832 case INITIALIZED:
833 return true;
834 case DISABLED:
835 return false;
836 case INITIALIZING:
837 pending_tasks_.push_back(callback);
838 return false;
839 case UNINITIALIZED:
840 pending_tasks_.push_back(callback);
841 // Fall-through.
844 state_ = INITIALIZING;
845 database_task_manager_->GetTaskRunner()->PostTask(
846 FROM_HERE,
847 base::Bind(&ReadInitialDataFromDB,
848 database_.get(),
849 base::MessageLoopProxy::current(),
850 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
851 weak_factory_.GetWeakPtr())));
852 return false;
855 void ServiceWorkerStorage::DidReadInitialData(
856 InitialData* data,
857 ServiceWorkerDatabase::Status status) {
858 DCHECK(data);
859 DCHECK_EQ(INITIALIZING, state_);
861 if (status == ServiceWorkerDatabase::STATUS_OK) {
862 next_registration_id_ = data->next_registration_id;
863 next_version_id_ = data->next_version_id;
864 next_resource_id_ = data->next_resource_id;
865 registered_origins_.swap(data->origins);
866 state_ = INITIALIZED;
867 } else {
868 DVLOG(2) << "Failed to initialize: "
869 << ServiceWorkerDatabase::StatusToString(status);
870 ScheduleDeleteAndStartOver();
873 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
874 it != pending_tasks_.end(); ++it) {
875 RunSoon(FROM_HERE, *it);
877 pending_tasks_.clear();
880 void ServiceWorkerStorage::DidFindRegistrationForDocument(
881 const GURL& document_url,
882 const FindRegistrationCallback& callback,
883 int64 callback_id,
884 const ServiceWorkerDatabase::RegistrationData& data,
885 const ResourceList& resources,
886 ServiceWorkerDatabase::Status status) {
887 if (status == ServiceWorkerDatabase::STATUS_OK) {
888 ReturnFoundRegistration(callback, data, resources);
889 TRACE_EVENT_ASYNC_END1(
890 "ServiceWorker",
891 "ServiceWorkerStorage::FindRegistrationForDocument",
892 callback_id,
893 "Status", ServiceWorkerDatabase::StatusToString(status));
894 return;
897 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
898 // Look for something currently being installed.
899 scoped_refptr<ServiceWorkerRegistration> installing_registration =
900 FindInstallingRegistrationForDocument(document_url);
901 ServiceWorkerStatusCode installing_status = installing_registration.get() ?
902 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
903 callback.Run(installing_status, installing_registration);
904 TRACE_EVENT_ASYNC_END2(
905 "ServiceWorker",
906 "ServiceWorkerStorage::FindRegistrationForDocument",
907 callback_id,
908 "Status", ServiceWorkerDatabase::StatusToString(status),
909 "Info",
910 (installing_status == SERVICE_WORKER_OK) ?
911 "Installing registration is found" :
912 "Any registrations are not found");
913 return;
916 ScheduleDeleteAndStartOver();
917 callback.Run(DatabaseStatusToStatusCode(status),
918 scoped_refptr<ServiceWorkerRegistration>());
919 TRACE_EVENT_ASYNC_END1(
920 "ServiceWorker",
921 "ServiceWorkerStorage::FindRegistrationForDocument",
922 callback_id,
923 "Status", ServiceWorkerDatabase::StatusToString(status));
926 void ServiceWorkerStorage::DidFindRegistrationForPattern(
927 const GURL& scope,
928 const FindRegistrationCallback& callback,
929 const ServiceWorkerDatabase::RegistrationData& data,
930 const ResourceList& resources,
931 ServiceWorkerDatabase::Status status) {
932 if (status == ServiceWorkerDatabase::STATUS_OK) {
933 ReturnFoundRegistration(callback, data, resources);
934 return;
937 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
938 scoped_refptr<ServiceWorkerRegistration> installing_registration =
939 FindInstallingRegistrationForPattern(scope);
940 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
941 : SERVICE_WORKER_ERROR_NOT_FOUND,
942 installing_registration);
943 return;
946 ScheduleDeleteAndStartOver();
947 callback.Run(DatabaseStatusToStatusCode(status),
948 scoped_refptr<ServiceWorkerRegistration>());
951 void ServiceWorkerStorage::DidFindRegistrationForId(
952 const FindRegistrationCallback& callback,
953 const ServiceWorkerDatabase::RegistrationData& data,
954 const ResourceList& resources,
955 ServiceWorkerDatabase::Status status) {
956 if (status == ServiceWorkerDatabase::STATUS_OK) {
957 ReturnFoundRegistration(callback, data, resources);
958 return;
961 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
962 // TODO(nhiroki): Find a registration in |installing_registrations_|.
963 callback.Run(DatabaseStatusToStatusCode(status),
964 scoped_refptr<ServiceWorkerRegistration>());
965 return;
968 ScheduleDeleteAndStartOver();
969 callback.Run(DatabaseStatusToStatusCode(status),
970 scoped_refptr<ServiceWorkerRegistration>());
973 void ServiceWorkerStorage::ReturnFoundRegistration(
974 const FindRegistrationCallback& callback,
975 const ServiceWorkerDatabase::RegistrationData& data,
976 const ResourceList& resources) {
977 scoped_refptr<ServiceWorkerRegistration> registration =
978 GetOrCreateRegistration(data, resources);
979 if (registration->is_deleted()) {
980 // It's past the point of no return and no longer findable.
981 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
982 return;
984 callback.Run(SERVICE_WORKER_OK, registration);
987 void ServiceWorkerStorage::DidGetAllRegistrations(
988 const GetAllRegistrationInfosCallback& callback,
989 RegistrationList* registrations,
990 ServiceWorkerDatabase::Status status) {
991 DCHECK(registrations);
992 if (status != ServiceWorkerDatabase::STATUS_OK &&
993 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
994 ScheduleDeleteAndStartOver();
995 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
996 return;
999 // Add all stored registrations.
1000 std::set<int64> pushed_registrations;
1001 std::vector<ServiceWorkerRegistrationInfo> infos;
1002 for (const auto& registration_data : *registrations) {
1003 const bool inserted =
1004 pushed_registrations.insert(registration_data.registration_id).second;
1005 DCHECK(inserted);
1007 ServiceWorkerRegistration* registration =
1008 context_->GetLiveRegistration(registration_data.registration_id);
1009 if (registration) {
1010 infos.push_back(registration->GetInfo());
1011 continue;
1014 ServiceWorkerRegistrationInfo info;
1015 info.pattern = registration_data.scope;
1016 info.registration_id = registration_data.registration_id;
1017 info.stored_version_size_bytes =
1018 registration_data.resources_total_size_bytes;
1019 if (ServiceWorkerVersion* version =
1020 context_->GetLiveVersion(registration_data.version_id)) {
1021 if (registration_data.is_active)
1022 info.active_version = version->GetInfo();
1023 else
1024 info.waiting_version = version->GetInfo();
1025 infos.push_back(info);
1026 continue;
1029 if (registration_data.is_active) {
1030 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
1031 info.active_version.version_id = registration_data.version_id;
1032 } else {
1033 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
1034 info.waiting_version.version_id = registration_data.version_id;
1036 infos.push_back(info);
1039 // Add unstored registrations that are being installed.
1040 for (RegistrationRefsById::const_iterator it =
1041 installing_registrations_.begin();
1042 it != installing_registrations_.end(); ++it) {
1043 if (pushed_registrations.insert(it->first).second)
1044 infos.push_back(it->second->GetInfo());
1047 callback.Run(infos);
1050 void ServiceWorkerStorage::DidStoreRegistration(
1051 const StatusCallback& callback,
1052 const ServiceWorkerDatabase::RegistrationData& new_version,
1053 const GURL& origin,
1054 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1055 const std::vector<int64>& newly_purgeable_resources,
1056 ServiceWorkerDatabase::Status status) {
1057 if (status != ServiceWorkerDatabase::STATUS_OK) {
1058 ScheduleDeleteAndStartOver();
1059 callback.Run(DatabaseStatusToStatusCode(status));
1060 return;
1062 registered_origins_.insert(origin);
1064 scoped_refptr<ServiceWorkerRegistration> registration =
1065 context_->GetLiveRegistration(new_version.registration_id);
1066 registration->set_resources_total_size_bytes(
1067 new_version.resources_total_size_bytes);
1068 if (quota_manager_proxy_.get()) {
1069 // Can be nullptr in tests.
1070 quota_manager_proxy_->NotifyStorageModified(
1071 storage::QuotaClient::kServiceWorker,
1072 origin,
1073 storage::StorageType::kStorageTypeTemporary,
1074 new_version.resources_total_size_bytes -
1075 deleted_version.resources_total_size_bytes);
1078 callback.Run(SERVICE_WORKER_OK);
1080 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1081 StartPurgingResources(newly_purgeable_resources);
1084 void ServiceWorkerStorage::DidUpdateToActiveState(
1085 const StatusCallback& callback,
1086 ServiceWorkerDatabase::Status status) {
1087 if (status != ServiceWorkerDatabase::STATUS_OK &&
1088 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1089 ScheduleDeleteAndStartOver();
1091 callback.Run(DatabaseStatusToStatusCode(status));
1094 void ServiceWorkerStorage::DidDeleteRegistration(
1095 const DidDeleteRegistrationParams& params,
1096 bool origin_is_deletable,
1097 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1098 const std::vector<int64>& newly_purgeable_resources,
1099 ServiceWorkerDatabase::Status status) {
1100 pending_deletions_.erase(params.registration_id);
1101 if (status != ServiceWorkerDatabase::STATUS_OK) {
1102 ScheduleDeleteAndStartOver();
1103 params.callback.Run(DatabaseStatusToStatusCode(status));
1104 return;
1106 if (quota_manager_proxy_.get()) {
1107 // Can be nullptr in tests.
1108 quota_manager_proxy_->NotifyStorageModified(
1109 storage::QuotaClient::kServiceWorker,
1110 params.origin,
1111 storage::StorageType::kStorageTypeTemporary,
1112 -deleted_version.resources_total_size_bytes);
1114 if (origin_is_deletable)
1115 registered_origins_.erase(params.origin);
1116 params.callback.Run(SERVICE_WORKER_OK);
1118 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1119 StartPurgingResources(newly_purgeable_resources);
1122 void ServiceWorkerStorage::DidStoreUserData(
1123 const StatusCallback& callback,
1124 ServiceWorkerDatabase::Status status) {
1125 // |status| can be NOT_FOUND when the associated registration did not exist in
1126 // the database. In the case, we don't have to schedule the corruption
1127 // recovery.
1128 if (status != ServiceWorkerDatabase::STATUS_OK &&
1129 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1130 ScheduleDeleteAndStartOver();
1132 callback.Run(DatabaseStatusToStatusCode(status));
1135 void ServiceWorkerStorage::DidGetUserData(
1136 const GetUserDataCallback& callback,
1137 const std::string& data,
1138 ServiceWorkerDatabase::Status status) {
1139 if (status != ServiceWorkerDatabase::STATUS_OK &&
1140 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1141 ScheduleDeleteAndStartOver();
1143 callback.Run(data, DatabaseStatusToStatusCode(status));
1146 void ServiceWorkerStorage::DidDeleteUserData(
1147 const StatusCallback& callback,
1148 ServiceWorkerDatabase::Status status) {
1149 if (status != ServiceWorkerDatabase::STATUS_OK)
1150 ScheduleDeleteAndStartOver();
1151 callback.Run(DatabaseStatusToStatusCode(status));
1154 scoped_refptr<ServiceWorkerRegistration>
1155 ServiceWorkerStorage::GetOrCreateRegistration(
1156 const ServiceWorkerDatabase::RegistrationData& data,
1157 const ResourceList& resources) {
1158 scoped_refptr<ServiceWorkerRegistration> registration =
1159 context_->GetLiveRegistration(data.registration_id);
1160 if (registration.get())
1161 return registration;
1163 registration = new ServiceWorkerRegistration(
1164 data.scope, data.registration_id, context_);
1165 registration->set_resources_total_size_bytes(data.resources_total_size_bytes);
1166 registration->set_last_update_check(data.last_update_check);
1167 if (pending_deletions_.find(data.registration_id) !=
1168 pending_deletions_.end()) {
1169 registration->set_is_deleted(true);
1171 scoped_refptr<ServiceWorkerVersion> version =
1172 context_->GetLiveVersion(data.version_id);
1173 if (!version.get()) {
1174 version = new ServiceWorkerVersion(
1175 registration.get(), data.script, data.version_id, context_);
1176 version->SetStatus(data.is_active ?
1177 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1178 version->script_cache_map()->SetResources(resources);
1181 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1182 registration->SetActiveVersion(version.get());
1183 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1184 registration->SetWaitingVersion(version.get());
1185 else
1186 NOTREACHED();
1188 return registration;
1191 ServiceWorkerRegistration*
1192 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1193 const GURL& document_url) {
1194 DCHECK(!document_url.has_ref());
1196 LongestScopeMatcher matcher(document_url);
1197 ServiceWorkerRegistration* match = NULL;
1199 // TODO(nhiroki): This searches over installing registrations linearly and it
1200 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1201 for (RegistrationRefsById::const_iterator it =
1202 installing_registrations_.begin();
1203 it != installing_registrations_.end(); ++it) {
1204 if (matcher.MatchLongest(it->second->pattern()))
1205 match = it->second.get();
1207 return match;
1210 ServiceWorkerRegistration*
1211 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1212 const GURL& scope) {
1213 for (RegistrationRefsById::const_iterator it =
1214 installing_registrations_.begin();
1215 it != installing_registrations_.end(); ++it) {
1216 if (it->second->pattern() == scope)
1217 return it->second.get();
1219 return NULL;
1222 ServiceWorkerRegistration*
1223 ServiceWorkerStorage::FindInstallingRegistrationForId(
1224 int64 registration_id) {
1225 RegistrationRefsById::const_iterator found =
1226 installing_registrations_.find(registration_id);
1227 if (found == installing_registrations_.end())
1228 return NULL;
1229 return found->second.get();
1232 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1233 if (disk_cache_)
1234 return disk_cache_.get();
1236 disk_cache_.reset(new ServiceWorkerDiskCache);
1238 base::FilePath path = GetDiskCachePath();
1239 if (path.empty()) {
1240 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1241 net::CompletionCallback());
1242 DCHECK_EQ(net::OK, rv);
1243 return disk_cache_.get();
1246 int rv = disk_cache_->InitWithDiskBackend(
1247 path,
1248 kMaxDiskCacheSize,
1249 false,
1250 disk_cache_thread_,
1251 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1252 weak_factory_.GetWeakPtr()));
1253 if (rv != net::ERR_IO_PENDING)
1254 OnDiskCacheInitialized(rv);
1256 return disk_cache_.get();
1259 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1260 if (rv != net::OK) {
1261 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1262 << net::ErrorToString(rv);
1263 ScheduleDeleteAndStartOver();
1265 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1268 void ServiceWorkerStorage::StartPurgingResources(
1269 const std::vector<int64>& ids) {
1270 DCHECK(has_checked_for_stale_resources_);
1271 for (size_t i = 0; i < ids.size(); ++i)
1272 purgeable_resource_ids_.push_back(ids[i]);
1273 ContinuePurgingResources();
1276 void ServiceWorkerStorage::StartPurgingResources(
1277 const ResourceList& resources) {
1278 DCHECK(has_checked_for_stale_resources_);
1279 for (size_t i = 0; i < resources.size(); ++i)
1280 purgeable_resource_ids_.push_back(resources[i].resource_id);
1281 ContinuePurgingResources();
1284 void ServiceWorkerStorage::ContinuePurgingResources() {
1285 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1286 return;
1288 // Do one at a time until we're done, use RunSoon to avoid recursion when
1289 // DoomEntry returns immediately.
1290 is_purge_pending_ = true;
1291 int64 id = purgeable_resource_ids_.front();
1292 purgeable_resource_ids_.pop_front();
1293 RunSoon(FROM_HERE,
1294 base::Bind(&ServiceWorkerStorage::PurgeResource,
1295 weak_factory_.GetWeakPtr(), id));
1298 void ServiceWorkerStorage::PurgeResource(int64 id) {
1299 DCHECK(is_purge_pending_);
1300 int rv = disk_cache()->DoomEntry(
1301 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1302 weak_factory_.GetWeakPtr(), id));
1303 if (rv != net::ERR_IO_PENDING)
1304 OnResourcePurged(id, rv);
1307 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1308 DCHECK(is_purge_pending_);
1309 is_purge_pending_ = false;
1311 database_task_manager_->GetTaskRunner()->PostTask(
1312 FROM_HERE,
1313 base::Bind(base::IgnoreResult(
1314 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1315 base::Unretained(database_.get()),
1316 std::set<int64>(&id, &id + 1)));
1318 ContinuePurgingResources();
1321 void ServiceWorkerStorage::DeleteStaleResources() {
1322 DCHECK(!has_checked_for_stale_resources_);
1323 has_checked_for_stale_resources_ = true;
1324 database_task_manager_->GetTaskRunner()->PostTask(
1325 FROM_HERE,
1326 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1327 database_.get(),
1328 base::MessageLoopProxy::current(),
1329 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1330 weak_factory_.GetWeakPtr())));
1333 void ServiceWorkerStorage::DidCollectStaleResources(
1334 const std::vector<int64>& stale_resource_ids,
1335 ServiceWorkerDatabase::Status status) {
1336 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1337 if (status != ServiceWorkerDatabase::STATUS_OK)
1338 return;
1339 StartPurgingResources(stale_resource_ids);
1342 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1343 // Can be null in tests.
1344 if (!special_storage_policy_.get())
1345 return;
1347 if (!special_storage_policy_->HasSessionOnlyOrigins())
1348 return;
1350 std::set<GURL> session_only_origins;
1351 for (const GURL& origin : registered_origins_) {
1352 if (special_storage_policy_->IsStorageSessionOnly(origin))
1353 session_only_origins.insert(origin);
1356 database_task_manager_->GetShutdownBlockingTaskRunner()->PostTask(
1357 FROM_HERE,
1358 base::Bind(&DeleteAllDataForOriginsFromDB,
1359 database_.get(),
1360 session_only_origins));
1363 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1364 ServiceWorkerDatabase* database,
1365 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1366 const GetResourcesCallback& callback) {
1367 std::set<int64> ids;
1368 ServiceWorkerDatabase::Status status =
1369 database->GetUncommittedResourceIds(&ids);
1370 if (status != ServiceWorkerDatabase::STATUS_OK) {
1371 original_task_runner->PostTask(
1372 FROM_HERE,
1373 base::Bind(
1374 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1375 return;
1378 status = database->PurgeUncommittedResourceIds(ids);
1379 if (status != ServiceWorkerDatabase::STATUS_OK) {
1380 original_task_runner->PostTask(
1381 FROM_HERE,
1382 base::Bind(
1383 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1384 return;
1387 ids.clear();
1388 status = database->GetPurgeableResourceIds(&ids);
1389 original_task_runner->PostTask(
1390 FROM_HERE,
1391 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1394 void ServiceWorkerStorage::ReadInitialDataFromDB(
1395 ServiceWorkerDatabase* database,
1396 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1397 const InitializeCallback& callback) {
1398 DCHECK(database);
1399 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1400 new ServiceWorkerStorage::InitialData());
1402 ServiceWorkerDatabase::Status status =
1403 database->GetNextAvailableIds(&data->next_registration_id,
1404 &data->next_version_id,
1405 &data->next_resource_id);
1406 if (status != ServiceWorkerDatabase::STATUS_OK) {
1407 original_task_runner->PostTask(
1408 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1409 return;
1412 status = database->GetOriginsWithRegistrations(&data->origins);
1413 original_task_runner->PostTask(
1414 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1417 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1418 ServiceWorkerDatabase* database,
1419 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1420 int64 registration_id,
1421 const GURL& origin,
1422 const DeleteRegistrationCallback& callback) {
1423 DCHECK(database);
1425 ServiceWorkerDatabase::RegistrationData deleted_version;
1426 std::vector<int64> newly_purgeable_resources;
1427 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1428 registration_id, origin, &deleted_version, &newly_purgeable_resources);
1429 if (status != ServiceWorkerDatabase::STATUS_OK) {
1430 original_task_runner->PostTask(
1431 FROM_HERE,
1432 base::Bind(
1433 callback, false, deleted_version, std::vector<int64>(), status));
1434 return;
1437 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1438 // unique origin list.
1439 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1440 status = database->GetRegistrationsForOrigin(origin, &registrations);
1441 if (status != ServiceWorkerDatabase::STATUS_OK) {
1442 original_task_runner->PostTask(
1443 FROM_HERE,
1444 base::Bind(
1445 callback, false, deleted_version, std::vector<int64>(), status));
1446 return;
1449 bool deletable = registrations.empty();
1450 original_task_runner->PostTask(FROM_HERE,
1451 base::Bind(callback,
1452 deletable,
1453 deleted_version,
1454 newly_purgeable_resources,
1455 status));
1458 void ServiceWorkerStorage::WriteRegistrationInDB(
1459 ServiceWorkerDatabase* database,
1460 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1461 const ServiceWorkerDatabase::RegistrationData& data,
1462 const ResourceList& resources,
1463 const WriteRegistrationCallback& callback) {
1464 DCHECK(database);
1465 ServiceWorkerDatabase::RegistrationData deleted_version;
1466 std::vector<int64> newly_purgeable_resources;
1467 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1468 data, resources, &deleted_version, &newly_purgeable_resources);
1469 original_task_runner->PostTask(FROM_HERE,
1470 base::Bind(callback,
1471 data.script.GetOrigin(),
1472 deleted_version,
1473 newly_purgeable_resources,
1474 status));
1477 void ServiceWorkerStorage::FindForDocumentInDB(
1478 ServiceWorkerDatabase* database,
1479 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1480 const GURL& document_url,
1481 const FindInDBCallback& callback) {
1482 GURL origin = document_url.GetOrigin();
1483 RegistrationList registrations;
1484 ServiceWorkerDatabase::Status status =
1485 database->GetRegistrationsForOrigin(origin, &registrations);
1486 if (status != ServiceWorkerDatabase::STATUS_OK) {
1487 original_task_runner->PostTask(
1488 FROM_HERE,
1489 base::Bind(callback,
1490 ServiceWorkerDatabase::RegistrationData(),
1491 ResourceList(),
1492 status));
1493 return;
1496 ServiceWorkerDatabase::RegistrationData data;
1497 ResourceList resources;
1498 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1500 // Find one with a pattern match.
1501 LongestScopeMatcher matcher(document_url);
1502 int64 match = kInvalidServiceWorkerRegistrationId;
1503 for (size_t i = 0; i < registrations.size(); ++i) {
1504 if (matcher.MatchLongest(registrations[i].scope))
1505 match = registrations[i].registration_id;
1508 if (match != kInvalidServiceWorkerRegistrationId)
1509 status = database->ReadRegistration(match, origin, &data, &resources);
1511 original_task_runner->PostTask(
1512 FROM_HERE,
1513 base::Bind(callback, data, resources, status));
1516 void ServiceWorkerStorage::FindForPatternInDB(
1517 ServiceWorkerDatabase* database,
1518 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1519 const GURL& scope,
1520 const FindInDBCallback& callback) {
1521 GURL origin = scope.GetOrigin();
1522 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1523 ServiceWorkerDatabase::Status status =
1524 database->GetRegistrationsForOrigin(origin, &registrations);
1525 if (status != ServiceWorkerDatabase::STATUS_OK) {
1526 original_task_runner->PostTask(
1527 FROM_HERE,
1528 base::Bind(callback,
1529 ServiceWorkerDatabase::RegistrationData(),
1530 ResourceList(),
1531 status));
1532 return;
1535 // Find one with an exact matching scope.
1536 ServiceWorkerDatabase::RegistrationData data;
1537 ResourceList resources;
1538 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1539 for (RegistrationList::const_iterator it = registrations.begin();
1540 it != registrations.end(); ++it) {
1541 if (scope != it->scope)
1542 continue;
1543 status = database->ReadRegistration(it->registration_id, origin,
1544 &data, &resources);
1545 break; // We're done looping.
1548 original_task_runner->PostTask(
1549 FROM_HERE,
1550 base::Bind(callback, data, resources, status));
1553 void ServiceWorkerStorage::FindForIdInDB(
1554 ServiceWorkerDatabase* database,
1555 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1556 int64 registration_id,
1557 const GURL& origin,
1558 const FindInDBCallback& callback) {
1559 ServiceWorkerDatabase::RegistrationData data;
1560 ResourceList resources;
1561 ServiceWorkerDatabase::Status status =
1562 database->ReadRegistration(registration_id, origin, &data, &resources);
1563 original_task_runner->PostTask(
1564 FROM_HERE, base::Bind(callback, data, resources, status));
1567 void ServiceWorkerStorage::GetUserDataInDB(
1568 ServiceWorkerDatabase* database,
1569 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1570 int64 registration_id,
1571 const std::string& key,
1572 const GetUserDataInDBCallback& callback) {
1573 std::string data;
1574 ServiceWorkerDatabase::Status status =
1575 database->ReadUserData(registration_id, key, &data);
1576 original_task_runner->PostTask(
1577 FROM_HERE, base::Bind(callback, data, status));
1580 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1581 ServiceWorkerDatabase* database,
1582 const std::set<GURL>& origins) {
1583 DCHECK(database);
1585 std::vector<int64> newly_purgeable_resources;
1586 database->DeleteAllDataForOrigins(origins, &newly_purgeable_resources);
1589 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1590 // is transient and it can get healed soon (e.g. IO error). To do that, the
1591 // database should not disable itself when an error occurs and the storage
1592 // controls it instead.
1593 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1594 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1595 // removed.
1596 if (state_ == DISABLED) {
1597 // Recovery process has already been scheduled.
1598 return;
1600 Disable();
1602 DVLOG(1) << "Schedule to delete the context and start over.";
1603 context_->ScheduleDeleteAndStartOver();
1606 void ServiceWorkerStorage::DidDeleteDatabase(
1607 const StatusCallback& callback,
1608 ServiceWorkerDatabase::Status status) {
1609 DCHECK_EQ(DISABLED, state_);
1610 if (status != ServiceWorkerDatabase::STATUS_OK) {
1611 // Give up the corruption recovery until the browser restarts.
1612 LOG(ERROR) << "Failed to delete the database: "
1613 << ServiceWorkerDatabase::StatusToString(status);
1614 callback.Run(DatabaseStatusToStatusCode(status));
1615 return;
1617 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1619 // Delete the disk cache on the cache thread.
1620 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1621 // Deleting the directory could take a long time and restart could be delayed.
1622 // We should probably rename the directory and delete it later.
1623 PostTaskAndReplyWithResult(
1624 database_task_manager_->GetTaskRunner(),
1625 FROM_HERE,
1626 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1627 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1628 weak_factory_.GetWeakPtr(),
1629 callback));
1632 void ServiceWorkerStorage::DidDeleteDiskCache(
1633 const StatusCallback& callback, bool result) {
1634 DCHECK_EQ(DISABLED, state_);
1635 if (!result) {
1636 // Give up the corruption recovery until the browser restarts.
1637 LOG(ERROR) << "Failed to delete the diskcache.";
1638 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1639 return;
1641 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1642 callback.Run(SERVICE_WORKER_OK);
1645 } // namespace content