Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blob065683385b29a0be52736e029b37d6f0eb7dcb0c
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/files/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/task_runner_util.h"
13 #include "base/trace_event/trace_event.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 scoped_ptr<ServiceWorkerResponseMetadataWriter>
647 ServiceWorkerStorage::CreateResponseMetadataWriter(int64 response_id) {
648 return make_scoped_ptr(
649 new ServiceWorkerResponseMetadataWriter(response_id, disk_cache()));
652 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
653 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
654 DCHECK_EQ(INITIALIZED, state_);
656 if (!has_checked_for_stale_resources_)
657 DeleteStaleResources();
659 database_task_manager_->GetTaskRunner()->PostTask(
660 FROM_HERE,
661 base::Bind(base::IgnoreResult(
662 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
663 base::Unretained(database_.get()),
664 std::set<int64>(&id, &id + 1)));
667 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
668 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
669 database_task_manager_->GetTaskRunner()->PostTask(
670 FROM_HERE,
671 base::Bind(base::IgnoreResult(
672 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
673 base::Unretained(database_.get()),
674 std::set<int64>(&id, &id + 1)));
675 StartPurgingResources(std::vector<int64>(1, id));
678 void ServiceWorkerStorage::CompareScriptResources(
679 int64 lhs_id, int64 rhs_id,
680 const CompareCallback& callback) {
681 DCHECK(!callback.is_null());
682 scoped_refptr<ResponseComparer> comparer =
683 new ResponseComparer(weak_factory_.GetWeakPtr(),
684 CreateResponseReader(lhs_id),
685 CreateResponseReader(rhs_id),
686 callback);
687 comparer->Start(); // It deletes itself when done.
690 void ServiceWorkerStorage::StoreUserData(
691 int64 registration_id,
692 const GURL& origin,
693 const std::string& key,
694 const std::string& data,
695 const StatusCallback& callback) {
696 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
697 if (IsDisabled() || !context_) {
698 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
699 return;
702 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
703 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
704 return;
707 PostTaskAndReplyWithResult(
708 database_task_manager_->GetTaskRunner(),
709 FROM_HERE,
710 base::Bind(&ServiceWorkerDatabase::WriteUserData,
711 base::Unretained(database_.get()),
712 registration_id, origin, key, data),
713 base::Bind(&ServiceWorkerStorage::DidStoreUserData,
714 weak_factory_.GetWeakPtr(),
715 callback));
718 void ServiceWorkerStorage::GetUserData(
719 int64 registration_id,
720 const std::string& key,
721 const GetUserDataCallback& callback) {
722 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
723 if (IsDisabled() || !context_) {
724 RunSoon(FROM_HERE,
725 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
726 return;
729 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
730 RunSoon(FROM_HERE,
731 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
732 return;
735 database_task_manager_->GetTaskRunner()->PostTask(
736 FROM_HERE,
737 base::Bind(&ServiceWorkerStorage::GetUserDataInDB,
738 database_.get(),
739 base::MessageLoopProxy::current(),
740 registration_id,
741 key,
742 base::Bind(&ServiceWorkerStorage::DidGetUserData,
743 weak_factory_.GetWeakPtr(), callback)));
746 void ServiceWorkerStorage::ClearUserData(
747 int64 registration_id,
748 const std::string& key,
749 const StatusCallback& callback) {
750 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
751 if (IsDisabled() || !context_) {
752 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
753 return;
756 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
757 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
758 return;
761 PostTaskAndReplyWithResult(
762 database_task_manager_->GetTaskRunner(),
763 FROM_HERE,
764 base::Bind(&ServiceWorkerDatabase::DeleteUserData,
765 base::Unretained(database_.get()),
766 registration_id, key),
767 base::Bind(&ServiceWorkerStorage::DidDeleteUserData,
768 weak_factory_.GetWeakPtr(),
769 callback));
772 void ServiceWorkerStorage::GetUserDataForAllRegistrations(
773 const std::string& key,
774 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
775 callback) {
776 if (!LazyInitialize(
777 base::Bind(&ServiceWorkerStorage::GetUserDataForAllRegistrations,
778 weak_factory_.GetWeakPtr(), key, callback))) {
779 if (state_ != INITIALIZING || !context_) {
780 RunSoon(FROM_HERE,
781 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
782 SERVICE_WORKER_ERROR_FAILED));
784 return;
786 DCHECK_EQ(INITIALIZED, state_);
788 if (key.empty()) {
789 RunSoon(FROM_HERE,
790 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
791 SERVICE_WORKER_ERROR_FAILED));
792 return;
795 database_task_manager_->GetTaskRunner()->PostTask(
796 FROM_HERE,
797 base::Bind(
798 &ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB,
799 database_.get(), base::MessageLoopProxy::current(), key,
800 base::Bind(&ServiceWorkerStorage::DidGetUserDataForAllRegistrations,
801 weak_factory_.GetWeakPtr(), callback)));
804 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
805 Disable();
807 // Delete the database on the database thread.
808 PostTaskAndReplyWithResult(
809 database_task_manager_->GetTaskRunner(),
810 FROM_HERE,
811 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
812 base::Unretained(database_.get())),
813 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
814 weak_factory_.GetWeakPtr(),
815 callback));
818 int64 ServiceWorkerStorage::NewRegistrationId() {
819 if (state_ == DISABLED)
820 return kInvalidServiceWorkerRegistrationId;
821 DCHECK_EQ(INITIALIZED, state_);
822 return next_registration_id_++;
825 int64 ServiceWorkerStorage::NewVersionId() {
826 if (state_ == DISABLED)
827 return kInvalidServiceWorkerVersionId;
828 DCHECK_EQ(INITIALIZED, state_);
829 return next_version_id_++;
832 int64 ServiceWorkerStorage::NewResourceId() {
833 if (state_ == DISABLED)
834 return kInvalidServiceWorkerResourceId;
835 DCHECK_EQ(INITIALIZED, state_);
836 return next_resource_id_++;
839 void ServiceWorkerStorage::NotifyInstallingRegistration(
840 ServiceWorkerRegistration* registration) {
841 DCHECK(installing_registrations_.find(registration->id()) ==
842 installing_registrations_.end());
843 installing_registrations_[registration->id()] = registration;
846 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
847 ServiceWorkerRegistration* registration,
848 ServiceWorkerVersion* version,
849 ServiceWorkerStatusCode status) {
850 installing_registrations_.erase(registration->id());
851 if (status != SERVICE_WORKER_OK && version) {
852 ResourceList resources;
853 version->script_cache_map()->GetResources(&resources);
855 std::set<int64> ids;
856 for (size_t i = 0; i < resources.size(); ++i)
857 ids.insert(resources[i].resource_id);
859 database_task_manager_->GetTaskRunner()->PostTask(
860 FROM_HERE,
861 base::Bind(base::IgnoreResult(
862 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
863 base::Unretained(database_.get()),
864 ids));
868 void ServiceWorkerStorage::NotifyUninstallingRegistration(
869 ServiceWorkerRegistration* registration) {
870 DCHECK(uninstalling_registrations_.find(registration->id()) ==
871 uninstalling_registrations_.end());
872 uninstalling_registrations_[registration->id()] = registration;
875 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
876 ServiceWorkerRegistration* registration) {
877 uninstalling_registrations_.erase(registration->id());
880 void ServiceWorkerStorage::Disable() {
881 state_ = DISABLED;
882 if (disk_cache_)
883 disk_cache_->Disable();
886 bool ServiceWorkerStorage::IsDisabled() const {
887 return state_ == DISABLED;
890 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
891 if (!has_checked_for_stale_resources_)
892 DeleteStaleResources();
893 StartPurgingResources(resources);
896 ServiceWorkerStorage::ServiceWorkerStorage(
897 const base::FilePath& path,
898 base::WeakPtr<ServiceWorkerContextCore> context,
899 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
900 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
901 storage::QuotaManagerProxy* quota_manager_proxy,
902 storage::SpecialStoragePolicy* special_storage_policy)
903 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
904 next_version_id_(kInvalidServiceWorkerVersionId),
905 next_resource_id_(kInvalidServiceWorkerResourceId),
906 state_(UNINITIALIZED),
907 path_(path),
908 context_(context),
909 database_task_manager_(database_task_manager.Pass()),
910 disk_cache_thread_(disk_cache_thread),
911 quota_manager_proxy_(quota_manager_proxy),
912 special_storage_policy_(special_storage_policy),
913 is_purge_pending_(false),
914 has_checked_for_stale_resources_(false),
915 weak_factory_(this) {
916 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
919 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
920 if (path_.empty())
921 return base::FilePath();
922 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
923 .Append(kDatabaseName);
926 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
927 if (path_.empty())
928 return base::FilePath();
929 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
930 .Append(kDiskCacheName);
933 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
934 if (!context_)
935 return false;
937 switch (state_) {
938 case INITIALIZED:
939 return true;
940 case DISABLED:
941 return false;
942 case INITIALIZING:
943 pending_tasks_.push_back(callback);
944 return false;
945 case UNINITIALIZED:
946 pending_tasks_.push_back(callback);
947 // Fall-through.
950 state_ = INITIALIZING;
951 database_task_manager_->GetTaskRunner()->PostTask(
952 FROM_HERE,
953 base::Bind(&ReadInitialDataFromDB,
954 database_.get(),
955 base::MessageLoopProxy::current(),
956 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
957 weak_factory_.GetWeakPtr())));
958 return false;
961 void ServiceWorkerStorage::DidReadInitialData(
962 InitialData* data,
963 ServiceWorkerDatabase::Status status) {
964 DCHECK(data);
965 DCHECK_EQ(INITIALIZING, state_);
967 if (status == ServiceWorkerDatabase::STATUS_OK) {
968 next_registration_id_ = data->next_registration_id;
969 next_version_id_ = data->next_version_id;
970 next_resource_id_ = data->next_resource_id;
971 registered_origins_.swap(data->origins);
972 state_ = INITIALIZED;
973 } else {
974 DVLOG(2) << "Failed to initialize: "
975 << ServiceWorkerDatabase::StatusToString(status);
976 ScheduleDeleteAndStartOver();
979 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
980 it != pending_tasks_.end(); ++it) {
981 RunSoon(FROM_HERE, *it);
983 pending_tasks_.clear();
986 void ServiceWorkerStorage::DidFindRegistrationForDocument(
987 const GURL& document_url,
988 const FindRegistrationCallback& callback,
989 int64 callback_id,
990 const ServiceWorkerDatabase::RegistrationData& data,
991 const ResourceList& resources,
992 ServiceWorkerDatabase::Status status) {
993 if (status == ServiceWorkerDatabase::STATUS_OK) {
994 ReturnFoundRegistration(callback, data, resources);
995 TRACE_EVENT_ASYNC_END1(
996 "ServiceWorker",
997 "ServiceWorkerStorage::FindRegistrationForDocument",
998 callback_id,
999 "Status", ServiceWorkerDatabase::StatusToString(status));
1000 return;
1003 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1004 // Look for something currently being installed.
1005 scoped_refptr<ServiceWorkerRegistration> installing_registration =
1006 FindInstallingRegistrationForDocument(document_url);
1007 ServiceWorkerStatusCode installing_status = installing_registration.get() ?
1008 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
1009 callback.Run(installing_status, installing_registration);
1010 TRACE_EVENT_ASYNC_END2(
1011 "ServiceWorker",
1012 "ServiceWorkerStorage::FindRegistrationForDocument",
1013 callback_id,
1014 "Status", ServiceWorkerDatabase::StatusToString(status),
1015 "Info",
1016 (installing_status == SERVICE_WORKER_OK) ?
1017 "Installing registration is found" :
1018 "Any registrations are not found");
1019 return;
1022 ScheduleDeleteAndStartOver();
1023 callback.Run(DatabaseStatusToStatusCode(status),
1024 scoped_refptr<ServiceWorkerRegistration>());
1025 TRACE_EVENT_ASYNC_END1(
1026 "ServiceWorker",
1027 "ServiceWorkerStorage::FindRegistrationForDocument",
1028 callback_id,
1029 "Status", ServiceWorkerDatabase::StatusToString(status));
1032 void ServiceWorkerStorage::DidFindRegistrationForPattern(
1033 const GURL& scope,
1034 const FindRegistrationCallback& callback,
1035 const ServiceWorkerDatabase::RegistrationData& data,
1036 const ResourceList& resources,
1037 ServiceWorkerDatabase::Status status) {
1038 if (status == ServiceWorkerDatabase::STATUS_OK) {
1039 ReturnFoundRegistration(callback, data, resources);
1040 return;
1043 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1044 scoped_refptr<ServiceWorkerRegistration> installing_registration =
1045 FindInstallingRegistrationForPattern(scope);
1046 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
1047 : SERVICE_WORKER_ERROR_NOT_FOUND,
1048 installing_registration);
1049 return;
1052 ScheduleDeleteAndStartOver();
1053 callback.Run(DatabaseStatusToStatusCode(status),
1054 scoped_refptr<ServiceWorkerRegistration>());
1057 void ServiceWorkerStorage::DidFindRegistrationForId(
1058 const FindRegistrationCallback& callback,
1059 const ServiceWorkerDatabase::RegistrationData& data,
1060 const ResourceList& resources,
1061 ServiceWorkerDatabase::Status status) {
1062 if (status == ServiceWorkerDatabase::STATUS_OK) {
1063 ReturnFoundRegistration(callback, data, resources);
1064 return;
1067 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1068 // TODO(nhiroki): Find a registration in |installing_registrations_|.
1069 callback.Run(DatabaseStatusToStatusCode(status),
1070 scoped_refptr<ServiceWorkerRegistration>());
1071 return;
1074 ScheduleDeleteAndStartOver();
1075 callback.Run(DatabaseStatusToStatusCode(status),
1076 scoped_refptr<ServiceWorkerRegistration>());
1079 void ServiceWorkerStorage::ReturnFoundRegistration(
1080 const FindRegistrationCallback& callback,
1081 const ServiceWorkerDatabase::RegistrationData& data,
1082 const ResourceList& resources) {
1083 scoped_refptr<ServiceWorkerRegistration> registration =
1084 GetOrCreateRegistration(data, resources);
1085 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
1088 void ServiceWorkerStorage::DidGetRegistrations(
1089 const GetRegistrationsInfosCallback& callback,
1090 RegistrationList* registrations,
1091 const GURL& origin_filter,
1092 ServiceWorkerDatabase::Status status) {
1093 DCHECK(registrations);
1094 if (status != ServiceWorkerDatabase::STATUS_OK &&
1095 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1096 ScheduleDeleteAndStartOver();
1097 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
1098 return;
1101 // Add all stored registrations.
1102 std::set<int64> pushed_registrations;
1103 std::vector<ServiceWorkerRegistrationInfo> infos;
1104 for (const auto& registration_data : *registrations) {
1105 const bool inserted =
1106 pushed_registrations.insert(registration_data.registration_id).second;
1107 DCHECK(inserted);
1109 ServiceWorkerRegistration* registration =
1110 context_->GetLiveRegistration(registration_data.registration_id);
1111 if (registration) {
1112 infos.push_back(registration->GetInfo());
1113 continue;
1116 ServiceWorkerRegistrationInfo info;
1117 info.pattern = registration_data.scope;
1118 info.registration_id = registration_data.registration_id;
1119 info.stored_version_size_bytes =
1120 registration_data.resources_total_size_bytes;
1121 if (ServiceWorkerVersion* version =
1122 context_->GetLiveVersion(registration_data.version_id)) {
1123 if (registration_data.is_active)
1124 info.active_version = version->GetInfo();
1125 else
1126 info.waiting_version = version->GetInfo();
1127 infos.push_back(info);
1128 continue;
1131 if (registration_data.is_active) {
1132 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
1133 info.active_version.script_url = registration_data.script;
1134 info.active_version.version_id = registration_data.version_id;
1135 info.active_version.registration_id = registration_data.registration_id;
1136 } else {
1137 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
1138 info.waiting_version.script_url = registration_data.script;
1139 info.waiting_version.version_id = registration_data.version_id;
1140 info.waiting_version.registration_id = registration_data.registration_id;
1142 infos.push_back(info);
1145 // Add unstored registrations that are being installed.
1146 for (RegistrationRefsById::const_iterator it =
1147 installing_registrations_.begin();
1148 it != installing_registrations_.end(); ++it) {
1149 if ((!origin_filter.is_valid() ||
1150 it->second->pattern().GetOrigin() == origin_filter) &&
1151 pushed_registrations.insert(it->first).second) {
1152 infos.push_back(it->second->GetInfo());
1156 callback.Run(infos);
1159 void ServiceWorkerStorage::DidStoreRegistration(
1160 const StatusCallback& callback,
1161 const ServiceWorkerDatabase::RegistrationData& new_version,
1162 const GURL& origin,
1163 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1164 const std::vector<int64>& newly_purgeable_resources,
1165 ServiceWorkerDatabase::Status status) {
1166 if (status != ServiceWorkerDatabase::STATUS_OK) {
1167 ScheduleDeleteAndStartOver();
1168 callback.Run(DatabaseStatusToStatusCode(status));
1169 return;
1171 registered_origins_.insert(origin);
1173 scoped_refptr<ServiceWorkerRegistration> registration =
1174 context_->GetLiveRegistration(new_version.registration_id);
1175 registration->set_resources_total_size_bytes(
1176 new_version.resources_total_size_bytes);
1177 if (quota_manager_proxy_.get()) {
1178 // Can be nullptr in tests.
1179 quota_manager_proxy_->NotifyStorageModified(
1180 storage::QuotaClient::kServiceWorker,
1181 origin,
1182 storage::StorageType::kStorageTypeTemporary,
1183 new_version.resources_total_size_bytes -
1184 deleted_version.resources_total_size_bytes);
1187 callback.Run(SERVICE_WORKER_OK);
1189 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1190 StartPurgingResources(newly_purgeable_resources);
1193 void ServiceWorkerStorage::DidUpdateToActiveState(
1194 const StatusCallback& callback,
1195 ServiceWorkerDatabase::Status status) {
1196 if (status != ServiceWorkerDatabase::STATUS_OK &&
1197 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1198 ScheduleDeleteAndStartOver();
1200 callback.Run(DatabaseStatusToStatusCode(status));
1203 void ServiceWorkerStorage::DidDeleteRegistration(
1204 const DidDeleteRegistrationParams& params,
1205 bool origin_is_deletable,
1206 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1207 const std::vector<int64>& newly_purgeable_resources,
1208 ServiceWorkerDatabase::Status status) {
1209 pending_deletions_.erase(params.registration_id);
1210 if (status != ServiceWorkerDatabase::STATUS_OK) {
1211 ScheduleDeleteAndStartOver();
1212 params.callback.Run(DatabaseStatusToStatusCode(status));
1213 return;
1215 if (quota_manager_proxy_.get()) {
1216 // Can be nullptr in tests.
1217 quota_manager_proxy_->NotifyStorageModified(
1218 storage::QuotaClient::kServiceWorker,
1219 params.origin,
1220 storage::StorageType::kStorageTypeTemporary,
1221 -deleted_version.resources_total_size_bytes);
1223 if (origin_is_deletable)
1224 registered_origins_.erase(params.origin);
1225 params.callback.Run(SERVICE_WORKER_OK);
1227 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1228 StartPurgingResources(newly_purgeable_resources);
1231 void ServiceWorkerStorage::DidStoreUserData(
1232 const StatusCallback& callback,
1233 ServiceWorkerDatabase::Status status) {
1234 // |status| can be NOT_FOUND when the associated registration did not exist in
1235 // the database. In the case, we don't have to schedule the corruption
1236 // recovery.
1237 if (status != ServiceWorkerDatabase::STATUS_OK &&
1238 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1239 ScheduleDeleteAndStartOver();
1241 callback.Run(DatabaseStatusToStatusCode(status));
1244 void ServiceWorkerStorage::DidGetUserData(
1245 const GetUserDataCallback& callback,
1246 const std::string& data,
1247 ServiceWorkerDatabase::Status status) {
1248 if (status != ServiceWorkerDatabase::STATUS_OK &&
1249 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1250 ScheduleDeleteAndStartOver();
1252 callback.Run(data, DatabaseStatusToStatusCode(status));
1255 void ServiceWorkerStorage::DidDeleteUserData(
1256 const StatusCallback& callback,
1257 ServiceWorkerDatabase::Status status) {
1258 if (status != ServiceWorkerDatabase::STATUS_OK)
1259 ScheduleDeleteAndStartOver();
1260 callback.Run(DatabaseStatusToStatusCode(status));
1263 void ServiceWorkerStorage::DidGetUserDataForAllRegistrations(
1264 const GetUserDataForAllRegistrationsCallback& callback,
1265 const std::vector<std::pair<int64, std::string>>& user_data,
1266 ServiceWorkerDatabase::Status status) {
1267 if (status != ServiceWorkerDatabase::STATUS_OK)
1268 ScheduleDeleteAndStartOver();
1269 callback.Run(user_data, DatabaseStatusToStatusCode(status));
1272 scoped_refptr<ServiceWorkerRegistration>
1273 ServiceWorkerStorage::GetOrCreateRegistration(
1274 const ServiceWorkerDatabase::RegistrationData& data,
1275 const ResourceList& resources) {
1276 scoped_refptr<ServiceWorkerRegistration> registration =
1277 context_->GetLiveRegistration(data.registration_id);
1278 if (registration.get())
1279 return registration;
1281 registration = new ServiceWorkerRegistration(
1282 data.scope, data.registration_id, context_);
1283 registration->set_resources_total_size_bytes(data.resources_total_size_bytes);
1284 registration->set_last_update_check(data.last_update_check);
1285 if (pending_deletions_.find(data.registration_id) !=
1286 pending_deletions_.end()) {
1287 registration->set_is_deleted(true);
1289 scoped_refptr<ServiceWorkerVersion> version =
1290 context_->GetLiveVersion(data.version_id);
1291 if (!version.get()) {
1292 version = new ServiceWorkerVersion(
1293 registration.get(), data.script, data.version_id, context_);
1294 version->SetStatus(data.is_active ?
1295 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1296 version->script_cache_map()->SetResources(resources);
1299 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1300 registration->SetActiveVersion(version.get());
1301 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1302 registration->SetWaitingVersion(version.get());
1303 else
1304 NOTREACHED();
1306 return registration;
1309 ServiceWorkerRegistration*
1310 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1311 const GURL& document_url) {
1312 DCHECK(!document_url.has_ref());
1314 LongestScopeMatcher matcher(document_url);
1315 ServiceWorkerRegistration* match = NULL;
1317 // TODO(nhiroki): This searches over installing registrations linearly and it
1318 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1319 for (RegistrationRefsById::const_iterator it =
1320 installing_registrations_.begin();
1321 it != installing_registrations_.end(); ++it) {
1322 if (matcher.MatchLongest(it->second->pattern()))
1323 match = it->second.get();
1325 return match;
1328 ServiceWorkerRegistration*
1329 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1330 const GURL& scope) {
1331 for (RegistrationRefsById::const_iterator it =
1332 installing_registrations_.begin();
1333 it != installing_registrations_.end(); ++it) {
1334 if (it->second->pattern() == scope)
1335 return it->second.get();
1337 return NULL;
1340 ServiceWorkerRegistration*
1341 ServiceWorkerStorage::FindInstallingRegistrationForId(
1342 int64 registration_id) {
1343 RegistrationRefsById::const_iterator found =
1344 installing_registrations_.find(registration_id);
1345 if (found == installing_registrations_.end())
1346 return NULL;
1347 return found->second.get();
1350 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1351 if (disk_cache_)
1352 return disk_cache_.get();
1354 disk_cache_.reset(new ServiceWorkerDiskCache);
1356 base::FilePath path = GetDiskCachePath();
1357 if (path.empty()) {
1358 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1359 net::CompletionCallback());
1360 DCHECK_EQ(net::OK, rv);
1361 return disk_cache_.get();
1364 int rv = disk_cache_->InitWithDiskBackend(
1365 path,
1366 kMaxDiskCacheSize,
1367 false,
1368 disk_cache_thread_,
1369 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1370 weak_factory_.GetWeakPtr()));
1371 if (rv != net::ERR_IO_PENDING)
1372 OnDiskCacheInitialized(rv);
1374 return disk_cache_.get();
1377 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1378 if (rv != net::OK) {
1379 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1380 << net::ErrorToString(rv);
1381 ScheduleDeleteAndStartOver();
1383 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1386 void ServiceWorkerStorage::StartPurgingResources(
1387 const std::vector<int64>& ids) {
1388 DCHECK(has_checked_for_stale_resources_);
1389 for (size_t i = 0; i < ids.size(); ++i)
1390 purgeable_resource_ids_.push_back(ids[i]);
1391 ContinuePurgingResources();
1394 void ServiceWorkerStorage::StartPurgingResources(
1395 const ResourceList& resources) {
1396 DCHECK(has_checked_for_stale_resources_);
1397 for (size_t i = 0; i < resources.size(); ++i)
1398 purgeable_resource_ids_.push_back(resources[i].resource_id);
1399 ContinuePurgingResources();
1402 void ServiceWorkerStorage::ContinuePurgingResources() {
1403 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1404 return;
1406 // Do one at a time until we're done, use RunSoon to avoid recursion when
1407 // DoomEntry returns immediately.
1408 is_purge_pending_ = true;
1409 int64 id = purgeable_resource_ids_.front();
1410 purgeable_resource_ids_.pop_front();
1411 RunSoon(FROM_HERE,
1412 base::Bind(&ServiceWorkerStorage::PurgeResource,
1413 weak_factory_.GetWeakPtr(), id));
1416 void ServiceWorkerStorage::PurgeResource(int64 id) {
1417 DCHECK(is_purge_pending_);
1418 int rv = disk_cache()->DoomEntry(
1419 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1420 weak_factory_.GetWeakPtr(), id));
1421 if (rv != net::ERR_IO_PENDING)
1422 OnResourcePurged(id, rv);
1425 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1426 DCHECK(is_purge_pending_);
1427 is_purge_pending_ = false;
1429 database_task_manager_->GetTaskRunner()->PostTask(
1430 FROM_HERE,
1431 base::Bind(base::IgnoreResult(
1432 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1433 base::Unretained(database_.get()),
1434 std::set<int64>(&id, &id + 1)));
1436 ContinuePurgingResources();
1439 void ServiceWorkerStorage::DeleteStaleResources() {
1440 DCHECK(!has_checked_for_stale_resources_);
1441 has_checked_for_stale_resources_ = true;
1442 database_task_manager_->GetTaskRunner()->PostTask(
1443 FROM_HERE,
1444 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1445 database_.get(),
1446 base::MessageLoopProxy::current(),
1447 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1448 weak_factory_.GetWeakPtr())));
1451 void ServiceWorkerStorage::DidCollectStaleResources(
1452 const std::vector<int64>& stale_resource_ids,
1453 ServiceWorkerDatabase::Status status) {
1454 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1455 if (status != ServiceWorkerDatabase::STATUS_OK)
1456 return;
1457 StartPurgingResources(stale_resource_ids);
1460 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1461 // Can be null in tests.
1462 if (!special_storage_policy_.get())
1463 return;
1465 if (!special_storage_policy_->HasSessionOnlyOrigins())
1466 return;
1468 std::set<GURL> session_only_origins;
1469 for (const GURL& origin : registered_origins_) {
1470 if (special_storage_policy_->IsStorageSessionOnly(origin))
1471 session_only_origins.insert(origin);
1474 database_task_manager_->GetShutdownBlockingTaskRunner()->PostTask(
1475 FROM_HERE,
1476 base::Bind(&DeleteAllDataForOriginsFromDB,
1477 database_.get(),
1478 session_only_origins));
1481 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1482 ServiceWorkerDatabase* database,
1483 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1484 const GetResourcesCallback& callback) {
1485 std::set<int64> ids;
1486 ServiceWorkerDatabase::Status status =
1487 database->GetUncommittedResourceIds(&ids);
1488 if (status != ServiceWorkerDatabase::STATUS_OK) {
1489 original_task_runner->PostTask(
1490 FROM_HERE,
1491 base::Bind(
1492 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1493 return;
1496 status = database->PurgeUncommittedResourceIds(ids);
1497 if (status != ServiceWorkerDatabase::STATUS_OK) {
1498 original_task_runner->PostTask(
1499 FROM_HERE,
1500 base::Bind(
1501 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1502 return;
1505 ids.clear();
1506 status = database->GetPurgeableResourceIds(&ids);
1507 original_task_runner->PostTask(
1508 FROM_HERE,
1509 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1512 void ServiceWorkerStorage::ReadInitialDataFromDB(
1513 ServiceWorkerDatabase* database,
1514 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1515 const InitializeCallback& callback) {
1516 DCHECK(database);
1517 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1518 new ServiceWorkerStorage::InitialData());
1520 ServiceWorkerDatabase::Status status =
1521 database->GetNextAvailableIds(&data->next_registration_id,
1522 &data->next_version_id,
1523 &data->next_resource_id);
1524 if (status != ServiceWorkerDatabase::STATUS_OK) {
1525 original_task_runner->PostTask(
1526 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1527 return;
1530 status = database->GetOriginsWithRegistrations(&data->origins);
1531 original_task_runner->PostTask(
1532 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1535 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1536 ServiceWorkerDatabase* database,
1537 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1538 int64 registration_id,
1539 const GURL& origin,
1540 const DeleteRegistrationCallback& callback) {
1541 DCHECK(database);
1543 ServiceWorkerDatabase::RegistrationData deleted_version;
1544 std::vector<int64> newly_purgeable_resources;
1545 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1546 registration_id, origin, &deleted_version, &newly_purgeable_resources);
1547 if (status != ServiceWorkerDatabase::STATUS_OK) {
1548 original_task_runner->PostTask(
1549 FROM_HERE,
1550 base::Bind(
1551 callback, false, deleted_version, std::vector<int64>(), status));
1552 return;
1555 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1556 // unique origin list.
1557 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1558 status = database->GetRegistrationsForOrigin(origin, &registrations);
1559 if (status != ServiceWorkerDatabase::STATUS_OK) {
1560 original_task_runner->PostTask(
1561 FROM_HERE,
1562 base::Bind(
1563 callback, false, deleted_version, std::vector<int64>(), status));
1564 return;
1567 bool deletable = registrations.empty();
1568 original_task_runner->PostTask(FROM_HERE,
1569 base::Bind(callback,
1570 deletable,
1571 deleted_version,
1572 newly_purgeable_resources,
1573 status));
1576 void ServiceWorkerStorage::WriteRegistrationInDB(
1577 ServiceWorkerDatabase* database,
1578 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1579 const ServiceWorkerDatabase::RegistrationData& data,
1580 const ResourceList& resources,
1581 const WriteRegistrationCallback& callback) {
1582 DCHECK(database);
1583 ServiceWorkerDatabase::RegistrationData deleted_version;
1584 std::vector<int64> newly_purgeable_resources;
1585 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1586 data, resources, &deleted_version, &newly_purgeable_resources);
1587 original_task_runner->PostTask(FROM_HERE,
1588 base::Bind(callback,
1589 data.script.GetOrigin(),
1590 deleted_version,
1591 newly_purgeable_resources,
1592 status));
1595 void ServiceWorkerStorage::FindForDocumentInDB(
1596 ServiceWorkerDatabase* database,
1597 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1598 const GURL& document_url,
1599 const FindInDBCallback& callback) {
1600 GURL origin = document_url.GetOrigin();
1601 RegistrationList registrations;
1602 ServiceWorkerDatabase::Status status =
1603 database->GetRegistrationsForOrigin(origin, &registrations);
1604 if (status != ServiceWorkerDatabase::STATUS_OK) {
1605 original_task_runner->PostTask(
1606 FROM_HERE,
1607 base::Bind(callback,
1608 ServiceWorkerDatabase::RegistrationData(),
1609 ResourceList(),
1610 status));
1611 return;
1614 ServiceWorkerDatabase::RegistrationData data;
1615 ResourceList resources;
1616 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1618 // Find one with a pattern match.
1619 LongestScopeMatcher matcher(document_url);
1620 int64 match = kInvalidServiceWorkerRegistrationId;
1621 for (size_t i = 0; i < registrations.size(); ++i) {
1622 if (matcher.MatchLongest(registrations[i].scope))
1623 match = registrations[i].registration_id;
1626 if (match != kInvalidServiceWorkerRegistrationId)
1627 status = database->ReadRegistration(match, origin, &data, &resources);
1629 original_task_runner->PostTask(
1630 FROM_HERE,
1631 base::Bind(callback, data, resources, status));
1634 void ServiceWorkerStorage::FindForPatternInDB(
1635 ServiceWorkerDatabase* database,
1636 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1637 const GURL& scope,
1638 const FindInDBCallback& callback) {
1639 GURL origin = scope.GetOrigin();
1640 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1641 ServiceWorkerDatabase::Status status =
1642 database->GetRegistrationsForOrigin(origin, &registrations);
1643 if (status != ServiceWorkerDatabase::STATUS_OK) {
1644 original_task_runner->PostTask(
1645 FROM_HERE,
1646 base::Bind(callback,
1647 ServiceWorkerDatabase::RegistrationData(),
1648 ResourceList(),
1649 status));
1650 return;
1653 // Find one with an exact matching scope.
1654 ServiceWorkerDatabase::RegistrationData data;
1655 ResourceList resources;
1656 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1657 for (RegistrationList::const_iterator it = registrations.begin();
1658 it != registrations.end(); ++it) {
1659 if (scope != it->scope)
1660 continue;
1661 status = database->ReadRegistration(it->registration_id, origin,
1662 &data, &resources);
1663 break; // We're done looping.
1666 original_task_runner->PostTask(
1667 FROM_HERE,
1668 base::Bind(callback, data, resources, status));
1671 void ServiceWorkerStorage::FindForIdInDB(
1672 ServiceWorkerDatabase* database,
1673 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1674 int64 registration_id,
1675 const GURL& origin,
1676 const FindInDBCallback& callback) {
1677 ServiceWorkerDatabase::RegistrationData data;
1678 ResourceList resources;
1679 ServiceWorkerDatabase::Status status =
1680 database->ReadRegistration(registration_id, origin, &data, &resources);
1681 original_task_runner->PostTask(
1682 FROM_HERE, base::Bind(callback, data, resources, status));
1685 void ServiceWorkerStorage::FindForIdOnlyInDB(
1686 ServiceWorkerDatabase* database,
1687 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1688 int64 registration_id,
1689 const FindInDBCallback& callback) {
1690 GURL origin;
1691 ServiceWorkerDatabase::Status status =
1692 database->ReadRegistrationOrigin(registration_id, &origin);
1693 if (status != ServiceWorkerDatabase::STATUS_OK) {
1694 original_task_runner->PostTask(
1695 FROM_HERE,
1696 base::Bind(callback, ServiceWorkerDatabase::RegistrationData(),
1697 ResourceList(), status));
1698 return;
1700 FindForIdInDB(database, original_task_runner, registration_id, origin,
1701 callback);
1704 void ServiceWorkerStorage::GetUserDataInDB(
1705 ServiceWorkerDatabase* database,
1706 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1707 int64 registration_id,
1708 const std::string& key,
1709 const GetUserDataInDBCallback& callback) {
1710 std::string data;
1711 ServiceWorkerDatabase::Status status =
1712 database->ReadUserData(registration_id, key, &data);
1713 original_task_runner->PostTask(
1714 FROM_HERE, base::Bind(callback, data, status));
1717 void ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB(
1718 ServiceWorkerDatabase* database,
1719 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1720 const std::string& key,
1721 const GetUserDataForAllRegistrationsInDBCallback& callback) {
1722 std::vector<std::pair<int64, std::string>> user_data;
1723 ServiceWorkerDatabase::Status status =
1724 database->ReadUserDataForAllRegistrations(key, &user_data);
1725 original_task_runner->PostTask(FROM_HERE,
1726 base::Bind(callback, user_data, status));
1729 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1730 ServiceWorkerDatabase* database,
1731 const std::set<GURL>& origins) {
1732 DCHECK(database);
1734 std::vector<int64> newly_purgeable_resources;
1735 database->DeleteAllDataForOrigins(origins, &newly_purgeable_resources);
1738 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1739 // is transient and it can get healed soon (e.g. IO error). To do that, the
1740 // database should not disable itself when an error occurs and the storage
1741 // controls it instead.
1742 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1743 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1744 // removed.
1745 if (state_ == DISABLED) {
1746 // Recovery process has already been scheduled.
1747 return;
1749 Disable();
1751 DVLOG(1) << "Schedule to delete the context and start over.";
1752 context_->ScheduleDeleteAndStartOver();
1755 void ServiceWorkerStorage::DidDeleteDatabase(
1756 const StatusCallback& callback,
1757 ServiceWorkerDatabase::Status status) {
1758 DCHECK_EQ(DISABLED, state_);
1759 if (status != ServiceWorkerDatabase::STATUS_OK) {
1760 // Give up the corruption recovery until the browser restarts.
1761 LOG(ERROR) << "Failed to delete the database: "
1762 << ServiceWorkerDatabase::StatusToString(status);
1763 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1764 ServiceWorkerMetrics::DELETE_DATABASE_ERROR);
1765 callback.Run(DatabaseStatusToStatusCode(status));
1766 return;
1768 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1770 // Delete the disk cache on the cache thread.
1771 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1772 // Deleting the directory could take a long time and restart could be delayed.
1773 // We should probably rename the directory and delete it later.
1774 PostTaskAndReplyWithResult(
1775 database_task_manager_->GetTaskRunner(),
1776 FROM_HERE,
1777 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1778 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1779 weak_factory_.GetWeakPtr(),
1780 callback));
1783 void ServiceWorkerStorage::DidDeleteDiskCache(
1784 const StatusCallback& callback, bool result) {
1785 DCHECK_EQ(DISABLED, state_);
1786 if (!result) {
1787 // Give up the corruption recovery until the browser restarts.
1788 LOG(ERROR) << "Failed to delete the diskcache.";
1789 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1790 ServiceWorkerMetrics::DELETE_DISK_CACHE_ERROR);
1791 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1792 return;
1794 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1795 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1796 ServiceWorkerMetrics::DELETE_OK);
1797 callback.Run(SERVICE_WORKER_OK);
1800 } // namespace content