[Storage] Blob Storage Refactoring pt 1:
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blob7c998c69d4562b791d16126adf6acd6e90f48139
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 if (registration && registration->is_deleted()) {
43 // It's past the point of no return and no longer findable.
44 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, nullptr);
45 return;
47 callback.Run(status, registration);
50 void CompleteFindSoon(
51 const tracked_objects::Location& from_here,
52 const scoped_refptr<ServiceWorkerRegistration>& registration,
53 ServiceWorkerStatusCode status,
54 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
55 RunSoon(from_here,
56 base::Bind(&CompleteFindNow, registration, status, callback));
59 const base::FilePath::CharType kDatabaseName[] =
60 FILE_PATH_LITERAL("Database");
61 const base::FilePath::CharType kDiskCacheName[] =
62 FILE_PATH_LITERAL("Cache");
64 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
65 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
67 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
68 ServiceWorkerDatabase::Status status) {
69 switch (status) {
70 case ServiceWorkerDatabase::STATUS_OK:
71 return SERVICE_WORKER_OK;
72 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
73 return SERVICE_WORKER_ERROR_NOT_FOUND;
74 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
75 NOTREACHED();
76 default:
77 return SERVICE_WORKER_ERROR_FAILED;
81 class ResponseComparer : public base::RefCounted<ResponseComparer> {
82 public:
83 ResponseComparer(
84 base::WeakPtr<ServiceWorkerStorage> owner,
85 scoped_ptr<ServiceWorkerResponseReader> lhs,
86 scoped_ptr<ServiceWorkerResponseReader> rhs,
87 const ServiceWorkerStorage::CompareCallback& callback)
88 : owner_(owner),
89 completion_callback_(callback),
90 lhs_reader_(lhs.release()),
91 rhs_reader_(rhs.release()),
92 completion_count_(0),
93 previous_result_(0) {
96 void Start();
98 private:
99 friend class base::RefCounted<ResponseComparer>;
101 static const int kBufferSize = 16 * 1024;
103 ~ResponseComparer() {}
104 void ReadInfos();
105 void OnReadInfoComplete(int result);
106 void ReadSomeData();
107 void OnReadDataComplete(int result);
109 base::WeakPtr<ServiceWorkerStorage> owner_;
110 ServiceWorkerStorage::CompareCallback completion_callback_;
111 scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
112 scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
113 scoped_refptr<net::IOBuffer> lhs_buffer_;
114 scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
115 scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
116 scoped_refptr<net::IOBuffer> rhs_buffer_;
117 int completion_count_;
118 int previous_result_;
119 DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
122 void ResponseComparer::Start() {
123 lhs_buffer_ = new net::IOBuffer(kBufferSize);
124 lhs_info_ = new HttpResponseInfoIOBuffer();
125 rhs_buffer_ = new net::IOBuffer(kBufferSize);
126 rhs_info_ = new HttpResponseInfoIOBuffer();
128 ReadInfos();
131 void ResponseComparer::ReadInfos() {
132 lhs_reader_->ReadInfo(
133 lhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
134 rhs_reader_->ReadInfo(
135 rhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
138 void ResponseComparer::OnReadInfoComplete(int result) {
139 if (completion_callback_.is_null() || !owner_)
140 return;
141 if (result < 0) {
142 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
143 completion_callback_.Reset();
144 return;
146 if (++completion_count_ != 2)
147 return;
149 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
150 completion_callback_.Run(SERVICE_WORKER_OK, false);
151 return;
153 ReadSomeData();
156 void ResponseComparer::ReadSomeData() {
157 completion_count_ = 0;
158 lhs_reader_->ReadData(
159 lhs_buffer_.get(),
160 kBufferSize,
161 base::Bind(&ResponseComparer::OnReadDataComplete, this));
162 rhs_reader_->ReadData(
163 rhs_buffer_.get(),
164 kBufferSize,
165 base::Bind(&ResponseComparer::OnReadDataComplete, this));
168 void ResponseComparer::OnReadDataComplete(int result) {
169 if (completion_callback_.is_null() || !owner_)
170 return;
171 if (result < 0) {
172 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
173 completion_callback_.Reset();
174 return;
176 if (++completion_count_ != 2) {
177 previous_result_ = result;
178 return;
181 // TODO(michaeln): Probably shouldn't assume that the amounts read from
182 // each reader will always be the same. This would wrongly signal false
183 // in that case.
184 if (result != previous_result_) {
185 completion_callback_.Run(SERVICE_WORKER_OK, false);
186 return;
189 if (result == 0) {
190 completion_callback_.Run(SERVICE_WORKER_OK, true);
191 return;
194 int compare_result =
195 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
196 if (compare_result != 0) {
197 completion_callback_.Run(SERVICE_WORKER_OK, false);
198 return;
201 ReadSomeData();
204 } // namespace
206 ServiceWorkerStorage::InitialData::InitialData()
207 : next_registration_id(kInvalidServiceWorkerRegistrationId),
208 next_version_id(kInvalidServiceWorkerVersionId),
209 next_resource_id(kInvalidServiceWorkerResourceId) {
212 ServiceWorkerStorage::InitialData::~InitialData() {
215 ServiceWorkerStorage::
216 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
217 : registration_id(kInvalidServiceWorkerRegistrationId) {
220 ServiceWorkerStorage::
221 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
224 ServiceWorkerStorage::~ServiceWorkerStorage() {
225 ClearSessionOnlyOrigins();
226 weak_factory_.InvalidateWeakPtrs();
227 database_task_manager_->GetTaskRunner()->DeleteSoon(FROM_HERE,
228 database_.release());
231 // static
232 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
233 const base::FilePath& path,
234 base::WeakPtr<ServiceWorkerContextCore> context,
235 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
236 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
237 storage::QuotaManagerProxy* quota_manager_proxy,
238 storage::SpecialStoragePolicy* special_storage_policy) {
239 return make_scoped_ptr(new ServiceWorkerStorage(path,
240 context,
241 database_task_manager.Pass(),
242 disk_cache_thread,
243 quota_manager_proxy,
244 special_storage_policy));
247 // static
248 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
249 base::WeakPtr<ServiceWorkerContextCore> context,
250 ServiceWorkerStorage* old_storage) {
251 return make_scoped_ptr(
252 new ServiceWorkerStorage(old_storage->path_,
253 context,
254 old_storage->database_task_manager_->Clone(),
255 old_storage->disk_cache_thread_,
256 old_storage->quota_manager_proxy_.get(),
257 old_storage->special_storage_policy_.get()));
260 void ServiceWorkerStorage::FindRegistrationForDocument(
261 const GURL& document_url,
262 const FindRegistrationCallback& callback) {
263 DCHECK(!document_url.has_ref());
264 if (!LazyInitialize(base::Bind(
265 &ServiceWorkerStorage::FindRegistrationForDocument,
266 weak_factory_.GetWeakPtr(), document_url, callback))) {
267 if (state_ != INITIALIZING || !context_) {
268 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
269 SERVICE_WORKER_ERROR_FAILED, callback);
271 TRACE_EVENT_INSTANT1(
272 "ServiceWorker",
273 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
274 TRACE_EVENT_SCOPE_THREAD,
275 "URL", document_url.spec());
276 return;
278 DCHECK_EQ(INITIALIZED, state_);
280 // See if there are any stored registrations for the origin.
281 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
282 // Look for something currently being installed.
283 scoped_refptr<ServiceWorkerRegistration> installing_registration =
284 FindInstallingRegistrationForDocument(document_url);
285 ServiceWorkerStatusCode status = installing_registration.get() ?
286 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
287 TRACE_EVENT_INSTANT2(
288 "ServiceWorker",
289 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
290 TRACE_EVENT_SCOPE_THREAD,
291 "URL", document_url.spec(),
292 "Status", ServiceWorkerStatusToString(status));
293 CompleteFindNow(installing_registration,
294 status,
295 callback);
296 return;
299 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
300 // callback id.
301 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
302 TRACE_EVENT_ASYNC_BEGIN1(
303 "ServiceWorker",
304 "ServiceWorkerStorage::FindRegistrationForDocument",
305 callback_id,
306 "URL", document_url.spec());
307 database_task_manager_->GetTaskRunner()->PostTask(
308 FROM_HERE,
309 base::Bind(
310 &FindForDocumentInDB,
311 database_.get(),
312 base::MessageLoopProxy::current(),
313 document_url,
314 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
315 weak_factory_.GetWeakPtr(),
316 document_url,
317 callback,
318 callback_id)));
321 void ServiceWorkerStorage::FindRegistrationForPattern(
322 const GURL& scope,
323 const FindRegistrationCallback& callback) {
324 if (!LazyInitialize(base::Bind(
325 &ServiceWorkerStorage::FindRegistrationForPattern,
326 weak_factory_.GetWeakPtr(), scope, callback))) {
327 if (state_ != INITIALIZING || !context_) {
328 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
329 SERVICE_WORKER_ERROR_FAILED, callback);
331 return;
333 DCHECK_EQ(INITIALIZED, state_);
335 // See if there are any stored registrations for the origin.
336 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
337 // Look for something currently being installed.
338 scoped_refptr<ServiceWorkerRegistration> installing_registration =
339 FindInstallingRegistrationForPattern(scope);
340 CompleteFindSoon(FROM_HERE,
341 installing_registration,
342 installing_registration.get()
343 ? SERVICE_WORKER_OK
344 : SERVICE_WORKER_ERROR_NOT_FOUND,
345 callback);
346 return;
349 database_task_manager_->GetTaskRunner()->PostTask(
350 FROM_HERE,
351 base::Bind(
352 &FindForPatternInDB,
353 database_.get(),
354 base::MessageLoopProxy::current(),
355 scope,
356 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
357 weak_factory_.GetWeakPtr(), scope, callback)));
360 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
361 const GURL& scope) {
362 if (state_ != INITIALIZED || !context_)
363 return NULL;
364 for (RegistrationRefsById::const_iterator it =
365 uninstalling_registrations_.begin();
366 it != uninstalling_registrations_.end();
367 ++it) {
368 if (it->second->pattern() == scope) {
369 DCHECK(it->second->is_uninstalling());
370 return it->second.get();
373 return NULL;
376 void ServiceWorkerStorage::FindRegistrationForId(
377 int64 registration_id,
378 const GURL& origin,
379 const FindRegistrationCallback& callback) {
380 if (!LazyInitialize(base::Bind(
381 &ServiceWorkerStorage::FindRegistrationForId,
382 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
383 if (state_ != INITIALIZING || !context_) {
384 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
385 SERVICE_WORKER_ERROR_FAILED, callback);
387 return;
389 DCHECK_EQ(INITIALIZED, state_);
391 // See if there are any stored registrations for the origin.
392 if (!ContainsKey(registered_origins_, origin)) {
393 // Look for something currently being installed.
394 scoped_refptr<ServiceWorkerRegistration> installing_registration =
395 FindInstallingRegistrationForId(registration_id);
396 CompleteFindNow(installing_registration,
397 installing_registration.get()
398 ? SERVICE_WORKER_OK
399 : SERVICE_WORKER_ERROR_NOT_FOUND,
400 callback);
401 return;
404 scoped_refptr<ServiceWorkerRegistration> registration =
405 context_->GetLiveRegistration(registration_id);
406 if (registration.get()) {
407 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
408 return;
411 database_task_manager_->GetTaskRunner()->PostTask(
412 FROM_HERE,
413 base::Bind(&FindForIdInDB,
414 database_.get(),
415 base::MessageLoopProxy::current(),
416 registration_id, origin,
417 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
418 weak_factory_.GetWeakPtr(), callback)));
421 void ServiceWorkerStorage::FindRegistrationForIdOnly(
422 int64 registration_id,
423 const FindRegistrationCallback& callback) {
424 if (!LazyInitialize(
425 base::Bind(&ServiceWorkerStorage::FindRegistrationForIdOnly,
426 weak_factory_.GetWeakPtr(), registration_id, callback))) {
427 if (state_ != INITIALIZING || !context_) {
428 CompleteFindNow(nullptr, SERVICE_WORKER_ERROR_FAILED, callback);
430 return;
432 DCHECK_EQ(INITIALIZED, state_);
434 scoped_refptr<ServiceWorkerRegistration> registration =
435 context_->GetLiveRegistration(registration_id);
436 if (registration) {
437 // Delegate to FindRegistrationForId to make sure the same subset of live
438 // registrations is returned.
439 // TODO(mek): CompleteFindNow should really do all the required checks, so
440 // calling that directly here should be enough.
441 FindRegistrationForId(registration_id, registration->pattern().GetOrigin(),
442 callback);
443 return;
446 database_task_manager_->GetTaskRunner()->PostTask(
447 FROM_HERE,
448 base::Bind(&FindForIdOnlyInDB, database_.get(),
449 base::MessageLoopProxy::current(), registration_id,
450 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
451 weak_factory_.GetWeakPtr(), callback)));
454 void ServiceWorkerStorage::GetRegistrationsForOrigin(
455 const GURL& origin, const GetRegistrationsInfosCallback& callback) {
456 if (!LazyInitialize(base::Bind(
457 &ServiceWorkerStorage::GetRegistrationsForOrigin,
458 weak_factory_.GetWeakPtr(), origin, callback))) {
459 if (state_ != INITIALIZING || !context_) {
460 RunSoon(FROM_HERE, base::Bind(
461 callback, std::vector<ServiceWorkerRegistrationInfo>()));
463 return;
465 DCHECK_EQ(INITIALIZED, state_);
467 RegistrationList* registrations = new RegistrationList;
468 PostTaskAndReplyWithResult(
469 database_task_manager_->GetTaskRunner(),
470 FROM_HERE,
471 base::Bind(&ServiceWorkerDatabase::GetRegistrationsForOrigin,
472 base::Unretained(database_.get()),
473 origin,
474 base::Unretained(registrations)),
475 base::Bind(&ServiceWorkerStorage::DidGetRegistrations,
476 weak_factory_.GetWeakPtr(),
477 callback,
478 base::Owned(registrations),
479 origin));
482 void ServiceWorkerStorage::GetAllRegistrations(
483 const GetRegistrationsInfosCallback& callback) {
484 if (!LazyInitialize(base::Bind(
485 &ServiceWorkerStorage::GetAllRegistrations,
486 weak_factory_.GetWeakPtr(), callback))) {
487 if (state_ != INITIALIZING || !context_) {
488 RunSoon(FROM_HERE, base::Bind(
489 callback, std::vector<ServiceWorkerRegistrationInfo>()));
491 return;
493 DCHECK_EQ(INITIALIZED, state_);
495 RegistrationList* registrations = new RegistrationList;
496 PostTaskAndReplyWithResult(
497 database_task_manager_->GetTaskRunner(),
498 FROM_HERE,
499 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
500 base::Unretained(database_.get()),
501 base::Unretained(registrations)),
502 base::Bind(&ServiceWorkerStorage::DidGetRegistrations,
503 weak_factory_.GetWeakPtr(),
504 callback,
505 base::Owned(registrations),
506 GURL()));
509 void ServiceWorkerStorage::StoreRegistration(
510 ServiceWorkerRegistration* registration,
511 ServiceWorkerVersion* version,
512 const StatusCallback& callback) {
513 DCHECK(registration);
514 DCHECK(version);
516 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
517 if (IsDisabled() || !context_) {
518 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
519 return;
522 ServiceWorkerDatabase::RegistrationData data;
523 data.registration_id = registration->id();
524 data.scope = registration->pattern();
525 data.script = version->script_url();
526 data.has_fetch_handler = true;
527 data.version_id = version->version_id();
528 data.last_update_check = registration->last_update_check();
529 data.is_active = (version == registration->active_version());
531 ResourceList resources;
532 version->script_cache_map()->GetResources(&resources);
534 uint64 resources_total_size_bytes = 0;
535 for (const auto& resource : resources) {
536 resources_total_size_bytes += resource.size_bytes;
538 data.resources_total_size_bytes = resources_total_size_bytes;
540 if (!has_checked_for_stale_resources_)
541 DeleteStaleResources();
543 database_task_manager_->GetTaskRunner()->PostTask(
544 FROM_HERE,
545 base::Bind(&WriteRegistrationInDB,
546 database_.get(),
547 base::MessageLoopProxy::current(),
548 data,
549 resources,
550 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
551 weak_factory_.GetWeakPtr(),
552 callback,
553 data)));
555 registration->set_is_deleted(false);
558 void ServiceWorkerStorage::UpdateToActiveState(
559 ServiceWorkerRegistration* registration,
560 const StatusCallback& callback) {
561 DCHECK(registration);
563 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
564 if (IsDisabled() || !context_) {
565 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
566 return;
569 PostTaskAndReplyWithResult(
570 database_task_manager_->GetTaskRunner(),
571 FROM_HERE,
572 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
573 base::Unretained(database_.get()),
574 registration->id(),
575 registration->pattern().GetOrigin()),
576 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
577 weak_factory_.GetWeakPtr(),
578 callback));
581 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
582 ServiceWorkerRegistration* registration) {
583 DCHECK(registration);
585 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
586 if (IsDisabled() || !context_)
587 return;
589 database_task_manager_->GetTaskRunner()->PostTask(
590 FROM_HERE,
591 base::Bind(
592 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
593 base::Unretained(database_.get()),
594 registration->id(),
595 registration->pattern().GetOrigin(),
596 registration->last_update_check()));
599 void ServiceWorkerStorage::DeleteRegistration(
600 int64 registration_id,
601 const GURL& origin,
602 const StatusCallback& callback) {
603 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
604 if (IsDisabled() || !context_) {
605 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
606 return;
609 if (!has_checked_for_stale_resources_)
610 DeleteStaleResources();
612 DidDeleteRegistrationParams params;
613 params.registration_id = registration_id;
614 params.origin = origin;
615 params.callback = callback;
617 database_task_manager_->GetTaskRunner()->PostTask(
618 FROM_HERE,
619 base::Bind(&DeleteRegistrationFromDB,
620 database_.get(),
621 base::MessageLoopProxy::current(),
622 registration_id, origin,
623 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
624 weak_factory_.GetWeakPtr(), params)));
626 // The registration should no longer be findable.
627 pending_deletions_.insert(registration_id);
628 ServiceWorkerRegistration* registration =
629 context_->GetLiveRegistration(registration_id);
630 if (registration)
631 registration->set_is_deleted(true);
634 scoped_ptr<ServiceWorkerResponseReader>
635 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
636 return make_scoped_ptr(
637 new ServiceWorkerResponseReader(response_id, disk_cache()));
640 scoped_ptr<ServiceWorkerResponseWriter>
641 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
642 return make_scoped_ptr(
643 new ServiceWorkerResponseWriter(response_id, disk_cache()));
646 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
647 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
648 DCHECK_EQ(INITIALIZED, state_);
650 if (!has_checked_for_stale_resources_)
651 DeleteStaleResources();
653 database_task_manager_->GetTaskRunner()->PostTask(
654 FROM_HERE,
655 base::Bind(base::IgnoreResult(
656 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
657 base::Unretained(database_.get()),
658 std::set<int64>(&id, &id + 1)));
661 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
662 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
663 database_task_manager_->GetTaskRunner()->PostTask(
664 FROM_HERE,
665 base::Bind(base::IgnoreResult(
666 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
667 base::Unretained(database_.get()),
668 std::set<int64>(&id, &id + 1)));
669 StartPurgingResources(std::vector<int64>(1, id));
672 void ServiceWorkerStorage::CompareScriptResources(
673 int64 lhs_id, int64 rhs_id,
674 const CompareCallback& callback) {
675 DCHECK(!callback.is_null());
676 scoped_refptr<ResponseComparer> comparer =
677 new ResponseComparer(weak_factory_.GetWeakPtr(),
678 CreateResponseReader(lhs_id),
679 CreateResponseReader(rhs_id),
680 callback);
681 comparer->Start(); // It deletes itself when done.
684 void ServiceWorkerStorage::StoreUserData(
685 int64 registration_id,
686 const GURL& origin,
687 const std::string& key,
688 const std::string& data,
689 const StatusCallback& callback) {
690 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
691 if (IsDisabled() || !context_) {
692 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
693 return;
696 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
697 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
698 return;
701 PostTaskAndReplyWithResult(
702 database_task_manager_->GetTaskRunner(),
703 FROM_HERE,
704 base::Bind(&ServiceWorkerDatabase::WriteUserData,
705 base::Unretained(database_.get()),
706 registration_id, origin, key, data),
707 base::Bind(&ServiceWorkerStorage::DidStoreUserData,
708 weak_factory_.GetWeakPtr(),
709 callback));
712 void ServiceWorkerStorage::GetUserData(
713 int64 registration_id,
714 const std::string& key,
715 const GetUserDataCallback& callback) {
716 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
717 if (IsDisabled() || !context_) {
718 RunSoon(FROM_HERE,
719 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
720 return;
723 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
724 RunSoon(FROM_HERE,
725 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
726 return;
729 database_task_manager_->GetTaskRunner()->PostTask(
730 FROM_HERE,
731 base::Bind(&ServiceWorkerStorage::GetUserDataInDB,
732 database_.get(),
733 base::MessageLoopProxy::current(),
734 registration_id,
735 key,
736 base::Bind(&ServiceWorkerStorage::DidGetUserData,
737 weak_factory_.GetWeakPtr(), callback)));
740 void ServiceWorkerStorage::ClearUserData(
741 int64 registration_id,
742 const std::string& key,
743 const StatusCallback& callback) {
744 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
745 if (IsDisabled() || !context_) {
746 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
747 return;
750 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
751 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
752 return;
755 PostTaskAndReplyWithResult(
756 database_task_manager_->GetTaskRunner(),
757 FROM_HERE,
758 base::Bind(&ServiceWorkerDatabase::DeleteUserData,
759 base::Unretained(database_.get()),
760 registration_id, key),
761 base::Bind(&ServiceWorkerStorage::DidDeleteUserData,
762 weak_factory_.GetWeakPtr(),
763 callback));
766 void ServiceWorkerStorage::GetUserDataForAllRegistrations(
767 const std::string& key,
768 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
769 callback) {
770 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
771 if (IsDisabled() || !context_) {
772 RunSoon(FROM_HERE,
773 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
774 SERVICE_WORKER_ERROR_FAILED));
775 return;
778 if (key.empty()) {
779 RunSoon(FROM_HERE,
780 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
781 SERVICE_WORKER_ERROR_FAILED));
782 return;
785 database_task_manager_->GetTaskRunner()->PostTask(
786 FROM_HERE,
787 base::Bind(
788 &ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB,
789 database_.get(), base::MessageLoopProxy::current(), key,
790 base::Bind(&ServiceWorkerStorage::DidGetUserDataForAllRegistrations,
791 weak_factory_.GetWeakPtr(), callback)));
794 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
795 Disable();
797 // Delete the database on the database thread.
798 PostTaskAndReplyWithResult(
799 database_task_manager_->GetTaskRunner(),
800 FROM_HERE,
801 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
802 base::Unretained(database_.get())),
803 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
804 weak_factory_.GetWeakPtr(),
805 callback));
808 int64 ServiceWorkerStorage::NewRegistrationId() {
809 if (state_ == DISABLED)
810 return kInvalidServiceWorkerRegistrationId;
811 DCHECK_EQ(INITIALIZED, state_);
812 return next_registration_id_++;
815 int64 ServiceWorkerStorage::NewVersionId() {
816 if (state_ == DISABLED)
817 return kInvalidServiceWorkerVersionId;
818 DCHECK_EQ(INITIALIZED, state_);
819 return next_version_id_++;
822 int64 ServiceWorkerStorage::NewResourceId() {
823 if (state_ == DISABLED)
824 return kInvalidServiceWorkerResourceId;
825 DCHECK_EQ(INITIALIZED, state_);
826 return next_resource_id_++;
829 void ServiceWorkerStorage::NotifyInstallingRegistration(
830 ServiceWorkerRegistration* registration) {
831 DCHECK(installing_registrations_.find(registration->id()) ==
832 installing_registrations_.end());
833 installing_registrations_[registration->id()] = registration;
836 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
837 ServiceWorkerRegistration* registration,
838 ServiceWorkerVersion* version,
839 ServiceWorkerStatusCode status) {
840 installing_registrations_.erase(registration->id());
841 if (status != SERVICE_WORKER_OK && version) {
842 ResourceList resources;
843 version->script_cache_map()->GetResources(&resources);
845 std::set<int64> ids;
846 for (size_t i = 0; i < resources.size(); ++i)
847 ids.insert(resources[i].resource_id);
849 database_task_manager_->GetTaskRunner()->PostTask(
850 FROM_HERE,
851 base::Bind(base::IgnoreResult(
852 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
853 base::Unretained(database_.get()),
854 ids));
858 void ServiceWorkerStorage::NotifyUninstallingRegistration(
859 ServiceWorkerRegistration* registration) {
860 DCHECK(uninstalling_registrations_.find(registration->id()) ==
861 uninstalling_registrations_.end());
862 uninstalling_registrations_[registration->id()] = registration;
865 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
866 ServiceWorkerRegistration* registration) {
867 uninstalling_registrations_.erase(registration->id());
870 void ServiceWorkerStorage::Disable() {
871 state_ = DISABLED;
872 if (disk_cache_)
873 disk_cache_->Disable();
876 bool ServiceWorkerStorage::IsDisabled() const {
877 return state_ == DISABLED;
880 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
881 if (!has_checked_for_stale_resources_)
882 DeleteStaleResources();
883 StartPurgingResources(resources);
886 ServiceWorkerStorage::ServiceWorkerStorage(
887 const base::FilePath& path,
888 base::WeakPtr<ServiceWorkerContextCore> context,
889 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
890 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
891 storage::QuotaManagerProxy* quota_manager_proxy,
892 storage::SpecialStoragePolicy* special_storage_policy)
893 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
894 next_version_id_(kInvalidServiceWorkerVersionId),
895 next_resource_id_(kInvalidServiceWorkerResourceId),
896 state_(UNINITIALIZED),
897 path_(path),
898 context_(context),
899 database_task_manager_(database_task_manager.Pass()),
900 disk_cache_thread_(disk_cache_thread),
901 quota_manager_proxy_(quota_manager_proxy),
902 special_storage_policy_(special_storage_policy),
903 is_purge_pending_(false),
904 has_checked_for_stale_resources_(false),
905 weak_factory_(this) {
906 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
909 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
910 if (path_.empty())
911 return base::FilePath();
912 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
913 .Append(kDatabaseName);
916 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
917 if (path_.empty())
918 return base::FilePath();
919 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
920 .Append(kDiskCacheName);
923 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
924 if (!context_)
925 return false;
927 switch (state_) {
928 case INITIALIZED:
929 return true;
930 case DISABLED:
931 return false;
932 case INITIALIZING:
933 pending_tasks_.push_back(callback);
934 return false;
935 case UNINITIALIZED:
936 pending_tasks_.push_back(callback);
937 // Fall-through.
940 state_ = INITIALIZING;
941 database_task_manager_->GetTaskRunner()->PostTask(
942 FROM_HERE,
943 base::Bind(&ReadInitialDataFromDB,
944 database_.get(),
945 base::MessageLoopProxy::current(),
946 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
947 weak_factory_.GetWeakPtr())));
948 return false;
951 void ServiceWorkerStorage::DidReadInitialData(
952 InitialData* data,
953 ServiceWorkerDatabase::Status status) {
954 DCHECK(data);
955 DCHECK_EQ(INITIALIZING, state_);
957 if (status == ServiceWorkerDatabase::STATUS_OK) {
958 next_registration_id_ = data->next_registration_id;
959 next_version_id_ = data->next_version_id;
960 next_resource_id_ = data->next_resource_id;
961 registered_origins_.swap(data->origins);
962 state_ = INITIALIZED;
963 } else {
964 DVLOG(2) << "Failed to initialize: "
965 << ServiceWorkerDatabase::StatusToString(status);
966 ScheduleDeleteAndStartOver();
969 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
970 it != pending_tasks_.end(); ++it) {
971 RunSoon(FROM_HERE, *it);
973 pending_tasks_.clear();
976 void ServiceWorkerStorage::DidFindRegistrationForDocument(
977 const GURL& document_url,
978 const FindRegistrationCallback& callback,
979 int64 callback_id,
980 const ServiceWorkerDatabase::RegistrationData& data,
981 const ResourceList& resources,
982 ServiceWorkerDatabase::Status status) {
983 if (status == ServiceWorkerDatabase::STATUS_OK) {
984 ReturnFoundRegistration(callback, data, resources);
985 TRACE_EVENT_ASYNC_END1(
986 "ServiceWorker",
987 "ServiceWorkerStorage::FindRegistrationForDocument",
988 callback_id,
989 "Status", ServiceWorkerDatabase::StatusToString(status));
990 return;
993 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
994 // Look for something currently being installed.
995 scoped_refptr<ServiceWorkerRegistration> installing_registration =
996 FindInstallingRegistrationForDocument(document_url);
997 ServiceWorkerStatusCode installing_status = installing_registration.get() ?
998 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
999 callback.Run(installing_status, installing_registration);
1000 TRACE_EVENT_ASYNC_END2(
1001 "ServiceWorker",
1002 "ServiceWorkerStorage::FindRegistrationForDocument",
1003 callback_id,
1004 "Status", ServiceWorkerDatabase::StatusToString(status),
1005 "Info",
1006 (installing_status == SERVICE_WORKER_OK) ?
1007 "Installing registration is found" :
1008 "Any registrations are not found");
1009 return;
1012 ScheduleDeleteAndStartOver();
1013 callback.Run(DatabaseStatusToStatusCode(status),
1014 scoped_refptr<ServiceWorkerRegistration>());
1015 TRACE_EVENT_ASYNC_END1(
1016 "ServiceWorker",
1017 "ServiceWorkerStorage::FindRegistrationForDocument",
1018 callback_id,
1019 "Status", ServiceWorkerDatabase::StatusToString(status));
1022 void ServiceWorkerStorage::DidFindRegistrationForPattern(
1023 const GURL& scope,
1024 const FindRegistrationCallback& callback,
1025 const ServiceWorkerDatabase::RegistrationData& data,
1026 const ResourceList& resources,
1027 ServiceWorkerDatabase::Status status) {
1028 if (status == ServiceWorkerDatabase::STATUS_OK) {
1029 ReturnFoundRegistration(callback, data, resources);
1030 return;
1033 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1034 scoped_refptr<ServiceWorkerRegistration> installing_registration =
1035 FindInstallingRegistrationForPattern(scope);
1036 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
1037 : SERVICE_WORKER_ERROR_NOT_FOUND,
1038 installing_registration);
1039 return;
1042 ScheduleDeleteAndStartOver();
1043 callback.Run(DatabaseStatusToStatusCode(status),
1044 scoped_refptr<ServiceWorkerRegistration>());
1047 void ServiceWorkerStorage::DidFindRegistrationForId(
1048 const FindRegistrationCallback& callback,
1049 const ServiceWorkerDatabase::RegistrationData& data,
1050 const ResourceList& resources,
1051 ServiceWorkerDatabase::Status status) {
1052 if (status == ServiceWorkerDatabase::STATUS_OK) {
1053 ReturnFoundRegistration(callback, data, resources);
1054 return;
1057 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1058 // TODO(nhiroki): Find a registration in |installing_registrations_|.
1059 callback.Run(DatabaseStatusToStatusCode(status),
1060 scoped_refptr<ServiceWorkerRegistration>());
1061 return;
1064 ScheduleDeleteAndStartOver();
1065 callback.Run(DatabaseStatusToStatusCode(status),
1066 scoped_refptr<ServiceWorkerRegistration>());
1069 void ServiceWorkerStorage::ReturnFoundRegistration(
1070 const FindRegistrationCallback& callback,
1071 const ServiceWorkerDatabase::RegistrationData& data,
1072 const ResourceList& resources) {
1073 scoped_refptr<ServiceWorkerRegistration> registration =
1074 GetOrCreateRegistration(data, resources);
1075 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
1078 void ServiceWorkerStorage::DidGetRegistrations(
1079 const GetRegistrationsInfosCallback& callback,
1080 RegistrationList* registrations,
1081 const GURL& origin_filter,
1082 ServiceWorkerDatabase::Status status) {
1083 DCHECK(registrations);
1084 if (status != ServiceWorkerDatabase::STATUS_OK &&
1085 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1086 ScheduleDeleteAndStartOver();
1087 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
1088 return;
1091 // Add all stored registrations.
1092 std::set<int64> pushed_registrations;
1093 std::vector<ServiceWorkerRegistrationInfo> infos;
1094 for (const auto& registration_data : *registrations) {
1095 const bool inserted =
1096 pushed_registrations.insert(registration_data.registration_id).second;
1097 DCHECK(inserted);
1099 ServiceWorkerRegistration* registration =
1100 context_->GetLiveRegistration(registration_data.registration_id);
1101 if (registration) {
1102 infos.push_back(registration->GetInfo());
1103 continue;
1106 ServiceWorkerRegistrationInfo info;
1107 info.pattern = registration_data.scope;
1108 info.registration_id = registration_data.registration_id;
1109 info.stored_version_size_bytes =
1110 registration_data.resources_total_size_bytes;
1111 if (ServiceWorkerVersion* version =
1112 context_->GetLiveVersion(registration_data.version_id)) {
1113 if (registration_data.is_active)
1114 info.active_version = version->GetInfo();
1115 else
1116 info.waiting_version = version->GetInfo();
1117 infos.push_back(info);
1118 continue;
1121 if (registration_data.is_active) {
1122 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
1123 info.active_version.version_id = registration_data.version_id;
1124 } else {
1125 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
1126 info.waiting_version.version_id = registration_data.version_id;
1128 infos.push_back(info);
1131 // Add unstored registrations that are being installed.
1132 for (RegistrationRefsById::const_iterator it =
1133 installing_registrations_.begin();
1134 it != installing_registrations_.end(); ++it) {
1135 if ((!origin_filter.is_valid() ||
1136 it->second->pattern().GetOrigin() == origin_filter) &&
1137 pushed_registrations.insert(it->first).second) {
1138 infos.push_back(it->second->GetInfo());
1142 callback.Run(infos);
1145 void ServiceWorkerStorage::DidStoreRegistration(
1146 const StatusCallback& callback,
1147 const ServiceWorkerDatabase::RegistrationData& new_version,
1148 const GURL& origin,
1149 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1150 const std::vector<int64>& newly_purgeable_resources,
1151 ServiceWorkerDatabase::Status status) {
1152 if (status != ServiceWorkerDatabase::STATUS_OK) {
1153 ScheduleDeleteAndStartOver();
1154 callback.Run(DatabaseStatusToStatusCode(status));
1155 return;
1157 registered_origins_.insert(origin);
1159 scoped_refptr<ServiceWorkerRegistration> registration =
1160 context_->GetLiveRegistration(new_version.registration_id);
1161 registration->set_resources_total_size_bytes(
1162 new_version.resources_total_size_bytes);
1163 if (quota_manager_proxy_.get()) {
1164 // Can be nullptr in tests.
1165 quota_manager_proxy_->NotifyStorageModified(
1166 storage::QuotaClient::kServiceWorker,
1167 origin,
1168 storage::StorageType::kStorageTypeTemporary,
1169 new_version.resources_total_size_bytes -
1170 deleted_version.resources_total_size_bytes);
1173 callback.Run(SERVICE_WORKER_OK);
1175 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1176 StartPurgingResources(newly_purgeable_resources);
1179 void ServiceWorkerStorage::DidUpdateToActiveState(
1180 const StatusCallback& callback,
1181 ServiceWorkerDatabase::Status status) {
1182 if (status != ServiceWorkerDatabase::STATUS_OK &&
1183 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1184 ScheduleDeleteAndStartOver();
1186 callback.Run(DatabaseStatusToStatusCode(status));
1189 void ServiceWorkerStorage::DidDeleteRegistration(
1190 const DidDeleteRegistrationParams& params,
1191 bool origin_is_deletable,
1192 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1193 const std::vector<int64>& newly_purgeable_resources,
1194 ServiceWorkerDatabase::Status status) {
1195 pending_deletions_.erase(params.registration_id);
1196 if (status != ServiceWorkerDatabase::STATUS_OK) {
1197 ScheduleDeleteAndStartOver();
1198 params.callback.Run(DatabaseStatusToStatusCode(status));
1199 return;
1201 if (quota_manager_proxy_.get()) {
1202 // Can be nullptr in tests.
1203 quota_manager_proxy_->NotifyStorageModified(
1204 storage::QuotaClient::kServiceWorker,
1205 params.origin,
1206 storage::StorageType::kStorageTypeTemporary,
1207 -deleted_version.resources_total_size_bytes);
1209 if (origin_is_deletable)
1210 registered_origins_.erase(params.origin);
1211 params.callback.Run(SERVICE_WORKER_OK);
1213 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1214 StartPurgingResources(newly_purgeable_resources);
1217 void ServiceWorkerStorage::DidStoreUserData(
1218 const StatusCallback& callback,
1219 ServiceWorkerDatabase::Status status) {
1220 // |status| can be NOT_FOUND when the associated registration did not exist in
1221 // the database. In the case, we don't have to schedule the corruption
1222 // recovery.
1223 if (status != ServiceWorkerDatabase::STATUS_OK &&
1224 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1225 ScheduleDeleteAndStartOver();
1227 callback.Run(DatabaseStatusToStatusCode(status));
1230 void ServiceWorkerStorage::DidGetUserData(
1231 const GetUserDataCallback& callback,
1232 const std::string& data,
1233 ServiceWorkerDatabase::Status status) {
1234 if (status != ServiceWorkerDatabase::STATUS_OK &&
1235 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1236 ScheduleDeleteAndStartOver();
1238 callback.Run(data, DatabaseStatusToStatusCode(status));
1241 void ServiceWorkerStorage::DidDeleteUserData(
1242 const StatusCallback& callback,
1243 ServiceWorkerDatabase::Status status) {
1244 if (status != ServiceWorkerDatabase::STATUS_OK)
1245 ScheduleDeleteAndStartOver();
1246 callback.Run(DatabaseStatusToStatusCode(status));
1249 void ServiceWorkerStorage::DidGetUserDataForAllRegistrations(
1250 const GetUserDataForAllRegistrationsCallback& callback,
1251 const std::vector<std::pair<int64, std::string>>& user_data,
1252 ServiceWorkerDatabase::Status status) {
1253 if (status != ServiceWorkerDatabase::STATUS_OK)
1254 ScheduleDeleteAndStartOver();
1255 callback.Run(user_data, DatabaseStatusToStatusCode(status));
1258 scoped_refptr<ServiceWorkerRegistration>
1259 ServiceWorkerStorage::GetOrCreateRegistration(
1260 const ServiceWorkerDatabase::RegistrationData& data,
1261 const ResourceList& resources) {
1262 scoped_refptr<ServiceWorkerRegistration> registration =
1263 context_->GetLiveRegistration(data.registration_id);
1264 if (registration.get())
1265 return registration;
1267 registration = new ServiceWorkerRegistration(
1268 data.scope, data.registration_id, context_);
1269 registration->set_resources_total_size_bytes(data.resources_total_size_bytes);
1270 registration->set_last_update_check(data.last_update_check);
1271 if (pending_deletions_.find(data.registration_id) !=
1272 pending_deletions_.end()) {
1273 registration->set_is_deleted(true);
1275 scoped_refptr<ServiceWorkerVersion> version =
1276 context_->GetLiveVersion(data.version_id);
1277 if (!version.get()) {
1278 version = new ServiceWorkerVersion(
1279 registration.get(), data.script, data.version_id, context_);
1280 version->SetStatus(data.is_active ?
1281 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1282 version->script_cache_map()->SetResources(resources);
1285 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1286 registration->SetActiveVersion(version.get());
1287 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1288 registration->SetWaitingVersion(version.get());
1289 else
1290 NOTREACHED();
1292 return registration;
1295 ServiceWorkerRegistration*
1296 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1297 const GURL& document_url) {
1298 DCHECK(!document_url.has_ref());
1300 LongestScopeMatcher matcher(document_url);
1301 ServiceWorkerRegistration* match = NULL;
1303 // TODO(nhiroki): This searches over installing registrations linearly and it
1304 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1305 for (RegistrationRefsById::const_iterator it =
1306 installing_registrations_.begin();
1307 it != installing_registrations_.end(); ++it) {
1308 if (matcher.MatchLongest(it->second->pattern()))
1309 match = it->second.get();
1311 return match;
1314 ServiceWorkerRegistration*
1315 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1316 const GURL& scope) {
1317 for (RegistrationRefsById::const_iterator it =
1318 installing_registrations_.begin();
1319 it != installing_registrations_.end(); ++it) {
1320 if (it->second->pattern() == scope)
1321 return it->second.get();
1323 return NULL;
1326 ServiceWorkerRegistration*
1327 ServiceWorkerStorage::FindInstallingRegistrationForId(
1328 int64 registration_id) {
1329 RegistrationRefsById::const_iterator found =
1330 installing_registrations_.find(registration_id);
1331 if (found == installing_registrations_.end())
1332 return NULL;
1333 return found->second.get();
1336 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1337 if (disk_cache_)
1338 return disk_cache_.get();
1340 disk_cache_.reset(new ServiceWorkerDiskCache);
1342 base::FilePath path = GetDiskCachePath();
1343 if (path.empty()) {
1344 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1345 net::CompletionCallback());
1346 DCHECK_EQ(net::OK, rv);
1347 return disk_cache_.get();
1350 int rv = disk_cache_->InitWithDiskBackend(
1351 path,
1352 kMaxDiskCacheSize,
1353 false,
1354 disk_cache_thread_,
1355 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1356 weak_factory_.GetWeakPtr()));
1357 if (rv != net::ERR_IO_PENDING)
1358 OnDiskCacheInitialized(rv);
1360 return disk_cache_.get();
1363 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1364 if (rv != net::OK) {
1365 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1366 << net::ErrorToString(rv);
1367 ScheduleDeleteAndStartOver();
1369 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1372 void ServiceWorkerStorage::StartPurgingResources(
1373 const std::vector<int64>& ids) {
1374 DCHECK(has_checked_for_stale_resources_);
1375 for (size_t i = 0; i < ids.size(); ++i)
1376 purgeable_resource_ids_.push_back(ids[i]);
1377 ContinuePurgingResources();
1380 void ServiceWorkerStorage::StartPurgingResources(
1381 const ResourceList& resources) {
1382 DCHECK(has_checked_for_stale_resources_);
1383 for (size_t i = 0; i < resources.size(); ++i)
1384 purgeable_resource_ids_.push_back(resources[i].resource_id);
1385 ContinuePurgingResources();
1388 void ServiceWorkerStorage::ContinuePurgingResources() {
1389 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1390 return;
1392 // Do one at a time until we're done, use RunSoon to avoid recursion when
1393 // DoomEntry returns immediately.
1394 is_purge_pending_ = true;
1395 int64 id = purgeable_resource_ids_.front();
1396 purgeable_resource_ids_.pop_front();
1397 RunSoon(FROM_HERE,
1398 base::Bind(&ServiceWorkerStorage::PurgeResource,
1399 weak_factory_.GetWeakPtr(), id));
1402 void ServiceWorkerStorage::PurgeResource(int64 id) {
1403 DCHECK(is_purge_pending_);
1404 int rv = disk_cache()->DoomEntry(
1405 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1406 weak_factory_.GetWeakPtr(), id));
1407 if (rv != net::ERR_IO_PENDING)
1408 OnResourcePurged(id, rv);
1411 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1412 DCHECK(is_purge_pending_);
1413 is_purge_pending_ = false;
1415 database_task_manager_->GetTaskRunner()->PostTask(
1416 FROM_HERE,
1417 base::Bind(base::IgnoreResult(
1418 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1419 base::Unretained(database_.get()),
1420 std::set<int64>(&id, &id + 1)));
1422 ContinuePurgingResources();
1425 void ServiceWorkerStorage::DeleteStaleResources() {
1426 DCHECK(!has_checked_for_stale_resources_);
1427 has_checked_for_stale_resources_ = true;
1428 database_task_manager_->GetTaskRunner()->PostTask(
1429 FROM_HERE,
1430 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1431 database_.get(),
1432 base::MessageLoopProxy::current(),
1433 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1434 weak_factory_.GetWeakPtr())));
1437 void ServiceWorkerStorage::DidCollectStaleResources(
1438 const std::vector<int64>& stale_resource_ids,
1439 ServiceWorkerDatabase::Status status) {
1440 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1441 if (status != ServiceWorkerDatabase::STATUS_OK)
1442 return;
1443 StartPurgingResources(stale_resource_ids);
1446 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1447 // Can be null in tests.
1448 if (!special_storage_policy_.get())
1449 return;
1451 if (!special_storage_policy_->HasSessionOnlyOrigins())
1452 return;
1454 std::set<GURL> session_only_origins;
1455 for (const GURL& origin : registered_origins_) {
1456 if (special_storage_policy_->IsStorageSessionOnly(origin))
1457 session_only_origins.insert(origin);
1460 database_task_manager_->GetShutdownBlockingTaskRunner()->PostTask(
1461 FROM_HERE,
1462 base::Bind(&DeleteAllDataForOriginsFromDB,
1463 database_.get(),
1464 session_only_origins));
1467 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1468 ServiceWorkerDatabase* database,
1469 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1470 const GetResourcesCallback& callback) {
1471 std::set<int64> ids;
1472 ServiceWorkerDatabase::Status status =
1473 database->GetUncommittedResourceIds(&ids);
1474 if (status != ServiceWorkerDatabase::STATUS_OK) {
1475 original_task_runner->PostTask(
1476 FROM_HERE,
1477 base::Bind(
1478 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1479 return;
1482 status = database->PurgeUncommittedResourceIds(ids);
1483 if (status != ServiceWorkerDatabase::STATUS_OK) {
1484 original_task_runner->PostTask(
1485 FROM_HERE,
1486 base::Bind(
1487 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1488 return;
1491 ids.clear();
1492 status = database->GetPurgeableResourceIds(&ids);
1493 original_task_runner->PostTask(
1494 FROM_HERE,
1495 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1498 void ServiceWorkerStorage::ReadInitialDataFromDB(
1499 ServiceWorkerDatabase* database,
1500 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1501 const InitializeCallback& callback) {
1502 DCHECK(database);
1503 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1504 new ServiceWorkerStorage::InitialData());
1506 ServiceWorkerDatabase::Status status =
1507 database->GetNextAvailableIds(&data->next_registration_id,
1508 &data->next_version_id,
1509 &data->next_resource_id);
1510 if (status != ServiceWorkerDatabase::STATUS_OK) {
1511 original_task_runner->PostTask(
1512 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1513 return;
1516 status = database->GetOriginsWithRegistrations(&data->origins);
1517 original_task_runner->PostTask(
1518 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1521 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1522 ServiceWorkerDatabase* database,
1523 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1524 int64 registration_id,
1525 const GURL& origin,
1526 const DeleteRegistrationCallback& callback) {
1527 DCHECK(database);
1529 ServiceWorkerDatabase::RegistrationData deleted_version;
1530 std::vector<int64> newly_purgeable_resources;
1531 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1532 registration_id, origin, &deleted_version, &newly_purgeable_resources);
1533 if (status != ServiceWorkerDatabase::STATUS_OK) {
1534 original_task_runner->PostTask(
1535 FROM_HERE,
1536 base::Bind(
1537 callback, false, deleted_version, std::vector<int64>(), status));
1538 return;
1541 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1542 // unique origin list.
1543 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1544 status = database->GetRegistrationsForOrigin(origin, &registrations);
1545 if (status != ServiceWorkerDatabase::STATUS_OK) {
1546 original_task_runner->PostTask(
1547 FROM_HERE,
1548 base::Bind(
1549 callback, false, deleted_version, std::vector<int64>(), status));
1550 return;
1553 bool deletable = registrations.empty();
1554 original_task_runner->PostTask(FROM_HERE,
1555 base::Bind(callback,
1556 deletable,
1557 deleted_version,
1558 newly_purgeable_resources,
1559 status));
1562 void ServiceWorkerStorage::WriteRegistrationInDB(
1563 ServiceWorkerDatabase* database,
1564 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1565 const ServiceWorkerDatabase::RegistrationData& data,
1566 const ResourceList& resources,
1567 const WriteRegistrationCallback& callback) {
1568 DCHECK(database);
1569 ServiceWorkerDatabase::RegistrationData deleted_version;
1570 std::vector<int64> newly_purgeable_resources;
1571 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1572 data, resources, &deleted_version, &newly_purgeable_resources);
1573 original_task_runner->PostTask(FROM_HERE,
1574 base::Bind(callback,
1575 data.script.GetOrigin(),
1576 deleted_version,
1577 newly_purgeable_resources,
1578 status));
1581 void ServiceWorkerStorage::FindForDocumentInDB(
1582 ServiceWorkerDatabase* database,
1583 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1584 const GURL& document_url,
1585 const FindInDBCallback& callback) {
1586 GURL origin = document_url.GetOrigin();
1587 RegistrationList registrations;
1588 ServiceWorkerDatabase::Status status =
1589 database->GetRegistrationsForOrigin(origin, &registrations);
1590 if (status != ServiceWorkerDatabase::STATUS_OK) {
1591 original_task_runner->PostTask(
1592 FROM_HERE,
1593 base::Bind(callback,
1594 ServiceWorkerDatabase::RegistrationData(),
1595 ResourceList(),
1596 status));
1597 return;
1600 ServiceWorkerDatabase::RegistrationData data;
1601 ResourceList resources;
1602 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1604 // Find one with a pattern match.
1605 LongestScopeMatcher matcher(document_url);
1606 int64 match = kInvalidServiceWorkerRegistrationId;
1607 for (size_t i = 0; i < registrations.size(); ++i) {
1608 if (matcher.MatchLongest(registrations[i].scope))
1609 match = registrations[i].registration_id;
1612 if (match != kInvalidServiceWorkerRegistrationId)
1613 status = database->ReadRegistration(match, origin, &data, &resources);
1615 original_task_runner->PostTask(
1616 FROM_HERE,
1617 base::Bind(callback, data, resources, status));
1620 void ServiceWorkerStorage::FindForPatternInDB(
1621 ServiceWorkerDatabase* database,
1622 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1623 const GURL& scope,
1624 const FindInDBCallback& callback) {
1625 GURL origin = scope.GetOrigin();
1626 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1627 ServiceWorkerDatabase::Status status =
1628 database->GetRegistrationsForOrigin(origin, &registrations);
1629 if (status != ServiceWorkerDatabase::STATUS_OK) {
1630 original_task_runner->PostTask(
1631 FROM_HERE,
1632 base::Bind(callback,
1633 ServiceWorkerDatabase::RegistrationData(),
1634 ResourceList(),
1635 status));
1636 return;
1639 // Find one with an exact matching scope.
1640 ServiceWorkerDatabase::RegistrationData data;
1641 ResourceList resources;
1642 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1643 for (RegistrationList::const_iterator it = registrations.begin();
1644 it != registrations.end(); ++it) {
1645 if (scope != it->scope)
1646 continue;
1647 status = database->ReadRegistration(it->registration_id, origin,
1648 &data, &resources);
1649 break; // We're done looping.
1652 original_task_runner->PostTask(
1653 FROM_HERE,
1654 base::Bind(callback, data, resources, status));
1657 void ServiceWorkerStorage::FindForIdInDB(
1658 ServiceWorkerDatabase* database,
1659 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1660 int64 registration_id,
1661 const GURL& origin,
1662 const FindInDBCallback& callback) {
1663 ServiceWorkerDatabase::RegistrationData data;
1664 ResourceList resources;
1665 ServiceWorkerDatabase::Status status =
1666 database->ReadRegistration(registration_id, origin, &data, &resources);
1667 original_task_runner->PostTask(
1668 FROM_HERE, base::Bind(callback, data, resources, status));
1671 void ServiceWorkerStorage::FindForIdOnlyInDB(
1672 ServiceWorkerDatabase* database,
1673 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1674 int64 registration_id,
1675 const FindInDBCallback& callback) {
1676 GURL origin;
1677 ServiceWorkerDatabase::Status status =
1678 database->ReadRegistrationOrigin(registration_id, &origin);
1679 if (status != ServiceWorkerDatabase::STATUS_OK) {
1680 original_task_runner->PostTask(
1681 FROM_HERE,
1682 base::Bind(callback, ServiceWorkerDatabase::RegistrationData(),
1683 ResourceList(), status));
1684 return;
1686 FindForIdInDB(database, original_task_runner, registration_id, origin,
1687 callback);
1690 void ServiceWorkerStorage::GetUserDataInDB(
1691 ServiceWorkerDatabase* database,
1692 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1693 int64 registration_id,
1694 const std::string& key,
1695 const GetUserDataInDBCallback& callback) {
1696 std::string data;
1697 ServiceWorkerDatabase::Status status =
1698 database->ReadUserData(registration_id, key, &data);
1699 original_task_runner->PostTask(
1700 FROM_HERE, base::Bind(callback, data, status));
1703 void ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB(
1704 ServiceWorkerDatabase* database,
1705 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1706 const std::string& key,
1707 const GetUserDataForAllRegistrationsInDBCallback& callback) {
1708 std::vector<std::pair<int64, std::string>> user_data;
1709 ServiceWorkerDatabase::Status status =
1710 database->ReadUserDataForAllRegistrations(key, &user_data);
1711 original_task_runner->PostTask(FROM_HERE,
1712 base::Bind(callback, user_data, status));
1715 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1716 ServiceWorkerDatabase* database,
1717 const std::set<GURL>& origins) {
1718 DCHECK(database);
1720 std::vector<int64> newly_purgeable_resources;
1721 database->DeleteAllDataForOrigins(origins, &newly_purgeable_resources);
1724 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1725 // is transient and it can get healed soon (e.g. IO error). To do that, the
1726 // database should not disable itself when an error occurs and the storage
1727 // controls it instead.
1728 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1729 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1730 // removed.
1731 if (state_ == DISABLED) {
1732 // Recovery process has already been scheduled.
1733 return;
1735 Disable();
1737 DVLOG(1) << "Schedule to delete the context and start over.";
1738 context_->ScheduleDeleteAndStartOver();
1741 void ServiceWorkerStorage::DidDeleteDatabase(
1742 const StatusCallback& callback,
1743 ServiceWorkerDatabase::Status status) {
1744 DCHECK_EQ(DISABLED, state_);
1745 if (status != ServiceWorkerDatabase::STATUS_OK) {
1746 // Give up the corruption recovery until the browser restarts.
1747 LOG(ERROR) << "Failed to delete the database: "
1748 << ServiceWorkerDatabase::StatusToString(status);
1749 callback.Run(DatabaseStatusToStatusCode(status));
1750 return;
1752 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1754 // Delete the disk cache on the cache thread.
1755 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1756 // Deleting the directory could take a long time and restart could be delayed.
1757 // We should probably rename the directory and delete it later.
1758 PostTaskAndReplyWithResult(
1759 database_task_manager_->GetTaskRunner(),
1760 FROM_HERE,
1761 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1762 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1763 weak_factory_.GetWeakPtr(),
1764 callback));
1767 void ServiceWorkerStorage::DidDeleteDiskCache(
1768 const StatusCallback& callback, bool result) {
1769 DCHECK_EQ(DISABLED, state_);
1770 if (!result) {
1771 // Give up the corruption recovery until the browser restarts.
1772 LOG(ERROR) << "Failed to delete the diskcache.";
1773 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1774 return;
1776 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1777 callback.Run(SERVICE_WORKER_OK);
1780 } // namespace content