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 "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/browser/service_worker/service_worker_utils.h"
18 #include "content/common/service_worker/service_worker_types.h"
19 #include "net/base/net_errors.h"
25 void RunSoon(const base::Closure
& closure
) {
26 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, closure
);
31 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType
;
33 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
34 base::WeakPtr
<ServiceWorkerContextCore
> context
,
36 const GURL
& script_url
)
38 job_type_(REGISTRATION_JOB
),
40 script_url_(script_url
),
42 doom_installing_worker_(false),
43 is_promise_resolved_(false),
44 should_uninstall_on_failure_(false),
45 force_bypass_cache_(false),
46 promise_resolved_status_(SERVICE_WORKER_OK
),
50 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
51 base::WeakPtr
<ServiceWorkerContextCore
> context
,
52 ServiceWorkerRegistration
* registration
,
53 bool force_bypass_cache
)
55 job_type_(UPDATE_JOB
),
56 pattern_(registration
->pattern()),
57 script_url_(registration
->GetNewestVersion()->script_url()),
59 doom_installing_worker_(false),
60 is_promise_resolved_(false),
61 should_uninstall_on_failure_(false),
62 force_bypass_cache_(force_bypass_cache
),
63 promise_resolved_status_(SERVICE_WORKER_OK
),
65 internal_
.registration
= registration
;
68 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
70 phase_
== INITIAL
|| phase_
== COMPLETE
|| phase_
== ABORT
)
71 << "Jobs should only be interrupted during shutdown.";
74 void ServiceWorkerRegisterJob::AddCallback(
75 const RegistrationCallback
& callback
,
76 ServiceWorkerProviderHost
* provider_host
) {
77 if (!is_promise_resolved_
) {
78 callbacks_
.push_back(callback
);
80 provider_host
->AddScopedProcessReferenceToPattern(pattern_
);
83 RunSoon(base::Bind(callback
, promise_resolved_status_
,
84 promise_resolved_status_message_
,
85 promise_resolved_registration_
));
88 void ServiceWorkerRegisterJob::Start() {
90 ServiceWorkerStorage::FindRegistrationCallback next_step
;
91 if (job_type_
== REGISTRATION_JOB
) {
92 next_step
= base::Bind(
93 &ServiceWorkerRegisterJob::ContinueWithRegistration
,
94 weak_factory_
.GetWeakPtr());
96 next_step
= base::Bind(
97 &ServiceWorkerRegisterJob::ContinueWithUpdate
,
98 weak_factory_
.GetWeakPtr());
101 scoped_refptr
<ServiceWorkerRegistration
> registration
=
102 context_
->storage()->GetUninstallingRegistration(pattern_
);
103 if (registration
.get())
104 RunSoon(base::Bind(next_step
, SERVICE_WORKER_OK
, registration
));
106 context_
->storage()->FindRegistrationForPattern(pattern_
, next_step
);
109 void ServiceWorkerRegisterJob::Abort() {
111 CompleteInternal(SERVICE_WORKER_ERROR_ABORT
, std::string());
112 // Don't have to call FinishJob() because the caller takes care of removing
113 // the jobs from the queue.
116 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase
* job
) const {
117 if (job
->GetType() != GetType())
119 ServiceWorkerRegisterJob
* register_job
=
120 static_cast<ServiceWorkerRegisterJob
*>(job
);
121 return register_job
->pattern_
== pattern_
&&
122 register_job
->script_url_
== script_url_
;
125 RegistrationJobType
ServiceWorkerRegisterJob::GetType() const {
129 void ServiceWorkerRegisterJob::DoomInstallingWorker() {
130 doom_installing_worker_
= true;
131 if (phase_
== INSTALL
)
132 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED
, std::string());
135 ServiceWorkerRegisterJob::Internal::Internal() {}
137 ServiceWorkerRegisterJob::Internal::~Internal() {}
139 void ServiceWorkerRegisterJob::set_registration(
140 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
141 DCHECK(phase_
== START
|| phase_
== REGISTER
) << phase_
;
142 DCHECK(!internal_
.registration
.get());
143 internal_
.registration
= registration
;
146 ServiceWorkerRegistration
* ServiceWorkerRegisterJob::registration() {
147 DCHECK(phase_
>= REGISTER
|| job_type_
== UPDATE_JOB
) << phase_
;
148 return internal_
.registration
.get();
151 void ServiceWorkerRegisterJob::set_new_version(
152 ServiceWorkerVersion
* version
) {
153 DCHECK(phase_
== UPDATE
) << phase_
;
154 DCHECK(!internal_
.new_version
.get());
155 internal_
.new_version
= version
;
158 ServiceWorkerVersion
* ServiceWorkerRegisterJob::new_version() {
159 DCHECK(phase_
>= UPDATE
) << phase_
;
160 return internal_
.new_version
.get();
163 void ServiceWorkerRegisterJob::SetPhase(Phase phase
) {
169 DCHECK(phase_
== INITIAL
) << phase_
;
172 DCHECK(phase_
== START
) << phase_
;
175 DCHECK(phase_
== START
|| phase_
== REGISTER
) << phase_
;
178 DCHECK(phase_
== UPDATE
) << phase_
;
181 DCHECK(phase_
== INSTALL
) << phase_
;
184 DCHECK(phase_
!= INITIAL
&& phase_
!= COMPLETE
) << phase_
;
192 // This function corresponds to the steps in [[Register]] following
193 // "Let registration be the result of running the [[GetRegistration]] algorithm.
194 // Throughout this file, comments in quotes are excerpts from the spec.
195 void ServiceWorkerRegisterJob::ContinueWithRegistration(
196 ServiceWorkerStatusCode status
,
197 const scoped_refptr
<ServiceWorkerRegistration
>& existing_registration
) {
198 DCHECK_EQ(REGISTRATION_JOB
, job_type_
);
199 if (status
!= SERVICE_WORKER_ERROR_NOT_FOUND
&& status
!= SERVICE_WORKER_OK
) {
204 if (!existing_registration
.get() || existing_registration
->is_uninstalled()) {
205 RegisterAndContinue();
209 DCHECK(existing_registration
->GetNewestVersion());
210 // "If scriptURL is equal to registration.[[ScriptURL]], then:"
211 if (existing_registration
->GetNewestVersion()->script_url() == script_url_
) {
212 // "Set registration.[[Uninstalling]] to false."
213 existing_registration
->AbortPendingClear(base::Bind(
214 &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl
,
215 weak_factory_
.GetWeakPtr(),
216 existing_registration
));
220 if (existing_registration
->is_uninstalling()) {
221 existing_registration
->AbortPendingClear(base::Bind(
222 &ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration
,
223 weak_factory_
.GetWeakPtr(),
224 existing_registration
));
228 // "Return the result of running the [[Update]] algorithm, or its equivalent,
229 // passing registration as the argument."
230 set_registration(existing_registration
);
234 void ServiceWorkerRegisterJob::ContinueWithUpdate(
235 ServiceWorkerStatusCode status
,
236 const scoped_refptr
<ServiceWorkerRegistration
>& existing_registration
) {
237 DCHECK_EQ(UPDATE_JOB
, job_type_
);
238 if (status
!= SERVICE_WORKER_OK
) {
243 if (existing_registration
.get() != registration()) {
244 Complete(SERVICE_WORKER_ERROR_NOT_FOUND
);
248 // A previous job may have unregistered or installed a new version to this
250 if (registration()->is_uninstalling() ||
251 registration()->GetNewestVersion()->script_url() != script_url_
) {
252 Complete(SERVICE_WORKER_ERROR_NOT_FOUND
);
256 // TODO(michaeln): If the last update check was less than 24 hours
257 // ago, depending on the freshness of the cached worker script we
258 // may be able to complete the update job right here.
263 // Creates a new ServiceWorkerRegistration.
264 void ServiceWorkerRegisterJob::RegisterAndContinue() {
267 set_registration(new ServiceWorkerRegistration(
268 pattern_
, context_
->storage()->NewRegistrationId(), context_
));
269 AddRegistrationToMatchingProviderHosts(registration());
273 void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration(
274 const scoped_refptr
<ServiceWorkerRegistration
>& existing_registration
,
275 ServiceWorkerStatusCode status
) {
276 if (status
!= SERVICE_WORKER_OK
) {
280 should_uninstall_on_failure_
= true;
281 set_registration(existing_registration
);
285 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
286 const scoped_refptr
<ServiceWorkerRegistration
>& existing_registration
,
287 ServiceWorkerStatusCode status
) {
288 if (status
!= SERVICE_WORKER_OK
) {
292 set_registration(existing_registration
);
294 // "If newestWorker is not null, and scriptURL is equal to
295 // newestWorker.scriptURL, then:
296 // Return a promise resolved with registration."
297 // We resolve only if there's an active version. If there's not,
298 // then there is either no version or only a waiting version from
299 // the last browser session; it makes sense to proceed with registration in
301 DCHECK(!existing_registration
->installing_version());
302 if (existing_registration
->active_version()) {
303 ResolvePromise(status
, std::string(), existing_registration
.get());
304 Complete(SERVICE_WORKER_OK
);
308 // "Return the result of running the [[Update]] algorithm, or its equivalent,
309 // passing registration as the argument."
313 // This function corresponds to the spec's [[Update]] algorithm.
314 void ServiceWorkerRegisterJob::UpdateAndContinue() {
316 context_
->storage()->NotifyInstallingRegistration(registration());
318 // "Let worker be a new ServiceWorker object..." and start
320 set_new_version(new ServiceWorkerVersion(registration(),
322 context_
->storage()->NewVersionId(),
324 new_version()->set_force_bypass_cache_for_scripts(force_bypass_cache_
);
325 new_version()->StartWorker(
326 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished
,
327 weak_factory_
.GetWeakPtr()));
330 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
331 ServiceWorkerStatusCode status
) {
332 if (status
== SERVICE_WORKER_OK
) {
333 InstallAndContinue();
337 // The updated worker is identical to the incumbent.
338 if (status
== SERVICE_WORKER_ERROR_EXISTS
) {
339 // Only bump the last check time when we've bypassed the browser cache.
340 base::TimeDelta time_since_last_check
=
341 base::Time::Now() - registration()->last_update_check();
342 if (time_since_last_check
> base::TimeDelta::FromHours(
343 kServiceWorkerScriptMaxCacheAgeInHours
) ||
344 new_version()->force_bypass_cache_for_scripts()) {
345 registration()->set_last_update_check(base::Time::Now());
346 context_
->storage()->UpdateLastUpdateCheckTime(registration());
349 ResolvePromise(SERVICE_WORKER_OK
, std::string(), registration());
350 Complete(status
, "The updated worker is identical to the incumbent.");
354 // "If serviceWorker fails to start up..." then reject the promise with an
356 if (status
== SERVICE_WORKER_ERROR_TIMEOUT
) {
357 Complete(status
, "Timed out while trying to start the Service Worker.");
361 const net::URLRequestStatus
& main_script_status
=
362 new_version()->script_cache_map()->main_script_status();
364 if (main_script_status
.status() != net::URLRequestStatus::SUCCESS
) {
365 message
= new_version()->script_cache_map()->main_script_status_message();
367 message
= kFetchScriptError
;
369 Complete(status
, message
);
372 // This function corresponds to the spec's [[Install]] algorithm.
373 void ServiceWorkerRegisterJob::InstallAndContinue() {
376 // "Set registration.installingWorker to worker."
377 DCHECK(!registration()->installing_version());
378 registration()->SetInstallingVersion(new_version());
380 // "Run the Update State algorithm passing registration's installing worker
381 // and installing as the arguments."
382 new_version()->SetStatus(ServiceWorkerVersion::INSTALLING
);
384 // "Resolve registrationPromise with registration."
385 ResolvePromise(SERVICE_WORKER_OK
, std::string(), registration());
387 // "Fire a simple event named updatefound..."
388 registration()->NotifyUpdateFound();
390 // "Fire an event named install..."
391 new_version()->DispatchInstallEvent(
392 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished
,
393 weak_factory_
.GetWeakPtr()));
395 // A subsequent registration job may terminate our installing worker. It can
396 // only do so after we've started the worker and dispatched the install
397 // event, as those are atomic substeps in the [[Install]] algorithm.
398 if (doom_installing_worker_
)
399 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED
);
402 void ServiceWorkerRegisterJob::OnInstallFinished(
403 ServiceWorkerStatusCode status
) {
404 ServiceWorkerMetrics::RecordInstallEventStatus(status
);
406 if (status
!= SERVICE_WORKER_OK
) {
407 // "8. If installFailed is true, then:..."
413 registration()->set_last_update_check(base::Time::Now());
414 context_
->storage()->StoreRegistration(
417 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete
,
418 weak_factory_
.GetWeakPtr()));
421 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
422 ServiceWorkerStatusCode status
) {
423 if (status
!= SERVICE_WORKER_OK
) {
428 // "9. If registration.waitingWorker is not null, then:..."
429 if (registration()->waiting_version()) {
430 // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
431 // and "redundant" as the arguments."
432 registration()->waiting_version()->SetStatus(
433 ServiceWorkerVersion::REDUNDANT
);
436 // "10. Set registration.waitingWorker to registration.installingWorker."
437 // "11. Set registration.installingWorker to null."
438 registration()->SetWaitingVersion(new_version());
440 // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
441 // and "installed" as the arguments."
442 new_version()->SetStatus(ServiceWorkerVersion::INSTALLED
);
444 // "If registration's waiting worker's skip waiting flag is set:" then
445 // activate the worker immediately otherwise "wait until no service worker
446 // client is using registration as their service worker registration."
447 registration()->ActivateWaitingVersionWhenReady();
449 Complete(SERVICE_WORKER_OK
);
452 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status
) {
453 Complete(status
, std::string());
456 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status
,
457 const std::string
& status_message
) {
458 CompleteInternal(status
, status_message
);
459 context_
->job_coordinator()->FinishJob(pattern_
, this);
462 void ServiceWorkerRegisterJob::CompleteInternal(
463 ServiceWorkerStatusCode status
,
464 const std::string
& status_message
) {
466 if (status
!= SERVICE_WORKER_OK
) {
467 if (registration()) {
468 if (should_uninstall_on_failure_
)
469 registration()->ClearWhenReady();
471 if (status
== SERVICE_WORKER_ERROR_EXISTS
)
472 new_version()->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS
);
474 new_version()->ReportError(status
, status_message
);
475 registration()->UnsetVersion(new_version());
476 new_version()->Doom();
478 if (!registration()->waiting_version() &&
479 !registration()->active_version()) {
480 registration()->NotifyRegistrationFailed();
481 context_
->storage()->DeleteRegistration(
482 registration()->id(),
483 registration()->pattern().GetOrigin(),
484 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
487 if (!is_promise_resolved_
)
488 ResolvePromise(status
, status_message
, NULL
);
490 DCHECK(callbacks_
.empty());
491 if (registration()) {
492 context_
->storage()->NotifyDoneInstallingRegistration(
493 registration(), new_version(), status
);
494 if (registration()->waiting_version() || registration()->active_version())
495 registration()->set_is_uninstalled(false);
499 void ServiceWorkerRegisterJob::ResolvePromise(
500 ServiceWorkerStatusCode status
,
501 const std::string
& status_message
,
502 ServiceWorkerRegistration
* registration
) {
503 DCHECK(!is_promise_resolved_
);
505 is_promise_resolved_
= true;
506 promise_resolved_status_
= status
;
507 promise_resolved_status_message_
= status_message
,
508 promise_resolved_registration_
= registration
;
509 for (std::vector
<RegistrationCallback
>::iterator it
= callbacks_
.begin();
510 it
!= callbacks_
.end();
512 it
->Run(status
, status_message
, registration
);
517 void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts(
518 ServiceWorkerRegistration
* registration
) {
519 DCHECK(registration
);
520 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
521 context_
->GetProviderHostIterator();
522 !it
->IsAtEnd(); it
->Advance()) {
523 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
524 if (host
->IsHostToRunningServiceWorker())
526 if (!ServiceWorkerUtils::ScopeMatches(registration
->pattern(),
527 host
->document_url()))
529 host
->AddMatchingRegistration(registration
);
533 } // namespace content