#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"
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[] =
const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
const int kMaxDiskCacheSize = 250 * 1024 * 1024;
-void EmptyCompletionCallback(int) {}
-
ServiceWorkerStatusCode DatabaseStatusToStatusCode(
ServiceWorkerDatabase::Status status) {
switch (status) {
}
}
+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()
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(
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,
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;
}
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,
base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
weak_factory_.GetWeakPtr(),
callback)));
+
+ registration->set_is_deleted(false);
}
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;
}
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,
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>
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(
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;
void ServiceWorkerStorage::NotifyInstallingRegistration(
ServiceWorkerRegistration* registration) {
+ DCHECK(installing_registrations_.find(registration->id()) ==
+ installing_registrations_.end());
installing_registrations_[registration->id()] = registration;
}
&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) {
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();
const ResourceList& resources,
ServiceWorkerDatabase::Status status) {
if (status == ServiceWorkerDatabase::STATUS_OK) {
- callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
+ ReturnFoundRegistration(callback, data, resources);
return;
}
return;
}
- // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ ScheduleDeleteAndStartOver();
callback.Run(DatabaseStatusToStatusCode(status),
scoped_refptr<ServiceWorkerRegistration>());
}
const ResourceList& resources,
ServiceWorkerDatabase::Status status) {
if (status == ServiceWorkerDatabase::STATUS_OK) {
- callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
+ ReturnFoundRegistration(callback, data, resources);
return;
}
return;
}
- // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ ScheduleDeleteAndStartOver();
callback.Run(DatabaseStatusToStatusCode(status),
scoped_refptr<ServiceWorkerRegistration>());
}
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;
}
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;
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>
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;
}
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();
}
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));
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,
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;
}
std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
status = database->GetRegistrationsForOrigin(origin, ®istrations);
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(
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(
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