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.
5 #include "content/browser/service_worker/service_worker_registration.h"
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"
17 ServiceWorkerVersionInfo GetVersionInfo(ServiceWorkerVersion* version) {
19 return ServiceWorkerVersionInfo();
20 return version->GetInfo();
25 ServiceWorkerRegistration::ServiceWorkerRegistration(
27 int64 registration_id,
28 base::WeakPtr<ServiceWorkerContextCore> context)
30 registration_id_(registration_id),
32 is_uninstalling_(false),
33 is_uninstalled_(false),
34 should_activate_when_ready_(false),
36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
38 context_->AddLiveRegistration(this);
41 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
43 DCHECK(!listeners_.might_have_observers());
45 context_->RemoveLiveRegistration(registration_id_);
47 active_version()->RemoveListener(this);
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();
58 void ServiceWorkerRegistration::AddListener(Listener* listener) {
59 listeners_.AddObserver(listener);
62 void ServiceWorkerRegistration::RemoveListener(Listener* listener) {
63 listeners_.RemoveObserver(listener);
66 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
67 FOR_EACH_OBSERVER(Listener, listeners_, OnRegistrationFailed(this));
70 void ServiceWorkerRegistration::NotifyUpdateFound() {
71 FOR_EACH_OBSERVER(Listener, listeners_, OnUpdateFound(this));
74 ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
76 return ServiceWorkerRegistrationInfo(
79 GetVersionInfo(active_version_.get()),
80 GetVersionInfo(waiting_version_.get()),
81 GetVersionInfo(installing_version_.get()));
84 void ServiceWorkerRegistration::SetActiveVersion(
85 ServiceWorkerVersion* version) {
86 should_activate_when_ready_ = false;
87 SetVersionInternal(version, &active_version_,
88 ChangedVersionAttributesMask::ACTIVE_VERSION);
91 void ServiceWorkerRegistration::SetWaitingVersion(
92 ServiceWorkerVersion* version) {
93 should_activate_when_ready_ = false;
94 SetVersionInternal(version, &waiting_version_,
95 ChangedVersionAttributesMask::WAITING_VERSION);
98 void ServiceWorkerRegistration::SetInstallingVersion(
99 ServiceWorkerVersion* version) {
100 SetVersionInternal(version, &installing_version_,
101 ChangedVersionAttributesMask::INSTALLING_VERSION);
104 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion* version) {
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));
116 void ServiceWorkerRegistration::SetVersionInternal(
117 ServiceWorkerVersion* version,
118 scoped_refptr<ServiceWorkerVersion>* data_member,
120 if (version == data_member->get())
122 scoped_refptr<ServiceWorkerVersion> protect(version);
123 ChangedVersionAttributesMask mask;
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));
135 void ServiceWorkerRegistration::UnsetVersionInternal(
136 ServiceWorkerVersion* version,
137 ChangedVersionAttributesMask* mask) {
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);
152 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
153 DCHECK(waiting_version());
154 should_activate_when_ready_ = true;
155 if (!active_version() || !active_version()->HasControllee())
156 ActivateWaitingVersion();
159 void ServiceWorkerRegistration::ClearWhenReady() {
161 if (is_uninstalling_)
163 is_uninstalling_ = true;
165 context_->storage()->NotifyUninstallingRegistration(this);
166 context_->storage()->DeleteRegistration(
168 pattern().GetOrigin(),
169 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
171 if (!active_version() || !active_version()->HasControllee())
175 void ServiceWorkerRegistration::AbortPendingClear(
176 const StatusCallback& callback) {
178 if (!is_uninstalling()) {
179 callback.Run(SERVICE_WORKER_OK);
182 is_uninstalling_ = false;
183 context_->storage()->NotifyDoneUninstallingRegistration(this);
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(
191 most_recent_version.get(),
192 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished,
195 most_recent_version));
198 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) {
199 DCHECK_EQ(active_version(), version);
200 if (is_uninstalling_)
202 else if (should_activate_when_ready_)
203 ActivateWaitingVersion();
204 is_uninstalling_ = false;
205 should_activate_when_ready_ = false;
208 void ServiceWorkerRegistration::ActivateWaitingVersion() {
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();
216 if (activating_version->is_doomed() ||
217 activating_version->status() == ServiceWorkerVersion::REDUNDANT) {
218 return; // Activation is no longer relevant.
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);
234 // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker."
235 // "6. Set serviceWorkerRegistration.waitingWorker to null."
236 SetActiveVersion(activating_version.get());
238 // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and
239 // "activating" as arguments."
240 activating_version->SetStatus(ServiceWorkerVersion::ACTIVATING);
242 // TODO(nhiroki): "8. Fire a simple event named controllerchange..."
244 // "9. Queue a task to fire an event named activate..."
245 activating_version->DispatchActivateEvent(
246 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished,
247 this, activating_version));
250 void ServiceWorkerRegistration::OnActivateEventFinished(
251 ServiceWorkerVersion* activating_version,
252 ServiceWorkerStatusCode status) {
253 if (!context_ || activating_version != active_version())
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())
273 // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
274 // and "activated" as the arguments."
275 activating_version->SetStatus(ServiceWorkerVersion::ACTIVATED);
277 context_->storage()->UpdateToActiveState(
279 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
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.
289 void ServiceWorkerRegistration::Clear() {
290 is_uninstalling_ = false;
291 is_uninstalled_ = true;
293 context_->storage()->NotifyDoneUninstallingRegistration(this);
295 ChangedVersionAttributesMask mask;
296 if (installing_version_.get()) {
297 installing_version_->Doom();
298 installing_version_ = NULL;
299 mask.add(ChangedVersionAttributesMask::INSTALLING_VERSION);
301 if (waiting_version_.get()) {
302 waiting_version_->Doom();
303 waiting_version_ = NULL;
304 mask.add(ChangedVersionAttributesMask::WAITING_VERSION);
306 if (active_version_.get()) {
307 active_version_->Doom();
308 active_version_->RemoveListener(this);
309 active_version_ = NULL;
310 mask.add(ChangedVersionAttributesMask::ACTIVE_VERSION);
312 if (mask.changed()) {
313 ServiceWorkerRegistrationInfo info = GetInfo();
314 FOR_EACH_OBSERVER(Listener, listeners_,
315 OnVersionAttributesChanged(this, mask, info));
319 Listener, listeners_, OnRegistrationFinishedUninstalling(this));
322 void ServiceWorkerRegistration::OnRestoreFinished(
323 const StatusCallback& callback,
324 scoped_refptr<ServiceWorkerVersion> version,
325 ServiceWorkerStatusCode status) {
327 callback.Run(SERVICE_WORKER_ERROR_ABORT);
330 context_->storage()->NotifyDoneInstallingRegistration(
331 this, version.get(), status);
332 callback.Run(status);
335 } // namespace content