Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_register_job.cc
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.
4
5 #include "content/browser/service_worker/service_worker_register_job.h"
6
7 #include <vector>
8
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 "net/base/net_errors.h"
16
17 namespace content {
18
19 namespace {
20
21 void RunSoon(const base::Closure& closure) {
22   base::MessageLoop::current()->PostTask(FROM_HERE, closure);
23 }
24
25 }  // namespace
26
27 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
28
29 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
30     base::WeakPtr<ServiceWorkerContextCore> context,
31     const GURL& pattern,
32     const GURL& script_url)
33     : context_(context),
34       job_type_(REGISTRATION_JOB),
35       pattern_(pattern),
36       script_url_(script_url),
37       phase_(INITIAL),
38       is_promise_resolved_(false),
39       promise_resolved_status_(SERVICE_WORKER_OK),
40       weak_factory_(this) {}
41
42 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
43     base::WeakPtr<ServiceWorkerContextCore> context,
44     ServiceWorkerRegistration* registration)
45     : context_(context),
46       job_type_(UPDATE_JOB),
47       pattern_(registration->pattern()),
48       script_url_(registration->GetNewestVersion()->script_url()),
49       phase_(INITIAL),
50       is_promise_resolved_(false),
51       promise_resolved_status_(SERVICE_WORKER_OK),
52       weak_factory_(this) {
53   internal_.registration = registration;
54 }
55
56 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
57   DCHECK(!context_ ||
58          phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
59       << "Jobs should only be interrupted during shutdown.";
60 }
61
62 void ServiceWorkerRegisterJob::AddCallback(
63     const RegistrationCallback& callback,
64     ServiceWorkerProviderHost* provider_host) {
65   if (!is_promise_resolved_) {
66     callbacks_.push_back(callback);
67     if (provider_host)
68       provider_host->AddScopedProcessReferenceToPattern(pattern_);
69     return;
70   }
71   RunSoon(base::Bind(
72       callback, promise_resolved_status_, promise_resolved_registration_));
73 }
74
75 void ServiceWorkerRegisterJob::Start() {
76   SetPhase(START);
77   ServiceWorkerStorage::FindRegistrationCallback next_step;
78   if (job_type_ == REGISTRATION_JOB) {
79     next_step = base::Bind(
80         &ServiceWorkerRegisterJob::ContinueWithRegistration,
81         weak_factory_.GetWeakPtr());
82   } else {
83     next_step = base::Bind(
84         &ServiceWorkerRegisterJob::ContinueWithUpdate,
85         weak_factory_.GetWeakPtr());
86   }
87
88   scoped_refptr<ServiceWorkerRegistration> registration =
89       context_->storage()->GetUninstallingRegistration(pattern_);
90   if (registration.get())
91     RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
92   else
93     context_->storage()->FindRegistrationForPattern(pattern_, next_step);
94 }
95
96 void ServiceWorkerRegisterJob::Abort() {
97   SetPhase(ABORT);
98   CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
99   // Don't have to call FinishJob() because the caller takes care of removing
100   // the jobs from the queue.
101 }
102
103 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
104   if (job->GetType() != GetType())
105     return false;
106   ServiceWorkerRegisterJob* register_job =
107       static_cast<ServiceWorkerRegisterJob*>(job);
108   return register_job->pattern_ == pattern_ &&
109          register_job->script_url_ == script_url_;
110 }
111
112 RegistrationJobType ServiceWorkerRegisterJob::GetType() {
113   return job_type_;
114 }
115
116 ServiceWorkerRegisterJob::Internal::Internal() {}
117
118 ServiceWorkerRegisterJob::Internal::~Internal() {}
119
120 void ServiceWorkerRegisterJob::set_registration(
121     const scoped_refptr<ServiceWorkerRegistration>& registration) {
122   DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
123   DCHECK(!internal_.registration.get());
124   internal_.registration = registration;
125 }
126
127 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
128   DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
129   return internal_.registration.get();
130 }
131
132 void ServiceWorkerRegisterJob::set_new_version(
133     ServiceWorkerVersion* version) {
134   DCHECK(phase_ == UPDATE) << phase_;
135   DCHECK(!internal_.new_version.get());
136   internal_.new_version = version;
137 }
138
139 ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
140   DCHECK(phase_ >= UPDATE) << phase_;
141   return internal_.new_version.get();
142 }
143
144 void ServiceWorkerRegisterJob::set_uninstalling_registration(
145     const scoped_refptr<ServiceWorkerRegistration>& registration) {
146   DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
147   internal_.uninstalling_registration = registration;
148 }
149
150 ServiceWorkerRegistration*
151 ServiceWorkerRegisterJob::uninstalling_registration() {
152   DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
153   return internal_.uninstalling_registration.get();
154 }
155
156 void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
157   switch (phase) {
158     case INITIAL:
159       NOTREACHED();
160       break;
161     case START:
162       DCHECK(phase_ == INITIAL) << phase_;
163       break;
164     case WAIT_FOR_UNINSTALL:
165       DCHECK(phase_ == START) << phase_;
166       break;
167     case REGISTER:
168       DCHECK(phase_ == START || phase_ == WAIT_FOR_UNINSTALL) << phase_;
169       break;
170     case UPDATE:
171       DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
172       break;
173     case INSTALL:
174       DCHECK(phase_ == UPDATE) << phase_;
175       break;
176     case STORE:
177       DCHECK(phase_ == INSTALL) << phase_;
178       break;
179     case COMPLETE:
180       DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
181       break;
182     case ABORT:
183       break;
184   }
185   phase_ = phase;
186 }
187
188 // This function corresponds to the steps in [[Register]] following
189 // "Let registration be the result of running the [[GetRegistration]] algorithm.
190 // Throughout this file, comments in quotes are excerpts from the spec.
191 void ServiceWorkerRegisterJob::ContinueWithRegistration(
192     ServiceWorkerStatusCode status,
193     const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
194   DCHECK_EQ(REGISTRATION_JOB, job_type_);
195   if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
196     Complete(status);
197     return;
198   }
199
200   if (!existing_registration.get() || existing_registration->is_uninstalled()) {
201     RegisterAndContinue(SERVICE_WORKER_OK);
202     return;
203   }
204
205   DCHECK(existing_registration->GetNewestVersion());
206   // "If scriptURL is equal to registration.[[ScriptURL]], then:"
207   if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
208     // "Set registration.[[Uninstalling]] to false."
209     existing_registration->AbortPendingClear(base::Bind(
210         &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
211         weak_factory_.GetWeakPtr(),
212         existing_registration));
213     return;
214   }
215
216   if (existing_registration->is_uninstalling()) {
217     // "Wait until the Record {[[key]], [[value]]} entry of its
218     // [[ScopeToRegistrationMap]] where registation.scope matches entry.[[key]]
219     // is deleted."
220     WaitForUninstall(existing_registration);
221     return;
222   }
223
224   // "Set registration.[[Uninstalling]] to false."
225   DCHECK(!existing_registration->is_uninstalling());
226
227   // "Return the result of running the [[Update]] algorithm, or its equivalent,
228   // passing registration as the argument."
229   set_registration(existing_registration);
230   UpdateAndContinue();
231 }
232
233 void ServiceWorkerRegisterJob::ContinueWithUpdate(
234     ServiceWorkerStatusCode status,
235     const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
236   DCHECK_EQ(UPDATE_JOB, job_type_);
237   if (status != SERVICE_WORKER_OK) {
238     Complete(status);
239     return;
240   }
241
242   if (existing_registration.get() != registration()) {
243     Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
244     return;
245   }
246
247   // A previous job may have unregistered or installed a new version to this
248   // registration.
249   if (registration()->is_uninstalling() ||
250       registration()->GetNewestVersion()->script_url() != script_url_) {
251     Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
252     return;
253   }
254
255   // TODO(michaeln): If the last update check was less than 24 hours
256   // ago, depending on the freshness of the cached worker script we
257   // may be able to complete the update job right here.
258
259   UpdateAndContinue();
260 }
261
262 // Creates a new ServiceWorkerRegistration.
263 void ServiceWorkerRegisterJob::RegisterAndContinue(
264     ServiceWorkerStatusCode status) {
265   SetPhase(REGISTER);
266   if (status != SERVICE_WORKER_OK) {
267     // Abort this registration job.
268     Complete(status);
269     return;
270   }
271
272   set_registration(new ServiceWorkerRegistration(
273       pattern_, context_->storage()->NewRegistrationId(), context_));
274   AssociateProviderHostsToRegistration(registration());
275   UpdateAndContinue();
276 }
277
278 void ServiceWorkerRegisterJob::WaitForUninstall(
279     const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
280   SetPhase(WAIT_FOR_UNINSTALL);
281   set_uninstalling_registration(existing_registration);
282   uninstalling_registration()->AddListener(this);
283 }
284
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;
291   }
292   set_registration(existing_registration);
293
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, existing_registration.get());
304     Complete(SERVICE_WORKER_OK);
305     return;
306   }
307
308   // "Return the result of running the [[Update]] algorithm, or its equivalent,
309   // passing registration as the argument."
310   UpdateAndContinue();
311 }
312
313 // This function corresponds to the spec's [[Update]] algorithm.
314 void ServiceWorkerRegisterJob::UpdateAndContinue() {
315   SetPhase(UPDATE);
316   context_->storage()->NotifyInstallingRegistration(registration());
317
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
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()));
332 }
333
334 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
335     ServiceWorkerStatusCode status) {
336   if (status == SERVICE_WORKER_OK) {
337     InstallAndContinue();
338     return;
339   }
340
341   // "If serviceWorker fails to start up..." then reject the promise with an
342   // error and abort. When there is a main script network error, the status will
343   // be updated to a more specific one.
344   const net::URLRequestStatus& main_script_status =
345       new_version()->script_cache_map()->main_script_status();
346   if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
347     switch (main_script_status.error()) {
348       case net::ERR_INSECURE_RESPONSE:
349       case net::ERR_UNSAFE_REDIRECT:
350         status = SERVICE_WORKER_ERROR_SECURITY;
351         break;
352       case net::ERR_ABORTED:
353         status = SERVICE_WORKER_ERROR_ABORT;
354         break;
355       default:
356         status = SERVICE_WORKER_ERROR_NETWORK;
357     }
358   }
359   Complete(status);
360 }
361
362 // This function corresponds to the spec's [[Install]] algorithm.
363 void ServiceWorkerRegisterJob::InstallAndContinue() {
364   SetPhase(INSTALL);
365
366   // "Set registration.installingWorker to worker."
367   DCHECK(!registration()->installing_version());
368   registration()->SetInstallingVersion(new_version());
369
370   // "Run the Update State algorithm passing registration's installing worker
371   // and installing as the arguments."
372   new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
373
374   // "Resolve registrationPromise with registration."
375   ResolvePromise(SERVICE_WORKER_OK, registration());
376
377   // "Fire a simple event named updatefound..."
378   registration()->NotifyUpdateFound();
379
380   // "Fire an event named install..."
381   new_version()->DispatchInstallEvent(
382       -1,
383       base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
384                  weak_factory_.GetWeakPtr()));
385 }
386
387 void ServiceWorkerRegisterJob::OnInstallFinished(
388     ServiceWorkerStatusCode status) {
389   // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
390   // unexpectedly terminated) we may want to retry sending the event again.
391   if (status != SERVICE_WORKER_OK) {
392     // "8. If installFailed is true, then:..."
393     Complete(status);
394     return;
395   }
396
397   SetPhase(STORE);
398   registration()->set_last_update_check(base::Time::Now());
399   context_->storage()->StoreRegistration(
400       registration(),
401       new_version(),
402       base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
403                  weak_factory_.GetWeakPtr()));
404 }
405
406 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
407     ServiceWorkerStatusCode status) {
408   if (status != SERVICE_WORKER_OK) {
409     Complete(status);
410     return;
411   }
412
413   // "9. If registration.waitingWorker is not null, then:..."
414   if (registration()->waiting_version()) {
415     // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
416     // and "redundant" as the arguments."
417     registration()->waiting_version()->SetStatus(
418         ServiceWorkerVersion::REDUNDANT);
419   }
420
421   // "10. Set registration.waitingWorker to registration.installingWorker."
422   // "11. Set registration.installingWorker to null."
423   registration()->SetWaitingVersion(new_version());
424
425   // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
426   // and "installed" as the arguments."
427   new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
428
429   // TODO(michaeln): "13. If activateImmediate is true, then..."
430
431   // "14. Wait until no document is using registration as their
432   // Service Worker registration."
433   registration()->ActivateWaitingVersionWhenReady();
434
435   Complete(SERVICE_WORKER_OK);
436 }
437
438 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
439   CompleteInternal(status);
440   context_->job_coordinator()->FinishJob(pattern_, this);
441 }
442
443 void ServiceWorkerRegisterJob::CompleteInternal(
444     ServiceWorkerStatusCode status) {
445   SetPhase(COMPLETE);
446   if (status != SERVICE_WORKER_OK) {
447     if (registration()) {
448       if (new_version()) {
449         registration()->UnsetVersion(new_version());
450         new_version()->Doom();
451       }
452       if (!registration()->waiting_version() &&
453           !registration()->active_version()) {
454         registration()->NotifyRegistrationFailed();
455         context_->storage()->DeleteRegistration(
456             registration()->id(),
457             registration()->pattern().GetOrigin(),
458             base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
459       }
460     }
461     if (!is_promise_resolved_)
462       ResolvePromise(status, NULL);
463   }
464   DCHECK(callbacks_.empty());
465   if (registration()) {
466     context_->storage()->NotifyDoneInstallingRegistration(
467         registration(), new_version(), status);
468   }
469   if (new_version())
470     new_version()->embedded_worker()->RemoveListener(this);
471 }
472
473 void ServiceWorkerRegisterJob::ResolvePromise(
474     ServiceWorkerStatusCode status,
475     ServiceWorkerRegistration* registration) {
476   DCHECK(!is_promise_resolved_);
477   is_promise_resolved_ = true;
478   promise_resolved_status_ = status;
479   promise_resolved_registration_ = registration;
480   for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
481        it != callbacks_.end();
482        ++it) {
483     it->Run(status, registration);
484   }
485   callbacks_.clear();
486 }
487
488 void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
489   // This happens prior to OnStartWorkerFinished time.
490   scoped_refptr<ServiceWorkerVersion> most_recent_version =
491       registration()->waiting_version() ?
492           registration()->waiting_version() :
493           registration()->active_version();
494   DCHECK(most_recent_version.get());
495   int64 most_recent_script_id =
496       most_recent_version->script_cache_map()->LookupResourceId(script_url_);
497   int64 new_script_id =
498       new_version()->script_cache_map()->LookupResourceId(script_url_);
499
500   // TODO(michaeln): It would be better to compare as the new resource
501   // is being downloaded and to avoid writing it to disk until we know
502   // its needed.
503   context_->storage()->CompareScriptResources(
504       most_recent_script_id,
505       new_script_id,
506       base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
507                  weak_factory_.GetWeakPtr()));
508 }
509
510 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
511   return false;
512 }
513
514 void ServiceWorkerRegisterJob::OnRegistrationFinishedUninstalling(
515     ServiceWorkerRegistration* existing_registration) {
516   DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
517   DCHECK_EQ(existing_registration, uninstalling_registration());
518   existing_registration->RemoveListener(this);
519   set_uninstalling_registration(NULL);
520   RegisterAndContinue(SERVICE_WORKER_OK);
521 }
522
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       registration()->set_last_update_check(base::Time::Now());
532       context_->storage()->UpdateLastUpdateCheckTime(registration());
533     }
534
535     ResolvePromise(SERVICE_WORKER_OK, registration());
536     Complete(SERVICE_WORKER_ERROR_EXISTS);
537     return;
538   }
539
540   // Proceed with really starting the worker.
541   new_version()->embedded_worker()->ResumeAfterDownload();
542   new_version()->embedded_worker()->RemoveListener(this);
543 }
544
545 void ServiceWorkerRegisterJob::AssociateProviderHostsToRegistration(
546     ServiceWorkerRegistration* registration) {
547   DCHECK(registration);
548   for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
549            context_->GetProviderHostIterator();
550        !it->IsAtEnd(); it->Advance()) {
551     ServiceWorkerProviderHost* host = it->GetProviderHost();
552     if (ServiceWorkerUtils::ScopeMatches(registration->pattern(),
553                                          host->document_url())) {
554       if (host->CanAssociateRegistration(registration))
555         host->AssociateRegistration(registration);
556     }
557   }
558 }
559
560 }  // namespace content