Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_register_job.cc
blob3435177898635c109f587822a8d9d34bcd910953
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 force_bypass_cache_(false),
43 promise_resolved_status_(SERVICE_WORKER_OK),
44 weak_factory_(this) {
47 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
48 base::WeakPtr<ServiceWorkerContextCore> context,
49 ServiceWorkerRegistration* registration,
50 bool force_bypass_cache)
51 : context_(context),
52 job_type_(UPDATE_JOB),
53 pattern_(registration->pattern()),
54 script_url_(registration->GetNewestVersion()->script_url()),
55 phase_(INITIAL),
56 doom_installing_worker_(false),
57 is_promise_resolved_(false),
58 should_uninstall_on_failure_(false),
59 force_bypass_cache_(force_bypass_cache),
60 promise_resolved_status_(SERVICE_WORKER_OK),
61 weak_factory_(this) {
62 internal_.registration = registration;
65 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
66 DCHECK(!context_ ||
67 phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
68 << "Jobs should only be interrupted during shutdown.";
71 void ServiceWorkerRegisterJob::AddCallback(
72 const RegistrationCallback& callback,
73 ServiceWorkerProviderHost* provider_host) {
74 if (!is_promise_resolved_) {
75 callbacks_.push_back(callback);
76 if (provider_host)
77 provider_host->AddScopedProcessReferenceToPattern(pattern_);
78 return;
80 RunSoon(base::Bind(callback, promise_resolved_status_,
81 promise_resolved_status_message_,
82 promise_resolved_registration_));
85 void ServiceWorkerRegisterJob::Start() {
86 SetPhase(START);
87 ServiceWorkerStorage::FindRegistrationCallback next_step;
88 if (job_type_ == REGISTRATION_JOB) {
89 next_step = base::Bind(
90 &ServiceWorkerRegisterJob::ContinueWithRegistration,
91 weak_factory_.GetWeakPtr());
92 } else {
93 next_step = base::Bind(
94 &ServiceWorkerRegisterJob::ContinueWithUpdate,
95 weak_factory_.GetWeakPtr());
98 scoped_refptr<ServiceWorkerRegistration> registration =
99 context_->storage()->GetUninstallingRegistration(pattern_);
100 if (registration.get())
101 RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
102 else
103 context_->storage()->FindRegistrationForPattern(pattern_, next_step);
106 void ServiceWorkerRegisterJob::Abort() {
107 SetPhase(ABORT);
108 CompleteInternal(SERVICE_WORKER_ERROR_ABORT, std::string());
109 // Don't have to call FinishJob() because the caller takes care of removing
110 // the jobs from the queue.
113 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) const {
114 if (job->GetType() != GetType())
115 return false;
116 ServiceWorkerRegisterJob* register_job =
117 static_cast<ServiceWorkerRegisterJob*>(job);
118 return register_job->pattern_ == pattern_ &&
119 register_job->script_url_ == script_url_;
122 RegistrationJobType ServiceWorkerRegisterJob::GetType() const {
123 return job_type_;
126 void ServiceWorkerRegisterJob::DoomInstallingWorker() {
127 doom_installing_worker_ = true;
128 if (phase_ == INSTALL)
129 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED, std::string());
132 ServiceWorkerRegisterJob::Internal::Internal() {}
134 ServiceWorkerRegisterJob::Internal::~Internal() {}
136 void ServiceWorkerRegisterJob::set_registration(
137 const scoped_refptr<ServiceWorkerRegistration>& registration) {
138 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
139 DCHECK(!internal_.registration.get());
140 internal_.registration = registration;
143 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
144 DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
145 return internal_.registration.get();
148 void ServiceWorkerRegisterJob::set_new_version(
149 ServiceWorkerVersion* version) {
150 DCHECK(phase_ == UPDATE) << phase_;
151 DCHECK(!internal_.new_version.get());
152 internal_.new_version = version;
155 ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
156 DCHECK(phase_ >= UPDATE) << phase_;
157 return internal_.new_version.get();
160 void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
161 switch (phase) {
162 case INITIAL:
163 NOTREACHED();
164 break;
165 case START:
166 DCHECK(phase_ == INITIAL) << phase_;
167 break;
168 case REGISTER:
169 DCHECK(phase_ == START) << phase_;
170 break;
171 case UPDATE:
172 DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
173 break;
174 case INSTALL:
175 DCHECK(phase_ == UPDATE) << phase_;
176 break;
177 case STORE:
178 DCHECK(phase_ == INSTALL) << phase_;
179 break;
180 case COMPLETE:
181 DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
182 break;
183 case ABORT:
184 break;
186 phase_ = phase;
189 // This function corresponds to the steps in [[Register]] following
190 // "Let registration be the result of running the [[GetRegistration]] algorithm.
191 // Throughout this file, comments in quotes are excerpts from the spec.
192 void ServiceWorkerRegisterJob::ContinueWithRegistration(
193 ServiceWorkerStatusCode status,
194 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
195 DCHECK_EQ(REGISTRATION_JOB, job_type_);
196 if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
197 Complete(status);
198 return;
201 if (!existing_registration.get() || existing_registration->is_uninstalled()) {
202 RegisterAndContinue();
203 return;
206 DCHECK(existing_registration->GetNewestVersion());
207 // "If scriptURL is equal to registration.[[ScriptURL]], then:"
208 if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
209 // "Set registration.[[Uninstalling]] to false."
210 existing_registration->AbortPendingClear(base::Bind(
211 &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
212 weak_factory_.GetWeakPtr(),
213 existing_registration));
214 return;
217 if (existing_registration->is_uninstalling()) {
218 existing_registration->AbortPendingClear(base::Bind(
219 &ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration,
220 weak_factory_.GetWeakPtr(),
221 existing_registration));
222 return;
225 // "Return the result of running the [[Update]] algorithm, or its equivalent,
226 // passing registration as the argument."
227 set_registration(existing_registration);
228 UpdateAndContinue();
231 void ServiceWorkerRegisterJob::ContinueWithUpdate(
232 ServiceWorkerStatusCode status,
233 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
234 DCHECK_EQ(UPDATE_JOB, job_type_);
235 if (status != SERVICE_WORKER_OK) {
236 Complete(status);
237 return;
240 if (existing_registration.get() != registration()) {
241 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
242 return;
245 // A previous job may have unregistered or installed a new version to this
246 // registration.
247 if (registration()->is_uninstalling() ||
248 registration()->GetNewestVersion()->script_url() != script_url_) {
249 Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
250 return;
253 // TODO(michaeln): If the last update check was less than 24 hours
254 // ago, depending on the freshness of the cached worker script we
255 // may be able to complete the update job right here.
257 UpdateAndContinue();
260 // Creates a new ServiceWorkerRegistration.
261 void ServiceWorkerRegisterJob::RegisterAndContinue() {
262 SetPhase(REGISTER);
264 set_registration(new ServiceWorkerRegistration(
265 pattern_, context_->storage()->NewRegistrationId(), context_));
266 AddRegistrationToMatchingProviderHosts(registration());
267 UpdateAndContinue();
270 void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration(
271 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
272 ServiceWorkerStatusCode status) {
273 if (status != SERVICE_WORKER_OK) {
274 Complete(status);
275 return;
277 should_uninstall_on_failure_ = true;
278 set_registration(existing_registration);
279 UpdateAndContinue();
282 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
283 const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
284 ServiceWorkerStatusCode status) {
285 if (status != SERVICE_WORKER_OK) {
286 Complete(status);
287 return;
289 set_registration(existing_registration);
291 // "If newestWorker is not null, and scriptURL is equal to
292 // newestWorker.scriptURL, then:
293 // Return a promise resolved with registration."
294 // We resolve only if there's an active version. If there's not,
295 // then there is either no version or only a waiting version from
296 // the last browser session; it makes sense to proceed with registration in
297 // either case.
298 DCHECK(!existing_registration->installing_version());
299 if (existing_registration->active_version()) {
300 ResolvePromise(status, std::string(), existing_registration.get());
301 Complete(SERVICE_WORKER_OK);
302 return;
305 // "Return the result of running the [[Update]] algorithm, or its equivalent,
306 // passing registration as the argument."
307 UpdateAndContinue();
310 // This function corresponds to the spec's [[Update]] algorithm.
311 void ServiceWorkerRegisterJob::UpdateAndContinue() {
312 SetPhase(UPDATE);
313 context_->storage()->NotifyInstallingRegistration(registration());
315 // "Let worker be a new ServiceWorker object..." and start
316 // the worker.
317 set_new_version(new ServiceWorkerVersion(registration(),
318 script_url_,
319 context_->storage()->NewVersionId(),
320 context_));
321 new_version()->set_force_bypass_cache_for_scripts(force_bypass_cache_);
322 bool pause_after_download = job_type_ == UPDATE_JOB;
323 if (pause_after_download)
324 new_version()->embedded_worker()->AddListener(this);
325 new_version()->StartWorker(
326 pause_after_download,
327 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
328 weak_factory_.GetWeakPtr()));
331 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
332 ServiceWorkerStatusCode status) {
333 if (status == SERVICE_WORKER_OK) {
334 InstallAndContinue();
335 return;
338 // "If serviceWorker fails to start up..." then reject the promise with an
339 // error and abort.
340 if (status == SERVICE_WORKER_ERROR_TIMEOUT) {
341 Complete(status, "Timed out while trying to start the Service Worker.");
342 return;
345 const net::URLRequestStatus& main_script_status =
346 new_version()->script_cache_map()->main_script_status();
347 std::string message;
348 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
349 message = new_version()->script_cache_map()->main_script_status_message();
350 if (message.empty())
351 message = kFetchScriptError;
353 Complete(status, message);
356 // This function corresponds to the spec's [[Install]] algorithm.
357 void ServiceWorkerRegisterJob::InstallAndContinue() {
358 SetPhase(INSTALL);
360 // "Set registration.installingWorker to worker."
361 DCHECK(!registration()->installing_version());
362 registration()->SetInstallingVersion(new_version());
364 // "Run the Update State algorithm passing registration's installing worker
365 // and installing as the arguments."
366 new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
368 // "Resolve registrationPromise with registration."
369 ResolvePromise(SERVICE_WORKER_OK, std::string(), registration());
371 // "Fire a simple event named updatefound..."
372 registration()->NotifyUpdateFound();
374 // "Fire an event named install..."
375 new_version()->DispatchInstallEvent(
376 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
377 weak_factory_.GetWeakPtr()));
379 // A subsequent registration job may terminate our installing worker. It can
380 // only do so after we've started the worker and dispatched the install
381 // event, as those are atomic substeps in the [[Install]] algorithm.
382 if (doom_installing_worker_)
383 Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
386 void ServiceWorkerRegisterJob::OnInstallFinished(
387 ServiceWorkerStatusCode status) {
388 if (status != SERVICE_WORKER_OK) {
389 // "8. If installFailed is true, then:..."
390 Complete(status);
391 return;
394 SetPhase(STORE);
395 registration()->set_last_update_check(base::Time::Now());
396 context_->storage()->StoreRegistration(
397 registration(),
398 new_version(),
399 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
400 weak_factory_.GetWeakPtr()));
403 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
404 ServiceWorkerStatusCode status) {
405 if (status != SERVICE_WORKER_OK) {
406 Complete(status);
407 return;
410 // "9. If registration.waitingWorker is not null, then:..."
411 if (registration()->waiting_version()) {
412 // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
413 // and "redundant" as the arguments."
414 registration()->waiting_version()->SetStatus(
415 ServiceWorkerVersion::REDUNDANT);
418 // "10. Set registration.waitingWorker to registration.installingWorker."
419 // "11. Set registration.installingWorker to null."
420 registration()->SetWaitingVersion(new_version());
422 // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
423 // and "installed" as the arguments."
424 new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
426 // "If registration's waiting worker's skip waiting flag is set:" then
427 // activate the worker immediately otherwise "wait until no service worker
428 // client is using registration as their service worker registration."
429 registration()->ActivateWaitingVersionWhenReady();
431 Complete(SERVICE_WORKER_OK);
434 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
435 Complete(status, std::string());
438 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status,
439 const std::string& status_message) {
440 CompleteInternal(status, status_message);
441 context_->job_coordinator()->FinishJob(pattern_, this);
444 void ServiceWorkerRegisterJob::CompleteInternal(
445 ServiceWorkerStatusCode status,
446 const std::string& status_message) {
447 SetPhase(COMPLETE);
448 if (status != SERVICE_WORKER_OK) {
449 if (registration()) {
450 if (should_uninstall_on_failure_)
451 registration()->ClearWhenReady();
452 if (new_version()) {
453 if (status != SERVICE_WORKER_ERROR_EXISTS)
454 new_version()->ReportError(status, status_message);
455 registration()->UnsetVersion(new_version());
456 new_version()->Doom();
458 if (!registration()->waiting_version() &&
459 !registration()->active_version()) {
460 registration()->NotifyRegistrationFailed();
461 context_->storage()->DeleteRegistration(
462 registration()->id(),
463 registration()->pattern().GetOrigin(),
464 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
467 if (!is_promise_resolved_)
468 ResolvePromise(status, status_message, NULL);
470 DCHECK(callbacks_.empty());
471 if (registration()) {
472 context_->storage()->NotifyDoneInstallingRegistration(
473 registration(), new_version(), status);
475 if (new_version())
476 new_version()->embedded_worker()->RemoveListener(this);
479 void ServiceWorkerRegisterJob::ResolvePromise(
480 ServiceWorkerStatusCode status,
481 const std::string& status_message,
482 ServiceWorkerRegistration* registration) {
483 DCHECK(!is_promise_resolved_);
485 is_promise_resolved_ = true;
486 promise_resolved_status_ = status;
487 promise_resolved_status_message_ = status_message,
488 promise_resolved_registration_ = registration;
489 for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
490 it != callbacks_.end();
491 ++it) {
492 it->Run(status, status_message, registration);
494 callbacks_.clear();
497 void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
498 // This happens prior to OnStartWorkerFinished time.
499 scoped_refptr<ServiceWorkerVersion> most_recent_version =
500 registration()->waiting_version() ?
501 registration()->waiting_version() :
502 registration()->active_version();
503 DCHECK(most_recent_version.get());
504 int64 most_recent_script_id =
505 most_recent_version->script_cache_map()->LookupResourceId(script_url_);
506 int64 new_script_id =
507 new_version()->script_cache_map()->LookupResourceId(script_url_);
509 // TODO(michaeln): It would be better to compare as the new resource
510 // is being downloaded and to avoid writing it to disk until we know
511 // its needed.
512 context_->storage()->CompareScriptResources(
513 most_recent_script_id,
514 new_script_id,
515 base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
516 weak_factory_.GetWeakPtr()));
519 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
520 return false;
523 void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
524 ServiceWorkerStatusCode status,
525 bool are_equal) {
526 if (are_equal) {
527 // Only bump the last check time when we've bypassed the browser cache.
528 base::TimeDelta time_since_last_check =
529 base::Time::Now() - registration()->last_update_check();
530 if (time_since_last_check > base::TimeDelta::FromHours(24) ||
531 new_version()->force_bypass_cache_for_scripts()) {
532 registration()->set_last_update_check(base::Time::Now());
533 context_->storage()->UpdateLastUpdateCheckTime(registration());
536 ResolvePromise(SERVICE_WORKER_OK, std::string(), registration());
537 Complete(SERVICE_WORKER_ERROR_EXISTS);
538 return;
541 // Proceed with really starting the worker.
542 new_version()->embedded_worker()->ResumeAfterDownload();
543 new_version()->embedded_worker()->RemoveListener(this);
546 void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts(
547 ServiceWorkerRegistration* registration) {
548 DCHECK(registration);
549 for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
550 context_->GetProviderHostIterator();
551 !it->IsAtEnd(); it->Advance()) {
552 ServiceWorkerProviderHost* host = it->GetProviderHost();
553 if (host->IsHostToRunningServiceWorker())
554 continue;
555 if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(),
556 host->document_url()))
557 continue;
558 host->AddMatchingRegistration(registration);
562 } // namespace content