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