Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_storage.cc
index 8a924e0..38e8c62 100644 (file)
@@ -7,18 +7,21 @@
 #include <string>
 
 #include "base/bind_helpers.h"
+#include "base/file_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_runner_util.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_disk_cache.h"
-#include "content/browser/service_worker/service_worker_histograms.h"
 #include "content/browser/service_worker/service_worker_info.h"
+#include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_thread.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "webkit/browser/quota/quota_manager_proxy.h"
 
@@ -46,8 +49,6 @@ void CompleteFindSoon(
   RunSoon(from_here, base::Bind(callback, status, registration));
 }
 
-const base::FilePath::CharType kServiceWorkerDirectory[] =
-    FILE_PATH_LITERAL("Service Worker");
 const base::FilePath::CharType kDatabaseName[] =
     FILE_PATH_LITERAL("Database");
 const base::FilePath::CharType kDiskCacheName[] =
@@ -56,8 +57,6 @@ const base::FilePath::CharType kDiskCacheName[] =
 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
 
-void EmptyCompletionCallback(int) {}
-
 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
     ServiceWorkerDatabase::Status status) {
   switch (status) {
@@ -72,6 +71,133 @@ ServiceWorkerStatusCode DatabaseStatusToStatusCode(
   }
 }
 
+class ResponseComparer : public base::RefCounted<ResponseComparer> {
+ public:
+  ResponseComparer(
+      base::WeakPtr<ServiceWorkerStorage> owner,
+      scoped_ptr<ServiceWorkerResponseReader> lhs,
+      scoped_ptr<ServiceWorkerResponseReader> rhs,
+      const ServiceWorkerStorage::CompareCallback& callback)
+      : owner_(owner),
+        completion_callback_(callback),
+        lhs_reader_(lhs.release()),
+        rhs_reader_(rhs.release()),
+        completion_count_(0),
+        previous_result_(0) {
+  }
+
+  void Start();
+
+ private:
+  friend class base::RefCounted<ResponseComparer>;
+
+  static const int kBufferSize = 16 * 1024;
+
+  ~ResponseComparer() {}
+  void ReadInfos();
+  void OnReadInfoComplete(int result);
+  void ReadSomeData();
+  void OnReadDataComplete(int result);
+
+  base::WeakPtr<ServiceWorkerStorage> owner_;
+  ServiceWorkerStorage::CompareCallback completion_callback_;
+  scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
+  scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
+  scoped_refptr<net::IOBuffer> lhs_buffer_;
+  scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
+  scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
+  scoped_refptr<net::IOBuffer> rhs_buffer_;
+  int completion_count_;
+  int previous_result_;
+  DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
+};
+
+void ResponseComparer::Start() {
+  lhs_buffer_ = new net::IOBuffer(kBufferSize);
+  lhs_info_ = new HttpResponseInfoIOBuffer();
+  rhs_buffer_ = new net::IOBuffer(kBufferSize);
+  rhs_info_ = new HttpResponseInfoIOBuffer();
+
+  ReadInfos();
+}
+
+void ResponseComparer::ReadInfos() {
+  lhs_reader_->ReadInfo(
+      lhs_info_,
+      base::Bind(&ResponseComparer::OnReadInfoComplete,
+                 this));
+  rhs_reader_->ReadInfo(
+      rhs_info_,
+      base::Bind(&ResponseComparer::OnReadInfoComplete,
+                 this));
+}
+
+void ResponseComparer::OnReadInfoComplete(int result) {
+  if (completion_callback_.is_null() || !owner_)
+    return;
+  if (result < 0) {
+    completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
+    completion_callback_.Reset();
+    return;
+  }
+  if (++completion_count_ != 2)
+    return;
+
+  if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
+    completion_callback_.Run(SERVICE_WORKER_OK, false);
+    return;
+  }
+  ReadSomeData();
+}
+
+void ResponseComparer::ReadSomeData() {
+  completion_count_ = 0;
+  lhs_reader_->ReadData(
+      lhs_buffer_,
+      kBufferSize,
+      base::Bind(&ResponseComparer::OnReadDataComplete, this));
+  rhs_reader_->ReadData(
+      rhs_buffer_,
+      kBufferSize,
+      base::Bind(&ResponseComparer::OnReadDataComplete, this));
+}
+
+void ResponseComparer::OnReadDataComplete(int result) {
+  if (completion_callback_.is_null() || !owner_)
+    return;
+  if (result < 0) {
+    completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
+    completion_callback_.Reset();
+    return;
+  }
+  if (++completion_count_ != 2) {
+    previous_result_ = result;
+    return;
+  }
+
+  // TODO(michaeln): Probably shouldn't assume that the amounts read from
+  // each reader will always be the same. This would wrongly signal false
+  // in that case.
+  if (result != previous_result_) {
+    completion_callback_.Run(SERVICE_WORKER_OK, false);
+    return;
+  }
+
+  if (result == 0) {
+    completion_callback_.Run(SERVICE_WORKER_OK, true);
+    return;
+  }
+
+  int compare_result =
+      memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
+  if (compare_result != 0) {
+    completion_callback_.Run(SERVICE_WORKER_OK, false);
+    return;
+  }
+
+  ReadSomeData();
+}
+
 }  // namespace
 
 ServiceWorkerStorage::InitialData::InitialData()
@@ -83,30 +209,45 @@ ServiceWorkerStorage::InitialData::InitialData()
 ServiceWorkerStorage::InitialData::~InitialData() {
 }
 
-ServiceWorkerStorage::ServiceWorkerStorage(
+ServiceWorkerStorage::
+DidDeleteRegistrationParams::DidDeleteRegistrationParams()
+    : registration_id(kInvalidServiceWorkerRegistrationId) {
+}
+
+ServiceWorkerStorage::
+DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
+}
+
+ServiceWorkerStorage::~ServiceWorkerStorage() {
+  weak_factory_.InvalidateWeakPtrs();
+  database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
+}
+
+// static
+scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
     const base::FilePath& path,
     base::WeakPtr<ServiceWorkerContextCore> context,
     base::SequencedTaskRunner* database_task_runner,
     base::MessageLoopProxy* disk_cache_thread,
-    quota::QuotaManagerProxy* quota_manager_proxy)
-    : next_registration_id_(kInvalidServiceWorkerRegistrationId),
-      next_version_id_(kInvalidServiceWorkerVersionId),
-      next_resource_id_(kInvalidServiceWorkerResourceId),
-      state_(UNINITIALIZED),
-      context_(context),
-      database_task_runner_(database_task_runner),
-      disk_cache_thread_(disk_cache_thread),
-      quota_manager_proxy_(quota_manager_proxy),
-      is_purge_pending_(false),
-      weak_factory_(this) {
-  if (!path.empty())
-    path_ = path.Append(kServiceWorkerDirectory);
-  database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
+    quota::QuotaManagerProxy* quota_manager_proxy) {
+  return make_scoped_ptr(
+      new ServiceWorkerStorage(path,
+                               context,
+                               database_task_runner,
+                               disk_cache_thread,
+                               quota_manager_proxy));
 }
 
-ServiceWorkerStorage::~ServiceWorkerStorage() {
-  weak_factory_.InvalidateWeakPtrs();
-  database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
+// static
+scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
+    base::WeakPtr<ServiceWorkerContextCore> context,
+    ServiceWorkerStorage* old_storage) {
+  return make_scoped_ptr(
+      new ServiceWorkerStorage(old_storage->path_,
+                               context,
+                               old_storage->database_task_runner_,
+                               old_storage->disk_cache_thread_,
+                               old_storage->quota_manager_proxy_));
 }
 
 void ServiceWorkerStorage::FindRegistrationForDocument(
@@ -186,6 +327,22 @@ void ServiceWorkerStorage::FindRegistrationForPattern(
                      weak_factory_.GetWeakPtr(), scope, callback)));
 }
 
+ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
+    const GURL& scope) {
+  if (state_ != INITIALIZED || !context_)
+    return NULL;
+  for (RegistrationRefsById::const_iterator it =
+           uninstalling_registrations_.begin();
+       it != uninstalling_registrations_.end();
+       ++it) {
+    if (it->second->pattern() == scope) {
+      DCHECK(it->second->is_uninstalling());
+      return it->second;
+    }
+  }
+  return NULL;
+}
+
 void ServiceWorkerStorage::FindRegistrationForId(
     int64 registration_id,
     const GURL& origin,
@@ -264,8 +421,8 @@ void ServiceWorkerStorage::StoreRegistration(
   DCHECK(registration);
   DCHECK(version);
 
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED);
-  if (state_ != INITIALIZED || !context_) {
+  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
+  if (IsDisabled() || !context_) {
     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
     return;
   }
@@ -276,12 +433,15 @@ void ServiceWorkerStorage::StoreRegistration(
   data.script = registration->script_url();
   data.has_fetch_handler = true;
   data.version_id = version->version_id();
-  data.last_update_check = base::Time::Now();
-  data.is_active = false;  // initially stored in the waiting state
+  data.last_update_check = registration->last_update_check();
+  data.is_active = (version == registration->active_version());
 
   ResourceList resources;
   version->script_cache_map()->GetResources(&resources);
 
+  if (!has_checked_for_stale_resources_)
+    DeleteStaleResources();
+
   database_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&WriteRegistrationInDB,
@@ -291,6 +451,8 @@ void ServiceWorkerStorage::StoreRegistration(
                  base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
                             weak_factory_.GetWeakPtr(),
                             callback)));
+
+  registration->set_is_deleted(false);
 }
 
 void ServiceWorkerStorage::UpdateToActiveState(
@@ -298,8 +460,8 @@ void ServiceWorkerStorage::UpdateToActiveState(
     const StatusCallback& callback) {
   DCHECK(registration);
 
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED);
-  if (state_ != INITIALIZED || !context_) {
+  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
+  if (IsDisabled() || !context_) {
     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
     return;
   }
@@ -316,16 +478,42 @@ void ServiceWorkerStorage::UpdateToActiveState(
                  callback));
 }
 
+void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
+    ServiceWorkerRegistration* registration) {
+  DCHECK(registration);
+
+  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
+  if (IsDisabled() || !context_)
+    return;
+
+  database_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
+          base::Unretained(database_.get()),
+          registration->id(),
+          registration->script_url().GetOrigin(),
+          registration->last_update_check()));
+}
+
 void ServiceWorkerStorage::DeleteRegistration(
     int64 registration_id,
     const GURL& origin,
     const StatusCallback& callback) {
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED);
-  if (state_ != INITIALIZED || !context_) {
+  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
+  if (IsDisabled() || !context_) {
     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
     return;
   }
 
+  if (!has_checked_for_stale_resources_)
+    DeleteStaleResources();
+
+  DidDeleteRegistrationParams params;
+  params.registration_id = registration_id;
+  params.origin = origin;
+  params.callback = callback;
+
   database_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&DeleteRegistrationFromDB,
@@ -333,12 +521,14 @@ void ServiceWorkerStorage::DeleteRegistration(
                  base::MessageLoopProxy::current(),
                  registration_id, origin,
                  base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
-                            weak_factory_.GetWeakPtr(), origin, callback)));
+                            weak_factory_.GetWeakPtr(), params)));
 
-  // TODO(michaeln): Either its instance should also be
-  // removed from liveregistrations map or the live object
-  // should marked as deleted in some way and not 'findable'
-  // thereafter.
+  // The registration should no longer be findable.
+  pending_deletions_.insert(registration_id);
+  ServiceWorkerRegistration* registration =
+      context_->GetLiveRegistration(registration_id);
+  if (registration)
+    registration->set_is_deleted(true);
 }
 
 scoped_ptr<ServiceWorkerResponseReader>
@@ -353,8 +543,13 @@ ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
       new ServiceWorkerResponseWriter(response_id, disk_cache()));
 }
 
-void ServiceWorkerStorage::StoreUncommittedReponseId(int64 id) {
+void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
+  DCHECK_EQ(INITIALIZED, state_);
+
+  if (!has_checked_for_stale_resources_)
+    DeleteStaleResources();
+
   database_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(base::IgnoreResult(
@@ -374,6 +569,31 @@ void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
   StartPurgingResources(std::vector<int64>(1, id));
 }
 
+void ServiceWorkerStorage::CompareScriptResources(
+    int64 lhs_id, int64 rhs_id,
+    const CompareCallback& callback) {
+  DCHECK(!callback.is_null());
+  scoped_refptr<ResponseComparer> comparer =
+      new ResponseComparer(weak_factory_.GetWeakPtr(),
+                           CreateResponseReader(lhs_id),
+                           CreateResponseReader(rhs_id),
+                           callback);
+  comparer->Start();  // It deletes itself when done.
+}
+
+void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
+  Disable();
+
+  // Delete the database on the database thread.
+  PostTaskAndReplyWithResult(
+      database_task_runner_,
+      FROM_HERE,
+      base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
+                 base::Unretained(database_.get())),
+      base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
+                 weak_factory_.GetWeakPtr(), callback));
+}
+
 int64 ServiceWorkerStorage::NewRegistrationId() {
   if (state_ == DISABLED)
     return kInvalidServiceWorkerRegistrationId;
@@ -397,6 +617,8 @@ int64 ServiceWorkerStorage::NewResourceId() {
 
 void ServiceWorkerStorage::NotifyInstallingRegistration(
       ServiceWorkerRegistration* registration) {
+  DCHECK(installing_registrations_.find(registration->id()) ==
+         installing_registrations_.end());
   installing_registrations_[registration->id()] = registration;
 }
 
@@ -419,21 +641,70 @@ void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
             &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
             base::Unretained(database_.get()),
             ids));
-
-    StartPurgingResources(resources);
   }
 }
 
+void ServiceWorkerStorage::NotifyUninstallingRegistration(
+    ServiceWorkerRegistration* registration) {
+  DCHECK(uninstalling_registrations_.find(registration->id()) ==
+         uninstalling_registrations_.end());
+  uninstalling_registrations_[registration->id()] = registration;
+}
+
+void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
+    ServiceWorkerRegistration* registration) {
+  uninstalling_registrations_.erase(registration->id());
+}
+
+void ServiceWorkerStorage::Disable() {
+  state_ = DISABLED;
+  if (disk_cache_)
+    disk_cache_->Disable();
+}
+
+bool ServiceWorkerStorage::IsDisabled() const {
+  return state_ == DISABLED;
+}
+
+void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
+  if (!has_checked_for_stale_resources_)
+    DeleteStaleResources();
+  StartPurgingResources(resources);
+}
+
+ServiceWorkerStorage::ServiceWorkerStorage(
+    const base::FilePath& path,
+    base::WeakPtr<ServiceWorkerContextCore> context,
+    base::SequencedTaskRunner* database_task_runner,
+    base::MessageLoopProxy* disk_cache_thread,
+    quota::QuotaManagerProxy* quota_manager_proxy)
+    : next_registration_id_(kInvalidServiceWorkerRegistrationId),
+      next_version_id_(kInvalidServiceWorkerVersionId),
+      next_resource_id_(kInvalidServiceWorkerResourceId),
+      state_(UNINITIALIZED),
+      path_(path),
+      context_(context),
+      database_task_runner_(database_task_runner),
+      disk_cache_thread_(disk_cache_thread),
+      quota_manager_proxy_(quota_manager_proxy),
+      is_purge_pending_(false),
+      has_checked_for_stale_resources_(false),
+      weak_factory_(this) {
+  database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
+}
+
 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
   if (path_.empty())
     return base::FilePath();
-  return path_.Append(kDatabaseName);
+  return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
+      .Append(kDatabaseName);
 }
 
 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
   if (path_.empty())
     return base::FilePath();
-  return path_.Append(kDiskCacheName);
+  return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
+      .Append(kDiskCacheName);
 }
 
 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
@@ -477,10 +748,10 @@ void ServiceWorkerStorage::DidReadInitialData(
     registered_origins_.swap(data->origins);
     state_ = INITIALIZED;
   } else {
-    // TODO(nhiroki): If status==STATUS_ERROR_CORRUPTED, do corruption recovery
-    // (http://crbug.com/371675).
-    DLOG(WARNING) << "Failed to initialize: " << status;
-    state_ = DISABLED;
+    // TODO(nhiroki): Stringify |status| using StatusToString() defined in
+    // service_worker_database.cc.
+    DVLOG(2) << "Failed to initialize: " << status;
+    ScheduleDeleteAndStartOver();
   }
 
   for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
@@ -497,7 +768,7 @@ void ServiceWorkerStorage::DidFindRegistrationForDocument(
     const ResourceList& resources,
     ServiceWorkerDatabase::Status status) {
   if (status == ServiceWorkerDatabase::STATUS_OK) {
-    callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
+    ReturnFoundRegistration(callback, data, resources);
     return;
   }
 
@@ -511,7 +782,7 @@ void ServiceWorkerStorage::DidFindRegistrationForDocument(
     return;
   }
 
-  // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+  ScheduleDeleteAndStartOver();
   callback.Run(DatabaseStatusToStatusCode(status),
                scoped_refptr<ServiceWorkerRegistration>());
 }
@@ -523,7 +794,7 @@ void ServiceWorkerStorage::DidFindRegistrationForPattern(
     const ResourceList& resources,
     ServiceWorkerDatabase::Status status) {
   if (status == ServiceWorkerDatabase::STATUS_OK) {
-    callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
+    ReturnFoundRegistration(callback, data, resources);
     return;
   }
 
@@ -536,7 +807,7 @@ void ServiceWorkerStorage::DidFindRegistrationForPattern(
     return;
   }
 
-  // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+  ScheduleDeleteAndStartOver();
   callback.Run(DatabaseStatusToStatusCode(status),
                scoped_refptr<ServiceWorkerRegistration>());
 }
@@ -547,22 +818,44 @@ void ServiceWorkerStorage::DidFindRegistrationForId(
     const ResourceList& resources,
     ServiceWorkerDatabase::Status status) {
   if (status == ServiceWorkerDatabase::STATUS_OK) {
-    callback.Run(SERVICE_WORKER_OK,
-                 GetOrCreateRegistration(data, resources));
+    ReturnFoundRegistration(callback, data, resources);
+    return;
+  }
+
+  if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
+    // TODO(nhiroki): Find a registration in |installing_registrations_|.
+    callback.Run(DatabaseStatusToStatusCode(status),
+                 scoped_refptr<ServiceWorkerRegistration>());
     return;
   }
-  // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+
+  ScheduleDeleteAndStartOver();
   callback.Run(DatabaseStatusToStatusCode(status),
                scoped_refptr<ServiceWorkerRegistration>());
 }
 
+void ServiceWorkerStorage::ReturnFoundRegistration(
+    const FindRegistrationCallback& callback,
+    const ServiceWorkerDatabase::RegistrationData& data,
+    const ResourceList& resources) {
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      GetOrCreateRegistration(data, resources);
+  if (registration->is_deleted()) {
+    // It's past the point of no return and no longer findable.
+    callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
+    return;
+  }
+  callback.Run(SERVICE_WORKER_OK, registration);
+}
+
 void ServiceWorkerStorage::DidGetAllRegistrations(
     const GetAllRegistrationInfosCallback& callback,
     RegistrationList* registrations,
     ServiceWorkerDatabase::Status status) {
   DCHECK(registrations);
-  if (status != ServiceWorkerDatabase::STATUS_OK) {
-    // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+  if (status != ServiceWorkerDatabase::STATUS_OK &&
+      status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
+    ScheduleDeleteAndStartOver();
     callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
     return;
   }
@@ -599,7 +892,7 @@ void ServiceWorkerStorage::DidGetAllRegistrations(
 
     if (it->is_active) {
       info.active_version.is_null = false;
-      info.active_version.status = ServiceWorkerVersion::ACTIVE;
+      info.active_version.status = ServiceWorkerVersion::ACTIVATED;
       info.active_version.version_id = it->version_id;
     } else {
       info.waiting_version.is_null = false;
@@ -623,40 +916,49 @@ void ServiceWorkerStorage::DidGetAllRegistrations(
 void ServiceWorkerStorage::DidStoreRegistration(
     const StatusCallback& callback,
     const GURL& origin,
+    int64 deleted_version_id,
     const std::vector<int64>& newly_purgeable_resources,
     ServiceWorkerDatabase::Status status) {
   if (status != ServiceWorkerDatabase::STATUS_OK) {
-    // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+    ScheduleDeleteAndStartOver();
     callback.Run(DatabaseStatusToStatusCode(status));
     return;
   }
   registered_origins_.insert(origin);
   callback.Run(SERVICE_WORKER_OK);
-  StartPurgingResources(newly_purgeable_resources);
+
+  if (!context_ || !context_->GetLiveVersion(deleted_version_id))
+    StartPurgingResources(newly_purgeable_resources);
 }
 
 void ServiceWorkerStorage::DidUpdateToActiveState(
     const StatusCallback& callback,
     ServiceWorkerDatabase::Status status) {
-  // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+  if (status != ServiceWorkerDatabase::STATUS_OK &&
+      status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
+    ScheduleDeleteAndStartOver();
+  }
   callback.Run(DatabaseStatusToStatusCode(status));
 }
 
 void ServiceWorkerStorage::DidDeleteRegistration(
-    const GURL& origin,
-    const StatusCallback& callback,
+    const DidDeleteRegistrationParams& params,
     bool origin_is_deletable,
+    int64 version_id,
     const std::vector<int64>& newly_purgeable_resources,
     ServiceWorkerDatabase::Status status) {
+  pending_deletions_.erase(params.registration_id);
   if (status != ServiceWorkerDatabase::STATUS_OK) {
-    // TODO(nhiroki): Handle database error (http://crbug.com/371675).
-    callback.Run(DatabaseStatusToStatusCode(status));
+    ScheduleDeleteAndStartOver();
+    params.callback.Run(DatabaseStatusToStatusCode(status));
     return;
   }
   if (origin_is_deletable)
-    registered_origins_.erase(origin);
-  callback.Run(SERVICE_WORKER_OK);
-  StartPurgingResources(newly_purgeable_resources);
+    registered_origins_.erase(params.origin);
+  params.callback.Run(SERVICE_WORKER_OK);
+
+  if (!context_ || !context_->GetLiveVersion(version_id))
+    StartPurgingResources(newly_purgeable_resources);
 }
 
 scoped_refptr<ServiceWorkerRegistration>
@@ -670,23 +972,27 @@ ServiceWorkerStorage::GetOrCreateRegistration(
 
   registration = new ServiceWorkerRegistration(
       data.scope, data.script, data.registration_id, context_);
+  registration->set_last_update_check(data.last_update_check);
+  if (pending_deletions_.find(data.registration_id) !=
+      pending_deletions_.end()) {
+    registration->set_is_deleted(true);
+  }
   scoped_refptr<ServiceWorkerVersion> version =
       context_->GetLiveVersion(data.version_id);
   if (!version) {
     version = new ServiceWorkerVersion(registration, data.version_id, context_);
     version->SetStatus(data.is_active ?
-        ServiceWorkerVersion::ACTIVE : ServiceWorkerVersion::INSTALLED);
+        ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
     version->script_cache_map()->SetResources(resources);
   }
 
-  if (version->status() == ServiceWorkerVersion::ACTIVE)
-    registration->set_active_version(version);
+  if (version->status() == ServiceWorkerVersion::ACTIVATED)
+    registration->SetActiveVersion(version);
   else if (version->status() == ServiceWorkerVersion::INSTALLED)
-    registration->set_waiting_version(version);
+    registration->SetWaitingVersion(version);
   else
     NOTREACHED();
-  // TODO(michaeln): Hmmm, what if DeleteReg was invoked after
-  // the Find result we're returning here? NOTREACHED condition?
+
   return registration;
 }
 
@@ -739,9 +1045,8 @@ ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
 
   base::FilePath path = GetDiskCachePath();
   if (path.empty()) {
-    int rv = disk_cache_->InitWithMemBackend(
-        kMaxMemDiskCacheSize,
-        base::Bind(&EmptyCompletionCallback));
+    int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
+                                             net::CompletionCallback());
     DCHECK_EQ(net::OK, rv);
     return disk_cache_.get();
   }
@@ -761,36 +1066,36 @@ void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
   if (rv != net::OK) {
     LOG(ERROR) << "Failed to open the serviceworker diskcache: "
                << net::ErrorToString(rv);
-    // TODO(michaeln): DeleteAndStartOver()
-    disk_cache_->Disable();
-    state_ = DISABLED;
+    ScheduleDeleteAndStartOver();
   }
-  ServiceWorkerHistograms::CountInitDiskCacheResult(rv == net::OK);
+  ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
 }
 
 void ServiceWorkerStorage::StartPurgingResources(
     const std::vector<int64>& ids) {
+  DCHECK(has_checked_for_stale_resources_);
   for (size_t i = 0; i < ids.size(); ++i)
-    purgeable_reource_ids_.push_back(ids[i]);
+    purgeable_resource_ids_.push_back(ids[i]);
   ContinuePurgingResources();
 }
 
 void ServiceWorkerStorage::StartPurgingResources(
     const ResourceList& resources) {
+  DCHECK(has_checked_for_stale_resources_);
   for (size_t i = 0; i < resources.size(); ++i)
-    purgeable_reource_ids_.push_back(resources[i].resource_id);
+    purgeable_resource_ids_.push_back(resources[i].resource_id);
   ContinuePurgingResources();
 }
 
 void ServiceWorkerStorage::ContinuePurgingResources() {
-  if (purgeable_reource_ids_.empty() || is_purge_pending_)
+  if (purgeable_resource_ids_.empty() || is_purge_pending_)
     return;
 
   // Do one at a time until we're done, use RunSoon to avoid recursion when
   // DoomEntry returns immediately.
   is_purge_pending_ = true;
-  int64 id = purgeable_reource_ids_.front();
-  purgeable_reource_ids_.pop_front();
+  int64 id = purgeable_resource_ids_.front();
+  purgeable_resource_ids_.pop_front();
   RunSoon(FROM_HERE,
           base::Bind(&ServiceWorkerStorage::PurgeResource,
                      weak_factory_.GetWeakPtr(), id));
@@ -819,6 +1124,58 @@ void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
   ContinuePurgingResources();
 }
 
+void ServiceWorkerStorage::DeleteStaleResources() {
+  DCHECK(!has_checked_for_stale_resources_);
+  has_checked_for_stale_resources_ = true;
+  database_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
+                 database_.get(),
+                 base::MessageLoopProxy::current(),
+                 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
+                            weak_factory_.GetWeakPtr())));
+}
+
+void ServiceWorkerStorage::DidCollectStaleResources(
+    const std::vector<int64>& stale_resource_ids,
+    ServiceWorkerDatabase::Status status) {
+  DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
+  if (status != ServiceWorkerDatabase::STATUS_OK)
+    return;
+  StartPurgingResources(stale_resource_ids);
+}
+
+void ServiceWorkerStorage::CollectStaleResourcesFromDB(
+    ServiceWorkerDatabase* database,
+    scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+    const GetResourcesCallback& callback) {
+  std::set<int64> ids;
+  ServiceWorkerDatabase::Status status =
+      database->GetUncommittedResourceIds(&ids);
+  if (status != ServiceWorkerDatabase::STATUS_OK) {
+    original_task_runner->PostTask(
+        FROM_HERE,
+        base::Bind(
+            callback, std::vector<int64>(ids.begin(), ids.end()), status));
+    return;
+  }
+
+  status = database->PurgeUncommittedResourceIds(ids);
+  if (status != ServiceWorkerDatabase::STATUS_OK) {
+    original_task_runner->PostTask(
+        FROM_HERE,
+        base::Bind(
+            callback, std::vector<int64>(ids.begin(), ids.end()), status));
+    return;
+  }
+
+  ids.clear();
+  status = database->GetPurgeableResourceIds(&ids);
+  original_task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
+}
+
 void ServiceWorkerStorage::ReadInitialDataFromDB(
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
@@ -850,13 +1207,17 @@ void ServiceWorkerStorage::DeleteRegistrationFromDB(
     const DeleteRegistrationCallback& callback) {
   DCHECK(database);
 
+  int64 version_id = kInvalidServiceWorkerVersionId;
   std::vector<int64> newly_purgeable_resources;
-  ServiceWorkerDatabase::Status status =
-      database->DeleteRegistration(registration_id, origin,
-                                   &newly_purgeable_resources);
+  ServiceWorkerDatabase::Status status = database->DeleteRegistration(
+      registration_id, origin, &version_id, &newly_purgeable_resources);
   if (status != ServiceWorkerDatabase::STATUS_OK) {
-    original_task_runner->PostTask(
-        FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
+    original_task_runner->PostTask(FROM_HERE,
+                                   base::Bind(callback,
+                                              false,
+                                              kInvalidServiceWorkerVersionId,
+                                              std::vector<int64>(),
+                                              status));
     return;
   }
 
@@ -865,15 +1226,20 @@ void ServiceWorkerStorage::DeleteRegistrationFromDB(
   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
   status = database->GetRegistrationsForOrigin(origin, &registrations);
   if (status != ServiceWorkerDatabase::STATUS_OK) {
-    original_task_runner->PostTask(
-        FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
+    original_task_runner->PostTask(FROM_HERE,
+                                   base::Bind(callback,
+                                              false,
+                                              kInvalidServiceWorkerVersionId,
+                                              std::vector<int64>(),
+                                              status));
     return;
   }
 
   bool deletable = registrations.empty();
   original_task_runner->PostTask(
-      FROM_HERE, base::Bind(callback, deletable,
-                            newly_purgeable_resources, status));
+      FROM_HERE,
+      base::Bind(
+          callback, deletable, version_id, newly_purgeable_resources, status));
 }
 
 void ServiceWorkerStorage::WriteRegistrationInDB(
@@ -883,13 +1249,16 @@ void ServiceWorkerStorage::WriteRegistrationInDB(
     const ResourceList& resources,
     const WriteRegistrationCallback& callback) {
   DCHECK(database);
+  int64 deleted_version_id = kInvalidServiceWorkerVersionId;
   std::vector<int64> newly_purgeable_resources;
-  ServiceWorkerDatabase::Status status =
-      database->WriteRegistration(data, resources, &newly_purgeable_resources);
-  original_task_runner->PostTask(
-      FROM_HERE,
-      base::Bind(callback, data.script.GetOrigin(),
-                 newly_purgeable_resources, status));
+  ServiceWorkerDatabase::Status status = database->WriteRegistration(
+      data, resources, &deleted_version_id, &newly_purgeable_resources);
+  original_task_runner->PostTask(FROM_HERE,
+                                 base::Bind(callback,
+                                            data.script.GetOrigin(),
+                                            deleted_version_id,
+                                            newly_purgeable_resources,
+                                            status));
 }
 
 void ServiceWorkerStorage::FindForDocumentInDB(
@@ -982,4 +1351,56 @@ void ServiceWorkerStorage::FindForIdInDB(
       FROM_HERE, base::Bind(callback, data, resources, status));
 }
 
+// TODO(nhiroki): The corruption recovery should not be scheduled if the error
+// is transient and it can get healed soon (e.g. IO error). To do that, the
+// database should not disable itself when an error occurs and the storage
+// controls it instead.
+void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
+  if (state_ == DISABLED) {
+    // Recovery process has already been scheduled.
+    return;
+  }
+  Disable();
+
+  DVLOG(1) << "Schedule to delete the context and start over.";
+  context_->ScheduleDeleteAndStartOver();
+}
+
+void ServiceWorkerStorage::DidDeleteDatabase(
+    const StatusCallback& callback,
+    ServiceWorkerDatabase::Status status) {
+  DCHECK_EQ(DISABLED, state_);
+  if (status != ServiceWorkerDatabase::STATUS_OK) {
+    // Give up the corruption recovery until the browser restarts.
+    LOG(ERROR) << "Failed to delete the database: " << status;
+    callback.Run(DatabaseStatusToStatusCode(status));
+    return;
+  }
+  DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
+
+  // Delete the disk cache on the cache thread.
+  // TODO(nhiroki): What if there is a bunch of files in the cache directory?
+  // Deleting the directory could take a long time and restart could be delayed.
+  // We should probably rename the directory and delete it later.
+  PostTaskAndReplyWithResult(
+      database_task_runner_,
+      FROM_HERE,
+      base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
+      base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
+                 weak_factory_.GetWeakPtr(), callback));
+}
+
+void ServiceWorkerStorage::DidDeleteDiskCache(
+    const StatusCallback& callback, bool result) {
+  DCHECK_EQ(DISABLED, state_);
+  if (!result) {
+    // Give up the corruption recovery until the browser restarts.
+    LOG(ERROR) << "Failed to delete the diskcache.";
+    callback.Run(SERVICE_WORKER_ERROR_FAILED);
+    return;
+  }
+  DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
+  callback.Run(SERVICE_WORKER_OK);
+}
+
 }  // namespace content