1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_storage.h"
7 #include "base/bind_helpers.h"
8 #include "base/files/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/task_runner_util.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/browser/service_worker/service_worker_context_core.h"
15 #include "content/browser/service_worker/service_worker_disk_cache.h"
16 #include "content/browser/service_worker/service_worker_info.h"
17 #include "content/browser/service_worker/service_worker_metrics.h"
18 #include "content/browser/service_worker/service_worker_registration.h"
19 #include "content/browser/service_worker/service_worker_utils.h"
20 #include "content/browser/service_worker/service_worker_version.h"
21 #include "content/common/service_worker/service_worker_types.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "net/base/completion_callback.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/net_errors.h"
26 #include "storage/browser/quota/quota_manager_proxy.h"
27 #include "storage/browser/quota/special_storage_policy.h"
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 scoped_ptr
<ServiceWorkerResponseMetadataWriter
>
647 ServiceWorkerStorage::CreateResponseMetadataWriter(int64 response_id
) {
648 return make_scoped_ptr(
649 new ServiceWorkerResponseMetadataWriter(response_id
, disk_cache()));
652 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id
) {
653 DCHECK_NE(kInvalidServiceWorkerResponseId
, id
);
654 DCHECK_EQ(INITIALIZED
, state_
);
656 if (!has_checked_for_stale_resources_
)
657 DeleteStaleResources();
659 database_task_manager_
->GetTaskRunner()->PostTask(
661 base::Bind(base::IgnoreResult(
662 &ServiceWorkerDatabase::WriteUncommittedResourceIds
),
663 base::Unretained(database_
.get()),
664 std::set
<int64
>(&id
, &id
+ 1)));
667 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id
) {
668 DCHECK_NE(kInvalidServiceWorkerResponseId
, id
);
669 database_task_manager_
->GetTaskRunner()->PostTask(
671 base::Bind(base::IgnoreResult(
672 &ServiceWorkerDatabase::PurgeUncommittedResourceIds
),
673 base::Unretained(database_
.get()),
674 std::set
<int64
>(&id
, &id
+ 1)));
675 StartPurgingResources(std::vector
<int64
>(1, id
));
678 void ServiceWorkerStorage::CompareScriptResources(
679 int64 lhs_id
, int64 rhs_id
,
680 const CompareCallback
& callback
) {
681 DCHECK(!callback
.is_null());
682 scoped_refptr
<ResponseComparer
> comparer
=
683 new ResponseComparer(weak_factory_
.GetWeakPtr(),
684 CreateResponseReader(lhs_id
),
685 CreateResponseReader(rhs_id
),
687 comparer
->Start(); // It deletes itself when done.
690 void ServiceWorkerStorage::StoreUserData(
691 int64 registration_id
,
693 const std::string
& key
,
694 const std::string
& data
,
695 const StatusCallback
& callback
) {
696 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
697 if (IsDisabled() || !context_
) {
698 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
702 if (registration_id
== kInvalidServiceWorkerRegistrationId
|| key
.empty()) {
703 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
707 PostTaskAndReplyWithResult(
708 database_task_manager_
->GetTaskRunner(),
710 base::Bind(&ServiceWorkerDatabase::WriteUserData
,
711 base::Unretained(database_
.get()),
712 registration_id
, origin
, key
, data
),
713 base::Bind(&ServiceWorkerStorage::DidStoreUserData
,
714 weak_factory_
.GetWeakPtr(),
718 void ServiceWorkerStorage::GetUserData(
719 int64 registration_id
,
720 const std::string
& key
,
721 const GetUserDataCallback
& callback
) {
722 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
723 if (IsDisabled() || !context_
) {
725 base::Bind(callback
, std::string(), SERVICE_WORKER_ERROR_FAILED
));
729 if (registration_id
== kInvalidServiceWorkerRegistrationId
|| key
.empty()) {
731 base::Bind(callback
, std::string(), SERVICE_WORKER_ERROR_FAILED
));
735 database_task_manager_
->GetTaskRunner()->PostTask(
737 base::Bind(&ServiceWorkerStorage::GetUserDataInDB
,
739 base::MessageLoopProxy::current(),
742 base::Bind(&ServiceWorkerStorage::DidGetUserData
,
743 weak_factory_
.GetWeakPtr(), callback
)));
746 void ServiceWorkerStorage::ClearUserData(
747 int64 registration_id
,
748 const std::string
& key
,
749 const StatusCallback
& callback
) {
750 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
751 if (IsDisabled() || !context_
) {
752 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
756 if (registration_id
== kInvalidServiceWorkerRegistrationId
|| key
.empty()) {
757 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
761 PostTaskAndReplyWithResult(
762 database_task_manager_
->GetTaskRunner(),
764 base::Bind(&ServiceWorkerDatabase::DeleteUserData
,
765 base::Unretained(database_
.get()),
766 registration_id
, key
),
767 base::Bind(&ServiceWorkerStorage::DidDeleteUserData
,
768 weak_factory_
.GetWeakPtr(),
772 void ServiceWorkerStorage::GetUserDataForAllRegistrations(
773 const std::string
& key
,
774 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback
&
776 DCHECK(state_
== INITIALIZED
|| state_
== DISABLED
) << state_
;
777 if (IsDisabled() || !context_
) {
779 base::Bind(callback
, std::vector
<std::pair
<int64
, std::string
>>(),
780 SERVICE_WORKER_ERROR_FAILED
));
786 base::Bind(callback
, std::vector
<std::pair
<int64
, std::string
>>(),
787 SERVICE_WORKER_ERROR_FAILED
));
791 database_task_manager_
->GetTaskRunner()->PostTask(
794 &ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB
,
795 database_
.get(), base::MessageLoopProxy::current(), key
,
796 base::Bind(&ServiceWorkerStorage::DidGetUserDataForAllRegistrations
,
797 weak_factory_
.GetWeakPtr(), callback
)));
800 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback
& callback
) {
803 // Delete the database on the database thread.
804 PostTaskAndReplyWithResult(
805 database_task_manager_
->GetTaskRunner(),
807 base::Bind(&ServiceWorkerDatabase::DestroyDatabase
,
808 base::Unretained(database_
.get())),
809 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase
,
810 weak_factory_
.GetWeakPtr(),
814 int64
ServiceWorkerStorage::NewRegistrationId() {
815 if (state_
== DISABLED
)
816 return kInvalidServiceWorkerRegistrationId
;
817 DCHECK_EQ(INITIALIZED
, state_
);
818 return next_registration_id_
++;
821 int64
ServiceWorkerStorage::NewVersionId() {
822 if (state_
== DISABLED
)
823 return kInvalidServiceWorkerVersionId
;
824 DCHECK_EQ(INITIALIZED
, state_
);
825 return next_version_id_
++;
828 int64
ServiceWorkerStorage::NewResourceId() {
829 if (state_
== DISABLED
)
830 return kInvalidServiceWorkerResourceId
;
831 DCHECK_EQ(INITIALIZED
, state_
);
832 return next_resource_id_
++;
835 void ServiceWorkerStorage::NotifyInstallingRegistration(
836 ServiceWorkerRegistration
* registration
) {
837 DCHECK(installing_registrations_
.find(registration
->id()) ==
838 installing_registrations_
.end());
839 installing_registrations_
[registration
->id()] = registration
;
842 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
843 ServiceWorkerRegistration
* registration
,
844 ServiceWorkerVersion
* version
,
845 ServiceWorkerStatusCode status
) {
846 installing_registrations_
.erase(registration
->id());
847 if (status
!= SERVICE_WORKER_OK
&& version
) {
848 ResourceList resources
;
849 version
->script_cache_map()->GetResources(&resources
);
852 for (size_t i
= 0; i
< resources
.size(); ++i
)
853 ids
.insert(resources
[i
].resource_id
);
855 database_task_manager_
->GetTaskRunner()->PostTask(
857 base::Bind(base::IgnoreResult(
858 &ServiceWorkerDatabase::PurgeUncommittedResourceIds
),
859 base::Unretained(database_
.get()),
864 void ServiceWorkerStorage::NotifyUninstallingRegistration(
865 ServiceWorkerRegistration
* registration
) {
866 DCHECK(uninstalling_registrations_
.find(registration
->id()) ==
867 uninstalling_registrations_
.end());
868 uninstalling_registrations_
[registration
->id()] = registration
;
871 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
872 ServiceWorkerRegistration
* registration
) {
873 uninstalling_registrations_
.erase(registration
->id());
876 void ServiceWorkerStorage::Disable() {
879 disk_cache_
->Disable();
882 bool ServiceWorkerStorage::IsDisabled() const {
883 return state_
== DISABLED
;
886 void ServiceWorkerStorage::PurgeResources(const ResourceList
& resources
) {
887 if (!has_checked_for_stale_resources_
)
888 DeleteStaleResources();
889 StartPurgingResources(resources
);
892 ServiceWorkerStorage::ServiceWorkerStorage(
893 const base::FilePath
& path
,
894 base::WeakPtr
<ServiceWorkerContextCore
> context
,
895 scoped_ptr
<ServiceWorkerDatabaseTaskManager
> database_task_manager
,
896 const scoped_refptr
<base::SingleThreadTaskRunner
>& disk_cache_thread
,
897 storage::QuotaManagerProxy
* quota_manager_proxy
,
898 storage::SpecialStoragePolicy
* special_storage_policy
)
899 : next_registration_id_(kInvalidServiceWorkerRegistrationId
),
900 next_version_id_(kInvalidServiceWorkerVersionId
),
901 next_resource_id_(kInvalidServiceWorkerResourceId
),
902 state_(UNINITIALIZED
),
905 database_task_manager_(database_task_manager
.Pass()),
906 disk_cache_thread_(disk_cache_thread
),
907 quota_manager_proxy_(quota_manager_proxy
),
908 special_storage_policy_(special_storage_policy
),
909 is_purge_pending_(false),
910 has_checked_for_stale_resources_(false),
911 weak_factory_(this) {
912 database_
.reset(new ServiceWorkerDatabase(GetDatabasePath()));
915 base::FilePath
ServiceWorkerStorage::GetDatabasePath() {
917 return base::FilePath();
918 return path_
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
919 .Append(kDatabaseName
);
922 base::FilePath
ServiceWorkerStorage::GetDiskCachePath() {
924 return base::FilePath();
925 return path_
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
926 .Append(kDiskCacheName
);
929 bool ServiceWorkerStorage::LazyInitialize(const base::Closure
& callback
) {
939 pending_tasks_
.push_back(callback
);
942 pending_tasks_
.push_back(callback
);
946 state_
= INITIALIZING
;
947 database_task_manager_
->GetTaskRunner()->PostTask(
949 base::Bind(&ReadInitialDataFromDB
,
951 base::MessageLoopProxy::current(),
952 base::Bind(&ServiceWorkerStorage::DidReadInitialData
,
953 weak_factory_
.GetWeakPtr())));
957 void ServiceWorkerStorage::DidReadInitialData(
959 ServiceWorkerDatabase::Status status
) {
961 DCHECK_EQ(INITIALIZING
, state_
);
963 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
964 next_registration_id_
= data
->next_registration_id
;
965 next_version_id_
= data
->next_version_id
;
966 next_resource_id_
= data
->next_resource_id
;
967 registered_origins_
.swap(data
->origins
);
968 state_
= INITIALIZED
;
970 DVLOG(2) << "Failed to initialize: "
971 << ServiceWorkerDatabase::StatusToString(status
);
972 ScheduleDeleteAndStartOver();
975 for (std::vector
<base::Closure
>::const_iterator it
= pending_tasks_
.begin();
976 it
!= pending_tasks_
.end(); ++it
) {
977 RunSoon(FROM_HERE
, *it
);
979 pending_tasks_
.clear();
982 void ServiceWorkerStorage::DidFindRegistrationForDocument(
983 const GURL
& document_url
,
984 const FindRegistrationCallback
& callback
,
986 const ServiceWorkerDatabase::RegistrationData
& data
,
987 const ResourceList
& resources
,
988 ServiceWorkerDatabase::Status status
) {
989 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
990 ReturnFoundRegistration(callback
, data
, resources
);
991 TRACE_EVENT_ASYNC_END1(
993 "ServiceWorkerStorage::FindRegistrationForDocument",
995 "Status", ServiceWorkerDatabase::StatusToString(status
));
999 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1000 // Look for something currently being installed.
1001 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
1002 FindInstallingRegistrationForDocument(document_url
);
1003 ServiceWorkerStatusCode installing_status
= installing_registration
.get() ?
1004 SERVICE_WORKER_OK
: SERVICE_WORKER_ERROR_NOT_FOUND
;
1005 callback
.Run(installing_status
, installing_registration
);
1006 TRACE_EVENT_ASYNC_END2(
1008 "ServiceWorkerStorage::FindRegistrationForDocument",
1010 "Status", ServiceWorkerDatabase::StatusToString(status
),
1012 (installing_status
== SERVICE_WORKER_OK
) ?
1013 "Installing registration is found" :
1014 "Any registrations are not found");
1018 ScheduleDeleteAndStartOver();
1019 callback
.Run(DatabaseStatusToStatusCode(status
),
1020 scoped_refptr
<ServiceWorkerRegistration
>());
1021 TRACE_EVENT_ASYNC_END1(
1023 "ServiceWorkerStorage::FindRegistrationForDocument",
1025 "Status", ServiceWorkerDatabase::StatusToString(status
));
1028 void ServiceWorkerStorage::DidFindRegistrationForPattern(
1030 const FindRegistrationCallback
& callback
,
1031 const ServiceWorkerDatabase::RegistrationData
& data
,
1032 const ResourceList
& resources
,
1033 ServiceWorkerDatabase::Status status
) {
1034 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
1035 ReturnFoundRegistration(callback
, data
, resources
);
1039 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1040 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
1041 FindInstallingRegistrationForPattern(scope
);
1042 callback
.Run(installing_registration
.get() ? SERVICE_WORKER_OK
1043 : SERVICE_WORKER_ERROR_NOT_FOUND
,
1044 installing_registration
);
1048 ScheduleDeleteAndStartOver();
1049 callback
.Run(DatabaseStatusToStatusCode(status
),
1050 scoped_refptr
<ServiceWorkerRegistration
>());
1053 void ServiceWorkerStorage::DidFindRegistrationForId(
1054 const FindRegistrationCallback
& callback
,
1055 const ServiceWorkerDatabase::RegistrationData
& data
,
1056 const ResourceList
& resources
,
1057 ServiceWorkerDatabase::Status status
) {
1058 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
1059 ReturnFoundRegistration(callback
, data
, resources
);
1063 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1064 // TODO(nhiroki): Find a registration in |installing_registrations_|.
1065 callback
.Run(DatabaseStatusToStatusCode(status
),
1066 scoped_refptr
<ServiceWorkerRegistration
>());
1070 ScheduleDeleteAndStartOver();
1071 callback
.Run(DatabaseStatusToStatusCode(status
),
1072 scoped_refptr
<ServiceWorkerRegistration
>());
1075 void ServiceWorkerStorage::ReturnFoundRegistration(
1076 const FindRegistrationCallback
& callback
,
1077 const ServiceWorkerDatabase::RegistrationData
& data
,
1078 const ResourceList
& resources
) {
1079 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1080 GetOrCreateRegistration(data
, resources
);
1081 CompleteFindNow(registration
, SERVICE_WORKER_OK
, callback
);
1084 void ServiceWorkerStorage::DidGetRegistrations(
1085 const GetRegistrationsInfosCallback
& callback
,
1086 RegistrationList
* registrations
,
1087 const GURL
& origin_filter
,
1088 ServiceWorkerDatabase::Status status
) {
1089 DCHECK(registrations
);
1090 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1091 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1092 ScheduleDeleteAndStartOver();
1093 callback
.Run(std::vector
<ServiceWorkerRegistrationInfo
>());
1097 // Add all stored registrations.
1098 std::set
<int64
> pushed_registrations
;
1099 std::vector
<ServiceWorkerRegistrationInfo
> infos
;
1100 for (const auto& registration_data
: *registrations
) {
1101 const bool inserted
=
1102 pushed_registrations
.insert(registration_data
.registration_id
).second
;
1105 ServiceWorkerRegistration
* registration
=
1106 context_
->GetLiveRegistration(registration_data
.registration_id
);
1108 infos
.push_back(registration
->GetInfo());
1112 ServiceWorkerRegistrationInfo info
;
1113 info
.pattern
= registration_data
.scope
;
1114 info
.registration_id
= registration_data
.registration_id
;
1115 info
.stored_version_size_bytes
=
1116 registration_data
.resources_total_size_bytes
;
1117 if (ServiceWorkerVersion
* version
=
1118 context_
->GetLiveVersion(registration_data
.version_id
)) {
1119 if (registration_data
.is_active
)
1120 info
.active_version
= version
->GetInfo();
1122 info
.waiting_version
= version
->GetInfo();
1123 infos
.push_back(info
);
1127 if (registration_data
.is_active
) {
1128 info
.active_version
.status
= ServiceWorkerVersion::ACTIVATED
;
1129 info
.active_version
.version_id
= registration_data
.version_id
;
1131 info
.waiting_version
.status
= ServiceWorkerVersion::INSTALLED
;
1132 info
.waiting_version
.version_id
= registration_data
.version_id
;
1134 infos
.push_back(info
);
1137 // Add unstored registrations that are being installed.
1138 for (RegistrationRefsById::const_iterator it
=
1139 installing_registrations_
.begin();
1140 it
!= installing_registrations_
.end(); ++it
) {
1141 if ((!origin_filter
.is_valid() ||
1142 it
->second
->pattern().GetOrigin() == origin_filter
) &&
1143 pushed_registrations
.insert(it
->first
).second
) {
1144 infos
.push_back(it
->second
->GetInfo());
1148 callback
.Run(infos
);
1151 void ServiceWorkerStorage::DidStoreRegistration(
1152 const StatusCallback
& callback
,
1153 const ServiceWorkerDatabase::RegistrationData
& new_version
,
1155 const ServiceWorkerDatabase::RegistrationData
& deleted_version
,
1156 const std::vector
<int64
>& newly_purgeable_resources
,
1157 ServiceWorkerDatabase::Status status
) {
1158 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1159 ScheduleDeleteAndStartOver();
1160 callback
.Run(DatabaseStatusToStatusCode(status
));
1163 registered_origins_
.insert(origin
);
1165 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1166 context_
->GetLiveRegistration(new_version
.registration_id
);
1167 registration
->set_resources_total_size_bytes(
1168 new_version
.resources_total_size_bytes
);
1169 if (quota_manager_proxy_
.get()) {
1170 // Can be nullptr in tests.
1171 quota_manager_proxy_
->NotifyStorageModified(
1172 storage::QuotaClient::kServiceWorker
,
1174 storage::StorageType::kStorageTypeTemporary
,
1175 new_version
.resources_total_size_bytes
-
1176 deleted_version
.resources_total_size_bytes
);
1179 callback
.Run(SERVICE_WORKER_OK
);
1181 if (!context_
|| !context_
->GetLiveVersion(deleted_version
.version_id
))
1182 StartPurgingResources(newly_purgeable_resources
);
1185 void ServiceWorkerStorage::DidUpdateToActiveState(
1186 const StatusCallback
& callback
,
1187 ServiceWorkerDatabase::Status status
) {
1188 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1189 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1190 ScheduleDeleteAndStartOver();
1192 callback
.Run(DatabaseStatusToStatusCode(status
));
1195 void ServiceWorkerStorage::DidDeleteRegistration(
1196 const DidDeleteRegistrationParams
& params
,
1197 bool origin_is_deletable
,
1198 const ServiceWorkerDatabase::RegistrationData
& deleted_version
,
1199 const std::vector
<int64
>& newly_purgeable_resources
,
1200 ServiceWorkerDatabase::Status status
) {
1201 pending_deletions_
.erase(params
.registration_id
);
1202 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1203 ScheduleDeleteAndStartOver();
1204 params
.callback
.Run(DatabaseStatusToStatusCode(status
));
1207 if (quota_manager_proxy_
.get()) {
1208 // Can be nullptr in tests.
1209 quota_manager_proxy_
->NotifyStorageModified(
1210 storage::QuotaClient::kServiceWorker
,
1212 storage::StorageType::kStorageTypeTemporary
,
1213 -deleted_version
.resources_total_size_bytes
);
1215 if (origin_is_deletable
)
1216 registered_origins_
.erase(params
.origin
);
1217 params
.callback
.Run(SERVICE_WORKER_OK
);
1219 if (!context_
|| !context_
->GetLiveVersion(deleted_version
.version_id
))
1220 StartPurgingResources(newly_purgeable_resources
);
1223 void ServiceWorkerStorage::DidStoreUserData(
1224 const StatusCallback
& callback
,
1225 ServiceWorkerDatabase::Status status
) {
1226 // |status| can be NOT_FOUND when the associated registration did not exist in
1227 // the database. In the case, we don't have to schedule the corruption
1229 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1230 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1231 ScheduleDeleteAndStartOver();
1233 callback
.Run(DatabaseStatusToStatusCode(status
));
1236 void ServiceWorkerStorage::DidGetUserData(
1237 const GetUserDataCallback
& callback
,
1238 const std::string
& data
,
1239 ServiceWorkerDatabase::Status status
) {
1240 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1241 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1242 ScheduleDeleteAndStartOver();
1244 callback
.Run(data
, DatabaseStatusToStatusCode(status
));
1247 void ServiceWorkerStorage::DidDeleteUserData(
1248 const StatusCallback
& callback
,
1249 ServiceWorkerDatabase::Status status
) {
1250 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1251 ScheduleDeleteAndStartOver();
1252 callback
.Run(DatabaseStatusToStatusCode(status
));
1255 void ServiceWorkerStorage::DidGetUserDataForAllRegistrations(
1256 const GetUserDataForAllRegistrationsCallback
& callback
,
1257 const std::vector
<std::pair
<int64
, std::string
>>& user_data
,
1258 ServiceWorkerDatabase::Status status
) {
1259 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1260 ScheduleDeleteAndStartOver();
1261 callback
.Run(user_data
, DatabaseStatusToStatusCode(status
));
1264 scoped_refptr
<ServiceWorkerRegistration
>
1265 ServiceWorkerStorage::GetOrCreateRegistration(
1266 const ServiceWorkerDatabase::RegistrationData
& data
,
1267 const ResourceList
& resources
) {
1268 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1269 context_
->GetLiveRegistration(data
.registration_id
);
1270 if (registration
.get())
1271 return registration
;
1273 registration
= new ServiceWorkerRegistration(
1274 data
.scope
, data
.registration_id
, context_
);
1275 registration
->set_resources_total_size_bytes(data
.resources_total_size_bytes
);
1276 registration
->set_last_update_check(data
.last_update_check
);
1277 if (pending_deletions_
.find(data
.registration_id
) !=
1278 pending_deletions_
.end()) {
1279 registration
->set_is_deleted(true);
1281 scoped_refptr
<ServiceWorkerVersion
> version
=
1282 context_
->GetLiveVersion(data
.version_id
);
1283 if (!version
.get()) {
1284 version
= new ServiceWorkerVersion(
1285 registration
.get(), data
.script
, data
.version_id
, context_
);
1286 version
->SetStatus(data
.is_active
?
1287 ServiceWorkerVersion::ACTIVATED
: ServiceWorkerVersion::INSTALLED
);
1288 version
->script_cache_map()->SetResources(resources
);
1291 if (version
->status() == ServiceWorkerVersion::ACTIVATED
)
1292 registration
->SetActiveVersion(version
.get());
1293 else if (version
->status() == ServiceWorkerVersion::INSTALLED
)
1294 registration
->SetWaitingVersion(version
.get());
1298 return registration
;
1301 ServiceWorkerRegistration
*
1302 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1303 const GURL
& document_url
) {
1304 DCHECK(!document_url
.has_ref());
1306 LongestScopeMatcher
matcher(document_url
);
1307 ServiceWorkerRegistration
* match
= NULL
;
1309 // TODO(nhiroki): This searches over installing registrations linearly and it
1310 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1311 for (RegistrationRefsById::const_iterator it
=
1312 installing_registrations_
.begin();
1313 it
!= installing_registrations_
.end(); ++it
) {
1314 if (matcher
.MatchLongest(it
->second
->pattern()))
1315 match
= it
->second
.get();
1320 ServiceWorkerRegistration
*
1321 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1322 const GURL
& scope
) {
1323 for (RegistrationRefsById::const_iterator it
=
1324 installing_registrations_
.begin();
1325 it
!= installing_registrations_
.end(); ++it
) {
1326 if (it
->second
->pattern() == scope
)
1327 return it
->second
.get();
1332 ServiceWorkerRegistration
*
1333 ServiceWorkerStorage::FindInstallingRegistrationForId(
1334 int64 registration_id
) {
1335 RegistrationRefsById::const_iterator found
=
1336 installing_registrations_
.find(registration_id
);
1337 if (found
== installing_registrations_
.end())
1339 return found
->second
.get();
1342 ServiceWorkerDiskCache
* ServiceWorkerStorage::disk_cache() {
1344 return disk_cache_
.get();
1346 disk_cache_
.reset(new ServiceWorkerDiskCache
);
1348 base::FilePath path
= GetDiskCachePath();
1350 int rv
= disk_cache_
->InitWithMemBackend(kMaxMemDiskCacheSize
,
1351 net::CompletionCallback());
1352 DCHECK_EQ(net::OK
, rv
);
1353 return disk_cache_
.get();
1356 int rv
= disk_cache_
->InitWithDiskBackend(
1361 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized
,
1362 weak_factory_
.GetWeakPtr()));
1363 if (rv
!= net::ERR_IO_PENDING
)
1364 OnDiskCacheInitialized(rv
);
1366 return disk_cache_
.get();
1369 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv
) {
1370 if (rv
!= net::OK
) {
1371 LOG(ERROR
) << "Failed to open the serviceworker diskcache: "
1372 << net::ErrorToString(rv
);
1373 ScheduleDeleteAndStartOver();
1375 ServiceWorkerMetrics::CountInitDiskCacheResult(rv
== net::OK
);
1378 void ServiceWorkerStorage::StartPurgingResources(
1379 const std::vector
<int64
>& ids
) {
1380 DCHECK(has_checked_for_stale_resources_
);
1381 for (size_t i
= 0; i
< ids
.size(); ++i
)
1382 purgeable_resource_ids_
.push_back(ids
[i
]);
1383 ContinuePurgingResources();
1386 void ServiceWorkerStorage::StartPurgingResources(
1387 const ResourceList
& resources
) {
1388 DCHECK(has_checked_for_stale_resources_
);
1389 for (size_t i
= 0; i
< resources
.size(); ++i
)
1390 purgeable_resource_ids_
.push_back(resources
[i
].resource_id
);
1391 ContinuePurgingResources();
1394 void ServiceWorkerStorage::ContinuePurgingResources() {
1395 if (purgeable_resource_ids_
.empty() || is_purge_pending_
)
1398 // Do one at a time until we're done, use RunSoon to avoid recursion when
1399 // DoomEntry returns immediately.
1400 is_purge_pending_
= true;
1401 int64 id
= purgeable_resource_ids_
.front();
1402 purgeable_resource_ids_
.pop_front();
1404 base::Bind(&ServiceWorkerStorage::PurgeResource
,
1405 weak_factory_
.GetWeakPtr(), id
));
1408 void ServiceWorkerStorage::PurgeResource(int64 id
) {
1409 DCHECK(is_purge_pending_
);
1410 int rv
= disk_cache()->DoomEntry(
1411 id
, base::Bind(&ServiceWorkerStorage::OnResourcePurged
,
1412 weak_factory_
.GetWeakPtr(), id
));
1413 if (rv
!= net::ERR_IO_PENDING
)
1414 OnResourcePurged(id
, rv
);
1417 void ServiceWorkerStorage::OnResourcePurged(int64 id
, int rv
) {
1418 DCHECK(is_purge_pending_
);
1419 is_purge_pending_
= false;
1421 database_task_manager_
->GetTaskRunner()->PostTask(
1423 base::Bind(base::IgnoreResult(
1424 &ServiceWorkerDatabase::ClearPurgeableResourceIds
),
1425 base::Unretained(database_
.get()),
1426 std::set
<int64
>(&id
, &id
+ 1)));
1428 ContinuePurgingResources();
1431 void ServiceWorkerStorage::DeleteStaleResources() {
1432 DCHECK(!has_checked_for_stale_resources_
);
1433 has_checked_for_stale_resources_
= true;
1434 database_task_manager_
->GetTaskRunner()->PostTask(
1436 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB
,
1438 base::MessageLoopProxy::current(),
1439 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources
,
1440 weak_factory_
.GetWeakPtr())));
1443 void ServiceWorkerStorage::DidCollectStaleResources(
1444 const std::vector
<int64
>& stale_resource_ids
,
1445 ServiceWorkerDatabase::Status status
) {
1446 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK
, status
);
1447 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1449 StartPurgingResources(stale_resource_ids
);
1452 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1453 // Can be null in tests.
1454 if (!special_storage_policy_
.get())
1457 if (!special_storage_policy_
->HasSessionOnlyOrigins())
1460 std::set
<GURL
> session_only_origins
;
1461 for (const GURL
& origin
: registered_origins_
) {
1462 if (special_storage_policy_
->IsStorageSessionOnly(origin
))
1463 session_only_origins
.insert(origin
);
1466 database_task_manager_
->GetShutdownBlockingTaskRunner()->PostTask(
1468 base::Bind(&DeleteAllDataForOriginsFromDB
,
1470 session_only_origins
));
1473 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1474 ServiceWorkerDatabase
* database
,
1475 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1476 const GetResourcesCallback
& callback
) {
1477 std::set
<int64
> ids
;
1478 ServiceWorkerDatabase::Status status
=
1479 database
->GetUncommittedResourceIds(&ids
);
1480 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1481 original_task_runner
->PostTask(
1484 callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1488 status
= database
->PurgeUncommittedResourceIds(ids
);
1489 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1490 original_task_runner
->PostTask(
1493 callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1498 status
= database
->GetPurgeableResourceIds(&ids
);
1499 original_task_runner
->PostTask(
1501 base::Bind(callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1504 void ServiceWorkerStorage::ReadInitialDataFromDB(
1505 ServiceWorkerDatabase
* database
,
1506 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1507 const InitializeCallback
& callback
) {
1509 scoped_ptr
<ServiceWorkerStorage::InitialData
> data(
1510 new ServiceWorkerStorage::InitialData());
1512 ServiceWorkerDatabase::Status status
=
1513 database
->GetNextAvailableIds(&data
->next_registration_id
,
1514 &data
->next_version_id
,
1515 &data
->next_resource_id
);
1516 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1517 original_task_runner
->PostTask(
1518 FROM_HERE
, base::Bind(callback
, base::Owned(data
.release()), status
));
1522 status
= database
->GetOriginsWithRegistrations(&data
->origins
);
1523 original_task_runner
->PostTask(
1524 FROM_HERE
, base::Bind(callback
, base::Owned(data
.release()), status
));
1527 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1528 ServiceWorkerDatabase
* database
,
1529 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1530 int64 registration_id
,
1532 const DeleteRegistrationCallback
& callback
) {
1535 ServiceWorkerDatabase::RegistrationData deleted_version
;
1536 std::vector
<int64
> newly_purgeable_resources
;
1537 ServiceWorkerDatabase::Status status
= database
->DeleteRegistration(
1538 registration_id
, origin
, &deleted_version
, &newly_purgeable_resources
);
1539 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1540 original_task_runner
->PostTask(
1543 callback
, false, deleted_version
, std::vector
<int64
>(), status
));
1547 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1548 // unique origin list.
1549 std::vector
<ServiceWorkerDatabase::RegistrationData
> registrations
;
1550 status
= database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1551 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1552 original_task_runner
->PostTask(
1555 callback
, false, deleted_version
, std::vector
<int64
>(), status
));
1559 bool deletable
= registrations
.empty();
1560 original_task_runner
->PostTask(FROM_HERE
,
1561 base::Bind(callback
,
1564 newly_purgeable_resources
,
1568 void ServiceWorkerStorage::WriteRegistrationInDB(
1569 ServiceWorkerDatabase
* database
,
1570 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1571 const ServiceWorkerDatabase::RegistrationData
& data
,
1572 const ResourceList
& resources
,
1573 const WriteRegistrationCallback
& callback
) {
1575 ServiceWorkerDatabase::RegistrationData deleted_version
;
1576 std::vector
<int64
> newly_purgeable_resources
;
1577 ServiceWorkerDatabase::Status status
= database
->WriteRegistration(
1578 data
, resources
, &deleted_version
, &newly_purgeable_resources
);
1579 original_task_runner
->PostTask(FROM_HERE
,
1580 base::Bind(callback
,
1581 data
.script
.GetOrigin(),
1583 newly_purgeable_resources
,
1587 void ServiceWorkerStorage::FindForDocumentInDB(
1588 ServiceWorkerDatabase
* database
,
1589 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1590 const GURL
& document_url
,
1591 const FindInDBCallback
& callback
) {
1592 GURL origin
= document_url
.GetOrigin();
1593 RegistrationList registrations
;
1594 ServiceWorkerDatabase::Status status
=
1595 database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1596 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1597 original_task_runner
->PostTask(
1599 base::Bind(callback
,
1600 ServiceWorkerDatabase::RegistrationData(),
1606 ServiceWorkerDatabase::RegistrationData data
;
1607 ResourceList resources
;
1608 status
= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
1610 // Find one with a pattern match.
1611 LongestScopeMatcher
matcher(document_url
);
1612 int64 match
= kInvalidServiceWorkerRegistrationId
;
1613 for (size_t i
= 0; i
< registrations
.size(); ++i
) {
1614 if (matcher
.MatchLongest(registrations
[i
].scope
))
1615 match
= registrations
[i
].registration_id
;
1618 if (match
!= kInvalidServiceWorkerRegistrationId
)
1619 status
= database
->ReadRegistration(match
, origin
, &data
, &resources
);
1621 original_task_runner
->PostTask(
1623 base::Bind(callback
, data
, resources
, status
));
1626 void ServiceWorkerStorage::FindForPatternInDB(
1627 ServiceWorkerDatabase
* database
,
1628 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1630 const FindInDBCallback
& callback
) {
1631 GURL origin
= scope
.GetOrigin();
1632 std::vector
<ServiceWorkerDatabase::RegistrationData
> registrations
;
1633 ServiceWorkerDatabase::Status status
=
1634 database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1635 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1636 original_task_runner
->PostTask(
1638 base::Bind(callback
,
1639 ServiceWorkerDatabase::RegistrationData(),
1645 // Find one with an exact matching scope.
1646 ServiceWorkerDatabase::RegistrationData data
;
1647 ResourceList resources
;
1648 status
= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
1649 for (RegistrationList::const_iterator it
= registrations
.begin();
1650 it
!= registrations
.end(); ++it
) {
1651 if (scope
!= it
->scope
)
1653 status
= database
->ReadRegistration(it
->registration_id
, origin
,
1655 break; // We're done looping.
1658 original_task_runner
->PostTask(
1660 base::Bind(callback
, data
, resources
, status
));
1663 void ServiceWorkerStorage::FindForIdInDB(
1664 ServiceWorkerDatabase
* database
,
1665 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1666 int64 registration_id
,
1668 const FindInDBCallback
& callback
) {
1669 ServiceWorkerDatabase::RegistrationData data
;
1670 ResourceList resources
;
1671 ServiceWorkerDatabase::Status status
=
1672 database
->ReadRegistration(registration_id
, origin
, &data
, &resources
);
1673 original_task_runner
->PostTask(
1674 FROM_HERE
, base::Bind(callback
, data
, resources
, status
));
1677 void ServiceWorkerStorage::FindForIdOnlyInDB(
1678 ServiceWorkerDatabase
* database
,
1679 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1680 int64 registration_id
,
1681 const FindInDBCallback
& callback
) {
1683 ServiceWorkerDatabase::Status status
=
1684 database
->ReadRegistrationOrigin(registration_id
, &origin
);
1685 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1686 original_task_runner
->PostTask(
1688 base::Bind(callback
, ServiceWorkerDatabase::RegistrationData(),
1689 ResourceList(), status
));
1692 FindForIdInDB(database
, original_task_runner
, registration_id
, origin
,
1696 void ServiceWorkerStorage::GetUserDataInDB(
1697 ServiceWorkerDatabase
* database
,
1698 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1699 int64 registration_id
,
1700 const std::string
& key
,
1701 const GetUserDataInDBCallback
& callback
) {
1703 ServiceWorkerDatabase::Status status
=
1704 database
->ReadUserData(registration_id
, key
, &data
);
1705 original_task_runner
->PostTask(
1706 FROM_HERE
, base::Bind(callback
, data
, status
));
1709 void ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB(
1710 ServiceWorkerDatabase
* database
,
1711 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1712 const std::string
& key
,
1713 const GetUserDataForAllRegistrationsInDBCallback
& callback
) {
1714 std::vector
<std::pair
<int64
, std::string
>> user_data
;
1715 ServiceWorkerDatabase::Status status
=
1716 database
->ReadUserDataForAllRegistrations(key
, &user_data
);
1717 original_task_runner
->PostTask(FROM_HERE
,
1718 base::Bind(callback
, user_data
, status
));
1721 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1722 ServiceWorkerDatabase
* database
,
1723 const std::set
<GURL
>& origins
) {
1726 std::vector
<int64
> newly_purgeable_resources
;
1727 database
->DeleteAllDataForOrigins(origins
, &newly_purgeable_resources
);
1730 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1731 // is transient and it can get healed soon (e.g. IO error). To do that, the
1732 // database should not disable itself when an error occurs and the storage
1733 // controls it instead.
1734 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1735 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1737 if (state_
== DISABLED
) {
1738 // Recovery process has already been scheduled.
1743 DVLOG(1) << "Schedule to delete the context and start over.";
1744 context_
->ScheduleDeleteAndStartOver();
1747 void ServiceWorkerStorage::DidDeleteDatabase(
1748 const StatusCallback
& callback
,
1749 ServiceWorkerDatabase::Status status
) {
1750 DCHECK_EQ(DISABLED
, state_
);
1751 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1752 // Give up the corruption recovery until the browser restarts.
1753 LOG(ERROR
) << "Failed to delete the database: "
1754 << ServiceWorkerDatabase::StatusToString(status
);
1755 callback
.Run(DatabaseStatusToStatusCode(status
));
1758 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1760 // Delete the disk cache on the cache thread.
1761 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1762 // Deleting the directory could take a long time and restart could be delayed.
1763 // We should probably rename the directory and delete it later.
1764 PostTaskAndReplyWithResult(
1765 database_task_manager_
->GetTaskRunner(),
1767 base::Bind(&base::DeleteFile
, GetDiskCachePath(), true),
1768 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache
,
1769 weak_factory_
.GetWeakPtr(),
1773 void ServiceWorkerStorage::DidDeleteDiskCache(
1774 const StatusCallback
& callback
, bool result
) {
1775 DCHECK_EQ(DISABLED
, state_
);
1777 // Give up the corruption recovery until the browser restarts.
1778 LOG(ERROR
) << "Failed to delete the diskcache.";
1779 callback
.Run(SERVICE_WORKER_ERROR_FAILED
);
1782 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1783 callback
.Run(SERVICE_WORKER_OK
);
1786 } // namespace content