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 bool pause_after_download
= job_type_
== UPDATE_JOB
;
326 if (pause_after_download
)
327 new_version()->embedded_worker()->AddListener(this);
328 new_version()->StartWorker(
329 pause_after_download
,
330 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished
,
331 weak_factory_
.GetWeakPtr()));
334 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
335 ServiceWorkerStatusCode status
) {
336 if (status
== SERVICE_WORKER_OK
) {
337 InstallAndContinue();
341 // "If serviceWorker fails to start up..." then reject the promise with an
343 if (status
== SERVICE_WORKER_ERROR_TIMEOUT
) {
344 Complete(status
, "Timed out while trying to start the Service Worker.");
348 const net::URLRequestStatus
& main_script_status
=
349 new_version()->script_cache_map()->main_script_status();
351 if (main_script_status
.status() != net::URLRequestStatus::SUCCESS
) {
352 message
= new_version()->script_cache_map()->main_script_status_message();
354 message
= kFetchScriptError
;
356 Complete(status
, message
);
359 // This function corresponds to the spec's [[Install]] algorithm.
360 void ServiceWorkerRegisterJob::InstallAndContinue() {
363 // "Set registration.installingWorker to worker."
364 DCHECK(!registration()->installing_version());
365 registration()->SetInstallingVersion(new_version());
367 // "Run the Update State algorithm passing registration's installing worker
368 // and installing as the arguments."
369 new_version()->SetStatus(ServiceWorkerVersion::INSTALLING
);
371 // "Resolve registrationPromise with registration."
372 ResolvePromise(SERVICE_WORKER_OK
, std::string(), registration());
374 // "Fire a simple event named updatefound..."
375 registration()->NotifyUpdateFound();
377 // "Fire an event named install..."
378 new_version()->DispatchInstallEvent(
379 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished
,
380 weak_factory_
.GetWeakPtr()));
382 // A subsequent registration job may terminate our installing worker. It can
383 // only do so after we've started the worker and dispatched the install
384 // event, as those are atomic substeps in the [[Install]] algorithm.
385 if (doom_installing_worker_
)
386 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED
);
389 void ServiceWorkerRegisterJob::OnInstallFinished(
390 ServiceWorkerStatusCode status
) {
391 ServiceWorkerMetrics::RecordInstallEventStatus(status
);
393 if (status
!= SERVICE_WORKER_OK
) {
394 // "8. If installFailed is true, then:..."
400 registration()->set_last_update_check(base::Time::Now());
401 context_
->storage()->StoreRegistration(
404 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete
,
405 weak_factory_
.GetWeakPtr()));
408 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
409 ServiceWorkerStatusCode status
) {
410 if (status
!= SERVICE_WORKER_OK
) {
415 // "9. If registration.waitingWorker is not null, then:..."
416 if (registration()->waiting_version()) {
417 // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
418 // and "redundant" as the arguments."
419 registration()->waiting_version()->SetStatus(
420 ServiceWorkerVersion::REDUNDANT
);
423 // "10. Set registration.waitingWorker to registration.installingWorker."
424 // "11. Set registration.installingWorker to null."
425 registration()->SetWaitingVersion(new_version());
427 // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
428 // and "installed" as the arguments."
429 new_version()->SetStatus(ServiceWorkerVersion::INSTALLED
);
431 // "If registration's waiting worker's skip waiting flag is set:" then
432 // activate the worker immediately otherwise "wait until no service worker
433 // client is using registration as their service worker registration."
434 registration()->ActivateWaitingVersionWhenReady();
436 Complete(SERVICE_WORKER_OK
);
439 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status
) {
440 Complete(status
, std::string());
443 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status
,
444 const std::string
& status_message
) {
445 CompleteInternal(status
, status_message
);
446 context_
->job_coordinator()->FinishJob(pattern_
, this);
449 void ServiceWorkerRegisterJob::CompleteInternal(
450 ServiceWorkerStatusCode status
,
451 const std::string
& status_message
) {
453 if (status
!= SERVICE_WORKER_OK
) {
454 if (registration()) {
455 if (should_uninstall_on_failure_
)
456 registration()->ClearWhenReady();
458 if (status
== SERVICE_WORKER_ERROR_EXISTS
)
459 new_version()->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS
);
461 new_version()->ReportError(status
, status_message
);
462 registration()->UnsetVersion(new_version());
463 new_version()->Doom();
465 if (!registration()->waiting_version() &&
466 !registration()->active_version()) {
467 registration()->NotifyRegistrationFailed();
468 context_
->storage()->DeleteRegistration(
469 registration()->id(),
470 registration()->pattern().GetOrigin(),
471 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
474 if (!is_promise_resolved_
)
475 ResolvePromise(status
, status_message
, NULL
);
477 DCHECK(callbacks_
.empty());
478 if (registration()) {
479 context_
->storage()->NotifyDoneInstallingRegistration(
480 registration(), new_version(), status
);
481 if (registration()->waiting_version() || registration()->active_version())
482 registration()->set_is_uninstalled(false);
485 new_version()->embedded_worker()->RemoveListener(this);
488 void ServiceWorkerRegisterJob::ResolvePromise(
489 ServiceWorkerStatusCode status
,
490 const std::string
& status_message
,
491 ServiceWorkerRegistration
* registration
) {
492 DCHECK(!is_promise_resolved_
);
494 is_promise_resolved_
= true;
495 promise_resolved_status_
= status
;
496 promise_resolved_status_message_
= status_message
,
497 promise_resolved_registration_
= registration
;
498 for (std::vector
<RegistrationCallback
>::iterator it
= callbacks_
.begin();
499 it
!= callbacks_
.end();
501 it
->Run(status
, status_message
, registration
);
506 void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
507 // This happens prior to OnStartWorkerFinished time.
508 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
509 registration()->waiting_version() ?
510 registration()->waiting_version() :
511 registration()->active_version();
513 if (!most_recent_version
) {
514 OnCompareScriptResourcesComplete(SERVICE_WORKER_OK
, false /* are_equal */);
518 int64 most_recent_script_id
=
519 most_recent_version
->script_cache_map()->LookupResourceId(script_url_
);
520 int64 new_script_id
=
521 new_version()->script_cache_map()->LookupResourceId(script_url_
);
523 // TODO(michaeln): It would be better to compare as the new resource
524 // is being downloaded and to avoid writing it to disk until we know
526 context_
->storage()->CompareScriptResources(
527 most_recent_script_id
,
529 base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete
,
530 weak_factory_
.GetWeakPtr()));
533 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message
& message
) {
537 void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
538 ServiceWorkerStatusCode status
,
541 // Only bump the last check time when we've bypassed the browser cache.
542 base::TimeDelta time_since_last_check
=
543 base::Time::Now() - registration()->last_update_check();
544 if (time_since_last_check
> base::TimeDelta::FromHours(24) ||
545 new_version()->force_bypass_cache_for_scripts()) {
546 registration()->set_last_update_check(base::Time::Now());
547 context_
->storage()->UpdateLastUpdateCheckTime(registration());
550 ResolvePromise(SERVICE_WORKER_OK
, std::string(), registration());
551 Complete(SERVICE_WORKER_ERROR_EXISTS
);
555 // Proceed with really starting the worker.
556 new_version()->embedded_worker()->ResumeAfterDownload();
557 new_version()->embedded_worker()->RemoveListener(this);
560 void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts(
561 ServiceWorkerRegistration
* registration
) {
562 DCHECK(registration
);
563 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
564 context_
->GetProviderHostIterator();
565 !it
->IsAtEnd(); it
->Advance()) {
566 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
567 if (host
->IsHostToRunningServiceWorker())
569 if (!ServiceWorkerUtils::ScopeMatches(registration
->pattern(),
570 host
->document_url()))
572 host
->AddMatchingRegistration(registration
);
576 } // namespace content