Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_register_job.cc
blob681930588a32d9371ed0401a340d9e87c4fa8857
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"
7 #include <vector>
9 #include "base/message_loop/message_loop.h"
10 #include "content/browser/service_worker/service_worker_context_core.h"
11 #include "content/browser/service_worker/service_worker_job_coordinator.h"
12 #include "content/browser/service_worker/service_worker_registration.h"
13 #include "content/browser/service_worker/service_worker_storage.h"
14 #include "content/browser/service_worker/service_worker_utils.h"
16 namespace content {
18 namespace {
20 void RunSoon(const base::Closure& closure) {
21 base::MessageLoop::current()->PostTask(FROM_HERE, closure);
26 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
28 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
29 base::WeakPtr<ServiceWorkerContextCore> context,
30 const GURL& pattern,
31 const GURL& script_url)
32 : context_(context),
33 pattern_(pattern),
34 script_url_(script_url),
35 phase_(INITIAL),
36 is_promise_resolved_(false),
37 promise_resolved_status_(SERVICE_WORKER_OK),
38 weak_factory_(this) {}
40 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
41 DCHECK(phase_ == INITIAL || phase_ == COMPLETE);
44 void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback,
45 int process_id) {
46 if (!is_promise_resolved_) {
47 callbacks_.push_back(callback);
48 if (process_id != -1 && (phase_ < UPDATE || !pending_version()))
49 pending_process_ids_.push_back(process_id);
50 return;
52 RunSoon(base::Bind(
53 callback, promise_resolved_status_,
54 promise_resolved_registration_, promise_resolved_version_));
57 void ServiceWorkerRegisterJob::Start() {
58 SetPhase(START);
59 context_->storage()->FindRegistrationForPattern(
60 pattern_,
61 base::Bind(
62 &ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue,
63 weak_factory_.GetWeakPtr()));
66 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
67 if (job->GetType() != GetType())
68 return false;
69 ServiceWorkerRegisterJob* register_job =
70 static_cast<ServiceWorkerRegisterJob*>(job);
71 return register_job->pattern_ == pattern_ &&
72 register_job->script_url_ == script_url_;
75 RegistrationJobType ServiceWorkerRegisterJob::GetType() {
76 return REGISTRATION;
79 ServiceWorkerRegisterJob::Internal::Internal() {}
81 ServiceWorkerRegisterJob::Internal::~Internal() {}
83 void ServiceWorkerRegisterJob::set_registration(
84 ServiceWorkerRegistration* registration) {
85 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
86 DCHECK(!internal_.registration);
87 internal_.registration = registration;
90 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
91 DCHECK(phase_ >= REGISTER) << phase_;
92 DCHECK(internal_.registration);
93 return internal_.registration;
96 void ServiceWorkerRegisterJob::set_pending_version(
97 ServiceWorkerVersion* version) {
98 DCHECK(phase_ == UPDATE || phase_ == ACTIVATE) << phase_;
99 DCHECK(!internal_.pending_version || !version);
100 internal_.pending_version = version;
103 ServiceWorkerVersion* ServiceWorkerRegisterJob::pending_version() {
104 DCHECK(phase_ >= UPDATE) << phase_;
105 return internal_.pending_version;
108 void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
109 switch (phase) {
110 case INITIAL:
111 NOTREACHED();
112 break;
113 case START:
114 DCHECK(phase_ == INITIAL) << phase_;
115 break;
116 case REGISTER:
117 DCHECK(phase_ == START) << phase_;
118 break;
119 case UPDATE:
120 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
121 break;
122 case INSTALL:
123 DCHECK(phase_ == UPDATE) << phase_;
124 break;
125 case STORE:
126 DCHECK(phase_ == INSTALL) << phase_;
127 break;
128 case ACTIVATE:
129 DCHECK(phase_ == STORE) << phase_;
130 break;
131 case COMPLETE:
132 DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
133 break;
135 phase_ = phase;
138 // This function corresponds to the steps in Register following
139 // "Let serviceWorkerRegistration be _GetRegistration(scope)"
140 // |existing_registration| corresponds to serviceWorkerRegistration.
141 // Throughout this file, comments in quotes are excerpts from the spec.
142 void ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue(
143 ServiceWorkerStatusCode status,
144 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
145 // On unexpected error, abort this registration job.
146 if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
147 Complete(status);
148 return;
151 // "If serviceWorkerRegistration is not null and script is equal to
152 // serviceWorkerRegistration.scriptUrl..." resolve with the existing
153 // registration and abort.
154 if (existing_registration.get() &&
155 existing_registration->script_url() == script_url_) {
156 set_registration(existing_registration);
157 // If there's no active version, go ahead to Update (this isn't in the spec
158 // but seems reasonable, and without SoftUpdate implemented we can never
159 // Update otherwise).
160 if (!existing_registration->active_version()) {
161 UpdateAndContinue(status);
162 return;
164 ResolvePromise(
165 status, existing_registration, existing_registration->active_version());
166 Complete(SERVICE_WORKER_OK);
167 return;
170 // "If serviceWorkerRegistration is null..." create a new registration.
171 if (!existing_registration.get()) {
172 RegisterAndContinue(SERVICE_WORKER_OK);
173 return;
176 // On script URL mismatch, "set serviceWorkerRegistration.scriptUrl to
177 // script." We accomplish this by deleting the existing registration and
178 // registering a new one.
179 // TODO(falken): Match the spec. We now throw away the active_version_ and
180 // pending_version_ of the existing registration, which isn't in the spec.
181 // TODO(michaeln): Deactivate the live existing_registration object and
182 // eventually call storage->DeleteVersionResources()
183 // when it no longer has any controllees.
184 context_->storage()->DeleteRegistration(
185 existing_registration->id(),
186 base::Bind(&ServiceWorkerRegisterJob::RegisterAndContinue,
187 weak_factory_.GetWeakPtr()));
190 // Creates a new ServiceWorkerRegistration.
191 void ServiceWorkerRegisterJob::RegisterAndContinue(
192 ServiceWorkerStatusCode status) {
193 SetPhase(REGISTER);
194 if (status != SERVICE_WORKER_OK) {
195 // Abort this registration job.
196 Complete(status);
197 return;
200 set_registration(new ServiceWorkerRegistration(
201 pattern_, script_url_, context_->storage()->NewRegistrationId(),
202 context_));
203 context_->storage()->NotifyInstallingRegistration(registration());
204 UpdateAndContinue(SERVICE_WORKER_OK);
207 // This function corresponds to the spec's _Update algorithm.
208 void ServiceWorkerRegisterJob::UpdateAndContinue(
209 ServiceWorkerStatusCode status) {
210 SetPhase(UPDATE);
211 if (status != SERVICE_WORKER_OK) {
212 // Abort this registration job.
213 Complete(status);
214 return;
217 // TODO(falken): "If serviceWorkerRegistration.pendingWorker is not null..."
218 // then terminate the pending worker. It doesn't make sense to implement yet
219 // since we always activate the worker if install completed, so there can be
220 // no pending worker at this point.
221 DCHECK(!registration()->pending_version());
223 // "Let serviceWorker be a newly-created ServiceWorker object..." and start
224 // the worker.
225 set_pending_version(new ServiceWorkerVersion(
226 registration(), context_->storage()->NewVersionId(), context_));
228 // TODO(michaeln): Start the worker into a paused state where the
229 // script resource is downloaded but not yet evaluated.
230 pending_version()->StartWorkerWithCandidateProcesses(
231 pending_process_ids_,
232 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
233 weak_factory_.GetWeakPtr()));
236 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
237 ServiceWorkerStatusCode status) {
238 // "If serviceWorker fails to start up..." then reject the promise with an
239 // error and abort.
240 if (status != SERVICE_WORKER_OK) {
241 Complete(status);
242 return;
245 // TODO(michaeln): Compare the old and new script.
246 // If different unpause the worker and continue with
247 // the job. If the same ResolvePromise with the current
248 // version and complete the job, throwing away the new version
249 // since there's nothing new.
251 // "Resolve promise with serviceWorker."
252 // Although the spec doesn't set pendingWorker until after resolving the
253 // promise, our system's resolving works by passing ServiceWorkerRegistration
254 // to the callbacks, so pendingWorker must be set first.
255 DCHECK(!registration()->pending_version());
256 registration()->set_pending_version(pending_version());
257 ResolvePromise(status, registration(), pending_version());
259 AssociatePendingVersionToDocuments(pending_version());
261 InstallAndContinue();
264 // This function corresponds to the spec's _Install algorithm.
265 void ServiceWorkerRegisterJob::InstallAndContinue() {
266 SetPhase(INSTALL);
267 // "Set serviceWorkerRegistration.pendingWorker._state to installing."
268 // "Fire install event on the associated ServiceWorkerGlobalScope object."
269 pending_version()->DispatchInstallEvent(
271 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
272 weak_factory_.GetWeakPtr()));
275 void ServiceWorkerRegisterJob::OnInstallFinished(
276 ServiceWorkerStatusCode status) {
277 // "If any handler called waitUntil()..." and the resulting promise
278 // is rejected, abort.
279 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
280 // unexpectedly terminated) we may want to retry sending the event again.
281 if (status != SERVICE_WORKER_OK) {
282 Complete(status);
283 return;
286 SetPhase(STORE);
287 context_->storage()->StoreRegistration(
288 registration(),
289 pending_version(),
290 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
291 weak_factory_.GetWeakPtr()));
294 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
295 ServiceWorkerStatusCode status) {
296 if (status != SERVICE_WORKER_OK) {
297 Complete(status);
298 return;
301 ActivateAndContinue();
304 // This function corresponds to the spec's _Activate algorithm.
305 void ServiceWorkerRegisterJob::ActivateAndContinue() {
306 SetPhase(ACTIVATE);
308 // "If existingWorker is not null, then: wait for exitingWorker to finish
309 // handling any in-progress requests."
310 // See if we already have an active_version for the scope and it has
311 // controllee documents (if so activating the new version should wait
312 // until we have no documents controlled by the version).
313 if (registration()->active_version() &&
314 registration()->active_version()->HasControllee()) {
315 // TODO(kinuko,falken): Currently we immediately return if the existing
316 // registration already has an active version, so we shouldn't come
317 // this way.
318 NOTREACHED();
319 // TODO(falken): Register an continuation task to wait for NoControllees
320 // notification so that we can resume activation later (see comments
321 // in ServiceWorkerVersion::RemoveControllee).
322 Complete(SERVICE_WORKER_OK);
323 return;
326 // "Set serviceWorkerRegistration.pendingWorker to null."
327 // "Set serviceWorkerRegistration.activeWorker to activatingWorker."
328 registration()->set_pending_version(NULL);
329 AssociatePendingVersionToDocuments(NULL);
330 DCHECK(!registration()->active_version());
331 registration()->set_active_version(pending_version());
333 // "Set serviceWorkerRegistration.activeWorker._state to activating."
334 // "Fire activate event on the associated ServiceWorkerGlobalScope object."
335 // "Set serviceWorkerRegistration.activeWorker._state to active."
336 pending_version()->DispatchActivateEvent(
337 base::Bind(&ServiceWorkerRegisterJob::OnActivateFinished,
338 weak_factory_.GetWeakPtr()));
341 void ServiceWorkerRegisterJob::OnActivateFinished(
342 ServiceWorkerStatusCode status) {
343 // "If any handler called waitUntil()..." and the resulting promise
344 // is rejected, abort.
345 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
346 // unexpectedly terminated) we may want to retry sending the event again.
347 if (status != SERVICE_WORKER_OK) {
348 registration()->set_active_version(NULL);
349 Complete(status);
350 return;
352 context_->storage()->UpdateToActiveState(
353 registration(),
354 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
355 Complete(SERVICE_WORKER_OK);
358 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
359 SetPhase(COMPLETE);
360 if (status != SERVICE_WORKER_OK) {
361 if (registration() && registration()->pending_version()) {
362 AssociatePendingVersionToDocuments(NULL);
363 registration()->set_pending_version(NULL);
364 // TODO(michaeln): Take care of deleteting the version's
365 // script resources too.
367 if (registration() && !registration()->active_version()) {
368 context_->storage()->DeleteRegistration(
369 registration()->id(),
370 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
372 if (!is_promise_resolved_)
373 ResolvePromise(status, NULL, NULL);
375 DCHECK(callbacks_.empty());
376 context_->storage()->NotifyDoneInstallingRegistration(registration());
377 context_->job_coordinator()->FinishJob(pattern_, this);
380 void ServiceWorkerRegisterJob::ResolvePromise(
381 ServiceWorkerStatusCode status,
382 ServiceWorkerRegistration* registration,
383 ServiceWorkerVersion* version) {
384 DCHECK(!is_promise_resolved_);
385 is_promise_resolved_ = true;
386 promise_resolved_status_ = status;
387 promise_resolved_registration_ = registration;
388 promise_resolved_version_ = version;
389 for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
390 it != callbacks_.end();
391 ++it) {
392 it->Run(status, registration, version);
394 callbacks_.clear();
397 void ServiceWorkerRegisterJob::AssociatePendingVersionToDocuments(
398 ServiceWorkerVersion* version) {
399 // TODO(michaeln): This needs to respect the longest prefix wins
400 // when it comes to finding a registration for a document url.
401 // This should should utilize storage->FindRegistrationForDocument().
402 for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
403 context_->GetProviderHostIterator();
404 !it->IsAtEnd();
405 it->Advance()) {
406 ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
407 if (ServiceWorkerUtils::ScopeMatches(pattern_,
408 provider_host->document_url()))
409 provider_host->SetPendingVersion(version);
413 } // namespace content