ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_storage.cc
blobad3d3d49e471b1d82c1b53ad929b4934b76f7cb8
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"
29 namespace content {
31 namespace {
33 void RunSoon(const tracked_objects::Location& from_here,
34 const base::Closure& closure) {
35 base::MessageLoop::current()->PostTask(from_here, closure);
38 void CompleteFindNow(
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);
45 return;
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) {
55 RunSoon(from_here,
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) {
69 switch (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:
75 NOTREACHED();
76 default:
77 return SERVICE_WORKER_ERROR_FAILED;
81 class ResponseComparer : public base::RefCounted<ResponseComparer> {
82 public:
83 ResponseComparer(
84 base::WeakPtr<ServiceWorkerStorage> owner,
85 scoped_ptr<ServiceWorkerResponseReader> lhs,
86 scoped_ptr<ServiceWorkerResponseReader> rhs,
87 const ServiceWorkerStorage::CompareCallback& callback)
88 : owner_(owner),
89 completion_callback_(callback),
90 lhs_reader_(lhs.release()),
91 rhs_reader_(rhs.release()),
92 completion_count_(0),
93 previous_result_(0) {
96 void Start();
98 private:
99 friend class base::RefCounted<ResponseComparer>;
101 static const int kBufferSize = 16 * 1024;
103 ~ResponseComparer() {}
104 void ReadInfos();
105 void OnReadInfoComplete(int result);
106 void ReadSomeData();
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();
128 ReadInfos();
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_)
140 return;
141 if (result < 0) {
142 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
143 completion_callback_.Reset();
144 return;
146 if (++completion_count_ != 2)
147 return;
149 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
150 completion_callback_.Run(SERVICE_WORKER_OK, false);
151 return;
153 ReadSomeData();
156 void ResponseComparer::ReadSomeData() {
157 completion_count_ = 0;
158 lhs_reader_->ReadData(
159 lhs_buffer_.get(),
160 kBufferSize,
161 base::Bind(&ResponseComparer::OnReadDataComplete, this));
162 rhs_reader_->ReadData(
163 rhs_buffer_.get(),
164 kBufferSize,
165 base::Bind(&ResponseComparer::OnReadDataComplete, this));
168 void ResponseComparer::OnReadDataComplete(int result) {
169 if (completion_callback_.is_null() || !owner_)
170 return;
171 if (result < 0) {
172 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
173 completion_callback_.Reset();
174 return;
176 if (++completion_count_ != 2) {
177 previous_result_ = result;
178 return;
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
183 // in that case.
184 if (result != previous_result_) {
185 completion_callback_.Run(SERVICE_WORKER_OK, false);
186 return;
189 if (result == 0) {
190 completion_callback_.Run(SERVICE_WORKER_OK, true);
191 return;
194 int compare_result =
195 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
196 if (compare_result != 0) {
197 completion_callback_.Run(SERVICE_WORKER_OK, false);
198 return;
201 ReadSomeData();
204 } // namespace
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());
231 // static
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,
240 context,
241 database_task_manager.Pass(),
242 disk_cache_thread,
243 quota_manager_proxy,
244 special_storage_policy));
247 // static
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_,
253 context,
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(
272 "ServiceWorker",
273 "ServiceWorkerStorage::FindRegistrationForDocument:LazyInitialize",
274 TRACE_EVENT_SCOPE_THREAD,
275 "URL", document_url.spec());
276 return;
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(
288 "ServiceWorker",
289 "ServiceWorkerStorage::FindRegistrationForDocument:CheckInstalling",
290 TRACE_EVENT_SCOPE_THREAD,
291 "URL", document_url.spec(),
292 "Status", ServiceWorkerStatusToString(status));
293 CompleteFindNow(installing_registration,
294 status,
295 callback);
296 return;
299 // To connect this TRACE_EVENT with the callback, TimeTicks is used for
300 // callback id.
301 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
302 TRACE_EVENT_ASYNC_BEGIN1(
303 "ServiceWorker",
304 "ServiceWorkerStorage::FindRegistrationForDocument",
305 callback_id,
306 "URL", document_url.spec());
307 database_task_manager_->GetTaskRunner()->PostTask(
308 FROM_HERE,
309 base::Bind(
310 &FindForDocumentInDB,
311 database_.get(),
312 base::MessageLoopProxy::current(),
313 document_url,
314 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
315 weak_factory_.GetWeakPtr(),
316 document_url,
317 callback,
318 callback_id)));
321 void ServiceWorkerStorage::FindRegistrationForPattern(
322 const GURL& scope,
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);
331 return;
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()
343 ? SERVICE_WORKER_OK
344 : SERVICE_WORKER_ERROR_NOT_FOUND,
345 callback);
346 return;
349 database_task_manager_->GetTaskRunner()->PostTask(
350 FROM_HERE,
351 base::Bind(
352 &FindForPatternInDB,
353 database_.get(),
354 base::MessageLoopProxy::current(),
355 scope,
356 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
357 weak_factory_.GetWeakPtr(), scope, callback)));
360 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
361 const GURL& scope) {
362 if (state_ != INITIALIZED || !context_)
363 return NULL;
364 for (RegistrationRefsById::const_iterator it =
365 uninstalling_registrations_.begin();
366 it != uninstalling_registrations_.end();
367 ++it) {
368 if (it->second->pattern() == scope) {
369 DCHECK(it->second->is_uninstalling());
370 return it->second.get();
373 return NULL;
376 void ServiceWorkerStorage::FindRegistrationForId(
377 int64 registration_id,
378 const GURL& origin,
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);
387 return;
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()
398 ? SERVICE_WORKER_OK
399 : SERVICE_WORKER_ERROR_NOT_FOUND,
400 callback);
401 return;
404 scoped_refptr<ServiceWorkerRegistration> registration =
405 context_->GetLiveRegistration(registration_id);
406 if (registration.get()) {
407 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
408 return;
411 database_task_manager_->GetTaskRunner()->PostTask(
412 FROM_HERE,
413 base::Bind(&FindForIdInDB,
414 database_.get(),
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) {
424 if (!LazyInitialize(
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);
430 return;
432 DCHECK_EQ(INITIALIZED, state_);
434 scoped_refptr<ServiceWorkerRegistration> registration =
435 context_->GetLiveRegistration(registration_id);
436 if (registration) {
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(),
442 callback);
443 return;
446 database_task_manager_->GetTaskRunner()->PostTask(
447 FROM_HERE,
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>()));
463 return;
465 DCHECK_EQ(INITIALIZED, state_);
467 RegistrationList* registrations = new RegistrationList;
468 PostTaskAndReplyWithResult(
469 database_task_manager_->GetTaskRunner(),
470 FROM_HERE,
471 base::Bind(&ServiceWorkerDatabase::GetRegistrationsForOrigin,
472 base::Unretained(database_.get()),
473 origin,
474 base::Unretained(registrations)),
475 base::Bind(&ServiceWorkerStorage::DidGetRegistrations,
476 weak_factory_.GetWeakPtr(),
477 callback,
478 base::Owned(registrations),
479 origin));
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>()));
491 return;
493 DCHECK_EQ(INITIALIZED, state_);
495 RegistrationList* registrations = new RegistrationList;
496 PostTaskAndReplyWithResult(
497 database_task_manager_->GetTaskRunner(),
498 FROM_HERE,
499 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
500 base::Unretained(database_.get()),
501 base::Unretained(registrations)),
502 base::Bind(&ServiceWorkerStorage::DidGetRegistrations,
503 weak_factory_.GetWeakPtr(),
504 callback,
505 base::Owned(registrations),
506 GURL()));
509 void ServiceWorkerStorage::StoreRegistration(
510 ServiceWorkerRegistration* registration,
511 ServiceWorkerVersion* version,
512 const StatusCallback& callback) {
513 DCHECK(registration);
514 DCHECK(version);
516 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
517 if (IsDisabled() || !context_) {
518 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
519 return;
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(
544 FROM_HERE,
545 base::Bind(&WriteRegistrationInDB,
546 database_.get(),
547 base::MessageLoopProxy::current(),
548 data,
549 resources,
550 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
551 weak_factory_.GetWeakPtr(),
552 callback,
553 data)));
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));
566 return;
569 PostTaskAndReplyWithResult(
570 database_task_manager_->GetTaskRunner(),
571 FROM_HERE,
572 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
573 base::Unretained(database_.get()),
574 registration->id(),
575 registration->pattern().GetOrigin()),
576 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
577 weak_factory_.GetWeakPtr(),
578 callback));
581 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
582 ServiceWorkerRegistration* registration) {
583 DCHECK(registration);
585 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
586 if (IsDisabled() || !context_)
587 return;
589 database_task_manager_->GetTaskRunner()->PostTask(
590 FROM_HERE,
591 base::Bind(
592 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
593 base::Unretained(database_.get()),
594 registration->id(),
595 registration->pattern().GetOrigin(),
596 registration->last_update_check()));
599 void ServiceWorkerStorage::DeleteRegistration(
600 int64 registration_id,
601 const GURL& origin,
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));
606 return;
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(
618 FROM_HERE,
619 base::Bind(&DeleteRegistrationFromDB,
620 database_.get(),
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);
630 if (registration)
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(
660 FROM_HERE,
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(
670 FROM_HERE,
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),
686 callback);
687 comparer->Start(); // It deletes itself when done.
690 void ServiceWorkerStorage::StoreUserData(
691 int64 registration_id,
692 const GURL& origin,
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));
699 return;
702 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
703 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
704 return;
707 PostTaskAndReplyWithResult(
708 database_task_manager_->GetTaskRunner(),
709 FROM_HERE,
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(),
715 callback));
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_) {
724 RunSoon(FROM_HERE,
725 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
726 return;
729 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
730 RunSoon(FROM_HERE,
731 base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_FAILED));
732 return;
735 database_task_manager_->GetTaskRunner()->PostTask(
736 FROM_HERE,
737 base::Bind(&ServiceWorkerStorage::GetUserDataInDB,
738 database_.get(),
739 base::MessageLoopProxy::current(),
740 registration_id,
741 key,
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));
753 return;
756 if (registration_id == kInvalidServiceWorkerRegistrationId || key.empty()) {
757 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
758 return;
761 PostTaskAndReplyWithResult(
762 database_task_manager_->GetTaskRunner(),
763 FROM_HERE,
764 base::Bind(&ServiceWorkerDatabase::DeleteUserData,
765 base::Unretained(database_.get()),
766 registration_id, key),
767 base::Bind(&ServiceWorkerStorage::DidDeleteUserData,
768 weak_factory_.GetWeakPtr(),
769 callback));
772 void ServiceWorkerStorage::GetUserDataForAllRegistrations(
773 const std::string& key,
774 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
775 callback) {
776 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
777 if (IsDisabled() || !context_) {
778 RunSoon(FROM_HERE,
779 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
780 SERVICE_WORKER_ERROR_FAILED));
781 return;
784 if (key.empty()) {
785 RunSoon(FROM_HERE,
786 base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
787 SERVICE_WORKER_ERROR_FAILED));
788 return;
791 database_task_manager_->GetTaskRunner()->PostTask(
792 FROM_HERE,
793 base::Bind(
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) {
801 Disable();
803 // Delete the database on the database thread.
804 PostTaskAndReplyWithResult(
805 database_task_manager_->GetTaskRunner(),
806 FROM_HERE,
807 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
808 base::Unretained(database_.get())),
809 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
810 weak_factory_.GetWeakPtr(),
811 callback));
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);
851 std::set<int64> ids;
852 for (size_t i = 0; i < resources.size(); ++i)
853 ids.insert(resources[i].resource_id);
855 database_task_manager_->GetTaskRunner()->PostTask(
856 FROM_HERE,
857 base::Bind(base::IgnoreResult(
858 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
859 base::Unretained(database_.get()),
860 ids));
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() {
877 state_ = DISABLED;
878 if (disk_cache_)
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),
903 path_(path),
904 context_(context),
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() {
916 if (path_.empty())
917 return base::FilePath();
918 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
919 .Append(kDatabaseName);
922 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
923 if (path_.empty())
924 return base::FilePath();
925 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
926 .Append(kDiskCacheName);
929 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
930 if (!context_)
931 return false;
933 switch (state_) {
934 case INITIALIZED:
935 return true;
936 case DISABLED:
937 return false;
938 case INITIALIZING:
939 pending_tasks_.push_back(callback);
940 return false;
941 case UNINITIALIZED:
942 pending_tasks_.push_back(callback);
943 // Fall-through.
946 state_ = INITIALIZING;
947 database_task_manager_->GetTaskRunner()->PostTask(
948 FROM_HERE,
949 base::Bind(&ReadInitialDataFromDB,
950 database_.get(),
951 base::MessageLoopProxy::current(),
952 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
953 weak_factory_.GetWeakPtr())));
954 return false;
957 void ServiceWorkerStorage::DidReadInitialData(
958 InitialData* data,
959 ServiceWorkerDatabase::Status status) {
960 DCHECK(data);
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;
969 } else {
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,
985 int64 callback_id,
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(
992 "ServiceWorker",
993 "ServiceWorkerStorage::FindRegistrationForDocument",
994 callback_id,
995 "Status", ServiceWorkerDatabase::StatusToString(status));
996 return;
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(
1007 "ServiceWorker",
1008 "ServiceWorkerStorage::FindRegistrationForDocument",
1009 callback_id,
1010 "Status", ServiceWorkerDatabase::StatusToString(status),
1011 "Info",
1012 (installing_status == SERVICE_WORKER_OK) ?
1013 "Installing registration is found" :
1014 "Any registrations are not found");
1015 return;
1018 ScheduleDeleteAndStartOver();
1019 callback.Run(DatabaseStatusToStatusCode(status),
1020 scoped_refptr<ServiceWorkerRegistration>());
1021 TRACE_EVENT_ASYNC_END1(
1022 "ServiceWorker",
1023 "ServiceWorkerStorage::FindRegistrationForDocument",
1024 callback_id,
1025 "Status", ServiceWorkerDatabase::StatusToString(status));
1028 void ServiceWorkerStorage::DidFindRegistrationForPattern(
1029 const GURL& scope,
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);
1036 return;
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);
1045 return;
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);
1060 return;
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>());
1067 return;
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>());
1094 return;
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;
1103 DCHECK(inserted);
1105 ServiceWorkerRegistration* registration =
1106 context_->GetLiveRegistration(registration_data.registration_id);
1107 if (registration) {
1108 infos.push_back(registration->GetInfo());
1109 continue;
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();
1121 else
1122 info.waiting_version = version->GetInfo();
1123 infos.push_back(info);
1124 continue;
1127 if (registration_data.is_active) {
1128 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
1129 info.active_version.version_id = registration_data.version_id;
1130 } else {
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,
1154 const GURL& origin,
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));
1161 return;
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,
1173 origin,
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));
1205 return;
1207 if (quota_manager_proxy_.get()) {
1208 // Can be nullptr in tests.
1209 quota_manager_proxy_->NotifyStorageModified(
1210 storage::QuotaClient::kServiceWorker,
1211 params.origin,
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
1228 // recovery.
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());
1295 else
1296 NOTREACHED();
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();
1317 return match;
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();
1329 return NULL;
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())
1338 return NULL;
1339 return found->second.get();
1342 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1343 if (disk_cache_)
1344 return disk_cache_.get();
1346 disk_cache_.reset(new ServiceWorkerDiskCache);
1348 base::FilePath path = GetDiskCachePath();
1349 if (path.empty()) {
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(
1357 path,
1358 kMaxDiskCacheSize,
1359 false,
1360 disk_cache_thread_,
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_)
1396 return;
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();
1403 RunSoon(FROM_HERE,
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(
1422 FROM_HERE,
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(
1435 FROM_HERE,
1436 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1437 database_.get(),
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)
1448 return;
1449 StartPurgingResources(stale_resource_ids);
1452 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
1453 // Can be null in tests.
1454 if (!special_storage_policy_.get())
1455 return;
1457 if (!special_storage_policy_->HasSessionOnlyOrigins())
1458 return;
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(
1467 FROM_HERE,
1468 base::Bind(&DeleteAllDataForOriginsFromDB,
1469 database_.get(),
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(
1482 FROM_HERE,
1483 base::Bind(
1484 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1485 return;
1488 status = database->PurgeUncommittedResourceIds(ids);
1489 if (status != ServiceWorkerDatabase::STATUS_OK) {
1490 original_task_runner->PostTask(
1491 FROM_HERE,
1492 base::Bind(
1493 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1494 return;
1497 ids.clear();
1498 status = database->GetPurgeableResourceIds(&ids);
1499 original_task_runner->PostTask(
1500 FROM_HERE,
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) {
1508 DCHECK(database);
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));
1519 return;
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,
1531 const GURL& origin,
1532 const DeleteRegistrationCallback& callback) {
1533 DCHECK(database);
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(
1541 FROM_HERE,
1542 base::Bind(
1543 callback, false, deleted_version, std::vector<int64>(), status));
1544 return;
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, &registrations);
1551 if (status != ServiceWorkerDatabase::STATUS_OK) {
1552 original_task_runner->PostTask(
1553 FROM_HERE,
1554 base::Bind(
1555 callback, false, deleted_version, std::vector<int64>(), status));
1556 return;
1559 bool deletable = registrations.empty();
1560 original_task_runner->PostTask(FROM_HERE,
1561 base::Bind(callback,
1562 deletable,
1563 deleted_version,
1564 newly_purgeable_resources,
1565 status));
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) {
1574 DCHECK(database);
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(),
1582 deleted_version,
1583 newly_purgeable_resources,
1584 status));
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, &registrations);
1596 if (status != ServiceWorkerDatabase::STATUS_OK) {
1597 original_task_runner->PostTask(
1598 FROM_HERE,
1599 base::Bind(callback,
1600 ServiceWorkerDatabase::RegistrationData(),
1601 ResourceList(),
1602 status));
1603 return;
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(
1622 FROM_HERE,
1623 base::Bind(callback, data, resources, status));
1626 void ServiceWorkerStorage::FindForPatternInDB(
1627 ServiceWorkerDatabase* database,
1628 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1629 const GURL& scope,
1630 const FindInDBCallback& callback) {
1631 GURL origin = scope.GetOrigin();
1632 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1633 ServiceWorkerDatabase::Status status =
1634 database->GetRegistrationsForOrigin(origin, &registrations);
1635 if (status != ServiceWorkerDatabase::STATUS_OK) {
1636 original_task_runner->PostTask(
1637 FROM_HERE,
1638 base::Bind(callback,
1639 ServiceWorkerDatabase::RegistrationData(),
1640 ResourceList(),
1641 status));
1642 return;
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)
1652 continue;
1653 status = database->ReadRegistration(it->registration_id, origin,
1654 &data, &resources);
1655 break; // We're done looping.
1658 original_task_runner->PostTask(
1659 FROM_HERE,
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,
1667 const GURL& origin,
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) {
1682 GURL origin;
1683 ServiceWorkerDatabase::Status status =
1684 database->ReadRegistrationOrigin(registration_id, &origin);
1685 if (status != ServiceWorkerDatabase::STATUS_OK) {
1686 original_task_runner->PostTask(
1687 FROM_HERE,
1688 base::Bind(callback, ServiceWorkerDatabase::RegistrationData(),
1689 ResourceList(), status));
1690 return;
1692 FindForIdInDB(database, original_task_runner, registration_id, origin,
1693 callback);
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) {
1702 std::string data;
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) {
1724 DCHECK(database);
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
1736 // removed.
1737 if (state_ == DISABLED) {
1738 // Recovery process has already been scheduled.
1739 return;
1741 Disable();
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));
1756 return;
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(),
1766 FROM_HERE,
1767 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1768 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1769 weak_factory_.GetWeakPtr(),
1770 callback));
1773 void ServiceWorkerStorage::DidDeleteDiskCache(
1774 const StatusCallback& callback, bool result) {
1775 DCHECK_EQ(DISABLED, state_);
1776 if (!result) {
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);
1780 return;
1782 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1783 callback.Run(SERVICE_WORKER_OK);
1786 } // namespace content