#include <vector>
+#include "base/message_loop/message_loop.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_job_coordinator.h"
#include "content/browser/service_worker/service_worker_registration.h"
-#include "content/public/browser/browser_thread.h"
-#include "url/gurl.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "net/base/net_errors.h"
namespace content {
+namespace {
+
+void RunSoon(const base::Closure& closure) {
+ base::MessageLoop::current()->PostTask(FROM_HERE, closure);
+}
+
+} // namespace
+
+typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
+
ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
- ServiceWorkerStorage* storage,
- EmbeddedWorkerRegistry* worker_registry,
- ServiceWorkerJobCoordinator* coordinator,
+ base::WeakPtr<ServiceWorkerContextCore> context,
const GURL& pattern,
- const GURL& script_url,
- RegistrationType type)
- : storage_(storage),
- worker_registry_(worker_registry),
- coordinator_(coordinator),
- pending_version_(NULL),
+ const GURL& script_url)
+ : context_(context),
+ job_type_(REGISTRATION_JOB),
pattern_(pattern),
script_url_(script_url),
- type_(type),
+ phase_(INITIAL),
+ is_promise_resolved_(false),
+ promise_resolved_status_(SERVICE_WORKER_OK),
weak_factory_(this) {}
-ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {}
+ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerRegistration* registration)
+ : context_(context),
+ job_type_(UPDATE_JOB),
+ pattern_(registration->pattern()),
+ script_url_(registration->GetNewestVersion()->script_url()),
+ phase_(INITIAL),
+ is_promise_resolved_(false),
+ promise_resolved_status_(SERVICE_WORKER_OK),
+ weak_factory_(this) {
+ internal_.registration = registration;
+}
-void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback,
- int process_id) {
- // if we've created a pending version, associate source_provider it with
- // that, otherwise queue it up
- callbacks_.push_back(callback);
- DCHECK(process_id != -1);
- if (pending_version_) {
- pending_version_->AddProcessToWorker(process_id);
- } else {
- pending_process_ids_.push_back(process_id);
+ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
+ DCHECK(!context_ ||
+ phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
+ << "Jobs should only be interrupted during shutdown.";
+}
+
+void ServiceWorkerRegisterJob::AddCallback(
+ const RegistrationCallback& callback,
+ ServiceWorkerProviderHost* provider_host) {
+ if (!is_promise_resolved_) {
+ callbacks_.push_back(callback);
+ if (provider_host)
+ provider_host->AddScopedProcessReferenceToPattern(pattern_);
+ return;
}
+ RunSoon(base::Bind(
+ callback, promise_resolved_status_,
+ promise_resolved_registration_, promise_resolved_version_));
}
void ServiceWorkerRegisterJob::Start() {
- if (type_ == REGISTER)
- StartRegister();
+ SetPhase(START);
+ ServiceWorkerStorage::FindRegistrationCallback next_step;
+ if (job_type_ == REGISTRATION_JOB) {
+ next_step = base::Bind(
+ &ServiceWorkerRegisterJob::ContinueWithRegistration,
+ weak_factory_.GetWeakPtr());
+ } else {
+ next_step = base::Bind(
+ &ServiceWorkerRegisterJob::ContinueWithUpdate,
+ weak_factory_.GetWeakPtr());
+ }
+
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ context_->storage()->GetUninstallingRegistration(pattern_);
+ if (registration.get())
+ RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
else
- StartUnregister();
+ context_->storage()->FindRegistrationForPattern(pattern_, next_step);
}
-bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJob* job) {
- return job->type_ == type_ &&
- (type_ == ServiceWorkerRegisterJob::UNREGISTER ||
- job->script_url_ == script_url_);
+void ServiceWorkerRegisterJob::Abort() {
+ SetPhase(ABORT);
+ CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
+ // Don't have to call FinishJob() because the caller takes care of removing
+ // the jobs from the queue.
}
-void ServiceWorkerRegisterJob::StartRegister() {
- // Set up a chain of callbacks, in reverse order. Each of these
- // callbacks may be called asynchronously by the previous callback.
- StatusCallback finish_registration(base::Bind(
- &ServiceWorkerRegisterJob::RegisterComplete, weak_factory_.GetWeakPtr()));
+bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
+ if (job->GetType() != GetType())
+ return false;
+ ServiceWorkerRegisterJob* register_job =
+ static_cast<ServiceWorkerRegisterJob*>(job);
+ return register_job->pattern_ == pattern_ &&
+ register_job->script_url_ == script_url_;
+}
- RegistrationCallback start_worker(
- base::Bind(&ServiceWorkerRegisterJob::StartWorkerAndContinue,
- weak_factory_.GetWeakPtr(),
- finish_registration));
+RegistrationJobType ServiceWorkerRegisterJob::GetType() {
+ return job_type_;
+}
- UnregistrationCallback register_new(
- base::Bind(&ServiceWorkerRegisterJob::RegisterPatternAndContinue,
- weak_factory_.GetWeakPtr(),
- start_worker));
+ServiceWorkerRegisterJob::Internal::Internal() {}
- ServiceWorkerStorage::FindRegistrationCallback unregister_old(
- base::Bind(&ServiceWorkerRegisterJob::UnregisterPatternAndContinue,
- weak_factory_.GetWeakPtr(),
- register_new));
+ServiceWorkerRegisterJob::Internal::~Internal() {}
- storage_->FindRegistrationForPattern(pattern_, unregister_old);
+void ServiceWorkerRegisterJob::set_registration(
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
+ DCHECK(!internal_.registration.get());
+ internal_.registration = registration;
}
-void ServiceWorkerRegisterJob::StartUnregister() {
- // Set up a chain of callbacks, in reverse order. Each of these
- // callbacks may be called asynchronously by the previous callback.
- UnregistrationCallback finish_unregistration(
- base::Bind(&ServiceWorkerRegisterJob::UnregisterComplete,
- weak_factory_.GetWeakPtr()));
+ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
+ DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
+ return internal_.registration.get();
+}
- ServiceWorkerStorage::FindRegistrationCallback unregister(
- base::Bind(&ServiceWorkerRegisterJob::UnregisterPatternAndContinue,
- weak_factory_.GetWeakPtr(),
- finish_unregistration));
+void ServiceWorkerRegisterJob::set_new_version(
+ ServiceWorkerVersion* version) {
+ DCHECK(phase_ == UPDATE) << phase_;
+ DCHECK(!internal_.new_version.get());
+ internal_.new_version = version;
+}
- storage_->FindRegistrationForPattern(pattern_, unregister);
+ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
+ DCHECK(phase_ >= UPDATE) << phase_;
+ return internal_.new_version.get();
}
-void ServiceWorkerRegisterJob::StartWorkerAndContinue(
- const StatusCallback& callback,
- ServiceWorkerStatusCode status,
+void ServiceWorkerRegisterJob::set_uninstalling_registration(
const scoped_refptr<ServiceWorkerRegistration>& registration) {
- if (registration->active_version()) {
- // We have an active version, so we can complete immediately, even
- // if the service worker isn't running.
- callback.Run(registration, SERVICE_WORKER_OK);
+ DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
+ internal_.uninstalling_registration = registration;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerRegisterJob::uninstalling_registration() {
+ DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
+ return internal_.uninstalling_registration.get();
+}
+
+void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
+ switch (phase) {
+ case INITIAL:
+ NOTREACHED();
+ break;
+ case START:
+ DCHECK(phase_ == INITIAL) << phase_;
+ break;
+ case WAIT_FOR_UNINSTALL:
+ DCHECK(phase_ == START) << phase_;
+ break;
+ case REGISTER:
+ DCHECK(phase_ == START || phase_ == WAIT_FOR_UNINSTALL) << phase_;
+ break;
+ case UPDATE:
+ DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
+ break;
+ case INSTALL:
+ DCHECK(phase_ == UPDATE) << phase_;
+ break;
+ case STORE:
+ DCHECK(phase_ == INSTALL) << phase_;
+ break;
+ case COMPLETE:
+ DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
+ break;
+ case ABORT:
+ break;
+ }
+ phase_ = phase;
+}
+
+// This function corresponds to the steps in [[Register]] following
+// "Let registration be the result of running the [[GetRegistration]] algorithm.
+// Throughout this file, comments in quotes are excerpts from the spec.
+void ServiceWorkerRegisterJob::ContinueWithRegistration(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+ DCHECK_EQ(REGISTRATION_JOB, job_type_);
+ if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
+ Complete(status);
return;
}
- pending_version_ = new ServiceWorkerVersion(
- registration, worker_registry_, registration->next_version_id());
- for (std::vector<int>::const_iterator it = pending_process_ids_.begin();
- it != pending_process_ids_.end();
- ++it)
- pending_version_->AddProcessToWorker(*it);
+ if (!existing_registration.get() || existing_registration->is_uninstalled()) {
+ RegisterAndContinue(SERVICE_WORKER_OK);
+ return;
+ }
+
+ DCHECK(existing_registration->GetNewestVersion());
+ // "If scriptURL is equal to registration.[[ScriptURL]], then:"
+ if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
+ // "Set registration.[[Uninstalling]] to false."
+ existing_registration->AbortPendingClear(base::Bind(
+ &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
+ weak_factory_.GetWeakPtr(),
+ existing_registration));
+ return;
+ }
+
+ if (existing_registration->is_uninstalling()) {
+ // "Wait until the Record {[[key]], [[value]]} entry of its
+ // [[ScopeToRegistrationMap]] where registation.scope matches entry.[[key]]
+ // is deleted."
+ WaitForUninstall(existing_registration);
+ return;
+ }
- // The callback to watch "installation" actually fires as soon as
- // the worker is up and running, just before the install event is
- // dispatched. The job will continue to run even though the main
- // callback has executed.
- pending_version_->StartWorker(base::Bind(callback, registration));
+ // "Set registration.[[Uninstalling]] to false."
+ DCHECK(!existing_registration->is_uninstalling());
- // TODO(alecflett): Don't set the active version until just before
- // the activate event is dispatched.
- registration->set_active_version(pending_version_);
+ // "Return the result of running the [[Update]] algorithm, or its equivalent,
+ // passing registration as the argument."
+ set_registration(existing_registration);
+ UpdateAndContinue();
}
-void ServiceWorkerRegisterJob::RegisterPatternAndContinue(
- const RegistrationCallback& callback,
- ServiceWorkerStatusCode previous_status) {
- if (previous_status != SERVICE_WORKER_OK) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(callback,
- previous_status,
- scoped_refptr<ServiceWorkerRegistration>()));
+void ServiceWorkerRegisterJob::ContinueWithUpdate(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+ DCHECK_EQ(UPDATE_JOB, job_type_);
+ if (status != SERVICE_WORKER_OK) {
+ Complete(status);
return;
}
- // TODO: Eventually RegisterInternal will be replaced by an asynchronous
- // operation. Pass its resulting status through 'callback'.
- scoped_refptr<ServiceWorkerRegistration> registration =
- storage_->RegisterInternal(pattern_, script_url_);
-
- BrowserThread::PostTask(BrowserThread::IO,
- FROM_HERE,
- base::Bind(callback, SERVICE_WORKER_OK,
- registration));
-}
-
-void ServiceWorkerRegisterJob::UnregisterPatternAndContinue(
- const UnregistrationCallback& callback,
- bool found,
- ServiceWorkerStatusCode previous_status,
- const scoped_refptr<ServiceWorkerRegistration>& previous_registration) {
-
- // The previous registration may not exist, which is ok.
- if (previous_status == SERVICE_WORKER_OK && found &&
- (script_url_.is_empty() ||
- previous_registration->script_url() != script_url_)) {
- // TODO: Eventually UnregisterInternal will be replaced by an
- // asynchronous operation. Pass its resulting status though
- // 'callback'.
- storage_->UnregisterInternal(pattern_);
- DCHECK(previous_registration->is_shutdown());
- } else {
- // TODO(alecflett): We have an existing registration, we should
- // schedule an update.
+ if (existing_registration.get() != registration()) {
+ Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
+ return;
+ }
+
+ // A previous job may have unregistered or installed a new version to this
+ // registration.
+ if (registration()->is_uninstalling() ||
+ registration()->GetNewestVersion()->script_url() != script_url_) {
+ Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
+ return;
+ }
+
+ // TODO(michaeln): If the last update check was less than 24 hours
+ // ago, depending on the freshness of the cached worker script we
+ // may be able to complete the update job right here.
+
+ UpdateAndContinue();
+}
+
+// Creates a new ServiceWorkerRegistration.
+void ServiceWorkerRegisterJob::RegisterAndContinue(
+ ServiceWorkerStatusCode status) {
+ SetPhase(REGISTER);
+ if (status != SERVICE_WORKER_OK) {
+ // Abort this registration job.
+ Complete(status);
+ return;
+ }
+
+ set_registration(new ServiceWorkerRegistration(
+ pattern_, context_->storage()->NewRegistrationId(), context_));
+ AssociateProviderHostsToRegistration(registration());
+ UpdateAndContinue();
+}
+
+void ServiceWorkerRegisterJob::WaitForUninstall(
+ const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+ SetPhase(WAIT_FOR_UNINSTALL);
+ set_uninstalling_registration(existing_registration);
+ uninstalling_registration()->AddListener(this);
+}
+
+void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
+ const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
+ ServiceWorkerStatusCode status) {
+ if (status != SERVICE_WORKER_OK) {
+ Complete(status);
+ return;
+ }
+ set_registration(existing_registration);
+
+ // TODO(falken): Follow the spec: resolve the promise
+ // with the newest version.
+
+ if (!existing_registration->active_version()) {
+ UpdateAndContinue();
+ return;
+ }
+
+ ResolvePromise(status,
+ existing_registration.get(),
+ existing_registration->active_version());
+ Complete(SERVICE_WORKER_OK);
+}
+
+// This function corresponds to the spec's [[Update]] algorithm.
+void ServiceWorkerRegisterJob::UpdateAndContinue() {
+ SetPhase(UPDATE);
+ context_->storage()->NotifyInstallingRegistration(registration());
+
+ // TODO(falken): "If serviceWorkerRegistration.installingWorker is not null.."
+ // then terminate the installing worker. It doesn't make sense to implement
+ // yet since we always activate the worker if install completed, so there can
+ // be no installing worker at this point.
+
+ // "Let serviceWorker be a newly-created ServiceWorker object..." and start
+ // the worker.
+ set_new_version(new ServiceWorkerVersion(registration(),
+ script_url_,
+ context_->storage()->NewVersionId(),
+ context_));
+
+ bool pause_after_download = job_type_ == UPDATE_JOB;
+ if (pause_after_download)
+ new_version()->embedded_worker()->AddListener(this);
+ new_version()->StartWorker(
+ pause_after_download,
+ base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnStartWorkerFinished(
+ ServiceWorkerStatusCode status) {
+ if (status == SERVICE_WORKER_OK) {
+ InstallAndContinue();
+ return;
+ }
+
+ // "If serviceWorker fails to start up..." then reject the promise with an
+ // error and abort. When there is a main script network error, the status will
+ // be updated to a more specific one.
+ const net::URLRequestStatus& main_script_status =
+ new_version()->script_cache_map()->main_script_status();
+ if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
+ switch (main_script_status.error()) {
+ case net::ERR_INSECURE_RESPONSE:
+ case net::ERR_UNSAFE_REDIRECT:
+ status = SERVICE_WORKER_ERROR_SECURITY;
+ break;
+ case net::ERR_ABORTED:
+ status = SERVICE_WORKER_ERROR_ABORT;
+ break;
+ case net::ERR_FAILED:
+ status = SERVICE_WORKER_ERROR_NETWORK;
+ break;
+ default:
+ NOTREACHED();
+ }
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE, base::Bind(callback, previous_status));
+ Complete(status);
}
-void ServiceWorkerRegisterJob::RunCallbacks(
+// This function corresponds to the spec's [[Install]] algorithm.
+void ServiceWorkerRegisterJob::InstallAndContinue() {
+ SetPhase(INSTALL);
+
+ // "2. Set registration.installingWorker to worker."
+ registration()->SetInstallingVersion(new_version());
+
+ // "3. Resolve promise with registration."
+ ResolvePromise(SERVICE_WORKER_OK, registration(), new_version());
+
+ // "4. Run the [[UpdateState]] algorithm passing registration.installingWorker
+ // and "installing" as the arguments."
+ new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
+
+ // "5. Fire a simple event named updatefound..."
+ registration()->NotifyUpdateFound();
+
+ // "6. Fire an event named install..."
+ new_version()->DispatchInstallEvent(
+ -1,
+ base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnInstallFinished(
+ ServiceWorkerStatusCode status) {
+ // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
+ // unexpectedly terminated) we may want to retry sending the event again.
+ if (status != SERVICE_WORKER_OK) {
+ // "8. If installFailed is true, then:..."
+ Complete(status);
+ return;
+ }
+
+ SetPhase(STORE);
+ registration()->set_last_update_check(base::Time::Now());
+ context_->storage()->StoreRegistration(
+ registration(),
+ new_version(),
+ base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
+ ServiceWorkerStatusCode status) {
+ if (status != SERVICE_WORKER_OK) {
+ Complete(status);
+ return;
+ }
+
+ // "9. If registration.waitingWorker is not null, then:..."
+ if (registration()->waiting_version()) {
+ // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
+ // and "redundant" as the arguments."
+ registration()->waiting_version()->SetStatus(
+ ServiceWorkerVersion::REDUNDANT);
+ }
+
+ // "10. Set registration.waitingWorker to registration.installingWorker."
+ // "11. Set registration.installingWorker to null."
+ registration()->SetWaitingVersion(new_version());
+
+ // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
+ // and "installed" as the arguments."
+ new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
+
+ // TODO(michaeln): "13. If activateImmediate is true, then..."
+
+ // "14. Wait until no document is using registration as their
+ // Service Worker registration."
+ registration()->ActivateWaitingVersionWhenReady();
+
+ Complete(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
+ CompleteInternal(status);
+ context_->job_coordinator()->FinishJob(pattern_, this);
+}
+
+void ServiceWorkerRegisterJob::CompleteInternal(
+ ServiceWorkerStatusCode status) {
+ SetPhase(COMPLETE);
+ if (status != SERVICE_WORKER_OK) {
+ if (registration()) {
+ if (new_version()) {
+ registration()->UnsetVersion(new_version());
+ new_version()->Doom();
+ }
+ if (!registration()->waiting_version() &&
+ !registration()->active_version()) {
+ registration()->NotifyRegistrationFailed();
+ context_->storage()->DeleteRegistration(
+ registration()->id(),
+ registration()->pattern().GetOrigin(),
+ base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+ }
+ }
+ if (!is_promise_resolved_)
+ ResolvePromise(status, NULL, NULL);
+ }
+ DCHECK(callbacks_.empty());
+ if (registration()) {
+ context_->storage()->NotifyDoneInstallingRegistration(
+ registration(), new_version(), status);
+ }
+ if (new_version())
+ new_version()->embedded_worker()->RemoveListener(this);
+}
+
+void ServiceWorkerRegisterJob::ResolvePromise(
ServiceWorkerStatusCode status,
- const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version) {
+ DCHECK(!is_promise_resolved_);
+ is_promise_resolved_ = true;
+ promise_resolved_status_ = status;
+ promise_resolved_registration_ = registration;
+ promise_resolved_version_ = version;
for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
it != callbacks_.end();
++it) {
- it->Run(status, registration);
+ it->Run(status, registration, version);
}
+ callbacks_.clear();
}
-void ServiceWorkerRegisterJob::RegisterComplete(
- const scoped_refptr<ServiceWorkerRegistration>& registration,
- ServiceWorkerStatusCode start_status) {
- RunCallbacks(start_status, registration);
- coordinator_->FinishJob(pattern_, this);
+void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
+ // This happens prior to OnStartWorkerFinished time.
+ scoped_refptr<ServiceWorkerVersion> most_recent_version =
+ registration()->waiting_version() ?
+ registration()->waiting_version() :
+ registration()->active_version();
+ DCHECK(most_recent_version.get());
+ int64 most_recent_script_id =
+ most_recent_version->script_cache_map()->Lookup(script_url_);
+ int64 new_script_id =
+ new_version()->script_cache_map()->Lookup(script_url_);
+
+ // TODO(michaeln): It would be better to compare as the new resource
+ // is being downloaded and to avoid writing it to disk until we know
+ // its needed.
+ context_->storage()->CompareScriptResources(
+ most_recent_script_id, new_script_id,
+ base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
+ weak_factory_.GetWeakPtr(),
+ most_recent_version));
}
-void ServiceWorkerRegisterJob::UnregisterComplete(
- ServiceWorkerStatusCode status) {
- RunCallbacks(status, NULL);
- coordinator_->FinishJob(pattern_, this);
+bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
+ return false;
+}
+
+void ServiceWorkerRegisterJob::OnRegistrationFinishedUninstalling(
+ ServiceWorkerRegistration* existing_registration) {
+ DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
+ DCHECK_EQ(existing_registration, uninstalling_registration());
+ existing_registration->RemoveListener(this);
+ set_uninstalling_registration(NULL);
+ RegisterAndContinue(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
+ ServiceWorkerVersion* most_recent_version,
+ ServiceWorkerStatusCode status,
+ bool are_equal) {
+ if (are_equal) {
+ // Only bump the last check time when we've bypassed the browser cache.
+ base::TimeDelta time_since_last_check =
+ base::Time::Now() - registration()->last_update_check();
+ if (time_since_last_check > base::TimeDelta::FromHours(24)) {
+ registration()->set_last_update_check(base::Time::Now());
+ context_->storage()->UpdateLastUpdateCheckTime(registration());
+ }
+
+ ResolvePromise(SERVICE_WORKER_OK, registration(), most_recent_version);
+ Complete(SERVICE_WORKER_ERROR_EXISTS);
+ return;
+ }
+
+ // Proceed with really starting the worker.
+ new_version()->embedded_worker()->ResumeAfterDownload();
+ new_version()->embedded_worker()->RemoveListener(this);
+}
+
+void ServiceWorkerRegisterJob::AssociateProviderHostsToRegistration(
+ ServiceWorkerRegistration* registration) {
+ DCHECK(registration);
+ for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
+ context_->GetProviderHostIterator();
+ !it->IsAtEnd(); it->Advance()) {
+ ServiceWorkerProviderHost* host = it->GetProviderHost();
+ if (ServiceWorkerUtils::ScopeMatches(registration->pattern(),
+ host->document_url())) {
+ if (host->CanAssociateRegistration(registration))
+ host->AssociateRegistration(registration);
+ }
+ }
}
} // namespace content