Upstream version 10.39.225.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_,
73       promise_resolved_registration_, promise_resolved_version_));
74 }
75
76 void ServiceWorkerRegisterJob::Start() {
77   SetPhase(START);
78   ServiceWorkerStorage::FindRegistrationCallback next_step;
79   if (job_type_ == REGISTRATION_JOB) {
80     next_step = base::Bind(
81         &ServiceWorkerRegisterJob::ContinueWithRegistration,
82         weak_factory_.GetWeakPtr());
83   } else {
84     next_step = base::Bind(
85         &ServiceWorkerRegisterJob::ContinueWithUpdate,
86         weak_factory_.GetWeakPtr());
87   }
88
89   scoped_refptr<ServiceWorkerRegistration> registration =
90       context_->storage()->GetUninstallingRegistration(pattern_);
91   if (registration.get())
92     RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
93   else
94     context_->storage()->FindRegistrationForPattern(pattern_, next_step);
95 }
96
97 void ServiceWorkerRegisterJob::Abort() {
98   SetPhase(ABORT);
99   CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
100   // Don't have to call FinishJob() because the caller takes care of removing
101   // the jobs from the queue.
102 }
103
104 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
105   if (job->GetType() != GetType())
106     return false;
107   ServiceWorkerRegisterJob* register_job =
108       static_cast<ServiceWorkerRegisterJob*>(job);
109   return register_job->pattern_ == pattern_ &&
110          register_job->script_url_ == script_url_;
111 }
112
113 RegistrationJobType ServiceWorkerRegisterJob::GetType() {
114   return job_type_;
115 }
116
117 ServiceWorkerRegisterJob::Internal::Internal() {}
118
119 ServiceWorkerRegisterJob::Internal::~Internal() {}
120
121 void ServiceWorkerRegisterJob::set_registration(
122     const scoped_refptr<ServiceWorkerRegistration>& registration) {
123   DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
124   DCHECK(!internal_.registration.get());
125   internal_.registration = registration;
126 }
127
128 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
129   DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
130   return internal_.registration.get();
131 }
132
133 void ServiceWorkerRegisterJob::set_new_version(
134     ServiceWorkerVersion* version) {
135   DCHECK(phase_ == UPDATE) << phase_;
136   DCHECK(!internal_.new_version.get());
137   internal_.new_version = version;
138 }
139
140 ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
141   DCHECK(phase_ >= UPDATE) << phase_;
142   return internal_.new_version.get();
143 }
144
145 void ServiceWorkerRegisterJob::set_uninstalling_registration(
146     const scoped_refptr<ServiceWorkerRegistration>& registration) {
147   DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
148   internal_.uninstalling_registration = registration;
149 }
150
151 ServiceWorkerRegistration*
152 ServiceWorkerRegisterJob::uninstalling_registration() {
153   DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
154   return internal_.uninstalling_registration.get();
155 }
156
157 void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
158   switch (phase) {
159     case INITIAL:
160       NOTREACHED();
161       break;
162     case START:
163       DCHECK(phase_ == INITIAL) << phase_;
164       break;
165     case WAIT_FOR_UNINSTALL:
166       DCHECK(phase_ == START) << phase_;
167       break;
168     case REGISTER:
169       DCHECK(phase_ == START || phase_ == WAIT_FOR_UNINSTALL) << 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;
185   }
186   phase_ = phase;
187 }
188
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;
199   }
200
201   if (!existing_registration.get() || existing_registration->is_uninstalled()) {
202     RegisterAndContinue(SERVICE_WORKER_OK);
203     return;
204   }
205
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;
215   }
216
217   if (existing_registration->is_uninstalling()) {
218     // "Wait until the Record {[[key]], [[value]]} entry of its
219     // [[ScopeToRegistrationMap]] where registation.scope matches entry.[[key]]
220     // is deleted."
221     WaitForUninstall(existing_registration);
222     return;
223   }
224
225   // "Set registration.[[Uninstalling]] to false."
226   DCHECK(!existing_registration->is_uninstalling());
227
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();
232 }
233
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;
241   }
242
243   if (existing_registration.get() != registration()) {
244     Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
245     return;
246   }
247
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;
254   }
255
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.
259
260   UpdateAndContinue();
261 }
262
263 // Creates a new ServiceWorkerRegistration.
264 void ServiceWorkerRegisterJob::RegisterAndContinue(
265     ServiceWorkerStatusCode status) {
266   SetPhase(REGISTER);
267   if (status != SERVICE_WORKER_OK) {
268     // Abort this registration job.
269     Complete(status);
270     return;
271   }
272
273   set_registration(new ServiceWorkerRegistration(
274       pattern_, context_->storage()->NewRegistrationId(), context_));
275   AssociateProviderHostsToRegistration(registration());
276   UpdateAndContinue();
277 }
278
279 void ServiceWorkerRegisterJob::WaitForUninstall(
280     const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
281   SetPhase(WAIT_FOR_UNINSTALL);
282   set_uninstalling_registration(existing_registration);
283   uninstalling_registration()->AddListener(this);
284 }
285
286 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
287     const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
288     ServiceWorkerStatusCode status) {
289   if (status != SERVICE_WORKER_OK) {
290     Complete(status);
291     return;
292   }
293   set_registration(existing_registration);
294
295   // TODO(falken): Follow the spec: resolve the promise
296   // with the newest version.
297
298   if (!existing_registration->active_version()) {
299     UpdateAndContinue();
300     return;
301   }
302
303   ResolvePromise(status,
304                  existing_registration.get(),
305                  existing_registration->active_version());
306   Complete(SERVICE_WORKER_OK);
307 }
308
309 // This function corresponds to the spec's [[Update]] algorithm.
310 void ServiceWorkerRegisterJob::UpdateAndContinue() {
311   SetPhase(UPDATE);
312   context_->storage()->NotifyInstallingRegistration(registration());
313
314   // TODO(falken): "If serviceWorkerRegistration.installingWorker is not null.."
315   // then terminate the installing worker. It doesn't make sense to implement
316   // yet since we always activate the worker if install completed, so there can
317   // be no installing worker at this point.
318
319   // "Let serviceWorker be a newly-created ServiceWorker object..." and start
320   // the worker.
321   set_new_version(new ServiceWorkerVersion(registration(),
322                                            script_url_,
323                                            context_->storage()->NewVersionId(),
324                                            context_));
325
326   bool pause_after_download = job_type_ == UPDATE_JOB;
327   if (pause_after_download)
328     new_version()->embedded_worker()->AddListener(this);
329   new_version()->StartWorker(
330       pause_after_download,
331       base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
332                  weak_factory_.GetWeakPtr()));
333 }
334
335 void ServiceWorkerRegisterJob::OnStartWorkerFinished(
336     ServiceWorkerStatusCode status) {
337   if (status == SERVICE_WORKER_OK) {
338     InstallAndContinue();
339     return;
340   }
341
342   // "If serviceWorker fails to start up..." then reject the promise with an
343   // error and abort. When there is a main script network error, the status will
344   // be updated to a more specific one.
345   const net::URLRequestStatus& main_script_status =
346       new_version()->script_cache_map()->main_script_status();
347   if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
348     switch (main_script_status.error()) {
349       case net::ERR_INSECURE_RESPONSE:
350       case net::ERR_UNSAFE_REDIRECT:
351         status = SERVICE_WORKER_ERROR_SECURITY;
352         break;
353       case net::ERR_ABORTED:
354         status = SERVICE_WORKER_ERROR_ABORT;
355         break;
356       case net::ERR_FAILED:
357         status = SERVICE_WORKER_ERROR_NETWORK;
358         break;
359       default:
360         NOTREACHED();
361     }
362   }
363   Complete(status);
364 }
365
366 // This function corresponds to the spec's [[Install]] algorithm.
367 void ServiceWorkerRegisterJob::InstallAndContinue() {
368   SetPhase(INSTALL);
369
370   // "2. Set registration.installingWorker to worker."
371   registration()->SetInstallingVersion(new_version());
372
373   // "3. Resolve promise with registration."
374   ResolvePromise(SERVICE_WORKER_OK, registration(), new_version());
375
376   // "4. Run the [[UpdateState]] algorithm passing registration.installingWorker
377   // and "installing" as the arguments."
378   new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
379
380   // "5. Fire a simple event named updatefound..."
381   registration()->NotifyUpdateFound();
382
383   // "6. Fire an event named install..."
384   new_version()->DispatchInstallEvent(
385       -1,
386       base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
387                  weak_factory_.GetWeakPtr()));
388 }
389
390 void ServiceWorkerRegisterJob::OnInstallFinished(
391     ServiceWorkerStatusCode status) {
392   // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
393   // unexpectedly terminated) we may want to retry sending the event again.
394   if (status != SERVICE_WORKER_OK) {
395     // "8. If installFailed is true, then:..."
396     Complete(status);
397     return;
398   }
399
400   SetPhase(STORE);
401   registration()->set_last_update_check(base::Time::Now());
402   context_->storage()->StoreRegistration(
403       registration(),
404       new_version(),
405       base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
406                  weak_factory_.GetWeakPtr()));
407 }
408
409 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
410     ServiceWorkerStatusCode status) {
411   if (status != SERVICE_WORKER_OK) {
412     Complete(status);
413     return;
414   }
415
416   // "9. If registration.waitingWorker is not null, then:..."
417   if (registration()->waiting_version()) {
418     // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
419     // and "redundant" as the arguments."
420     registration()->waiting_version()->SetStatus(
421         ServiceWorkerVersion::REDUNDANT);
422   }
423
424   // "10. Set registration.waitingWorker to registration.installingWorker."
425   // "11. Set registration.installingWorker to null."
426   registration()->SetWaitingVersion(new_version());
427
428   // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
429   // and "installed" as the arguments."
430   new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
431
432   // TODO(michaeln): "13. If activateImmediate is true, then..."
433
434   // "14. Wait until no document is using registration as their
435   // Service Worker registration."
436   registration()->ActivateWaitingVersionWhenReady();
437
438   Complete(SERVICE_WORKER_OK);
439 }
440
441 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
442   CompleteInternal(status);
443   context_->job_coordinator()->FinishJob(pattern_, this);
444 }
445
446 void ServiceWorkerRegisterJob::CompleteInternal(
447     ServiceWorkerStatusCode status) {
448   SetPhase(COMPLETE);
449   if (status != SERVICE_WORKER_OK) {
450     if (registration()) {
451       if (new_version()) {
452         registration()->UnsetVersion(new_version());
453         new_version()->Doom();
454       }
455       if (!registration()->waiting_version() &&
456           !registration()->active_version()) {
457         registration()->NotifyRegistrationFailed();
458         context_->storage()->DeleteRegistration(
459             registration()->id(),
460             registration()->pattern().GetOrigin(),
461             base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
462       }
463     }
464     if (!is_promise_resolved_)
465       ResolvePromise(status, NULL, NULL);
466   }
467   DCHECK(callbacks_.empty());
468   if (registration()) {
469     context_->storage()->NotifyDoneInstallingRegistration(
470         registration(), new_version(), status);
471   }
472   if (new_version())
473     new_version()->embedded_worker()->RemoveListener(this);
474 }
475
476 void ServiceWorkerRegisterJob::ResolvePromise(
477     ServiceWorkerStatusCode status,
478     ServiceWorkerRegistration* registration,
479     ServiceWorkerVersion* version) {
480   DCHECK(!is_promise_resolved_);
481   is_promise_resolved_ = true;
482   promise_resolved_status_ = status;
483   promise_resolved_registration_ = registration;
484   promise_resolved_version_ = version;
485   for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
486        it != callbacks_.end();
487        ++it) {
488     it->Run(status, registration, version);
489   }
490   callbacks_.clear();
491 }
492
493 void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
494   // This happens prior to OnStartWorkerFinished time.
495   scoped_refptr<ServiceWorkerVersion> most_recent_version =
496       registration()->waiting_version() ?
497           registration()->waiting_version() :
498           registration()->active_version();
499   DCHECK(most_recent_version.get());
500   int64 most_recent_script_id =
501       most_recent_version->script_cache_map()->Lookup(script_url_);
502   int64 new_script_id =
503       new_version()->script_cache_map()->Lookup(script_url_);
504
505   // TODO(michaeln): It would be better to compare as the new resource
506   // is being downloaded and to avoid writing it to disk until we know
507   // its needed.
508   context_->storage()->CompareScriptResources(
509       most_recent_script_id, new_script_id,
510       base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
511                  weak_factory_.GetWeakPtr(),
512                  most_recent_version));
513 }
514
515 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
516   return false;
517 }
518
519 void ServiceWorkerRegisterJob::OnRegistrationFinishedUninstalling(
520     ServiceWorkerRegistration* existing_registration) {
521   DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
522   DCHECK_EQ(existing_registration, uninstalling_registration());
523   existing_registration->RemoveListener(this);
524   set_uninstalling_registration(NULL);
525   RegisterAndContinue(SERVICE_WORKER_OK);
526 }
527
528 void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
529     ServiceWorkerVersion* most_recent_version,
530     ServiceWorkerStatusCode status,
531     bool are_equal) {
532   if (are_equal) {
533     // Only bump the last check time when we've bypassed the browser cache.
534     base::TimeDelta time_since_last_check =
535         base::Time::Now() - registration()->last_update_check();
536     if (time_since_last_check > base::TimeDelta::FromHours(24)) {
537       registration()->set_last_update_check(base::Time::Now());
538       context_->storage()->UpdateLastUpdateCheckTime(registration());
539     }
540
541     ResolvePromise(SERVICE_WORKER_OK, registration(), most_recent_version);
542     Complete(SERVICE_WORKER_ERROR_EXISTS);
543     return;
544   }
545
546   // Proceed with really starting the worker.
547   new_version()->embedded_worker()->ResumeAfterDownload();
548   new_version()->embedded_worker()->RemoveListener(this);
549 }
550
551 void ServiceWorkerRegisterJob::AssociateProviderHostsToRegistration(
552     ServiceWorkerRegistration* registration) {
553   DCHECK(registration);
554   for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
555            context_->GetProviderHostIterator();
556        !it->IsAtEnd(); it->Advance()) {
557     ServiceWorkerProviderHost* host = it->GetProviderHost();
558     if (ServiceWorkerUtils::ScopeMatches(registration->pattern(),
559                                          host->document_url())) {
560       if (host->CanAssociateRegistration(registration))
561         host->AssociateRegistration(registration);
562     }
563   }
564 }
565
566 }  // namespace content