Roll ANGLE e754fb8..6ffeb74
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blobb0c59e037b0dc89c26df81b5d30e45f53644db80
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/thread_task_runner_handle.h"
14 #include "base/trace_event/trace_event.h"
15 #include "content/browser/service_worker/service_worker_context_core.h"
16 #include "content/browser/service_worker/service_worker_disk_cache.h"
17 #include "content/browser/service_worker/service_worker_info.h"
18 #include "content/browser/service_worker/service_worker_metrics.h"
19 #include "content/browser/service_worker/service_worker_registration.h"
20 #include "content/browser/service_worker/service_worker_utils.h"
21 #include "content/browser/service_worker/service_worker_version.h"
22 #include "content/common/service_worker/service_worker_types.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/base/completion_callback.h"
25 #include "net/base/io_buffer.h"
26 #include "net/base/net_errors.h"
27 #include "storage/browser/quota/quota_manager_proxy.h"
28 #include "storage/browser/quota/special_storage_policy.h"
30 namespace content {
32 namespace {
34 void RunSoon(const tracked_objects::Location& from_here,
35 const base::Closure& closure) {
36 base::ThreadTaskRunnerHandle::Get()->PostTask(from_here, closure);
39 void CompleteFindNow(
40 const scoped_refptr<ServiceWorkerRegistration>& registration,
41 ServiceWorkerStatusCode status,
42 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
43 if (registration && registration->is_deleted()) {
44 // It's past the point of no return and no longer findable.
45 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, nullptr);
46 return;
48 callback.Run(status, registration);
51 void CompleteFindSoon(
52 const tracked_objects::Location& from_here,
53 const scoped_refptr<ServiceWorkerRegistration>& registration,
54 ServiceWorkerStatusCode status,
55 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
56 RunSoon(from_here,
57 base::Bind(&CompleteFindNow, registration, status, callback));
60 const base::FilePath::CharType kDatabaseName[] =
61 FILE_PATH_LITERAL("Database");
62 const base::FilePath::CharType kDiskCacheName[] =
63 FILE_PATH_LITERAL("Cache");
65 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
66 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
68 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
69 ServiceWorkerDatabase::Status status) {
70 switch (status) {
71 case ServiceWorkerDatabase::STATUS_OK:
72 return SERVICE_WORKER_OK;
73 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
74 return SERVICE_WORKER_ERROR_NOT_FOUND;
75 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
76 NOTREACHED();
77 default:
78 return SERVICE_WORKER_ERROR_FAILED;
82 class ResponseComparer : public base::RefCounted<ResponseComparer> {
83 public:
84 ResponseComparer(
85 base::WeakPtr<ServiceWorkerStorage> owner,
86 scoped_ptr<ServiceWorkerResponseReader> lhs,
87 scoped_ptr<ServiceWorkerResponseReader> rhs,
88 const ServiceWorkerStorage::CompareCallback& callback)
89 : owner_(owner),
90 completion_callback_(callback),
91 lhs_reader_(lhs.release()),
92 rhs_reader_(rhs.release()),
93 completion_count_(0),
94 previous_result_(0) {
97 void Start();
99 private:
100 friend class base::RefCounted<ResponseComparer>;
102 static const int kBufferSize = 16 * 1024;
104 ~ResponseComparer() {}
105 void ReadInfos();
106 void OnReadInfoComplete(int result);
107 void ReadSomeData();
108 void OnReadDataComplete(int result);
110 base::WeakPtr<ServiceWorkerStorage> owner_;
111 ServiceWorkerStorage::CompareCallback completion_callback_;
112 scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
113 scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
114 scoped_refptr<net::IOBuffer> lhs_buffer_;
115 scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
116 scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
117 scoped_refptr<net::IOBuffer> rhs_buffer_;
118 int completion_count_;
119 int previous_result_;
120 DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
123 void ResponseComparer::Start() {
124 lhs_buffer_ = new net::IOBuffer(kBufferSize);
125 lhs_info_ = new HttpResponseInfoIOBuffer();
126 rhs_buffer_ = new net::IOBuffer(kBufferSize);
127 rhs_info_ = new HttpResponseInfoIOBuffer();
129 ReadInfos();
132 void ResponseComparer::ReadInfos() {
133 lhs_reader_->ReadInfo(
134 lhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
135 rhs_reader_->ReadInfo(
136 rhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
139 void ResponseComparer::OnReadInfoComplete(int result) {
140 if (completion_callback_.is_null() || !owner_)
141 return;
142 if (result < 0) {
143 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
144 completion_callback_.Reset();
145 return;
147 if (++completion_count_ != 2)
148 return;
150 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
151 completion_callback_.Run(SERVICE_WORKER_OK, false);
152 return;
154 ReadSomeData();
157 void ResponseComparer::ReadSomeData() {
158 completion_count_ = 0;
159 lhs_reader_->ReadData(
160 lhs_buffer_.get(),
161 kBufferSize,
162 base::Bind(&ResponseComparer::OnReadDataComplete, this));
163 rhs_reader_->ReadData(
164 rhs_buffer_.get(),
165 kBufferSize,
166 base::Bind(&ResponseComparer::OnReadDataComplete, this));
169 void ResponseComparer::OnReadDataComplete(int result) {
170 if (completion_callback_.is_null() || !owner_)
171 return;
172 if (result < 0) {
173 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
174 completion_callback_.Reset();
175 return;
177 if (++completion_count_ != 2) {
178 previous_result_ = result;
179 return;
182 // TODO(michaeln): Probably shouldn't assume that the amounts read from
183 // each reader will always be the same. This would wrongly signal false
184 // in that case.
185 if (result != previous_result_) {
186 completion_callback_.Run(SERVICE_WORKER_OK, false);
187 return;
190 if (result == 0) {
191 completion_callback_.Run(SERVICE_WORKER_OK, true);
192 return;
195 int compare_result =
196 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
197 if (compare_result != 0) {
198 completion_callback_.Run(SERVICE_WORKER_OK, false);
199 return;
202 ReadSomeData();
205 } // namespace
207 ServiceWorkerStorage::InitialData::InitialData()
208 : next_registration_id(kInvalidServiceWorkerRegistrationId),
209 next_version_id(kInvalidServiceWorkerVersionId),
210 next_resource_id(kInvalidServiceWorkerResourceId) {
213 ServiceWorkerStorage::InitialData::~InitialData() {
216 ServiceWorkerStorage::
217 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
218 : registration_id(kInvalidServiceWorkerRegistrationId) {
221 ServiceWorkerStorage::
222 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
225 ServiceWorkerStorage::~ServiceWorkerStorage() {
226 ClearSessionOnlyOrigins();
227 weak_factory_.InvalidateWeakPtrs();
228 database_task_manager_->GetTaskRunner()->DeleteSoon(FROM_HERE,
229 database_.release());
232 // static
233 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
234 const base::FilePath& path,
235 base::WeakPtr<ServiceWorkerContextCore> context,
236 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
237 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
238 storage::QuotaManagerProxy* quota_manager_proxy,
239 storage::SpecialStoragePolicy* special_storage_policy) {
240 return make_scoped_ptr(new ServiceWorkerStorage(path,
241 context,
242 database_task_manager.Pass(),
243 disk_cache_thread,
244 quota_manager_proxy,
245 special_storage_policy));
248 // static
249 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
250 base::WeakPtr<ServiceWorkerContextCore> context,
251 ServiceWorkerStorage* old_storage) {
252 return make_scoped_ptr(
253 new ServiceWorkerStorage(old_storage->path_,
254 context,
255 old_storage->database_task_manager_->Clone(),
256 old_storage->disk_cache_thread_,
257 old_storage->quota_manager_proxy_.get(),
258 old_storage->special_storage_policy_.get()));
261 void ServiceWorkerStorage::FindRegistrationForDocument(
262 const GURL& document_url,
263 const FindRegistrationCallback& callback) {
264 DCHECK(!document_url.has_ref());
265 if (!LazyInitialize(base::Bind(
266 &ServiceWorkerStorage::FindRegistrationForDocument,
267 weak_factory_.GetWeakPtr(), document_url, callback))) {
268 if (state_ != INITIALIZING || !context_) {
269 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
270 SERVICE_WORKER_ERROR_FAILED, callback);
272 TRACE_EVENT_INSTANT1(
273 "ServiceWorker",
274 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
275 TRACE_EVENT_SCOPE_THREAD,
276 "URL", document_url.spec());
277 return;
279 DCHECK_EQ(INITIALIZED, state_);
281 // See if there are any stored registrations for the origin.
282 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
283 // Look for something currently being installed.
284 scoped_refptr<ServiceWorkerRegistration> installing_registration =
285 FindInstallingRegistrationForDocument(document_url);
286 ServiceWorkerStatusCode status = installing_registration.get() ?
287 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
288 TRACE_EVENT_INSTANT2(
289 "ServiceWorker",
290 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
291 TRACE_EVENT_SCOPE_THREAD,
292 "URL", document_url.spec(),
293 "Status", ServiceWorkerStatusToString(status));
294 CompleteFindNow(installing_registration,
295 status,
296 callback);
297 return;
300 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
301 // callback id.
302 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
303 TRACE_EVENT_ASYNC_BEGIN1(
304 "ServiceWorker",
305 "ServiceWorkerStorage::FindRegistrationForDocument",
306 callback_id,
307 "URL", document_url.spec());
308 database_task_manager_->GetTaskRunner()->PostTask(
309 FROM_HERE,
310 base::Bind(
311 &FindForDocumentInDB,
312 database_.get(),
313 base::ThreadTaskRunnerHandle::Get(),
314 document_url,
315 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
316 weak_factory_.GetWeakPtr(),
317 document_url,
318 callback,
319 callback_id)));
322 void ServiceWorkerStorage::FindRegistrationForPattern(
323 const GURL& scope,
324 const FindRegistrationCallback& callback) {
325 if (!LazyInitialize(base::Bind(
326 &ServiceWorkerStorage::FindRegistrationForPattern,
327 weak_factory_.GetWeakPtr(), scope, callback))) {
328 if (state_ != INITIALIZING || !context_) {
329 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
330 SERVICE_WORKER_ERROR_FAILED, callback);
332 return;
334 DCHECK_EQ(INITIALIZED, state_);
336 // See if there are any stored registrations for the origin.
337 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
338 // Look for something currently being installed.
339 scoped_refptr<ServiceWorkerRegistration> installing_registration =
340 FindInstallingRegistrationForPattern(scope);
341 CompleteFindSoon(FROM_HERE,
342 installing_registration,
343 installing_registration.get()
344 ? SERVICE_WORKER_OK
345 : SERVICE_WORKER_ERROR_NOT_FOUND,
346 callback);
347 return;
350 database_task_manager_->GetTaskRunner()->PostTask(
351 FROM_HERE,
352 base::Bind(
353 &FindForPatternInDB,
354 database_.get(),
355 base::ThreadTaskRunnerHandle::Get(),
356 scope,
357 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
358 weak_factory_.GetWeakPtr(),
359 scope,
360 callback)));
363 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
364 const GURL& scope) {
365 if (state_ != INITIALIZED || !context_)
366 return NULL;
367 for (RegistrationRefsById::const_iterator it =
368 uninstalling_registrations_.begin();
369 it != uninstalling_registrations_.end();
370 ++it) {
371 if (it->second->pattern() == scope) {
372 DCHECK(it->second->is_uninstalling());
373 return it->second.get();
376 return NULL;
379 void ServiceWorkerStorage::FindRegistrationForId(
380 int64 registration_id,
381 const GURL& origin,
382 const FindRegistrationCallback& callback) {
383 if (!LazyInitialize(base::Bind(
384 &ServiceWorkerStorage::FindRegistrationForId,
385 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
386 if (state_ != INITIALIZING || !context_) {
387 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
388 SERVICE_WORKER_ERROR_FAILED, callback);
390 return;
392 DCHECK_EQ(INITIALIZED, state_);
394 // See if there are any stored registrations for the origin.
395 if (!ContainsKey(registered_origins_, origin)) {
396 // Look for something currently being installed.
397 scoped_refptr<ServiceWorkerRegistration> installing_registration =
398 FindInstallingRegistrationForId(registration_id);
399 CompleteFindNow(installing_registration,
400 installing_registration.get()
401 ? SERVICE_WORKER_OK
402 : SERVICE_WORKER_ERROR_NOT_FOUND,
403 callback);
404 return;
407 scoped_refptr<ServiceWorkerRegistration> registration =
408 context_->GetLiveRegistration(registration_id);
409 if (registration.get()) {
410 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
411 return;
414 database_task_manager_->GetTaskRunner()->PostTask(
415 FROM_HERE,
416 base::Bind(&FindForIdInDB,
417 database_.get(),
418 base::ThreadTaskRunnerHandle::Get(),
419 registration_id,
420 origin,
421 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
422 weak_factory_.GetWeakPtr(),
423 callback)));
426 void ServiceWorkerStorage::FindRegistrationForIdOnly(
427 int64 registration_id,
428 const FindRegistrationCallback& callback) {
429 if (!LazyInitialize(
430 base::Bind(&ServiceWorkerStorage::FindRegistrationForIdOnly,
431 weak_factory_.GetWeakPtr(), registration_id, callback))) {
432 if (state_ != INITIALIZING || !context_) {
433 CompleteFindNow(nullptr, SERVICE_WORKER_ERROR_FAILED, callback);
435 return;
437 DCHECK_EQ(INITIALIZED, state_);
439 scoped_refptr<ServiceWorkerRegistration> registration =
440 context_->GetLiveRegistration(registration_id);
441 if (registration) {
442 // Delegate to FindRegistrationForId to make sure the same subset of live
443 // registrations is returned.
444 // TODO(mek): CompleteFindNow should really do all the required checks, so
445 // calling that directly here should be enough.
446 FindRegistrationForId(registration_id, registration->pattern().GetOrigin(),
447 callback);
448 return;
451 database_task_manager_->GetTaskRunner()->PostTask(
452 FROM_HERE,
453 base::Bind(&FindForIdOnlyInDB,
454 database_.get(),
455 base::ThreadTaskRunnerHandle::Get(),
456 registration_id,
457 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
458 weak_factory_.GetWeakPtr(),
459 callback)));
462 void ServiceWorkerStorage::GetRegistrationsForOrigin(
463 const GURL& origin,
464 const GetRegistrationsCallback& callback) {
465 if (!LazyInitialize(base::Bind(
466 &ServiceWorkerStorage::GetRegistrationsForOrigin,
467 weak_factory_.GetWeakPtr(), origin, callback))) {
468 if (state_ != INITIALIZING || !context_) {
469 RunSoon(
470 FROM_HERE,
471 base::Bind(callback,
472 std::vector<scoped_refptr<ServiceWorkerRegistration>>()));
474 return;
476 DCHECK_EQ(INITIALIZED, state_);
478 RegistrationList* registrations = new RegistrationList;
479 std::vector<ResourceList>* resource_lists = new std::vector<ResourceList>;
480 PostTaskAndReplyWithResult(
481 database_task_manager_->GetTaskRunner(), FROM_HERE,
482 base::Bind(&ServiceWorkerDatabase::GetRegistrationsForOrigin,
483 base::Unretained(database_.get()), origin,
484 base::Unretained(registrations),
485 base::Unretained(resource_lists)),
486 base::Bind(&ServiceWorkerStorage::DidGetRegistrations,
487 weak_factory_.GetWeakPtr(), callback,
488 base::Owned(registrations), base::Owned(resource_lists),
489 origin));
492 void ServiceWorkerStorage::GetAllRegistrationsInfos(
493 const GetRegistrationsInfosCallback& callback) {
494 if (!LazyInitialize(
495 base::Bind(&ServiceWorkerStorage::GetAllRegistrationsInfos,
496 weak_factory_.GetWeakPtr(), callback))) {
497 if (state_ != INITIALIZING || !context_) {
498 RunSoon(FROM_HERE, base::Bind(
499 callback, std::vector<ServiceWorkerRegistrationInfo>()));
501 return;
503 DCHECK_EQ(INITIALIZED, state_);
505 RegistrationList* registrations = new RegistrationList;
506 PostTaskAndReplyWithResult(
507 database_task_manager_->GetTaskRunner(), FROM_HERE,
508 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
509 base::Unretained(database_.get()),
510 base::Unretained(registrations)),
511 base::Bind(&ServiceWorkerStorage::DidGetRegistrationsInfos,
512 weak_factory_.GetWeakPtr(), callback,
513 base::Owned(registrations), GURL()));
516 void ServiceWorkerStorage::StoreRegistration(
517 ServiceWorkerRegistration* registration,
518 ServiceWorkerVersion* version,
519 const StatusCallback& callback) {
520 DCHECK(registration);
521 DCHECK(version);
523 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
524 if (IsDisabled() || !context_) {
525 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
526 return;
529 ServiceWorkerDatabase::RegistrationData data;
530 data.registration_id = registration->id();
531 data.scope = registration->pattern();
532 data.script = version->script_url();
533 data.has_fetch_handler = true;
534 data.version_id = version->version_id();
535 data.last_update_check = registration->last_update_check();
536 data.is_active = (version == registration->active_version());
538 ResourceList resources;
539 version->script_cache_map()->GetResources(&resources);
541 if (resources.empty()) {
542 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
543 return;
546 uint64 resources_total_size_bytes = 0;
547 for (const auto& resource : resources) {
548 resources_total_size_bytes += resource.size_bytes;
550 data.resources_total_size_bytes = resources_total_size_bytes;
552 if (!has_checked_for_stale_resources_)
553 DeleteStaleResources();
555 database_task_manager_->GetTaskRunner()->PostTask(
556 FROM_HERE,
557 base::Bind(&WriteRegistrationInDB,
558 database_.get(),
559 base::ThreadTaskRunnerHandle::Get(),
560 data,
561 resources,
562 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
563 weak_factory_.GetWeakPtr(),
564 callback,
565 data)));
567 registration->set_is_deleted(false);
570 void ServiceWorkerStorage::UpdateToActiveState(
571 ServiceWorkerRegistration* registration,
572 const StatusCallback& callback) {
573 DCHECK(registration);
575 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
576 if (IsDisabled() || !context_) {
577 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
578 return;
581 PostTaskAndReplyWithResult(
582 database_task_manager_->GetTaskRunner(),
583 FROM_HERE,
584 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
585 base::Unretained(database_.get()),
586 registration->id(),
587 registration->pattern().GetOrigin()),
588 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
589 weak_factory_.GetWeakPtr(),
590 callback));
593 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
594 ServiceWorkerRegistration* registration) {
595 DCHECK(registration);
597 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
598 if (IsDisabled() || !context_)
599 return;
601 database_task_manager_->GetTaskRunner()->PostTask(
602 FROM_HERE,
603 base::Bind(
604 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
605 base::Unretained(database_.get()),
606 registration->id(),
607 registration->pattern().GetOrigin(),
608 registration->last_update_check()));
611 void ServiceWorkerStorage::DeleteRegistration(
612 int64 registration_id,
613 const GURL& origin,
614 const StatusCallback& callback) {
615 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
616 if (IsDisabled() || !context_) {
617 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
618 return;
621 if (!has_checked_for_stale_resources_)
622 DeleteStaleResources();
624 DidDeleteRegistrationParams params;
625 params.registration_id = registration_id;
626 params.origin = origin;
627 params.callback = callback;
629 database_task_manager_->GetTaskRunner()->PostTask(
630 FROM_HERE,
631 base::Bind(&DeleteRegistrationFromDB,
632 database_.get(),
633 base::ThreadTaskRunnerHandle::Get(),
634 registration_id,
635 origin,
636 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
637 weak_factory_.GetWeakPtr(),
638 params)));
640 // The registration should no longer be findable.
641 pending_deletions_.insert(registration_id);
642 ServiceWorkerRegistration* registration =
643 context_->GetLiveRegistration(registration_id);
644 if (registration)
645 registration->set_is_deleted(true);
648 scoped_ptr<ServiceWorkerResponseReader>
649 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
650 return make_scoped_ptr(
651 new ServiceWorkerResponseReader(response_id, disk_cache()));
654 scoped_ptr<ServiceWorkerResponseWriter>
655 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
656 return make_scoped_ptr(
657 new ServiceWorkerResponseWriter(response_id, disk_cache()));
660 scoped_ptr<ServiceWorkerResponseMetadataWriter>
661 ServiceWorkerStorage::CreateResponseMetadataWriter(int64 response_id) {
662 return make_scoped_ptr(
663 new ServiceWorkerResponseMetadataWriter(response_id, disk_cache()));
666 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
667 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
668 DCHECK_EQ(INITIALIZED, state_);
670 if (!has_checked_for_stale_resources_)
671 DeleteStaleResources();
673 database_task_manager_->GetTaskRunner()->PostTask(
674 FROM_HERE,
675 base::Bind(base::IgnoreResult(
676 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
677 base::Unretained(database_.get()),
678 std::set<int64>(&id, &id + 1)));
681 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
682 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
683 database_task_manager_->GetTaskRunner()->PostTask(
684 FROM_HERE,
685 base::Bind(base::IgnoreResult(
686 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
687 base::Unretained(database_.get()),
688 std::set<int64>(&id, &id + 1)));
689 StartPurgingResources(std::vector<int64>(1, id));
692 void ServiceWorkerStorage::CompareScriptResources(
693 int64 lhs_id, int64 rhs_id,
694 const CompareCallback& callback) {
695 DCHECK(!callback.is_null());
696 scoped_refptr<ResponseComparer> comparer =
697 new ResponseComparer(weak_factory_.GetWeakPtr(),
698 CreateResponseReader(lhs_id),
699 CreateResponseReader(rhs_id),
700 callback);
701 comparer->Start(); // It deletes itself when done.
704 void ServiceWorkerStorage::StoreUserData(
705 int64 registration_id,
706 const GURL& origin,
707 const std::string& key,
708 const std::string& data,
709 const StatusCallback& callback) {
710 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
711 if (IsDisabled() || !context_) {
712 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
713 return;
716 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
717 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
718 return;
721 PostTaskAndReplyWithResult(
722 database_task_manager_->GetTaskRunner(),
723 FROM_HERE,
724 base::Bind(&ServiceWorkerDatabase::WriteUserData,
725 base::Unretained(database_.get()),
726 registration_id, origin, key, data),
727 base::Bind(&ServiceWorkerStorage::DidStoreUserData,
728 weak_factory_.GetWeakPtr(),
729 callback));
732 void ServiceWorkerStorage::GetUserData(
733 int64 registration_id,
734 const std::string& key,
735 const GetUserDataCallback& callback) {
736 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
737 if (IsDisabled() || !context_) {
738 RunSoon(FROM_HERE,
739 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
740 return;
743 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
744 RunSoon(FROM_HERE,
745 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
746 return;
749 database_task_manager_->GetTaskRunner()->PostTask(
750 FROM_HERE,
751 base::Bind(&ServiceWorkerStorage::GetUserDataInDB,
752 database_.get(),
753 base::ThreadTaskRunnerHandle::Get(),
754 registration_id,
755 key,
756 base::Bind(&ServiceWorkerStorage::DidGetUserData,
757 weak_factory_.GetWeakPtr(),
758 callback)));
761 void ServiceWorkerStorage::ClearUserData(
762 int64 registration_id,
763 const std::string& key,
764 const StatusCallback& callback) {
765 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
766 if (IsDisabled() || !context_) {
767 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
768 return;
771 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
772 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
773 return;
776 PostTaskAndReplyWithResult(
777 database_task_manager_->GetTaskRunner(),
778 FROM_HERE,
779 base::Bind(&ServiceWorkerDatabase::DeleteUserData,
780 base::Unretained(database_.get()),
781 registration_id, key),
782 base::Bind(&ServiceWorkerStorage::DidDeleteUserData,
783 weak_factory_.GetWeakPtr(),
784 callback));
787 void ServiceWorkerStorage::GetUserDataForAllRegistrations(
788 const std::string& key,
789 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
790 callback) {
791 if (!LazyInitialize(
792 base::Bind(&ServiceWorkerStorage::GetUserDataForAllRegistrations,
793 weak_factory_.GetWeakPtr(), key, callback))) {
794 if (state_ != INITIALIZING || !context_) {
795 RunSoon(FROM_HERE,
796 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
797 SERVICE_WORKER_ERROR_FAILED));
799 return;
801 DCHECK_EQ(INITIALIZED, state_);
803 if (key.empty()) {
804 RunSoon(FROM_HERE,
805 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
806 SERVICE_WORKER_ERROR_FAILED));
807 return;
810 database_task_manager_->GetTaskRunner()->PostTask(
811 FROM_HERE,
812 base::Bind(
813 &ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB,
814 database_.get(),
815 base::ThreadTaskRunnerHandle::Get(),
816 key,
817 base::Bind(&ServiceWorkerStorage::DidGetUserDataForAllRegistrations,
818 weak_factory_.GetWeakPtr(),
819 callback)));
822 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
823 Disable();
825 // Delete the database on the database thread.
826 PostTaskAndReplyWithResult(
827 database_task_manager_->GetTaskRunner(),
828 FROM_HERE,
829 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
830 base::Unretained(database_.get())),
831 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
832 weak_factory_.GetWeakPtr(),
833 callback));
836 int64 ServiceWorkerStorage::NewRegistrationId() {
837 if (state_ == DISABLED)
838 return kInvalidServiceWorkerRegistrationId;
839 DCHECK_EQ(INITIALIZED, state_);
840 return next_registration_id_++;
843 int64 ServiceWorkerStorage::NewVersionId() {
844 if (state_ == DISABLED)
845 return kInvalidServiceWorkerVersionId;
846 DCHECK_EQ(INITIALIZED, state_);
847 return next_version_id_++;
850 int64 ServiceWorkerStorage::NewResourceId() {
851 if (state_ == DISABLED)
852 return kInvalidServiceWorkerResourceId;
853 DCHECK_EQ(INITIALIZED, state_);
854 return next_resource_id_++;
857 void ServiceWorkerStorage::NotifyInstallingRegistration(
858 ServiceWorkerRegistration* registration) {
859 DCHECK(installing_registrations_.find(registration->id()) ==
860 installing_registrations_.end());
861 installing_registrations_[registration->id()] = registration;
864 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
865 ServiceWorkerRegistration* registration,
866 ServiceWorkerVersion* version,
867 ServiceWorkerStatusCode status) {
868 installing_registrations_.erase(registration->id());
869 if (status != SERVICE_WORKER_OK && version) {
870 ResourceList resources;
871 version->script_cache_map()->GetResources(&resources);
873 std::set<int64> ids;
874 for (size_t i = 0; i < resources.size(); ++i)
875 ids.insert(resources[i].resource_id);
877 database_task_manager_->GetTaskRunner()->PostTask(
878 FROM_HERE,
879 base::Bind(base::IgnoreResult(
880 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
881 base::Unretained(database_.get()),
882 ids));
886 void ServiceWorkerStorage::NotifyUninstallingRegistration(
887 ServiceWorkerRegistration* registration) {
888 DCHECK(uninstalling_registrations_.find(registration->id()) ==
889 uninstalling_registrations_.end());
890 uninstalling_registrations_[registration->id()] = registration;
893 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
894 ServiceWorkerRegistration* registration) {
895 uninstalling_registrations_.erase(registration->id());
898 void ServiceWorkerStorage::Disable() {
899 state_ = DISABLED;
900 if (disk_cache_)
901 disk_cache_->Disable();
904 bool ServiceWorkerStorage::IsDisabled() const {
905 return state_ == DISABLED;
908 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
909 if (!has_checked_for_stale_resources_)
910 DeleteStaleResources();
911 StartPurgingResources(resources);
914 ServiceWorkerStorage::ServiceWorkerStorage(
915 const base::FilePath& path,
916 base::WeakPtr<ServiceWorkerContextCore> context,
917 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
918 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
919 storage::QuotaManagerProxy* quota_manager_proxy,
920 storage::SpecialStoragePolicy* special_storage_policy)
921 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
922 next_version_id_(kInvalidServiceWorkerVersionId),
923 next_resource_id_(kInvalidServiceWorkerResourceId),
924 state_(UNINITIALIZED),
925 path_(path),
926 context_(context),
927 database_task_manager_(database_task_manager.Pass()),
928 disk_cache_thread_(disk_cache_thread),
929 quota_manager_proxy_(quota_manager_proxy),
930 special_storage_policy_(special_storage_policy),
931 is_purge_pending_(false),
932 has_checked_for_stale_resources_(false),
933 weak_factory_(this) {
934 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
937 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
938 if (path_.empty())
939 return base::FilePath();
940 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
941 .Append(kDatabaseName);
944 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
945 if (path_.empty())
946 return base::FilePath();
947 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
948 .Append(kDiskCacheName);
951 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
952 if (!context_)
953 return false;
955 switch (state_) {
956 case INITIALIZED:
957 return true;
958 case DISABLED:
959 return false;
960 case INITIALIZING:
961 pending_tasks_.push_back(callback);
962 return false;
963 case UNINITIALIZED:
964 pending_tasks_.push_back(callback);
965 // Fall-through.
968 state_ = INITIALIZING;
969 database_task_manager_->GetTaskRunner()->PostTask(
970 FROM_HERE,
971 base::Bind(&ReadInitialDataFromDB,
972 database_.get(),
973 base::ThreadTaskRunnerHandle::Get(),
974 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
975 weak_factory_.GetWeakPtr())));
976 return false;
979 void ServiceWorkerStorage::DidReadInitialData(
980 InitialData* data,
981 ServiceWorkerDatabase::Status status) {
982 DCHECK(data);
983 DCHECK_EQ(INITIALIZING, state_);
985 if (status == ServiceWorkerDatabase::STATUS_OK) {
986 next_registration_id_ = data->next_registration_id;
987 next_version_id_ = data->next_version_id;
988 next_resource_id_ = data->next_resource_id;
989 registered_origins_.swap(data->origins);
990 state_ = INITIALIZED;
991 } else {
992 DVLOG(2) << "Failed to initialize: "
993 << ServiceWorkerDatabase::StatusToString(status);
994 ScheduleDeleteAndStartOver();
997 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
998 it != pending_tasks_.end(); ++it) {
999 RunSoon(FROM_HERE, *it);
1001 pending_tasks_.clear();
1004 void ServiceWorkerStorage::DidFindRegistrationForDocument(
1005 const GURL& document_url,
1006 const FindRegistrationCallback& callback,
1007 int64 callback_id,
1008 const ServiceWorkerDatabase::RegistrationData& data,
1009 const ResourceList& resources,
1010 ServiceWorkerDatabase::Status status) {
1011 if (status == ServiceWorkerDatabase::STATUS_OK) {
1012 ReturnFoundRegistration(callback, data, resources);
1013 TRACE_EVENT_ASYNC_END1(
1014 "ServiceWorker",
1015 "ServiceWorkerStorage::FindRegistrationForDocument",
1016 callback_id,
1017 "Status", ServiceWorkerDatabase::StatusToString(status));
1018 return;
1021 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1022 // Look for something currently being installed.
1023 scoped_refptr<ServiceWorkerRegistration> installing_registration =
1024 FindInstallingRegistrationForDocument(document_url);
1025 ServiceWorkerStatusCode installing_status = installing_registration.get() ?
1026 SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND;
1027 callback.Run(installing_status, installing_registration);
1028 TRACE_EVENT_ASYNC_END2(
1029 "ServiceWorker",
1030 "ServiceWorkerStorage::FindRegistrationForDocument",
1031 callback_id,
1032 "Status", ServiceWorkerDatabase::StatusToString(status),
1033 "Info",
1034 (installing_status == SERVICE_WORKER_OK) ?
1035 "Installing registration is found" :
1036 "Any registrations are not found");
1037 return;
1040 ScheduleDeleteAndStartOver();
1041 callback.Run(DatabaseStatusToStatusCode(status),
1042 scoped_refptr<ServiceWorkerRegistration>());
1043 TRACE_EVENT_ASYNC_END1(
1044 "ServiceWorker",
1045 "ServiceWorkerStorage::FindRegistrationForDocument",
1046 callback_id,
1047 "Status", ServiceWorkerDatabase::StatusToString(status));
1050 void ServiceWorkerStorage::DidFindRegistrationForPattern(
1051 const GURL& scope,
1052 const FindRegistrationCallback& callback,
1053 const ServiceWorkerDatabase::RegistrationData& data,
1054 const ResourceList& resources,
1055 ServiceWorkerDatabase::Status status) {
1056 if (status == ServiceWorkerDatabase::STATUS_OK) {
1057 ReturnFoundRegistration(callback, data, resources);
1058 return;
1061 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1062 scoped_refptr<ServiceWorkerRegistration> installing_registration =
1063 FindInstallingRegistrationForPattern(scope);
1064 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
1065 : SERVICE_WORKER_ERROR_NOT_FOUND,
1066 installing_registration);
1067 return;
1070 ScheduleDeleteAndStartOver();
1071 callback.Run(DatabaseStatusToStatusCode(status),
1072 scoped_refptr<ServiceWorkerRegistration>());
1075 void ServiceWorkerStorage::DidFindRegistrationForId(
1076 const FindRegistrationCallback& callback,
1077 const ServiceWorkerDatabase::RegistrationData& data,
1078 const ResourceList& resources,
1079 ServiceWorkerDatabase::Status status) {
1080 if (status == ServiceWorkerDatabase::STATUS_OK) {
1081 ReturnFoundRegistration(callback, data, resources);
1082 return;
1085 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1086 // TODO(nhiroki): Find a registration in |installing_registrations_|.
1087 callback.Run(DatabaseStatusToStatusCode(status),
1088 scoped_refptr<ServiceWorkerRegistration>());
1089 return;
1092 ScheduleDeleteAndStartOver();
1093 callback.Run(DatabaseStatusToStatusCode(status),
1094 scoped_refptr<ServiceWorkerRegistration>());
1097 void ServiceWorkerStorage::ReturnFoundRegistration(
1098 const FindRegistrationCallback& callback,
1099 const ServiceWorkerDatabase::RegistrationData& data,
1100 const ResourceList& resources) {
1101 DCHECK(!resources.empty());
1102 scoped_refptr<ServiceWorkerRegistration> registration =
1103 GetOrCreateRegistration(data, resources);
1104 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
1107 void ServiceWorkerStorage::DidGetRegistrations(
1108 const GetRegistrationsCallback& callback,
1109 RegistrationList* registration_data_list,
1110 std::vector<ResourceList>* resources_list,
1111 const GURL& origin_filter,
1112 ServiceWorkerDatabase::Status status) {
1113 DCHECK(registration_data_list);
1114 DCHECK(resources_list);
1116 if (status != ServiceWorkerDatabase::STATUS_OK &&
1117 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1118 ScheduleDeleteAndStartOver();
1119 callback.Run(std::vector<scoped_refptr<ServiceWorkerRegistration>>());
1120 return;
1123 // Add all stored registrations.
1124 std::set<int64> registration_ids;
1125 std::vector<scoped_refptr<ServiceWorkerRegistration>> registrations;
1126 size_t index = 0;
1127 for (const auto& registration_data : *registration_data_list) {
1128 registration_ids.insert(registration_data.registration_id);
1129 registrations.push_back(GetOrCreateRegistration(
1130 registration_data, resources_list->at(index++)));
1133 // Add unstored registrations that are being installed.
1134 for (RegistrationRefsById::const_iterator it =
1135 installing_registrations_.begin();
1136 it != installing_registrations_.end(); ++it) {
1137 if ((!origin_filter.is_valid() ||
1138 it->second->pattern().GetOrigin() == origin_filter) &&
1139 registration_ids.insert(it->first).second) {
1140 registrations.push_back((it->second).get());
1144 callback.Run(registrations);
1147 void ServiceWorkerStorage::DidGetRegistrationsInfos(
1148 const GetRegistrationsInfosCallback& callback,
1149 RegistrationList* registration_data_list,
1150 const GURL& origin_filter,
1151 ServiceWorkerDatabase::Status status) {
1152 DCHECK(registration_data_list);
1153 if (status != ServiceWorkerDatabase::STATUS_OK &&
1154 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1155 ScheduleDeleteAndStartOver();
1156 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
1157 return;
1160 // Add all stored registrations.
1161 std::set<int64> pushed_registrations;
1162 std::vector<ServiceWorkerRegistrationInfo> infos;
1163 for (const auto& registration_data : *registration_data_list) {
1164 const bool inserted =
1165 pushed_registrations.insert(registration_data.registration_id).second;
1166 DCHECK(inserted);
1168 ServiceWorkerRegistration* registration =
1169 context_->GetLiveRegistration(registration_data.registration_id);
1170 if (registration) {
1171 infos.push_back(registration->GetInfo());
1172 continue;
1175 ServiceWorkerRegistrationInfo info;
1176 info.pattern = registration_data.scope;
1177 info.registration_id = registration_data.registration_id;
1178 info.stored_version_size_bytes =
1179 registration_data.resources_total_size_bytes;
1180 if (ServiceWorkerVersion* version =
1181 context_->GetLiveVersion(registration_data.version_id)) {
1182 if (registration_data.is_active)
1183 info.active_version = version->GetInfo();
1184 else
1185 info.waiting_version = version->GetInfo();
1186 infos.push_back(info);
1187 continue;
1190 if (registration_data.is_active) {
1191 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
1192 info.active_version.script_url = registration_data.script;
1193 info.active_version.version_id = registration_data.version_id;
1194 info.active_version.registration_id = registration_data.registration_id;
1195 } else {
1196 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
1197 info.waiting_version.script_url = registration_data.script;
1198 info.waiting_version.version_id = registration_data.version_id;
1199 info.waiting_version.registration_id = registration_data.registration_id;
1201 infos.push_back(info);
1204 // Add unstored registrations that are being installed.
1205 for (RegistrationRefsById::const_iterator it =
1206 installing_registrations_.begin();
1207 it != installing_registrations_.end(); ++it) {
1208 if ((!origin_filter.is_valid() ||
1209 it->second->pattern().GetOrigin() == origin_filter) &&
1210 pushed_registrations.insert(it->first).second) {
1211 infos.push_back(it->second->GetInfo());
1215 callback.Run(infos);
1218 void ServiceWorkerStorage::DidStoreRegistration(
1219 const StatusCallback& callback,
1220 const ServiceWorkerDatabase::RegistrationData& new_version,
1221 const GURL& origin,
1222 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1223 const std::vector<int64>& newly_purgeable_resources,
1224 ServiceWorkerDatabase::Status status) {
1225 if (status != ServiceWorkerDatabase::STATUS_OK) {
1226 ScheduleDeleteAndStartOver();
1227 callback.Run(DatabaseStatusToStatusCode(status));
1228 return;
1230 registered_origins_.insert(origin);
1232 scoped_refptr<ServiceWorkerRegistration> registration =
1233 context_->GetLiveRegistration(new_version.registration_id);
1234 registration->set_resources_total_size_bytes(
1235 new_version.resources_total_size_bytes);
1236 if (quota_manager_proxy_.get()) {
1237 // Can be nullptr in tests.
1238 quota_manager_proxy_->NotifyStorageModified(
1239 storage::QuotaClient::kServiceWorker,
1240 origin,
1241 storage::StorageType::kStorageTypeTemporary,
1242 new_version.resources_total_size_bytes -
1243 deleted_version.resources_total_size_bytes);
1246 callback.Run(SERVICE_WORKER_OK);
1248 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1249 StartPurgingResources(newly_purgeable_resources);
1252 void ServiceWorkerStorage::DidUpdateToActiveState(
1253 const StatusCallback& callback,
1254 ServiceWorkerDatabase::Status status) {
1255 if (status != ServiceWorkerDatabase::STATUS_OK &&
1256 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1257 ScheduleDeleteAndStartOver();
1259 callback.Run(DatabaseStatusToStatusCode(status));
1262 void ServiceWorkerStorage::DidDeleteRegistration(
1263 const DidDeleteRegistrationParams& params,
1264 bool origin_is_deletable,
1265 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1266 const std::vector<int64>& newly_purgeable_resources,
1267 ServiceWorkerDatabase::Status status) {
1268 pending_deletions_.erase(params.registration_id);
1269 if (status != ServiceWorkerDatabase::STATUS_OK) {
1270 ScheduleDeleteAndStartOver();
1271 params.callback.Run(DatabaseStatusToStatusCode(status));
1272 return;
1274 if (quota_manager_proxy_.get()) {
1275 // Can be nullptr in tests.
1276 quota_manager_proxy_->NotifyStorageModified(
1277 storage::QuotaClient::kServiceWorker,
1278 params.origin,
1279 storage::StorageType::kStorageTypeTemporary,
1280 -deleted_version.resources_total_size_bytes);
1282 if (origin_is_deletable)
1283 registered_origins_.erase(params.origin);
1284 params.callback.Run(SERVICE_WORKER_OK);
1286 if (!context_ || !context_->GetLiveVersion(deleted_version.version_id))
1287 StartPurgingResources(newly_purgeable_resources);
1290 void ServiceWorkerStorage::DidStoreUserData(
1291 const StatusCallback& callback,
1292 ServiceWorkerDatabase::Status status) {
1293 // |status| can be NOT_FOUND when the associated registration did not exist in
1294 // the database. In the case, we don't have to schedule the corruption
1295 // recovery.
1296 if (status != ServiceWorkerDatabase::STATUS_OK &&
1297 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1298 ScheduleDeleteAndStartOver();
1300 callback.Run(DatabaseStatusToStatusCode(status));
1303 void ServiceWorkerStorage::DidGetUserData(
1304 const GetUserDataCallback& callback,
1305 const std::string& data,
1306 ServiceWorkerDatabase::Status status) {
1307 if (status != ServiceWorkerDatabase::STATUS_OK &&
1308 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1309 ScheduleDeleteAndStartOver();
1311 callback.Run(data, DatabaseStatusToStatusCode(status));
1314 void ServiceWorkerStorage::DidDeleteUserData(
1315 const StatusCallback& callback,
1316 ServiceWorkerDatabase::Status status) {
1317 if (status != ServiceWorkerDatabase::STATUS_OK)
1318 ScheduleDeleteAndStartOver();
1319 callback.Run(DatabaseStatusToStatusCode(status));
1322 void ServiceWorkerStorage::DidGetUserDataForAllRegistrations(
1323 const GetUserDataForAllRegistrationsCallback& callback,
1324 const std::vector<std::pair<int64, std::string>>& user_data,
1325 ServiceWorkerDatabase::Status status) {
1326 if (status != ServiceWorkerDatabase::STATUS_OK)
1327 ScheduleDeleteAndStartOver();
1328 callback.Run(user_data, DatabaseStatusToStatusCode(status));
1331 scoped_refptr<ServiceWorkerRegistration>
1332 ServiceWorkerStorage::GetOrCreateRegistration(
1333 const ServiceWorkerDatabase::RegistrationData& data,
1334 const ResourceList& resources) {
1335 scoped_refptr<ServiceWorkerRegistration> registration =
1336 context_->GetLiveRegistration(data.registration_id);
1337 if (registration.get())
1338 return registration;
1340 registration = new ServiceWorkerRegistration(
1341 data.scope, data.registration_id, context_);
1342 registration->set_resources_total_size_bytes(data.resources_total_size_bytes);
1343 registration->set_last_update_check(data.last_update_check);
1344 if (pending_deletions_.find(data.registration_id) !=
1345 pending_deletions_.end()) {
1346 registration->set_is_deleted(true);
1348 scoped_refptr<ServiceWorkerVersion> version =
1349 context_->GetLiveVersion(data.version_id);
1350 if (!version.get()) {
1351 version = new ServiceWorkerVersion(
1352 registration.get(), data.script, data.version_id, context_);
1353 version->SetStatus(data.is_active ?
1354 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1355 version->script_cache_map()->SetResources(resources);
1358 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1359 registration->SetActiveVersion(version);
1360 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1361 registration->SetWaitingVersion(version);
1362 else
1363 NOTREACHED();
1365 return registration;
1368 ServiceWorkerRegistration*
1369 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1370 const GURL& document_url) {
1371 DCHECK(!document_url.has_ref());
1373 LongestScopeMatcher matcher(document_url);
1374 ServiceWorkerRegistration* match = NULL;
1376 // TODO(nhiroki): This searches over installing registrations linearly and it
1377 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1378 for (RegistrationRefsById::const_iterator it =
1379 installing_registrations_.begin();
1380 it != installing_registrations_.end(); ++it) {
1381 if (matcher.MatchLongest(it->second->pattern()))
1382 match = it->second.get();
1384 return match;
1387 ServiceWorkerRegistration*
1388 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1389 const GURL& scope) {
1390 for (RegistrationRefsById::const_iterator it =
1391 installing_registrations_.begin();
1392 it != installing_registrations_.end(); ++it) {
1393 if (it->second->pattern() == scope)
1394 return it->second.get();
1396 return NULL;
1399 ServiceWorkerRegistration*
1400 ServiceWorkerStorage::FindInstallingRegistrationForId(
1401 int64 registration_id) {
1402 RegistrationRefsById::const_iterator found =
1403 installing_registrations_.find(registration_id);
1404 if (found == installing_registrations_.end())
1405 return NULL;
1406 return found->second.get();
1409 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1410 if (disk_cache_)
1411 return disk_cache_.get();
1413 disk_cache_ = ServiceWorkerDiskCache::CreateWithBlockFileBackend();
1415 base::FilePath path = GetDiskCachePath();
1416 if (path.empty()) {
1417 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1418 net::CompletionCallback());
1419 DCHECK_EQ(net::OK, rv);
1420 return disk_cache_.get();
1423 int rv = disk_cache_->InitWithDiskBackend(
1424 path,
1425 kMaxDiskCacheSize,
1426 false,
1427 disk_cache_thread_,
1428 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1429 weak_factory_.GetWeakPtr()));
1430 if (rv != net::ERR_IO_PENDING)
1431 OnDiskCacheInitialized(rv);
1433 return disk_cache_.get();
1436 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1437 if (rv != net::OK) {
1438 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1439 << net::ErrorToString(rv);
1440 ScheduleDeleteAndStartOver();
1442 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1445 void ServiceWorkerStorage::StartPurgingResources(
1446 const std::vector<int64>& ids) {
1447 DCHECK(has_checked_for_stale_resources_);
1448 for (size_t i = 0; i < ids.size(); ++i)
1449 purgeable_resource_ids_.push_back(ids[i]);
1450 ContinuePurgingResources();
1453 void ServiceWorkerStorage::StartPurgingResources(
1454 const ResourceList& resources) {
1455 DCHECK(has_checked_for_stale_resources_);
1456 for (size_t i = 0; i < resources.size(); ++i)
1457 purgeable_resource_ids_.push_back(resources[i].resource_id);
1458 ContinuePurgingResources();
1461 void ServiceWorkerStorage::ContinuePurgingResources() {
1462 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1463 return;
1465 // Do one at a time until we're done, use RunSoon to avoid recursion when
1466 // DoomEntry returns immediately.
1467 is_purge_pending_ = true;
1468 int64 id = purgeable_resource_ids_.front();
1469 purgeable_resource_ids_.pop_front();
1470 RunSoon(FROM_HERE,
1471 base::Bind(&ServiceWorkerStorage::PurgeResource,
1472 weak_factory_.GetWeakPtr(), id));
1475 void ServiceWorkerStorage::PurgeResource(int64 id) {
1476 DCHECK(is_purge_pending_);
1477 int rv = disk_cache()->DoomEntry(
1478 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1479 weak_factory_.GetWeakPtr(), id));
1480 if (rv != net::ERR_IO_PENDING)
1481 OnResourcePurged(id, rv);
1484 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1485 DCHECK(is_purge_pending_);
1486 is_purge_pending_ = false;
1488 database_task_manager_->GetTaskRunner()->PostTask(
1489 FROM_HERE,
1490 base::Bind(base::IgnoreResult(
1491 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1492 base::Unretained(database_.get()),
1493 std::set<int64>(&id, &id + 1)));
1495 ContinuePurgingResources();
1498 void ServiceWorkerStorage::DeleteStaleResources() {
1499 DCHECK(!has_checked_for_stale_resources_);
1500 has_checked_for_stale_resources_ = true;
1501 database_task_manager_->GetTaskRunner()->PostTask(
1502 FROM_HERE,
1503 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1504 database_.get(),
1505 base::ThreadTaskRunnerHandle::Get(),
1506 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1507 weak_factory_.GetWeakPtr())));
1510 void ServiceWorkerStorage::DidCollectStaleResources(
1511 const std::vector<int64>& stale_resource_ids,
1512 ServiceWorkerDatabase::Status status) {
1513 if (status != ServiceWorkerDatabase::STATUS_OK) {
1514 DCHECK_NE(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, status);
1515 ScheduleDeleteAndStartOver();
1516 return;
1518 StartPurgingResources(stale_resource_ids);
1521 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1522 // Can be null in tests.
1523 if (!special_storage_policy_.get())
1524 return;
1526 if (!special_storage_policy_->HasSessionOnlyOrigins())
1527 return;
1529 std::set<GURL> session_only_origins;
1530 for (const GURL& origin : registered_origins_) {
1531 if (special_storage_policy_->IsStorageSessionOnly(origin))
1532 session_only_origins.insert(origin);
1535 database_task_manager_->GetShutdownBlockingTaskRunner()->PostTask(
1536 FROM_HERE,
1537 base::Bind(&DeleteAllDataForOriginsFromDB,
1538 database_.get(),
1539 session_only_origins));
1542 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1543 ServiceWorkerDatabase* database,
1544 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1545 const GetResourcesCallback& callback) {
1546 std::set<int64> ids;
1547 ServiceWorkerDatabase::Status status =
1548 database->GetUncommittedResourceIds(&ids);
1549 if (status != ServiceWorkerDatabase::STATUS_OK) {
1550 original_task_runner->PostTask(
1551 FROM_HERE,
1552 base::Bind(
1553 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1554 return;
1557 status = database->PurgeUncommittedResourceIds(ids);
1558 if (status != ServiceWorkerDatabase::STATUS_OK) {
1559 original_task_runner->PostTask(
1560 FROM_HERE,
1561 base::Bind(
1562 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1563 return;
1566 ids.clear();
1567 status = database->GetPurgeableResourceIds(&ids);
1568 original_task_runner->PostTask(
1569 FROM_HERE,
1570 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1573 void ServiceWorkerStorage::ReadInitialDataFromDB(
1574 ServiceWorkerDatabase* database,
1575 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1576 const InitializeCallback& callback) {
1577 DCHECK(database);
1578 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1579 new ServiceWorkerStorage::InitialData());
1581 ServiceWorkerDatabase::Status status =
1582 database->GetNextAvailableIds(&data->next_registration_id,
1583 &data->next_version_id,
1584 &data->next_resource_id);
1585 if (status != ServiceWorkerDatabase::STATUS_OK) {
1586 original_task_runner->PostTask(
1587 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1588 return;
1591 status = database->GetOriginsWithRegistrations(&data->origins);
1592 original_task_runner->PostTask(
1593 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1596 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1597 ServiceWorkerDatabase* database,
1598 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1599 int64 registration_id,
1600 const GURL& origin,
1601 const DeleteRegistrationCallback& callback) {
1602 DCHECK(database);
1604 ServiceWorkerDatabase::RegistrationData deleted_version;
1605 std::vector<int64> newly_purgeable_resources;
1606 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1607 registration_id, origin, &deleted_version, &newly_purgeable_resources);
1608 if (status != ServiceWorkerDatabase::STATUS_OK) {
1609 original_task_runner->PostTask(
1610 FROM_HERE,
1611 base::Bind(
1612 callback, false, deleted_version, std::vector<int64>(), status));
1613 return;
1616 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1617 // unique origin list.
1618 RegistrationList registrations;
1619 status = database->GetRegistrationsForOrigin(origin, &registrations, nullptr);
1620 if (status != ServiceWorkerDatabase::STATUS_OK) {
1621 original_task_runner->PostTask(
1622 FROM_HERE,
1623 base::Bind(
1624 callback, false, deleted_version, std::vector<int64>(), status));
1625 return;
1628 bool deletable = registrations.empty();
1629 original_task_runner->PostTask(FROM_HERE,
1630 base::Bind(callback,
1631 deletable,
1632 deleted_version,
1633 newly_purgeable_resources,
1634 status));
1637 void ServiceWorkerStorage::WriteRegistrationInDB(
1638 ServiceWorkerDatabase* database,
1639 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1640 const ServiceWorkerDatabase::RegistrationData& data,
1641 const ResourceList& resources,
1642 const WriteRegistrationCallback& callback) {
1643 DCHECK(database);
1644 ServiceWorkerDatabase::RegistrationData deleted_version;
1645 std::vector<int64> newly_purgeable_resources;
1646 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1647 data, resources, &deleted_version, &newly_purgeable_resources);
1648 original_task_runner->PostTask(FROM_HERE,
1649 base::Bind(callback,
1650 data.script.GetOrigin(),
1651 deleted_version,
1652 newly_purgeable_resources,
1653 status));
1656 void ServiceWorkerStorage::FindForDocumentInDB(
1657 ServiceWorkerDatabase* database,
1658 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1659 const GURL& document_url,
1660 const FindInDBCallback& callback) {
1661 GURL origin = document_url.GetOrigin();
1662 RegistrationList registrations;
1663 ServiceWorkerDatabase::Status status =
1664 database->GetRegistrationsForOrigin(origin, &registrations, nullptr);
1665 if (status != ServiceWorkerDatabase::STATUS_OK) {
1666 original_task_runner->PostTask(
1667 FROM_HERE,
1668 base::Bind(callback,
1669 ServiceWorkerDatabase::RegistrationData(),
1670 ResourceList(),
1671 status));
1672 return;
1675 ServiceWorkerDatabase::RegistrationData data;
1676 ResourceList resources;
1677 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1679 // Find one with a pattern match.
1680 LongestScopeMatcher matcher(document_url);
1681 int64 match = kInvalidServiceWorkerRegistrationId;
1682 for (size_t i = 0; i < registrations.size(); ++i) {
1683 if (matcher.MatchLongest(registrations[i].scope))
1684 match = registrations[i].registration_id;
1687 if (match != kInvalidServiceWorkerRegistrationId)
1688 status = database->ReadRegistration(match, origin, &data, &resources);
1690 original_task_runner->PostTask(
1691 FROM_HERE,
1692 base::Bind(callback, data, resources, status));
1695 void ServiceWorkerStorage::FindForPatternInDB(
1696 ServiceWorkerDatabase* database,
1697 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1698 const GURL& scope,
1699 const FindInDBCallback& callback) {
1700 GURL origin = scope.GetOrigin();
1701 RegistrationList registrations;
1702 ServiceWorkerDatabase::Status status =
1703 database->GetRegistrationsForOrigin(origin, &registrations, nullptr);
1704 if (status != ServiceWorkerDatabase::STATUS_OK) {
1705 original_task_runner->PostTask(
1706 FROM_HERE,
1707 base::Bind(callback,
1708 ServiceWorkerDatabase::RegistrationData(),
1709 ResourceList(),
1710 status));
1711 return;
1714 // Find one with an exact matching scope.
1715 ServiceWorkerDatabase::RegistrationData data;
1716 ResourceList resources;
1717 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1718 for (RegistrationList::const_iterator it = registrations.begin();
1719 it != registrations.end(); ++it) {
1720 if (scope != it->scope)
1721 continue;
1722 status = database->ReadRegistration(it->registration_id, origin,
1723 &data, &resources);
1724 break; // We're done looping.
1727 original_task_runner->PostTask(
1728 FROM_HERE,
1729 base::Bind(callback, data, resources, status));
1732 void ServiceWorkerStorage::FindForIdInDB(
1733 ServiceWorkerDatabase* database,
1734 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1735 int64 registration_id,
1736 const GURL& origin,
1737 const FindInDBCallback& callback) {
1738 ServiceWorkerDatabase::RegistrationData data;
1739 ResourceList resources;
1740 ServiceWorkerDatabase::Status status =
1741 database->ReadRegistration(registration_id, origin, &data, &resources);
1742 original_task_runner->PostTask(
1743 FROM_HERE, base::Bind(callback, data, resources, status));
1746 void ServiceWorkerStorage::FindForIdOnlyInDB(
1747 ServiceWorkerDatabase* database,
1748 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1749 int64 registration_id,
1750 const FindInDBCallback& callback) {
1751 GURL origin;
1752 ServiceWorkerDatabase::Status status =
1753 database->ReadRegistrationOrigin(registration_id, &origin);
1754 if (status != ServiceWorkerDatabase::STATUS_OK) {
1755 original_task_runner->PostTask(
1756 FROM_HERE,
1757 base::Bind(callback, ServiceWorkerDatabase::RegistrationData(),
1758 ResourceList(), status));
1759 return;
1761 FindForIdInDB(database, original_task_runner, registration_id, origin,
1762 callback);
1765 void ServiceWorkerStorage::GetUserDataInDB(
1766 ServiceWorkerDatabase* database,
1767 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1768 int64 registration_id,
1769 const std::string& key,
1770 const GetUserDataInDBCallback& callback) {
1771 std::string data;
1772 ServiceWorkerDatabase::Status status =
1773 database->ReadUserData(registration_id, key, &data);
1774 original_task_runner->PostTask(
1775 FROM_HERE, base::Bind(callback, data, status));
1778 void ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB(
1779 ServiceWorkerDatabase* database,
1780 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1781 const std::string& key,
1782 const GetUserDataForAllRegistrationsInDBCallback& callback) {
1783 std::vector<std::pair<int64, std::string>> user_data;
1784 ServiceWorkerDatabase::Status status =
1785 database->ReadUserDataForAllRegistrations(key, &user_data);
1786 original_task_runner->PostTask(FROM_HERE,
1787 base::Bind(callback, user_data, status));
1790 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1791 ServiceWorkerDatabase* database,
1792 const std::set<GURL>& origins) {
1793 DCHECK(database);
1795 std::vector<int64> newly_purgeable_resources;
1796 database->DeleteAllDataForOrigins(origins, &newly_purgeable_resources);
1799 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1800 // is transient and it can get healed soon (e.g. IO error). To do that, the
1801 // database should not disable itself when an error occurs and the storage
1802 // controls it instead.
1803 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1804 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1805 // removed.
1806 if (state_ == DISABLED) {
1807 // Recovery process has already been scheduled.
1808 return;
1810 Disable();
1812 DVLOG(1) << "Schedule to delete the context and start over.";
1813 context_->ScheduleDeleteAndStartOver();
1816 void ServiceWorkerStorage::DidDeleteDatabase(
1817 const StatusCallback& callback,
1818 ServiceWorkerDatabase::Status status) {
1819 DCHECK_EQ(DISABLED, state_);
1820 if (status != ServiceWorkerDatabase::STATUS_OK) {
1821 // Give up the corruption recovery until the browser restarts.
1822 LOG(ERROR) << "Failed to delete the database: "
1823 << ServiceWorkerDatabase::StatusToString(status);
1824 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1825 ServiceWorkerMetrics::DELETE_DATABASE_ERROR);
1826 callback.Run(DatabaseStatusToStatusCode(status));
1827 return;
1829 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1831 // Delete the disk cache on the cache thread.
1832 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1833 // Deleting the directory could take a long time and restart could be delayed.
1834 // We should probably rename the directory and delete it later.
1835 PostTaskAndReplyWithResult(
1836 disk_cache_thread_.get(), FROM_HERE,
1837 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1838 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1839 weak_factory_.GetWeakPtr(), callback));
1842 void ServiceWorkerStorage::DidDeleteDiskCache(
1843 const StatusCallback& callback, bool result) {
1844 DCHECK_EQ(DISABLED, state_);
1845 if (!result) {
1846 // Give up the corruption recovery until the browser restarts.
1847 LOG(ERROR) << "Failed to delete the diskcache.";
1848 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1849 ServiceWorkerMetrics::DELETE_DISK_CACHE_ERROR);
1850 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1851 return;
1853 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1854 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1855 ServiceWorkerMetrics::DELETE_OK);
1856 callback.Run(SERVICE_WORKER_OK);
1859 } // namespace content