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..dd96b604b3c19393659c30f33e7d5f79bc0b4c3b 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_JOB), |
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,17 @@ void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback, |
void ServiceWorkerRegisterJob::Start() { |
SetPhase(START); |
- context_->storage()->FindRegistrationForPattern( |
- pattern_, |
- base::Bind( |
- &ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue, |
- weak_factory_.GetWeakPtr())); |
+ 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()); |
+ } |
+ context_->storage()->FindRegistrationForPattern(pattern_, next_step); |
} |
void ServiceWorkerRegisterJob::Abort() { |
@@ -82,7 +134,7 @@ bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) { |
} |
RegistrationJobType ServiceWorkerRegisterJob::GetType() { |
- return REGISTRATION; |
+ return job_type_; |
} |
ServiceWorkerRegisterJob::Internal::Internal() {} |
@@ -97,7 +149,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 +201,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, job_type_); |
// On unexpected error, abort this registration job. |
if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) { |
Complete(status); |
@@ -168,7 +221,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 +251,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 +281,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) { |
+// This function corresponds to the spec's [[Update]] algorithm. |
+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 +299,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 +370,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 +387,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 +460,44 @@ 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, |
+ bool are_equal) { |
+ if (are_equal) { |
+ 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, |