Chromium Code Reviews| Index: content/browser/service_worker/service_worker_register_job.cc |
| diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc |
| index 67793c6004cc4baca63b83b4a2b8af526918e784..db7bdaa7f6c462cf7687e9b9d8dc209362ce29db 100644 |
| --- a/content/browser/service_worker/service_worker_register_job.cc |
| +++ b/content/browser/service_worker/service_worker_register_job.cc |
| @@ -21,7 +21,38 @@ void RunSoon(const base::Closure& closure) { |
| base::MessageLoop::current()->PostTask(FROM_HERE, closure); |
| } |
| -} |
| +// Helper class for the [[Update]] algo. |
| +class DeferredActivationHelper : public ServiceWorkerVersion::Listener { |
| + public: |
| + explicit DeferredActivationHelper(ServiceWorkerRegistration* registration) |
| + : registration_(registration), |
| + active_version_(registration->active_version()), |
| + waiting_version_(registration->waiting_version()) { |
| + active_version_->AddListener(this); |
| + } |
| + |
| + virtual ~DeferredActivationHelper() {} |
| + |
| + private: |
| + virtual void OnNoControllees(ServiceWorkerVersion* version) OVERRIDE { |
| + DCHECK_EQ(active_version_, version); |
| + scoped_ptr<DeferredActivationHelper> self_deletor(this); |
| + active_version_->RemoveListener(this); |
| + if (registration_->active_version() != active_version_ || |
| + registration_->waiting_version() != waiting_version_) { |
| + return; // Something has changed making activation n/a. |
| + } |
| + registration_->ActivateWaitingVersion( |
| + base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); |
| + } |
| + |
| + scoped_refptr<ServiceWorkerRegistration> registration_; |
| + scoped_refptr<ServiceWorkerVersion> active_version_; |
| + scoped_refptr<ServiceWorkerVersion> waiting_version_; |
| + DISALLOW_COPY_AND_ASSIGN(DeferredActivationHelper); |
| +}; |
| + |
| +} // namespace |
| typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType; |
| @@ -30,6 +61,7 @@ ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( |
| const GURL& pattern, |
| const GURL& script_url) |
| : context_(context), |
| + job_type_(REGISTRATION), |
| pattern_(pattern), |
| script_url_(script_url), |
| phase_(INITIAL), |
| @@ -37,6 +69,20 @@ ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( |
| promise_resolved_status_(SERVICE_WORKER_OK), |
| weak_factory_(this) {} |
| +ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( |
| + base::WeakPtr<ServiceWorkerContextCore> context, |
| + ServiceWorkerRegistration* registration) |
| + : context_(context), |
| + job_type_(UPDATE_JOB), |
| + pattern_(registration->pattern()), |
| + script_url_(registration->script_url()), |
| + phase_(INITIAL), |
| + is_promise_resolved_(false), |
| + promise_resolved_status_(SERVICE_WORKER_OK), |
| + weak_factory_(this) { |
| + internal_.registration = registration; |
| +} |
| + |
| ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() { |
| DCHECK(!context_ || |
| phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT) |
| @@ -58,11 +104,18 @@ void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback, |
| void ServiceWorkerRegisterJob::Start() { |
| SetPhase(START); |
| + ServiceWorkerStorage::FindRegistrationCallback next_step; |
| + if (job_type_ == REGISTRATION) { |
| + next_step = base::Bind( |
| + &ServiceWorkerRegisterJob::ContinueWithRegistration, |
| + weak_factory_.GetWeakPtr()); |
| + } else { |
| + next_step = base::Bind( |
| + &ServiceWorkerRegisterJob::ContinueWithUpdate, |
| + weak_factory_.GetWeakPtr()); |
| + } |
| context_->storage()->FindRegistrationForPattern( |
| - pattern_, |
| - base::Bind( |
| - &ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue, |
| - weak_factory_.GetWeakPtr())); |
| + pattern_, next_step); |
|
nhiroki
2014/07/15 03:57:04
nit: Maybe you can squash this line into the previ
michaeln
2014/07/16 00:32:53
Done.
|
| } |
| void ServiceWorkerRegisterJob::Abort() { |
| @@ -82,7 +135,7 @@ bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) { |
| } |
| RegistrationJobType ServiceWorkerRegisterJob::GetType() { |
| - return REGISTRATION; |
| + return job_type_; |
| } |
| ServiceWorkerRegisterJob::Internal::Internal() {} |
| @@ -97,7 +150,7 @@ void ServiceWorkerRegisterJob::set_registration( |
| } |
| ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() { |
| - DCHECK(phase_ >= REGISTER) << phase_; |
| + DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_; |
| return internal_.registration; |
| } |
| @@ -149,9 +202,10 @@ void ServiceWorkerRegisterJob::SetPhase(Phase phase) { |
| // "Let serviceWorkerRegistration be _GetRegistration(scope)" |
| // |existing_registration| corresponds to serviceWorkerRegistration. |
| // Throughout this file, comments in quotes are excerpts from the spec. |
| -void ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue( |
| +void ServiceWorkerRegisterJob::ContinueWithRegistration( |
| ServiceWorkerStatusCode status, |
| const scoped_refptr<ServiceWorkerRegistration>& existing_registration) { |
| + DCHECK_EQ(REGISTRATION, job_type_); |
| // On unexpected error, abort this registration job. |
| if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) { |
| Complete(status); |
| @@ -168,7 +222,7 @@ void ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue( |
| // but seems reasonable, and without SoftUpdate implemented we can never |
| // Update otherwise). |
| if (!existing_registration->active_version()) { |
| - UpdateAndContinue(status); |
| + UpdateAndContinue(); |
| return; |
| } |
| ResolvePromise( |
| @@ -198,6 +252,23 @@ void ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue( |
| weak_factory_.GetWeakPtr())); |
| } |
| +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; |
| + } |
| + |
| + if (existing_registration != registration()) { |
| + Complete(SERVICE_WORKER_ERROR_NOT_FOUND); |
| + return; |
| + } |
| + |
| + UpdateAndContinue(); |
| +} |
| + |
| // Creates a new ServiceWorkerRegistration. |
| void ServiceWorkerRegisterJob::RegisterAndContinue( |
| ServiceWorkerStatusCode status) { |
| @@ -211,19 +282,13 @@ void ServiceWorkerRegisterJob::RegisterAndContinue( |
| set_registration(new ServiceWorkerRegistration( |
| pattern_, script_url_, context_->storage()->NewRegistrationId(), |
| context_)); |
| - context_->storage()->NotifyInstallingRegistration(registration()); |
| - UpdateAndContinue(SERVICE_WORKER_OK); |
| + UpdateAndContinue(); |
| } |
| // This function corresponds to the spec's _Update algorithm. |
| -void ServiceWorkerRegisterJob::UpdateAndContinue( |
| - ServiceWorkerStatusCode status) { |
| +void ServiceWorkerRegisterJob::UpdateAndContinue() { |
| SetPhase(UPDATE); |
| - if (status != SERVICE_WORKER_OK) { |
| - // Abort this registration job. |
| - Complete(status); |
| - return; |
| - } |
| + context_->storage()->NotifyInstallingRegistration(registration()); |
| // TODO(falken): "If serviceWorkerRegistration.installingWorker is not null.." |
| // then terminate the installing worker. It doesn't make sense to implement |
| @@ -235,8 +300,7 @@ void ServiceWorkerRegisterJob::UpdateAndContinue( |
| set_new_version(new ServiceWorkerVersion( |
| registration(), context_->storage()->NewVersionId(), context_)); |
| - // TODO(michaeln): Pause after downloading when performing an update. |
| - bool pause_after_download = false; |
| + bool pause_after_download = job_type_ == UPDATE_JOB; |
| if (pause_after_download) |
| new_version()->embedded_worker()->AddListener(this); |
| new_version()->StartWorkerWithCandidateProcesses( |
| @@ -307,7 +371,13 @@ void ServiceWorkerRegisterJob::OnStoreRegistrationComplete( |
| return; |
| } |
| - // TODO(nhiroki): "9. If registration.waitingWorker is not null, then:..." |
| + // "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." |
| @@ -318,67 +388,27 @@ void ServiceWorkerRegisterJob::OnStoreRegistrationComplete( |
| // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker |
| // and "installed" as the arguments." |
| new_version()->SetStatus(ServiceWorkerVersion::INSTALLED); |
| - ActivateAndContinue(); |
| -} |
| -// This function corresponds to the spec's _Activate algorithm. |
| -void ServiceWorkerRegisterJob::ActivateAndContinue() { |
| - SetPhase(ACTIVATE); |
| + // TODO(michaeln): "13. If activateImmediate is true, then..." |
| - // "4. If existingWorker is not null, then: wait for exitingWorker to finish |
| - // handling any in-progress requests." |
| - // See if we already have an active_version for the scope and it has |
| - // controllee documents (if so activating the new version should wait |
| - // until we have no documents controlled by the version). |
| + // "14. Wait until no document is using registration as their |
| + // Service Worker registration." |
| if (registration()->active_version() && |
| registration()->active_version()->HasControllee()) { |
| - // TODO(kinuko,falken): Currently we immediately return if the existing |
| - // registration already has an active version, so we shouldn't come |
| - // this way. |
| - NOTREACHED(); |
| - // TODO(falken): Register an continuation task to wait for NoControllees |
| - // notification so that we can resume activation later. |
| + scoped_ptr<DeferredActivationHelper> deferred_activation( |
| + new DeferredActivationHelper(registration())); |
| + // It will delete itself when done. |
| + ignore_result(deferred_activation.release()); |
| Complete(SERVICE_WORKER_OK); |
| return; |
| } |
| - // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker." |
| - // "6. Set serviceWorkerRegistration.waitingWorker to null." |
| - DisassociateVersionFromDocuments(context_, new_version()); |
| - registration()->SetActiveVersion(new_version()); |
| - AssociateActiveVersionToDocuments(context_, new_version()); |
| - |
| - // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and |
| - // "activating" as arguments." |
| - new_version()->SetStatus(ServiceWorkerVersion::ACTIVATING); |
| - |
| - // TODO(nhiroki): "8. Fire a simple event named controllerchange..." |
| - |
| - // "9. Fire an event named activate..." |
| - new_version()->DispatchActivateEvent( |
| - base::Bind(&ServiceWorkerRegisterJob::OnActivateFinished, |
| + SetPhase(ACTIVATE); |
| + registration()->ActivateWaitingVersion( |
| + base::Bind(&ServiceWorkerRegisterJob::Complete, |
| weak_factory_.GetWeakPtr())); |
| } |
| -void ServiceWorkerRegisterJob::OnActivateFinished( |
| - 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) { |
| - // "11. If activateFailed is true, then:..." |
| - Complete(status); |
| - return; |
| - } |
| - |
| - // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker |
| - // and "activated" as the arguments." |
| - new_version()->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| - context_->storage()->UpdateToActiveState( |
| - registration(), |
| - base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); |
| - Complete(SERVICE_WORKER_OK); |
| -} |
| - |
| void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) { |
| CompleteInternal(status); |
| context_->job_coordinator()->FinishJob(pattern_, this); |
| @@ -431,19 +461,46 @@ void ServiceWorkerRegisterJob::ResolvePromise( |
| } |
| void ServiceWorkerRegisterJob::OnPausedAfterDownload() { |
| - // TODO(michaeln): Compare the old and new script. |
| - // If different unpause the worker and continue with |
| - // the job. If the same ResolvePromise with the current |
| - // version and complete the job, throwing away the new version |
| - // since there's nothing new. |
| - new_version()->embedded_worker()->RemoveListener(this); |
| - new_version()->embedded_worker()->ResumeAfterDownload(); |
| + // This happens prior to OnStartWorkerFinished time. |
| + scoped_refptr<ServiceWorkerVersion> current_version = |
| + registration()->active_version(); |
| + DCHECK(current_version); |
| + int64 current_script_id = |
| + current_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( |
| + current_script_id, new_script_id, |
| + base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete, |
| + weak_factory_.GetWeakPtr(), |
| + current_version)); |
| } |
| bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) { |
| return false; |
| } |
| +void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete( |
| + ServiceWorkerVersion* current_version, |
| + ServiceWorkerStatusCode status, |
| + int compare_result) { |
| + if (compare_result == 0) { |
| + new_version()->embedded_worker()->Stop(); |
| + ResolvePromise( |
| + SERVICE_WORKER_OK, registration(), current_version); |
| + Complete(SERVICE_WORKER_ERROR_EXISTS); |
| + return; |
| + } |
| + |
| + // Proceed with really starting the worker. |
| + new_version()->embedded_worker()->ResumeAfterDownload(); |
| + new_version()->embedded_worker()->RemoveListener(this); |
| +} |
| + |
| // static |
| void ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( |
| base::WeakPtr<ServiceWorkerContextCore> context, |