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/common/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 force_update_on_page_load_(false),
37 resources_total_size_bytes_(0),
39 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
41 context_
->AddLiveRegistration(this);
44 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
45 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
46 DCHECK(!listeners_
.might_have_observers());
48 context_
->RemoveLiveRegistration(registration_id_
);
50 active_version()->RemoveListener(this);
53 ServiceWorkerVersion
* ServiceWorkerRegistration::GetNewestVersion() const {
54 if (installing_version())
55 return installing_version();
56 if (waiting_version())
57 return waiting_version();
58 return active_version();
61 void ServiceWorkerRegistration::AddListener(Listener
* listener
) {
62 listeners_
.AddObserver(listener
);
65 void ServiceWorkerRegistration::RemoveListener(Listener
* listener
) {
66 listeners_
.RemoveObserver(listener
);
69 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
70 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
73 void ServiceWorkerRegistration::NotifyUpdateFound() {
74 FOR_EACH_OBSERVER(Listener
, listeners_
, OnUpdateFound(this));
77 ServiceWorkerRegistrationInfo
ServiceWorkerRegistration::GetInfo() {
78 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
79 return ServiceWorkerRegistrationInfo(
80 pattern(), registration_id_
,
81 is_deleted_
? ServiceWorkerRegistrationInfo::IS_DELETED
82 : ServiceWorkerRegistrationInfo::IS_NOT_DELETED
,
83 force_update_on_page_load_
? ServiceWorkerRegistrationInfo::IS_FORCED
84 : ServiceWorkerRegistrationInfo::IS_NOT_FORCED
,
85 GetVersionInfo(active_version_
.get()),
86 GetVersionInfo(waiting_version_
.get()),
87 GetVersionInfo(installing_version_
.get()), resources_total_size_bytes_
);
90 void ServiceWorkerRegistration::SetActiveVersion(
91 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
92 should_activate_when_ready_
= false;
93 if (active_version_
== version
)
96 ChangedVersionAttributesMask mask
;
98 UnsetVersionInternal(version
.get(), &mask
);
100 active_version_
->RemoveListener(this);
101 active_version_
= version
;
103 active_version_
->AddListener(this);
104 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
106 FOR_EACH_OBSERVER(Listener
, listeners_
,
107 OnVersionAttributesChanged(this, mask
, GetInfo()));
110 void ServiceWorkerRegistration::SetWaitingVersion(
111 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
112 should_activate_when_ready_
= false;
113 if (waiting_version_
== version
)
116 ChangedVersionAttributesMask mask
;
118 UnsetVersionInternal(version
.get(), &mask
);
119 waiting_version_
= version
;
120 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
122 FOR_EACH_OBSERVER(Listener
, listeners_
,
123 OnVersionAttributesChanged(this, mask
, GetInfo()));
126 void ServiceWorkerRegistration::SetInstallingVersion(
127 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
128 if (installing_version_
== version
)
131 ChangedVersionAttributesMask mask
;
133 UnsetVersionInternal(version
.get(), &mask
);
134 installing_version_
= version
;
135 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
137 FOR_EACH_OBSERVER(Listener
, listeners_
,
138 OnVersionAttributesChanged(this, mask
, GetInfo()));
141 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion
* version
) {
144 ChangedVersionAttributesMask mask
;
145 UnsetVersionInternal(version
, &mask
);
146 if (mask
.changed()) {
147 ServiceWorkerRegistrationInfo info
= GetInfo();
148 FOR_EACH_OBSERVER(Listener
, listeners_
,
149 OnVersionAttributesChanged(this, mask
, info
));
153 void ServiceWorkerRegistration::UnsetVersionInternal(
154 ServiceWorkerVersion
* version
,
155 ChangedVersionAttributesMask
* mask
) {
157 if (installing_version_
.get() == version
) {
158 installing_version_
= NULL
;
159 mask
->add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
160 } else if (waiting_version_
.get() == version
) {
161 waiting_version_
= NULL
;
162 mask
->add(ChangedVersionAttributesMask::WAITING_VERSION
);
163 } else if (active_version_
.get() == version
) {
164 active_version_
->RemoveListener(this);
165 active_version_
= NULL
;
166 mask
->add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
170 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
171 DCHECK(waiting_version());
172 should_activate_when_ready_
= true;
174 if (!active_version() || !active_version()->HasControllee() ||
175 waiting_version()->skip_waiting())
176 ActivateWaitingVersion();
179 void ServiceWorkerRegistration::ClaimClients() {
181 DCHECK(active_version());
183 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
184 context_
->GetProviderHostIterator();
185 !it
->IsAtEnd(); it
->Advance()) {
186 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
187 if (host
->IsHostToRunningServiceWorker())
189 if (host
->controlling_version() == active_version())
191 if (host
->MatchRegistration() == this)
192 host
->ClaimedByRegistration(this);
196 void ServiceWorkerRegistration::ClearWhenReady() {
198 if (is_uninstalling_
)
200 is_uninstalling_
= true;
202 context_
->storage()->NotifyUninstallingRegistration(this);
203 context_
->storage()->DeleteRegistration(
205 pattern().GetOrigin(),
206 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
208 if (!active_version() || !active_version()->HasControllee())
212 void ServiceWorkerRegistration::AbortPendingClear(
213 const StatusCallback
& callback
) {
215 if (!is_uninstalling()) {
216 callback
.Run(SERVICE_WORKER_OK
);
219 is_uninstalling_
= false;
220 context_
->storage()->NotifyDoneUninstallingRegistration(this);
222 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
223 waiting_version() ? waiting_version() : active_version();
224 DCHECK(most_recent_version
.get());
225 context_
->storage()->NotifyInstallingRegistration(this);
226 context_
->storage()->StoreRegistration(
228 most_recent_version
.get(),
229 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished
,
232 most_recent_version
));
235 void ServiceWorkerRegistration::GetUserData(
236 const std::string
& key
,
237 const GetUserDataCallback
& callback
) {
239 context_
->storage()->GetUserData(registration_id_
, key
, callback
);
242 void ServiceWorkerRegistration::StoreUserData(
243 const std::string
& key
,
244 const std::string
& data
,
245 const StatusCallback
& callback
) {
247 context_
->storage()->StoreUserData(
248 registration_id_
, pattern().GetOrigin(), key
, data
, callback
);
251 void ServiceWorkerRegistration::ClearUserData(
252 const std::string
& key
,
253 const StatusCallback
& callback
) {
255 context_
->storage()->ClearUserData(registration_id_
, key
, callback
);
258 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion
* version
) {
261 DCHECK_EQ(active_version(), version
);
262 if (is_uninstalling_
)
264 else if (should_activate_when_ready_
)
265 ActivateWaitingVersion();
266 is_uninstalling_
= false;
267 should_activate_when_ready_
= false;
270 void ServiceWorkerRegistration::ActivateWaitingVersion() {
272 DCHECK(waiting_version());
273 DCHECK(should_activate_when_ready_
);
274 should_activate_when_ready_
= false;
275 scoped_refptr
<ServiceWorkerVersion
> activating_version
= waiting_version();
276 scoped_refptr
<ServiceWorkerVersion
> exiting_version
= active_version();
278 if (activating_version
->is_redundant())
279 return; // Activation is no longer relevant.
281 // "5. If exitingWorker is not null,
282 if (exiting_version
.get()) {
283 // TODO(michaeln): should wait for events to be complete
284 // "1. Wait for exitingWorker to finish handling any in-progress requests."
285 // "2. Terminate exitingWorker."
286 exiting_version
->StopWorker(
287 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
288 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
289 // "redundant" as the arguments."
290 exiting_version
->SetStatus(ServiceWorkerVersion::REDUNDANT
);
293 // "6. Set serviceWorkerRegistration.activeWorker to activatingWorker."
294 // "7. Set serviceWorkerRegistration.waitingWorker to null."
295 SetActiveVersion(activating_version
);
297 // "8. Run the [[UpdateState]] algorithm passing registration.activeWorker and
298 // "activating" as arguments."
299 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATING
);
300 // "9. Fire a simple event named controllerchange..."
301 if (activating_version
->skip_waiting())
302 FOR_EACH_OBSERVER(Listener
, listeners_
, OnSkippedWaiting(this));
304 // "10. Queue a task to fire an event named activate..."
305 activating_version
->DispatchActivateEvent(
306 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished
,
307 this, activating_version
));
310 void ServiceWorkerRegistration::DeleteVersion(
311 const scoped_refptr
<ServiceWorkerVersion
>& version
) {
312 DCHECK_EQ(id(), version
->registration_id());
314 UnsetVersion(version
.get());
316 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
317 context_
->GetProviderHostIterator();
318 !it
->IsAtEnd(); it
->Advance()) {
319 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
320 if (host
->controlling_version() == version
)
321 host
->NotifyControllerLost();
326 if (!active_version() && !waiting_version()) {
327 // Delete the records from the db.
328 context_
->storage()->DeleteRegistration(
329 id(), pattern().GetOrigin(),
330 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished
, this));
331 // But not from memory if there is a version in the pipeline.
332 // TODO(falken): Fix this logic. There could be a running register job for
333 // this registration that hasn't set installing_version() yet.
334 if (installing_version()) {
337 is_uninstalled_
= true;
338 FOR_EACH_OBSERVER(Listener
, listeners_
, OnRegistrationFailed(this));
343 void ServiceWorkerRegistration::OnActivateEventFinished(
344 ServiceWorkerVersion
* activating_version
,
345 ServiceWorkerStatusCode status
) {
346 if (!context_
|| activating_version
!= active_version() ||
347 activating_version
->status() != ServiceWorkerVersion::ACTIVATING
)
350 // |status| is just for UMA. Once we've attempted to dispatch the activate
351 // event to an installed worker, it's committed to becoming active.
352 ServiceWorkerMetrics::RecordActivateEventStatus(status
);
354 // "Run the Update State algorithm passing registration's active worker and
355 // 'activated' as the arguments."
356 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATED
);
357 context_
->storage()->UpdateToActiveState(
358 this, base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
361 void ServiceWorkerRegistration::OnDeleteFinished(
362 ServiceWorkerStatusCode status
) {
363 // Intentionally empty completion callback, used to prevent
364 // |this| from being deleted until the storage method completes.
367 void ServiceWorkerRegistration::Clear() {
368 is_uninstalling_
= false;
369 is_uninstalled_
= true;
371 context_
->storage()->NotifyDoneUninstallingRegistration(this);
373 ChangedVersionAttributesMask mask
;
374 if (installing_version_
.get()) {
375 installing_version_
->Doom();
376 installing_version_
= NULL
;
377 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
379 if (waiting_version_
.get()) {
380 waiting_version_
->Doom();
381 waiting_version_
= NULL
;
382 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
384 if (active_version_
.get()) {
385 active_version_
->Doom();
386 active_version_
->RemoveListener(this);
387 active_version_
= NULL
;
388 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
390 if (mask
.changed()) {
391 ServiceWorkerRegistrationInfo info
= GetInfo();
392 FOR_EACH_OBSERVER(Listener
, listeners_
,
393 OnVersionAttributesChanged(this, mask
, info
));
397 Listener
, listeners_
, OnRegistrationFinishedUninstalling(this));
400 void ServiceWorkerRegistration::OnRestoreFinished(
401 const StatusCallback
& callback
,
402 scoped_refptr
<ServiceWorkerVersion
> version
,
403 ServiceWorkerStatusCode status
) {
405 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
408 context_
->storage()->NotifyDoneInstallingRegistration(
409 this, version
.get(), status
);
410 callback
.Run(status
);
413 } // namespace content