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),
35 resources_total_size_bytes_(0),
37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
39 context_
->AddLiveRegistration(this);
42 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
44 DCHECK(!listeners_
.might_have_observers());
46 context_
->RemoveLiveRegistration(registration_id_
);
48 active_version()->RemoveListener(this);
51 ServiceWorkerVersion
* ServiceWorkerRegistration::GetNewestVersion() const {
52 if (installing_version())
53 return installing_version();
54 if (waiting_version())
55 return waiting_version();
56 return active_version();
59 void ServiceWorkerRegistration::AddListener(Listener
* listener
) {
60 listeners_
.AddObserver(listener
);
63 void ServiceWorkerRegistration::RemoveListener(Listener
* listener
) {
64 listeners_
.RemoveObserver(listener
);
67 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
68 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
71 void ServiceWorkerRegistration::NotifyUpdateFound() {
72 FOR_EACH_OBSERVER(Listener
, listeners_
, OnUpdateFound(this));
75 ServiceWorkerRegistrationInfo
ServiceWorkerRegistration::GetInfo() {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
77 return ServiceWorkerRegistrationInfo(
80 GetVersionInfo(active_version_
.get()),
81 GetVersionInfo(waiting_version_
.get()),
82 GetVersionInfo(installing_version_
.get()),
83 resources_total_size_bytes_
);
86 void ServiceWorkerRegistration::SetActiveVersion(
87 ServiceWorkerVersion
* version
) {
88 should_activate_when_ready_
= false;
89 SetVersionInternal(version
, &active_version_
,
90 ChangedVersionAttributesMask::ACTIVE_VERSION
);
93 void ServiceWorkerRegistration::SetWaitingVersion(
94 ServiceWorkerVersion
* version
) {
95 should_activate_when_ready_
= false;
96 SetVersionInternal(version
, &waiting_version_
,
97 ChangedVersionAttributesMask::WAITING_VERSION
);
100 void ServiceWorkerRegistration::SetInstallingVersion(
101 ServiceWorkerVersion
* version
) {
102 SetVersionInternal(version
, &installing_version_
,
103 ChangedVersionAttributesMask::INSTALLING_VERSION
);
106 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion
* version
) {
109 ChangedVersionAttributesMask mask
;
110 UnsetVersionInternal(version
, &mask
);
111 if (mask
.changed()) {
112 ServiceWorkerRegistrationInfo info
= GetInfo();
113 FOR_EACH_OBSERVER(Listener
, listeners_
,
114 OnVersionAttributesChanged(this, mask
, info
));
118 void ServiceWorkerRegistration::SetVersionInternal(
119 ServiceWorkerVersion
* version
,
120 scoped_refptr
<ServiceWorkerVersion
>* data_member
,
122 if (version
== data_member
->get())
124 scoped_refptr
<ServiceWorkerVersion
> protect(version
);
125 ChangedVersionAttributesMask mask
;
127 UnsetVersionInternal(version
, &mask
);
128 *data_member
= version
;
129 if (active_version_
.get() && active_version_
.get() == version
)
130 active_version_
->AddListener(this);
131 mask
.add(change_flag
);
132 ServiceWorkerRegistrationInfo info
= GetInfo();
133 FOR_EACH_OBSERVER(Listener
, listeners_
,
134 OnVersionAttributesChanged(this, mask
, info
));
137 void ServiceWorkerRegistration::UnsetVersionInternal(
138 ServiceWorkerVersion
* version
,
139 ChangedVersionAttributesMask
* mask
) {
141 if (installing_version_
.get() == version
) {
142 installing_version_
= NULL
;
143 mask
->add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
144 } else if (waiting_version_
.get() == version
) {
145 waiting_version_
= NULL
;
146 mask
->add(ChangedVersionAttributesMask::WAITING_VERSION
);
147 } else if (active_version_
.get() == version
) {
148 active_version_
->RemoveListener(this);
149 active_version_
= NULL
;
150 mask
->add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
154 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
155 DCHECK(waiting_version());
156 should_activate_when_ready_
= true;
157 if (!active_version() || !active_version()->HasControllee())
158 ActivateWaitingVersion();
161 void ServiceWorkerRegistration::ClearWhenReady() {
163 if (is_uninstalling_
)
165 is_uninstalling_
= true;
167 context_
->storage()->NotifyUninstallingRegistration(this);
168 context_
->storage()->DeleteRegistration(
170 pattern().GetOrigin(),
171 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
173 if (!active_version() || !active_version()->HasControllee())
177 void ServiceWorkerRegistration::AbortPendingClear(
178 const StatusCallback
& callback
) {
180 if (!is_uninstalling()) {
181 callback
.Run(SERVICE_WORKER_OK
);
184 is_uninstalling_
= false;
185 context_
->storage()->NotifyDoneUninstallingRegistration(this);
187 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
188 waiting_version() ? waiting_version() : active_version();
189 DCHECK(most_recent_version
.get());
190 context_
->storage()->NotifyInstallingRegistration(this);
191 context_
->storage()->StoreRegistration(
193 most_recent_version
.get(),
194 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished
,
197 most_recent_version
));
200 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion
* version
) {
201 DCHECK_EQ(active_version(), version
);
202 if (is_uninstalling_
)
204 else if (should_activate_when_ready_
)
205 ActivateWaitingVersion();
206 is_uninstalling_
= false;
207 should_activate_when_ready_
= false;
210 void ServiceWorkerRegistration::ActivateWaitingVersion() {
212 DCHECK(waiting_version());
213 DCHECK(should_activate_when_ready_
);
214 should_activate_when_ready_
= false;
215 scoped_refptr
<ServiceWorkerVersion
> activating_version
= waiting_version();
216 scoped_refptr
<ServiceWorkerVersion
> exiting_version
= active_version();
218 if (activating_version
->is_doomed() ||
219 activating_version
->status() == ServiceWorkerVersion::REDUNDANT
) {
220 return; // Activation is no longer relevant.
223 // "4. If exitingWorker is not null,
224 if (exiting_version
.get()) {
225 DCHECK(!exiting_version
->HasControllee());
226 // TODO(michaeln): should wait for events to be complete
227 // "1. Wait for exitingWorker to finish handling any in-progress requests."
228 // "2. Terminate exitingWorker."
229 exiting_version
->StopWorker(
230 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
231 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
232 // "redundant" as the arguments."
233 exiting_version
->SetStatus(ServiceWorkerVersion::REDUNDANT
);
236 // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker."
237 // "6. Set serviceWorkerRegistration.waitingWorker to null."
238 SetActiveVersion(activating_version
.get());
240 // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and
241 // "activating" as arguments."
242 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATING
);
244 // TODO(nhiroki): "8. Fire a simple event named controllerchange..."
246 // "9. Queue a task to fire an event named activate..."
247 activating_version
->DispatchActivateEvent(
248 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished
,
249 this, activating_version
));
252 void ServiceWorkerRegistration::OnActivateEventFinished(
253 ServiceWorkerVersion
* activating_version
,
254 ServiceWorkerStatusCode status
) {
255 if (!context_
|| activating_version
!= active_version())
257 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
258 // unexpectedly terminated) we may want to retry sending the event again.
259 if (status
!= SERVICE_WORKER_OK
) {
260 // "11. If activateFailed is true, then:..."
261 UnsetVersion(activating_version
);
262 activating_version
->Doom();
263 if (!waiting_version()) {
264 // Delete the records from the db.
265 context_
->storage()->DeleteRegistration(
266 id(), pattern().GetOrigin(),
267 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished
, this));
268 // But not from memory if there is a version in the pipeline.
269 if (installing_version())
275 // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
276 // and "activated" as the arguments."
277 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATED
);
279 context_
->storage()->UpdateToActiveState(
281 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
285 void ServiceWorkerRegistration::OnDeleteFinished(
286 ServiceWorkerStatusCode status
) {
287 // Intentionally empty completion callback, used to prevent
288 // |this| from being deleted until the storage method completes.
291 void ServiceWorkerRegistration::Clear() {
292 is_uninstalling_
= false;
293 is_uninstalled_
= true;
295 context_
->storage()->NotifyDoneUninstallingRegistration(this);
297 ChangedVersionAttributesMask mask
;
298 if (installing_version_
.get()) {
299 installing_version_
->Doom();
300 installing_version_
= NULL
;
301 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
303 if (waiting_version_
.get()) {
304 waiting_version_
->Doom();
305 waiting_version_
= NULL
;
306 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
308 if (active_version_
.get()) {
309 active_version_
->Doom();
310 active_version_
->RemoveListener(this);
311 active_version_
= NULL
;
312 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
314 if (mask
.changed()) {
315 ServiceWorkerRegistrationInfo info
= GetInfo();
316 FOR_EACH_OBSERVER(Listener
, listeners_
,
317 OnVersionAttributesChanged(this, mask
, info
));
321 Listener
, listeners_
, OnRegistrationFinishedUninstalling(this));
324 void ServiceWorkerRegistration::OnRestoreFinished(
325 const StatusCallback
& callback
,
326 scoped_refptr
<ServiceWorkerVersion
> version
,
327 ServiceWorkerStatusCode status
) {
329 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
332 context_
->storage()->NotifyDoneInstallingRegistration(
333 this, version
.get(), status
);
334 callback
.Run(status
);
337 } // namespace content