#include "content/browser/service_worker/service_worker_storage.h"
#include <string>
+
+#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
-#include "base/strings/string_util.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_info.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/net_errors.h"
#include "webkit/browser/quota/quota_manager_proxy.h"
namespace content {
namespace {
-void RunSoon(const base::Closure& closure) {
- base::MessageLoop::current()->PostTask(FROM_HERE, closure);
+typedef base::Callback<void(
+ ServiceWorkerStorage::InitialData* data,
+ bool success)> InitializeCallback;
+typedef base::Callback<void(
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const std::vector<ServiceWorkerDatabase::ResourceRecord>& resources,
+ ServiceWorkerStatusCode status)> ReadRegistrationCallback;
+typedef base::Callback<void(
+ bool origin_is_deletable,
+ ServiceWorkerStatusCode status)> DeleteRegistrationCallback;
+
+void RunSoon(const tracked_objects::Location& from_here,
+ const base::Closure& closure) {
+ base::MessageLoop::current()->PostTask(from_here, closure);
+}
+
+void CompleteFindNow(
+ const scoped_refptr<ServiceWorkerRegistration>& registration,
+ ServiceWorkerStatusCode status,
+ const ServiceWorkerStorage::FindRegistrationCallback& callback) {
+ callback.Run(status, registration);
+}
+
+void CompleteFindSoon(
+ const tracked_objects::Location& from_here,
+ const scoped_refptr<ServiceWorkerRegistration>& registration,
+ ServiceWorkerStatusCode status,
+ const ServiceWorkerStorage::FindRegistrationCallback& callback) {
+ RunSoon(from_here, base::Bind(callback, status, registration));
}
const base::FilePath::CharType kServiceWorkerDirectory[] =
- FILE_PATH_LITERAL("ServiceWorker");
+ FILE_PATH_LITERAL("Service Worker");
+const base::FilePath::CharType kDatabaseName[] =
+ FILE_PATH_LITERAL("Database");
+
+const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
+
+void EmptyCompletionCallback(int) {}
+
+void ReadInitialDataFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const InitializeCallback& callback) {
+ DCHECK(database);
+ ServiceWorkerStorage::InitialData* data =
+ new ServiceWorkerStorage::InitialData();
+ bool success =
+ database->GetNextAvailableIds(&data->next_registration_id,
+ &data->next_version_id,
+ &data->next_resource_id) &&
+ database->GetOriginsWithRegistrations(&data->origins);
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, base::Owned(data), success));
+}
+
+void ReadRegistrationFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const ReadRegistrationCallback& callback) {
+ DCHECK(database);
+ ServiceWorkerDatabase::RegistrationData data;
+ std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
+
+ // TODO(nhiroki): The database should return more detailed status like
+ // ServiceWorkerStatusCode instead of bool value.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
+ if (!database->ReadRegistration(registration_id, origin, &data, &resources)) {
+ status = database->is_disabled() ? SERVICE_WORKER_ERROR_FAILED
+ : SERVICE_WORKER_ERROR_NOT_FOUND;
+ }
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, data, resources, status));
+}
+
+void DeleteRegistrationFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const DeleteRegistrationCallback& callback) {
+ DCHECK(database);
+ if (!database->DeleteRegistration(registration_id, origin)) {
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, false, SERVICE_WORKER_ERROR_FAILED));
+ return;
+ }
+
+ // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
+ // unique origin list.
+ std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
+ if (!database->GetRegistrationsForOrigin(origin, ®istrations)) {
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, false, SERVICE_WORKER_ERROR_FAILED));
+ return;
+ }
+
+ bool deletable = registrations.empty();
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, deletable, SERVICE_WORKER_OK));
+}
+
+void UpdateToActiveStateInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const ServiceWorkerStorage::StatusCallback& callback) {
+ DCHECK(database);
+
+ // TODO(nhiroki): The database should return more detailed status like
+ // ServiceWorkerStatusCode instead of bool value.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
+ if (!database->UpdateVersionToActive(registration_id, origin)) {
+ status = database->is_disabled() ? SERVICE_WORKER_ERROR_FAILED
+ : SERVICE_WORKER_ERROR_NOT_FOUND;
+ }
+ original_task_runner->PostTask(FROM_HERE, base::Bind(callback, status));
+}
} // namespace
+ServiceWorkerStorage::InitialData::InitialData()
+ : next_registration_id(kInvalidServiceWorkerRegistrationId),
+ next_version_id(kInvalidServiceWorkerVersionId),
+ next_resource_id(kInvalidServiceWorkerResourceId) {
+}
+
+ServiceWorkerStorage::InitialData::~InitialData() {
+}
+
ServiceWorkerStorage::ServiceWorkerStorage(
const base::FilePath& path,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::SequencedTaskRunner* database_task_runner,
quota::QuotaManagerProxy* quota_manager_proxy)
- : last_registration_id_(0), // TODO(kinuko): this should be read from disk.
- last_version_id_(0), // TODO(kinuko): this should be read from disk.
- quota_manager_proxy_(quota_manager_proxy) {
- if (!path.empty())
+ : next_registration_id_(kInvalidServiceWorkerRegistrationId),
+ next_version_id_(kInvalidServiceWorkerVersionId),
+ next_resource_id_(kInvalidServiceWorkerResourceId),
+ state_(UNINITIALIZED),
+ context_(context),
+ database_task_runner_(database_task_runner),
+ quota_manager_proxy_(quota_manager_proxy),
+ weak_factory_(this) {
+ if (!path.empty()) {
path_ = path.Append(kServiceWorkerDirectory);
+ database_.reset(new ServiceWorkerDatabase(path_.Append(kDatabaseName)));
+ } else {
+ // Create an in-memory database.
+ database_.reset(new ServiceWorkerDatabase(base::FilePath()));
+ }
}
ServiceWorkerStorage::~ServiceWorkerStorage() {
- for (PatternToRegistrationMap::const_iterator iter =
- registration_by_pattern_.begin();
- iter != registration_by_pattern_.end();
- ++iter) {
- iter->second->Shutdown();
- }
- registration_by_pattern_.clear();
+ weak_factory_.InvalidateWeakPtrs();
+ database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
}
void ServiceWorkerStorage::FindRegistrationForPattern(
- const GURL& pattern,
+ const GURL& scope,
const FindRegistrationCallback& callback) {
- ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_NOT_FOUND;
- scoped_refptr<ServiceWorkerRegistration> found;
- PatternToRegistrationMap::const_iterator match =
- registration_by_pattern_.find(pattern);
- if (match != registration_by_pattern_.end()) {
- status = SERVICE_WORKER_OK;
- found = match->second;
+ scoped_refptr<ServiceWorkerRegistration> null_registration;
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::FindRegistrationForPattern,
+ weak_factory_.GetWeakPtr(), scope, callback))) {
+ if (state_ != INITIALIZING || !context_) {
+ CompleteFindSoon(FROM_HERE, null_registration,
+ SERVICE_WORKER_ERROR_FAILED, callback);
+ }
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ // See if there are any stored registrations for the origin.
+ if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForPattern(scope);
+ if (installing_registration) {
+ CompleteFindSoon(
+ FROM_HERE, installing_registration, SERVICE_WORKER_OK, callback);
+ return;
+ }
+ CompleteFindSoon(
+ FROM_HERE, null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, callback);
+ return;
}
- // Always simulate asynchronous call for now.
- RunSoon(base::Bind(callback, status, found));
+
+ RegistrationList* registrations = new RegistrationList();
+ PostTaskAndReplyWithResult(
+ database_task_runner_,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerDatabase::GetRegistrationsForOrigin,
+ base::Unretained(database_.get()),
+ scope.GetOrigin(), base::Unretained(registrations)),
+ base::Bind(&ServiceWorkerStorage::DidGetRegistrationsForPattern,
+ weak_factory_.GetWeakPtr(), scope, callback,
+ base::Owned(registrations)));
}
void ServiceWorkerStorage::FindRegistrationForDocument(
const GURL& document_url,
const FindRegistrationCallback& callback) {
- // TODO(alecflett): This needs to be synchronous in the fast path,
- // but asynchronous in the slow path (when the patterns have to be
- // loaded from disk). For now it is always pessimistically async.
- ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_NOT_FOUND;
- scoped_refptr<ServiceWorkerRegistration> found;
- for (PatternToRegistrationMap::const_iterator it =
- registration_by_pattern_.begin();
- it != registration_by_pattern_.end();
- ++it) {
- if (PatternMatches(it->first, document_url)) {
- status = SERVICE_WORKER_OK;
- found = it->second;
- break;
- }
+ scoped_refptr<ServiceWorkerRegistration> null_registration;
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::FindRegistrationForDocument,
+ weak_factory_.GetWeakPtr(), document_url, callback))) {
+ if (state_ != INITIALIZING || !context_)
+ CompleteFindNow(null_registration, SERVICE_WORKER_ERROR_FAILED, callback);
+ return;
}
- // Always simulate asynchronous call for now.
- RunSoon(base::Bind(callback, status, found));
-}
+ DCHECK_EQ(INITIALIZED, state_);
-void ServiceWorkerStorage::GetAllRegistrations(
- const GetAllRegistrationInfosCallback& callback) {
- std::vector<ServiceWorkerRegistrationInfo> registrations;
- for (PatternToRegistrationMap::const_iterator it =
- registration_by_pattern_.begin();
- it != registration_by_pattern_.end();
- ++it) {
- ServiceWorkerRegistration* registration(it->second.get());
- registrations.push_back(registration->GetInfo());
+ // See if there are any stored registrations for the origin.
+ if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForDocument(document_url);
+ if (installing_registration) {
+ CompleteFindNow(installing_registration, SERVICE_WORKER_OK, callback);
+ return;
+ }
+ CompleteFindNow(
+ null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, callback);
+ return;
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE, base::Bind(callback, registrations));
+ RegistrationList* registrations = new RegistrationList();
+ PostTaskAndReplyWithResult(
+ database_task_runner_,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerDatabase::GetRegistrationsForOrigin,
+ base::Unretained(database_.get()),
+ document_url.GetOrigin(), base::Unretained(registrations)),
+ base::Bind(&ServiceWorkerStorage::DidGetRegistrationsForDocument,
+ weak_factory_.GetWeakPtr(), document_url, callback,
+ base::Owned(registrations)));
}
void ServiceWorkerStorage::FindRegistrationForId(
int64 registration_id,
+ const GURL& origin,
const FindRegistrationCallback& callback) {
- ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_NOT_FOUND;
- scoped_refptr<ServiceWorkerRegistration> found;
- for (PatternToRegistrationMap::const_iterator it =
- registration_by_pattern_.begin();
- it != registration_by_pattern_.end();
- ++it) {
- if (registration_id == it->second->id()) {
- status = SERVICE_WORKER_OK;
- found = it->second;
- break;
+ scoped_refptr<ServiceWorkerRegistration> null_registration;
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::FindRegistrationForId,
+ weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
+ if (state_ != INITIALIZING || !context_)
+ CompleteFindNow(null_registration, SERVICE_WORKER_ERROR_FAILED, callback);
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ // See if there are any stored registrations for the origin.
+ if (!ContainsKey(registered_origins_, origin)) {
+ // Look for somthing currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForId(registration_id);
+ if (installing_registration) {
+ CompleteFindNow(installing_registration, SERVICE_WORKER_OK, callback);
+ return;
}
+ CompleteFindNow(
+ null_registration, SERVICE_WORKER_ERROR_NOT_FOUND, callback);
+ return;
}
- RunSoon(base::Bind(callback, status, found));
+
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ context_->GetLiveRegistration(registration_id);
+ if (registration) {
+ CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
+ return;
+ }
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReadRegistrationFromDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ registration_id, origin,
+ base::Bind(&ServiceWorkerStorage::DidReadRegistrationForId,
+ weak_factory_.GetWeakPtr(), callback)));
+}
+
+void ServiceWorkerStorage::GetAllRegistrations(
+ const GetAllRegistrationInfosCallback& callback) {
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::GetAllRegistrations,
+ weak_factory_.GetWeakPtr(), callback))) {
+ if (state_ != INITIALIZING || !context_) {
+ RunSoon(FROM_HERE, base::Bind(
+ callback, std::vector<ServiceWorkerRegistrationInfo>()));
+ }
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ RegistrationList* registrations = new RegistrationList;
+ PostTaskAndReplyWithResult(
+ database_task_runner_,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
+ base::Unretained(database_.get()),
+ base::Unretained(registrations)),
+ base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ base::Owned(registrations)));
}
void ServiceWorkerStorage::StoreRegistration(
ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version,
const StatusCallback& callback) {
DCHECK(registration);
+ DCHECK(version);
- PatternToRegistrationMap::const_iterator current(
- registration_by_pattern_.find(registration->pattern()));
- if (current != registration_by_pattern_.end() &&
- current->second->script_url() != registration->script_url()) {
- RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_EXISTS));
+ DCHECK(state_ == INITIALIZED || state_ == DISABLED);
+ if (state_ != INITIALIZED || !context_) {
+ RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
return;
}
- // This may update the existing registration information.
- registration_by_pattern_[registration->pattern()] = registration;
+ ServiceWorkerDatabase::RegistrationData data;
+ data.registration_id = registration->id();
+ data.scope = registration->pattern();
+ 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
+
+ ResourceList resources;
+ PostTaskAndReplyWithResult(
+ database_task_runner_,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerDatabase::WriteRegistration,
+ base::Unretained(database_.get()), data, resources),
+ base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
+ weak_factory_.GetWeakPtr(),
+ registration->script_url().GetOrigin(),
+ callback));
+}
+
+void ServiceWorkerStorage::UpdateToActiveState(
+ ServiceWorkerRegistration* registration,
+ const StatusCallback& callback) {
+ DCHECK(registration);
+
+ DCHECK(state_ == INITIALIZED || state_ == DISABLED);
+ if (state_ != INITIALIZED || !context_) {
+ RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
+ return;
+ }
- RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UpdateToActiveStateInDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ registration->id(),
+ registration->script_url().GetOrigin(),
+ callback));
}
void ServiceWorkerStorage::DeleteRegistration(
- const GURL& pattern,
+ int64 registration_id,
+ const GURL& origin,
const StatusCallback& callback) {
- PatternToRegistrationMap::iterator match =
- registration_by_pattern_.find(pattern);
- if (match == registration_by_pattern_.end()) {
- RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_NOT_FOUND));
+ DCHECK(state_ == INITIALIZED || state_ == DISABLED);
+ if (state_ != INITIALIZED || !context_) {
+ RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
return;
}
- registration_by_pattern_.erase(match);
- RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DeleteRegistrationFromDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ registration_id, origin,
+ base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
+ weak_factory_.GetWeakPtr(), origin, callback)));
+
+ // 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.
+}
+
+scoped_ptr<ServiceWorkerResponseReader>
+ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
+ return make_scoped_ptr(
+ new ServiceWorkerResponseReader(response_id, disk_cache()));
+}
+
+scoped_ptr<ServiceWorkerResponseWriter>
+ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
+ return make_scoped_ptr(
+ new ServiceWorkerResponseWriter(response_id, disk_cache()));
}
int64 ServiceWorkerStorage::NewRegistrationId() {
- return ++last_registration_id_;
+ if (state_ == DISABLED)
+ return kInvalidServiceWorkerRegistrationId;
+ DCHECK_EQ(INITIALIZED, state_);
+ return next_registration_id_++;
}
int64 ServiceWorkerStorage::NewVersionId() {
- return ++last_version_id_;
-}
-
-bool ServiceWorkerStorage::PatternMatches(const GURL& pattern,
- const GURL& url) {
- // This is a really basic, naive
- // TODO(alecflett): Formalize what pattern matches mean.
- // Temporarily borrowed directly from appcache::Namespace::IsMatch().
- // We have to escape '?' characters since MatchPattern also treats those
- // as wildcards which we don't want here, we only do '*'s.
- std::string pattern_spec(pattern.spec());
- if (pattern.has_query())
- ReplaceSubstringsAfterOffset(&pattern_spec, 0, "?", "\\?");
- return MatchPattern(url.spec(), pattern_spec);
+ if (state_ == DISABLED)
+ return kInvalidServiceWorkerVersionId;
+ DCHECK_EQ(INITIALIZED, state_);
+ return next_version_id_++;
+}
+
+int64 ServiceWorkerStorage::NewResourceId() {
+ if (state_ == DISABLED)
+ return kInvalidServiceWorkerResourceId;
+ DCHECK_EQ(INITIALIZED, state_);
+ return next_resource_id_++;
+}
+
+void ServiceWorkerStorage::NotifyInstallingRegistration(
+ ServiceWorkerRegistration* registration) {
+ installing_registrations_[registration->id()] = registration;
+}
+
+void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
+ ServiceWorkerRegistration* registration) {
+ installing_registrations_.erase(registration->id());
+}
+
+bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
+ if (!context_)
+ return false;
+
+ switch (state_) {
+ case INITIALIZED:
+ return true;
+ case DISABLED:
+ return false;
+ case INITIALIZING:
+ pending_tasks_.push_back(callback);
+ return false;
+ case UNINITIALIZED:
+ pending_tasks_.push_back(callback);
+ // Fall-through.
+ }
+
+ state_ = INITIALIZING;
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReadInitialDataFromDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ base::Bind(&ServiceWorkerStorage::DidReadInitialData,
+ weak_factory_.GetWeakPtr())));
+ return false;
+}
+
+void ServiceWorkerStorage::DidReadInitialData(
+ InitialData* data,
+ bool success) {
+ DCHECK(data);
+ DCHECK_EQ(INITIALIZING, state_);
+
+ if (success) {
+ next_registration_id_ = data->next_registration_id;
+ next_version_id_ = data->next_version_id;
+ next_resource_id_ = data->next_resource_id;
+ registered_origins_.swap(data->origins);
+ state_ = INITIALIZED;
+ } else {
+ DLOG(WARNING) << "Failed to initialize.";
+ state_ = DISABLED;
+ }
+
+ for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
+ it != pending_tasks_.end(); ++it) {
+ RunSoon(FROM_HERE, *it);
+ }
+ pending_tasks_.clear();
+}
+
+void ServiceWorkerStorage::DidGetRegistrationsForPattern(
+ const GURL& scope,
+ const FindRegistrationCallback& callback,
+ RegistrationList* registrations,
+ bool success) {
+ DCHECK(registrations);
+ if (!success) {
+ callback.Run(SERVICE_WORKER_ERROR_FAILED,
+ scoped_refptr<ServiceWorkerRegistration>());
+ return;
+ }
+
+ // Find one with a matching scope.
+ for (RegistrationList::const_iterator it = registrations->begin();
+ it != registrations->end(); ++it) {
+ if (scope == it->scope) {
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ context_->GetLiveRegistration(it->registration_id);
+ if (!registration)
+ registration = CreateRegistration(*it);
+ callback.Run(SERVICE_WORKER_OK, registration);
+ return;
+ }
+ }
+
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForPattern(scope);
+ if (installing_registration) {
+ callback.Run(SERVICE_WORKER_OK, installing_registration);
+ return;
+ }
+
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND,
+ scoped_refptr<ServiceWorkerRegistration>());
+}
+
+void ServiceWorkerStorage::DidGetRegistrationsForDocument(
+ const GURL& document_url,
+ const FindRegistrationCallback& callback,
+ RegistrationList* registrations,
+ bool success) {
+ DCHECK(registrations);
+ if (!success) {
+ callback.Run(SERVICE_WORKER_ERROR_FAILED,
+ scoped_refptr<ServiceWorkerRegistration>());
+ return;
+ }
+
+ // Find one with a pattern match.
+ for (RegistrationList::const_iterator it = registrations->begin();
+ it != registrations->end(); ++it) {
+ // TODO(michaeln): if there are multiple matches the one with
+ // the longest scope should win.
+ if (ServiceWorkerUtils::ScopeMatches(it->scope, document_url)) {
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ context_->GetLiveRegistration(it->registration_id);
+ if (registration) {
+ callback.Run(SERVICE_WORKER_OK, registration);
+ return;
+ }
+ callback.Run(SERVICE_WORKER_OK, CreateRegistration(*it));
+ return;
+ }
+ }
+
+ // Look for something currently being installed.
+ // TODO(michaeln): Should be mixed in with the stored registrations
+ // for this test.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForDocument(document_url);
+ if (installing_registration) {
+ callback.Run(SERVICE_WORKER_OK, installing_registration);
+ return;
+ }
+
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND,
+ scoped_refptr<ServiceWorkerRegistration>());
+}
+
+void ServiceWorkerStorage::DidReadRegistrationForId(
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& registration,
+ const ResourceList& resources,
+ ServiceWorkerStatusCode status) {
+ if (status == SERVICE_WORKER_OK) {
+ callback.Run(status, CreateRegistration(registration));
+ return;
+ }
+
+ if (status == SERVICE_WORKER_ERROR_NOT_FOUND) {
+ // Look for somthing currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForId(registration.registration_id);
+ if (installing_registration) {
+ callback.Run(SERVICE_WORKER_OK, installing_registration);
+ return;
+ }
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND,
+ scoped_refptr<ServiceWorkerRegistration>());
+ return;
+ }
+
+ callback.Run(status, scoped_refptr<ServiceWorkerRegistration>());
+ return;
+}
+
+void ServiceWorkerStorage::DidGetAllRegistrations(
+ const GetAllRegistrationInfosCallback& callback,
+ RegistrationList* registrations,
+ bool success) {
+ DCHECK(registrations);
+ if (!success) {
+ callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
+ return;
+ }
+
+ // Add all stored registrations.
+ std::set<int64> pushed_registrations;
+ std::vector<ServiceWorkerRegistrationInfo> infos;
+ for (RegistrationList::const_iterator it = registrations->begin();
+ it != registrations->end(); ++it) {
+ DCHECK(pushed_registrations.insert(it->registration_id).second);
+ ServiceWorkerRegistration* registration =
+ context_->GetLiveRegistration(it->registration_id);
+ if (registration) {
+ infos.push_back(registration->GetInfo());
+ continue;
+ }
+ ServiceWorkerRegistrationInfo info;
+ info.pattern = it->scope;
+ info.script_url = it->script;
+ info.active_version.is_null = false;
+ if (it->is_active)
+ info.active_version.status = ServiceWorkerVersion::ACTIVE;
+ else
+ info.active_version.status = ServiceWorkerVersion::INSTALLED;
+ info.active_version.version_id = it->version_id;
+ infos.push_back(info);
+ }
+
+ // Add unstored registrations that are being installed.
+ for (RegistrationRefsById::const_iterator it =
+ installing_registrations_.begin();
+ it != installing_registrations_.end(); ++it) {
+ if (pushed_registrations.insert(it->first).second)
+ infos.push_back(it->second->GetInfo());
+ }
+
+ callback.Run(infos);
+}
+
+void ServiceWorkerStorage::DidStoreRegistration(
+ const GURL& origin,
+ const StatusCallback& callback,
+ bool success) {
+ if (!success) {
+ callback.Run(SERVICE_WORKER_ERROR_FAILED);
+ return;
+ }
+ registered_origins_.insert(origin);
+ callback.Run(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerStorage::DidDeleteRegistration(
+ const GURL& origin,
+ const StatusCallback& callback,
+ bool origin_is_deletable,
+ ServiceWorkerStatusCode status) {
+ if (origin_is_deletable)
+ registered_origins_.erase(origin);
+ callback.Run(status);
+}
+
+scoped_refptr<ServiceWorkerRegistration>
+ServiceWorkerStorage::CreateRegistration(
+ const ServiceWorkerDatabase::RegistrationData& data) {
+ scoped_refptr<ServiceWorkerRegistration> registration(
+ new ServiceWorkerRegistration(
+ data.scope, data.script, data.registration_id, context_));
+
+ scoped_refptr<ServiceWorkerVersion> version =
+ context_->GetLiveVersion(data.version_id);
+ if (!version) {
+ version = new ServiceWorkerVersion(registration, data.version_id, context_);
+ version->SetStatus(data.GetVersionStatus());
+ }
+
+ if (version->status() == ServiceWorkerVersion::ACTIVE)
+ registration->set_active_version(version);
+ else if (version->status() == ServiceWorkerVersion::INSTALLED)
+ registration->set_pending_version(version);
+ else
+ NOTREACHED();
+ // TODO(michaeln): Hmmm, what if DeleteReg was invoked after
+ // the Find result we're returning here? NOTREACHED condition?
+
+ return registration;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerStorage::FindInstallingRegistrationForDocument(
+ const GURL& document_url) {
+ // TODO(michaeln): if there are multiple matches the one with
+ // the longest scope should win, and these should on equal footing
+ // with the stored registrations in FindRegistrationForDocument().
+ for (RegistrationRefsById::const_iterator it =
+ installing_registrations_.begin();
+ it != installing_registrations_.end(); ++it) {
+ if (ServiceWorkerUtils::ScopeMatches(
+ it->second->pattern(), document_url)) {
+ return it->second;
+ }
+ }
+ return NULL;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerStorage::FindInstallingRegistrationForPattern(
+ const GURL& scope) {
+ for (RegistrationRefsById::const_iterator it =
+ installing_registrations_.begin();
+ it != installing_registrations_.end(); ++it) {
+ if (it->second->pattern() == scope)
+ return it->second;
+ }
+ return NULL;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerStorage::FindInstallingRegistrationForId(
+ int64 registration_id) {
+ RegistrationRefsById::const_iterator found =
+ installing_registrations_.find(registration_id);
+ if (found == installing_registrations_.end())
+ return NULL;
+ return found->second;
+}
+
+ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
+ if (disk_cache_)
+ return disk_cache_.get();
+
+ // TODO(michaeln): Store data on disk and do error checking.
+ disk_cache_.reset(new ServiceWorkerDiskCache);
+ int rv = disk_cache_->InitWithMemBackend(
+ kMaxMemDiskCacheSize,
+ base::Bind(&EmptyCompletionCallback));
+ DCHECK_EQ(net::OK, rv);
+ return disk_cache_.get();
}
} // namespace content