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 should_activate_when_ready_(false),
35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
37 context_
->AddLiveRegistration(this);
40 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
42 DCHECK(!listeners_
.might_have_observers());
44 context_
->RemoveLiveRegistration(registration_id_
);
46 active_version()->RemoveListener(this);
49 ServiceWorkerVersion
* ServiceWorkerRegistration::GetNewestVersion() const {
50 if (installing_version())
51 return installing_version();
52 if (waiting_version())
53 return waiting_version();
54 return active_version();
57 void ServiceWorkerRegistration::AddListener(Listener
* listener
) {
58 listeners_
.AddObserver(listener
);
61 void ServiceWorkerRegistration::RemoveListener(Listener
* listener
) {
62 listeners_
.RemoveObserver(listener
);
65 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
66 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
69 ServiceWorkerRegistrationInfo
ServiceWorkerRegistration::GetInfo() {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
71 return ServiceWorkerRegistrationInfo(
74 GetVersionInfo(active_version_
.get()),
75 GetVersionInfo(waiting_version_
.get()),
76 GetVersionInfo(installing_version_
.get()));
79 void ServiceWorkerRegistration::SetActiveVersion(
80 ServiceWorkerVersion
* version
) {
81 should_activate_when_ready_
= false;
82 SetVersionInternal(version
, &active_version_
,
83 ChangedVersionAttributesMask::ACTIVE_VERSION
);
86 void ServiceWorkerRegistration::SetWaitingVersion(
87 ServiceWorkerVersion
* version
) {
88 should_activate_when_ready_
= false;
89 SetVersionInternal(version
, &waiting_version_
,
90 ChangedVersionAttributesMask::WAITING_VERSION
);
93 void ServiceWorkerRegistration::SetInstallingVersion(
94 ServiceWorkerVersion
* version
) {
95 SetVersionInternal(version
, &installing_version_
,
96 ChangedVersionAttributesMask::INSTALLING_VERSION
);
99 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion
* version
) {
102 ChangedVersionAttributesMask mask
;
103 UnsetVersionInternal(version
, &mask
);
104 if (mask
.changed()) {
105 ServiceWorkerRegistrationInfo info
= GetInfo();
106 FOR_EACH_OBSERVER(Listener
, listeners_
,
107 OnVersionAttributesChanged(this, mask
, info
));
111 void ServiceWorkerRegistration::SetVersionInternal(
112 ServiceWorkerVersion
* version
,
113 scoped_refptr
<ServiceWorkerVersion
>* data_member
,
115 if (version
== data_member
->get())
117 scoped_refptr
<ServiceWorkerVersion
> protect(version
);
118 ChangedVersionAttributesMask mask
;
120 UnsetVersionInternal(version
, &mask
);
121 *data_member
= version
;
122 if (active_version_
.get() && active_version_
.get() == version
)
123 active_version_
->AddListener(this);
124 mask
.add(change_flag
);
125 ServiceWorkerRegistrationInfo info
= GetInfo();
126 FOR_EACH_OBSERVER(Listener
, listeners_
,
127 OnVersionAttributesChanged(this, mask
, info
));
130 void ServiceWorkerRegistration::UnsetVersionInternal(
131 ServiceWorkerVersion
* version
,
132 ChangedVersionAttributesMask
* mask
) {
134 if (installing_version_
.get() == version
) {
135 installing_version_
= NULL
;
136 mask
->add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
137 } else if (waiting_version_
.get() == version
) {
138 waiting_version_
= NULL
;
139 mask
->add(ChangedVersionAttributesMask::WAITING_VERSION
);
140 } else if (active_version_
.get() == version
) {
141 active_version_
->RemoveListener(this);
142 active_version_
= NULL
;
143 mask
->add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
147 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
148 DCHECK(waiting_version());
149 should_activate_when_ready_
= true;
150 if (!active_version() || !active_version()->HasControllee())
151 ActivateWaitingVersion();
154 void ServiceWorkerRegistration::ClearWhenReady() {
156 if (is_uninstalling_
)
158 is_uninstalling_
= true;
160 context_
->storage()->NotifyUninstallingRegistration(this);
161 context_
->storage()->DeleteRegistration(
163 pattern().GetOrigin(),
164 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
166 if (!active_version() || !active_version()->HasControllee())
170 void ServiceWorkerRegistration::AbortPendingClear(
171 const StatusCallback
& callback
) {
173 if (!is_uninstalling()) {
174 callback
.Run(SERVICE_WORKER_OK
);
177 is_uninstalling_
= false;
178 context_
->storage()->NotifyDoneUninstallingRegistration(this);
180 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
181 waiting_version() ? waiting_version() : active_version();
182 DCHECK(most_recent_version
.get());
183 context_
->storage()->NotifyInstallingRegistration(this);
184 context_
->storage()->StoreRegistration(
186 most_recent_version
.get(),
187 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished
,
190 most_recent_version
));
193 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion
* version
) {
194 DCHECK_EQ(active_version(), version
);
195 if (is_uninstalling_
)
197 else if (should_activate_when_ready_
)
198 ActivateWaitingVersion();
199 is_uninstalling_
= false;
200 should_activate_when_ready_
= false;
203 void ServiceWorkerRegistration::ActivateWaitingVersion() {
205 DCHECK(waiting_version());
206 DCHECK(should_activate_when_ready_
);
207 should_activate_when_ready_
= false;
208 scoped_refptr
<ServiceWorkerVersion
> activating_version
= waiting_version();
209 scoped_refptr
<ServiceWorkerVersion
> exiting_version
= active_version();
211 if (activating_version
->is_doomed() ||
212 activating_version
->status() == ServiceWorkerVersion::REDUNDANT
) {
213 return; // Activation is no longer relevant.
216 // "4. If exitingWorker is not null,
217 if (exiting_version
.get()) {
218 DCHECK(!exiting_version
->HasControllee());
219 // TODO(michaeln): should wait for events to be complete
220 // "1. Wait for exitingWorker to finish handling any in-progress requests."
221 // "2. Terminate exitingWorker."
222 exiting_version
->StopWorker(
223 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
224 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
225 // "redundant" as the arguments."
226 exiting_version
->SetStatus(ServiceWorkerVersion::REDUNDANT
);
229 // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker."
230 // "6. Set serviceWorkerRegistration.waitingWorker to null."
231 SetActiveVersion(activating_version
.get());
233 // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and
234 // "activating" as arguments."
235 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATING
);
237 // TODO(nhiroki): "8. Fire a simple event named controllerchange..."
239 // "9. Queue a task to fire an event named activate..."
240 activating_version
->DispatchActivateEvent(
241 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished
,
242 this, activating_version
));
245 void ServiceWorkerRegistration::OnActivateEventFinished(
246 ServiceWorkerVersion
* activating_version
,
247 ServiceWorkerStatusCode status
) {
248 if (!context_
|| activating_version
!= active_version())
250 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
251 // unexpectedly terminated) we may want to retry sending the event again.
252 if (status
!= SERVICE_WORKER_OK
) {
253 // "11. If activateFailed is true, then:..."
254 UnsetVersion(activating_version
);
255 activating_version
->Doom();
256 if (!waiting_version()) {
257 // Delete the records from the db.
258 context_
->storage()->DeleteRegistration(
259 id(), pattern().GetOrigin(),
260 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished
, this));
261 // But not from memory if there is a version in the pipeline.
262 if (installing_version())
268 // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
269 // and "activated" as the arguments."
270 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATED
);
272 context_
->storage()->UpdateToActiveState(
274 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
278 void ServiceWorkerRegistration::OnDeleteFinished(
279 ServiceWorkerStatusCode status
) {
280 // Intentionally empty completion callback, used to prevent
281 // |this| from being deleted until the storage method completes.
284 void ServiceWorkerRegistration::Clear() {
285 is_uninstalling_
= false;
287 context_
->storage()->NotifyDoneUninstallingRegistration(this);
289 ChangedVersionAttributesMask mask
;
290 if (installing_version_
.get()) {
291 installing_version_
->Doom();
292 installing_version_
= NULL
;
293 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
295 if (waiting_version_
.get()) {
296 waiting_version_
->Doom();
297 waiting_version_
= NULL
;
298 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
300 if (active_version_
.get()) {
301 active_version_
->Doom();
302 active_version_
->RemoveListener(this);
303 active_version_
= NULL
;
304 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
306 if (mask
.changed()) {
307 ServiceWorkerRegistrationInfo info
= GetInfo();
308 FOR_EACH_OBSERVER(Listener
, listeners_
,
309 OnVersionAttributesChanged(this, mask
, info
));
313 Listener
, listeners_
, OnRegistrationFinishedUninstalling(this));
316 void ServiceWorkerRegistration::OnRestoreFinished(
317 const StatusCallback
& callback
,
318 scoped_refptr
<ServiceWorkerVersion
> version
,
319 ServiceWorkerStatusCode status
) {
321 callback
.Run(ServiceWorkerStatusCode::SERVICE_WORKER_ERROR_ABORT
);
324 context_
->storage()->NotifyDoneInstallingRegistration(
325 this, version
.get(), status
);
326 callback
.Run(status
);
329 } // namespace content