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
13namespace content {
14
15namespace {
16
17ServiceWorkerVersionInfo GetVersionInfo(ServiceWorkerVersion* version) {
18  if (!version)
19    return ServiceWorkerVersionInfo();
20  return version->GetInfo();
21}
22
23}  // namespace
24
25ServiceWorkerRegistration::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
41ServiceWorkerRegistration::~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
50ServiceWorkerVersion* 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
58void ServiceWorkerRegistration::AddListener(Listener* listener) {
59  listeners_.AddObserver(listener);
60}
61
62void ServiceWorkerRegistration::RemoveListener(Listener* listener) {
63  listeners_.RemoveObserver(listener);
64}
65
66void ServiceWorkerRegistration::NotifyRegistrationFailed() {
67  FOR_EACH_OBSERVER(Listener, listeners_, OnRegistrationFailed(this));
68}
69
70void ServiceWorkerRegistration::NotifyUpdateFound() {
71  FOR_EACH_OBSERVER(Listener, listeners_, OnUpdateFound(this));
72}
73
74ServiceWorkerRegistrationInfo 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
84void ServiceWorkerRegistration::SetActiveVersion(
85    ServiceWorkerVersion* version) {
86  should_activate_when_ready_ = false;
87  SetVersionInternal(version, &active_version_,
88                     ChangedVersionAttributesMask::ACTIVE_VERSION);
89}
90
91void ServiceWorkerRegistration::SetWaitingVersion(
92    ServiceWorkerVersion* version) {
93  should_activate_when_ready_ = false;
94  SetVersionInternal(version, &waiting_version_,
95                     ChangedVersionAttributesMask::WAITING_VERSION);
96}
97
98void ServiceWorkerRegistration::SetInstallingVersion(
99    ServiceWorkerVersion* version) {
100  SetVersionInternal(version, &installing_version_,
101                     ChangedVersionAttributesMask::INSTALLING_VERSION);
102}
103
104void 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
116void 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
135void 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
152void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
153  DCHECK(waiting_version());
154  should_activate_when_ready_ = true;
155  if (!active_version() || !active_version()->HasControllee())
156    ActivateWaitingVersion();
157}
158
159void 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
175void 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
198void 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
208void 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
250void 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
283void 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
289void 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
322void 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
336