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 if (installing_version()) {
279 installing_version()->Doom();
280 UnsetVersion(installing_version());
283 if (waiting_version()) {
284 waiting_version()->Doom();
285 UnsetVersion(waiting_version());
288 if (active_version()) {
289 active_version()->Doom();
290 UnsetVersion(active_version());
294 void ServiceWorkerRegistration::OnStoreFinished(
295 scoped_refptr<ServiceWorkerVersion> version,
296 ServiceWorkerStatusCode status) {
299 context_->storage()->NotifyDoneInstallingRegistration(
300 this, version.get(), status);
303 } // namespace content