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 if (*data_member
&& *data_member
== active_version_
)
129 active_version_
->RemoveListener(this);
130 *data_member
= version
;
131 if (active_version_
.get() && active_version_
.get() == version
)
132 active_version_
->AddListener(this);
133 mask
.add(change_flag
);
134 ServiceWorkerRegistrationInfo info
= GetInfo();
135 FOR_EACH_OBSERVER(Listener
, listeners_
,
136 OnVersionAttributesChanged(this, mask
, info
));
139 void ServiceWorkerRegistration::UnsetVersionInternal(
140 ServiceWorkerVersion
* version
,
141 ChangedVersionAttributesMask
* mask
) {
143 if (installing_version_
.get() == version
) {
144 installing_version_
= NULL
;
145 mask
->add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
146 } else if (waiting_version_
.get() == version
) {
147 waiting_version_
= NULL
;
148 mask
->add(ChangedVersionAttributesMask::WAITING_VERSION
);
149 } else if (active_version_
.get() == version
) {
150 active_version_
->RemoveListener(this);
151 active_version_
= NULL
;
152 mask
->add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
156 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
157 DCHECK(waiting_version());
158 should_activate_when_ready_
= true;
160 if (!active_version() || !active_version()->HasControllee() ||
161 waiting_version()->skip_waiting())
162 ActivateWaitingVersion();
165 void ServiceWorkerRegistration::ClaimClients(const StatusCallback
& callback
) {
167 DCHECK(active_version());
168 // TODO(xiang): Should better not hit the database http://crbug.com/454250.
169 context_
->storage()->GetRegistrationsForOrigin(
170 pattern_
.GetOrigin(),
171 base::Bind(&ServiceWorkerRegistration::DidGetRegistrationsForClaimClients
,
172 this, callback
, active_version_
));
175 void ServiceWorkerRegistration::ClearWhenReady() {
177 if (is_uninstalling_
)
179 is_uninstalling_
= true;
181 context_
->storage()->NotifyUninstallingRegistration(this);
182 context_
->storage()->DeleteRegistration(
184 pattern().GetOrigin(),
185 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
187 if (!active_version() || !active_version()->HasControllee())
191 void ServiceWorkerRegistration::AbortPendingClear(
192 const StatusCallback
& callback
) {
194 if (!is_uninstalling()) {
195 callback
.Run(SERVICE_WORKER_OK
);
198 is_uninstalling_
= false;
199 context_
->storage()->NotifyDoneUninstallingRegistration(this);
201 scoped_refptr
<ServiceWorkerVersion
> most_recent_version
=
202 waiting_version() ? waiting_version() : active_version();
203 DCHECK(most_recent_version
.get());
204 context_
->storage()->NotifyInstallingRegistration(this);
205 context_
->storage()->StoreRegistration(
207 most_recent_version
.get(),
208 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished
,
211 most_recent_version
));
214 void ServiceWorkerRegistration::GetUserData(
215 const std::string
& key
,
216 const GetUserDataCallback
& callback
) {
218 context_
->storage()->GetUserData(registration_id_
, key
, callback
);
221 void ServiceWorkerRegistration::StoreUserData(
222 const std::string
& key
,
223 const std::string
& data
,
224 const StatusCallback
& callback
) {
226 context_
->storage()->StoreUserData(
227 registration_id_
, pattern().GetOrigin(), key
, data
, callback
);
230 void ServiceWorkerRegistration::ClearUserData(
231 const std::string
& key
,
232 const StatusCallback
& callback
) {
234 context_
->storage()->ClearUserData(registration_id_
, key
, callback
);
237 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion
* version
) {
238 DCHECK_EQ(active_version(), version
);
239 if (is_uninstalling_
)
241 else if (should_activate_when_ready_
)
242 ActivateWaitingVersion();
243 is_uninstalling_
= false;
244 should_activate_when_ready_
= false;
247 void ServiceWorkerRegistration::ActivateWaitingVersion() {
249 DCHECK(waiting_version());
250 DCHECK(should_activate_when_ready_
);
251 should_activate_when_ready_
= false;
252 scoped_refptr
<ServiceWorkerVersion
> activating_version
= waiting_version();
253 scoped_refptr
<ServiceWorkerVersion
> exiting_version
= active_version();
255 if (activating_version
->is_doomed() ||
256 activating_version
->status() == ServiceWorkerVersion::REDUNDANT
) {
257 return; // Activation is no longer relevant.
260 // "5. If exitingWorker is not null,
261 if (exiting_version
.get()) {
262 // TODO(michaeln): should wait for events to be complete
263 // "1. Wait for exitingWorker to finish handling any in-progress requests."
264 // "2. Terminate exitingWorker."
265 exiting_version
->StopWorker(
266 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
267 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
268 // "redundant" as the arguments."
269 exiting_version
->SetStatus(ServiceWorkerVersion::REDUNDANT
);
272 // "6. Set serviceWorkerRegistration.activeWorker to activatingWorker."
273 // "7. Set serviceWorkerRegistration.waitingWorker to null."
274 SetActiveVersion(activating_version
.get());
276 // "8. Run the [[UpdateState]] algorithm passing registration.activeWorker and
277 // "activating" as arguments."
278 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATING
);
279 // "9. Fire a simple event named controllerchange..."
280 if (activating_version
->skip_waiting())
281 FOR_EACH_OBSERVER(Listener
, listeners_
, OnSkippedWaiting(this));
283 // "10. Queue a task to fire an event named activate..."
284 activating_version
->DispatchActivateEvent(
285 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished
,
286 this, activating_version
));
289 void ServiceWorkerRegistration::OnActivateEventFinished(
290 ServiceWorkerVersion
* activating_version
,
291 ServiceWorkerStatusCode status
) {
292 if (!context_
|| activating_version
!= active_version())
294 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
295 // unexpectedly terminated) we may want to retry sending the event again.
296 if (status
!= SERVICE_WORKER_OK
) {
297 // "11. If activateFailed is true, then:..."
298 UnsetVersion(activating_version
);
299 activating_version
->Doom();
300 if (!waiting_version()) {
301 // Delete the records from the db.
302 context_
->storage()->DeleteRegistration(
303 id(), pattern().GetOrigin(),
304 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished
, this));
305 // But not from memory if there is a version in the pipeline.
306 if (installing_version())
309 is_uninstalled_
= true;
314 // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
315 // and "activated" as the arguments."
316 activating_version
->SetStatus(ServiceWorkerVersion::ACTIVATED
);
318 context_
->storage()->UpdateToActiveState(
320 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
324 void ServiceWorkerRegistration::OnDeleteFinished(
325 ServiceWorkerStatusCode status
) {
326 // Intentionally empty completion callback, used to prevent
327 // |this| from being deleted until the storage method completes.
330 void ServiceWorkerRegistration::Clear() {
331 is_uninstalling_
= false;
332 is_uninstalled_
= true;
334 context_
->storage()->NotifyDoneUninstallingRegistration(this);
336 ChangedVersionAttributesMask mask
;
337 if (installing_version_
.get()) {
338 installing_version_
->Doom();
339 installing_version_
= NULL
;
340 mask
.add(ChangedVersionAttributesMask::INSTALLING_VERSION
);
342 if (waiting_version_
.get()) {
343 waiting_version_
->Doom();
344 waiting_version_
= NULL
;
345 mask
.add(ChangedVersionAttributesMask::WAITING_VERSION
);
347 if (active_version_
.get()) {
348 active_version_
->Doom();
349 active_version_
->RemoveListener(this);
350 active_version_
= NULL
;
351 mask
.add(ChangedVersionAttributesMask::ACTIVE_VERSION
);
353 if (mask
.changed()) {
354 ServiceWorkerRegistrationInfo info
= GetInfo();
355 FOR_EACH_OBSERVER(Listener
, listeners_
,
356 OnVersionAttributesChanged(this, mask
, info
));
360 void ServiceWorkerRegistration::OnRestoreFinished(
361 const StatusCallback
& callback
,
362 scoped_refptr
<ServiceWorkerVersion
> version
,
363 ServiceWorkerStatusCode status
) {
365 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
368 context_
->storage()->NotifyDoneInstallingRegistration(
369 this, version
.get(), status
);
370 callback
.Run(status
);
373 void ServiceWorkerRegistration::DidGetRegistrationsForClaimClients(
374 const StatusCallback
& callback
,
375 scoped_refptr
<ServiceWorkerVersion
> version
,
376 const std::vector
<ServiceWorkerRegistrationInfo
>& registrations
) {
378 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
381 if (!active_version() || version
!= active_version()) {
382 callback
.Run(SERVICE_WORKER_ERROR_STATE
);
386 for (scoped_ptr
<ServiceWorkerContextCore::ProviderHostIterator
> it
=
387 context_
->GetProviderHostIterator();
388 !it
->IsAtEnd(); it
->Advance()) {
389 ServiceWorkerProviderHost
* host
= it
->GetProviderHost();
390 if (ShouldClaim(host
, registrations
))
391 host
->ClaimedByRegistration(this);
393 callback
.Run(SERVICE_WORKER_OK
);
396 bool ServiceWorkerRegistration::ShouldClaim(
397 ServiceWorkerProviderHost
* provider_host
,
398 const std::vector
<ServiceWorkerRegistrationInfo
>& registrations
) {
399 if (provider_host
->IsHostToRunningServiceWorker())
401 if (provider_host
->controlling_version() == active_version())
404 LongestScopeMatcher
matcher(provider_host
->document_url());
405 if (!matcher
.MatchLongest(pattern_
))
407 for (const ServiceWorkerRegistrationInfo
& info
: registrations
) {
408 ServiceWorkerRegistration
* registration
=
409 context_
->GetLiveRegistration(info
.registration_id
);
411 (registration
->is_uninstalling() || registration
->is_uninstalled()))
413 if (matcher
.MatchLongest(info
.pattern
))
419 } // namespace content