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