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