Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_registration.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_registration.h"
6
7 #include "content/browser/service_worker/service_worker_context_core.h"
8 #include "content/browser/service_worker/service_worker_info.h"
9 #include "content/browser/service_worker/service_worker_register_job.h"
10 #include "content/browser/service_worker/service_worker_utils.h"
11 #include "content/public/browser/browser_thread.h"
12
13 namespace content {
14
15 namespace {
16
17 ServiceWorkerVersionInfo GetVersionInfo(ServiceWorkerVersion* version) {
18   if (!version)
19     return ServiceWorkerVersionInfo();
20   return version->GetInfo();
21 }
22
23 }  // namespace
24
25 ServiceWorkerRegistration::ServiceWorkerRegistration(
26     const GURL& pattern,
27     const GURL& script_url,
28     int64 registration_id,
29     base::WeakPtr<ServiceWorkerContextCore> context)
30     : pattern_(pattern),
31       script_url_(script_url),
32       registration_id_(registration_id),
33       is_deleted_(false),
34       is_uninstalling_(false),
35       should_activate_when_ready_(false),
36       context_(context) {
37   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
38   DCHECK(context_);
39   context_->AddLiveRegistration(this);
40 }
41
42 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
43   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
44   DCHECK(!listeners_.might_have_observers());
45   if (context_)
46     context_->RemoveLiveRegistration(registration_id_);
47   if (active_version())
48     active_version()->RemoveListener(this);
49 }
50
51 void ServiceWorkerRegistration::AddListener(Listener* listener) {
52   listeners_.AddObserver(listener);
53 }
54
55 void ServiceWorkerRegistration::RemoveListener(Listener* listener) {
56   listeners_.RemoveObserver(listener);
57 }
58
59 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
60   FOR_EACH_OBSERVER(Listener, listeners_, OnRegistrationFailed(this));
61 }
62
63 ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() {
64   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
65   return ServiceWorkerRegistrationInfo(
66       script_url(),
67       pattern(),
68       registration_id_,
69       GetVersionInfo(active_version_),
70       GetVersionInfo(waiting_version_),
71       GetVersionInfo(installing_version_));
72 }
73
74 void ServiceWorkerRegistration::SetActiveVersion(
75     ServiceWorkerVersion* version) {
76   should_activate_when_ready_ = false;
77   SetVersionInternal(version, &active_version_,
78                      ChangedVersionAttributesMask::ACTIVE_VERSION);
79 }
80
81 void ServiceWorkerRegistration::SetWaitingVersion(
82     ServiceWorkerVersion* version) {
83   should_activate_when_ready_ = false;
84   SetVersionInternal(version, &waiting_version_,
85                      ChangedVersionAttributesMask::WAITING_VERSION);
86 }
87
88 void ServiceWorkerRegistration::SetInstallingVersion(
89     ServiceWorkerVersion* version) {
90   SetVersionInternal(version, &installing_version_,
91                      ChangedVersionAttributesMask::INSTALLING_VERSION);
92 }
93
94 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion* version) {
95   if (!version)
96     return;
97   ChangedVersionAttributesMask mask;
98   UnsetVersionInternal(version, &mask);
99   if (mask.changed()) {
100     ServiceWorkerRegistrationInfo info = GetInfo();
101     FOR_EACH_OBSERVER(Listener, listeners_,
102                       OnVersionAttributesChanged(this, mask, info));
103   }
104 }
105
106 void ServiceWorkerRegistration::SetVersionInternal(
107     ServiceWorkerVersion* version,
108     scoped_refptr<ServiceWorkerVersion>* data_member,
109     int change_flag) {
110   if (version == data_member->get())
111     return;
112   scoped_refptr<ServiceWorkerVersion> protect(version);
113   ChangedVersionAttributesMask mask;
114   if (version)
115     UnsetVersionInternal(version, &mask);
116   *data_member = version;
117   if (active_version_ && active_version_ == version)
118     active_version_->AddListener(this);
119   mask.add(change_flag);
120   ServiceWorkerRegistrationInfo info = GetInfo();
121   FOR_EACH_OBSERVER(Listener, listeners_,
122                     OnVersionAttributesChanged(this, mask, info));
123 }
124
125 void ServiceWorkerRegistration::UnsetVersionInternal(
126     ServiceWorkerVersion* version,
127     ChangedVersionAttributesMask* mask) {
128   DCHECK(version);
129   if (installing_version_ == version) {
130     installing_version_ = NULL;
131     mask->add(ChangedVersionAttributesMask::INSTALLING_VERSION);
132   } else if (waiting_version_  == version) {
133     waiting_version_ = NULL;
134     mask->add(ChangedVersionAttributesMask::WAITING_VERSION);
135   } else if (active_version_ == version) {
136     active_version_->RemoveListener(this);
137     active_version_ = NULL;
138     mask->add(ChangedVersionAttributesMask::ACTIVE_VERSION);
139   }
140 }
141
142 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
143   DCHECK(waiting_version());
144   should_activate_when_ready_ = true;
145   if (!active_version() || !active_version()->HasControllee())
146     ActivateWaitingVersion();
147 }
148
149 void ServiceWorkerRegistration::ClearWhenReady() {
150   DCHECK(context_);
151   if (is_uninstalling_)
152     return;
153   is_uninstalling_ = true;
154
155   context_->storage()->NotifyUninstallingRegistration(this);
156   context_->storage()->DeleteRegistration(
157       id(),
158       script_url().GetOrigin(),
159       base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
160
161   if (!active_version() || !active_version()->HasControllee())
162     Clear();
163 }
164
165 void ServiceWorkerRegistration::AbortPendingClear() {
166   DCHECK(context_);
167   if (!is_uninstalling())
168     return;
169   is_uninstalling_ = false;
170   context_->storage()->NotifyDoneUninstallingRegistration(this);
171
172   scoped_refptr<ServiceWorkerVersion> most_recent_version =
173       waiting_version() ? waiting_version() : active_version();
174   DCHECK(most_recent_version);
175   context_->storage()->NotifyInstallingRegistration(this);
176   context_->storage()->StoreRegistration(
177       this,
178       most_recent_version,
179       base::Bind(&ServiceWorkerRegistration::OnStoreFinished,
180                  this,
181                  most_recent_version));
182 }
183
184 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) {
185   DCHECK_EQ(active_version(), version);
186   if (is_uninstalling_)
187     Clear();
188   else if (should_activate_when_ready_)
189     ActivateWaitingVersion();
190   is_uninstalling_ = false;
191   should_activate_when_ready_ = false;
192 }
193
194 void ServiceWorkerRegistration::ActivateWaitingVersion() {
195   DCHECK(context_);
196   DCHECK(waiting_version());
197   DCHECK(should_activate_when_ready_);
198   should_activate_when_ready_ = false;
199   scoped_refptr<ServiceWorkerVersion> activating_version = waiting_version();
200   scoped_refptr<ServiceWorkerVersion> exiting_version = active_version();
201
202   if (activating_version->is_doomed() ||
203       activating_version->status() == ServiceWorkerVersion::REDUNDANT) {
204     return;  // Activation is no longer relevant.
205   }
206
207   // "4. If exitingWorker is not null,
208   if (exiting_version) {
209     DCHECK(!exiting_version->HasControllee());
210     // TODO(michaeln): should wait for events to be complete
211     // "1. Wait for exitingWorker to finish handling any in-progress requests."
212     // "2. Terminate exitingWorker."
213     exiting_version->StopWorker(
214         base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
215     // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
216     // "redundant" as the arguments."
217     exiting_version->SetStatus(ServiceWorkerVersion::REDUNDANT);
218   }
219
220   // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker."
221   // "6. Set serviceWorkerRegistration.waitingWorker to null."
222   SetActiveVersion(activating_version);
223
224   // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and
225   // "activating" as arguments."
226   activating_version->SetStatus(ServiceWorkerVersion::ACTIVATING);
227
228   // TODO(nhiroki): "8. Fire a simple event named controllerchange..."
229
230   // "9. Queue a task to fire an event named activate..."
231   activating_version->DispatchActivateEvent(
232       base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished,
233                  this, activating_version));
234 }
235
236 void ServiceWorkerRegistration::OnActivateEventFinished(
237     ServiceWorkerVersion* activating_version,
238     ServiceWorkerStatusCode status) {
239   if (!context_ || activating_version != active_version())
240     return;
241   // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
242   // unexpectedly terminated) we may want to retry sending the event again.
243   if (status != SERVICE_WORKER_OK) {
244     // "11. If activateFailed is true, then:..."
245     UnsetVersion(activating_version);
246     activating_version->Doom();
247     if (!waiting_version()) {
248       // Delete the records from the db.
249       context_->storage()->DeleteRegistration(
250           id(), script_url().GetOrigin(),
251           base::Bind(&ServiceWorkerRegistration::OnDeleteFinished, this));
252       // But not from memory if there is a version in the pipeline.
253       if (installing_version())
254         is_deleted_ = false;
255     }
256     return;
257   }
258
259   // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
260   // and "activated" as the arguments."
261   activating_version->SetStatus(ServiceWorkerVersion::ACTIVATED);
262   if (context_) {
263     context_->storage()->UpdateToActiveState(
264         this,
265         base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
266   }
267 }
268
269 void ServiceWorkerRegistration::OnDeleteFinished(
270     ServiceWorkerStatusCode status) {
271   // Intentionally empty completion callback, used to prevent
272   // |this| from being deleted until the storage method completes.
273 }
274
275 void ServiceWorkerRegistration::Clear() {
276   context_->storage()->NotifyDoneUninstallingRegistration(this);
277
278   if (installing_version()) {
279     installing_version()->Doom();
280     UnsetVersion(installing_version());
281   }
282
283   if (waiting_version()) {
284     waiting_version()->Doom();
285     UnsetVersion(waiting_version());
286   }
287
288   if (active_version()) {
289     active_version()->Doom();
290     UnsetVersion(active_version());
291   }
292 }
293
294 void ServiceWorkerRegistration::OnStoreFinished(
295     scoped_refptr<ServiceWorkerVersion> version,
296     ServiceWorkerStatusCode status) {
297   if (!context_)
298     return;
299   context_->storage()->NotifyDoneInstallingRegistration(
300       this, version.get(), status);
301 }
302
303 }  // namespace content