Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_register_job.cc
blob4f6620aecc88af04b43d6bddf4b4931c962104d6
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"
15 #include "content/common/service_worker/service_worker_types.h"
16 #include "net/base/net_errors.h"
18 namespace content {
20 namespace {
22 void RunSoon(const base::Closure& closure) {
23 base::MessageLoop::current()->PostTask(FROM_HERE, closure);
26 } // namespace
28 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
30 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
31 base::WeakPtr<ServiceWorkerContextCore> context,
32 const GURL& pattern,
33 const GURL& script_url)
34 : context_(context),
35 job_type_(REGISTRATION_JOB),
36 pattern_(pattern),
37 script_url_(script_url),
38 phase_(INITIAL),
39 doom_installing_worker_(false),
40 is_promise_resolved_(false),
41 should_uninstall_on_failure_(false),
42 promise_resolved_status_(SERVICE_WORKER_OK),
43 weak_factory_(this) {
46 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
47 base::WeakPtr<ServiceWorkerContextCore> context,
48 ServiceWorkerRegistration* registration)
49 : context_(context),
50 job_type_(UPDATE_JOB),
51 pattern_(registration->pattern()),
52 script_url_(registration->GetNewestVersion()->script_url()),
53 phase_(INITIAL),
54 doom_installing_worker_(false),
55 is_promise_resolved_(false),
56 should_uninstall_on_failure_(false),
57 promise_resolved_status_(SERVICE_WORKER_OK),
58 weak_factory_(this) {
59 internal_.registration = registration;
62 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
63 DCHECK(!context_ ||
64 phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
65 << "Jobs should only be interrupted during shutdown.";
68 void ServiceWorkerRegisterJob::AddCallback(
69 const RegistrationCallback& callback,
70 ServiceWorkerProviderHost* provider_host) {
71 if (!is_promise_resolved_) {
72 callbacks_.push_back(callback);
73 if (provider_host)
74 provider_host->AddScopedProcessReferenceToPattern(pattern_);
75 return;
77 RunSoon(base::Bind(callback, promise_resolved_status_,
78 promise_resolved_status_message_,
79 promise_resolved_registration_));
82 void ServiceWorkerRegisterJob::Start() {
83 SetPhase(START);
84 ServiceWorkerStorage::FindRegistrationCallback next_step;
85 if (job_type_ == REGISTRATION_JOB) {
86 next_step = base::Bind(
87 &ServiceWorkerRegisterJob::ContinueWithRegistration,
88 weak_factory_.GetWeakPtr());
89 } else {
90 next_step = base::Bind(
91 &ServiceWorkerRegisterJob::ContinueWithUpdate,
92 weak_factory_.GetWeakPtr());
95 scoped_refptr<ServiceWorkerRegistration> registration =
96 context_->storage()->GetUninstallingRegistration(pattern_);
97 if (registration.get())
98 RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
99 else
100 context_->storage()->FindRegistrationForPattern(pattern_, next_step);
103 void ServiceWorkerRegisterJob::Abort() {
104 SetPhase(ABORT);
105 CompleteInternal(SERVICE_WORKER_ERROR_ABORT, std::string());
106 // Don't have to call FinishJob() because the caller takes care of removing
107 // the jobs from the queue.
110 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) const {
111 if (job->GetType() != GetType())
112 return false;
113 ServiceWorkerRegisterJob* register_job =
114 static_cast<ServiceWorkerRegisterJob*>(job);
115 return register_job->pattern_ == pattern_ &&
116 register_job->script_url_ == script_url_;
119 RegistrationJobType ServiceWorkerRegisterJob::GetType() const {
120 return job_type_;
123 void ServiceWorkerRegisterJob::DoomInstallingWorker() {
124 doom_installing_worker_ = true;
125 if (phase_ == INSTALL)
126 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED, std::string());
129 ServiceWorkerRegisterJob::Internal::Internal() {}
131 ServiceWorkerRegisterJob::Internal::~Internal() {}
133 void ServiceWorkerRegisterJob::set_registration(
134 const scoped_refptr<ServiceWorkerRegistration>& registration) {
135 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
136 DCHECK(!internal_.registration.get());
137 internal_.registration = registration;
140 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
141 DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
142 return internal_.registration.get();
145 void ServiceWorkerRegisterJob::set_new_version(
146 ServiceWorkerVersion* version) {
147 DCHECK(phase_ == UPDATE) << phase_;
148 DCHECK(!internal_.new_version.get());
149 internal_.new_version = version;
152 ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
153 DCHECK(phase_ >= UPDATE) << phase_;
154 return internal_.new_version.get();
157 void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
158 switch (phase) {
159 case INITIAL:
160 NOTREACHED();
161 break;
162 case START:
163 DCHECK(phase_ == INITIAL) << phase_;
164 break;
165 case REGISTER:
166 DCHECK(phase_ == START) << phase_;
167 break;
168 case UPDATE:
169 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
170 break;
171 case INSTALL:
172 DCHECK(phase_ == UPDATE) << phase_;
173 break;
174 case STORE:
175 DCHECK(phase_ == INSTALL) << phase_;
176 break;
177 case COMPLETE:
178 DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
179 break;
180 case ABORT:
181 break;
183 phase_ = phase;
186 // This function corresponds to the steps in [[Register]] following
187 // "Let registration be the result of running the [[GetRegistration]] algorithm.
188 // Throughout this file, comments in quotes are excerpts from the spec.
189 void ServiceWorkerRegisterJob::ContinueWithRegistration(
190 ServiceWorkerStatusCode status,
191 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
192 DCHECK_EQ(REGISTRATION_JOB, job_type_);
193 if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
194 Complete(status);
195 return;
198 if (!existing_registration.get() || existing_registration->is_uninstalled()) {
199 RegisterAndContinue();
200 return;
203 DCHECK(existing_registration->GetNewestVersion());
204 // "If scriptURL is equal to registration.[[ScriptURL]], then:"
205 if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
206 // "Set registration.[[Uninstalling]] to false."
207 existing_registration->AbortPendingClear(base::Bind(
208 &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
209 weak_factory_.GetWeakPtr(),
210 existing_registration));
211 return;
214 if (existing_registration->is_uninstalling()) {
215 existing_registration->AbortPendingClear(base::Bind(
216 &ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration,
217 weak_factory_.GetWeakPtr(),
218 existing_registration));
219 return;
222 // "Return the result of running the [[Update]] algorithm, or its equivalent,
223 // passing registration as the argument."
224 set_registration(existing_registration);
225 UpdateAndContinue();
228 void ServiceWorkerRegisterJob::ContinueWithUpdate(
229 ServiceWorkerStatusCode status,
230 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
231 DCHECK_EQ(UPDATE_JOB, job_type_);
232 if (status != SERVICE_WORKER_OK) {
233 Complete(status);
234 return;
237 if (existing_registration.get() != registration()) {
238 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
239 return;
242 // A previous job may have unregistered or installed a new version to this
243 // registration.
244 if (registration()->is_uninstalling() ||
245 registration()->GetNewestVersion()->script_url() != script_url_) {
246 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
247 return;
250 // TODO(michaeln): If the last update check was less than 24 hours
251 // ago, depending on the freshness of the cached worker script we
252 // may be able to complete the update job right here.
254 UpdateAndContinue();
257 // Creates a new ServiceWorkerRegistration.
258 void ServiceWorkerRegisterJob::RegisterAndContinue() {
259 SetPhase(REGISTER);
261 set_registration(new ServiceWorkerRegistration(
262 pattern_, context_->storage()->NewRegistrationId(), context_));
263 AddRegistrationToMatchingProviderHosts(registration());
264 UpdateAndContinue();
267 void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration(
268 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
269 ServiceWorkerStatusCode status) {
270 if (status != SERVICE_WORKER_OK) {
271 Complete(status);
272 return;
274 should_uninstall_on_failure_ = true;
275 set_registration(existing_registration);
276 UpdateAndContinue();
279 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
280 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
281 ServiceWorkerStatusCode status) {
282 if (status != SERVICE_WORKER_OK) {
283 Complete(status);
284 return;
286 set_registration(existing_registration);
288 // "If newestWorker is not null, and scriptURL is equal to
289 // newestWorker.scriptURL, then:
290 // Return a promise resolved with registration."
291 // We resolve only if there's an active version. If there's not,
292 // then there is either no version or only a waiting version from
293 // the last browser session; it makes sense to proceed with registration in
294 // either case.
295 DCHECK(!existing_registration->installing_version());
296 if (existing_registration->active_version()) {
297 ResolvePromise(status, std::string(), existing_registration.get());
298 Complete(SERVICE_WORKER_OK);
299 return;
302 // "Return the result of running the [[Update]] algorithm, or its equivalent,
303 // passing registration as the argument."
304 UpdateAndContinue();
307 // This function corresponds to the spec's [[Update]] algorithm.
308 void ServiceWorkerRegisterJob::UpdateAndContinue() {
309 SetPhase(UPDATE);
310 context_->storage()->NotifyInstallingRegistration(registration());
312 // "Let worker be a new ServiceWorker object..." and start
313 // the worker.
314 set_new_version(new ServiceWorkerVersion(registration(),
315 script_url_,
316 context_->storage()->NewVersionId(),
317 context_));
319 bool pause_after_download = job_type_ == UPDATE_JOB;
320 if (pause_after_download)
321 new_version()->embedded_worker()->AddListener(this);
322 new_version()->StartWorker(
323 pause_after_download,
324 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
325 weak_factory_.GetWeakPtr()));
328 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
329 ServiceWorkerStatusCode status) {
330 if (status == SERVICE_WORKER_OK) {
331 InstallAndContinue();
332 return;
335 // "If serviceWorker fails to start up..." then reject the promise with an
336 // error and abort. When there is a main script network error, the status will
337 // be updated to a more specific one.
338 const net::URLRequestStatus& main_script_status =
339 new_version()->script_cache_map()->main_script_status();
340 std::string message;
341 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
342 switch (main_script_status.error()) {
343 case net::ERR_INSECURE_RESPONSE:
344 case net::ERR_UNSAFE_REDIRECT:
345 status = SERVICE_WORKER_ERROR_SECURITY;
346 break;
347 case net::ERR_ABORTED:
348 status = SERVICE_WORKER_ERROR_ABORT;
349 break;
350 default:
351 status = SERVICE_WORKER_ERROR_NETWORK;
353 message = new_version()->script_cache_map()->main_script_status_message();
354 if (message.empty())
355 message = kFetchScriptError;
358 if (status == SERVICE_WORKER_ERROR_TIMEOUT)
359 message = "Timed out while trying to start the Service Worker.";
361 Complete(status, message);
364 // This function corresponds to the spec's [[Install]] algorithm.
365 void ServiceWorkerRegisterJob::InstallAndContinue() {
366 SetPhase(INSTALL);
368 // "Set registration.installingWorker to worker."
369 DCHECK(!registration()->installing_version());
370 registration()->SetInstallingVersion(new_version());
372 // "Run the Update State algorithm passing registration's installing worker
373 // and installing as the arguments."
374 new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
376 // "Resolve registrationPromise with registration."
377 ResolvePromise(SERVICE_WORKER_OK, std::string(), registration());
379 // "Fire a simple event named updatefound..."
380 registration()->NotifyUpdateFound();
382 // "Fire an event named install..."
383 new_version()->DispatchInstallEvent(
384 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
385 weak_factory_.GetWeakPtr()));
387 // A subsequent registration job may terminate our installing worker. It can
388 // only do so after we've started the worker and dispatched the install
389 // event, as those are atomic substeps in the [[Install]] algorithm.
390 if (doom_installing_worker_)
391 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
394 void ServiceWorkerRegisterJob::OnInstallFinished(
395 ServiceWorkerStatusCode status) {
396 if (status != SERVICE_WORKER_OK) {
397 // "8. If installFailed is true, then:..."
398 Complete(status);
399 return;
402 SetPhase(STORE);
403 registration()->set_last_update_check(base::Time::Now());
404 context_->storage()->StoreRegistration(
405 registration(),
406 new_version(),
407 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
408 weak_factory_.GetWeakPtr()));
411 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
412 ServiceWorkerStatusCode status) {
413 if (status != SERVICE_WORKER_OK) {
414 Complete(status);
415 return;
418 // "9. If registration.waitingWorker is not null, then:..."
419 if (registration()->waiting_version()) {
420 // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
421 // and "redundant" as the arguments."
422 registration()->waiting_version()->SetStatus(
423 ServiceWorkerVersion::REDUNDANT);
426 // "10. Set registration.waitingWorker to registration.installingWorker."
427 // "11. Set registration.installingWorker to null."
428 registration()->SetWaitingVersion(new_version());
430 // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
431 // and "installed" as the arguments."
432 new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
434 // "If registration's waiting worker's skip waiting flag is set:" then
435 // activate the worker immediately otherwise "wait until no service worker
436 // client is using registration as their service worker registration."
437 registration()->ActivateWaitingVersionWhenReady();
439 Complete(SERVICE_WORKER_OK);
442 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
443 Complete(status, std::string());
446 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status,
447 const std::string& status_message) {
448 CompleteInternal(status, status_message);
449 context_->job_coordinator()->FinishJob(pattern_, this);
452 void ServiceWorkerRegisterJob::CompleteInternal(
453 ServiceWorkerStatusCode status,
454 const std::string& status_message) {
455 SetPhase(COMPLETE);
456 if (status != SERVICE_WORKER_OK) {
457 if (registration()) {
458 if (should_uninstall_on_failure_)
459 registration()->ClearWhenReady();
460 if (new_version()) {
461 if (status != SERVICE_WORKER_ERROR_EXISTS)
462 new_version()->ReportError(status, status_message);
463 registration()->UnsetVersion(new_version());
464 new_version()->Doom();
466 if (!registration()->waiting_version() &&
467 !registration()->active_version()) {
468 registration()->NotifyRegistrationFailed();
469 context_->storage()->DeleteRegistration(
470 registration()->id(),
471 registration()->pattern().GetOrigin(),
472 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
475 if (!is_promise_resolved_)
476 ResolvePromise(status, status_message, NULL);
478 DCHECK(callbacks_.empty());
479 if (registration()) {
480 context_->storage()->NotifyDoneInstallingRegistration(
481 registration(), new_version(), status);
483 if (new_version())
484 new_version()->embedded_worker()->RemoveListener(this);
487 void ServiceWorkerRegisterJob::ResolvePromise(
488 ServiceWorkerStatusCode status,
489 const std::string& status_message,
490 ServiceWorkerRegistration* registration) {
491 DCHECK(!is_promise_resolved_);
493 is_promise_resolved_ = true;
494 promise_resolved_status_ = status;
495 promise_resolved_status_message_ = status_message,
496 promise_resolved_registration_ = registration;
497 for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
498 it != callbacks_.end();
499 ++it) {
500 it->Run(status, status_message, registration);
502 callbacks_.clear();
505 void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
506 // This happens prior to OnStartWorkerFinished time.
507 scoped_refptr<ServiceWorkerVersion> most_recent_version =
508 registration()->waiting_version() ?
509 registration()->waiting_version() :
510 registration()->active_version();
511 DCHECK(most_recent_version.get());
512 int64 most_recent_script_id =
513 most_recent_version->script_cache_map()->LookupResourceId(script_url_);
514 int64 new_script_id =
515 new_version()->script_cache_map()->LookupResourceId(script_url_);
517 // TODO(michaeln): It would be better to compare as the new resource
518 // is being downloaded and to avoid writing it to disk until we know
519 // its needed.
520 context_->storage()->CompareScriptResources(
521 most_recent_script_id,
522 new_script_id,
523 base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
524 weak_factory_.GetWeakPtr()));
527 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
528 return false;
531 void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
532 ServiceWorkerStatusCode status,
533 bool are_equal) {
534 if (are_equal) {
535 // Only bump the last check time when we've bypassed the browser cache.
536 base::TimeDelta time_since_last_check =
537 base::Time::Now() - registration()->last_update_check();
538 if (time_since_last_check > base::TimeDelta::FromHours(24)) {
539 registration()->set_last_update_check(base::Time::Now());
540 context_->storage()->UpdateLastUpdateCheckTime(registration());
543 ResolvePromise(SERVICE_WORKER_OK, std::string(), registration());
544 Complete(SERVICE_WORKER_ERROR_EXISTS);
545 return;
548 // Proceed with really starting the worker.
549 new_version()->embedded_worker()->ResumeAfterDownload();
550 new_version()->embedded_worker()->RemoveListener(this);
553 void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts(
554 ServiceWorkerRegistration* registration) {
555 DCHECK(registration);
556 for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
557 context_->GetProviderHostIterator();
558 !it->IsAtEnd(); it->Advance()) {
559 ServiceWorkerProviderHost* host = it->GetProviderHost();
560 if (host->IsHostToRunningServiceWorker())
561 continue;
562 if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(),
563 host->document_url()))
564 continue;
565 host->AddMatchingRegistration(registration);
569 } // namespace content