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 const GURL
& script_url
,
28 int64 registration_id
,
29 base::WeakPtr
<ServiceWorkerContextCore
> context
)
31 script_url_(script_url
),
32 registration_id_(registration_id
),
34 is_uninstalling_(false),
35 should_activate_when_ready_(false),
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 void ServiceWorkerRegistration::AddListener(Listener
* listener
) {
52 listeners_
.AddObserver(listener
);
55 void ServiceWorkerRegistration::RemoveListener(Listener
* listener
) {
56 listeners_
.RemoveObserver(listener
);
59 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
60 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
63 ServiceWorkerRegistrationInfo
ServiceWorkerRegistration::GetInfo() {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
65 return ServiceWorkerRegistrationInfo(
69 GetVersionInfo(active_version_
),
70 GetVersionInfo(waiting_version_
),
71 GetVersionInfo(installing_version_
));
74 void ServiceWorkerRegistration::SetActiveVersion(
75 ServiceWorkerVersion
* version
) {
76 should_activate_when_ready_
= false;
77 SetVersionInternal(version
, &active_version_
,
78 ChangedVersionAttributesMask::ACTIVE_VERSION
);
81 void ServiceWorkerRegistration::SetWaitingVersion(
82 ServiceWorkerVersion
* version
) {
83 should_activate_when_ready_
= false;
84 SetVersionInternal(version
, &waiting_version_
,
85 ChangedVersionAttributesMask::WAITING_VERSION
);
88 void ServiceWorkerRegistration::SetInstallingVersion(
89 ServiceWorkerVersion
* version
) {
90 SetVersionInternal(version
, &installing_version_
,
91 ChangedVersionAttributesMask::INSTALLING_VERSION
);
94 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion
* version
) {
97 ChangedVersionAttributesMask mask
;
98 UnsetVersionInternal(version
, &mask
);
100 ServiceWorkerRegistrationInfo info
= GetInfo();
101 FOR_EACH_OBSERVER(Listener
, listeners_
,
102 OnVersionAttributesChanged(this, mask
, info
));
106 void ServiceWorkerRegistration::SetVersionInternal(
107 ServiceWorkerVersion
* version
,
108 scoped_refptr
<ServiceWorkerVersion
>* data_member
,
110 if (version
== data_member
->get())
112 scoped_refptr
<ServiceWorkerVersion
> protect(version
);
113 ChangedVersionAttributesMask mask
;
115 UnsetVersionInternal(version
, &mask
);
116 *data_member
= version
;
117 if (active_version_
&& active_version_
== version
)
118 active_version_
->AddListener(this);
119 mask
.add(change_flag
);
120 ServiceWorkerRegistrationInfo info
= GetInfo();
121 FOR_EACH_OBSERVER(Listener
, listeners_
,
122 OnVersionAttributesChanged(this, mask
, info
));
125 void ServiceWorkerRegistration::UnsetVersionInternal(
126 ServiceWorkerVersion
* version
,
127 ChangedVersionAttributesMask
* mask
) {
129 if (installing_version_
== version
) {
130 installing_version_
= NULL
;
131 mask
->add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
132 } else if (waiting_version_
== version
) {
133 waiting_version_
= NULL
;
134 mask
->add(ChangedVersionAttributesMask::WAITING_VERSION
);
135 } else if (active_version_
== version
) {
136 active_version_
->RemoveListener(this);
137 active_version_
= NULL
;
138 mask
->add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
142 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
143 DCHECK(waiting_version());
144 should_activate_when_ready_
= true;
145 if (!active_version() || !active_version()->HasControllee())
146 ActivateWaitingVersion();
149 void ServiceWorkerRegistration::ClearWhenReady() {
151 if (is_uninstalling_
)
153 is_uninstalling_
= true;
155 context_
->storage()->NotifyUninstallingRegistration(this);
156 context_
->storage()->DeleteRegistration(
158 script_url().GetOrigin(),
159 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
161 if (!active_version() || !active_version()->HasControllee())
165 void ServiceWorkerRegistration::AbortPendingClear() {
167 if (!is_uninstalling())
169 is_uninstalling_
= false;
170 context_
->storage()->NotifyDoneUninstallingRegistration(this);
172 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
173 waiting_version() ? waiting_version() : active_version();
174 DCHECK(most_recent_version
);
175 context_
->storage()->NotifyInstallingRegistration(this);
176 context_
->storage()->StoreRegistration(
179 base::Bind(&ServiceWorkerRegistration::OnStoreFinished
,
181 most_recent_version
));
184 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion
* version
) {
185 DCHECK_EQ(active_version(), version
);
186 if (is_uninstalling_
)
188 else if (should_activate_when_ready_
)
189 ActivateWaitingVersion();
190 is_uninstalling_
= false;
191 should_activate_when_ready_
= false;
194 void ServiceWorkerRegistration::ActivateWaitingVersion() {
196 DCHECK(waiting_version());
197 DCHECK(should_activate_when_ready_
);
198 should_activate_when_ready_
= false;
199 scoped_refptr
<ServiceWorkerVersion
> activating_version
= waiting_version();
200 scoped_refptr
<ServiceWorkerVersion
> exiting_version
= active_version();
202 if (activating_version
->is_doomed() ||
203 activating_version
->status() == ServiceWorkerVersion::REDUNDANT
) {
204 return; // Activation is no longer relevant.
207 // "4. If exitingWorker is not null,
208 if (exiting_version
) {
209 DCHECK(!exiting_version
->HasControllee());
210 // TODO(michaeln): should wait for events to be complete
211 // "1. Wait for exitingWorker to finish handling any in-progress requests."
212 // "2. Terminate exitingWorker."
213 exiting_version
->StopWorker(
214 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
215 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
216 // "redundant" as the arguments."
217 exiting_version
->SetStatus(ServiceWorkerVersion::REDUNDANT
);
220 // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker."
221 // "6. Set serviceWorkerRegistration.waitingWorker to null."
222 SetActiveVersion(activating_version
);
224 // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and
225 // "activating" as arguments."
226 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATING
);
228 // TODO(nhiroki): "8. Fire a simple event named controllerchange..."
230 // "9. Queue a task to fire an event named activate..."
231 activating_version
->DispatchActivateEvent(
232 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished
,
233 this, activating_version
));
236 void ServiceWorkerRegistration::OnActivateEventFinished(
237 ServiceWorkerVersion
* activating_version
,
238 ServiceWorkerStatusCode status
) {
239 if (!context_
|| activating_version
!= active_version())
241 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
242 // unexpectedly terminated) we may want to retry sending the event again.
243 if (status
!= SERVICE_WORKER_OK
) {
244 // "11. If activateFailed is true, then:..."
245 UnsetVersion(activating_version
);
246 activating_version
->Doom();
247 if (!waiting_version()) {
248 // Delete the records from the db.
249 context_
->storage()->DeleteRegistration(
250 id(), script_url().GetOrigin(),
251 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished
, this));
252 // But not from memory if there is a version in the pipeline.
253 if (installing_version())
259 // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
260 // and "activated" as the arguments."
261 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATED
);
263 context_
->storage()->UpdateToActiveState(
265 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
269 void ServiceWorkerRegistration::OnDeleteFinished(
270 ServiceWorkerStatusCode status
) {
271 // Intentionally empty completion callback, used to prevent
272 // |this| from being deleted until the storage method completes.
275 void ServiceWorkerRegistration::Clear() {
276 context_
->storage()->NotifyDoneUninstallingRegistration(this);
278 ChangedVersionAttributesMask mask
;
279 if (installing_version_
) {
280 installing_version_
->Doom();
281 installing_version_
= NULL
;
282 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
284 if (waiting_version_
) {
285 waiting_version_
->Doom();
286 waiting_version_
= NULL
;
287 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
289 if (active_version_
) {
290 active_version_
->Doom();
291 active_version_
->RemoveListener(this);
292 active_version_
= NULL
;
293 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
295 if (mask
.changed()) {
296 ServiceWorkerRegistrationInfo info
= GetInfo();
297 FOR_EACH_OBSERVER(Listener
, listeners_
,
298 OnVersionAttributesChanged(this, mask
, info
));
302 void ServiceWorkerRegistration::OnStoreFinished(
303 scoped_refptr
<ServiceWorkerVersion
> version
,
304 ServiceWorkerStatusCode status
) {
307 context_
->storage()->NotifyDoneInstallingRegistration(
308 this, version
.get(), status
);
311 } // namespace content