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