X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fcontent%2Fbrowser%2Fservice_worker%2Fservice_worker_storage.cc;h=1a3dd36b0d43bf866cdec54f92a81d2d3cc20444;hb=004985e17e624662a4c85c76a7654039dc83f028;hp=5be0fbbc771af7de1a92d3772948e5804d830b77;hpb=2f108dbacb161091e42a3479f4e171339b7e7623;p=platform%2Fframework%2Fweb%2Fcrosswalk.git diff --git a/src/content/browser/service_worker/service_worker_storage.cc b/src/content/browser/service_worker/service_worker_storage.cc index 5be0fbb..1a3dd36 100644 --- a/src/content/browser/service_worker/service_worker_storage.cc +++ b/src/content/browser/service_worker/service_worker_storage.cc @@ -5,167 +5,763 @@ #include "content/browser/service_worker/service_worker_storage.h" #include + +#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 InitializeCallback; +typedef base::Callback& resources, + ServiceWorkerStatusCode status)> ReadRegistrationCallback; +typedef base::Callback 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& registration, + ServiceWorkerStatusCode status, + const ServiceWorkerStorage::FindRegistrationCallback& callback) { + callback.Run(status, registration); +} + +void CompleteFindSoon( + const tracked_objects::Location& from_here, + const scoped_refptr& 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 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 original_task_runner, + int64 registration_id, + const GURL& origin, + const ReadRegistrationCallback& callback) { + DCHECK(database); + ServiceWorkerDatabase::RegistrationData data; + std::vector 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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())); + } + 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 +ServiceWorkerStorage::CreateResponseReader(int64 response_id) { + return make_scoped_ptr( + new ServiceWorkerResponseReader(response_id, disk_cache())); +} + +scoped_ptr +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::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()); + return; + } + + // Find one with a matching scope. + for (RegistrationList::const_iterator it = registrations->begin(); + it != registrations->end(); ++it) { + if (scope == it->scope) { + scoped_refptr 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 installing_registration = + FindInstallingRegistrationForPattern(scope); + if (installing_registration) { + callback.Run(SERVICE_WORKER_OK, installing_registration); + return; + } + + callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, + scoped_refptr()); +} + +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()); + 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 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 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()); +} + +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 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()); + return; + } + + callback.Run(status, scoped_refptr()); + return; +} + +void ServiceWorkerStorage::DidGetAllRegistrations( + const GetAllRegistrationInfosCallback& callback, + RegistrationList* registrations, + bool success) { + DCHECK(registrations); + if (!success) { + callback.Run(std::vector()); + return; + } + + // Add all stored registrations. + std::set pushed_registrations; + std::vector 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 +ServiceWorkerStorage::CreateRegistration( + const ServiceWorkerDatabase::RegistrationData& data) { + scoped_refptr registration( + new ServiceWorkerRegistration( + data.scope, data.script, data.registration_id, context_)); + + scoped_refptr 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