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/message_loop/message_loop.h"
     11 #include "base/sequenced_task_runner.h"
     12 #include "base/task_runner_util.h"
     13 #include "content/browser/service_worker/service_worker_context_core.h"
     14 #include "content/browser/service_worker/service_worker_disk_cache.h"
     15 #include "content/browser/service_worker/service_worker_histograms.h"
     16 #include "content/browser/service_worker/service_worker_info.h"
     17 #include "content/browser/service_worker/service_worker_registration.h"
     18 #include "content/browser/service_worker/service_worker_utils.h"
     19 #include "content/browser/service_worker/service_worker_version.h"
     20 #include "content/common/service_worker/service_worker_types.h"
     21 #include "content/public/browser/browser_thread.h"
     22 #include "net/base/net_errors.h"
     23 #include "webkit/browser/quota/quota_manager_proxy.h"
     24 
     25 namespace content {
     26 
     27 namespace {
     28 
     29 void RunSoon(const tracked_objects::Location& from_here,
     30              const base::Closure& closure) {
     31   base::MessageLoop::current()->PostTask(from_here, closure);
     32 }
     33 
     34 void CompleteFindNow(
     35     const scoped_refptr<ServiceWorkerRegistration>& registration,
     36     ServiceWorkerStatusCode status,
     37     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
     38   callback.Run(status, registration);
     39 }
     40 
     41 void CompleteFindSoon(
     42     const tracked_objects::Location& from_here,
     43     const scoped_refptr<ServiceWorkerRegistration>& registration,
     44     ServiceWorkerStatusCode status,
     45     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
     46   RunSoon(from_here, base::Bind(callback, status, registration));
     47 }
     48 
     49 const base::FilePath::CharType kServiceWorkerDirectory[] =
     50     FILE_PATH_LITERAL("Service Worker");
     51 const base::FilePath::CharType kDatabaseName[] =
     52     FILE_PATH_LITERAL("Database");
     53 const base::FilePath::CharType kDiskCacheName[] =
     54     FILE_PATH_LITERAL("Cache");
     55 
     56 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
     57 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
     58 
     59 void EmptyCompletionCallback(int) {}
     60 
     61 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
     62     ServiceWorkerDatabase::Status status) {
     63   switch (status) {
     64     case ServiceWorkerDatabase::STATUS_OK:
     65       return SERVICE_WORKER_OK;
     66     case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
     67       return SERVICE_WORKER_ERROR_NOT_FOUND;
     68     case ServiceWorkerDatabase::STATUS_ERROR_MAX:
     69       NOTREACHED();
     70     default:
     71       return SERVICE_WORKER_ERROR_FAILED;
     72   }
     73 }
     74 
     75 }  // namespace
     76 
     77 ServiceWorkerStorage::InitialData::InitialData()
     78     : next_registration_id(kInvalidServiceWorkerRegistrationId),
     79       next_version_id(kInvalidServiceWorkerVersionId),
     80       next_resource_id(kInvalidServiceWorkerResourceId) {
     81 }
     82 
     83 ServiceWorkerStorage::InitialData::~InitialData() {
     84 }
     85 
     86 ServiceWorkerStorage::ServiceWorkerStorage(
     87     const base::FilePath& path,
     88     base::WeakPtr<ServiceWorkerContextCore> context,
     89     base::SequencedTaskRunner* database_task_runner,
     90     base::MessageLoopProxy* disk_cache_thread,
     91     quota::QuotaManagerProxy* quota_manager_proxy)
     92     : next_registration_id_(kInvalidServiceWorkerRegistrationId),
     93       next_version_id_(kInvalidServiceWorkerVersionId),
     94       next_resource_id_(kInvalidServiceWorkerResourceId),
     95       state_(UNINITIALIZED),
     96       context_(context),
     97       database_task_runner_(database_task_runner),
     98       disk_cache_thread_(disk_cache_thread),
     99       quota_manager_proxy_(quota_manager_proxy),
    100       is_purge_pending_(false),
    101       weak_factory_(this) {
    102   if (!path.empty())
    103     path_ = path.Append(kServiceWorkerDirectory);
    104   database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
    105 }
    106 
    107 ServiceWorkerStorage::~ServiceWorkerStorage() {
    108   weak_factory_.InvalidateWeakPtrs();
    109   database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
    110 }
    111 
    112 void ServiceWorkerStorage::FindRegistrationForDocument(
    113     const GURL& document_url,
    114     const FindRegistrationCallback& callback) {
    115   DCHECK(!document_url.has_ref());
    116   if (!LazyInitialize(base::Bind(
    117           &ServiceWorkerStorage::FindRegistrationForDocument,
    118           weak_factory_.GetWeakPtr(), document_url, callback))) {
    119     if (state_ != INITIALIZING || !context_) {
    120       CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
    121                       SERVICE_WORKER_ERROR_FAILED, callback);
    122     }
    123     return;
    124   }
    125   DCHECK_EQ(INITIALIZED, state_);
    126 
    127   // See if there are any stored registrations for the origin.
    128   if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
    129     // Look for something currently being installed.
    130     scoped_refptr<ServiceWorkerRegistration> installing_registration =
    131         FindInstallingRegistrationForDocument(document_url);
    132     CompleteFindNow(
    133         installing_registration,
    134         installing_registration ?
    135             SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
    136         callback);
    137     return;
    138   }
    139 
    140   database_task_runner_->PostTask(
    141       FROM_HERE,
    142       base::Bind(
    143           &FindForDocumentInDB,
    144           database_.get(),
    145           base::MessageLoopProxy::current(),
    146           document_url,
    147           base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
    148                      weak_factory_.GetWeakPtr(), document_url, callback)));
    149 }
    150 
    151 void ServiceWorkerStorage::FindRegistrationForPattern(
    152     const GURL& scope,
    153     const FindRegistrationCallback& callback) {
    154   if (!LazyInitialize(base::Bind(
    155           &ServiceWorkerStorage::FindRegistrationForPattern,
    156           weak_factory_.GetWeakPtr(), scope, callback))) {
    157     if (state_ != INITIALIZING || !context_) {
    158       CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
    159                        SERVICE_WORKER_ERROR_FAILED, callback);
    160     }
    161     return;
    162   }
    163   DCHECK_EQ(INITIALIZED, state_);
    164 
    165   // See if there are any stored registrations for the origin.
    166   if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
    167     // Look for something currently being installed.
    168     scoped_refptr<ServiceWorkerRegistration> installing_registration =
    169         FindInstallingRegistrationForPattern(scope);
    170     CompleteFindSoon(
    171         FROM_HERE, installing_registration,
    172         installing_registration ?
    173             SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
    174         callback);
    175     return;
    176   }
    177 
    178   database_task_runner_->PostTask(
    179       FROM_HERE,
    180       base::Bind(
    181           &FindForPatternInDB,
    182           database_.get(),
    183           base::MessageLoopProxy::current(),
    184           scope,
    185           base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
    186                      weak_factory_.GetWeakPtr(), scope, callback)));
    187 }
    188 
    189 void ServiceWorkerStorage::FindRegistrationForId(
    190     int64 registration_id,
    191     const GURL& origin,
    192     const FindRegistrationCallback& callback) {
    193   if (!LazyInitialize(base::Bind(
    194           &ServiceWorkerStorage::FindRegistrationForId,
    195           weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
    196     if (state_ != INITIALIZING || !context_) {
    197       CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
    198                       SERVICE_WORKER_ERROR_FAILED, callback);
    199     }
    200     return;
    201   }
    202   DCHECK_EQ(INITIALIZED, state_);
    203 
    204   // See if there are any stored registrations for the origin.
    205   if (!ContainsKey(registered_origins_, origin)) {
    206     // Look for something currently being installed.
    207     scoped_refptr<ServiceWorkerRegistration> installing_registration =
    208         FindInstallingRegistrationForId(registration_id);
    209     CompleteFindNow(
    210         installing_registration,
    211         installing_registration ?
    212             SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
    213         callback);
    214     return;
    215   }
    216 
    217   scoped_refptr<ServiceWorkerRegistration> registration =
    218       context_->GetLiveRegistration(registration_id);
    219   if (registration) {
    220     CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
    221     return;
    222   }
    223 
    224   database_task_runner_->PostTask(
    225       FROM_HERE,
    226       base::Bind(&FindForIdInDB,
    227                  database_.get(),
    228                  base::MessageLoopProxy::current(),
    229                  registration_id, origin,
    230                  base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
    231                             weak_factory_.GetWeakPtr(), callback)));
    232 }
    233 
    234 void ServiceWorkerStorage::GetAllRegistrations(
    235     const GetAllRegistrationInfosCallback& callback) {
    236   if (!LazyInitialize(base::Bind(
    237           &ServiceWorkerStorage::GetAllRegistrations,
    238           weak_factory_.GetWeakPtr(), callback))) {
    239     if (state_ != INITIALIZING || !context_) {
    240       RunSoon(FROM_HERE, base::Bind(
    241           callback, std::vector<ServiceWorkerRegistrationInfo>()));
    242     }
    243     return;
    244   }
    245   DCHECK_EQ(INITIALIZED, state_);
    246 
    247   RegistrationList* registrations = new RegistrationList;
    248   PostTaskAndReplyWithResult(
    249       database_task_runner_,
    250       FROM_HERE,
    251       base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
    252                  base::Unretained(database_.get()),
    253                  base::Unretained(registrations)),
    254       base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
    255                  weak_factory_.GetWeakPtr(),
    256                  callback,
    257                  base::Owned(registrations)));
    258 }
    259 
    260 void ServiceWorkerStorage::StoreRegistration(
    261     ServiceWorkerRegistration* registration,
    262     ServiceWorkerVersion* version,
    263     const StatusCallback& callback) {
    264   DCHECK(registration);
    265   DCHECK(version);
    266 
    267   DCHECK(state_ == INITIALIZED || state_ == DISABLED);
    268   if (state_ != INITIALIZED || !context_) {
    269     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
    270     return;
    271   }
    272 
    273   ServiceWorkerDatabase::RegistrationData data;
    274   data.registration_id = registration->id();
    275   data.scope = registration->pattern();
    276   data.script = registration->script_url();
    277   data.has_fetch_handler = true;
    278   data.version_id = version->version_id();
    279   data.last_update_check = base::Time::Now();
    280   data.is_active = false;  // initially stored in the waiting state
    281 
    282   ResourceList resources;
    283   version->script_cache_map()->GetResources(&resources);
    284 
    285   database_task_runner_->PostTask(
    286       FROM_HERE,
    287       base::Bind(&WriteRegistrationInDB,
    288                  database_.get(),
    289                  base::MessageLoopProxy::current(),
    290                  data, resources,
    291                  base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
    292                             weak_factory_.GetWeakPtr(),
    293                             callback)));
    294 }
    295 
    296 void ServiceWorkerStorage::UpdateToActiveState(
    297     ServiceWorkerRegistration* registration,
    298     const StatusCallback& callback) {
    299   DCHECK(registration);
    300 
    301   DCHECK(state_ == INITIALIZED || state_ == DISABLED);
    302   if (state_ != INITIALIZED || !context_) {
    303     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
    304     return;
    305   }
    306 
    307   PostTaskAndReplyWithResult(
    308       database_task_runner_,
    309       FROM_HERE,
    310       base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
    311                  base::Unretained(database_.get()),
    312                  registration->id(),
    313                  registration->script_url().GetOrigin()),
    314       base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
    315                  weak_factory_.GetWeakPtr(),
    316                  callback));
    317 }
    318 
    319 void ServiceWorkerStorage::DeleteRegistration(
    320     int64 registration_id,
    321     const GURL& origin,
    322     const StatusCallback& callback) {
    323   DCHECK(state_ == INITIALIZED || state_ == DISABLED);
    324   if (state_ != INITIALIZED || !context_) {
    325     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
    326     return;
    327   }
    328 
    329   database_task_runner_->PostTask(
    330       FROM_HERE,
    331       base::Bind(&DeleteRegistrationFromDB,
    332                  database_.get(),
    333                  base::MessageLoopProxy::current(),
    334                  registration_id, origin,
    335                  base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
    336                             weak_factory_.GetWeakPtr(), origin, callback)));
    337 
    338   // TODO(michaeln): Either its instance should also be
    339   // removed from liveregistrations map or the live object
    340   // should marked as deleted in some way and not 'findable'
    341   // thereafter.
    342 }
    343 
    344 scoped_ptr<ServiceWorkerResponseReader>
    345 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
    346   return make_scoped_ptr(
    347       new ServiceWorkerResponseReader(response_id, disk_cache()));
    348 }
    349 
    350 scoped_ptr<ServiceWorkerResponseWriter>
    351 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
    352   return make_scoped_ptr(
    353       new ServiceWorkerResponseWriter(response_id, disk_cache()));
    354 }
    355 
    356 void ServiceWorkerStorage::StoreUncommittedReponseId(int64 id) {
    357   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
    358   database_task_runner_->PostTask(
    359       FROM_HERE,
    360       base::Bind(base::IgnoreResult(
    361           &ServiceWorkerDatabase::WriteUncommittedResourceIds),
    362           base::Unretained(database_.get()),
    363           std::set<int64>(&id, &id + 1)));
    364 }
    365 
    366 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
    367   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
    368   database_task_runner_->PostTask(
    369       FROM_HERE,
    370       base::Bind(base::IgnoreResult(
    371           &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
    372           base::Unretained(database_.get()),
    373           std::set<int64>(&id, &id + 1)));
    374   StartPurgingResources(std::vector<int64>(1, id));
    375 }
    376 
    377 int64 ServiceWorkerStorage::NewRegistrationId() {
    378   if (state_ == DISABLED)
    379     return kInvalidServiceWorkerRegistrationId;
    380   DCHECK_EQ(INITIALIZED, state_);
    381   return next_registration_id_++;
    382 }
    383 
    384 int64 ServiceWorkerStorage::NewVersionId() {
    385   if (state_ == DISABLED)
    386     return kInvalidServiceWorkerVersionId;
    387   DCHECK_EQ(INITIALIZED, state_);
    388   return next_version_id_++;
    389 }
    390 
    391 int64 ServiceWorkerStorage::NewResourceId() {
    392   if (state_ == DISABLED)
    393     return kInvalidServiceWorkerResourceId;
    394   DCHECK_EQ(INITIALIZED, state_);
    395   return next_resource_id_++;
    396 }
    397 
    398 void ServiceWorkerStorage::NotifyInstallingRegistration(
    399       ServiceWorkerRegistration* registration) {
    400   installing_registrations_[registration->id()] = registration;
    401 }
    402 
    403 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
    404       ServiceWorkerRegistration* registration,
    405       ServiceWorkerVersion* version,
    406       ServiceWorkerStatusCode status) {
    407   installing_registrations_.erase(registration->id());
    408   if (status != SERVICE_WORKER_OK && version) {
    409     ResourceList resources;
    410     version->script_cache_map()->GetResources(&resources);
    411 
    412     std::set<int64> ids;
    413     for (size_t i = 0; i < resources.size(); ++i)
    414       ids.insert(resources[i].resource_id);
    415 
    416     database_task_runner_->PostTask(
    417         FROM_HERE,
    418         base::Bind(base::IgnoreResult(
    419             &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
    420             base::Unretained(database_.get()),
    421             ids));
    422 
    423     StartPurgingResources(resources);
    424   }
    425 }
    426 
    427 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
    428   if (path_.empty())
    429     return base::FilePath();
    430   return path_.Append(kDatabaseName);
    431 }
    432 
    433 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
    434   if (path_.empty())
    435     return base::FilePath();
    436   return path_.Append(kDiskCacheName);
    437 }
    438 
    439 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
    440   if (!context_)
    441     return false;
    442 
    443   switch (state_) {
    444     case INITIALIZED:
    445       return true;
    446     case DISABLED:
    447       return false;
    448     case INITIALIZING:
    449       pending_tasks_.push_back(callback);
    450       return false;
    451     case UNINITIALIZED:
    452       pending_tasks_.push_back(callback);
    453       // Fall-through.
    454   }
    455 
    456   state_ = INITIALIZING;
    457   database_task_runner_->PostTask(
    458       FROM_HERE,
    459       base::Bind(&ReadInitialDataFromDB,
    460                  database_.get(),
    461                  base::MessageLoopProxy::current(),
    462                  base::Bind(&ServiceWorkerStorage::DidReadInitialData,
    463                             weak_factory_.GetWeakPtr())));
    464   return false;
    465 }
    466 
    467 void ServiceWorkerStorage::DidReadInitialData(
    468     InitialData* data,
    469     ServiceWorkerDatabase::Status status) {
    470   DCHECK(data);
    471   DCHECK_EQ(INITIALIZING, state_);
    472 
    473   if (status == ServiceWorkerDatabase::STATUS_OK) {
    474     next_registration_id_ = data->next_registration_id;
    475     next_version_id_ = data->next_version_id;
    476     next_resource_id_ = data->next_resource_id;
    477     registered_origins_.swap(data->origins);
    478     state_ = INITIALIZED;
    479   } else {
    480     // TODO(nhiroki): If status==STATUS_ERROR_CORRUPTED, do corruption recovery
    481     // (http://crbug.com/371675).
    482     DLOG(WARNING) << "Failed to initialize: " << status;
    483     state_ = DISABLED;
    484   }
    485 
    486   for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
    487        it != pending_tasks_.end(); ++it) {
    488     RunSoon(FROM_HERE, *it);
    489   }
    490   pending_tasks_.clear();
    491 }
    492 
    493 void ServiceWorkerStorage::DidFindRegistrationForDocument(
    494     const GURL& document_url,
    495     const FindRegistrationCallback& callback,
    496     const ServiceWorkerDatabase::RegistrationData& data,
    497     const ResourceList& resources,
    498     ServiceWorkerDatabase::Status status) {
    499   if (status == ServiceWorkerDatabase::STATUS_OK) {
    500     callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
    501     return;
    502   }
    503 
    504   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
    505     // Look for something currently being installed.
    506     scoped_refptr<ServiceWorkerRegistration> installing_registration =
    507         FindInstallingRegistrationForDocument(document_url);
    508     callback.Run(installing_registration ?
    509         SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
    510         installing_registration);
    511     return;
    512   }
    513 
    514   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    515   callback.Run(DatabaseStatusToStatusCode(status),
    516                scoped_refptr<ServiceWorkerRegistration>());
    517 }
    518 
    519 void ServiceWorkerStorage::DidFindRegistrationForPattern(
    520     const GURL& scope,
    521     const FindRegistrationCallback& callback,
    522     const ServiceWorkerDatabase::RegistrationData& data,
    523     const ResourceList& resources,
    524     ServiceWorkerDatabase::Status status) {
    525   if (status == ServiceWorkerDatabase::STATUS_OK) {
    526     callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
    527     return;
    528   }
    529 
    530   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
    531     scoped_refptr<ServiceWorkerRegistration> installing_registration =
    532         FindInstallingRegistrationForPattern(scope);
    533     callback.Run(installing_registration ?
    534         SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
    535         installing_registration);
    536     return;
    537   }
    538 
    539   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    540   callback.Run(DatabaseStatusToStatusCode(status),
    541                scoped_refptr<ServiceWorkerRegistration>());
    542 }
    543 
    544 void ServiceWorkerStorage::DidFindRegistrationForId(
    545     const FindRegistrationCallback& callback,
    546     const ServiceWorkerDatabase::RegistrationData& data,
    547     const ResourceList& resources,
    548     ServiceWorkerDatabase::Status status) {
    549   if (status == ServiceWorkerDatabase::STATUS_OK) {
    550     callback.Run(SERVICE_WORKER_OK,
    551                  GetOrCreateRegistration(data, resources));
    552     return;
    553   }
    554   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    555   callback.Run(DatabaseStatusToStatusCode(status),
    556                scoped_refptr<ServiceWorkerRegistration>());
    557 }
    558 
    559 void ServiceWorkerStorage::DidGetAllRegistrations(
    560     const GetAllRegistrationInfosCallback& callback,
    561     RegistrationList* registrations,
    562     ServiceWorkerDatabase::Status status) {
    563   DCHECK(registrations);
    564   if (status != ServiceWorkerDatabase::STATUS_OK) {
    565     // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    566     callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
    567     return;
    568   }
    569 
    570   // Add all stored registrations.
    571   std::set<int64> pushed_registrations;
    572   std::vector<ServiceWorkerRegistrationInfo> infos;
    573   for (RegistrationList::const_iterator it = registrations->begin();
    574        it != registrations->end(); ++it) {
    575     const bool inserted =
    576         pushed_registrations.insert(it->registration_id).second;
    577     DCHECK(inserted);
    578 
    579     ServiceWorkerRegistration* registration =
    580         context_->GetLiveRegistration(it->registration_id);
    581     if (registration) {
    582       infos.push_back(registration->GetInfo());
    583       continue;
    584     }
    585 
    586     ServiceWorkerRegistrationInfo info;
    587     info.pattern = it->scope;
    588     info.script_url = it->script;
    589     info.registration_id = it->registration_id;
    590     if (ServiceWorkerVersion* version =
    591             context_->GetLiveVersion(it->version_id)) {
    592       if (it->is_active)
    593         info.active_version = version->GetInfo();
    594       else
    595         info.waiting_version = version->GetInfo();
    596       infos.push_back(info);
    597       continue;
    598     }
    599 
    600     if (it->is_active) {
    601       info.active_version.is_null = false;
    602       info.active_version.status = ServiceWorkerVersion::ACTIVE;
    603       info.active_version.version_id = it->version_id;
    604     } else {
    605       info.waiting_version.is_null = false;
    606       info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
    607       info.waiting_version.version_id = it->version_id;
    608     }
    609     infos.push_back(info);
    610   }
    611 
    612   // Add unstored registrations that are being installed.
    613   for (RegistrationRefsById::const_iterator it =
    614            installing_registrations_.begin();
    615        it != installing_registrations_.end(); ++it) {
    616     if (pushed_registrations.insert(it->first).second)
    617       infos.push_back(it->second->GetInfo());
    618   }
    619 
    620   callback.Run(infos);
    621 }
    622 
    623 void ServiceWorkerStorage::DidStoreRegistration(
    624     const StatusCallback& callback,
    625     const GURL& origin,
    626     const std::vector<int64>& newly_purgeable_resources,
    627     ServiceWorkerDatabase::Status status) {
    628   if (status != ServiceWorkerDatabase::STATUS_OK) {
    629     // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    630     callback.Run(DatabaseStatusToStatusCode(status));
    631     return;
    632   }
    633   registered_origins_.insert(origin);
    634   callback.Run(SERVICE_WORKER_OK);
    635   StartPurgingResources(newly_purgeable_resources);
    636 }
    637 
    638 void ServiceWorkerStorage::DidUpdateToActiveState(
    639     const StatusCallback& callback,
    640     ServiceWorkerDatabase::Status status) {
    641   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    642   callback.Run(DatabaseStatusToStatusCode(status));
    643 }
    644 
    645 void ServiceWorkerStorage::DidDeleteRegistration(
    646     const GURL& origin,
    647     const StatusCallback& callback,
    648     bool origin_is_deletable,
    649     const std::vector<int64>& newly_purgeable_resources,
    650     ServiceWorkerDatabase::Status status) {
    651   if (status != ServiceWorkerDatabase::STATUS_OK) {
    652     // TODO(nhiroki): Handle database error (http://crbug.com/371675).
    653     callback.Run(DatabaseStatusToStatusCode(status));
    654     return;
    655   }
    656   if (origin_is_deletable)
    657     registered_origins_.erase(origin);
    658   callback.Run(SERVICE_WORKER_OK);
    659   StartPurgingResources(newly_purgeable_resources);
    660 }
    661 
    662 scoped_refptr<ServiceWorkerRegistration>
    663 ServiceWorkerStorage::GetOrCreateRegistration(
    664     const ServiceWorkerDatabase::RegistrationData& data,
    665     const ResourceList& resources) {
    666   scoped_refptr<ServiceWorkerRegistration> registration =
    667       context_->GetLiveRegistration(data.registration_id);
    668   if (registration)
    669     return registration;
    670 
    671   registration = new ServiceWorkerRegistration(
    672       data.scope, data.script, data.registration_id, context_);
    673   scoped_refptr<ServiceWorkerVersion> version =
    674       context_->GetLiveVersion(data.version_id);
    675   if (!version) {
    676     version = new ServiceWorkerVersion(registration, data.version_id, context_);
    677     version->SetStatus(data.is_active ?
    678         ServiceWorkerVersion::ACTIVE : ServiceWorkerVersion::INSTALLED);
    679     version->script_cache_map()->SetResources(resources);
    680   }
    681 
    682   if (version->status() == ServiceWorkerVersion::ACTIVE)
    683     registration->set_active_version(version);
    684   else if (version->status() == ServiceWorkerVersion::INSTALLED)
    685     registration->set_waiting_version(version);
    686   else
    687     NOTREACHED();
    688   // TODO(michaeln): Hmmm, what if DeleteReg was invoked after
    689   // the Find result we're returning here? NOTREACHED condition?
    690   return registration;
    691 }
    692 
    693 ServiceWorkerRegistration*
    694 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
    695     const GURL& document_url) {
    696   DCHECK(!document_url.has_ref());
    697 
    698   LongestScopeMatcher matcher(document_url);
    699   ServiceWorkerRegistration* match = NULL;
    700 
    701   // TODO(nhiroki): This searches over installing registrations linearly and it
    702   // couldn't be scalable. Maybe the regs should be partitioned by origin.
    703   for (RegistrationRefsById::const_iterator it =
    704            installing_registrations_.begin();
    705        it != installing_registrations_.end(); ++it) {
    706     if (matcher.MatchLongest(it->second->pattern()))
    707       match = it->second;
    708   }
    709   return match;
    710 }
    711 
    712 ServiceWorkerRegistration*
    713 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
    714     const GURL& scope) {
    715   for (RegistrationRefsById::const_iterator it =
    716            installing_registrations_.begin();
    717        it != installing_registrations_.end(); ++it) {
    718     if (it->second->pattern() == scope)
    719       return it->second;
    720   }
    721   return NULL;
    722 }
    723 
    724 ServiceWorkerRegistration*
    725 ServiceWorkerStorage::FindInstallingRegistrationForId(
    726     int64 registration_id) {
    727   RegistrationRefsById::const_iterator found =
    728       installing_registrations_.find(registration_id);
    729   if (found == installing_registrations_.end())
    730     return NULL;
    731   return found->second;
    732 }
    733 
    734 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
    735   if (disk_cache_)
    736     return disk_cache_.get();
    737 
    738   disk_cache_.reset(new ServiceWorkerDiskCache);
    739 
    740   base::FilePath path = GetDiskCachePath();
    741   if (path.empty()) {
    742     int rv = disk_cache_->InitWithMemBackend(
    743         kMaxMemDiskCacheSize,
    744         base::Bind(&EmptyCompletionCallback));
    745     DCHECK_EQ(net::OK, rv);
    746     return disk_cache_.get();
    747   }
    748 
    749   int rv = disk_cache_->InitWithDiskBackend(
    750       path, kMaxDiskCacheSize, false,
    751       disk_cache_thread_.get(),
    752       base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
    753                  weak_factory_.GetWeakPtr()));
    754   if (rv != net::ERR_IO_PENDING)
    755     OnDiskCacheInitialized(rv);
    756 
    757   return disk_cache_.get();
    758 }
    759 
    760 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
    761   if (rv != net::OK) {
    762     LOG(ERROR) << "Failed to open the serviceworker diskcache: "
    763                << net::ErrorToString(rv);
    764     // TODO(michaeln): DeleteAndStartOver()
    765     disk_cache_->Disable();
    766     state_ = DISABLED;
    767   }
    768   ServiceWorkerHistograms::CountInitDiskCacheResult(rv == net::OK);
    769 }
    770 
    771 void ServiceWorkerStorage::StartPurgingResources(
    772     const std::vector<int64>& ids) {
    773   for (size_t i = 0; i < ids.size(); ++i)
    774     purgeable_reource_ids_.push_back(ids[i]);
    775   ContinuePurgingResources();
    776 }
    777 
    778 void ServiceWorkerStorage::StartPurgingResources(
    779     const ResourceList& resources) {
    780   for (size_t i = 0; i < resources.size(); ++i)
    781     purgeable_reource_ids_.push_back(resources[i].resource_id);
    782   ContinuePurgingResources();
    783 }
    784 
    785 void ServiceWorkerStorage::ContinuePurgingResources() {
    786   if (purgeable_reource_ids_.empty() || is_purge_pending_)
    787     return;
    788 
    789   // Do one at a time until we're done, use RunSoon to avoid recursion when
    790   // DoomEntry returns immediately.
    791   is_purge_pending_ = true;
    792   int64 id = purgeable_reource_ids_.front();
    793   purgeable_reource_ids_.pop_front();
    794   RunSoon(FROM_HERE,
    795           base::Bind(&ServiceWorkerStorage::PurgeResource,
    796                      weak_factory_.GetWeakPtr(), id));
    797 }
    798 
    799 void ServiceWorkerStorage::PurgeResource(int64 id) {
    800   DCHECK(is_purge_pending_);
    801   int rv = disk_cache()->DoomEntry(
    802       id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
    803                      weak_factory_.GetWeakPtr(), id));
    804   if (rv != net::ERR_IO_PENDING)
    805     OnResourcePurged(id, rv);
    806 }
    807 
    808 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
    809   DCHECK(is_purge_pending_);
    810   is_purge_pending_ = false;
    811 
    812   database_task_runner_->PostTask(
    813       FROM_HERE,
    814       base::Bind(base::IgnoreResult(
    815           &ServiceWorkerDatabase::ClearPurgeableResourceIds),
    816           base::Unretained(database_.get()),
    817           std::set<int64>(&id, &id + 1)));
    818 
    819   ContinuePurgingResources();
    820 }
    821 
    822 void ServiceWorkerStorage::ReadInitialDataFromDB(
    823     ServiceWorkerDatabase* database,
    824     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
    825     const InitializeCallback& callback) {
    826   DCHECK(database);
    827   scoped_ptr<ServiceWorkerStorage::InitialData> data(
    828       new ServiceWorkerStorage::InitialData());
    829 
    830   ServiceWorkerDatabase::Status status =
    831       database->GetNextAvailableIds(&data->next_registration_id,
    832                                     &data->next_version_id,
    833                                     &data->next_resource_id);
    834   if (status != ServiceWorkerDatabase::STATUS_OK) {
    835     original_task_runner->PostTask(
    836         FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
    837     return;
    838   }
    839 
    840   status = database->GetOriginsWithRegistrations(&data->origins);
    841   original_task_runner->PostTask(
    842       FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
    843 }
    844 
    845 void ServiceWorkerStorage::DeleteRegistrationFromDB(
    846     ServiceWorkerDatabase* database,
    847     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
    848     int64 registration_id,
    849     const GURL& origin,
    850     const DeleteRegistrationCallback& callback) {
    851   DCHECK(database);
    852 
    853   std::vector<int64> newly_purgeable_resources;
    854   ServiceWorkerDatabase::Status status =
    855       database->DeleteRegistration(registration_id, origin,
    856                                    &newly_purgeable_resources);
    857   if (status != ServiceWorkerDatabase::STATUS_OK) {
    858     original_task_runner->PostTask(
    859         FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
    860     return;
    861   }
    862 
    863   // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
    864   // unique origin list.
    865   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
    866   status = database->GetRegistrationsForOrigin(origin, &registrations);
    867   if (status != ServiceWorkerDatabase::STATUS_OK) {
    868     original_task_runner->PostTask(
    869         FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
    870     return;
    871   }
    872 
    873   bool deletable = registrations.empty();
    874   original_task_runner->PostTask(
    875       FROM_HERE, base::Bind(callback, deletable,
    876                             newly_purgeable_resources, status));
    877 }
    878 
    879 void ServiceWorkerStorage::WriteRegistrationInDB(
    880     ServiceWorkerDatabase* database,
    881     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
    882     const ServiceWorkerDatabase::RegistrationData& data,
    883     const ResourceList& resources,
    884     const WriteRegistrationCallback& callback) {
    885   DCHECK(database);
    886   std::vector<int64> newly_purgeable_resources;
    887   ServiceWorkerDatabase::Status status =
    888       database->WriteRegistration(data, resources, &newly_purgeable_resources);
    889   original_task_runner->PostTask(
    890       FROM_HERE,
    891       base::Bind(callback, data.script.GetOrigin(),
    892                  newly_purgeable_resources, status));
    893 }
    894 
    895 void ServiceWorkerStorage::FindForDocumentInDB(
    896     ServiceWorkerDatabase* database,
    897     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
    898     const GURL& document_url,
    899     const FindInDBCallback& callback) {
    900   GURL origin = document_url.GetOrigin();
    901   RegistrationList registrations;
    902   ServiceWorkerDatabase::Status status =
    903       database->GetRegistrationsForOrigin(origin, &registrations);
    904   if (status != ServiceWorkerDatabase::STATUS_OK) {
    905     original_task_runner->PostTask(
    906         FROM_HERE,
    907         base::Bind(callback,
    908                    ServiceWorkerDatabase::RegistrationData(),
    909                    ResourceList(),
    910                    status));
    911     return;
    912   }
    913 
    914   ServiceWorkerDatabase::RegistrationData data;
    915   ResourceList resources;
    916   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
    917 
    918   // Find one with a pattern match.
    919   LongestScopeMatcher matcher(document_url);
    920   int64 match = kInvalidServiceWorkerRegistrationId;
    921   for (size_t i = 0; i < registrations.size(); ++i) {
    922     if (matcher.MatchLongest(registrations[i].scope))
    923       match = registrations[i].registration_id;
    924   }
    925 
    926   if (match != kInvalidServiceWorkerRegistrationId)
    927     status = database->ReadRegistration(match, origin, &data, &resources);
    928 
    929   original_task_runner->PostTask(
    930       FROM_HERE,
    931       base::Bind(callback, data, resources, status));
    932 }
    933 
    934 void ServiceWorkerStorage::FindForPatternInDB(
    935     ServiceWorkerDatabase* database,
    936     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
    937     const GURL& scope,
    938     const FindInDBCallback& callback) {
    939   GURL origin = scope.GetOrigin();
    940   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
    941   ServiceWorkerDatabase::Status status =
    942       database->GetRegistrationsForOrigin(origin, &registrations);
    943   if (status != ServiceWorkerDatabase::STATUS_OK) {
    944     original_task_runner->PostTask(
    945         FROM_HERE,
    946         base::Bind(callback,
    947                    ServiceWorkerDatabase::RegistrationData(),
    948                    ResourceList(),
    949                    status));
    950     return;
    951   }
    952 
    953   // Find one with an exact matching scope.
    954   ServiceWorkerDatabase::RegistrationData data;
    955   ResourceList resources;
    956   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
    957   for (RegistrationList::const_iterator it = registrations.begin();
    958        it != registrations.end(); ++it) {
    959     if (scope != it->scope)
    960       continue;
    961     status = database->ReadRegistration(it->registration_id, origin,
    962                                         &data, &resources);
    963     break;  // We're done looping.
    964   }
    965 
    966   original_task_runner->PostTask(
    967       FROM_HERE,
    968       base::Bind(callback, data, resources, status));
    969 }
    970 
    971 void ServiceWorkerStorage::FindForIdInDB(
    972     ServiceWorkerDatabase* database,
    973     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
    974     int64 registration_id,
    975     const GURL& origin,
    976     const FindInDBCallback& callback) {
    977   ServiceWorkerDatabase::RegistrationData data;
    978   ResourceList resources;
    979   ServiceWorkerDatabase::Status status =
    980       database->ReadRegistration(registration_id, origin, &data, &resources);
    981   original_task_runner->PostTask(
    982       FROM_HERE, base::Bind(callback, data, resources, status));
    983 }
    984 
    985 }  // namespace content
    986