1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_register_job.h"
9 #include "content/browser/service_worker/service_worker_context_core.h"
10 #include "content/browser/service_worker/service_worker_job_coordinator.h"
11 #include "content/browser/service_worker/service_worker_registration.h"
12 #include "content/browser/service_worker/service_worker_storage.h"
16 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType
;
18 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
19 base::WeakPtr
<ServiceWorkerContextCore
> context
,
21 const GURL
& script_url
)
24 script_url_(script_url
),
25 weak_factory_(this) {}
27 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {}
29 void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback
& callback
,
31 // If we've created a pending version, associate source_provider it with that,
32 // otherwise queue it up.
33 callbacks_
.push_back(callback
);
34 DCHECK_NE(-1, process_id
);
35 if (pending_version_
) {
36 pending_version_
->AddProcessToWorker(process_id
);
38 pending_process_ids_
.push_back(process_id
);
42 void ServiceWorkerRegisterJob::Start() {
43 context_
->storage()->FindRegistrationForPattern(
46 &ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue
,
47 weak_factory_
.GetWeakPtr()));
50 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase
* job
) {
51 if (job
->GetType() != GetType())
53 ServiceWorkerRegisterJob
* register_job
=
54 static_cast<ServiceWorkerRegisterJob
*>(job
);
55 return register_job
->pattern_
== pattern_
&&
56 register_job
->script_url_
== script_url_
;
59 RegistrationJobType
ServiceWorkerRegisterJob::GetType() {
63 // This function corresponds to the steps in Register following
64 // "Let serviceWorkerRegistration be _GetRegistration(scope)"
65 // |registration| corresponds to serviceWorkerRegistration.
66 // Throughout this file, comments in quotes are excerpts from the spec.
67 void ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue(
68 ServiceWorkerStatusCode status
,
69 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
70 // On unexpected error, abort this registration job.
71 if (status
!= SERVICE_WORKER_ERROR_NOT_FOUND
&& status
!= SERVICE_WORKER_OK
) {
76 // "If serviceWorkerRegistration is not null and script is equal to
77 // serviceWorkerRegistration.scriptUrl..." resolve with the existing
78 // registration and abort.
79 if (registration
.get() && registration
->script_url() == script_url_
) {
80 registration_
= registration
;
81 // If there's no active version, go ahead to Update (this isn't in the spec
82 // but seems reasonable, and without SoftUpdate implemented we can never
84 if (!registration_
->active_version()) {
85 UpdateAndContinue(status
);
88 Complete(SERVICE_WORKER_OK
);
92 // "If serviceWorkerRegistration is null..." create a new registration.
93 if (!registration
.get()) {
94 RegisterAndContinue(SERVICE_WORKER_OK
);
98 // On script URL mismatch, "set serviceWorkerRegistration.scriptUrl to
99 // script." We accomplish this by deleting the existing registration and
100 // registering a new one.
101 // TODO(falken): Match the spec. We now throw away the active_version_ and
102 // pending_version_ of the existing registration, which isn't in the spec.
103 registration
->Shutdown();
104 context_
->storage()->DeleteRegistration(
106 base::Bind(&ServiceWorkerRegisterJob::RegisterAndContinue
,
107 weak_factory_
.GetWeakPtr()));
110 // Registers a new ServiceWorkerRegistration.
111 void ServiceWorkerRegisterJob::RegisterAndContinue(
112 ServiceWorkerStatusCode status
) {
113 DCHECK(!registration_
);
114 if (status
!= SERVICE_WORKER_OK
) {
115 // Abort this registration job.
120 registration_
= new ServiceWorkerRegistration(
121 pattern_
, script_url_
, context_
->storage()->NewRegistrationId(),
123 context_
->storage()->StoreRegistration(
125 base::Bind(&ServiceWorkerRegisterJob::UpdateAndContinue
,
126 weak_factory_
.GetWeakPtr()));
129 // This function corresponds to the spec's _Update algorithm.
130 void ServiceWorkerRegisterJob::UpdateAndContinue(
131 ServiceWorkerStatusCode status
) {
132 DCHECK(registration_
);
133 if (status
!= SERVICE_WORKER_OK
) {
134 // Abort this registration job.
139 // TODO: "If serviceWorkerRegistration.pendingWorker is not null..." then
140 // terminate the pending worker. It doesn't make sense to implement yet since
141 // we always activate the worker if install completed, so there can be no
142 // pending worker at this point.
143 DCHECK(!registration_
->pending_version());
145 // TODO: Script fetching and comparing the old and new script belongs here.
147 // "Let serviceWorker be a newly-created ServiceWorker object..." and start
149 pending_version_
= new ServiceWorkerVersion(
150 registration_
, context_
->storage()->NewVersionId(), context_
);
151 for (std::vector
<int>::const_iterator it
= pending_process_ids_
.begin();
152 it
!= pending_process_ids_
.end();
154 pending_version_
->AddProcessToWorker(*it
);
156 pending_version_
->StartWorker(
157 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished
,
158 weak_factory_
.GetWeakPtr()));
161 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
162 ServiceWorkerStatusCode status
) {
163 // "If serviceWorker fails to start up..." then reject the promise with an
165 if (status
!= SERVICE_WORKER_OK
) {
170 // "Resolve promise with serviceWorker."
171 // Although the spec doesn't set pendingWorker until after resolving the
172 // promise, our system's resolving works by passing ServiceWorkerRegistration
173 // to the callbacks, so pendingWorker must be set first.
174 DCHECK(!registration_
->pending_version());
175 registration_
->set_pending_version(pending_version_
);
176 RunCallbacks(status
);
178 InstallAndContinue();
181 // This function corresponds to the spec's _Install algorithm.
182 void ServiceWorkerRegisterJob::InstallAndContinue() {
183 // "Set serviceWorkerRegistration.pendingWorker._state to installing."
184 // "Fire install event on the associated ServiceWorkerGlobalScope object."
185 pending_version_
->DispatchInstallEvent(
187 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished
,
188 weak_factory_
.GetWeakPtr()));
191 void ServiceWorkerRegisterJob::OnInstallFinished(
192 ServiceWorkerStatusCode status
) {
193 // "If any handler called waitUntil()..." and the resulting promise
194 // is rejected, abort.
195 if (status
!= SERVICE_WORKER_OK
) {
196 registration_
->set_pending_version(NULL
);
201 // TODO: Per spec, only activate if no document is using the registration.
202 ActivateAndContinue();
205 // This function corresponds to the spec's _Activate algorithm.
206 void ServiceWorkerRegisterJob::ActivateAndContinue() {
207 // "Set serviceWorkerRegistration.pendingWorker to null."
208 registration_
->set_pending_version(NULL
);
210 // TODO: Dispatch the activate event.
211 // TODO(michaeln): Persist the newly ACTIVE version.
212 pending_version_
->SetStatus(ServiceWorkerVersion::ACTIVE
);
213 DCHECK(!registration_
->active_version());
214 registration_
->set_active_version(pending_version_
);
215 pending_version_
= NULL
;
216 Complete(SERVICE_WORKER_OK
);
219 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status
) {
220 RunCallbacks(status
);
222 // If |pending_version_| exists, it was not activated, so we are the sole
223 // owner of it, so it will be destroyed when this job ends, so Shutdown here.
224 // We should be able to remove this code later, when something else holds a
225 // reference to |pending_version_|.
226 // TODO(kinuko): Fix these ownership and shutdown semantics.
227 if (pending_version_
) {
228 DCHECK(!registration_
->pending_version());
229 DCHECK(!registration_
->active_version());
230 pending_version_
->Shutdown();
233 context_
->job_coordinator()->FinishJob(pattern_
, this);
236 void ServiceWorkerRegisterJob::RunCallbacks(ServiceWorkerStatusCode status
) {
237 for (std::vector
<RegistrationCallback
>::iterator it
= callbacks_
.begin();
238 it
!= callbacks_
.end();
240 it
->Run(status
, status
== SERVICE_WORKER_OK
? registration_
: NULL
);
245 } // namespace content