Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_register_job.cc
blob3df118c292b6ffd79ebe7a8be39bc4a60832a7b5
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/browser/service_worker/service_worker_utils.h"
18 #include "content/common/service_worker/service_worker_types.h"
19 #include "net/base/net_errors.h"
21 namespace content {
23 namespace {
25 void RunSoon(const base::Closure& closure) {
26 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
29 } // namespace
31 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
33 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
34 base::WeakPtr<ServiceWorkerContextCore> context,
35 const GURL& pattern,
36 const GURL& script_url)
37 : context_(context),
38 job_type_(REGISTRATION_JOB),
39 pattern_(pattern),
40 script_url_(script_url),
41 phase_(INITIAL),
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),
47 weak_factory_(this) {
50 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
51 base::WeakPtr<ServiceWorkerContextCore> context,
52 ServiceWorkerRegistration* registration,
53 bool force_bypass_cache)
54 : context_(context),
55 job_type_(UPDATE_JOB),
56 pattern_(registration->pattern()),
57 script_url_(registration->GetNewestVersion()->script_url()),
58 phase_(INITIAL),
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),
64 weak_factory_(this) {
65 internal_.registration = registration;
68 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
69 DCHECK(!context_ ||
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);
79 if (provider_host)
80 provider_host->AddScopedProcessReferenceToPattern(pattern_);
81 return;
83 RunSoon(base::Bind(callback, promise_resolved_status_,
84 promise_resolved_status_message_,
85 promise_resolved_registration_));
88 void ServiceWorkerRegisterJob::Start() {
89 SetPhase(START);
90 ServiceWorkerStorage::FindRegistrationCallback next_step;
91 if (job_type_ == REGISTRATION_JOB) {
92 next_step = base::Bind(
93 &ServiceWorkerRegisterJob::ContinueWithRegistration,
94 weak_factory_.GetWeakPtr());
95 } else {
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));
105 else
106 context_->storage()->FindRegistrationForPattern(pattern_, next_step);
109 void ServiceWorkerRegisterJob::Abort() {
110 SetPhase(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())
118 return false;
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 {
126 return job_type_;
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) {
164 switch (phase) {
165 case INITIAL:
166 NOTREACHED();
167 break;
168 case START:
169 DCHECK(phase_ == INITIAL) << phase_;
170 break;
171 case REGISTER:
172 DCHECK(phase_ == START) << phase_;
173 break;
174 case UPDATE:
175 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
176 break;
177 case INSTALL:
178 DCHECK(phase_ == UPDATE) << phase_;
179 break;
180 case STORE:
181 DCHECK(phase_ == INSTALL) << phase_;
182 break;
183 case COMPLETE:
184 DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
185 break;
186 case ABORT:
187 break;
189 phase_ = 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) {
200 Complete(status);
201 return;
204 if (!existing_registration.get() || existing_registration->is_uninstalled()) {
205 RegisterAndContinue();
206 return;
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));
217 return;
220 if (existing_registration->is_uninstalling()) {
221 existing_registration->AbortPendingClear(base::Bind(
222 &ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration,
223 weak_factory_.GetWeakPtr(),
224 existing_registration));
225 return;
228 // "Return the result of running the [[Update]] algorithm, or its equivalent,
229 // passing registration as the argument."
230 set_registration(existing_registration);
231 UpdateAndContinue();
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) {
239 Complete(status);
240 return;
243 if (existing_registration.get() != registration()) {
244 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
245 return;
248 // A previous job may have unregistered or installed a new version to this
249 // registration.
250 if (registration()->is_uninstalling() ||
251 registration()->GetNewestVersion()->script_url() != script_url_) {
252 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
253 return;
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.
260 UpdateAndContinue();
263 // Creates a new ServiceWorkerRegistration.
264 void ServiceWorkerRegisterJob::RegisterAndContinue() {
265 SetPhase(REGISTER);
267 set_registration(new ServiceWorkerRegistration(
268 pattern_, context_->storage()->NewRegistrationId(), context_));
269 AddRegistrationToMatchingProviderHosts(registration());
270 UpdateAndContinue();
273 void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration(
274 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
275 ServiceWorkerStatusCode status) {
276 if (status != SERVICE_WORKER_OK) {
277 Complete(status);
278 return;
280 should_uninstall_on_failure_ = true;
281 set_registration(existing_registration);
282 UpdateAndContinue();
285 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
286 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
287 ServiceWorkerStatusCode status) {
288 if (status != SERVICE_WORKER_OK) {
289 Complete(status);
290 return;
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
300 // either case.
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);
305 return;
308 // "Return the result of running the [[Update]] algorithm, or its equivalent,
309 // passing registration as the argument."
310 UpdateAndContinue();
313 // This function corresponds to the spec's [[Update]] algorithm.
314 void ServiceWorkerRegisterJob::UpdateAndContinue() {
315 SetPhase(UPDATE);
316 context_->storage()->NotifyInstallingRegistration(registration());
318 // "Let worker be a new ServiceWorker object..." and start
319 // the worker.
320 set_new_version(new ServiceWorkerVersion(registration(),
321 script_url_,
322 context_->storage()->NewVersionId(),
323 context_));
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();
334 return;
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.");
351 return;
354 // "If serviceWorker fails to start up..." then reject the promise with an
355 // error and abort.
356 if (status == SERVICE_WORKER_ERROR_TIMEOUT) {
357 Complete(status, "Timed out while trying to start the Service Worker.");
358 return;
361 const net::URLRequestStatus& main_script_status =
362 new_version()->script_cache_map()->main_script_status();
363 std::string message;
364 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
365 message = new_version()->script_cache_map()->main_script_status_message();
366 if (message.empty())
367 message = kFetchScriptError;
369 Complete(status, message);
372 // This function corresponds to the spec's [[Install]] algorithm.
373 void ServiceWorkerRegisterJob::InstallAndContinue() {
374 SetPhase(INSTALL);
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:..."
408 Complete(status);
409 return;
412 SetPhase(STORE);
413 registration()->set_last_update_check(base::Time::Now());
414 context_->storage()->StoreRegistration(
415 registration(),
416 new_version(),
417 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
418 weak_factory_.GetWeakPtr()));
421 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
422 ServiceWorkerStatusCode status) {
423 if (status != SERVICE_WORKER_OK) {
424 Complete(status);
425 return;
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) {
465 SetPhase(COMPLETE);
466 if (status != SERVICE_WORKER_OK) {
467 if (registration()) {
468 if (should_uninstall_on_failure_)
469 registration()->ClearWhenReady();
470 if (new_version()) {
471 if (status == SERVICE_WORKER_ERROR_EXISTS)
472 new_version()->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS);
473 else
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();
511 ++it) {
512 it->Run(status, status_message, registration);
514 callbacks_.clear();
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())
525 continue;
526 if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(),
527 host->document_url()))
528 continue;
529 host->AddMatchingRegistration(registration);
533 } // namespace content