Update broken references to image assets
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_register_job.cc
blob16a192858271e92bc9532362686253dc9bb308fb
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/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "content/browser/service_worker/service_worker_context_core.h"
13 #include "content/browser/service_worker/service_worker_job_coordinator.h"
14 #include "content/browser/service_worker/service_worker_metrics.h"
15 #include "content/browser/service_worker/service_worker_registration.h"
16 #include "content/browser/service_worker/service_worker_storage.h"
17 #include "content/common/service_worker/service_worker_types.h"
18 #include "content/common/service_worker/service_worker_utils.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "net/base/net_errors.h"
22 namespace content {
24 namespace {
26 void RunSoon(const base::Closure& closure) {
27 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
30 } // namespace
32 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
34 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
35 base::WeakPtr<ServiceWorkerContextCore> context,
36 const GURL& pattern,
37 const GURL& script_url)
38 : context_(context),
39 job_type_(REGISTRATION_JOB),
40 pattern_(pattern),
41 script_url_(script_url),
42 phase_(INITIAL),
43 doom_installing_worker_(false),
44 is_promise_resolved_(false),
45 should_uninstall_on_failure_(false),
46 force_bypass_cache_(false),
47 promise_resolved_status_(SERVICE_WORKER_OK),
48 weak_factory_(this) {
51 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
52 base::WeakPtr<ServiceWorkerContextCore> context,
53 ServiceWorkerRegistration* registration,
54 bool force_bypass_cache)
55 : context_(context),
56 job_type_(UPDATE_JOB),
57 pattern_(registration->pattern()),
58 script_url_(registration->GetNewestVersion()->script_url()),
59 phase_(INITIAL),
60 doom_installing_worker_(false),
61 is_promise_resolved_(false),
62 should_uninstall_on_failure_(false),
63 force_bypass_cache_(force_bypass_cache),
64 promise_resolved_status_(SERVICE_WORKER_OK),
65 weak_factory_(this) {
66 internal_.registration = registration;
69 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
70 DCHECK(!context_ ||
71 phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
72 << "Jobs should only be interrupted during shutdown.";
75 void ServiceWorkerRegisterJob::AddCallback(
76 const RegistrationCallback& callback,
77 ServiceWorkerProviderHost* provider_host) {
78 if (!is_promise_resolved_) {
79 callbacks_.push_back(callback);
80 if (provider_host)
81 provider_host->AddScopedProcessReferenceToPattern(pattern_);
82 return;
84 RunSoon(base::Bind(callback, promise_resolved_status_,
85 promise_resolved_status_message_,
86 promise_resolved_registration_));
89 void ServiceWorkerRegisterJob::Start() {
90 BrowserThread::PostAfterStartupTask(
91 FROM_HERE, base::ThreadTaskRunnerHandle::Get(),
92 base::Bind(&ServiceWorkerRegisterJob::StartImpl,
93 weak_factory_.GetWeakPtr()));
96 void ServiceWorkerRegisterJob::StartImpl() {
97 SetPhase(START);
98 ServiceWorkerStorage::FindRegistrationCallback next_step;
99 if (job_type_ == REGISTRATION_JOB) {
100 next_step = base::Bind(
101 &ServiceWorkerRegisterJob::ContinueWithRegistration,
102 weak_factory_.GetWeakPtr());
103 } else {
104 next_step = base::Bind(
105 &ServiceWorkerRegisterJob::ContinueWithUpdate,
106 weak_factory_.GetWeakPtr());
109 scoped_refptr<ServiceWorkerRegistration> registration =
110 context_->storage()->GetUninstallingRegistration(pattern_);
111 if (registration.get())
112 RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
113 else
114 context_->storage()->FindRegistrationForPattern(pattern_, next_step);
117 void ServiceWorkerRegisterJob::Abort() {
118 SetPhase(ABORT);
119 CompleteInternal(SERVICE_WORKER_ERROR_ABORT, std::string());
120 // Don't have to call FinishJob() because the caller takes care of removing
121 // the jobs from the queue.
124 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) const {
125 if (job->GetType() != GetType())
126 return false;
127 ServiceWorkerRegisterJob* register_job =
128 static_cast<ServiceWorkerRegisterJob*>(job);
129 return register_job->pattern_ == pattern_ &&
130 register_job->script_url_ == script_url_;
133 RegistrationJobType ServiceWorkerRegisterJob::GetType() const {
134 return job_type_;
137 void ServiceWorkerRegisterJob::DoomInstallingWorker() {
138 doom_installing_worker_ = true;
139 if (phase_ == INSTALL)
140 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED, std::string());
143 ServiceWorkerRegisterJob::Internal::Internal() {}
145 ServiceWorkerRegisterJob::Internal::~Internal() {}
147 void ServiceWorkerRegisterJob::set_registration(
148 const scoped_refptr<ServiceWorkerRegistration>& registration) {
149 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
150 DCHECK(!internal_.registration.get());
151 internal_.registration = registration;
154 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
155 DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
156 return internal_.registration.get();
159 void ServiceWorkerRegisterJob::set_new_version(
160 ServiceWorkerVersion* version) {
161 DCHECK(phase_ == UPDATE) << phase_;
162 DCHECK(!internal_.new_version.get());
163 internal_.new_version = version;
166 ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
167 DCHECK(phase_ >= UPDATE) << phase_;
168 return internal_.new_version.get();
171 void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
172 switch (phase) {
173 case INITIAL:
174 NOTREACHED();
175 break;
176 case START:
177 DCHECK(phase_ == INITIAL) << phase_;
178 break;
179 case REGISTER:
180 DCHECK(phase_ == START) << phase_;
181 break;
182 case UPDATE:
183 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
184 break;
185 case INSTALL:
186 DCHECK(phase_ == UPDATE) << phase_;
187 break;
188 case STORE:
189 DCHECK(phase_ == INSTALL) << phase_;
190 break;
191 case COMPLETE:
192 DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
193 break;
194 case ABORT:
195 break;
197 phase_ = phase;
200 // This function corresponds to the steps in [[Register]] following
201 // "Let registration be the result of running the [[GetRegistration]] algorithm.
202 // Throughout this file, comments in quotes are excerpts from the spec.
203 void ServiceWorkerRegisterJob::ContinueWithRegistration(
204 ServiceWorkerStatusCode status,
205 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
206 DCHECK_EQ(REGISTRATION_JOB, job_type_);
207 if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
208 Complete(status);
209 return;
212 if (!existing_registration.get() || existing_registration->is_uninstalled()) {
213 RegisterAndContinue();
214 return;
217 DCHECK(existing_registration->GetNewestVersion());
218 // "If scriptURL is equal to registration.[[ScriptURL]], then:"
219 if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
220 // "Set registration.[[Uninstalling]] to false."
221 existing_registration->AbortPendingClear(base::Bind(
222 &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
223 weak_factory_.GetWeakPtr(),
224 existing_registration));
225 return;
228 if (existing_registration->is_uninstalling()) {
229 existing_registration->AbortPendingClear(base::Bind(
230 &ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration,
231 weak_factory_.GetWeakPtr(),
232 existing_registration));
233 return;
236 // "Return the result of running the [[Update]] algorithm, or its equivalent,
237 // passing registration as the argument."
238 set_registration(existing_registration);
239 UpdateAndContinue();
242 void ServiceWorkerRegisterJob::ContinueWithUpdate(
243 ServiceWorkerStatusCode status,
244 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
245 DCHECK_EQ(UPDATE_JOB, job_type_);
246 if (status != SERVICE_WORKER_OK) {
247 Complete(status);
248 return;
251 if (existing_registration.get() != registration()) {
252 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
253 return;
256 // A previous job may have unregistered or installed a new version to this
257 // registration.
258 if (registration()->is_uninstalling() ||
259 registration()->GetNewestVersion()->script_url() != script_url_) {
260 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
261 return;
264 // TODO(michaeln): If the last update check was less than 24 hours
265 // ago, depending on the freshness of the cached worker script we
266 // may be able to complete the update job right here.
268 UpdateAndContinue();
271 // Creates a new ServiceWorkerRegistration.
272 void ServiceWorkerRegisterJob::RegisterAndContinue() {
273 SetPhase(REGISTER);
275 set_registration(new ServiceWorkerRegistration(
276 pattern_, context_->storage()->NewRegistrationId(), context_));
277 AddRegistrationToMatchingProviderHosts(registration());
278 UpdateAndContinue();
281 void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration(
282 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
283 ServiceWorkerStatusCode status) {
284 if (status != SERVICE_WORKER_OK) {
285 Complete(status);
286 return;
288 should_uninstall_on_failure_ = true;
289 set_registration(existing_registration);
290 UpdateAndContinue();
293 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
294 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
295 ServiceWorkerStatusCode status) {
296 if (status != SERVICE_WORKER_OK) {
297 Complete(status);
298 return;
300 set_registration(existing_registration);
302 // "If newestWorker is not null, and scriptURL is equal to
303 // newestWorker.scriptURL, then:
304 // Return a promise resolved with registration."
305 // We resolve only if there's an active version. If there's not,
306 // then there is either no version or only a waiting version from
307 // the last browser session; it makes sense to proceed with registration in
308 // either case.
309 DCHECK(!existing_registration->installing_version());
310 if (existing_registration->active_version()) {
311 ResolvePromise(status, std::string(), existing_registration.get());
312 Complete(SERVICE_WORKER_OK);
313 return;
316 // "Return the result of running the [[Update]] algorithm, or its equivalent,
317 // passing registration as the argument."
318 UpdateAndContinue();
321 // This function corresponds to the spec's [[Update]] algorithm.
322 void ServiceWorkerRegisterJob::UpdateAndContinue() {
323 SetPhase(UPDATE);
324 context_->storage()->NotifyInstallingRegistration(registration());
326 // "Let worker be a new ServiceWorker object..." and start
327 // the worker.
328 set_new_version(new ServiceWorkerVersion(registration(),
329 script_url_,
330 context_->storage()->NewVersionId(),
331 context_));
332 new_version()->set_force_bypass_cache_for_scripts(force_bypass_cache_);
333 new_version()->StartWorker(
334 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
335 weak_factory_.GetWeakPtr()));
338 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
339 ServiceWorkerStatusCode status) {
340 if (status == SERVICE_WORKER_OK) {
341 InstallAndContinue();
342 return;
345 // The updated worker is identical to the incumbent.
346 if (status == SERVICE_WORKER_ERROR_EXISTS) {
347 // Only bump the last check time when we've bypassed the browser cache.
348 base::TimeDelta time_since_last_check =
349 base::Time::Now() - registration()->last_update_check();
350 if (time_since_last_check > base::TimeDelta::FromHours(
351 kServiceWorkerScriptMaxCacheAgeInHours) ||
352 new_version()->force_bypass_cache_for_scripts()) {
353 registration()->set_last_update_check(base::Time::Now());
354 context_->storage()->UpdateLastUpdateCheckTime(registration());
357 ResolvePromise(SERVICE_WORKER_OK, std::string(), registration());
358 Complete(status, "The updated worker is identical to the incumbent.");
359 return;
362 // "If serviceWorker fails to start up..." then reject the promise with an
363 // error and abort.
364 if (status == SERVICE_WORKER_ERROR_TIMEOUT) {
365 Complete(status, "Timed out while trying to start the Service Worker.");
366 return;
369 const net::URLRequestStatus& main_script_status =
370 new_version()->script_cache_map()->main_script_status();
371 std::string message;
372 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
373 message = new_version()->script_cache_map()->main_script_status_message();
374 if (message.empty())
375 message = kFetchScriptError;
377 Complete(status, message);
380 // This function corresponds to the spec's [[Install]] algorithm.
381 void ServiceWorkerRegisterJob::InstallAndContinue() {
382 SetPhase(INSTALL);
384 // "Set registration.installingWorker to worker."
385 DCHECK(!registration()->installing_version());
386 registration()->SetInstallingVersion(new_version());
388 // "Run the Update State algorithm passing registration's installing worker
389 // and installing as the arguments."
390 new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
392 // "Resolve registrationPromise with registration."
393 ResolvePromise(SERVICE_WORKER_OK, std::string(), registration());
395 // "Fire a simple event named updatefound..."
396 registration()->NotifyUpdateFound();
398 // "Fire an event named install..."
399 new_version()->DispatchInstallEvent(
400 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
401 weak_factory_.GetWeakPtr()));
403 // A subsequent registration job may terminate our installing worker. It can
404 // only do so after we've started the worker and dispatched the install
405 // event, as those are atomic substeps in the [[Install]] algorithm.
406 if (doom_installing_worker_)
407 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
410 void ServiceWorkerRegisterJob::OnInstallFinished(
411 ServiceWorkerStatusCode status) {
412 ServiceWorkerMetrics::RecordInstallEventStatus(status);
414 if (status != SERVICE_WORKER_OK) {
415 // "8. If installFailed is true, then:..."
416 Complete(status);
417 return;
420 SetPhase(STORE);
421 registration()->set_last_update_check(base::Time::Now());
422 context_->storage()->StoreRegistration(
423 registration(),
424 new_version(),
425 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
426 weak_factory_.GetWeakPtr()));
429 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
430 ServiceWorkerStatusCode status) {
431 if (status != SERVICE_WORKER_OK) {
432 Complete(status);
433 return;
436 // "9. If registration.waitingWorker is not null, then:..."
437 if (registration()->waiting_version()) {
438 // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
439 // and "redundant" as the arguments."
440 registration()->waiting_version()->SetStatus(
441 ServiceWorkerVersion::REDUNDANT);
444 // "10. Set registration.waitingWorker to registration.installingWorker."
445 // "11. Set registration.installingWorker to null."
446 registration()->SetWaitingVersion(new_version());
448 // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
449 // and "installed" as the arguments."
450 new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
452 // "If registration's waiting worker's skip waiting flag is set:" then
453 // activate the worker immediately otherwise "wait until no service worker
454 // client is using registration as their service worker registration."
455 registration()->ActivateWaitingVersionWhenReady();
457 Complete(SERVICE_WORKER_OK);
460 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
461 Complete(status, std::string());
464 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status,
465 const std::string& status_message) {
466 CompleteInternal(status, status_message);
467 context_->job_coordinator()->FinishJob(pattern_, this);
470 void ServiceWorkerRegisterJob::CompleteInternal(
471 ServiceWorkerStatusCode status,
472 const std::string& status_message) {
473 SetPhase(COMPLETE);
474 if (status != SERVICE_WORKER_OK) {
475 if (registration()) {
476 if (should_uninstall_on_failure_)
477 registration()->ClearWhenReady();
478 if (new_version()) {
479 if (status == SERVICE_WORKER_ERROR_EXISTS)
480 new_version()->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS);
481 else
482 new_version()->ReportError(status, status_message);
483 registration()->UnsetVersion(new_version());
484 new_version()->Doom();
486 if (!registration()->waiting_version() &&
487 !registration()->active_version()) {
488 registration()->NotifyRegistrationFailed();
489 context_->storage()->DeleteRegistration(
490 registration()->id(),
491 registration()->pattern().GetOrigin(),
492 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
495 if (!is_promise_resolved_)
496 ResolvePromise(status, status_message, NULL);
498 DCHECK(callbacks_.empty());
499 if (registration()) {
500 context_->storage()->NotifyDoneInstallingRegistration(
501 registration(), new_version(), status);
502 if (registration()->waiting_version() || registration()->active_version())
503 registration()->set_is_uninstalled(false);
507 void ServiceWorkerRegisterJob::ResolvePromise(
508 ServiceWorkerStatusCode status,
509 const std::string& status_message,
510 ServiceWorkerRegistration* registration) {
511 DCHECK(!is_promise_resolved_);
513 is_promise_resolved_ = true;
514 promise_resolved_status_ = status;
515 promise_resolved_status_message_ = status_message,
516 promise_resolved_registration_ = registration;
517 for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
518 it != callbacks_.end();
519 ++it) {
520 it->Run(status, status_message, registration);
522 callbacks_.clear();
525 void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts(
526 ServiceWorkerRegistration* registration) {
527 DCHECK(registration);
528 for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
529 context_->GetProviderHostIterator();
530 !it->IsAtEnd(); it->Advance()) {
531 ServiceWorkerProviderHost* host = it->GetProviderHost();
532 if (host->IsHostToRunningServiceWorker())
533 continue;
534 if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(),
535 host->document_url()))
536 continue;
537 host->AddMatchingRegistration(registration);
541 } // namespace content