Move Service Worker %2f path validation logic from browser into Blink (1)
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blob8f0bba109dae3c7d4fdea1490d3f7c09c6e9eebb
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_disk_cache_migrator.h"
18 #include "content/browser/service_worker/service_worker_info.h"
19 #include "content/browser/service_worker/service_worker_registration.h"
20 #include "content/browser/service_worker/service_worker_version.h"
21 #include "content/common/service_worker/service_worker_types.h"
22 #include "content/common/service_worker/service_worker_utils.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("ScriptCache");
64 const base::FilePath::CharType kOldDiskCacheName[] = FILE_PATH_LITERAL("Cache");
66 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
67 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
69 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
70 ServiceWorkerDatabase::Status status) {
71 switch (status) {
72 case ServiceWorkerDatabase::STATUS_OK:
73 return SERVICE_WORKER_OK;
74 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
75 return SERVICE_WORKER_ERROR_NOT_FOUND;
76 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
77 NOTREACHED();
78 default:
79 return SERVICE_WORKER_ERROR_FAILED;
83 } // namespace
85 ServiceWorkerStorage::InitialData::InitialData()
86 : next_registration_id(kInvalidServiceWorkerRegistrationId),
87 next_version_id(kInvalidServiceWorkerVersionId),
88 next_resource_id(kInvalidServiceWorkerResourceId),
89 disk_cache_migration_needed(false),
90 old_disk_cache_deletion_needed(false) {
93 ServiceWorkerStorage::InitialData::~InitialData() {
96 ServiceWorkerStorage::
97 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
98 : registration_id(kInvalidServiceWorkerRegistrationId) {
101 ServiceWorkerStorage::
102 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
105 ServiceWorkerStorage::~ServiceWorkerStorage() {
106 ClearSessionOnlyOrigins();
107 weak_factory_.InvalidateWeakPtrs();
108 database_task_manager_->GetTaskRunner()->DeleteSoon(FROM_HERE,
109 database_.release());
112 // static
113 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
114 const base::FilePath& path,
115 const base::WeakPtr<ServiceWorkerContextCore>& context,
116 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
117 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
118 storage::QuotaManagerProxy* quota_manager_proxy,
119 storage::SpecialStoragePolicy* special_storage_policy) {
120 return make_scoped_ptr(new ServiceWorkerStorage(path,
121 context,
122 database_task_manager.Pass(),
123 disk_cache_thread,
124 quota_manager_proxy,
125 special_storage_policy));
128 // static
129 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
130 const base::WeakPtr<ServiceWorkerContextCore>& context,
131 ServiceWorkerStorage* old_storage) {
132 return make_scoped_ptr(
133 new ServiceWorkerStorage(old_storage->path_,
134 context,
135 old_storage->database_task_manager_->Clone(),
136 old_storage->disk_cache_thread_,
137 old_storage->quota_manager_proxy_.get(),
138 old_storage->special_storage_policy_.get()));
141 void ServiceWorkerStorage::FindRegistrationForDocument(
142 const GURL& document_url,
143 const FindRegistrationCallback& callback) {
144 DCHECK(!document_url.has_ref());
145 if (!LazyInitialize(base::Bind(
146 &ServiceWorkerStorage::FindRegistrationForDocument,
147 weak_factory_.GetWeakPtr(), document_url, callback))) {
148 if (state_ != INITIALIZING) {
149 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
150 SERVICE_WORKER_ERROR_FAILED, callback);
152 TRACE_EVENT_INSTANT1(
153 "ServiceWorker",
154 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
155 TRACE_EVENT_SCOPE_THREAD,
156 "URL", document_url.spec());
157 return;
159 DCHECK_EQ(INITIALIZED, state_);
161 // See if there are any stored registrations for the origin.
162 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
163 // Look for something currently being installed.
164 scoped_refptr<ServiceWorkerRegistration> installing_registration =
165 FindInstallingRegistrationForDocument(document_url);
166 ServiceWorkerStatusCode status = installing_registration
167 ? SERVICE_WORKER_OK
168 : SERVICE_WORKER_ERROR_NOT_FOUND;
169 TRACE_EVENT_INSTANT2(
170 "ServiceWorker",
171 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
172 TRACE_EVENT_SCOPE_THREAD,
173 "URL", document_url.spec(),
174 "Status", ServiceWorkerStatusToString(status));
175 CompleteFindNow(installing_registration,
176 status,
177 callback);
178 return;
181 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
182 // callback id.
183 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
184 TRACE_EVENT_ASYNC_BEGIN1(
185 "ServiceWorker",
186 "ServiceWorkerStorage::FindRegistrationForDocument",
187 callback_id,
188 "URL", document_url.spec());
189 database_task_manager_->GetTaskRunner()->PostTask(
190 FROM_HERE,
191 base::Bind(
192 &FindForDocumentInDB,
193 database_.get(),
194 base::ThreadTaskRunnerHandle::Get(),
195 document_url,
196 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
197 weak_factory_.GetWeakPtr(),
198 document_url,
199 callback,
200 callback_id)));
203 void ServiceWorkerStorage::FindRegistrationForPattern(
204 const GURL& scope,
205 const FindRegistrationCallback& callback) {
206 if (!LazyInitialize(base::Bind(
207 &ServiceWorkerStorage::FindRegistrationForPattern,
208 weak_factory_.GetWeakPtr(), scope, callback))) {
209 if (state_ != INITIALIZING) {
210 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
211 SERVICE_WORKER_ERROR_FAILED, callback);
213 return;
215 DCHECK_EQ(INITIALIZED, state_);
217 // See if there are any stored registrations for the origin.
218 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
219 // Look for something currently being installed.
220 scoped_refptr<ServiceWorkerRegistration> installing_registration =
221 FindInstallingRegistrationForPattern(scope);
222 CompleteFindSoon(FROM_HERE, installing_registration,
223 installing_registration ? SERVICE_WORKER_OK
224 : SERVICE_WORKER_ERROR_NOT_FOUND,
225 callback);
226 return;
229 database_task_manager_->GetTaskRunner()->PostTask(
230 FROM_HERE,
231 base::Bind(
232 &FindForPatternInDB,
233 database_.get(),
234 base::ThreadTaskRunnerHandle::Get(),
235 scope,
236 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
237 weak_factory_.GetWeakPtr(),
238 scope,
239 callback)));
242 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
243 const GURL& scope) {
244 if (state_ != INITIALIZED)
245 return nullptr;
246 for (const auto& registration : uninstalling_registrations_) {
247 if (registration.second->pattern() == scope) {
248 DCHECK(registration.second->is_uninstalling());
249 return registration.second.get();
252 return nullptr;
255 void ServiceWorkerStorage::FindRegistrationForId(
256 int64 registration_id,
257 const GURL& origin,
258 const FindRegistrationCallback& callback) {
259 if (!LazyInitialize(base::Bind(
260 &ServiceWorkerStorage::FindRegistrationForId,
261 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
262 if (state_ != INITIALIZING) {
263 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
264 SERVICE_WORKER_ERROR_FAILED, callback);
266 return;
268 DCHECK_EQ(INITIALIZED, state_);
270 // See if there are any stored registrations for the origin.
271 if (!ContainsKey(registered_origins_, origin)) {
272 // Look for something currently being installed.
273 scoped_refptr<ServiceWorkerRegistration> installing_registration =
274 FindInstallingRegistrationForId(registration_id);
275 CompleteFindNow(installing_registration,
276 installing_registration ? SERVICE_WORKER_OK
277 : SERVICE_WORKER_ERROR_NOT_FOUND,
278 callback);
279 return;
282 scoped_refptr<ServiceWorkerRegistration> registration =
283 context_->GetLiveRegistration(registration_id);
284 if (registration) {
285 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
286 return;
289 database_task_manager_->GetTaskRunner()->PostTask(
290 FROM_HERE,
291 base::Bind(&FindForIdInDB,
292 database_.get(),
293 base::ThreadTaskRunnerHandle::Get(),
294 registration_id,
295 origin,
296 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
297 weak_factory_.GetWeakPtr(),
298 callback)));
301 void ServiceWorkerStorage::FindRegistrationForIdOnly(
302 int64 registration_id,
303 const FindRegistrationCallback& callback) {
304 if (!LazyInitialize(
305 base::Bind(&ServiceWorkerStorage::FindRegistrationForIdOnly,
306 weak_factory_.GetWeakPtr(), registration_id, callback))) {
307 if (state_ != INITIALIZING) {
308 CompleteFindNow(nullptr, SERVICE_WORKER_ERROR_FAILED, callback);
310 return;
312 DCHECK_EQ(INITIALIZED, state_);
314 scoped_refptr<ServiceWorkerRegistration> registration =
315 context_->GetLiveRegistration(registration_id);
316 if (registration) {
317 // Delegate to FindRegistrationForId to make sure the same subset of live
318 // registrations is returned.
319 // TODO(mek): CompleteFindNow should really do all the required checks, so
320 // calling that directly here should be enough.
321 FindRegistrationForId(registration_id, registration->pattern().GetOrigin(),
322 callback);
323 return;
326 database_task_manager_->GetTaskRunner()->PostTask(
327 FROM_HERE,
328 base::Bind(&FindForIdOnlyInDB,
329 database_.get(),
330 base::ThreadTaskRunnerHandle::Get(),
331 registration_id,
332 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
333 weak_factory_.GetWeakPtr(),
334 callback)));
337 void ServiceWorkerStorage::GetRegistrationsForOrigin(
338 const GURL& origin,
339 const GetRegistrationsCallback& callback) {
340 if (!LazyInitialize(base::Bind(
341 &ServiceWorkerStorage::GetRegistrationsForOrigin,
342 weak_factory_.GetWeakPtr(), origin, callback))) {
343 if (state_ != INITIALIZING) {
344 RunSoon(
345 FROM_HERE,
346 base::Bind(callback,
347 std::vector<scoped_refptr<ServiceWorkerRegistration>>()));
349 return;
351 DCHECK_EQ(INITIALIZED, state_);
353 RegistrationList* registrations = new RegistrationList;
354 std::vector<ResourceList>* resource_lists = new std::vector<ResourceList>;
355 PostTaskAndReplyWithResult(
356 database_task_manager_->GetTaskRunner(), FROM_HERE,
357 base::Bind(&ServiceWorkerDatabase::GetRegistrationsForOrigin,
358 base::Unretained(database_.get()), origin, registrations,
359 resource_lists),
360 base::Bind(&ServiceWorkerStorage::DidGetRegistrations,
361 weak_factory_.GetWeakPtr(), callback,
362 base::Owned(registrations), base::Owned(resource_lists),
363 origin));
366 void ServiceWorkerStorage::GetAllRegistrationsInfos(
367 const GetRegistrationsInfosCallback& callback) {
368 if (!LazyInitialize(
369 base::Bind(&ServiceWorkerStorage::GetAllRegistrationsInfos,
370 weak_factory_.GetWeakPtr(), callback))) {
371 if (state_ != INITIALIZING) {
372 RunSoon(FROM_HERE, base::Bind(
373 callback, std::vector<ServiceWorkerRegistrationInfo>()));
375 return;
377 DCHECK_EQ(INITIALIZED, state_);
379 RegistrationList* registrations = new RegistrationList;
380 PostTaskAndReplyWithResult(
381 database_task_manager_->GetTaskRunner(), FROM_HERE,
382 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
383 base::Unretained(database_.get()), registrations),
384 base::Bind(&ServiceWorkerStorage::DidGetRegistrationsInfos,
385 weak_factory_.GetWeakPtr(), callback,
386 base::Owned(registrations), GURL()));
389 void ServiceWorkerStorage::StoreRegistration(
390 ServiceWorkerRegistration* registration,
391 ServiceWorkerVersion* version,
392 const StatusCallback& callback) {
393 DCHECK(registration);
394 DCHECK(version);
396 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
397 if (IsDisabled()) {
398 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
399 return;
402 ServiceWorkerDatabase::RegistrationData data;
403 data.registration_id = registration->id();
404 data.scope = registration->pattern();
405 data.script = version->script_url();
406 data.has_fetch_handler = true;
407 data.version_id = version->version_id();
408 data.last_update_check = registration->last_update_check();
409 data.is_active = (version == registration->active_version());
411 ResourceList resources;
412 version->script_cache_map()->GetResources(&resources);
414 if (resources.empty()) {
415 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
416 return;
419 uint64 resources_total_size_bytes = 0;
420 for (const auto& resource : resources) {
421 resources_total_size_bytes += resource.size_bytes;
423 data.resources_total_size_bytes = resources_total_size_bytes;
425 if (!has_checked_for_stale_resources_)
426 DeleteStaleResources();
428 database_task_manager_->GetTaskRunner()->PostTask(
429 FROM_HERE,
430 base::Bind(&WriteRegistrationInDB,
431 database_.get(),
432 base::ThreadTaskRunnerHandle::Get(),
433 data,
434 resources,
435 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
436 weak_factory_.GetWeakPtr(),
437 callback,
438 data)));
440 registration->set_is_deleted(false);
443 void ServiceWorkerStorage::UpdateToActiveState(
444 ServiceWorkerRegistration* registration,
445 const StatusCallback& callback) {
446 DCHECK(registration);
448 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
449 if (IsDisabled()) {
450 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
451 return;
454 PostTaskAndReplyWithResult(
455 database_task_manager_->GetTaskRunner(),
456 FROM_HERE,
457 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
458 base::Unretained(database_.get()),
459 registration->id(),
460 registration->pattern().GetOrigin()),
461 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
462 weak_factory_.GetWeakPtr(),
463 callback));
466 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
467 ServiceWorkerRegistration* registration) {
468 DCHECK(registration);
470 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
471 if (IsDisabled())
472 return;
474 database_task_manager_->GetTaskRunner()->PostTask(
475 FROM_HERE,
476 base::Bind(
477 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
478 base::Unretained(database_.get()),
479 registration->id(),
480 registration->pattern().GetOrigin(),
481 registration->last_update_check()));
484 void ServiceWorkerStorage::DeleteRegistration(
485 int64 registration_id,
486 const GURL& origin,
487 const StatusCallback& callback) {
488 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
489 if (IsDisabled()) {
490 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
491 return;
494 if (!has_checked_for_stale_resources_)
495 DeleteStaleResources();
497 DidDeleteRegistrationParams params;
498 params.registration_id = registration_id;
499 params.origin = origin;
500 params.callback = callback;
502 database_task_manager_->GetTaskRunner()->PostTask(
503 FROM_HERE,
504 base::Bind(&DeleteRegistrationFromDB,
505 database_.get(),
506 base::ThreadTaskRunnerHandle::Get(),
507 registration_id,
508 origin,
509 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
510 weak_factory_.GetWeakPtr(),
511 params)));
513 // The registration should no longer be findable.
514 pending_deletions_.insert(registration_id);
515 ServiceWorkerRegistration* registration =
516 context_->GetLiveRegistration(registration_id);
517 if (registration)
518 registration->set_is_deleted(true);
521 scoped_ptr<ServiceWorkerResponseReader>
522 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
523 return make_scoped_ptr(
524 new ServiceWorkerResponseReader(response_id, disk_cache()));
527 scoped_ptr<ServiceWorkerResponseWriter>
528 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
529 return make_scoped_ptr(
530 new ServiceWorkerResponseWriter(response_id, disk_cache()));
533 scoped_ptr<ServiceWorkerResponseMetadataWriter>
534 ServiceWorkerStorage::CreateResponseMetadataWriter(int64 response_id) {
535 return make_scoped_ptr(
536 new ServiceWorkerResponseMetadataWriter(response_id, disk_cache()));
539 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
540 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
541 DCHECK_EQ(INITIALIZED, state_);
543 if (!has_checked_for_stale_resources_)
544 DeleteStaleResources();
546 database_task_manager_->GetTaskRunner()->PostTask(
547 FROM_HERE,
548 base::Bind(base::IgnoreResult(
549 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
550 base::Unretained(database_.get()),
551 std::set<int64>(&id, &id + 1)));
554 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
555 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
556 database_task_manager_->GetTaskRunner()->PostTask(
557 FROM_HERE,
558 base::Bind(base::IgnoreResult(
559 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
560 base::Unretained(database_.get()),
561 std::set<int64>(&id, &id + 1)));
562 StartPurgingResources(std::vector<int64>(1, id));
565 void ServiceWorkerStorage::StoreUserData(
566 int64 registration_id,
567 const GURL& origin,
568 const std::string& key,
569 const std::string& data,
570 const StatusCallback& callback) {
571 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
572 if (IsDisabled()) {
573 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
574 return;
577 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
578 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
579 return;
582 PostTaskAndReplyWithResult(
583 database_task_manager_->GetTaskRunner(),
584 FROM_HERE,
585 base::Bind(&ServiceWorkerDatabase::WriteUserData,
586 base::Unretained(database_.get()),
587 registration_id, origin, key, data),
588 base::Bind(&ServiceWorkerStorage::DidStoreUserData,
589 weak_factory_.GetWeakPtr(),
590 callback));
593 void ServiceWorkerStorage::GetUserData(
594 int64 registration_id,
595 const std::string& key,
596 const GetUserDataCallback& callback) {
597 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
598 if (IsDisabled()) {
599 RunSoon(FROM_HERE,
600 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
601 return;
604 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
605 RunSoon(FROM_HERE,
606 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
607 return;
610 database_task_manager_->GetTaskRunner()->PostTask(
611 FROM_HERE,
612 base::Bind(&ServiceWorkerStorage::GetUserDataInDB,
613 database_.get(),
614 base::ThreadTaskRunnerHandle::Get(),
615 registration_id,
616 key,
617 base::Bind(&ServiceWorkerStorage::DidGetUserData,
618 weak_factory_.GetWeakPtr(),
619 callback)));
622 void ServiceWorkerStorage::ClearUserData(
623 int64 registration_id,
624 const std::string& key,
625 const StatusCallback& callback) {
626 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
627 if (IsDisabled()) {
628 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
629 return;
632 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
633 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
634 return;
637 PostTaskAndReplyWithResult(
638 database_task_manager_->GetTaskRunner(),
639 FROM_HERE,
640 base::Bind(&ServiceWorkerDatabase::DeleteUserData,
641 base::Unretained(database_.get()),
642 registration_id, key),
643 base::Bind(&ServiceWorkerStorage::DidDeleteUserData,
644 weak_factory_.GetWeakPtr(),
645 callback));
648 void ServiceWorkerStorage::GetUserDataForAllRegistrations(
649 const std::string& key,
650 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
651 callback) {
652 if (!LazyInitialize(
653 base::Bind(&ServiceWorkerStorage::GetUserDataForAllRegistrations,
654 weak_factory_.GetWeakPtr(), key, callback))) {
655 if (state_ != INITIALIZING) {
656 RunSoon(FROM_HERE,
657 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
658 SERVICE_WORKER_ERROR_FAILED));
660 return;
662 DCHECK_EQ(INITIALIZED, state_);
664 if (key.empty()) {
665 RunSoon(FROM_HERE,
666 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
667 SERVICE_WORKER_ERROR_FAILED));
668 return;
671 database_task_manager_->GetTaskRunner()->PostTask(
672 FROM_HERE,
673 base::Bind(
674 &ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB,
675 database_.get(),
676 base::ThreadTaskRunnerHandle::Get(),
677 key,
678 base::Bind(&ServiceWorkerStorage::DidGetUserDataForAllRegistrations,
679 weak_factory_.GetWeakPtr(),
680 callback)));
683 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
684 Disable();
686 // Delete the database on the database thread.
687 PostTaskAndReplyWithResult(
688 database_task_manager_->GetTaskRunner(), FROM_HERE,
689 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
690 base::Unretained(database_.get())),
691 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
692 weak_factory_.GetWeakPtr(), callback));
695 int64 ServiceWorkerStorage::NewRegistrationId() {
696 if (state_ == DISABLED)
697 return kInvalidServiceWorkerRegistrationId;
698 DCHECK_EQ(INITIALIZED, state_);
699 return next_registration_id_++;
702 int64 ServiceWorkerStorage::NewVersionId() {
703 if (state_ == DISABLED)
704 return kInvalidServiceWorkerVersionId;
705 DCHECK_EQ(INITIALIZED, state_);
706 return next_version_id_++;
709 int64 ServiceWorkerStorage::NewResourceId() {
710 if (state_ == DISABLED)
711 return kInvalidServiceWorkerResourceId;
712 DCHECK_EQ(INITIALIZED, state_);
713 return next_resource_id_++;
716 void ServiceWorkerStorage::NotifyInstallingRegistration(
717 ServiceWorkerRegistration* registration) {
718 DCHECK(installing_registrations_.find(registration->id()) ==
719 installing_registrations_.end());
720 installing_registrations_[registration->id()] = registration;
723 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
724 ServiceWorkerRegistration* registration,
725 ServiceWorkerVersion* version,
726 ServiceWorkerStatusCode status) {
727 installing_registrations_.erase(registration->id());
728 if (status != SERVICE_WORKER_OK && version) {
729 ResourceList resources;
730 version->script_cache_map()->GetResources(&resources);
732 std::set<int64> ids;
733 for (const auto& resource : resources)
734 ids.insert(resource.resource_id);
736 database_task_manager_->GetTaskRunner()->PostTask(
737 FROM_HERE,
738 base::Bind(base::IgnoreResult(
739 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
740 base::Unretained(database_.get()),
741 ids));
745 void ServiceWorkerStorage::NotifyUninstallingRegistration(
746 ServiceWorkerRegistration* registration) {
747 DCHECK(uninstalling_registrations_.find(registration->id()) ==
748 uninstalling_registrations_.end());
749 uninstalling_registrations_[registration->id()] = registration;
752 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
753 ServiceWorkerRegistration* registration) {
754 uninstalling_registrations_.erase(registration->id());
757 void ServiceWorkerStorage::Disable() {
758 state_ = DISABLED;
759 if (disk_cache_)
760 disk_cache_->Disable();
763 bool ServiceWorkerStorage::IsDisabled() const {
764 return state_ == DISABLED;
767 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
768 if (!has_checked_for_stale_resources_)
769 DeleteStaleResources();
770 StartPurgingResources(resources);
773 ServiceWorkerStorage::ServiceWorkerStorage(
774 const base::FilePath& path,
775 base::WeakPtr<ServiceWorkerContextCore> context,
776 scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
777 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
778 storage::QuotaManagerProxy* quota_manager_proxy,
779 storage::SpecialStoragePolicy* special_storage_policy)
780 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
781 next_version_id_(kInvalidServiceWorkerVersionId),
782 next_resource_id_(kInvalidServiceWorkerResourceId),
783 state_(UNINITIALIZED),
784 path_(path),
785 context_(context),
786 database_task_manager_(database_task_manager.Pass()),
787 disk_cache_thread_(disk_cache_thread),
788 quota_manager_proxy_(quota_manager_proxy),
789 special_storage_policy_(special_storage_policy),
790 disk_cache_migration_needed_(false),
791 old_disk_cache_deletion_needed_(false),
792 is_purge_pending_(false),
793 has_checked_for_stale_resources_(false),
794 weak_factory_(this) {
795 DCHECK(context_);
796 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
799 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
800 if (path_.empty())
801 return base::FilePath();
802 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
803 .Append(kDatabaseName);
806 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
807 if (path_.empty())
808 return base::FilePath();
809 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
810 .Append(kDiskCacheName);
813 base::FilePath ServiceWorkerStorage::GetOldDiskCachePath() {
814 if (path_.empty())
815 return base::FilePath();
816 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
817 .Append(kOldDiskCacheName);
820 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
821 switch (state_) {
822 case INITIALIZED:
823 return true;
824 case DISABLED:
825 return false;
826 case INITIALIZING:
827 pending_tasks_.push_back(callback);
828 return false;
829 case UNINITIALIZED:
830 pending_tasks_.push_back(callback);
831 // Fall-through.
834 state_ = INITIALIZING;
835 database_task_manager_->GetTaskRunner()->PostTask(
836 FROM_HERE,
837 base::Bind(&ReadInitialDataFromDB,
838 database_.get(),
839 base::ThreadTaskRunnerHandle::Get(),
840 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
841 weak_factory_.GetWeakPtr())));
842 return false;
845 void ServiceWorkerStorage::DidReadInitialData(
846 scoped_ptr<InitialData> data,
847 ServiceWorkerDatabase::Status status) {
848 DCHECK(data);
849 DCHECK_EQ(INITIALIZING, state_);
851 if (status == ServiceWorkerDatabase::STATUS_OK) {
852 next_registration_id_ = data->next_registration_id;
853 next_version_id_ = data->next_version_id;
854 next_resource_id_ = data->next_resource_id;
855 registered_origins_.swap(data->origins);
856 disk_cache_migration_needed_ = data->disk_cache_migration_needed;
857 old_disk_cache_deletion_needed_ = data->old_disk_cache_deletion_needed;
858 state_ = INITIALIZED;
859 } else {
860 DVLOG(2) << "Failed to initialize: "
861 << ServiceWorkerDatabase::StatusToString(status);
862 ScheduleDeleteAndStartOver();
865 for (const auto& task : pending_tasks_)
866 RunSoon(FROM_HERE, task);
867 pending_tasks_.clear();
870 void ServiceWorkerStorage::DidFindRegistrationForDocument(
871 const GURL& document_url,
872 const FindRegistrationCallback& callback,
873 int64 callback_id,
874 const ServiceWorkerDatabase::RegistrationData& data,
875 const ResourceList& resources,
876 ServiceWorkerDatabase::Status status) {
877 if (status == ServiceWorkerDatabase::STATUS_OK) {
878 ReturnFoundRegistration(callback, data, resources);
879 TRACE_EVENT_ASYNC_END1(
880 "ServiceWorker",
881 "ServiceWorkerStorage::FindRegistrationForDocument",
882 callback_id,
883 "Status", ServiceWorkerDatabase::StatusToString(status));
884 return;
887 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
888 // Look for something currently being installed.
889 scoped_refptr<ServiceWorkerRegistration> installing_registration =
890 FindInstallingRegistrationForDocument(document_url);
891 ServiceWorkerStatusCode installing_status =
892 installing_registration ? SERVICE_WORKER_OK
893 : SERVICE_WORKER_ERROR_NOT_FOUND;
894 callback.Run(installing_status, installing_registration);
895 TRACE_EVENT_ASYNC_END2(
896 "ServiceWorker",
897 "ServiceWorkerStorage::FindRegistrationForDocument",
898 callback_id,
899 "Status", ServiceWorkerDatabase::StatusToString(status),
900 "Info",
901 (installing_status == SERVICE_WORKER_OK) ?
902 "Installing registration is found" :
903 "Any registrations are not found");
904 return;
907 ScheduleDeleteAndStartOver();
908 callback.Run(DatabaseStatusToStatusCode(status),
909 scoped_refptr<ServiceWorkerRegistration>());
910 TRACE_EVENT_ASYNC_END1(
911 "ServiceWorker",
912 "ServiceWorkerStorage::FindRegistrationForDocument",
913 callback_id,
914 "Status", ServiceWorkerDatabase::StatusToString(status));
917 void ServiceWorkerStorage::DidFindRegistrationForPattern(
918 const GURL& scope,
919 const FindRegistrationCallback& callback,
920 const ServiceWorkerDatabase::RegistrationData& data,
921 const ResourceList& resources,
922 ServiceWorkerDatabase::Status status) {
923 if (status == ServiceWorkerDatabase::STATUS_OK) {
924 ReturnFoundRegistration(callback, data, resources);
925 return;
928 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
929 scoped_refptr<ServiceWorkerRegistration> installing_registration =
930 FindInstallingRegistrationForPattern(scope);
931 callback.Run(installing_registration ? SERVICE_WORKER_OK
932 : SERVICE_WORKER_ERROR_NOT_FOUND,
933 installing_registration);
934 return;
937 ScheduleDeleteAndStartOver();
938 callback.Run(DatabaseStatusToStatusCode(status),
939 scoped_refptr<ServiceWorkerRegistration>());
942 void ServiceWorkerStorage::DidFindRegistrationForId(
943 const FindRegistrationCallback& callback,
944 const ServiceWorkerDatabase::RegistrationData& data,
945 const ResourceList& resources,
946 ServiceWorkerDatabase::Status status) {
947 if (status == ServiceWorkerDatabase::STATUS_OK) {
948 ReturnFoundRegistration(callback, data, resources);
949 return;
952 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
953 // TODO(nhiroki): Find a registration in |installing_registrations_|.
954 callback.Run(DatabaseStatusToStatusCode(status),
955 scoped_refptr<ServiceWorkerRegistration>());
956 return;
959 ScheduleDeleteAndStartOver();
960 callback.Run(DatabaseStatusToStatusCode(status),
961 scoped_refptr<ServiceWorkerRegistration>());
964 void ServiceWorkerStorage::ReturnFoundRegistration(
965 const FindRegistrationCallback& callback,
966 const ServiceWorkerDatabase::RegistrationData& data,
967 const ResourceList& resources) {
968 DCHECK(!resources.empty());
969 scoped_refptr<ServiceWorkerRegistration> registration =
970 GetOrCreateRegistration(data, resources);
971 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
974 void ServiceWorkerStorage::DidGetRegistrations(
975 const GetRegistrationsCallback& callback,
976 RegistrationList* registration_data_list,
977 std::vector<ResourceList>* resources_list,
978 const GURL& origin_filter,
979 ServiceWorkerDatabase::Status status) {
980 DCHECK(registration_data_list);
981 DCHECK(resources_list);
983 if (status != ServiceWorkerDatabase::STATUS_OK &&
984 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
985 ScheduleDeleteAndStartOver();
986 callback.Run(std::vector<scoped_refptr<ServiceWorkerRegistration>>());
987 return;
990 // Add all stored registrations.
991 std::set<int64> registration_ids;
992 std::vector<scoped_refptr<ServiceWorkerRegistration>> registrations;
993 size_t index = 0;
994 for (const auto& registration_data : *registration_data_list) {
995 registration_ids.insert(registration_data.registration_id);
996 registrations.push_back(GetOrCreateRegistration(
997 registration_data, resources_list->at(index++)));
1000 // Add unstored registrations that are being installed.
1001 for (const auto& registration : installing_registrations_) {
1002 if ((!origin_filter.is_valid() ||
1003 registration.second->pattern().GetOrigin() == origin_filter) &&
1004 registration_ids.insert(registration.first).second) {
1005 registrations.push_back(registration.second);
1009 callback.Run(registrations);
1012 void ServiceWorkerStorage::DidGetRegistrationsInfos(
1013 const GetRegistrationsInfosCallback& callback,
1014 RegistrationList* registration_data_list,
1015 const GURL& origin_filter,
1016 ServiceWorkerDatabase::Status status) {
1017 DCHECK(registration_data_list);
1018 if (status != ServiceWorkerDatabase::STATUS_OK &&
1019 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1020 ScheduleDeleteAndStartOver();
1021 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
1022 return;
1025 // Add all stored registrations.
1026 std::set<int64> pushed_registrations;
1027 std::vector<ServiceWorkerRegistrationInfo> infos;
1028 for (const auto& registration_data : *registration_data_list) {
1029 const bool inserted =
1030 pushed_registrations.insert(registration_data.registration_id).second;
1031 DCHECK(inserted);
1033 ServiceWorkerRegistration* registration =
1034 context_->GetLiveRegistration(registration_data.registration_id);
1035 if (registration) {
1036 infos.push_back(registration->GetInfo());
1037 continue;
1040 ServiceWorkerRegistrationInfo info;
1041 info.pattern = registration_data.scope;
1042 info.registration_id = registration_data.registration_id;
1043 info.stored_version_size_bytes =
1044 registration_data.resources_total_size_bytes;
1045 if (ServiceWorkerVersion* version =
1046 context_->GetLiveVersion(registration_data.version_id)) {
1047 if (registration_data.is_active)
1048 info.active_version = version->GetInfo();
1049 else
1050 info.waiting_version = version->GetInfo();
1051 infos.push_back(info);
1052 continue;
1055 if (registration_data.is_active) {
1056 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
1057 info.active_version.script_url = registration_data.script;
1058 info.active_version.version_id = registration_data.version_id;
1059 info.active_version.registration_id = registration_data.registration_id;
1060 } else {
1061 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
1062 info.waiting_version.script_url = registration_data.script;
1063 info.waiting_version.version_id = registration_data.version_id;
1064 info.waiting_version.registration_id = registration_data.registration_id;
1066 infos.push_back(info);
1069 // Add unstored registrations that are being installed.
1070 for (const auto& registration : installing_registrations_) {
1071 if ((!origin_filter.is_valid() ||
1072 registration.second->pattern().GetOrigin() == origin_filter) &&
1073 pushed_registrations.insert(registration.first).second) {
1074 infos.push_back(registration.second->GetInfo());
1078 callback.Run(infos);
1081 void ServiceWorkerStorage::DidStoreRegistration(
1082 const StatusCallback& callback,
1083 const ServiceWorkerDatabase::RegistrationData& new_version,
1084 const GURL& origin,
1085 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1086 const std::vector<int64>& newly_purgeable_resources,
1087 ServiceWorkerDatabase::Status status) {
1088 if (status != ServiceWorkerDatabase::STATUS_OK) {
1089 ScheduleDeleteAndStartOver();
1090 callback.Run(DatabaseStatusToStatusCode(status));
1091 return;
1093 registered_origins_.insert(origin);
1095 scoped_refptr<ServiceWorkerRegistration> registration =
1096 context_->GetLiveRegistration(new_version.registration_id);
1097 if (registration) {
1098 registration->set_resources_total_size_bytes(
1099 new_version.resources_total_size_bytes);
1101 if (quota_manager_proxy_) {
1102 // Can be nullptr in tests.
1103 quota_manager_proxy_->NotifyStorageModified(
1104 storage::QuotaClient::kServiceWorker,
1105 origin,
1106 storage::StorageType::kStorageTypeTemporary,
1107 new_version.resources_total_size_bytes -
1108 deleted_version.resources_total_size_bytes);
1111 callback.Run(SERVICE_WORKER_OK);
1113 if (!context_->GetLiveVersion(deleted_version.version_id))
1114 StartPurgingResources(newly_purgeable_resources);
1117 void ServiceWorkerStorage::DidUpdateToActiveState(
1118 const StatusCallback& callback,
1119 ServiceWorkerDatabase::Status status) {
1120 if (status != ServiceWorkerDatabase::STATUS_OK &&
1121 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1122 ScheduleDeleteAndStartOver();
1124 callback.Run(DatabaseStatusToStatusCode(status));
1127 void ServiceWorkerStorage::DidDeleteRegistration(
1128 const DidDeleteRegistrationParams& params,
1129 bool origin_is_deletable,
1130 const ServiceWorkerDatabase::RegistrationData& deleted_version,
1131 const std::vector<int64>& newly_purgeable_resources,
1132 ServiceWorkerDatabase::Status status) {
1133 pending_deletions_.erase(params.registration_id);
1134 if (status != ServiceWorkerDatabase::STATUS_OK) {
1135 ScheduleDeleteAndStartOver();
1136 params.callback.Run(DatabaseStatusToStatusCode(status));
1137 return;
1139 if (quota_manager_proxy_) {
1140 // Can be nullptr in tests.
1141 quota_manager_proxy_->NotifyStorageModified(
1142 storage::QuotaClient::kServiceWorker,
1143 params.origin,
1144 storage::StorageType::kStorageTypeTemporary,
1145 -deleted_version.resources_total_size_bytes);
1147 if (origin_is_deletable)
1148 registered_origins_.erase(params.origin);
1149 params.callback.Run(SERVICE_WORKER_OK);
1151 if (!context_->GetLiveVersion(deleted_version.version_id))
1152 StartPurgingResources(newly_purgeable_resources);
1155 void ServiceWorkerStorage::DidStoreUserData(
1156 const StatusCallback& callback,
1157 ServiceWorkerDatabase::Status status) {
1158 // |status| can be NOT_FOUND when the associated registration did not exist in
1159 // the database. In the case, we don't have to schedule the corruption
1160 // recovery.
1161 if (status != ServiceWorkerDatabase::STATUS_OK &&
1162 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1163 ScheduleDeleteAndStartOver();
1165 callback.Run(DatabaseStatusToStatusCode(status));
1168 void ServiceWorkerStorage::DidGetUserData(
1169 const GetUserDataCallback& callback,
1170 const std::string& data,
1171 ServiceWorkerDatabase::Status status) {
1172 if (status != ServiceWorkerDatabase::STATUS_OK &&
1173 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
1174 ScheduleDeleteAndStartOver();
1176 callback.Run(data, DatabaseStatusToStatusCode(status));
1179 void ServiceWorkerStorage::DidDeleteUserData(
1180 const StatusCallback& callback,
1181 ServiceWorkerDatabase::Status status) {
1182 if (status != ServiceWorkerDatabase::STATUS_OK)
1183 ScheduleDeleteAndStartOver();
1184 callback.Run(DatabaseStatusToStatusCode(status));
1187 void ServiceWorkerStorage::DidGetUserDataForAllRegistrations(
1188 const GetUserDataForAllRegistrationsCallback& callback,
1189 const std::vector<std::pair<int64, std::string>>& user_data,
1190 ServiceWorkerDatabase::Status status) {
1191 if (status != ServiceWorkerDatabase::STATUS_OK)
1192 ScheduleDeleteAndStartOver();
1193 callback.Run(user_data, DatabaseStatusToStatusCode(status));
1196 scoped_refptr<ServiceWorkerRegistration>
1197 ServiceWorkerStorage::GetOrCreateRegistration(
1198 const ServiceWorkerDatabase::RegistrationData& data,
1199 const ResourceList& resources) {
1200 scoped_refptr<ServiceWorkerRegistration> registration =
1201 context_->GetLiveRegistration(data.registration_id);
1202 if (registration)
1203 return registration;
1205 registration = new ServiceWorkerRegistration(
1206 data.scope, data.registration_id, context_);
1207 registration->set_resources_total_size_bytes(data.resources_total_size_bytes);
1208 registration->set_last_update_check(data.last_update_check);
1209 if (pending_deletions_.find(data.registration_id) !=
1210 pending_deletions_.end()) {
1211 registration->set_is_deleted(true);
1213 scoped_refptr<ServiceWorkerVersion> version =
1214 context_->GetLiveVersion(data.version_id);
1215 if (!version) {
1216 version = new ServiceWorkerVersion(
1217 registration.get(), data.script, data.version_id, context_);
1218 version->SetStatus(data.is_active ?
1219 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1220 version->script_cache_map()->SetResources(resources);
1223 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1224 registration->SetActiveVersion(version);
1225 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1226 registration->SetWaitingVersion(version);
1227 else
1228 NOTREACHED();
1230 return registration;
1233 ServiceWorkerRegistration*
1234 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1235 const GURL& document_url) {
1236 DCHECK(!document_url.has_ref());
1238 LongestScopeMatcher matcher(document_url);
1239 ServiceWorkerRegistration* match = nullptr;
1241 // TODO(nhiroki): This searches over installing registrations linearly and it
1242 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1243 for (const auto& registration : installing_registrations_)
1244 if (matcher.MatchLongest(registration.second->pattern()))
1245 match = registration.second.get();
1246 return match;
1249 ServiceWorkerRegistration*
1250 ServiceWorkerStorage::FindInstallingRegistrationForPattern(const GURL& scope) {
1251 for (const auto& registration : installing_registrations_)
1252 if (registration.second->pattern() == scope)
1253 return registration.second.get();
1254 return nullptr;
1257 ServiceWorkerRegistration*
1258 ServiceWorkerStorage::FindInstallingRegistrationForId(int64 registration_id) {
1259 RegistrationRefsById::const_iterator found =
1260 installing_registrations_.find(registration_id);
1261 if (found == installing_registrations_.end())
1262 return nullptr;
1263 return found->second.get();
1266 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1267 DCHECK_EQ(INITIALIZED, state_);
1268 if (disk_cache_)
1269 return disk_cache_.get();
1271 disk_cache_ = ServiceWorkerDiskCache::CreateWithSimpleBackend();
1273 base::FilePath path = GetDiskCachePath();
1274 if (path.empty()) {
1275 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1276 net::CompletionCallback());
1277 DCHECK_EQ(net::OK, rv);
1278 return disk_cache_.get();
1281 if (disk_cache_migration_needed_) {
1282 // Defer the start of initialization until a migration is complete.
1283 disk_cache_->set_is_waiting_to_initialize(true);
1284 DCHECK(!disk_cache_migrator_);
1285 disk_cache_migrator_.reset(new ServiceWorkerDiskCacheMigrator(
1286 GetOldDiskCachePath(), GetDiskCachePath(), kMaxDiskCacheSize,
1287 disk_cache_thread_));
1288 disk_cache_migrator_->Start(
1289 base::Bind(&ServiceWorkerStorage::DidMigrateDiskCache,
1290 weak_factory_.GetWeakPtr()));
1291 return disk_cache_.get();
1294 if (old_disk_cache_deletion_needed_) {
1295 // Lazily delete the old diskcache.
1296 BrowserThread::PostAfterStartupTask(
1297 FROM_HERE, base::ThreadTaskRunnerHandle::Get(),
1298 base::Bind(&ServiceWorkerStorage::DeleteOldDiskCache,
1299 weak_factory_.GetWeakPtr()));
1302 ServiceWorkerMetrics::RecordDiskCacheMigrationResult(
1303 ServiceWorkerMetrics::MIGRATION_NOT_NECESSARY);
1305 InitializeDiskCache();
1306 return disk_cache_.get();
1309 void ServiceWorkerStorage::DidMigrateDiskCache(ServiceWorkerStatusCode status) {
1310 disk_cache_migrator_.reset();
1311 if (status != SERVICE_WORKER_OK) {
1312 OnDiskCacheMigrationFailed(
1313 ServiceWorkerMetrics::MIGRATION_ERROR_MIGRATION_FAILED);
1314 return;
1317 PostTaskAndReplyWithResult(
1318 database_task_manager_->GetTaskRunner(), FROM_HERE,
1319 base::Bind(&ServiceWorkerDatabase::SetDiskCacheMigrationNotNeeded,
1320 base::Unretained(database_.get())),
1321 base::Bind(&ServiceWorkerStorage::DidSetDiskCacheMigrationNotNeeded,
1322 weak_factory_.GetWeakPtr()));
1325 void ServiceWorkerStorage::DidSetDiskCacheMigrationNotNeeded(
1326 ServiceWorkerDatabase::Status status) {
1327 if (status != ServiceWorkerDatabase::STATUS_OK) {
1328 OnDiskCacheMigrationFailed(
1329 ServiceWorkerMetrics::MIGRATION_ERROR_UPDATE_DATABASE);
1330 return;
1333 // Lazily delete the old diskcache and update the database.
1334 BrowserThread::PostAfterStartupTask(
1335 FROM_HERE, base::ThreadTaskRunnerHandle::Get(),
1336 base::Bind(&ServiceWorkerStorage::DeleteOldDiskCache,
1337 weak_factory_.GetWeakPtr()));
1339 ServiceWorkerMetrics::RecordDiskCacheMigrationResult(
1340 ServiceWorkerMetrics::MIGRATION_OK);
1341 InitializeDiskCache();
1344 void ServiceWorkerStorage::OnDiskCacheMigrationFailed(
1345 ServiceWorkerMetrics::DiskCacheMigrationResult result) {
1346 DCHECK(ServiceWorkerMetrics::MIGRATION_ERROR_MIGRATION_FAILED == result ||
1347 ServiceWorkerMetrics::MIGRATION_ERROR_UPDATE_DATABASE == result)
1348 << result;
1349 ServiceWorkerMetrics::RecordDiskCacheMigrationResult(result);
1351 // Give up migration and recreate the whole storage.
1352 ScheduleDeleteAndStartOver();
1354 // Lazily delete the old diskcache. Don't have to update the database
1355 // because it will be deleted by DeleteAndStartOver.
1356 BrowserThread::PostAfterStartupTask(
1357 FROM_HERE, disk_cache_thread_.get(),
1358 base::Bind(base::IgnoreResult(&base::DeleteFile), GetOldDiskCachePath(),
1359 true));
1362 void ServiceWorkerStorage::InitializeDiskCache() {
1363 disk_cache_->set_is_waiting_to_initialize(false);
1364 int rv = disk_cache_->InitWithDiskBackend(
1365 GetDiskCachePath(), kMaxDiskCacheSize, false, disk_cache_thread_,
1366 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1367 weak_factory_.GetWeakPtr()));
1368 if (rv != net::ERR_IO_PENDING)
1369 OnDiskCacheInitialized(rv);
1372 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1373 if (rv != net::OK) {
1374 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1375 << net::ErrorToString(rv);
1376 ScheduleDeleteAndStartOver();
1378 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1381 void ServiceWorkerStorage::DeleteOldDiskCache() {
1382 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
1383 if (IsDisabled())
1384 return;
1385 database_task_manager_->GetTaskRunner()->PostTask(
1386 FROM_HERE, base::Bind(&ServiceWorkerStorage::DeleteOldDiskCacheInDB,
1387 database_.get(), GetOldDiskCachePath()));
1390 void ServiceWorkerStorage::StartPurgingResources(
1391 const std::vector<int64>& ids) {
1392 DCHECK(has_checked_for_stale_resources_);
1393 for (const auto& id : ids)
1394 purgeable_resource_ids_.push_back(id);
1395 ContinuePurgingResources();
1398 void ServiceWorkerStorage::StartPurgingResources(
1399 const ResourceList& resources) {
1400 DCHECK(has_checked_for_stale_resources_);
1401 for (const auto& resource : resources)
1402 purgeable_resource_ids_.push_back(resource.resource_id);
1403 ContinuePurgingResources();
1406 void ServiceWorkerStorage::ContinuePurgingResources() {
1407 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1408 return;
1410 // Do one at a time until we're done, use RunSoon to avoid recursion when
1411 // DoomEntry returns immediately.
1412 is_purge_pending_ = true;
1413 int64 id = purgeable_resource_ids_.front();
1414 purgeable_resource_ids_.pop_front();
1415 RunSoon(FROM_HERE,
1416 base::Bind(&ServiceWorkerStorage::PurgeResource,
1417 weak_factory_.GetWeakPtr(), id));
1420 void ServiceWorkerStorage::PurgeResource(int64 id) {
1421 DCHECK(is_purge_pending_);
1422 int rv = disk_cache()->DoomEntry(
1423 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1424 weak_factory_.GetWeakPtr(), id));
1425 if (rv != net::ERR_IO_PENDING)
1426 OnResourcePurged(id, rv);
1429 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1430 DCHECK(is_purge_pending_);
1431 is_purge_pending_ = false;
1433 ServiceWorkerMetrics::RecordPurgeResourceResult(rv);
1435 database_task_manager_->GetTaskRunner()->PostTask(
1436 FROM_HERE,
1437 base::Bind(base::IgnoreResult(
1438 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1439 base::Unretained(database_.get()),
1440 std::set<int64>(&id, &id + 1)));
1442 // Continue purging resources regardless of the previous result.
1443 ContinuePurgingResources();
1446 void ServiceWorkerStorage::DeleteStaleResources() {
1447 DCHECK(!has_checked_for_stale_resources_);
1448 has_checked_for_stale_resources_ = true;
1449 database_task_manager_->GetTaskRunner()->PostTask(
1450 FROM_HERE,
1451 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1452 database_.get(),
1453 base::ThreadTaskRunnerHandle::Get(),
1454 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1455 weak_factory_.GetWeakPtr())));
1458 void ServiceWorkerStorage::DidCollectStaleResources(
1459 const std::vector<int64>& stale_resource_ids,
1460 ServiceWorkerDatabase::Status status) {
1461 if (status != ServiceWorkerDatabase::STATUS_OK) {
1462 DCHECK_NE(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, status);
1463 ScheduleDeleteAndStartOver();
1464 return;
1466 StartPurgingResources(stale_resource_ids);
1469 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1470 // Can be null in tests.
1471 if (!special_storage_policy_)
1472 return;
1474 if (!special_storage_policy_->HasSessionOnlyOrigins())
1475 return;
1477 std::set<GURL> session_only_origins;
1478 for (const GURL& origin : registered_origins_) {
1479 if (special_storage_policy_->IsStorageSessionOnly(origin))
1480 session_only_origins.insert(origin);
1483 database_task_manager_->GetShutdownBlockingTaskRunner()->PostTask(
1484 FROM_HERE,
1485 base::Bind(&DeleteAllDataForOriginsFromDB,
1486 database_.get(),
1487 session_only_origins));
1490 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1491 ServiceWorkerDatabase* database,
1492 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1493 const GetResourcesCallback& callback) {
1494 std::set<int64> ids;
1495 ServiceWorkerDatabase::Status status =
1496 database->GetUncommittedResourceIds(&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 status = database->PurgeUncommittedResourceIds(ids);
1506 if (status != ServiceWorkerDatabase::STATUS_OK) {
1507 original_task_runner->PostTask(
1508 FROM_HERE,
1509 base::Bind(
1510 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1511 return;
1514 ids.clear();
1515 status = database->GetPurgeableResourceIds(&ids);
1516 original_task_runner->PostTask(
1517 FROM_HERE,
1518 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1521 void ServiceWorkerStorage::ReadInitialDataFromDB(
1522 ServiceWorkerDatabase* database,
1523 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1524 const InitializeCallback& callback) {
1525 DCHECK(database);
1526 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1527 new ServiceWorkerStorage::InitialData());
1529 ServiceWorkerDatabase::Status status =
1530 database->GetNextAvailableIds(&data->next_registration_id,
1531 &data->next_version_id,
1532 &data->next_resource_id);
1533 if (status != ServiceWorkerDatabase::STATUS_OK) {
1534 original_task_runner->PostTask(
1535 FROM_HERE, base::Bind(callback, base::Passed(data.Pass()), status));
1536 return;
1539 status =
1540 database->IsDiskCacheMigrationNeeded(&data->disk_cache_migration_needed);
1541 if (status != ServiceWorkerDatabase::STATUS_OK) {
1542 original_task_runner->PostTask(
1543 FROM_HERE, base::Bind(callback, base::Passed(data.Pass()), status));
1544 return;
1547 status = database->IsOldDiskCacheDeletionNeeded(
1548 &data->old_disk_cache_deletion_needed);
1549 if (status != ServiceWorkerDatabase::STATUS_OK) {
1550 original_task_runner->PostTask(
1551 FROM_HERE, base::Bind(callback, base::Passed(data.Pass()), status));
1552 return;
1555 status = database->GetOriginsWithRegistrations(&data->origins);
1556 original_task_runner->PostTask(
1557 FROM_HERE, base::Bind(callback, base::Passed(data.Pass()), status));
1560 void ServiceWorkerStorage::DeleteOldDiskCacheInDB(
1561 ServiceWorkerDatabase* database,
1562 const base::FilePath& disk_cache_path) {
1563 // Ignore a failure. A retry will happen on the next initialization.
1564 if (!base::DeleteFile(disk_cache_path, true))
1565 return;
1566 database->SetOldDiskCacheDeletionNotNeeded();
1569 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1570 ServiceWorkerDatabase* database,
1571 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1572 int64 registration_id,
1573 const GURL& origin,
1574 const DeleteRegistrationCallback& callback) {
1575 DCHECK(database);
1577 ServiceWorkerDatabase::RegistrationData deleted_version;
1578 std::vector<int64> newly_purgeable_resources;
1579 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1580 registration_id, origin, &deleted_version, &newly_purgeable_resources);
1581 if (status != ServiceWorkerDatabase::STATUS_OK) {
1582 original_task_runner->PostTask(
1583 FROM_HERE,
1584 base::Bind(
1585 callback, false, deleted_version, std::vector<int64>(), status));
1586 return;
1589 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1590 // unique origin list.
1591 RegistrationList registrations;
1592 status = database->GetRegistrationsForOrigin(origin, &registrations, nullptr);
1593 if (status != ServiceWorkerDatabase::STATUS_OK) {
1594 original_task_runner->PostTask(
1595 FROM_HERE,
1596 base::Bind(
1597 callback, false, deleted_version, std::vector<int64>(), status));
1598 return;
1601 bool deletable = registrations.empty();
1602 original_task_runner->PostTask(FROM_HERE,
1603 base::Bind(callback,
1604 deletable,
1605 deleted_version,
1606 newly_purgeable_resources,
1607 status));
1610 void ServiceWorkerStorage::WriteRegistrationInDB(
1611 ServiceWorkerDatabase* database,
1612 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1613 const ServiceWorkerDatabase::RegistrationData& data,
1614 const ResourceList& resources,
1615 const WriteRegistrationCallback& callback) {
1616 DCHECK(database);
1617 ServiceWorkerDatabase::RegistrationData deleted_version;
1618 std::vector<int64> newly_purgeable_resources;
1619 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1620 data, resources, &deleted_version, &newly_purgeable_resources);
1621 original_task_runner->PostTask(FROM_HERE,
1622 base::Bind(callback,
1623 data.script.GetOrigin(),
1624 deleted_version,
1625 newly_purgeable_resources,
1626 status));
1629 void ServiceWorkerStorage::FindForDocumentInDB(
1630 ServiceWorkerDatabase* database,
1631 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1632 const GURL& document_url,
1633 const FindInDBCallback& callback) {
1634 GURL origin = document_url.GetOrigin();
1635 RegistrationList registration_data_list;
1636 ServiceWorkerDatabase::Status status = database->GetRegistrationsForOrigin(
1637 origin, &registration_data_list, nullptr);
1638 if (status != ServiceWorkerDatabase::STATUS_OK) {
1639 original_task_runner->PostTask(
1640 FROM_HERE,
1641 base::Bind(callback,
1642 ServiceWorkerDatabase::RegistrationData(),
1643 ResourceList(),
1644 status));
1645 return;
1648 ServiceWorkerDatabase::RegistrationData data;
1649 ResourceList resources;
1650 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1652 // Find one with a pattern match.
1653 LongestScopeMatcher matcher(document_url);
1654 int64 match = kInvalidServiceWorkerRegistrationId;
1655 for (const auto& registration_data : registration_data_list)
1656 if (matcher.MatchLongest(registration_data.scope))
1657 match = registration_data.registration_id;
1658 if (match != kInvalidServiceWorkerRegistrationId)
1659 status = database->ReadRegistration(match, origin, &data, &resources);
1661 original_task_runner->PostTask(
1662 FROM_HERE,
1663 base::Bind(callback, data, resources, status));
1666 void ServiceWorkerStorage::FindForPatternInDB(
1667 ServiceWorkerDatabase* database,
1668 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1669 const GURL& scope,
1670 const FindInDBCallback& callback) {
1671 GURL origin = scope.GetOrigin();
1672 RegistrationList registration_data_list;
1673 ServiceWorkerDatabase::Status status = database->GetRegistrationsForOrigin(
1674 origin, &registration_data_list, nullptr);
1675 if (status != ServiceWorkerDatabase::STATUS_OK) {
1676 original_task_runner->PostTask(
1677 FROM_HERE,
1678 base::Bind(callback,
1679 ServiceWorkerDatabase::RegistrationData(),
1680 ResourceList(),
1681 status));
1682 return;
1685 // Find one with an exact matching scope.
1686 ServiceWorkerDatabase::RegistrationData data;
1687 ResourceList resources;
1688 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1689 for (const auto& registration_data : registration_data_list) {
1690 if (scope != registration_data.scope)
1691 continue;
1692 status = database->ReadRegistration(registration_data.registration_id,
1693 origin, &data, &resources);
1694 break; // We're done looping.
1697 original_task_runner->PostTask(
1698 FROM_HERE,
1699 base::Bind(callback, data, resources, status));
1702 void ServiceWorkerStorage::FindForIdInDB(
1703 ServiceWorkerDatabase* database,
1704 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1705 int64 registration_id,
1706 const GURL& origin,
1707 const FindInDBCallback& callback) {
1708 ServiceWorkerDatabase::RegistrationData data;
1709 ResourceList resources;
1710 ServiceWorkerDatabase::Status status =
1711 database->ReadRegistration(registration_id, origin, &data, &resources);
1712 original_task_runner->PostTask(
1713 FROM_HERE, base::Bind(callback, data, resources, status));
1716 void ServiceWorkerStorage::FindForIdOnlyInDB(
1717 ServiceWorkerDatabase* database,
1718 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1719 int64 registration_id,
1720 const FindInDBCallback& callback) {
1721 GURL origin;
1722 ServiceWorkerDatabase::Status status =
1723 database->ReadRegistrationOrigin(registration_id, &origin);
1724 if (status != ServiceWorkerDatabase::STATUS_OK) {
1725 original_task_runner->PostTask(
1726 FROM_HERE,
1727 base::Bind(callback, ServiceWorkerDatabase::RegistrationData(),
1728 ResourceList(), status));
1729 return;
1731 FindForIdInDB(database, original_task_runner, registration_id, origin,
1732 callback);
1735 void ServiceWorkerStorage::GetUserDataInDB(
1736 ServiceWorkerDatabase* database,
1737 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1738 int64 registration_id,
1739 const std::string& key,
1740 const GetUserDataInDBCallback& callback) {
1741 std::string data;
1742 ServiceWorkerDatabase::Status status =
1743 database->ReadUserData(registration_id, key, &data);
1744 original_task_runner->PostTask(
1745 FROM_HERE, base::Bind(callback, data, status));
1748 void ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB(
1749 ServiceWorkerDatabase* database,
1750 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1751 const std::string& key,
1752 const GetUserDataForAllRegistrationsInDBCallback& callback) {
1753 std::vector<std::pair<int64, std::string>> user_data;
1754 ServiceWorkerDatabase::Status status =
1755 database->ReadUserDataForAllRegistrations(key, &user_data);
1756 original_task_runner->PostTask(FROM_HERE,
1757 base::Bind(callback, user_data, status));
1760 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1761 ServiceWorkerDatabase* database,
1762 const std::set<GURL>& origins) {
1763 DCHECK(database);
1765 std::vector<int64> newly_purgeable_resources;
1766 database->DeleteAllDataForOrigins(origins, &newly_purgeable_resources);
1769 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1770 // is transient and it can get healed soon (e.g. IO error). To do that, the
1771 // database should not disable itself when an error occurs and the storage
1772 // controls it instead.
1773 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1774 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1775 // removed.
1776 if (state_ == DISABLED) {
1777 // Recovery process has already been scheduled.
1778 return;
1780 Disable();
1782 DVLOG(1) << "Schedule to delete the context and start over.";
1783 context_->ScheduleDeleteAndStartOver();
1786 void ServiceWorkerStorage::DidDeleteDatabase(
1787 const StatusCallback& callback,
1788 ServiceWorkerDatabase::Status status) {
1789 DCHECK_EQ(DISABLED, state_);
1790 if (status != ServiceWorkerDatabase::STATUS_OK) {
1791 // Give up the corruption recovery until the browser restarts.
1792 LOG(ERROR) << "Failed to delete the database: "
1793 << ServiceWorkerDatabase::StatusToString(status);
1794 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1795 ServiceWorkerMetrics::DELETE_DATABASE_ERROR);
1796 callback.Run(DatabaseStatusToStatusCode(status));
1797 return;
1799 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1801 // Delete the disk cache on the cache thread.
1802 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1803 // Deleting the directory could take a long time and restart could be delayed.
1804 // We should probably rename the directory and delete it later.
1805 PostTaskAndReplyWithResult(
1806 disk_cache_thread_.get(), FROM_HERE,
1807 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1808 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1809 weak_factory_.GetWeakPtr(), callback));
1812 void ServiceWorkerStorage::DidDeleteDiskCache(
1813 const StatusCallback& callback, bool result) {
1814 DCHECK_EQ(DISABLED, state_);
1815 if (!result) {
1816 // Give up the corruption recovery until the browser restarts.
1817 LOG(ERROR) << "Failed to delete the diskcache.";
1818 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1819 ServiceWorkerMetrics::DELETE_DISK_CACHE_ERROR);
1820 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1821 return;
1823 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1824 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1825 ServiceWorkerMetrics::DELETE_OK);
1826 callback.Run(SERVICE_WORKER_OK);
1829 } // namespace content