Android: Store language .pak files in res/raw rather than assets
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_register_job.cc
blobc4f7754fcfa406894a88ec85700517c043a6b773
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 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();
338 return;
341 // "If serviceWorker fails to start up..." then reject the promise with an
342 // error and abort.
343 if (status == SERVICE_WORKER_ERROR_TIMEOUT) {
344 Complete(status, "Timed out while trying to start the Service Worker.");
345 return;
348 const net::URLRequestStatus& main_script_status =
349 new_version()->script_cache_map()->main_script_status();
350 std::string message;
351 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
352 message = new_version()->script_cache_map()->main_script_status_message();
353 if (message.empty())
354 message = kFetchScriptError;
356 Complete(status, message);
359 // This function corresponds to the spec's [[Install]] algorithm.
360 void ServiceWorkerRegisterJob::InstallAndContinue() {
361 SetPhase(INSTALL);
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:..."
395 Complete(status);
396 return;
399 SetPhase(STORE);
400 registration()->set_last_update_check(base::Time::Now());
401 context_->storage()->StoreRegistration(
402 registration(),
403 new_version(),
404 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
405 weak_factory_.GetWeakPtr()));
408 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
409 ServiceWorkerStatusCode status) {
410 if (status != SERVICE_WORKER_OK) {
411 Complete(status);
412 return;
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) {
452 SetPhase(COMPLETE);
453 if (status != SERVICE_WORKER_OK) {
454 if (registration()) {
455 if (should_uninstall_on_failure_)
456 registration()->ClearWhenReady();
457 if (new_version()) {
458 if (status == SERVICE_WORKER_ERROR_EXISTS)
459 new_version()->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS);
460 else
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);
484 if (new_version())
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();
500 ++it) {
501 it->Run(status, status_message, registration);
503 callbacks_.clear();
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 */);
515 return;
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
525 // its needed.
526 context_->storage()->CompareScriptResources(
527 most_recent_script_id,
528 new_script_id,
529 base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
530 weak_factory_.GetWeakPtr()));
533 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
534 return false;
537 void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
538 ServiceWorkerStatusCode status,
539 bool are_equal) {
540 if (are_equal) {
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);
552 return;
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())
568 continue;
569 if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(),
570 host->document_url()))
571 continue;
572 host->AddMatchingRegistration(registration);
576 } // namespace content