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
&
777 base::Bind(&ServiceWorkerStorage::GetUserDataForAllRegistrations
,
778 weak_factory_
.GetWeakPtr(), key
, callback
))) {
779 if (state_
!= INITIALIZING
|| !context_
) {
781 base::Bind(callback
, std::vector
<std::pair
<int64
, std::string
>>(),
782 SERVICE_WORKER_ERROR_FAILED
));
786 DCHECK_EQ(INITIALIZED
, state_
);
790 base::Bind(callback
, std::vector
<std::pair
<int64
, std::string
>>(),
791 SERVICE_WORKER_ERROR_FAILED
));
795 database_task_manager_
->GetTaskRunner()->PostTask(
798 &ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB
,
799 database_
.get(), base::MessageLoopProxy::current(), key
,
800 base::Bind(&ServiceWorkerStorage::DidGetUserDataForAllRegistrations
,
801 weak_factory_
.GetWeakPtr(), callback
)));
804 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback
& callback
) {
807 // Delete the database on the database thread.
808 PostTaskAndReplyWithResult(
809 database_task_manager_
->GetTaskRunner(),
811 base::Bind(&ServiceWorkerDatabase::DestroyDatabase
,
812 base::Unretained(database_
.get())),
813 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase
,
814 weak_factory_
.GetWeakPtr(),
818 int64
ServiceWorkerStorage::NewRegistrationId() {
819 if (state_
== DISABLED
)
820 return kInvalidServiceWorkerRegistrationId
;
821 DCHECK_EQ(INITIALIZED
, state_
);
822 return next_registration_id_
++;
825 int64
ServiceWorkerStorage::NewVersionId() {
826 if (state_
== DISABLED
)
827 return kInvalidServiceWorkerVersionId
;
828 DCHECK_EQ(INITIALIZED
, state_
);
829 return next_version_id_
++;
832 int64
ServiceWorkerStorage::NewResourceId() {
833 if (state_
== DISABLED
)
834 return kInvalidServiceWorkerResourceId
;
835 DCHECK_EQ(INITIALIZED
, state_
);
836 return next_resource_id_
++;
839 void ServiceWorkerStorage::NotifyInstallingRegistration(
840 ServiceWorkerRegistration
* registration
) {
841 DCHECK(installing_registrations_
.find(registration
->id()) ==
842 installing_registrations_
.end());
843 installing_registrations_
[registration
->id()] = registration
;
846 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
847 ServiceWorkerRegistration
* registration
,
848 ServiceWorkerVersion
* version
,
849 ServiceWorkerStatusCode status
) {
850 installing_registrations_
.erase(registration
->id());
851 if (status
!= SERVICE_WORKER_OK
&& version
) {
852 ResourceList resources
;
853 version
->script_cache_map()->GetResources(&resources
);
856 for (size_t i
= 0; i
< resources
.size(); ++i
)
857 ids
.insert(resources
[i
].resource_id
);
859 database_task_manager_
->GetTaskRunner()->PostTask(
861 base::Bind(base::IgnoreResult(
862 &ServiceWorkerDatabase::PurgeUncommittedResourceIds
),
863 base::Unretained(database_
.get()),
868 void ServiceWorkerStorage::NotifyUninstallingRegistration(
869 ServiceWorkerRegistration
* registration
) {
870 DCHECK(uninstalling_registrations_
.find(registration
->id()) ==
871 uninstalling_registrations_
.end());
872 uninstalling_registrations_
[registration
->id()] = registration
;
875 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
876 ServiceWorkerRegistration
* registration
) {
877 uninstalling_registrations_
.erase(registration
->id());
880 void ServiceWorkerStorage::Disable() {
883 disk_cache_
->Disable();
886 bool ServiceWorkerStorage::IsDisabled() const {
887 return state_
== DISABLED
;
890 void ServiceWorkerStorage::PurgeResources(const ResourceList
& resources
) {
891 if (!has_checked_for_stale_resources_
)
892 DeleteStaleResources();
893 StartPurgingResources(resources
);
896 ServiceWorkerStorage::ServiceWorkerStorage(
897 const base::FilePath
& path
,
898 base::WeakPtr
<ServiceWorkerContextCore
> context
,
899 scoped_ptr
<ServiceWorkerDatabaseTaskManager
> database_task_manager
,
900 const scoped_refptr
<base::SingleThreadTaskRunner
>& disk_cache_thread
,
901 storage::QuotaManagerProxy
* quota_manager_proxy
,
902 storage::SpecialStoragePolicy
* special_storage_policy
)
903 : next_registration_id_(kInvalidServiceWorkerRegistrationId
),
904 next_version_id_(kInvalidServiceWorkerVersionId
),
905 next_resource_id_(kInvalidServiceWorkerResourceId
),
906 state_(UNINITIALIZED
),
909 database_task_manager_(database_task_manager
.Pass()),
910 disk_cache_thread_(disk_cache_thread
),
911 quota_manager_proxy_(quota_manager_proxy
),
912 special_storage_policy_(special_storage_policy
),
913 is_purge_pending_(false),
914 has_checked_for_stale_resources_(false),
915 weak_factory_(this) {
916 database_
.reset(new ServiceWorkerDatabase(GetDatabasePath()));
919 base::FilePath
ServiceWorkerStorage::GetDatabasePath() {
921 return base::FilePath();
922 return path_
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
923 .Append(kDatabaseName
);
926 base::FilePath
ServiceWorkerStorage::GetDiskCachePath() {
928 return base::FilePath();
929 return path_
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
930 .Append(kDiskCacheName
);
933 bool ServiceWorkerStorage::LazyInitialize(const base::Closure
& callback
) {
943 pending_tasks_
.push_back(callback
);
946 pending_tasks_
.push_back(callback
);
950 state_
= INITIALIZING
;
951 database_task_manager_
->GetTaskRunner()->PostTask(
953 base::Bind(&ReadInitialDataFromDB
,
955 base::MessageLoopProxy::current(),
956 base::Bind(&ServiceWorkerStorage::DidReadInitialData
,
957 weak_factory_
.GetWeakPtr())));
961 void ServiceWorkerStorage::DidReadInitialData(
963 ServiceWorkerDatabase::Status status
) {
965 DCHECK_EQ(INITIALIZING
, state_
);
967 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
968 next_registration_id_
= data
->next_registration_id
;
969 next_version_id_
= data
->next_version_id
;
970 next_resource_id_
= data
->next_resource_id
;
971 registered_origins_
.swap(data
->origins
);
972 state_
= INITIALIZED
;
974 DVLOG(2) << "Failed to initialize: "
975 << ServiceWorkerDatabase::StatusToString(status
);
976 ScheduleDeleteAndStartOver();
979 for (std::vector
<base::Closure
>::const_iterator it
= pending_tasks_
.begin();
980 it
!= pending_tasks_
.end(); ++it
) {
981 RunSoon(FROM_HERE
, *it
);
983 pending_tasks_
.clear();
986 void ServiceWorkerStorage::DidFindRegistrationForDocument(
987 const GURL
& document_url
,
988 const FindRegistrationCallback
& callback
,
990 const ServiceWorkerDatabase::RegistrationData
& data
,
991 const ResourceList
& resources
,
992 ServiceWorkerDatabase::Status status
) {
993 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
994 ReturnFoundRegistration(callback
, data
, resources
);
995 TRACE_EVENT_ASYNC_END1(
997 "ServiceWorkerStorage::FindRegistrationForDocument",
999 "Status", ServiceWorkerDatabase::StatusToString(status
));
1003 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1004 // Look for something currently being installed.
1005 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
1006 FindInstallingRegistrationForDocument(document_url
);
1007 ServiceWorkerStatusCode installing_status
= installing_registration
.get() ?
1008 SERVICE_WORKER_OK
: SERVICE_WORKER_ERROR_NOT_FOUND
;
1009 callback
.Run(installing_status
, installing_registration
);
1010 TRACE_EVENT_ASYNC_END2(
1012 "ServiceWorkerStorage::FindRegistrationForDocument",
1014 "Status", ServiceWorkerDatabase::StatusToString(status
),
1016 (installing_status
== SERVICE_WORKER_OK
) ?
1017 "Installing registration is found" :
1018 "Any registrations are not found");
1022 ScheduleDeleteAndStartOver();
1023 callback
.Run(DatabaseStatusToStatusCode(status
),
1024 scoped_refptr
<ServiceWorkerRegistration
>());
1025 TRACE_EVENT_ASYNC_END1(
1027 "ServiceWorkerStorage::FindRegistrationForDocument",
1029 "Status", ServiceWorkerDatabase::StatusToString(status
));
1032 void ServiceWorkerStorage::DidFindRegistrationForPattern(
1034 const FindRegistrationCallback
& callback
,
1035 const ServiceWorkerDatabase::RegistrationData
& data
,
1036 const ResourceList
& resources
,
1037 ServiceWorkerDatabase::Status status
) {
1038 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
1039 ReturnFoundRegistration(callback
, data
, resources
);
1043 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1044 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
1045 FindInstallingRegistrationForPattern(scope
);
1046 callback
.Run(installing_registration
.get() ? SERVICE_WORKER_OK
1047 : SERVICE_WORKER_ERROR_NOT_FOUND
,
1048 installing_registration
);
1052 ScheduleDeleteAndStartOver();
1053 callback
.Run(DatabaseStatusToStatusCode(status
),
1054 scoped_refptr
<ServiceWorkerRegistration
>());
1057 void ServiceWorkerStorage::DidFindRegistrationForId(
1058 const FindRegistrationCallback
& callback
,
1059 const ServiceWorkerDatabase::RegistrationData
& data
,
1060 const ResourceList
& resources
,
1061 ServiceWorkerDatabase::Status status
) {
1062 if (status
== ServiceWorkerDatabase::STATUS_OK
) {
1063 ReturnFoundRegistration(callback
, data
, resources
);
1067 if (status
== ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1068 // TODO(nhiroki): Find a registration in |installing_registrations_|.
1069 callback
.Run(DatabaseStatusToStatusCode(status
),
1070 scoped_refptr
<ServiceWorkerRegistration
>());
1074 ScheduleDeleteAndStartOver();
1075 callback
.Run(DatabaseStatusToStatusCode(status
),
1076 scoped_refptr
<ServiceWorkerRegistration
>());
1079 void ServiceWorkerStorage::ReturnFoundRegistration(
1080 const FindRegistrationCallback
& callback
,
1081 const ServiceWorkerDatabase::RegistrationData
& data
,
1082 const ResourceList
& resources
) {
1083 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1084 GetOrCreateRegistration(data
, resources
);
1085 CompleteFindNow(registration
, SERVICE_WORKER_OK
, callback
);
1088 void ServiceWorkerStorage::DidGetRegistrations(
1089 const GetRegistrationsInfosCallback
& callback
,
1090 RegistrationList
* registrations
,
1091 const GURL
& origin_filter
,
1092 ServiceWorkerDatabase::Status status
) {
1093 DCHECK(registrations
);
1094 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1095 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1096 ScheduleDeleteAndStartOver();
1097 callback
.Run(std::vector
<ServiceWorkerRegistrationInfo
>());
1101 // Add all stored registrations.
1102 std::set
<int64
> pushed_registrations
;
1103 std::vector
<ServiceWorkerRegistrationInfo
> infos
;
1104 for (const auto& registration_data
: *registrations
) {
1105 const bool inserted
=
1106 pushed_registrations
.insert(registration_data
.registration_id
).second
;
1109 ServiceWorkerRegistration
* registration
=
1110 context_
->GetLiveRegistration(registration_data
.registration_id
);
1112 infos
.push_back(registration
->GetInfo());
1116 ServiceWorkerRegistrationInfo info
;
1117 info
.pattern
= registration_data
.scope
;
1118 info
.registration_id
= registration_data
.registration_id
;
1119 info
.stored_version_size_bytes
=
1120 registration_data
.resources_total_size_bytes
;
1121 if (ServiceWorkerVersion
* version
=
1122 context_
->GetLiveVersion(registration_data
.version_id
)) {
1123 if (registration_data
.is_active
)
1124 info
.active_version
= version
->GetInfo();
1126 info
.waiting_version
= version
->GetInfo();
1127 infos
.push_back(info
);
1131 if (registration_data
.is_active
) {
1132 info
.active_version
.status
= ServiceWorkerVersion::ACTIVATED
;
1133 info
.active_version
.script_url
= registration_data
.script
;
1134 info
.active_version
.version_id
= registration_data
.version_id
;
1135 info
.active_version
.registration_id
= registration_data
.registration_id
;
1137 info
.waiting_version
.status
= ServiceWorkerVersion::INSTALLED
;
1138 info
.waiting_version
.script_url
= registration_data
.script
;
1139 info
.waiting_version
.version_id
= registration_data
.version_id
;
1140 info
.waiting_version
.registration_id
= registration_data
.registration_id
;
1142 infos
.push_back(info
);
1145 // Add unstored registrations that are being installed.
1146 for (RegistrationRefsById::const_iterator it
=
1147 installing_registrations_
.begin();
1148 it
!= installing_registrations_
.end(); ++it
) {
1149 if ((!origin_filter
.is_valid() ||
1150 it
->second
->pattern().GetOrigin() == origin_filter
) &&
1151 pushed_registrations
.insert(it
->first
).second
) {
1152 infos
.push_back(it
->second
->GetInfo());
1156 callback
.Run(infos
);
1159 void ServiceWorkerStorage::DidStoreRegistration(
1160 const StatusCallback
& callback
,
1161 const ServiceWorkerDatabase::RegistrationData
& new_version
,
1163 const ServiceWorkerDatabase::RegistrationData
& deleted_version
,
1164 const std::vector
<int64
>& newly_purgeable_resources
,
1165 ServiceWorkerDatabase::Status status
) {
1166 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1167 ScheduleDeleteAndStartOver();
1168 callback
.Run(DatabaseStatusToStatusCode(status
));
1171 registered_origins_
.insert(origin
);
1173 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1174 context_
->GetLiveRegistration(new_version
.registration_id
);
1175 registration
->set_resources_total_size_bytes(
1176 new_version
.resources_total_size_bytes
);
1177 if (quota_manager_proxy_
.get()) {
1178 // Can be nullptr in tests.
1179 quota_manager_proxy_
->NotifyStorageModified(
1180 storage::QuotaClient::kServiceWorker
,
1182 storage::StorageType::kStorageTypeTemporary
,
1183 new_version
.resources_total_size_bytes
-
1184 deleted_version
.resources_total_size_bytes
);
1187 callback
.Run(SERVICE_WORKER_OK
);
1189 if (!context_
|| !context_
->GetLiveVersion(deleted_version
.version_id
))
1190 StartPurgingResources(newly_purgeable_resources
);
1193 void ServiceWorkerStorage::DidUpdateToActiveState(
1194 const StatusCallback
& callback
,
1195 ServiceWorkerDatabase::Status status
) {
1196 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1197 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1198 ScheduleDeleteAndStartOver();
1200 callback
.Run(DatabaseStatusToStatusCode(status
));
1203 void ServiceWorkerStorage::DidDeleteRegistration(
1204 const DidDeleteRegistrationParams
& params
,
1205 bool origin_is_deletable
,
1206 const ServiceWorkerDatabase::RegistrationData
& deleted_version
,
1207 const std::vector
<int64
>& newly_purgeable_resources
,
1208 ServiceWorkerDatabase::Status status
) {
1209 pending_deletions_
.erase(params
.registration_id
);
1210 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1211 ScheduleDeleteAndStartOver();
1212 params
.callback
.Run(DatabaseStatusToStatusCode(status
));
1215 if (quota_manager_proxy_
.get()) {
1216 // Can be nullptr in tests.
1217 quota_manager_proxy_
->NotifyStorageModified(
1218 storage::QuotaClient::kServiceWorker
,
1220 storage::StorageType::kStorageTypeTemporary
,
1221 -deleted_version
.resources_total_size_bytes
);
1223 if (origin_is_deletable
)
1224 registered_origins_
.erase(params
.origin
);
1225 params
.callback
.Run(SERVICE_WORKER_OK
);
1227 if (!context_
|| !context_
->GetLiveVersion(deleted_version
.version_id
))
1228 StartPurgingResources(newly_purgeable_resources
);
1231 void ServiceWorkerStorage::DidStoreUserData(
1232 const StatusCallback
& callback
,
1233 ServiceWorkerDatabase::Status status
) {
1234 // |status| can be NOT_FOUND when the associated registration did not exist in
1235 // the database. In the case, we don't have to schedule the corruption
1237 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1238 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1239 ScheduleDeleteAndStartOver();
1241 callback
.Run(DatabaseStatusToStatusCode(status
));
1244 void ServiceWorkerStorage::DidGetUserData(
1245 const GetUserDataCallback
& callback
,
1246 const std::string
& data
,
1247 ServiceWorkerDatabase::Status status
) {
1248 if (status
!= ServiceWorkerDatabase::STATUS_OK
&&
1249 status
!= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
) {
1250 ScheduleDeleteAndStartOver();
1252 callback
.Run(data
, DatabaseStatusToStatusCode(status
));
1255 void ServiceWorkerStorage::DidDeleteUserData(
1256 const StatusCallback
& callback
,
1257 ServiceWorkerDatabase::Status status
) {
1258 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1259 ScheduleDeleteAndStartOver();
1260 callback
.Run(DatabaseStatusToStatusCode(status
));
1263 void ServiceWorkerStorage::DidGetUserDataForAllRegistrations(
1264 const GetUserDataForAllRegistrationsCallback
& callback
,
1265 const std::vector
<std::pair
<int64
, std::string
>>& user_data
,
1266 ServiceWorkerDatabase::Status status
) {
1267 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1268 ScheduleDeleteAndStartOver();
1269 callback
.Run(user_data
, DatabaseStatusToStatusCode(status
));
1272 scoped_refptr
<ServiceWorkerRegistration
>
1273 ServiceWorkerStorage::GetOrCreateRegistration(
1274 const ServiceWorkerDatabase::RegistrationData
& data
,
1275 const ResourceList
& resources
) {
1276 scoped_refptr
<ServiceWorkerRegistration
> registration
=
1277 context_
->GetLiveRegistration(data
.registration_id
);
1278 if (registration
.get())
1279 return registration
;
1281 registration
= new ServiceWorkerRegistration(
1282 data
.scope
, data
.registration_id
, context_
);
1283 registration
->set_resources_total_size_bytes(data
.resources_total_size_bytes
);
1284 registration
->set_last_update_check(data
.last_update_check
);
1285 if (pending_deletions_
.find(data
.registration_id
) !=
1286 pending_deletions_
.end()) {
1287 registration
->set_is_deleted(true);
1289 scoped_refptr
<ServiceWorkerVersion
> version
=
1290 context_
->GetLiveVersion(data
.version_id
);
1291 if (!version
.get()) {
1292 version
= new ServiceWorkerVersion(
1293 registration
.get(), data
.script
, data
.version_id
, context_
);
1294 version
->SetStatus(data
.is_active
?
1295 ServiceWorkerVersion::ACTIVATED
: ServiceWorkerVersion::INSTALLED
);
1296 version
->script_cache_map()->SetResources(resources
);
1299 if (version
->status() == ServiceWorkerVersion::ACTIVATED
)
1300 registration
->SetActiveVersion(version
.get());
1301 else if (version
->status() == ServiceWorkerVersion::INSTALLED
)
1302 registration
->SetWaitingVersion(version
.get());
1306 return registration
;
1309 ServiceWorkerRegistration
*
1310 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1311 const GURL
& document_url
) {
1312 DCHECK(!document_url
.has_ref());
1314 LongestScopeMatcher
matcher(document_url
);
1315 ServiceWorkerRegistration
* match
= NULL
;
1317 // TODO(nhiroki): This searches over installing registrations linearly and it
1318 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1319 for (RegistrationRefsById::const_iterator it
=
1320 installing_registrations_
.begin();
1321 it
!= installing_registrations_
.end(); ++it
) {
1322 if (matcher
.MatchLongest(it
->second
->pattern()))
1323 match
= it
->second
.get();
1328 ServiceWorkerRegistration
*
1329 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1330 const GURL
& scope
) {
1331 for (RegistrationRefsById::const_iterator it
=
1332 installing_registrations_
.begin();
1333 it
!= installing_registrations_
.end(); ++it
) {
1334 if (it
->second
->pattern() == scope
)
1335 return it
->second
.get();
1340 ServiceWorkerRegistration
*
1341 ServiceWorkerStorage::FindInstallingRegistrationForId(
1342 int64 registration_id
) {
1343 RegistrationRefsById::const_iterator found
=
1344 installing_registrations_
.find(registration_id
);
1345 if (found
== installing_registrations_
.end())
1347 return found
->second
.get();
1350 ServiceWorkerDiskCache
* ServiceWorkerStorage::disk_cache() {
1352 return disk_cache_
.get();
1354 disk_cache_
.reset(new ServiceWorkerDiskCache
);
1356 base::FilePath path
= GetDiskCachePath();
1358 int rv
= disk_cache_
->InitWithMemBackend(kMaxMemDiskCacheSize
,
1359 net::CompletionCallback());
1360 DCHECK_EQ(net::OK
, rv
);
1361 return disk_cache_
.get();
1364 int rv
= disk_cache_
->InitWithDiskBackend(
1369 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized
,
1370 weak_factory_
.GetWeakPtr()));
1371 if (rv
!= net::ERR_IO_PENDING
)
1372 OnDiskCacheInitialized(rv
);
1374 return disk_cache_
.get();
1377 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv
) {
1378 if (rv
!= net::OK
) {
1379 LOG(ERROR
) << "Failed to open the serviceworker diskcache: "
1380 << net::ErrorToString(rv
);
1381 ScheduleDeleteAndStartOver();
1383 ServiceWorkerMetrics::CountInitDiskCacheResult(rv
== net::OK
);
1386 void ServiceWorkerStorage::StartPurgingResources(
1387 const std::vector
<int64
>& ids
) {
1388 DCHECK(has_checked_for_stale_resources_
);
1389 for (size_t i
= 0; i
< ids
.size(); ++i
)
1390 purgeable_resource_ids_
.push_back(ids
[i
]);
1391 ContinuePurgingResources();
1394 void ServiceWorkerStorage::StartPurgingResources(
1395 const ResourceList
& resources
) {
1396 DCHECK(has_checked_for_stale_resources_
);
1397 for (size_t i
= 0; i
< resources
.size(); ++i
)
1398 purgeable_resource_ids_
.push_back(resources
[i
].resource_id
);
1399 ContinuePurgingResources();
1402 void ServiceWorkerStorage::ContinuePurgingResources() {
1403 if (purgeable_resource_ids_
.empty() || is_purge_pending_
)
1406 // Do one at a time until we're done, use RunSoon to avoid recursion when
1407 // DoomEntry returns immediately.
1408 is_purge_pending_
= true;
1409 int64 id
= purgeable_resource_ids_
.front();
1410 purgeable_resource_ids_
.pop_front();
1412 base::Bind(&ServiceWorkerStorage::PurgeResource
,
1413 weak_factory_
.GetWeakPtr(), id
));
1416 void ServiceWorkerStorage::PurgeResource(int64 id
) {
1417 DCHECK(is_purge_pending_
);
1418 int rv
= disk_cache()->DoomEntry(
1419 id
, base::Bind(&ServiceWorkerStorage::OnResourcePurged
,
1420 weak_factory_
.GetWeakPtr(), id
));
1421 if (rv
!= net::ERR_IO_PENDING
)
1422 OnResourcePurged(id
, rv
);
1425 void ServiceWorkerStorage::OnResourcePurged(int64 id
, int rv
) {
1426 DCHECK(is_purge_pending_
);
1427 is_purge_pending_
= false;
1429 database_task_manager_
->GetTaskRunner()->PostTask(
1431 base::Bind(base::IgnoreResult(
1432 &ServiceWorkerDatabase::ClearPurgeableResourceIds
),
1433 base::Unretained(database_
.get()),
1434 std::set
<int64
>(&id
, &id
+ 1)));
1436 ContinuePurgingResources();
1439 void ServiceWorkerStorage::DeleteStaleResources() {
1440 DCHECK(!has_checked_for_stale_resources_
);
1441 has_checked_for_stale_resources_
= true;
1442 database_task_manager_
->GetTaskRunner()->PostTask(
1444 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB
,
1446 base::MessageLoopProxy::current(),
1447 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources
,
1448 weak_factory_
.GetWeakPtr())));
1451 void ServiceWorkerStorage::DidCollectStaleResources(
1452 const std::vector
<int64
>& stale_resource_ids
,
1453 ServiceWorkerDatabase::Status status
) {
1454 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK
, status
);
1455 if (status
!= ServiceWorkerDatabase::STATUS_OK
)
1457 StartPurgingResources(stale_resource_ids
);
1460 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1461 // Can be null in tests.
1462 if (!special_storage_policy_
.get())
1465 if (!special_storage_policy_
->HasSessionOnlyOrigins())
1468 std::set
<GURL
> session_only_origins
;
1469 for (const GURL
& origin
: registered_origins_
) {
1470 if (special_storage_policy_
->IsStorageSessionOnly(origin
))
1471 session_only_origins
.insert(origin
);
1474 database_task_manager_
->GetShutdownBlockingTaskRunner()->PostTask(
1476 base::Bind(&DeleteAllDataForOriginsFromDB
,
1478 session_only_origins
));
1481 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1482 ServiceWorkerDatabase
* database
,
1483 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1484 const GetResourcesCallback
& callback
) {
1485 std::set
<int64
> ids
;
1486 ServiceWorkerDatabase::Status status
=
1487 database
->GetUncommittedResourceIds(&ids
);
1488 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1489 original_task_runner
->PostTask(
1492 callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1496 status
= database
->PurgeUncommittedResourceIds(ids
);
1497 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1498 original_task_runner
->PostTask(
1501 callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1506 status
= database
->GetPurgeableResourceIds(&ids
);
1507 original_task_runner
->PostTask(
1509 base::Bind(callback
, std::vector
<int64
>(ids
.begin(), ids
.end()), status
));
1512 void ServiceWorkerStorage::ReadInitialDataFromDB(
1513 ServiceWorkerDatabase
* database
,
1514 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1515 const InitializeCallback
& callback
) {
1517 scoped_ptr
<ServiceWorkerStorage::InitialData
> data(
1518 new ServiceWorkerStorage::InitialData());
1520 ServiceWorkerDatabase::Status status
=
1521 database
->GetNextAvailableIds(&data
->next_registration_id
,
1522 &data
->next_version_id
,
1523 &data
->next_resource_id
);
1524 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1525 original_task_runner
->PostTask(
1526 FROM_HERE
, base::Bind(callback
, base::Owned(data
.release()), status
));
1530 status
= database
->GetOriginsWithRegistrations(&data
->origins
);
1531 original_task_runner
->PostTask(
1532 FROM_HERE
, base::Bind(callback
, base::Owned(data
.release()), status
));
1535 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1536 ServiceWorkerDatabase
* database
,
1537 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1538 int64 registration_id
,
1540 const DeleteRegistrationCallback
& callback
) {
1543 ServiceWorkerDatabase::RegistrationData deleted_version
;
1544 std::vector
<int64
> newly_purgeable_resources
;
1545 ServiceWorkerDatabase::Status status
= database
->DeleteRegistration(
1546 registration_id
, origin
, &deleted_version
, &newly_purgeable_resources
);
1547 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1548 original_task_runner
->PostTask(
1551 callback
, false, deleted_version
, std::vector
<int64
>(), status
));
1555 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1556 // unique origin list.
1557 std::vector
<ServiceWorkerDatabase::RegistrationData
> registrations
;
1558 status
= database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1559 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1560 original_task_runner
->PostTask(
1563 callback
, false, deleted_version
, std::vector
<int64
>(), status
));
1567 bool deletable
= registrations
.empty();
1568 original_task_runner
->PostTask(FROM_HERE
,
1569 base::Bind(callback
,
1572 newly_purgeable_resources
,
1576 void ServiceWorkerStorage::WriteRegistrationInDB(
1577 ServiceWorkerDatabase
* database
,
1578 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1579 const ServiceWorkerDatabase::RegistrationData
& data
,
1580 const ResourceList
& resources
,
1581 const WriteRegistrationCallback
& callback
) {
1583 ServiceWorkerDatabase::RegistrationData deleted_version
;
1584 std::vector
<int64
> newly_purgeable_resources
;
1585 ServiceWorkerDatabase::Status status
= database
->WriteRegistration(
1586 data
, resources
, &deleted_version
, &newly_purgeable_resources
);
1587 original_task_runner
->PostTask(FROM_HERE
,
1588 base::Bind(callback
,
1589 data
.script
.GetOrigin(),
1591 newly_purgeable_resources
,
1595 void ServiceWorkerStorage::FindForDocumentInDB(
1596 ServiceWorkerDatabase
* database
,
1597 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1598 const GURL
& document_url
,
1599 const FindInDBCallback
& callback
) {
1600 GURL origin
= document_url
.GetOrigin();
1601 RegistrationList registrations
;
1602 ServiceWorkerDatabase::Status status
=
1603 database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1604 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1605 original_task_runner
->PostTask(
1607 base::Bind(callback
,
1608 ServiceWorkerDatabase::RegistrationData(),
1614 ServiceWorkerDatabase::RegistrationData data
;
1615 ResourceList resources
;
1616 status
= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
1618 // Find one with a pattern match.
1619 LongestScopeMatcher
matcher(document_url
);
1620 int64 match
= kInvalidServiceWorkerRegistrationId
;
1621 for (size_t i
= 0; i
< registrations
.size(); ++i
) {
1622 if (matcher
.MatchLongest(registrations
[i
].scope
))
1623 match
= registrations
[i
].registration_id
;
1626 if (match
!= kInvalidServiceWorkerRegistrationId
)
1627 status
= database
->ReadRegistration(match
, origin
, &data
, &resources
);
1629 original_task_runner
->PostTask(
1631 base::Bind(callback
, data
, resources
, status
));
1634 void ServiceWorkerStorage::FindForPatternInDB(
1635 ServiceWorkerDatabase
* database
,
1636 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1638 const FindInDBCallback
& callback
) {
1639 GURL origin
= scope
.GetOrigin();
1640 std::vector
<ServiceWorkerDatabase::RegistrationData
> registrations
;
1641 ServiceWorkerDatabase::Status status
=
1642 database
->GetRegistrationsForOrigin(origin
, ®istrations
);
1643 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1644 original_task_runner
->PostTask(
1646 base::Bind(callback
,
1647 ServiceWorkerDatabase::RegistrationData(),
1653 // Find one with an exact matching scope.
1654 ServiceWorkerDatabase::RegistrationData data
;
1655 ResourceList resources
;
1656 status
= ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
1657 for (RegistrationList::const_iterator it
= registrations
.begin();
1658 it
!= registrations
.end(); ++it
) {
1659 if (scope
!= it
->scope
)
1661 status
= database
->ReadRegistration(it
->registration_id
, origin
,
1663 break; // We're done looping.
1666 original_task_runner
->PostTask(
1668 base::Bind(callback
, data
, resources
, status
));
1671 void ServiceWorkerStorage::FindForIdInDB(
1672 ServiceWorkerDatabase
* database
,
1673 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1674 int64 registration_id
,
1676 const FindInDBCallback
& callback
) {
1677 ServiceWorkerDatabase::RegistrationData data
;
1678 ResourceList resources
;
1679 ServiceWorkerDatabase::Status status
=
1680 database
->ReadRegistration(registration_id
, origin
, &data
, &resources
);
1681 original_task_runner
->PostTask(
1682 FROM_HERE
, base::Bind(callback
, data
, resources
, status
));
1685 void ServiceWorkerStorage::FindForIdOnlyInDB(
1686 ServiceWorkerDatabase
* database
,
1687 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1688 int64 registration_id
,
1689 const FindInDBCallback
& callback
) {
1691 ServiceWorkerDatabase::Status status
=
1692 database
->ReadRegistrationOrigin(registration_id
, &origin
);
1693 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1694 original_task_runner
->PostTask(
1696 base::Bind(callback
, ServiceWorkerDatabase::RegistrationData(),
1697 ResourceList(), status
));
1700 FindForIdInDB(database
, original_task_runner
, registration_id
, origin
,
1704 void ServiceWorkerStorage::GetUserDataInDB(
1705 ServiceWorkerDatabase
* database
,
1706 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1707 int64 registration_id
,
1708 const std::string
& key
,
1709 const GetUserDataInDBCallback
& callback
) {
1711 ServiceWorkerDatabase::Status status
=
1712 database
->ReadUserData(registration_id
, key
, &data
);
1713 original_task_runner
->PostTask(
1714 FROM_HERE
, base::Bind(callback
, data
, status
));
1717 void ServiceWorkerStorage::GetUserDataForAllRegistrationsInDB(
1718 ServiceWorkerDatabase
* database
,
1719 scoped_refptr
<base::SequencedTaskRunner
> original_task_runner
,
1720 const std::string
& key
,
1721 const GetUserDataForAllRegistrationsInDBCallback
& callback
) {
1722 std::vector
<std::pair
<int64
, std::string
>> user_data
;
1723 ServiceWorkerDatabase::Status status
=
1724 database
->ReadUserDataForAllRegistrations(key
, &user_data
);
1725 original_task_runner
->PostTask(FROM_HERE
,
1726 base::Bind(callback
, user_data
, status
));
1729 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
1730 ServiceWorkerDatabase
* database
,
1731 const std::set
<GURL
>& origins
) {
1734 std::vector
<int64
> newly_purgeable_resources
;
1735 database
->DeleteAllDataForOrigins(origins
, &newly_purgeable_resources
);
1738 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1739 // is transient and it can get healed soon (e.g. IO error). To do that, the
1740 // database should not disable itself when an error occurs and the storage
1741 // controls it instead.
1742 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1743 // TODO(dmurph): Notify the quota manager somehow that all of our data is now
1745 if (state_
== DISABLED
) {
1746 // Recovery process has already been scheduled.
1751 DVLOG(1) << "Schedule to delete the context and start over.";
1752 context_
->ScheduleDeleteAndStartOver();
1755 void ServiceWorkerStorage::DidDeleteDatabase(
1756 const StatusCallback
& callback
,
1757 ServiceWorkerDatabase::Status status
) {
1758 DCHECK_EQ(DISABLED
, state_
);
1759 if (status
!= ServiceWorkerDatabase::STATUS_OK
) {
1760 // Give up the corruption recovery until the browser restarts.
1761 LOG(ERROR
) << "Failed to delete the database: "
1762 << ServiceWorkerDatabase::StatusToString(status
);
1763 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1764 ServiceWorkerMetrics::DELETE_DATABASE_ERROR
);
1765 callback
.Run(DatabaseStatusToStatusCode(status
));
1768 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1770 // Delete the disk cache on the cache thread.
1771 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1772 // Deleting the directory could take a long time and restart could be delayed.
1773 // We should probably rename the directory and delete it later.
1774 PostTaskAndReplyWithResult(
1775 database_task_manager_
->GetTaskRunner(),
1777 base::Bind(&base::DeleteFile
, GetDiskCachePath(), true),
1778 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache
,
1779 weak_factory_
.GetWeakPtr(),
1783 void ServiceWorkerStorage::DidDeleteDiskCache(
1784 const StatusCallback
& callback
, bool result
) {
1785 DCHECK_EQ(DISABLED
, state_
);
1787 // Give up the corruption recovery until the browser restarts.
1788 LOG(ERROR
) << "Failed to delete the diskcache.";
1789 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1790 ServiceWorkerMetrics::DELETE_DISK_CACHE_ERROR
);
1791 callback
.Run(SERVICE_WORKER_ERROR_FAILED
);
1794 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1795 ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
1796 ServiceWorkerMetrics::DELETE_OK
);
1797 callback
.Run(SERVICE_WORKER_OK
);
1800 } // namespace content