1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_storage.h"
7 #include "base/bind_helpers.h"
8 #include "base/debug/trace_event.h"
9 #include "base/files/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/task_runner_util.h"
14 #include "content/browser/service_worker/service_worker_context_core.h"
15 #include "content/browser/service_worker/service_worker_disk_cache.h"
16 #include "content/browser/service_worker/service_worker_info.h"
17 #include "content/browser/service_worker/service_worker_metrics.h"
18 #include "content/browser/service_worker/service_worker_registration.h"
19 #include "content/browser/service_worker/service_worker_utils.h"
20 #include "content/browser/service_worker/service_worker_version.h"
21 #include "content/common/service_worker/service_worker_types.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "net/base/completion_callback.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/net_errors.h"
26 #include "storage/browser/quota/quota_manager_proxy.h"
27 #include "storage/browser/quota/special_storage_policy.h"
33 void RunSoon(const tracked_objects::Location
& from_here
,
34 const base::Closure
& closure
) {
35 base::MessageLoop::current()->PostTask(from_here
, closure
);
39 const scoped_refptr
<ServiceWorkerRegistration
>& registration
,
40 ServiceWorkerStatusCode status
,
41 const ServiceWorkerStorage::FindRegistrationCallback
& callback
) {
42 callback
.Run(status
, registration
);
45 void CompleteFindSoon(
46 const tracked_objects::Location
& from_here
,
47 const scoped_refptr
<ServiceWorkerRegistration
>& registration
,
48 ServiceWorkerStatusCode status
,
49 const ServiceWorkerStorage::FindRegistrationCallback
& callback
) {
50 RunSoon(from_here
, base::Bind(callback
, status
, registration
));
53 const base::FilePath::CharType kDatabaseName
[] =
54 FILE_PATH_LITERAL("Database");
55 const base::FilePath::CharType kDiskCacheName
[] =
56 FILE_PATH_LITERAL("Cache");
58 const int kMaxMemDiskCacheSize
= 10 * 1024 * 1024;
59 const int kMaxDiskCacheSize
= 250 * 1024 * 1024;
61 ServiceWorkerStatusCode
DatabaseStatusToStatusCode(
62 ServiceWorkerDatabase::Status status
) {
64 case ServiceWorkerDatabase::STATUS_OK
:
65 return SERVICE_WORKER_OK
;
66 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
:
67 return SERVICE_WORKER_ERROR_NOT_FOUND
;
68 case ServiceWorkerDatabase::STATUS_ERROR_MAX
:
71 return SERVICE_WORKER_ERROR_FAILED
;
75 class ResponseComparer
: public base::RefCounted
<ResponseComparer
> {
78 base::WeakPtr
<ServiceWorkerStorage
> owner
,
79 scoped_ptr
<ServiceWorkerResponseReader
> lhs
,
80 scoped_ptr
<ServiceWorkerResponseReader
> rhs
,
81 const ServiceWorkerStorage::CompareCallback
& callback
)
83 completion_callback_(callback
),
84 lhs_reader_(lhs
.release()),
85 rhs_reader_(rhs
.release()),
93 friend class base::RefCounted
<ResponseComparer
>;
95 static const int kBufferSize
= 16 * 1024;
97 ~ResponseComparer() {}
99 void OnReadInfoComplete(int result
);
101 void OnReadDataComplete(int result
);
103 base::WeakPtr
<ServiceWorkerStorage
> owner_
;
104 ServiceWorkerStorage::CompareCallback completion_callback_
;
105 scoped_ptr
<ServiceWorkerResponseReader
> lhs_reader_
;
106 scoped_refptr
<HttpResponseInfoIOBuffer
> lhs_info_
;
107 scoped_refptr
<net::IOBuffer
> lhs_buffer_
;
108 scoped_ptr
<ServiceWorkerResponseReader
> rhs_reader_
;
109 scoped_refptr
<HttpResponseInfoIOBuffer
> rhs_info_
;
110 scoped_refptr
<net::IOBuffer
> rhs_buffer_
;
111 int completion_count_
;
112 int previous_result_
;
113 DISALLOW_COPY_AND_ASSIGN(ResponseComparer
);
116 void ResponseComparer::Start() {
117 lhs_buffer_
= new net::IOBuffer(kBufferSize
);
118 lhs_info_
= new HttpResponseInfoIOBuffer();
119 rhs_buffer_
= new net::IOBuffer(kBufferSize
);
120 rhs_info_
= new HttpResponseInfoIOBuffer();
125 void ResponseComparer::ReadInfos() {
126 lhs_reader_
->ReadInfo(
127 lhs_info_
.get(), base::Bind(&ResponseComparer::OnReadInfoComplete
, this));
128 rhs_reader_
->ReadInfo(
129 rhs_info_
.get(), base::Bind(&ResponseComparer::OnReadInfoComplete
, this));
132 void ResponseComparer::OnReadInfoComplete(int result
) {
133 if (completion_callback_
.is_null() || !owner_
)
136 completion_callback_
.Run(SERVICE_WORKER_ERROR_FAILED
, false);
137 completion_callback_
.Reset();
140 if (++completion_count_
!= 2)
143 if (lhs_info_
->response_data_size
!= rhs_info_
->response_data_size
) {
144 completion_callback_
.Run(SERVICE_WORKER_OK
, false);
150 void ResponseComparer::ReadSomeData() {
151 completion_count_
= 0;
152 lhs_reader_
->ReadData(
155 base::Bind(&ResponseComparer::OnReadDataComplete
, this));
156 rhs_reader_
->ReadData(
159 base::Bind(&ResponseComparer::OnReadDataComplete
, this));
162 void ResponseComparer::OnReadDataComplete(int result
) {
163 if (completion_callback_
.is_null() || !owner_
)
166 completion_callback_
.Run(SERVICE_WORKER_ERROR_FAILED
, false);
167 completion_callback_
.Reset();
170 if (++completion_count_
!= 2) {
171 previous_result_
= result
;
175 // TODO(michaeln): Probably shouldn't assume that the amounts read from
176 // each reader will always be the same. This would wrongly signal false
178 if (result
!= previous_result_
) {
179 completion_callback_
.Run(SERVICE_WORKER_OK
, false);
184 completion_callback_
.Run(SERVICE_WORKER_OK
, true);
189 memcmp(lhs_buffer_
->data(), rhs_buffer_
->data(), result
);
190 if (compare_result
!= 0) {
191 completion_callback_
.Run(SERVICE_WORKER_OK
, false);
200 ServiceWorkerStorage::InitialData::InitialData()
201 : next_registration_id(kInvalidServiceWorkerRegistrationId
),
202 next_version_id(kInvalidServiceWorkerVersionId
),
203 next_resource_id(kInvalidServiceWorkerResourceId
) {
206 ServiceWorkerStorage::InitialData::~InitialData() {
209 ServiceWorkerStorage::
210 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
211 : registration_id(kInvalidServiceWorkerRegistrationId
) {
214 ServiceWorkerStorage::
215 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
218 ServiceWorkerStorage::~ServiceWorkerStorage() {
219 ClearSessionOnlyOrigins();
220 weak_factory_
.InvalidateWeakPtrs();
221 database_task_manager_
->GetTaskRunner()->DeleteSoon(FROM_HERE
,
222 database_
.release());
226 scoped_ptr
<ServiceWorkerStorage
> ServiceWorkerStorage::Create(
227 const base::FilePath
& path
,
228 base::WeakPtr
<ServiceWorkerContextCore
> context
,
229 scoped_ptr
<ServiceWorkerDatabaseTaskManager
> database_task_manager
,
230 const scoped_refptr
<base::SingleThreadTaskRunner
>& disk_cache_thread
,
231 storage::QuotaManagerProxy
* quota_manager_proxy
,
232 storage::SpecialStoragePolicy
* special_storage_policy
) {
233 return make_scoped_ptr(new ServiceWorkerStorage(path
,
235 database_task_manager
.Pass(),
238 special_storage_policy
));
242 scoped_ptr
<ServiceWorkerStorage
> ServiceWorkerStorage::Create(
243 base::WeakPtr
<ServiceWorkerContextCore
> context
,
244 ServiceWorkerStorage
* old_storage
) {
245 return make_scoped_ptr(
246 new ServiceWorkerStorage(old_storage
->path_
,
248 old_storage
->database_task_manager_
->Clone(),
249 old_storage
->disk_cache_thread_
,
250 old_storage
->quota_manager_proxy_
.get(),
251 old_storage
->special_storage_policy_
.get()));
254 void ServiceWorkerStorage::FindRegistrationForDocument(
255 const GURL
& document_url
,
256 const FindRegistrationCallback
& callback
) {
257 DCHECK(!document_url
.has_ref());
258 if (!LazyInitialize(base::Bind(
259 &ServiceWorkerStorage::FindRegistrationForDocument
,
260 weak_factory_
.GetWeakPtr(), document_url
, callback
))) {
261 if (state_
!= INITIALIZING
|| !context_
) {
262 CompleteFindNow(scoped_refptr
<ServiceWorkerRegistration
>(),
263 SERVICE_WORKER_ERROR_FAILED
, callback
);
265 TRACE_EVENT_INSTANT1(
267 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
268 TRACE_EVENT_SCOPE_THREAD
,
269 "URL", document_url
.spec());
272 DCHECK_EQ(INITIALIZED
, state_
);
274 // See if there are any stored registrations for the origin.
275 if (!ContainsKey(registered_origins_
, document_url
.GetOrigin())) {
276 // Look for something currently being installed.
277 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
278 FindInstallingRegistrationForDocument(document_url
);
279 ServiceWorkerStatusCode status
= installing_registration
.get() ?
280 SERVICE_WORKER_OK
: SERVICE_WORKER_ERROR_NOT_FOUND
;
281 TRACE_EVENT_INSTANT2(
283 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
284 TRACE_EVENT_SCOPE_THREAD
,
285 "URL", document_url
.spec(),
286 "Status", ServiceWorkerStatusToString(status
));
287 CompleteFindNow(installing_registration
,
293 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
295 int64 callback_id
= base::TimeTicks::Now().ToInternalValue();
296 TRACE_EVENT_ASYNC_BEGIN1(
298 "ServiceWorkerStorage::FindRegistrationForDocument",
300 "URL", document_url
.spec());
301 database_task_manager_
->GetTaskRunner()->PostTask(
304 &FindForDocumentInDB
,
306 base::MessageLoopProxy::current(),
308 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument
,
309 weak_factory_
.GetWeakPtr(),
315 void ServiceWorkerStorage::FindRegistrationForPattern(
317 const FindRegistrationCallback
& callback
) {
318 if (!LazyInitialize(base::Bind(
319 &ServiceWorkerStorage::FindRegistrationForPattern
,
320 weak_factory_
.GetWeakPtr(), scope
, callback
))) {
321 if (state_
!= INITIALIZING
|| !context_
) {
322 CompleteFindSoon(FROM_HERE
, scoped_refptr
<ServiceWorkerRegistration
>(),
323 SERVICE_WORKER_ERROR_FAILED
, callback
);
327 DCHECK_EQ(INITIALIZED
, state_
);
329 // See if there are any stored registrations for the origin.
330 if (!ContainsKey(registered_origins_
, scope
.GetOrigin())) {
331 // Look for something currently being installed.
332 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
333 FindInstallingRegistrationForPattern(scope
);
334 CompleteFindSoon(FROM_HERE
,
335 installing_registration
,
336 installing_registration
.get()
338 : SERVICE_WORKER_ERROR_NOT_FOUND
,
343 database_task_manager_
->GetTaskRunner()->PostTask(
348 base::MessageLoopProxy::current(),
350 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern
,
351 weak_factory_
.GetWeakPtr(), scope
, callback
)));
354 ServiceWorkerRegistration
* ServiceWorkerStorage::GetUninstallingRegistration(
356 if (state_
!= INITIALIZED
|| !context_
)
358 for (RegistrationRefsById::const_iterator it
=
359 uninstalling_registrations_
.begin();
360 it
!= uninstalling_registrations_
.end();
362 if (it
->second
->pattern() == scope
) {
363 DCHECK(it
->second
->is_uninstalling());
364 return it
->second
.get();
370 void ServiceWorkerStorage::FindRegistrationForId(
371 int64 registration_id
,
373 const FindRegistrationCallback
& callback
) {
374 if (!LazyInitialize(base::Bind(
375 &ServiceWorkerStorage::FindRegistrationForId
,
376 weak_factory_
.GetWeakPtr(), registration_id
, origin
, callback
))) {
377 if (state_
!= INITIALIZING
|| !context_
) {
378 CompleteFindNow(scoped_refptr
<ServiceWorkerRegistration
>(),
379 SERVICE_WORKER_ERROR_FAILED
, callback
);
383 DCHECK_EQ(INITIALIZED
, state_
);
385 // See if there are any stored registrations for the origin.
386 if (!ContainsKey(registered_origins_
, origin
)) {
387 // Look for something currently being installed.
388 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
389 FindInstallingRegistrationForId(registration_id
);
390 CompleteFindNow(installing_registration
,
391 installing_registration
.get()
393 : SERVICE_WORKER_ERROR_NOT_FOUND
,
398 scoped_refptr
<ServiceWorkerRegistration
> registration
=
399 context_
->GetLiveRegistration(registration_id
);
400 if (registration
.get()) {
401 CompleteFindNow(registration
, SERVICE_WORKER_OK
, callback
);
405 database_task_manager_
->GetTaskRunner()->PostTask(
407 base::Bind(&FindForIdInDB
,
409 base::MessageLoopProxy::current(),
410 registration_id
, origin
,
411 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId
,
412 weak_factory_
.GetWeakPtr(), callback
)));
415 void ServiceWorkerStorage::GetAllRegistrations(
416 const GetAllRegistrationInfosCallback
& callback
) {
417 if (!LazyInitialize(base::Bind(
418 &ServiceWorkerStorage::GetAllRegistrations
,
419 weak_factory_
.GetWeakPtr(), callback
))) {
420 if (state_
!= INITIALIZING
|| !context_
) {
421 RunSoon(FROM_HERE
, base::Bind(
422 callback
, std::vector
<ServiceWorkerRegistrationInfo
>()));
426 DCHECK_EQ(INITIALIZED
, state_
);
428 RegistrationList
* registrations
= new RegistrationList
;
429 PostTaskAndReplyWithResult(
430 database_task_manager_
->GetTaskRunner(),
432 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations
,
433 base::Unretained(database_
.get()),
434 base::Unretained(registrations
)),
435 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations
,
436 weak_factory_
.GetWeakPtr(),
438 base::Owned(registrations
)));
441 void ServiceWorkerStorage::StoreRegistration(
442 ServiceWorkerRegistration
* registration
,
443 ServiceWorkerVersion
* version
,
444 const StatusCallback
& callback
) {
445 DCHECK(registration
);
448 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
449 if (IsDisabled() || !context_
) {
450 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
454 ServiceWorkerDatabase::RegistrationData data
;
455 data
.registration_id
= registration
->id();
456 data
.scope
= registration
->pattern();
457 data
.script
= version
->script_url();
458 data
.has_fetch_handler
= true;
459 data
.version_id
= version
->version_id();
460 data
.last_update_check
= registration
->last_update_check();
461 data
.is_active
= (version
== registration
->active_version());
463 ResourceList resources
;
464 version
->script_cache_map()->GetResources(&resources
);
466 uint64 resources_total_size_bytes
= 0;
467 for (const auto& resource
: resources
) {
468 resources_total_size_bytes
+= resource
.size_bytes
;
470 data
.resources_total_size_bytes
= resources_total_size_bytes
;
472 if (!has_checked_for_stale_resources_
)
473 DeleteStaleResources();
475 database_task_manager_
->GetTaskRunner()->PostTask(
477 base::Bind(&WriteRegistrationInDB
,
479 base::MessageLoopProxy::current(),
482 base::Bind(&ServiceWorkerStorage::DidStoreRegistration
,
483 weak_factory_
.GetWeakPtr(),
487 registration
->set_is_deleted(false);
490 void ServiceWorkerStorage::UpdateToActiveState(
491 ServiceWorkerRegistration
* registration
,
492 const StatusCallback
& callback
) {
493 DCHECK(registration
);
495 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
496 if (IsDisabled() || !context_
) {
497 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
501 PostTaskAndReplyWithResult(
502 database_task_manager_
->GetTaskRunner(),
504 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive
,
505 base::Unretained(database_
.get()),
507 registration
->pattern().GetOrigin()),
508 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState
,
509 weak_factory_
.GetWeakPtr(),
513 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
514 ServiceWorkerRegistration
* registration
) {
515 DCHECK(registration
);
517 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
518 if (IsDisabled() || !context_
)
521 database_task_manager_
->GetTaskRunner()->PostTask(
524 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime
),
525 base::Unretained(database_
.get()),
527 registration
->pattern().GetOrigin(),
528 registration
->last_update_check()));
531 void ServiceWorkerStorage::DeleteRegistration(
532 int64 registration_id
,
534 const StatusCallback
& callback
) {
535 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
536 if (IsDisabled() || !context_
) {
537 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
541 if (!has_checked_for_stale_resources_
)
542 DeleteStaleResources();
544 DidDeleteRegistrationParams params
;
545 params
.registration_id
= registration_id
;
546 params
.origin
= origin
;
547 params
.callback
= callback
;
549 database_task_manager_
->GetTaskRunner()->PostTask(
551 base::Bind(&DeleteRegistrationFromDB
,
553 base::MessageLoopProxy::current(),
554 registration_id
, origin
,
555 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration
,
556 weak_factory_
.GetWeakPtr(), params
)));
558 // The registration should no longer be findable.
559 pending_deletions_
.insert(registration_id
);
560 ServiceWorkerRegistration
* registration
=
561 context_
->GetLiveRegistration(registration_id
);
563 registration
->set_is_deleted(true);
566 scoped_ptr
<ServiceWorkerResponseReader
>
567 ServiceWorkerStorage::CreateResponseReader(int64 response_id
) {
568 return make_scoped_ptr(
569 new ServiceWorkerResponseReader(response_id
, disk_cache()));
572 scoped_ptr
<ServiceWorkerResponseWriter
>
573 ServiceWorkerStorage::CreateResponseWriter(int64 response_id
) {
574 return make_scoped_ptr(
575 new ServiceWorkerResponseWriter(response_id
, disk_cache()));
578 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id
) {
579 DCHECK_NE(kInvalidServiceWorkerResponseId
, id
);
580 DCHECK_EQ(INITIALIZED
, state_
);
582 if (!has_checked_for_stale_resources_
)
583 DeleteStaleResources();
585 database_task_manager_
->GetTaskRunner()->PostTask(
587 base::Bind(base::IgnoreResult(
588 &ServiceWorkerDatabase::WriteUncommittedResourceIds
),
589 base::Unretained(database_
.get()),
590 std::set
<int64
>(&id
, &id
+ 1)));
593 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id
) {
594 DCHECK_NE(kInvalidServiceWorkerResponseId
, id
);
595 database_task_manager_
->GetTaskRunner()->PostTask(
597 base::Bind(base::IgnoreResult(
598 &ServiceWorkerDatabase::PurgeUncommittedResourceIds
),
599 base::Unretained(database_
.get()),
600 std::set
<int64
>(&id
, &id
+ 1)));
601 StartPurgingResources(std::vector
<int64
>(1, id
));
604 void ServiceWorkerStorage::CompareScriptResources(
605 int64 lhs_id
, int64 rhs_id
,
606 const CompareCallback
& callback
) {
607 DCHECK(!callback
.is_null());
608 scoped_refptr
<ResponseComparer
> comparer
=
609 new ResponseComparer(weak_factory_
.GetWeakPtr(),
610 CreateResponseReader(lhs_id
),
611 CreateResponseReader(rhs_id
),
613 comparer
->Start(); // It deletes itself when done.
616 void ServiceWorkerStorage::StoreUserData(
617 int64 registration_id
,
619 const std::string
& key
,
620 const std::string
& data
,
621 const StatusCallback
& callback
) {
622 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
623 if (IsDisabled() || !context_
) {
624 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
628 if (registration_id
== kInvalidServiceWorkerRegistrationId
|| key
.empty()) {
629 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
633 PostTaskAndReplyWithResult(
634 database_task_manager_
->GetTaskRunner(),
636 base::Bind(&ServiceWorkerDatabase::WriteUserData
,
637 base::Unretained(database_
.get()),
638 registration_id
, origin
, key
, data
),
639 base::Bind(&ServiceWorkerStorage::DidStoreUserData
,
640 weak_factory_
.GetWeakPtr(),
644 void ServiceWorkerStorage::GetUserData(
645 int64 registration_id
,
646 const std::string
& key
,
647 const GetUserDataCallback
& callback
) {
648 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
649 if (IsDisabled() || !context_
) {
651 base::Bind(callback
, std::string(), SERVICE_WORKER_ERROR_FAILED
));
655 if (registration_id
== kInvalidServiceWorkerRegistrationId
|| key
.empty()) {
657 base::Bind(callback
, std::string(), SERVICE_WORKER_ERROR_FAILED
));
661 database_task_manager_
->GetTaskRunner()->PostTask(
663 base::Bind(&ServiceWorkerStorage::GetUserDataInDB
,
665 base::MessageLoopProxy::current(),
668 base::Bind(&ServiceWorkerStorage::DidGetUserData
,
669 weak_factory_
.GetWeakPtr(), callback
)));
672 void ServiceWorkerStorage::ClearUserData(
673 int64 registration_id
,
674 const std::string
& key
,
675 const StatusCallback
& callback
) {
676 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
677 if (IsDisabled() || !context_
) {
678 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
682 if (registration_id
== kInvalidServiceWorkerRegistrationId
|| key
.empty()) {
683 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
687 PostTaskAndReplyWithResult(
688 database_task_manager_
->GetTaskRunner(),
690 base::Bind(&ServiceWorkerDatabase::DeleteUserData
,
691 base::Unretained(database_
.get()),
692 registration_id
, key
),
693 base::Bind(&ServiceWorkerStorage::DidDeleteUserData
,
694 weak_factory_
.GetWeakPtr(),
698 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback
& callback
) {
701 // Delete the database on the database thread.
702 PostTaskAndReplyWithResult(
703 database_task_manager_
->GetTaskRunner(),
705 base::Bind(&ServiceWorkerDatabase::DestroyDatabase
,
706 base::Unretained(database_
.get())),
707 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase
,
708 weak_factory_
.GetWeakPtr(),
712 int64
ServiceWorkerStorage::NewRegistrationId() {
713 if (state_
== DISABLED
)
714 return kInvalidServiceWorkerRegistrationId
;
715 DCHECK_EQ(INITIALIZED
, state_
);
716 return next_registration_id_
++;
719 int64
ServiceWorkerStorage::NewVersionId() {
720 if (state_
== DISABLED
)
721 return kInvalidServiceWorkerVersionId
;
722 DCHECK_EQ(INITIALIZED
, state_
);
723 return next_version_id_
++;
726 int64
ServiceWorkerStorage::NewResourceId() {
727 if (state_
== DISABLED
)
728 return kInvalidServiceWorkerResourceId
;
729 DCHECK_EQ(INITIALIZED
, state_
);
730 return next_resource_id_
++;
733 void ServiceWorkerStorage::NotifyInstallingRegistration(
734 ServiceWorkerRegistration
* registration
) {
735 DCHECK(installing_registrations_
.find(registration
->id()) ==
736 installing_registrations_
.end());
737 installing_registrations_
[registration
->id()] = registration
;
740 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
741 ServiceWorkerRegistration
* registration
,
742 ServiceWorkerVersion
* version
,
743 ServiceWorkerStatusCode status
) {
744 installing_registrations_
.erase(registration
->id());
745 if (status
!= SERVICE_WORKER_OK
&& version
) {
746 ResourceList resources
;
747 version
->script_cache_map()->GetResources(&resources
);
750 for (size_t i
= 0; i
< resources
.size(); ++i
)
751 ids
.insert(resources
[i
].resource_id
);
753 database_task_manager_
->GetTaskRunner()->PostTask(
755 base::Bind(base::IgnoreResult(
756 &ServiceWorkerDatabase::PurgeUncommittedResourceIds
),
757 base::Unretained(database_
.get()),
762 void ServiceWorkerStorage::NotifyUninstallingRegistration(
763 ServiceWorkerRegistration
* registration
) {
764 DCHECK(uninstalling_registrations_
.find(registration
->id()) ==
765 uninstalling_registrations_
.end());
766 uninstalling_registrations_
[registration
->id()] = registration
;
769 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
770 ServiceWorkerRegistration
* registration
) {
771 uninstalling_registrations_
.erase(registration
->id());
774 void ServiceWorkerStorage::Disable() {
777 disk_cache_
->Disable();
780 bool ServiceWorkerStorage::IsDisabled() const {
781 return state_
== DISABLED
;
784 void ServiceWorkerStorage::PurgeResources(const ResourceList
& resources
) {
785 if (!has_checked_for_stale_resources_
)
786 DeleteStaleResources();
787 StartPurgingResources(resources
);
790 ServiceWorkerStorage::ServiceWorkerStorage(
791 const base::FilePath
& path
,
792 base::WeakPtr
<ServiceWorkerContextCore
> context
,
793 scoped_ptr
<ServiceWorkerDatabaseTaskManager
> database_task_manager
,
794 const scoped_refptr
<base::SingleThreadTaskRunner
>& disk_cache_thread
,
795 storage::QuotaManagerProxy
* quota_manager_proxy
,
796 storage::SpecialStoragePolicy
* special_storage_policy
)
797 : next_registration_id_(kInvalidServiceWorkerRegistrationId
),
798 next_version_id_(kInvalidServiceWorkerVersionId
),
799 next_resource_id_(kInvalidServiceWorkerResourceId
),
800 state_(UNINITIALIZED
),
803 database_task_manager_(database_task_manager
.Pass()),
804 disk_cache_thread_(disk_cache_thread
),
805 quota_manager_proxy_(quota_manager_proxy
),
806 special_storage_policy_(special_storage_policy
),
807 is_purge_pending_(false),
808 has_checked_for_stale_resources_(false),
809 weak_factory_(this) {
810 database_
.reset(new ServiceWorkerDatabase(GetDatabasePath()));
813 base::FilePath
ServiceWorkerStorage::GetDatabasePath() {
815 return base::FilePath();
816 return path_
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
817 .Append(kDatabaseName
);
820 base::FilePath
ServiceWorkerStorage::GetDiskCachePath() {
822 return base::FilePath();
823 return path_
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
824 .Append(kDiskCacheName
);
827 bool ServiceWorkerStorage::LazyInitialize(const base::Closure
& callback
) {
837 pending_tasks_
.push_back(callback
);
840 pending_tasks_
.push_back(callback
);
844 state_
= INITIALIZING
;
845 database_task_manager_
->GetTaskRunner()->PostTask(
847 base::Bind(&ReadInitialDataFromDB
,
849 base::MessageLoopProxy::current(),
850 base::Bind(&ServiceWorkerStorage::DidReadInitialData
,
851 weak_factory_
.GetWeakPtr())));
855 void ServiceWorkerStorage::DidReadInitialData(
857 ServiceWorkerDatabase::Status status
) {
859 DCHECK_EQ(INITIALIZING
, state_
);
861 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
862 next_registration_id_
= data
->next_registration_id
;
863 next_version_id_
= data
->next_version_id
;
864 next_resource_id_
= data
->next_resource_id
;
865 registered_origins_
.swap(data
->origins
);
866 state_
= INITIALIZED
;
868 DVLOG(2) << "Failed to initialize: "
869 << ServiceWorkerDatabase::StatusToString(status
);
870 ScheduleDeleteAndStartOver();
873 for (std::vector
<base::Closure
>::const_iterator it
= pending_tasks_
.begin();
874 it
!= pending_tasks_
.end(); ++it
) {
875 RunSoon(FROM_HERE
, *it
);
877 pending_tasks_
.clear();
880 void ServiceWorkerStorage::DidFindRegistrationForDocument(
881 const GURL
& document_url
,
882 const FindRegistrationCallback
& callback
,
884 const ServiceWorkerDatabase::RegistrationData
& data
,
885 const ResourceList
& resources
,
886 ServiceWorkerDatabase::Status status
) {
887 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
888 ReturnFoundRegistration(callback
, data
, resources
);
889 TRACE_EVENT_ASYNC_END1(
891 "ServiceWorkerStorage::FindRegistrationForDocument",
893 "Status", ServiceWorkerDatabase::StatusToString(status
));
897 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
898 // Look for something currently being installed.
899 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
900 FindInstallingRegistrationForDocument(document_url
);
901 ServiceWorkerStatusCode installing_status
= installing_registration
.get() ?
902 SERVICE_WORKER_OK
: SERVICE_WORKER_ERROR_NOT_FOUND
;
903 callback
.Run(installing_status
, installing_registration
);
904 TRACE_EVENT_ASYNC_END2(
906 "ServiceWorkerStorage::FindRegistrationForDocument",
908 "Status", ServiceWorkerDatabase::StatusToString(status
),
910 (installing_status
== SERVICE_WORKER_OK
) ?
911 "Installing registration is found" :
912 "Any registrations are not found");
916 ScheduleDeleteAndStartOver();
917 callback
.Run(DatabaseStatusToStatusCode(status
),
918 scoped_refptr
<ServiceWorkerRegistration
>());
919 TRACE_EVENT_ASYNC_END1(
921 "ServiceWorkerStorage::FindRegistrationForDocument",
923 "Status", ServiceWorkerDatabase::StatusToString(status
));
926 void ServiceWorkerStorage::DidFindRegistrationForPattern(
928 const FindRegistrationCallback
& callback
,
929 const ServiceWorkerDatabase::RegistrationData
& data
,
930 const ResourceList
& resources
,
931 ServiceWorkerDatabase::Status status
) {
932 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
933 ReturnFoundRegistration(callback
, data
, resources
);
937 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
938 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
939 FindInstallingRegistrationForPattern(scope
);
940 callback
.Run(installing_registration
.get() ? SERVICE_WORKER_OK
941 : SERVICE_WORKER_ERROR_NOT_FOUND
,
942 installing_registration
);
946 ScheduleDeleteAndStartOver();
947 callback
.Run(DatabaseStatusToStatusCode(status
),
948 scoped_refptr
<ServiceWorkerRegistration
>());
951 void ServiceWorkerStorage::DidFindRegistrationForId(
952 const FindRegistrationCallback
& callback
,
953 const ServiceWorkerDatabase::RegistrationData
& data
,
954 const ResourceList
& resources
,
955 ServiceWorkerDatabase::Status status
) {
956 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
957 ReturnFoundRegistration(callback
, data
, resources
);
961 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
962 // TODO(nhiroki): Find a registration in |installing_registrations_|.
963 callback
.Run(DatabaseStatusToStatusCode(status
),
964 scoped_refptr
<ServiceWorkerRegistration
>());
968 ScheduleDeleteAndStartOver();
969 callback
.Run(DatabaseStatusToStatusCode(status
),
970 scoped_refptr
<ServiceWorkerRegistration
>());
973 void ServiceWorkerStorage::ReturnFoundRegistration(
974 const FindRegistrationCallback
& callback
,
975 const ServiceWorkerDatabase::RegistrationData
& data
,
976 const ResourceList
& resources
) {
977 scoped_refptr
<ServiceWorkerRegistration
> registration
=
978 GetOrCreateRegistration(data
, resources
);
979 if (registration
->is_deleted()) {
980 // It's past the point of no return and no longer findable.
981 callback
.Run(SERVICE_WORKER_ERROR_NOT_FOUND
, NULL
);
984 callback
.Run(SERVICE_WORKER_OK
, registration
);
987 void ServiceWorkerStorage::DidGetAllRegistrations(
988 const GetAllRegistrationInfosCallback
& callback
,
989 RegistrationList
* registrations
,
990 ServiceWorkerDatabase::Status status
) {
991 DCHECK(registrations
);
992 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
993 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
994 ScheduleDeleteAndStartOver();
995 callback
.Run(std::vector
<ServiceWorkerRegistrationInfo
>());
999 // Add all stored registrations.
1000 std::set
<int64
> pushed_registrations
;
1001 std::vector
<ServiceWorkerRegistrationInfo
> infos
;
1002 for (const auto& registration_data
: *registrations
) {
1003 const bool inserted
=
1004 pushed_registrations
.insert(registration_data
.registration_id
).second
;
1007 ServiceWorkerRegistration
* registration
=
1008 context_
->GetLiveRegistration(registration_data
.registration_id
);
1010 infos
.push_back(registration
->GetInfo());
1014 ServiceWorkerRegistrationInfo info
;
1015 info
.pattern
= registration_data
.scope
;
1016 info
.registration_id
= registration_data
.registration_id
;
1017 info
.stored_version_size_bytes
=
1018 registration_data
.resources_total_size_bytes
;
1019 if (ServiceWorkerVersion
* version
=
1020 context_
->GetLiveVersion(registration_data
.version_id
)) {
1021 if (registration_data
.is_active
)
1022 info
.active_version
= version
->GetInfo();
1024 info
.waiting_version
= version
->GetInfo();
1025 infos
.push_back(info
);
1029 if (registration_data
.is_active
) {
1030 info
.active_version
.status
= ServiceWorkerVersion::ACTIVATED
;
1031 info
.active_version
.version_id
= registration_data
.version_id
;
1033 info
.waiting_version
.status
= ServiceWorkerVersion::INSTALLED
;
1034 info
.waiting_version
.version_id
= registration_data
.version_id
;
1036 infos
.push_back(info
);
1039 // Add unstored registrations that are being installed.
1040 for (RegistrationRefsById::const_iterator it
=
1041 installing_registrations_
.begin();
1042 it
!= installing_registrations_
.end(); ++it
) {
1043 if (pushed_registrations
.insert(it
->first
).second
)
1044 infos
.push_back(it
->second
->GetInfo());
1047 callback
.Run(infos
);
1050 void ServiceWorkerStorage::DidStoreRegistration(
1051 const StatusCallback
& callback
,
1052 const ServiceWorkerDatabase::RegistrationData
& new_version
,
1054 const ServiceWorkerDatabase::RegistrationData
& deleted_version
,
1055 const std::vector
<int64
>& newly_purgeable_resources
,
1056 ServiceWorkerDatabase::Status status
) {
1057 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1058 ScheduleDeleteAndStartOver();
1059 callback
.Run(DatabaseStatusToStatusCode(status
));
1062 registered_origins_
.insert(origin
);
1064 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1065 context_
->GetLiveRegistration(new_version
.registration_id
);
1066 registration
->set_resources_total_size_bytes(
1067 new_version
.resources_total_size_bytes
);
1068 if (quota_manager_proxy_
.get()) {
1069 // Can be nullptr in tests.
1070 quota_manager_proxy_
->NotifyStorageModified(
1071 storage::QuotaClient::kServiceWorker
,
1073 storage::StorageType::kStorageTypeTemporary
,
1074 new_version
.resources_total_size_bytes
-
1075 deleted_version
.resources_total_size_bytes
);
1078 callback
.Run(SERVICE_WORKER_OK
);
1080 if (!context_
|| !context_
->GetLiveVersion(deleted_version
.version_id
))
1081 StartPurgingResources(newly_purgeable_resources
);
1084 void ServiceWorkerStorage::DidUpdateToActiveState(
1085 const StatusCallback
& callback
,
1086 ServiceWorkerDatabase::Status status
) {
1087 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1088 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1089 ScheduleDeleteAndStartOver();
1091 callback
.Run(DatabaseStatusToStatusCode(status
));
1094 void ServiceWorkerStorage::DidDeleteRegistration(
1095 const DidDeleteRegistrationParams
& params
,
1096 bool origin_is_deletable
,
1097 const ServiceWorkerDatabase::RegistrationData
& deleted_version
,
1098 const std::vector
<int64
>& newly_purgeable_resources
,
1099 ServiceWorkerDatabase::Status status
) {
1100 pending_deletions_
.erase(params
.registration_id
);
1101 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1102 ScheduleDeleteAndStartOver();
1103 params
.callback
.Run(DatabaseStatusToStatusCode(status
));
1106 if (quota_manager_proxy_
.get()) {
1107 // Can be nullptr in tests.
1108 quota_manager_proxy_
->NotifyStorageModified(
1109 storage::QuotaClient::kServiceWorker
,
1111 storage::StorageType::kStorageTypeTemporary
,
1112 -deleted_version
.resources_total_size_bytes
);
1114 if (origin_is_deletable
)
1115 registered_origins_
.erase(params
.origin
);
1116 params
.callback
.Run(SERVICE_WORKER_OK
);
1118 if (!context_
|| !context_
->GetLiveVersion(deleted_version
.version_id
))
1119 StartPurgingResources(newly_purgeable_resources
);
1122 void ServiceWorkerStorage::DidStoreUserData(
1123 const StatusCallback
& callback
,
1124 ServiceWorkerDatabase::Status status
) {
1125 // |status| can be NOT_FOUND when the associated registration did not exist in
1126 // the database. In the case, we don't have to schedule the corruption
1128 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1129 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1130 ScheduleDeleteAndStartOver();
1132 callback
.Run(DatabaseStatusToStatusCode(status
));
1135 void ServiceWorkerStorage::DidGetUserData(
1136 const GetUserDataCallback
& callback
,
1137 const std::string
& data
,
1138 ServiceWorkerDatabase::Status status
) {
1139 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1140 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1141 ScheduleDeleteAndStartOver();
1143 callback
.Run(data
, DatabaseStatusToStatusCode(status
));
1146 void ServiceWorkerStorage::DidDeleteUserData(
1147 const StatusCallback
& callback
,
1148 ServiceWorkerDatabase::Status status
) {
1149 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1150 ScheduleDeleteAndStartOver();
1151 callback
.Run(DatabaseStatusToStatusCode(status
));
1154 scoped_refptr
<ServiceWorkerRegistration
>
1155 ServiceWorkerStorage::GetOrCreateRegistration(
1156 const ServiceWorkerDatabase::RegistrationData
& data
,
1157 const ResourceList
& resources
) {
1158 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1159 context_
->GetLiveRegistration(data
.registration_id
);
1160 if (registration
.get())
1161 return registration
;
1163 registration
= new ServiceWorkerRegistration(
1164 data
.scope
, data
.registration_id
, context_
);
1165 registration
->set_resources_total_size_bytes(data
.resources_total_size_bytes
);
1166 registration
->set_last_update_check(data
.last_update_check
);
1167 if (pending_deletions_
.find(data
.registration_id
) !=
1168 pending_deletions_
.end()) {
1169 registration
->set_is_deleted(true);
1171 scoped_refptr
<ServiceWorkerVersion
> version
=
1172 context_
->GetLiveVersion(data
.version_id
);
1173 if (!version
.get()) {
1174 version
= new ServiceWorkerVersion(
1175 registration
.get(), data
.script
, data
.version_id
, context_
);
1176 version
->SetStatus(data
.is_active
?
1177 ServiceWorkerVersion::ACTIVATED
: ServiceWorkerVersion::INSTALLED
);
1178 version
->script_cache_map()->SetResources(resources
);
1181 if (version
->status() == ServiceWorkerVersion::ACTIVATED
)
1182 registration
->SetActiveVersion(version
.get());
1183 else if (version
->status() == ServiceWorkerVersion::INSTALLED
)
1184 registration
->SetWaitingVersion(version
.get());
1188 return registration
;
1191 ServiceWorkerRegistration
*
1192 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1193 const GURL
& document_url
) {
1194 DCHECK(!document_url
.has_ref());
1196 LongestScopeMatcher
matcher(document_url
);
1197 ServiceWorkerRegistration
* match
= NULL
;
1199 // TODO(nhiroki): This searches over installing registrations linearly and it
1200 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1201 for (RegistrationRefsById::const_iterator it
=
1202 installing_registrations_
.begin();
1203 it
!= installing_registrations_
.end(); ++it
) {
1204 if (matcher
.MatchLongest(it
->second
->pattern()))
1205 match
= it
->second
.get();
1210 ServiceWorkerRegistration
*
1211 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1212 const GURL
& scope
) {
1213 for (RegistrationRefsById::const_iterator it
=
1214 installing_registrations_
.begin();
1215 it
!= installing_registrations_
.end(); ++it
) {
1216 if (it
->second
->pattern() == scope
)
1217 return it
->second
.get();
1222 ServiceWorkerRegistration
*
1223 ServiceWorkerStorage::FindInstallingRegistrationForId(
1224 int64 registration_id
) {
1225 RegistrationRefsById::const_iterator found
=
1226 installing_registrations_
.find(registration_id
);
1227 if (found
== installing_registrations_
.end())
1229 return found
->second
.get();
1232 ServiceWorkerDiskCache
* ServiceWorkerStorage::disk_cache() {
1234 return disk_cache_
.get();
1236 disk_cache_
.reset(new ServiceWorkerDiskCache
);
1238 base::FilePath path
= GetDiskCachePath();
1240 int rv
= disk_cache_
->InitWithMemBackend(kMaxMemDiskCacheSize
,
1241 net::CompletionCallback());
1242 DCHECK_EQ(net::OK
, rv
);
1243 return disk_cache_
.get();
1246 int rv
= disk_cache_
->InitWithDiskBackend(
1251 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized
,
1252 weak_factory_
.GetWeakPtr()));
1253 if (rv
!= net::ERR_IO_PENDING
)
1254 OnDiskCacheInitialized(rv
);
1256 return disk_cache_
.get();
1259 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv
) {
1260 if (rv
!= net::OK
) {
1261 LOG(ERROR
) << "Failed to open the serviceworker diskcache: "
1262 << net::ErrorToString(rv
);
1263 ScheduleDeleteAndStartOver();
1265 ServiceWorkerMetrics::CountInitDiskCacheResult(rv
== net::OK
);
1268 void ServiceWorkerStorage::StartPurgingResources(
1269 const std::vector
<int64
>& ids
) {
1270 DCHECK(has_checked_for_stale_resources_
);
1271 for (size_t i
= 0; i
< ids
.size(); ++i
)
1272 purgeable_resource_ids_
.push_back(ids
[i
]);
1273 ContinuePurgingResources();
1276 void ServiceWorkerStorage::StartPurgingResources(
1277 const ResourceList
& resources
) {
1278 DCHECK(has_checked_for_stale_resources_
);
1279 for (size_t i
= 0; i
< resources
.size(); ++i
)
1280 purgeable_resource_ids_
.push_back(resources
[i
].resource_id
);
1281 ContinuePurgingResources();
1284 void ServiceWorkerStorage::ContinuePurgingResources() {
1285 if (purgeable_resource_ids_
.empty() || is_purge_pending_
)
1288 // Do one at a time until we're done, use RunSoon to avoid recursion when
1289 // DoomEntry returns immediately.
1290 is_purge_pending_
= true;
1291 int64 id
= purgeable_resource_ids_
.front();
1292 purgeable_resource_ids_
.pop_front();
1294 base::Bind(&ServiceWorkerStorage::PurgeResource
,
1295 weak_factory_
.GetWeakPtr(), id
));
1298 void ServiceWorkerStorage::PurgeResource(int64 id
) {
1299 DCHECK(is_purge_pending_
);
1300 int rv
= disk_cache()->DoomEntry(
1301 id
, base::Bind(&ServiceWorkerStorage::OnResourcePurged
,
1302 weak_factory_
.GetWeakPtr(), id
));
1303 if (rv
!= net::ERR_IO_PENDING
)
1304 OnResourcePurged(id
, rv
);
1307 void ServiceWorkerStorage::OnResourcePurged(int64 id
, int rv
) {
1308 DCHECK(is_purge_pending_
);
1309 is_purge_pending_
= false;
1311 database_task_manager_
->GetTaskRunner()->PostTask(
1313 base::Bind(base::IgnoreResult(
1314 &ServiceWorkerDatabase::ClearPurgeableResourceIds
),
1315 base::Unretained(database_
.get()),
1316 std::set
<int64
>(&id
, &id
+ 1)));
1318 ContinuePurgingResources();
1321 void ServiceWorkerStorage::DeleteStaleResources() {
1322 DCHECK(!has_checked_for_stale_resources_
);
1323 has_checked_for_stale_resources_
= true;
1324 database_task_manager_
->GetTaskRunner()->PostTask(
1326 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB
,
1328 base::MessageLoopProxy::current(),
1329 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources
,
1330 weak_factory_
.GetWeakPtr())));
1333 void ServiceWorkerStorage::DidCollectStaleResources(
1334 const std::vector
<int64
>& stale_resource_ids
,
1335 ServiceWorkerDatabase::Status status
) {
1336 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK
, status
);
1337 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1339 StartPurgingResources(stale_resource_ids
);
1342 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1343 // Can be null in tests.
1344 if (!special_storage_policy_
.get())
1347 if (!special_storage_policy_
->HasSessionOnlyOrigins())
1350 std::set
<GURL
> session_only_origins
;
1351 for (const GURL
& origin
: registered_origins_
) {
1352 if (special_storage_policy_
->IsStorageSessionOnly(origin
))
1353 session_only_origins
.insert(origin
);
1356 database_task_manager_
->GetShutdownBlockingTaskRunner()->PostTask(
1358 base::Bind(&DeleteAllDataForOriginsFromDB
,
1360 session_only_origins
));
1363 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1364 ServiceWorkerDatabase
* database
,
1365 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1366 const GetResourcesCallback
& callback
) {
1367 std::set
<int64
> ids
;
1368 ServiceWorkerDatabase::Status status
=
1369 database
->GetUncommittedResourceIds(&ids
);
1370 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1371 original_task_runner
->PostTask(
1374 callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1378 status
= database
->PurgeUncommittedResourceIds(ids
);
1379 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1380 original_task_runner
->PostTask(
1383 callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1388 status
= database
->GetPurgeableResourceIds(&ids
);
1389 original_task_runner
->PostTask(
1391 base::Bind(callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1394 void ServiceWorkerStorage::ReadInitialDataFromDB(
1395 ServiceWorkerDatabase
* database
,
1396 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1397 const InitializeCallback
& callback
) {
1399 scoped_ptr
<ServiceWorkerStorage::InitialData
> data(
1400 new ServiceWorkerStorage::InitialData());
1402 ServiceWorkerDatabase::Status status
=
1403 database
->GetNextAvailableIds(&data
->next_registration_id
,
1404 &data
->next_version_id
,
1405 &data
->next_resource_id
);
1406 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1407 original_task_runner
->PostTask(
1408 FROM_HERE
, base::Bind(callback
, base::Owned(data
.release()), status
));
1412 status
= database
->GetOriginsWithRegistrations(&data
->origins
);
1413 original_task_runner
->PostTask(
1414 FROM_HERE
, base::Bind(callback
, base::Owned(data
.release()), status
));
1417 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1418 ServiceWorkerDatabase
* database
,
1419 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1420 int64 registration_id
,
1422 const DeleteRegistrationCallback
& callback
) {
1425 ServiceWorkerDatabase::RegistrationData deleted_version
;
1426 std::vector
<int64
> newly_purgeable_resources
;
1427 ServiceWorkerDatabase::Status status
= database
->DeleteRegistration(
1428 registration_id
, origin
, &deleted_version
, &newly_purgeable_resources
);
1429 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1430 original_task_runner
->PostTask(
1433 callback
, false, deleted_version
, std::vector
<int64
>(), status
));
1437 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1438 // unique origin list.
1439 std::vector
<ServiceWorkerDatabase::RegistrationData
> registrations
;
1440 status
= database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1441 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1442 original_task_runner
->PostTask(
1445 callback
, false, deleted_version
, std::vector
<int64
>(), status
));
1449 bool deletable
= registrations
.empty();
1450 original_task_runner
->PostTask(FROM_HERE
,
1451 base::Bind(callback
,
1454 newly_purgeable_resources
,
1458 void ServiceWorkerStorage::WriteRegistrationInDB(
1459 ServiceWorkerDatabase
* database
,
1460 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1461 const ServiceWorkerDatabase::RegistrationData
& data
,
1462 const ResourceList
& resources
,
1463 const WriteRegistrationCallback
& callback
) {
1465 ServiceWorkerDatabase::RegistrationData deleted_version
;
1466 std::vector
<int64
> newly_purgeable_resources
;
1467 ServiceWorkerDatabase::Status status
= database
->WriteRegistration(
1468 data
, resources
, &deleted_version
, &newly_purgeable_resources
);
1469 original_task_runner
->PostTask(FROM_HERE
,
1470 base::Bind(callback
,
1471 data
.script
.GetOrigin(),
1473 newly_purgeable_resources
,
1477 void ServiceWorkerStorage::FindForDocumentInDB(
1478 ServiceWorkerDatabase
* database
,
1479 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1480 const GURL
& document_url
,
1481 const FindInDBCallback
& callback
) {
1482 GURL origin
= document_url
.GetOrigin();
1483 RegistrationList registrations
;
1484 ServiceWorkerDatabase::Status status
=
1485 database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1486 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1487 original_task_runner
->PostTask(
1489 base::Bind(callback
,
1490 ServiceWorkerDatabase::RegistrationData(),
1496 ServiceWorkerDatabase::RegistrationData data
;
1497 ResourceList resources
;
1498 status
= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
1500 // Find one with a pattern match.
1501 LongestScopeMatcher
matcher(document_url
);
1502 int64 match
= kInvalidServiceWorkerRegistrationId
;
1503 for (size_t i
= 0; i
< registrations
.size(); ++i
) {
1504 if (matcher
.MatchLongest(registrations
[i
].scope
))
1505 match
= registrations
[i
].registration_id
;
1508 if (match
!= kInvalidServiceWorkerRegistrationId
)
1509 status
= database
->ReadRegistration(match
, origin
, &data
, &resources
);
1511 original_task_runner
->PostTask(
1513 base::Bind(callback
, data
, resources
, status
));
1516 void ServiceWorkerStorage::FindForPatternInDB(
1517 ServiceWorkerDatabase
* database
,
1518 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1520 const FindInDBCallback
& callback
) {
1521 GURL origin
= scope
.GetOrigin();
1522 std::vector
<ServiceWorkerDatabase::RegistrationData
> registrations
;
1523 ServiceWorkerDatabase::Status status
=
1524 database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1525 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1526 original_task_runner
->PostTask(
1528 base::Bind(callback
,
1529 ServiceWorkerDatabase::RegistrationData(),
1535 // Find one with an exact matching scope.
1536 ServiceWorkerDatabase::RegistrationData data
;
1537 ResourceList resources
;
1538 status
= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
1539 for (RegistrationList::const_iterator it
= registrations
.begin();
1540 it
!= registrations
.end(); ++it
) {
1541 if (scope
!= it
->scope
)
1543 status
= database
->ReadRegistration(it
->registration_id
, origin
,
1545 break; // We're done looping.
1548 original_task_runner
->PostTask(
1550 base::Bind(callback
, data
, resources
, status
));
1553 void ServiceWorkerStorage::FindForIdInDB(
1554 ServiceWorkerDatabase
* database
,
1555 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1556 int64 registration_id
,
1558 const FindInDBCallback
& callback
) {
1559 ServiceWorkerDatabase::RegistrationData data
;
1560 ResourceList resources
;
1561 ServiceWorkerDatabase::Status status
=
1562 database
->ReadRegistration(registration_id
, origin
, &data
, &resources
);
1563 original_task_runner
->PostTask(
1564 FROM_HERE
, base::Bind(callback
, data
, resources
, status
));
1567 void ServiceWorkerStorage::GetUserDataInDB(
1568 ServiceWorkerDatabase
* database
,
1569 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1570 int64 registration_id
,
1571 const std::string
& key
,
1572 const GetUserDataInDBCallback
& callback
) {
1574 ServiceWorkerDatabase::Status status
=
1575 database
->ReadUserData(registration_id
, key
, &data
);
1576 original_task_runner
->PostTask(
1577 FROM_HERE
, base::Bind(callback
, data
, status
));
1580 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1581 ServiceWorkerDatabase
* database
,
1582 const std::set
<GURL
>& origins
) {
1585 std::vector
<int64
> newly_purgeable_resources
;
1586 database
->DeleteAllDataForOrigins(origins
, &newly_purgeable_resources
);
1589 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1590 // is transient and it can get healed soon (e.g. IO error). To do that, the
1591 // database should not disable itself when an error occurs and the storage
1592 // controls it instead.
1593 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1594 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1596 if (state_
== DISABLED
) {
1597 // Recovery process has already been scheduled.
1602 DVLOG(1) << "Schedule to delete the context and start over.";
1603 context_
->ScheduleDeleteAndStartOver();
1606 void ServiceWorkerStorage::DidDeleteDatabase(
1607 const StatusCallback
& callback
,
1608 ServiceWorkerDatabase::Status status
) {
1609 DCHECK_EQ(DISABLED
, state_
);
1610 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1611 // Give up the corruption recovery until the browser restarts.
1612 LOG(ERROR
) << "Failed to delete the database: "
1613 << ServiceWorkerDatabase::StatusToString(status
);
1614 callback
.Run(DatabaseStatusToStatusCode(status
));
1617 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1619 // Delete the disk cache on the cache thread.
1620 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1621 // Deleting the directory could take a long time and restart could be delayed.
1622 // We should probably rename the directory and delete it later.
1623 PostTaskAndReplyWithResult(
1624 database_task_manager_
->GetTaskRunner(),
1626 base::Bind(&base::DeleteFile
, GetDiskCachePath(), true),
1627 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache
,
1628 weak_factory_
.GetWeakPtr(),
1632 void ServiceWorkerStorage::DidDeleteDiskCache(
1633 const StatusCallback
& callback
, bool result
) {
1634 DCHECK_EQ(DISABLED
, state_
);
1636 // Give up the corruption recovery until the browser restarts.
1637 LOG(ERROR
) << "Failed to delete the diskcache.";
1638 callback
.Run(SERVICE_WORKER_ERROR_FAILED
);
1641 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1642 callback
.Run(SERVICE_WORKER_OK
);
1645 } // namespace content