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_metrics.h"
10 #include "content/browser/service_worker/service_worker_register_job.h"
11 #include "content/browser/service_worker/service_worker_utils.h"
12 #include "content/public/browser/browser_thread.h"
18 ServiceWorkerVersionInfo
GetVersionInfo(ServiceWorkerVersion
* version
) {
20 return ServiceWorkerVersionInfo();
21 return version
->GetInfo();
26 ServiceWorkerRegistration::ServiceWorkerRegistration(
28 int64 registration_id
,
29 base::WeakPtr
<ServiceWorkerContextCore
> context
)
31 registration_id_(registration_id
),
33 is_uninstalling_(false),
34 is_uninstalled_(false),
35 should_activate_when_ready_(false),
36 resources_total_size_bytes_(0),
38 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
40 context_
->AddLiveRegistration(this);
43 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
44 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
45 DCHECK(!listeners_
.might_have_observers());
47 context_
->RemoveLiveRegistration(registration_id_
);
49 active_version()->RemoveListener(this);
52 ServiceWorkerVersion
* ServiceWorkerRegistration::GetNewestVersion() const {
53 if (installing_version())
54 return installing_version();
55 if (waiting_version())
56 return waiting_version();
57 return active_version();
60 void ServiceWorkerRegistration::AddListener(Listener
* listener
) {
61 listeners_
.AddObserver(listener
);
64 void ServiceWorkerRegistration::RemoveListener(Listener
* listener
) {
65 listeners_
.RemoveObserver(listener
);
68 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
69 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
72 void ServiceWorkerRegistration::NotifyUpdateFound() {
73 FOR_EACH_OBSERVER(Listener
, listeners_
, OnUpdateFound(this));
76 ServiceWorkerRegistrationInfo
ServiceWorkerRegistration::GetInfo() {
77 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
78 return ServiceWorkerRegistrationInfo(
79 pattern(), registration_id_
,
80 is_deleted_
? ServiceWorkerRegistrationInfo::IS_DELETED
81 : ServiceWorkerRegistrationInfo::IS_NOT_DELETED
,
82 GetVersionInfo(active_version_
.get()),
83 GetVersionInfo(waiting_version_
.get()),
84 GetVersionInfo(installing_version_
.get()), resources_total_size_bytes_
);
87 void ServiceWorkerRegistration::SetActiveVersion(
88 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
89 should_activate_when_ready_
= false;
90 if (active_version_
== version
)
93 ChangedVersionAttributesMask mask
;
95 UnsetVersionInternal(version
.get(), &mask
);
97 active_version_
->RemoveListener(this);
98 active_version_
= version
;
100 active_version_
->AddListener(this);
101 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
103 FOR_EACH_OBSERVER(Listener
, listeners_
,
104 OnVersionAttributesChanged(this, mask
, GetInfo()));
107 void ServiceWorkerRegistration::SetWaitingVersion(
108 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
109 should_activate_when_ready_
= false;
110 if (waiting_version_
== version
)
113 ChangedVersionAttributesMask mask
;
115 UnsetVersionInternal(version
.get(), &mask
);
116 waiting_version_
= version
;
117 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
119 FOR_EACH_OBSERVER(Listener
, listeners_
,
120 OnVersionAttributesChanged(this, mask
, GetInfo()));
123 void ServiceWorkerRegistration::SetInstallingVersion(
124 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
125 if (installing_version_
== version
)
128 ChangedVersionAttributesMask mask
;
130 UnsetVersionInternal(version
.get(), &mask
);
131 installing_version_
= version
;
132 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
134 FOR_EACH_OBSERVER(Listener
, listeners_
,
135 OnVersionAttributesChanged(this, mask
, GetInfo()));
138 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion
* version
) {
141 ChangedVersionAttributesMask mask
;
142 UnsetVersionInternal(version
, &mask
);
143 if (mask
.changed()) {
144 ServiceWorkerRegistrationInfo info
= GetInfo();
145 FOR_EACH_OBSERVER(Listener
, listeners_
,
146 OnVersionAttributesChanged(this, mask
, info
));
150 void ServiceWorkerRegistration::UnsetVersionInternal(
151 ServiceWorkerVersion
* version
,
152 ChangedVersionAttributesMask
* mask
) {
154 if (installing_version_
.get() == version
) {
155 installing_version_
= NULL
;
156 mask
->add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
157 } else if (waiting_version_
.get() == version
) {
158 waiting_version_
= NULL
;
159 mask
->add(ChangedVersionAttributesMask::WAITING_VERSION
);
160 } else if (active_version_
.get() == version
) {
161 active_version_
->RemoveListener(this);
162 active_version_
= NULL
;
163 mask
->add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
167 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
168 DCHECK(waiting_version());
169 should_activate_when_ready_
= true;
171 if (!active_version() || !active_version()->HasControllee() ||
172 waiting_version()->skip_waiting())
173 ActivateWaitingVersion();
176 void ServiceWorkerRegistration::ClaimClients() {
178 DCHECK(active_version());
180 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
181 context_
->GetProviderHostIterator();
182 !it
->IsAtEnd(); it
->Advance()) {
183 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
184 if (host
->IsHostToRunningServiceWorker())
186 if (host
->controlling_version() == active_version())
188 if (host
->MatchRegistration() == this)
189 host
->ClaimedByRegistration(this);
193 void ServiceWorkerRegistration::ClearWhenReady() {
195 if (is_uninstalling_
)
197 is_uninstalling_
= true;
199 context_
->storage()->NotifyUninstallingRegistration(this);
200 context_
->storage()->DeleteRegistration(
202 pattern().GetOrigin(),
203 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
205 if (!active_version() || !active_version()->HasControllee())
209 void ServiceWorkerRegistration::AbortPendingClear(
210 const StatusCallback
& callback
) {
212 if (!is_uninstalling()) {
213 callback
.Run(SERVICE_WORKER_OK
);
216 is_uninstalling_
= false;
217 context_
->storage()->NotifyDoneUninstallingRegistration(this);
219 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
220 waiting_version() ? waiting_version() : active_version();
221 DCHECK(most_recent_version
.get());
222 context_
->storage()->NotifyInstallingRegistration(this);
223 context_
->storage()->StoreRegistration(
225 most_recent_version
.get(),
226 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished
,
229 most_recent_version
));
232 void ServiceWorkerRegistration::GetUserData(
233 const std::string
& key
,
234 const GetUserDataCallback
& callback
) {
236 context_
->storage()->GetUserData(registration_id_
, key
, callback
);
239 void ServiceWorkerRegistration::StoreUserData(
240 const std::string
& key
,
241 const std::string
& data
,
242 const StatusCallback
& callback
) {
244 context_
->storage()->StoreUserData(
245 registration_id_
, pattern().GetOrigin(), key
, data
, callback
);
248 void ServiceWorkerRegistration::ClearUserData(
249 const std::string
& key
,
250 const StatusCallback
& callback
) {
252 context_
->storage()->ClearUserData(registration_id_
, key
, callback
);
255 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion
* version
) {
258 DCHECK_EQ(active_version(), version
);
259 if (is_uninstalling_
)
261 else if (should_activate_when_ready_
)
262 ActivateWaitingVersion();
263 is_uninstalling_
= false;
264 should_activate_when_ready_
= false;
267 void ServiceWorkerRegistration::ActivateWaitingVersion() {
269 DCHECK(waiting_version());
270 DCHECK(should_activate_when_ready_
);
271 should_activate_when_ready_
= false;
272 scoped_refptr
<ServiceWorkerVersion
> activating_version
= waiting_version();
273 scoped_refptr
<ServiceWorkerVersion
> exiting_version
= active_version();
275 if (activating_version
->is_redundant())
276 return; // Activation is no longer relevant.
278 // "5. If exitingWorker is not null,
279 if (exiting_version
.get()) {
280 // TODO(michaeln): should wait for events to be complete
281 // "1. Wait for exitingWorker to finish handling any in-progress requests."
282 // "2. Terminate exitingWorker."
283 exiting_version
->StopWorker(
284 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
285 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
286 // "redundant" as the arguments."
287 exiting_version
->SetStatus(ServiceWorkerVersion::REDUNDANT
);
290 // "6. Set serviceWorkerRegistration.activeWorker to activatingWorker."
291 // "7. Set serviceWorkerRegistration.waitingWorker to null."
292 SetActiveVersion(activating_version
);
294 // "8. Run the [[UpdateState]] algorithm passing registration.activeWorker and
295 // "activating" as arguments."
296 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATING
);
297 // "9. Fire a simple event named controllerchange..."
298 if (activating_version
->skip_waiting())
299 FOR_EACH_OBSERVER(Listener
, listeners_
, OnSkippedWaiting(this));
301 // "10. Queue a task to fire an event named activate..."
302 activating_version
->DispatchActivateEvent(
303 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished
,
304 this, activating_version
));
307 void ServiceWorkerRegistration::DeleteVersion(
308 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
309 DCHECK_EQ(id(), version
->registration_id());
311 UnsetVersion(version
.get());
313 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
314 context_
->GetProviderHostIterator();
315 !it
->IsAtEnd(); it
->Advance()) {
316 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
317 if (host
->controlling_version() == version
)
318 host
->NotifyControllerLost();
323 if (!active_version() && !waiting_version()) {
324 // Delete the records from the db.
325 context_
->storage()->DeleteRegistration(
326 id(), pattern().GetOrigin(),
327 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished
, this));
328 // But not from memory if there is a version in the pipeline.
329 // TODO(falken): Fix this logic. There could be a running register job for
330 // this registration that hasn't set installing_version() yet.
331 if (installing_version()) {
334 is_uninstalled_
= true;
335 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
340 void ServiceWorkerRegistration::OnActivateEventFinished(
341 ServiceWorkerVersion
* activating_version
,
342 ServiceWorkerStatusCode status
) {
343 if (!context_
|| activating_version
!= active_version() ||
344 activating_version
->status() != ServiceWorkerVersion::ACTIVATING
)
347 // |status| is just for UMA. Once we've attempted to dispatch the activate
348 // event to an installed worker, it's committed to becoming active.
349 ServiceWorkerMetrics::RecordActivateEventStatus(status
);
351 // "Run the Update State algorithm passing registration's active worker and
352 // 'activated' as the arguments."
353 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATED
);
354 context_
->storage()->UpdateToActiveState(
355 this, base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
358 void ServiceWorkerRegistration::OnDeleteFinished(
359 ServiceWorkerStatusCode status
) {
360 // Intentionally empty completion callback, used to prevent
361 // |this| from being deleted until the storage method completes.
364 void ServiceWorkerRegistration::Clear() {
365 is_uninstalling_
= false;
366 is_uninstalled_
= true;
368 context_
->storage()->NotifyDoneUninstallingRegistration(this);
370 ChangedVersionAttributesMask mask
;
371 if (installing_version_
.get()) {
372 installing_version_
->Doom();
373 installing_version_
= NULL
;
374 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
376 if (waiting_version_
.get()) {
377 waiting_version_
->Doom();
378 waiting_version_
= NULL
;
379 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
381 if (active_version_
.get()) {
382 active_version_
->Doom();
383 active_version_
->RemoveListener(this);
384 active_version_
= NULL
;
385 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
387 if (mask
.changed()) {
388 ServiceWorkerRegistrationInfo info
= GetInfo();
389 FOR_EACH_OBSERVER(Listener
, listeners_
,
390 OnVersionAttributesChanged(this, mask
, info
));
394 Listener
, listeners_
, OnRegistrationFinishedUninstalling(this));
397 void ServiceWorkerRegistration::OnRestoreFinished(
398 const StatusCallback
& callback
,
399 scoped_refptr
<ServiceWorkerVersion
> version
,
400 ServiceWorkerStatusCode status
) {
402 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
405 context_
->storage()->NotifyDoneInstallingRegistration(
406 this, version
.get(), status
);
407 callback
.Run(status
);
410 } // namespace content