Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_storage.cc
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.
4
5 #include "content/browser/service_worker/service_worker_storage.h"
6
7 #include <string>
8
9 #include "base/bind_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/files/file_util.h"
12 #include "base/hash.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/task_runner_util.h"
17 #include "content/browser/service_worker/service_worker_context_core.h"
18 #include "content/browser/service_worker/service_worker_disk_cache.h"
19 #include "content/browser/service_worker/service_worker_info.h"
20 #include "content/browser/service_worker/service_worker_metrics.h"
21 #include "content/browser/service_worker/service_worker_registration.h"
22 #include "content/browser/service_worker/service_worker_utils.h"
23 #include "content/browser/service_worker/service_worker_version.h"
24 #include "content/common/service_worker/service_worker_types.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "net/base/completion_callback.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/net_errors.h"
29 #include "storage/browser/quota/quota_manager_proxy.h"
30
31 namespace content {
32
33 namespace {
34
35 void RunSoon(const tracked_objects::Location& from_here,
36              const base::Closure& closure) {
37   base::MessageLoop::current()->PostTask(from_here, closure);
38 }
39
40 void CompleteFindNow(
41     const scoped_refptr<ServiceWorkerRegistration>& registration,
42     ServiceWorkerStatusCode status,
43     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
44   callback.Run(status, registration);
45 }
46
47 void CompleteFindSoon(
48     const tracked_objects::Location& from_here,
49     const scoped_refptr<ServiceWorkerRegistration>& registration,
50     ServiceWorkerStatusCode status,
51     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
52   RunSoon(from_here, base::Bind(callback, status, registration));
53 }
54
55 const base::FilePath::CharType kDatabaseName[] =
56     FILE_PATH_LITERAL("Database");
57 const base::FilePath::CharType kDiskCacheName[] =
58     FILE_PATH_LITERAL("Cache");
59
60 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
61 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
62
63 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
64     ServiceWorkerDatabase::Status status) {
65   switch (status) {
66     case ServiceWorkerDatabase::STATUS_OK:
67       return SERVICE_WORKER_OK;
68     case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
69       return SERVICE_WORKER_ERROR_NOT_FOUND;
70     case ServiceWorkerDatabase::STATUS_ERROR_MAX:
71       NOTREACHED();
72     default:
73       return SERVICE_WORKER_ERROR_FAILED;
74   }
75 }
76
77 class ResponseComparer : public base::RefCounted<ResponseComparer> {
78  public:
79   ResponseComparer(
80       base::WeakPtr<ServiceWorkerStorage> owner,
81       scoped_ptr<ServiceWorkerResponseReader> lhs,
82       scoped_ptr<ServiceWorkerResponseReader> rhs,
83       const ServiceWorkerStorage::CompareCallback& callback)
84       : owner_(owner),
85         completion_callback_(callback),
86         lhs_reader_(lhs.release()),
87         rhs_reader_(rhs.release()),
88         completion_count_(0),
89         previous_result_(0) {
90   }
91
92   void Start();
93
94  private:
95   friend class base::RefCounted<ResponseComparer>;
96
97   static const int kBufferSize = 16 * 1024;
98
99   ~ResponseComparer() {}
100   void ReadInfos();
101   void OnReadInfoComplete(int result);
102   void ReadSomeData();
103   void OnReadDataComplete(int result);
104
105   base::WeakPtr<ServiceWorkerStorage> owner_;
106   ServiceWorkerStorage::CompareCallback completion_callback_;
107   scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
108   scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
109   scoped_refptr<net::IOBuffer> lhs_buffer_;
110   scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
111   scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
112   scoped_refptr<net::IOBuffer> rhs_buffer_;
113   int completion_count_;
114   int previous_result_;
115   DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
116 };
117
118 void ResponseComparer::Start() {
119   lhs_buffer_ = new net::IOBuffer(kBufferSize);
120   lhs_info_ = new HttpResponseInfoIOBuffer();
121   rhs_buffer_ = new net::IOBuffer(kBufferSize);
122   rhs_info_ = new HttpResponseInfoIOBuffer();
123
124   ReadInfos();
125 }
126
127 void ResponseComparer::ReadInfos() {
128   lhs_reader_->ReadInfo(
129       lhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
130   rhs_reader_->ReadInfo(
131       rhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
132 }
133
134 void ResponseComparer::OnReadInfoComplete(int result) {
135   if (completion_callback_.is_null() || !owner_)
136     return;
137   if (result < 0) {
138     completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
139     completion_callback_.Reset();
140     return;
141   }
142   if (++completion_count_ != 2)
143     return;
144
145   if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
146     completion_callback_.Run(SERVICE_WORKER_OK, false);
147     return;
148   }
149   ReadSomeData();
150 }
151
152 void ResponseComparer::ReadSomeData() {
153   completion_count_ = 0;
154   lhs_reader_->ReadData(
155       lhs_buffer_.get(),
156       kBufferSize,
157       base::Bind(&ResponseComparer::OnReadDataComplete, this));
158   rhs_reader_->ReadData(
159       rhs_buffer_.get(),
160       kBufferSize,
161       base::Bind(&ResponseComparer::OnReadDataComplete, this));
162 }
163
164 void ResponseComparer::OnReadDataComplete(int result) {
165   if (completion_callback_.is_null() || !owner_)
166     return;
167   if (result < 0) {
168     completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
169     completion_callback_.Reset();
170     return;
171   }
172   if (++completion_count_ != 2) {
173     previous_result_ = result;
174     return;
175   }
176
177   // TODO(michaeln): Probably shouldn't assume that the amounts read from
178   // each reader will always be the same. This would wrongly signal false
179   // in that case.
180   if (result != previous_result_) {
181     completion_callback_.Run(SERVICE_WORKER_OK, false);
182     return;
183   }
184
185   if (result == 0) {
186     completion_callback_.Run(SERVICE_WORKER_OK, true);
187     return;
188   }
189
190   int compare_result =
191       memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
192   if (compare_result != 0) {
193     completion_callback_.Run(SERVICE_WORKER_OK, false);
194     return;
195   }
196
197   ReadSomeData();
198 }
199
200 }  // namespace
201
202 ServiceWorkerStorage::InitialData::InitialData()
203     : next_registration_id(kInvalidServiceWorkerRegistrationId),
204       next_version_id(kInvalidServiceWorkerVersionId),
205       next_resource_id(kInvalidServiceWorkerResourceId) {
206 }
207
208 ServiceWorkerStorage::InitialData::~InitialData() {
209 }
210
211 ServiceWorkerStorage::
212 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
213     : registration_id(kInvalidServiceWorkerRegistrationId) {
214 }
215
216 ServiceWorkerStorage::
217 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
218 }
219
220 ServiceWorkerStorage::~ServiceWorkerStorage() {
221   weak_factory_.InvalidateWeakPtrs();
222   database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
223 }
224
225 // static
226 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
227     const base::FilePath& path,
228     base::WeakPtr<ServiceWorkerContextCore> context,
229     const scoped_refptr<base::SequencedTaskRunner>& database_task_runner,
230     const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
231     storage::QuotaManagerProxy* quota_manager_proxy) {
232   return make_scoped_ptr(new ServiceWorkerStorage(path,
233                                                   context,
234                                                   database_task_runner,
235                                                   disk_cache_thread,
236                                                   quota_manager_proxy));
237 }
238
239 // static
240 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
241     base::WeakPtr<ServiceWorkerContextCore> context,
242     ServiceWorkerStorage* old_storage) {
243   return make_scoped_ptr(
244       new ServiceWorkerStorage(old_storage->path_,
245                                context,
246                                old_storage->database_task_runner_,
247                                old_storage->disk_cache_thread_,
248                                old_storage->quota_manager_proxy_.get()));
249 }
250
251 void ServiceWorkerStorage::FindRegistrationForDocument(
252     const GURL& document_url,
253     const FindRegistrationCallback& callback) {
254   DCHECK(!document_url.has_ref());
255   TRACE_EVENT_ASYNC_BEGIN1(
256       "ServiceWorker",
257       "ServiceWorkerStorage::FindRegistrationForDocument",
258       base::Hash(document_url.spec()),
259       "URL", document_url.spec());
260   if (!LazyInitialize(base::Bind(
261           &ServiceWorkerStorage::FindRegistrationForDocument,
262           weak_factory_.GetWeakPtr(), document_url, callback))) {
263     if (state_ != INITIALIZING || !context_) {
264       CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
265                       SERVICE_WORKER_ERROR_FAILED, callback);
266     }
267     return;
268   }
269   DCHECK_EQ(INITIALIZED, state_);
270
271   // See if there are any stored registrations for the origin.
272   if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
273     // Look for something currently being installed.
274     scoped_refptr<ServiceWorkerRegistration> installing_registration =
275         FindInstallingRegistrationForDocument(document_url);
276     CompleteFindNow(installing_registration,
277                     installing_registration.get()
278                         ? SERVICE_WORKER_OK
279                         : SERVICE_WORKER_ERROR_NOT_FOUND,
280                     callback);
281     return;
282   }
283
284   database_task_runner_->PostTask(
285       FROM_HERE,
286       base::Bind(
287           &FindForDocumentInDB,
288           database_.get(),
289           base::MessageLoopProxy::current(),
290           document_url,
291           base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
292                      weak_factory_.GetWeakPtr(), document_url, callback)));
293 }
294
295 void ServiceWorkerStorage::FindRegistrationForPattern(
296     const GURL& scope,
297     const FindRegistrationCallback& callback) {
298   if (!LazyInitialize(base::Bind(
299           &ServiceWorkerStorage::FindRegistrationForPattern,
300           weak_factory_.GetWeakPtr(), scope, callback))) {
301     if (state_ != INITIALIZING || !context_) {
302       CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
303                        SERVICE_WORKER_ERROR_FAILED, callback);
304     }
305     return;
306   }
307   DCHECK_EQ(INITIALIZED, state_);
308
309   // See if there are any stored registrations for the origin.
310   if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
311     // Look for something currently being installed.
312     scoped_refptr<ServiceWorkerRegistration> installing_registration =
313         FindInstallingRegistrationForPattern(scope);
314     CompleteFindSoon(FROM_HERE,
315                      installing_registration,
316                      installing_registration.get()
317                          ? SERVICE_WORKER_OK
318                          : SERVICE_WORKER_ERROR_NOT_FOUND,
319                      callback);
320     return;
321   }
322
323   database_task_runner_->PostTask(
324       FROM_HERE,
325       base::Bind(
326           &FindForPatternInDB,
327           database_.get(),
328           base::MessageLoopProxy::current(),
329           scope,
330           base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
331                      weak_factory_.GetWeakPtr(), scope, callback)));
332 }
333
334 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
335     const GURL& scope) {
336   if (state_ != INITIALIZED || !context_)
337     return NULL;
338   for (RegistrationRefsById::const_iterator it =
339            uninstalling_registrations_.begin();
340        it != uninstalling_registrations_.end();
341        ++it) {
342     if (it->second->pattern() == scope) {
343       DCHECK(it->second->is_uninstalling());
344       return it->second.get();
345     }
346   }
347   return NULL;
348 }
349
350 void ServiceWorkerStorage::FindRegistrationForId(
351     int64 registration_id,
352     const GURL& origin,
353     const FindRegistrationCallback& callback) {
354   if (!LazyInitialize(base::Bind(
355           &ServiceWorkerStorage::FindRegistrationForId,
356           weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
357     if (state_ != INITIALIZING || !context_) {
358       CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
359                       SERVICE_WORKER_ERROR_FAILED, callback);
360     }
361     return;
362   }
363   DCHECK_EQ(INITIALIZED, state_);
364
365   // See if there are any stored registrations for the origin.
366   if (!ContainsKey(registered_origins_, origin)) {
367     // Look for something currently being installed.
368     scoped_refptr<ServiceWorkerRegistration> installing_registration =
369         FindInstallingRegistrationForId(registration_id);
370     CompleteFindNow(installing_registration,
371                     installing_registration.get()
372                         ? SERVICE_WORKER_OK
373                         : SERVICE_WORKER_ERROR_NOT_FOUND,
374                     callback);
375     return;
376   }
377
378   scoped_refptr<ServiceWorkerRegistration> registration =
379       context_->GetLiveRegistration(registration_id);
380   if (registration.get()) {
381     CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
382     return;
383   }
384
385   database_task_runner_->PostTask(
386       FROM_HERE,
387       base::Bind(&FindForIdInDB,
388                  database_.get(),
389                  base::MessageLoopProxy::current(),
390                  registration_id, origin,
391                  base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
392                             weak_factory_.GetWeakPtr(), callback)));
393 }
394
395 void ServiceWorkerStorage::GetAllRegistrations(
396     const GetAllRegistrationInfosCallback& callback) {
397   if (!LazyInitialize(base::Bind(
398           &ServiceWorkerStorage::GetAllRegistrations,
399           weak_factory_.GetWeakPtr(), callback))) {
400     if (state_ != INITIALIZING || !context_) {
401       RunSoon(FROM_HERE, base::Bind(
402           callback, std::vector<ServiceWorkerRegistrationInfo>()));
403     }
404     return;
405   }
406   DCHECK_EQ(INITIALIZED, state_);
407
408   RegistrationList* registrations = new RegistrationList;
409   PostTaskAndReplyWithResult(
410       database_task_runner_.get(),
411       FROM_HERE,
412       base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
413                  base::Unretained(database_.get()),
414                  base::Unretained(registrations)),
415       base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
416                  weak_factory_.GetWeakPtr(),
417                  callback,
418                  base::Owned(registrations)));
419 }
420
421 void ServiceWorkerStorage::StoreRegistration(
422     ServiceWorkerRegistration* registration,
423     ServiceWorkerVersion* version,
424     const StatusCallback& callback) {
425   DCHECK(registration);
426   DCHECK(version);
427
428   DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
429   if (IsDisabled() || !context_) {
430     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
431     return;
432   }
433
434   ServiceWorkerDatabase::RegistrationData data;
435   data.registration_id = registration->id();
436   data.scope = registration->pattern();
437   data.script = version->script_url();
438   data.has_fetch_handler = true;
439   data.version_id = version->version_id();
440   data.last_update_check = registration->last_update_check();
441   data.is_active = (version == registration->active_version());
442
443   ResourceList resources;
444   version->script_cache_map()->GetResources(&resources);
445
446   if (!has_checked_for_stale_resources_)
447     DeleteStaleResources();
448
449   database_task_runner_->PostTask(
450       FROM_HERE,
451       base::Bind(&WriteRegistrationInDB,
452                  database_.get(),
453                  base::MessageLoopProxy::current(),
454                  data, resources,
455                  base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
456                             weak_factory_.GetWeakPtr(),
457                             callback)));
458
459   registration->set_is_deleted(false);
460 }
461
462 void ServiceWorkerStorage::UpdateToActiveState(
463     ServiceWorkerRegistration* registration,
464     const StatusCallback& callback) {
465   DCHECK(registration);
466
467   DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
468   if (IsDisabled() || !context_) {
469     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
470     return;
471   }
472
473   PostTaskAndReplyWithResult(
474       database_task_runner_.get(),
475       FROM_HERE,
476       base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
477                  base::Unretained(database_.get()),
478                  registration->id(),
479                  registration->pattern().GetOrigin()),
480       base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
481                  weak_factory_.GetWeakPtr(),
482                  callback));
483 }
484
485 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
486     ServiceWorkerRegistration* registration) {
487   DCHECK(registration);
488
489   DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
490   if (IsDisabled() || !context_)
491     return;
492
493   database_task_runner_->PostTask(
494       FROM_HERE,
495       base::Bind(
496           base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
497           base::Unretained(database_.get()),
498           registration->id(),
499           registration->pattern().GetOrigin(),
500           registration->last_update_check()));
501 }
502
503 void ServiceWorkerStorage::DeleteRegistration(
504     int64 registration_id,
505     const GURL& origin,
506     const StatusCallback& callback) {
507   DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
508   if (IsDisabled() || !context_) {
509     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
510     return;
511   }
512
513   if (!has_checked_for_stale_resources_)
514     DeleteStaleResources();
515
516   DidDeleteRegistrationParams params;
517   params.registration_id = registration_id;
518   params.origin = origin;
519   params.callback = callback;
520
521   database_task_runner_->PostTask(
522       FROM_HERE,
523       base::Bind(&DeleteRegistrationFromDB,
524                  database_.get(),
525                  base::MessageLoopProxy::current(),
526                  registration_id, origin,
527                  base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
528                             weak_factory_.GetWeakPtr(), params)));
529
530   // The registration should no longer be findable.
531   pending_deletions_.insert(registration_id);
532   ServiceWorkerRegistration* registration =
533       context_->GetLiveRegistration(registration_id);
534   if (registration)
535     registration->set_is_deleted(true);
536 }
537
538 scoped_ptr<ServiceWorkerResponseReader>
539 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
540   return make_scoped_ptr(
541       new ServiceWorkerResponseReader(response_id, disk_cache()));
542 }
543
544 scoped_ptr<ServiceWorkerResponseWriter>
545 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
546   return make_scoped_ptr(
547       new ServiceWorkerResponseWriter(response_id, disk_cache()));
548 }
549
550 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
551   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
552   DCHECK_EQ(INITIALIZED, state_);
553
554   if (!has_checked_for_stale_resources_)
555     DeleteStaleResources();
556
557   database_task_runner_->PostTask(
558       FROM_HERE,
559       base::Bind(base::IgnoreResult(
560           &ServiceWorkerDatabase::WriteUncommittedResourceIds),
561           base::Unretained(database_.get()),
562           std::set<int64>(&id, &id + 1)));
563 }
564
565 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
566   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
567   database_task_runner_->PostTask(
568       FROM_HERE,
569       base::Bind(base::IgnoreResult(
570           &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
571           base::Unretained(database_.get()),
572           std::set<int64>(&id, &id + 1)));
573   StartPurgingResources(std::vector<int64>(1, id));
574 }
575
576 void ServiceWorkerStorage::CompareScriptResources(
577     int64 lhs_id, int64 rhs_id,
578     const CompareCallback& callback) {
579   DCHECK(!callback.is_null());
580   scoped_refptr<ResponseComparer> comparer =
581       new ResponseComparer(weak_factory_.GetWeakPtr(),
582                            CreateResponseReader(lhs_id),
583                            CreateResponseReader(rhs_id),
584                            callback);
585   comparer->Start();  // It deletes itself when done.
586 }
587
588 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
589   Disable();
590
591   // Delete the database on the database thread.
592   PostTaskAndReplyWithResult(
593       database_task_runner_.get(),
594       FROM_HERE,
595       base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
596                  base::Unretained(database_.get())),
597       base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
598                  weak_factory_.GetWeakPtr(),
599                  callback));
600 }
601
602 int64 ServiceWorkerStorage::NewRegistrationId() {
603   if (state_ == DISABLED)
604     return kInvalidServiceWorkerRegistrationId;
605   DCHECK_EQ(INITIALIZED, state_);
606   return next_registration_id_++;
607 }
608
609 int64 ServiceWorkerStorage::NewVersionId() {
610   if (state_ == DISABLED)
611     return kInvalidServiceWorkerVersionId;
612   DCHECK_EQ(INITIALIZED, state_);
613   return next_version_id_++;
614 }
615
616 int64 ServiceWorkerStorage::NewResourceId() {
617   if (state_ == DISABLED)
618     return kInvalidServiceWorkerResourceId;
619   DCHECK_EQ(INITIALIZED, state_);
620   return next_resource_id_++;
621 }
622
623 void ServiceWorkerStorage::NotifyInstallingRegistration(
624       ServiceWorkerRegistration* registration) {
625   DCHECK(installing_registrations_.find(registration->id()) ==
626          installing_registrations_.end());
627   installing_registrations_[registration->id()] = registration;
628 }
629
630 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
631       ServiceWorkerRegistration* registration,
632       ServiceWorkerVersion* version,
633       ServiceWorkerStatusCode status) {
634   installing_registrations_.erase(registration->id());
635   if (status != SERVICE_WORKER_OK && version) {
636     ResourceList resources;
637     version->script_cache_map()->GetResources(&resources);
638
639     std::set<int64> ids;
640     for (size_t i = 0; i < resources.size(); ++i)
641       ids.insert(resources[i].resource_id);
642
643     database_task_runner_->PostTask(
644         FROM_HERE,
645         base::Bind(base::IgnoreResult(
646             &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
647             base::Unretained(database_.get()),
648             ids));
649   }
650 }
651
652 void ServiceWorkerStorage::NotifyUninstallingRegistration(
653     ServiceWorkerRegistration* registration) {
654   DCHECK(uninstalling_registrations_.find(registration->id()) ==
655          uninstalling_registrations_.end());
656   uninstalling_registrations_[registration->id()] = registration;
657 }
658
659 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
660     ServiceWorkerRegistration* registration) {
661   uninstalling_registrations_.erase(registration->id());
662 }
663
664 void ServiceWorkerStorage::Disable() {
665   state_ = DISABLED;
666   if (disk_cache_)
667     disk_cache_->Disable();
668 }
669
670 bool ServiceWorkerStorage::IsDisabled() const {
671   return state_ == DISABLED;
672 }
673
674 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
675   if (!has_checked_for_stale_resources_)
676     DeleteStaleResources();
677   StartPurgingResources(resources);
678 }
679
680 ServiceWorkerStorage::ServiceWorkerStorage(
681     const base::FilePath& path,
682     base::WeakPtr<ServiceWorkerContextCore> context,
683     const scoped_refptr<base::SequencedTaskRunner>& database_task_runner,
684     const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
685     storage::QuotaManagerProxy* quota_manager_proxy)
686     : next_registration_id_(kInvalidServiceWorkerRegistrationId),
687       next_version_id_(kInvalidServiceWorkerVersionId),
688       next_resource_id_(kInvalidServiceWorkerResourceId),
689       state_(UNINITIALIZED),
690       path_(path),
691       context_(context),
692       database_task_runner_(database_task_runner),
693       disk_cache_thread_(disk_cache_thread),
694       quota_manager_proxy_(quota_manager_proxy),
695       is_purge_pending_(false),
696       has_checked_for_stale_resources_(false),
697       weak_factory_(this) {
698   database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
699 }
700
701 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
702   if (path_.empty())
703     return base::FilePath();
704   return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
705       .Append(kDatabaseName);
706 }
707
708 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
709   if (path_.empty())
710     return base::FilePath();
711   return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
712       .Append(kDiskCacheName);
713 }
714
715 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
716   if (!context_)
717     return false;
718
719   switch (state_) {
720     case INITIALIZED:
721       return true;
722     case DISABLED:
723       return false;
724     case INITIALIZING:
725       pending_tasks_.push_back(callback);
726       return false;
727     case UNINITIALIZED:
728       pending_tasks_.push_back(callback);
729       // Fall-through.
730   }
731
732   state_ = INITIALIZING;
733   database_task_runner_->PostTask(
734       FROM_HERE,
735       base::Bind(&ReadInitialDataFromDB,
736                  database_.get(),
737                  base::MessageLoopProxy::current(),
738                  base::Bind(&ServiceWorkerStorage::DidReadInitialData,
739                             weak_factory_.GetWeakPtr())));
740   return false;
741 }
742
743 void ServiceWorkerStorage::DidReadInitialData(
744     InitialData* data,
745     ServiceWorkerDatabase::Status status) {
746   DCHECK(data);
747   DCHECK_EQ(INITIALIZING, state_);
748
749   if (status == ServiceWorkerDatabase::STATUS_OK) {
750     next_registration_id_ = data->next_registration_id;
751     next_version_id_ = data->next_version_id;
752     next_resource_id_ = data->next_resource_id;
753     registered_origins_.swap(data->origins);
754     state_ = INITIALIZED;
755   } else {
756     // TODO(nhiroki): Stringify |status| using StatusToString() defined in
757     // service_worker_database.cc.
758     DVLOG(2) << "Failed to initialize: " << status;
759     ScheduleDeleteAndStartOver();
760   }
761
762   for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
763        it != pending_tasks_.end(); ++it) {
764     RunSoon(FROM_HERE, *it);
765   }
766   pending_tasks_.clear();
767 }
768
769 void ServiceWorkerStorage::DidFindRegistrationForDocument(
770     const GURL& document_url,
771     const FindRegistrationCallback& callback,
772     const ServiceWorkerDatabase::RegistrationData& data,
773     const ResourceList& resources,
774     ServiceWorkerDatabase::Status status) {
775   if (status == ServiceWorkerDatabase::STATUS_OK) {
776     ReturnFoundRegistration(callback, data, resources);
777     TRACE_EVENT_ASYNC_END1(
778         "ServiceWorker",
779         "ServiceWorkerStorage::FindRegistrationForDocument",
780         base::Hash(document_url.spec()),
781         "Status", "OK");
782     return;
783   }
784
785   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
786     // Look for something currently being installed.
787     scoped_refptr<ServiceWorkerRegistration> installing_registration =
788         FindInstallingRegistrationForDocument(document_url);
789     callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
790                                                : SERVICE_WORKER_ERROR_NOT_FOUND,
791                  installing_registration);
792     TRACE_EVENT_ASYNC_END1(
793         "ServiceWorker",
794         "ServiceWorkerStorage::FindRegistrationForDocument",
795         base::Hash(document_url.spec()),
796         "Status", status);
797     return;
798   }
799
800   ScheduleDeleteAndStartOver();
801   callback.Run(DatabaseStatusToStatusCode(status),
802                scoped_refptr<ServiceWorkerRegistration>());
803   TRACE_EVENT_ASYNC_END1(
804       "ServiceWorker",
805       "ServiceWorkerStorage::FindRegistrationForDocument",
806       base::Hash(document_url.spec()),
807       "Status", status);
808 }
809
810 void ServiceWorkerStorage::DidFindRegistrationForPattern(
811     const GURL& scope,
812     const FindRegistrationCallback& callback,
813     const ServiceWorkerDatabase::RegistrationData& data,
814     const ResourceList& resources,
815     ServiceWorkerDatabase::Status status) {
816   if (status == ServiceWorkerDatabase::STATUS_OK) {
817     ReturnFoundRegistration(callback, data, resources);
818     return;
819   }
820
821   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
822     scoped_refptr<ServiceWorkerRegistration> installing_registration =
823         FindInstallingRegistrationForPattern(scope);
824     callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
825                                                : SERVICE_WORKER_ERROR_NOT_FOUND,
826                  installing_registration);
827     return;
828   }
829
830   ScheduleDeleteAndStartOver();
831   callback.Run(DatabaseStatusToStatusCode(status),
832                scoped_refptr<ServiceWorkerRegistration>());
833 }
834
835 void ServiceWorkerStorage::DidFindRegistrationForId(
836     const FindRegistrationCallback& callback,
837     const ServiceWorkerDatabase::RegistrationData& data,
838     const ResourceList& resources,
839     ServiceWorkerDatabase::Status status) {
840   if (status == ServiceWorkerDatabase::STATUS_OK) {
841     ReturnFoundRegistration(callback, data, resources);
842     return;
843   }
844
845   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
846     // TODO(nhiroki): Find a registration in |installing_registrations_|.
847     callback.Run(DatabaseStatusToStatusCode(status),
848                  scoped_refptr<ServiceWorkerRegistration>());
849     return;
850   }
851
852   ScheduleDeleteAndStartOver();
853   callback.Run(DatabaseStatusToStatusCode(status),
854                scoped_refptr<ServiceWorkerRegistration>());
855 }
856
857 void ServiceWorkerStorage::ReturnFoundRegistration(
858     const FindRegistrationCallback& callback,
859     const ServiceWorkerDatabase::RegistrationData& data,
860     const ResourceList& resources) {
861   scoped_refptr<ServiceWorkerRegistration> registration =
862       GetOrCreateRegistration(data, resources);
863   if (registration->is_deleted()) {
864     // It's past the point of no return and no longer findable.
865     callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
866     return;
867   }
868   callback.Run(SERVICE_WORKER_OK, registration);
869 }
870
871 void ServiceWorkerStorage::DidGetAllRegistrations(
872     const GetAllRegistrationInfosCallback& callback,
873     RegistrationList* registrations,
874     ServiceWorkerDatabase::Status status) {
875   DCHECK(registrations);
876   if (status != ServiceWorkerDatabase::STATUS_OK &&
877       status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
878     ScheduleDeleteAndStartOver();
879     callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
880     return;
881   }
882
883   // Add all stored registrations.
884   std::set<int64> pushed_registrations;
885   std::vector<ServiceWorkerRegistrationInfo> infos;
886   for (RegistrationList::const_iterator it = registrations->begin();
887        it != registrations->end(); ++it) {
888     const bool inserted =
889         pushed_registrations.insert(it->registration_id).second;
890     DCHECK(inserted);
891
892     ServiceWorkerRegistration* registration =
893         context_->GetLiveRegistration(it->registration_id);
894     if (registration) {
895       infos.push_back(registration->GetInfo());
896       continue;
897     }
898
899     ServiceWorkerRegistrationInfo info;
900     info.pattern = it->scope;
901     info.registration_id = it->registration_id;
902     if (ServiceWorkerVersion* version =
903             context_->GetLiveVersion(it->version_id)) {
904       if (it->is_active)
905         info.active_version = version->GetInfo();
906       else
907         info.waiting_version = version->GetInfo();
908       infos.push_back(info);
909       continue;
910     }
911
912     if (it->is_active) {
913       info.active_version.is_null = false;
914       info.active_version.status = ServiceWorkerVersion::ACTIVATED;
915       info.active_version.version_id = it->version_id;
916     } else {
917       info.waiting_version.is_null = false;
918       info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
919       info.waiting_version.version_id = it->version_id;
920     }
921     infos.push_back(info);
922   }
923
924   // Add unstored registrations that are being installed.
925   for (RegistrationRefsById::const_iterator it =
926            installing_registrations_.begin();
927        it != installing_registrations_.end(); ++it) {
928     if (pushed_registrations.insert(it->first).second)
929       infos.push_back(it->second->GetInfo());
930   }
931
932   callback.Run(infos);
933 }
934
935 void ServiceWorkerStorage::DidStoreRegistration(
936     const StatusCallback& callback,
937     const GURL& origin,
938     int64 deleted_version_id,
939     const std::vector<int64>& newly_purgeable_resources,
940     ServiceWorkerDatabase::Status status) {
941   if (status != ServiceWorkerDatabase::STATUS_OK) {
942     ScheduleDeleteAndStartOver();
943     callback.Run(DatabaseStatusToStatusCode(status));
944     return;
945   }
946   registered_origins_.insert(origin);
947   callback.Run(SERVICE_WORKER_OK);
948
949   if (!context_ || !context_->GetLiveVersion(deleted_version_id))
950     StartPurgingResources(newly_purgeable_resources);
951 }
952
953 void ServiceWorkerStorage::DidUpdateToActiveState(
954     const StatusCallback& callback,
955     ServiceWorkerDatabase::Status status) {
956   if (status != ServiceWorkerDatabase::STATUS_OK &&
957       status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
958     ScheduleDeleteAndStartOver();
959   }
960   callback.Run(DatabaseStatusToStatusCode(status));
961 }
962
963 void ServiceWorkerStorage::DidDeleteRegistration(
964     const DidDeleteRegistrationParams& params,
965     bool origin_is_deletable,
966     int64 version_id,
967     const std::vector<int64>& newly_purgeable_resources,
968     ServiceWorkerDatabase::Status status) {
969   pending_deletions_.erase(params.registration_id);
970   if (status != ServiceWorkerDatabase::STATUS_OK) {
971     ScheduleDeleteAndStartOver();
972     params.callback.Run(DatabaseStatusToStatusCode(status));
973     return;
974   }
975   if (origin_is_deletable)
976     registered_origins_.erase(params.origin);
977   params.callback.Run(SERVICE_WORKER_OK);
978
979   if (!context_ || !context_->GetLiveVersion(version_id))
980     StartPurgingResources(newly_purgeable_resources);
981 }
982
983 scoped_refptr<ServiceWorkerRegistration>
984 ServiceWorkerStorage::GetOrCreateRegistration(
985     const ServiceWorkerDatabase::RegistrationData& data,
986     const ResourceList& resources) {
987   scoped_refptr<ServiceWorkerRegistration> registration =
988       context_->GetLiveRegistration(data.registration_id);
989   if (registration.get())
990     return registration;
991
992   registration = new ServiceWorkerRegistration(
993       data.scope, data.registration_id, context_);
994   registration->set_last_update_check(data.last_update_check);
995   if (pending_deletions_.find(data.registration_id) !=
996       pending_deletions_.end()) {
997     registration->set_is_deleted(true);
998   }
999   scoped_refptr<ServiceWorkerVersion> version =
1000       context_->GetLiveVersion(data.version_id);
1001   if (!version.get()) {
1002     version = new ServiceWorkerVersion(
1003         registration.get(), data.script, data.version_id, context_);
1004     version->SetStatus(data.is_active ?
1005         ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1006     version->script_cache_map()->SetResources(resources);
1007   }
1008
1009   if (version->status() == ServiceWorkerVersion::ACTIVATED)
1010     registration->SetActiveVersion(version.get());
1011   else if (version->status() == ServiceWorkerVersion::INSTALLED)
1012     registration->SetWaitingVersion(version.get());
1013   else
1014     NOTREACHED();
1015
1016   return registration;
1017 }
1018
1019 ServiceWorkerRegistration*
1020 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1021     const GURL& document_url) {
1022   DCHECK(!document_url.has_ref());
1023
1024   LongestScopeMatcher matcher(document_url);
1025   ServiceWorkerRegistration* match = NULL;
1026
1027   // TODO(nhiroki): This searches over installing registrations linearly and it
1028   // couldn't be scalable. Maybe the regs should be partitioned by origin.
1029   for (RegistrationRefsById::const_iterator it =
1030            installing_registrations_.begin();
1031        it != installing_registrations_.end(); ++it) {
1032     if (matcher.MatchLongest(it->second->pattern()))
1033       match = it->second.get();
1034   }
1035   return match;
1036 }
1037
1038 ServiceWorkerRegistration*
1039 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1040     const GURL& scope) {
1041   for (RegistrationRefsById::const_iterator it =
1042            installing_registrations_.begin();
1043        it != installing_registrations_.end(); ++it) {
1044     if (it->second->pattern() == scope)
1045       return it->second.get();
1046   }
1047   return NULL;
1048 }
1049
1050 ServiceWorkerRegistration*
1051 ServiceWorkerStorage::FindInstallingRegistrationForId(
1052     int64 registration_id) {
1053   RegistrationRefsById::const_iterator found =
1054       installing_registrations_.find(registration_id);
1055   if (found == installing_registrations_.end())
1056     return NULL;
1057   return found->second.get();
1058 }
1059
1060 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1061   if (disk_cache_)
1062     return disk_cache_.get();
1063
1064   disk_cache_.reset(new ServiceWorkerDiskCache);
1065
1066   base::FilePath path = GetDiskCachePath();
1067   if (path.empty()) {
1068     int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1069                                              net::CompletionCallback());
1070     DCHECK_EQ(net::OK, rv);
1071     return disk_cache_.get();
1072   }
1073
1074   int rv = disk_cache_->InitWithDiskBackend(
1075       path,
1076       kMaxDiskCacheSize,
1077       false,
1078       disk_cache_thread_,
1079       base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1080                  weak_factory_.GetWeakPtr()));
1081   if (rv != net::ERR_IO_PENDING)
1082     OnDiskCacheInitialized(rv);
1083
1084   return disk_cache_.get();
1085 }
1086
1087 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1088   if (rv != net::OK) {
1089     LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1090                << net::ErrorToString(rv);
1091     ScheduleDeleteAndStartOver();
1092   }
1093   ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1094 }
1095
1096 void ServiceWorkerStorage::StartPurgingResources(
1097     const std::vector<int64>& ids) {
1098   DCHECK(has_checked_for_stale_resources_);
1099   for (size_t i = 0; i < ids.size(); ++i)
1100     purgeable_resource_ids_.push_back(ids[i]);
1101   ContinuePurgingResources();
1102 }
1103
1104 void ServiceWorkerStorage::StartPurgingResources(
1105     const ResourceList& resources) {
1106   DCHECK(has_checked_for_stale_resources_);
1107   for (size_t i = 0; i < resources.size(); ++i)
1108     purgeable_resource_ids_.push_back(resources[i].resource_id);
1109   ContinuePurgingResources();
1110 }
1111
1112 void ServiceWorkerStorage::ContinuePurgingResources() {
1113   if (purgeable_resource_ids_.empty() || is_purge_pending_)
1114     return;
1115
1116   // Do one at a time until we're done, use RunSoon to avoid recursion when
1117   // DoomEntry returns immediately.
1118   is_purge_pending_ = true;
1119   int64 id = purgeable_resource_ids_.front();
1120   purgeable_resource_ids_.pop_front();
1121   RunSoon(FROM_HERE,
1122           base::Bind(&ServiceWorkerStorage::PurgeResource,
1123                      weak_factory_.GetWeakPtr(), id));
1124 }
1125
1126 void ServiceWorkerStorage::PurgeResource(int64 id) {
1127   DCHECK(is_purge_pending_);
1128   int rv = disk_cache()->DoomEntry(
1129       id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1130                      weak_factory_.GetWeakPtr(), id));
1131   if (rv != net::ERR_IO_PENDING)
1132     OnResourcePurged(id, rv);
1133 }
1134
1135 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1136   DCHECK(is_purge_pending_);
1137   is_purge_pending_ = false;
1138
1139   database_task_runner_->PostTask(
1140       FROM_HERE,
1141       base::Bind(base::IgnoreResult(
1142           &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1143           base::Unretained(database_.get()),
1144           std::set<int64>(&id, &id + 1)));
1145
1146   ContinuePurgingResources();
1147 }
1148
1149 void ServiceWorkerStorage::DeleteStaleResources() {
1150   DCHECK(!has_checked_for_stale_resources_);
1151   has_checked_for_stale_resources_ = true;
1152   database_task_runner_->PostTask(
1153       FROM_HERE,
1154       base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1155                  database_.get(),
1156                  base::MessageLoopProxy::current(),
1157                  base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1158                             weak_factory_.GetWeakPtr())));
1159 }
1160
1161 void ServiceWorkerStorage::DidCollectStaleResources(
1162     const std::vector<int64>& stale_resource_ids,
1163     ServiceWorkerDatabase::Status status) {
1164   DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1165   if (status != ServiceWorkerDatabase::STATUS_OK)
1166     return;
1167   StartPurgingResources(stale_resource_ids);
1168 }
1169
1170 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1171     ServiceWorkerDatabase* database,
1172     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1173     const GetResourcesCallback& callback) {
1174   std::set<int64> ids;
1175   ServiceWorkerDatabase::Status status =
1176       database->GetUncommittedResourceIds(&ids);
1177   if (status != ServiceWorkerDatabase::STATUS_OK) {
1178     original_task_runner->PostTask(
1179         FROM_HERE,
1180         base::Bind(
1181             callback, std::vector<int64>(ids.begin(), ids.end()), status));
1182     return;
1183   }
1184
1185   status = database->PurgeUncommittedResourceIds(ids);
1186   if (status != ServiceWorkerDatabase::STATUS_OK) {
1187     original_task_runner->PostTask(
1188         FROM_HERE,
1189         base::Bind(
1190             callback, std::vector<int64>(ids.begin(), ids.end()), status));
1191     return;
1192   }
1193
1194   ids.clear();
1195   status = database->GetPurgeableResourceIds(&ids);
1196   original_task_runner->PostTask(
1197       FROM_HERE,
1198       base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1199 }
1200
1201 void ServiceWorkerStorage::ReadInitialDataFromDB(
1202     ServiceWorkerDatabase* database,
1203     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1204     const InitializeCallback& callback) {
1205   DCHECK(database);
1206   scoped_ptr<ServiceWorkerStorage::InitialData> data(
1207       new ServiceWorkerStorage::InitialData());
1208
1209   ServiceWorkerDatabase::Status status =
1210       database->GetNextAvailableIds(&data->next_registration_id,
1211                                     &data->next_version_id,
1212                                     &data->next_resource_id);
1213   if (status != ServiceWorkerDatabase::STATUS_OK) {
1214     original_task_runner->PostTask(
1215         FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1216     return;
1217   }
1218
1219   status = database->GetOriginsWithRegistrations(&data->origins);
1220   original_task_runner->PostTask(
1221       FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1222 }
1223
1224 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1225     ServiceWorkerDatabase* database,
1226     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1227     int64 registration_id,
1228     const GURL& origin,
1229     const DeleteRegistrationCallback& callback) {
1230   DCHECK(database);
1231
1232   int64 version_id = kInvalidServiceWorkerVersionId;
1233   std::vector<int64> newly_purgeable_resources;
1234   ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1235       registration_id, origin, &version_id, &newly_purgeable_resources);
1236   if (status != ServiceWorkerDatabase::STATUS_OK) {
1237     original_task_runner->PostTask(FROM_HERE,
1238                                    base::Bind(callback,
1239                                               false,
1240                                               kInvalidServiceWorkerVersionId,
1241                                               std::vector<int64>(),
1242                                               status));
1243     return;
1244   }
1245
1246   // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1247   // unique origin list.
1248   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1249   status = database->GetRegistrationsForOrigin(origin, &registrations);
1250   if (status != ServiceWorkerDatabase::STATUS_OK) {
1251     original_task_runner->PostTask(FROM_HERE,
1252                                    base::Bind(callback,
1253                                               false,
1254                                               kInvalidServiceWorkerVersionId,
1255                                               std::vector<int64>(),
1256                                               status));
1257     return;
1258   }
1259
1260   bool deletable = registrations.empty();
1261   original_task_runner->PostTask(
1262       FROM_HERE,
1263       base::Bind(
1264           callback, deletable, version_id, newly_purgeable_resources, status));
1265 }
1266
1267 void ServiceWorkerStorage::WriteRegistrationInDB(
1268     ServiceWorkerDatabase* database,
1269     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1270     const ServiceWorkerDatabase::RegistrationData& data,
1271     const ResourceList& resources,
1272     const WriteRegistrationCallback& callback) {
1273   DCHECK(database);
1274   int64 deleted_version_id = kInvalidServiceWorkerVersionId;
1275   std::vector<int64> newly_purgeable_resources;
1276   ServiceWorkerDatabase::Status status = database->WriteRegistration(
1277       data, resources, &deleted_version_id, &newly_purgeable_resources);
1278   original_task_runner->PostTask(FROM_HERE,
1279                                  base::Bind(callback,
1280                                             data.script.GetOrigin(),
1281                                             deleted_version_id,
1282                                             newly_purgeable_resources,
1283                                             status));
1284 }
1285
1286 void ServiceWorkerStorage::FindForDocumentInDB(
1287     ServiceWorkerDatabase* database,
1288     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1289     const GURL& document_url,
1290     const FindInDBCallback& callback) {
1291   GURL origin = document_url.GetOrigin();
1292   RegistrationList registrations;
1293   ServiceWorkerDatabase::Status status =
1294       database->GetRegistrationsForOrigin(origin, &registrations);
1295   if (status != ServiceWorkerDatabase::STATUS_OK) {
1296     original_task_runner->PostTask(
1297         FROM_HERE,
1298         base::Bind(callback,
1299                    ServiceWorkerDatabase::RegistrationData(),
1300                    ResourceList(),
1301                    status));
1302     return;
1303   }
1304
1305   ServiceWorkerDatabase::RegistrationData data;
1306   ResourceList resources;
1307   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1308
1309   // Find one with a pattern match.
1310   LongestScopeMatcher matcher(document_url);
1311   int64 match = kInvalidServiceWorkerRegistrationId;
1312   for (size_t i = 0; i < registrations.size(); ++i) {
1313     if (matcher.MatchLongest(registrations[i].scope))
1314       match = registrations[i].registration_id;
1315   }
1316
1317   if (match != kInvalidServiceWorkerRegistrationId)
1318     status = database->ReadRegistration(match, origin, &data, &resources);
1319
1320   original_task_runner->PostTask(
1321       FROM_HERE,
1322       base::Bind(callback, data, resources, status));
1323 }
1324
1325 void ServiceWorkerStorage::FindForPatternInDB(
1326     ServiceWorkerDatabase* database,
1327     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1328     const GURL& scope,
1329     const FindInDBCallback& callback) {
1330   GURL origin = scope.GetOrigin();
1331   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1332   ServiceWorkerDatabase::Status status =
1333       database->GetRegistrationsForOrigin(origin, &registrations);
1334   if (status != ServiceWorkerDatabase::STATUS_OK) {
1335     original_task_runner->PostTask(
1336         FROM_HERE,
1337         base::Bind(callback,
1338                    ServiceWorkerDatabase::RegistrationData(),
1339                    ResourceList(),
1340                    status));
1341     return;
1342   }
1343
1344   // Find one with an exact matching scope.
1345   ServiceWorkerDatabase::RegistrationData data;
1346   ResourceList resources;
1347   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1348   for (RegistrationList::const_iterator it = registrations.begin();
1349        it != registrations.end(); ++it) {
1350     if (scope != it->scope)
1351       continue;
1352     status = database->ReadRegistration(it->registration_id, origin,
1353                                         &data, &resources);
1354     break;  // We're done looping.
1355   }
1356
1357   original_task_runner->PostTask(
1358       FROM_HERE,
1359       base::Bind(callback, data, resources, status));
1360 }
1361
1362 void ServiceWorkerStorage::FindForIdInDB(
1363     ServiceWorkerDatabase* database,
1364     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1365     int64 registration_id,
1366     const GURL& origin,
1367     const FindInDBCallback& callback) {
1368   ServiceWorkerDatabase::RegistrationData data;
1369   ResourceList resources;
1370   ServiceWorkerDatabase::Status status =
1371       database->ReadRegistration(registration_id, origin, &data, &resources);
1372   original_task_runner->PostTask(
1373       FROM_HERE, base::Bind(callback, data, resources, status));
1374 }
1375
1376 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1377 // is transient and it can get healed soon (e.g. IO error). To do that, the
1378 // database should not disable itself when an error occurs and the storage
1379 // controls it instead.
1380 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1381   if (state_ == DISABLED) {
1382     // Recovery process has already been scheduled.
1383     return;
1384   }
1385   Disable();
1386
1387   DVLOG(1) << "Schedule to delete the context and start over.";
1388   context_->ScheduleDeleteAndStartOver();
1389 }
1390
1391 void ServiceWorkerStorage::DidDeleteDatabase(
1392     const StatusCallback& callback,
1393     ServiceWorkerDatabase::Status status) {
1394   DCHECK_EQ(DISABLED, state_);
1395   if (status != ServiceWorkerDatabase::STATUS_OK) {
1396     // Give up the corruption recovery until the browser restarts.
1397     LOG(ERROR) << "Failed to delete the database: " << status;
1398     callback.Run(DatabaseStatusToStatusCode(status));
1399     return;
1400   }
1401   DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1402
1403   // Delete the disk cache on the cache thread.
1404   // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1405   // Deleting the directory could take a long time and restart could be delayed.
1406   // We should probably rename the directory and delete it later.
1407   PostTaskAndReplyWithResult(
1408       database_task_runner_.get(),
1409       FROM_HERE,
1410       base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1411       base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1412                  weak_factory_.GetWeakPtr(),
1413                  callback));
1414 }
1415
1416 void ServiceWorkerStorage::DidDeleteDiskCache(
1417     const StatusCallback& callback, bool result) {
1418   DCHECK_EQ(DISABLED, state_);
1419   if (!result) {
1420     // Give up the corruption recovery until the browser restarts.
1421     LOG(ERROR) << "Failed to delete the diskcache.";
1422     callback.Run(SERVICE_WORKER_ERROR_FAILED);
1423     return;
1424   }
1425   DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1426   callback.Run(SERVICE_WORKER_OK);
1427 }
1428
1429 }  // namespace content