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
31namespace content {
32
33namespace {
34
35void RunSoon(const tracked_objects::Location& from_here,
36             const base::Closure& closure) {
37  base::MessageLoop::current()->PostTask(from_here, closure);
38}
39
40void CompleteFindNow(
41    const scoped_refptr<ServiceWorkerRegistration>& registration,
42    ServiceWorkerStatusCode status,
43    const ServiceWorkerStorage::FindRegistrationCallback& callback) {
44  callback.Run(status, registration);
45}
46
47void 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
55const base::FilePath::CharType kDatabaseName[] =
56    FILE_PATH_LITERAL("Database");
57const base::FilePath::CharType kDiskCacheName[] =
58    FILE_PATH_LITERAL("Cache");
59
60const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
61const int kMaxDiskCacheSize = 250 * 1024 * 1024;
62
63ServiceWorkerStatusCode 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
77class 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
118void 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
127void 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
134void 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
152void 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
164void 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
202ServiceWorkerStorage::InitialData::InitialData()
203    : next_registration_id(kInvalidServiceWorkerRegistrationId),
204      next_version_id(kInvalidServiceWorkerVersionId),
205      next_resource_id(kInvalidServiceWorkerResourceId) {
206}
207
208ServiceWorkerStorage::InitialData::~InitialData() {
209}
210
211ServiceWorkerStorage::
212DidDeleteRegistrationParams::DidDeleteRegistrationParams()
213    : registration_id(kInvalidServiceWorkerRegistrationId) {
214}
215
216ServiceWorkerStorage::
217DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
218}
219
220ServiceWorkerStorage::~ServiceWorkerStorage() {
221  weak_factory_.InvalidateWeakPtrs();
222  database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
223}
224
225// static
226scoped_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
240scoped_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
251void 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
295void 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
334ServiceWorkerRegistration* 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
350void 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
395void 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
421void 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
462void 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
485void 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
503void 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
538scoped_ptr<ServiceWorkerResponseReader>
539ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
540  return make_scoped_ptr(
541      new ServiceWorkerResponseReader(response_id, disk_cache()));
542}
543
544scoped_ptr<ServiceWorkerResponseWriter>
545ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
546  return make_scoped_ptr(
547      new ServiceWorkerResponseWriter(response_id, disk_cache()));
548}
549
550void 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
565void 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
576void 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
588void 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
602int64 ServiceWorkerStorage::NewRegistrationId() {
603  if (state_ == DISABLED)
604    return kInvalidServiceWorkerRegistrationId;
605  DCHECK_EQ(INITIALIZED, state_);
606  return next_registration_id_++;
607}
608
609int64 ServiceWorkerStorage::NewVersionId() {
610  if (state_ == DISABLED)
611    return kInvalidServiceWorkerVersionId;
612  DCHECK_EQ(INITIALIZED, state_);
613  return next_version_id_++;
614}
615
616int64 ServiceWorkerStorage::NewResourceId() {
617  if (state_ == DISABLED)
618    return kInvalidServiceWorkerResourceId;
619  DCHECK_EQ(INITIALIZED, state_);
620  return next_resource_id_++;
621}
622
623void ServiceWorkerStorage::NotifyInstallingRegistration(
624      ServiceWorkerRegistration* registration) {
625  DCHECK(installing_registrations_.find(registration->id()) ==
626         installing_registrations_.end());
627  installing_registrations_[registration->id()] = registration;
628}
629
630void 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
652void ServiceWorkerStorage::NotifyUninstallingRegistration(
653    ServiceWorkerRegistration* registration) {
654  DCHECK(uninstalling_registrations_.find(registration->id()) ==
655         uninstalling_registrations_.end());
656  uninstalling_registrations_[registration->id()] = registration;
657}
658
659void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
660    ServiceWorkerRegistration* registration) {
661  uninstalling_registrations_.erase(registration->id());
662}
663
664void ServiceWorkerStorage::Disable() {
665  state_ = DISABLED;
666  if (disk_cache_)
667    disk_cache_->Disable();
668}
669
670bool ServiceWorkerStorage::IsDisabled() const {
671  return state_ == DISABLED;
672}
673
674void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
675  if (!has_checked_for_stale_resources_)
676    DeleteStaleResources();
677  StartPurgingResources(resources);
678}
679
680ServiceWorkerStorage::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
701base::FilePath ServiceWorkerStorage::GetDatabasePath() {
702  if (path_.empty())
703    return base::FilePath();
704  return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
705      .Append(kDatabaseName);
706}
707
708base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
709  if (path_.empty())
710    return base::FilePath();
711  return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
712      .Append(kDiskCacheName);
713}
714
715bool 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
743void 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
769void 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
810void 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
835void 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
857void 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
871void 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
935void 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
953void 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
963void 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
983scoped_refptr<ServiceWorkerRegistration>
984ServiceWorkerStorage::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
1019ServiceWorkerRegistration*
1020ServiceWorkerStorage::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
1038ServiceWorkerRegistration*
1039ServiceWorkerStorage::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
1050ServiceWorkerRegistration*
1051ServiceWorkerStorage::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
1060ServiceWorkerDiskCache* 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
1087void 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
1096void 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
1104void 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
1112void 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
1126void 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
1135void 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
1149void 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
1161void 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
1170void 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
1201void 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
1224void 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
1267void 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
1286void 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
1325void 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
1362void 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.
1380void 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
1391void 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
1416void 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
1430