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. 4 5 #include "content/browser/service_worker/service_worker_registration.h" 6 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" 12 13 namespace content { 14 15 namespace { 16 17 ServiceWorkerVersionInfo GetVersionInfo(ServiceWorkerVersion* version) { 18 if (!version) 19 return ServiceWorkerVersionInfo(); 20 return version->GetInfo(); 21 } 22 23 } // namespace 24 25 ServiceWorkerRegistration::ServiceWorkerRegistration( 26 const GURL& pattern, 27 int64 registration_id, 28 base::WeakPtr<ServiceWorkerContextCore> context) 29 : pattern_(pattern), 30 registration_id_(registration_id), 31 is_deleted_(false), 32 is_uninstalling_(false), 33 is_uninstalled_(false), 34 should_activate_when_ready_(false), 35 context_(context) { 36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 37 DCHECK(context_); 38 context_->AddLiveRegistration(this); 39 } 40 41 ServiceWorkerRegistration::~ServiceWorkerRegistration() { 42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 43 DCHECK(!listeners_.might_have_observers()); 44 if (context_) 45 context_->RemoveLiveRegistration(registration_id_); 46 if (active_version()) 47 active_version()->RemoveListener(this); 48 } 49 50 ServiceWorkerVersion* ServiceWorkerRegistration::GetNewestVersion() const { 51 if (installing_version()) 52 return installing_version(); 53 if (waiting_version()) 54 return waiting_version(); 55 return active_version(); 56 } 57 58 void ServiceWorkerRegistration::AddListener(Listener* listener) { 59 listeners_.AddObserver(listener); 60 } 61 62 void ServiceWorkerRegistration::RemoveListener(Listener* listener) { 63 listeners_.RemoveObserver(listener); 64 } 65 66 void ServiceWorkerRegistration::NotifyRegistrationFailed() { 67 FOR_EACH_OBSERVER(Listener, listeners_, OnRegistrationFailed(this)); 68 } 69 70 void ServiceWorkerRegistration::NotifyUpdateFound() { 71 FOR_EACH_OBSERVER(Listener, listeners_, OnUpdateFound(this)); 72 } 73 74 ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() { 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 76 return ServiceWorkerRegistrationInfo( 77 pattern(), 78 registration_id_, 79 GetVersionInfo(active_version_.get()), 80 GetVersionInfo(waiting_version_.get()), 81 GetVersionInfo(installing_version_.get())); 82 } 83 84 void ServiceWorkerRegistration::SetActiveVersion( 85 ServiceWorkerVersion* version) { 86 should_activate_when_ready_ = false; 87 SetVersionInternal(version, &active_version_, 88 ChangedVersionAttributesMask::ACTIVE_VERSION); 89 } 90 91 void ServiceWorkerRegistration::SetWaitingVersion( 92 ServiceWorkerVersion* version) { 93 should_activate_when_ready_ = false; 94 SetVersionInternal(version, &waiting_version_, 95 ChangedVersionAttributesMask::WAITING_VERSION); 96 } 97 98 void ServiceWorkerRegistration::SetInstallingVersion( 99 ServiceWorkerVersion* version) { 100 SetVersionInternal(version, &installing_version_, 101 ChangedVersionAttributesMask::INSTALLING_VERSION); 102 } 103 104 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion* version) { 105 if (!version) 106 return; 107 ChangedVersionAttributesMask mask; 108 UnsetVersionInternal(version, &mask); 109 if (mask.changed()) { 110 ServiceWorkerRegistrationInfo info = GetInfo(); 111 FOR_EACH_OBSERVER(Listener, listeners_, 112 OnVersionAttributesChanged(this, mask, info)); 113 } 114 } 115 116 void ServiceWorkerRegistration::SetVersionInternal( 117 ServiceWorkerVersion* version, 118 scoped_refptr<ServiceWorkerVersion>* data_member, 119 int change_flag) { 120 if (version == data_member->get()) 121 return; 122 scoped_refptr<ServiceWorkerVersion> protect(version); 123 ChangedVersionAttributesMask mask; 124 if (version) 125 UnsetVersionInternal(version, &mask); 126 *data_member = version; 127 if (active_version_.get() && active_version_.get() == version) 128 active_version_->AddListener(this); 129 mask.add(change_flag); 130 ServiceWorkerRegistrationInfo info = GetInfo(); 131 FOR_EACH_OBSERVER(Listener, listeners_, 132 OnVersionAttributesChanged(this, mask, info)); 133 } 134 135 void ServiceWorkerRegistration::UnsetVersionInternal( 136 ServiceWorkerVersion* version, 137 ChangedVersionAttributesMask* mask) { 138 DCHECK(version); 139 if (installing_version_.get() == version) { 140 installing_version_ = NULL; 141 mask->add(ChangedVersionAttributesMask::INSTALLING_VERSION); 142 } else if (waiting_version_.get() == version) { 143 waiting_version_ = NULL; 144 mask->add(ChangedVersionAttributesMask::WAITING_VERSION); 145 } else if (active_version_.get() == version) { 146 active_version_->RemoveListener(this); 147 active_version_ = NULL; 148 mask->add(ChangedVersionAttributesMask::ACTIVE_VERSION); 149 } 150 } 151 152 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() { 153 DCHECK(waiting_version()); 154 should_activate_when_ready_ = true; 155 if (!active_version() || !active_version()->HasControllee()) 156 ActivateWaitingVersion(); 157 } 158 159 void ServiceWorkerRegistration::ClearWhenReady() { 160 DCHECK(context_); 161 if (is_uninstalling_) 162 return; 163 is_uninstalling_ = true; 164 165 context_->storage()->NotifyUninstallingRegistration(this); 166 context_->storage()->DeleteRegistration( 167 id(), 168 pattern().GetOrigin(), 169 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); 170 171 if (!active_version() || !active_version()->HasControllee()) 172 Clear(); 173 } 174 175 void ServiceWorkerRegistration::AbortPendingClear( 176 const StatusCallback& callback) { 177 DCHECK(context_); 178 if (!is_uninstalling()) { 179 callback.Run(SERVICE_WORKER_OK); 180 return; 181 } 182 is_uninstalling_ = false; 183 context_->storage()->NotifyDoneUninstallingRegistration(this); 184 185 scoped_refptr<ServiceWorkerVersion> most_recent_version = 186 waiting_version() ? waiting_version() : active_version(); 187 DCHECK(most_recent_version.get()); 188 context_->storage()->NotifyInstallingRegistration(this); 189 context_->storage()->StoreRegistration( 190 this, 191 most_recent_version.get(), 192 base::Bind(&ServiceWorkerRegistration::OnRestoreFinished, 193 this, 194 callback, 195 most_recent_version)); 196 } 197 198 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) { 199 DCHECK_EQ(active_version(), version); 200 if (is_uninstalling_) 201 Clear(); 202 else if (should_activate_when_ready_) 203 ActivateWaitingVersion(); 204 is_uninstalling_ = false; 205 should_activate_when_ready_ = false; 206 } 207 208 void ServiceWorkerRegistration::ActivateWaitingVersion() { 209 DCHECK(context_); 210 DCHECK(waiting_version()); 211 DCHECK(should_activate_when_ready_); 212 should_activate_when_ready_ = false; 213 scoped_refptr<ServiceWorkerVersion> activating_version = waiting_version(); 214 scoped_refptr<ServiceWorkerVersion> exiting_version = active_version(); 215 216 if (activating_version->is_doomed() || 217 activating_version->status() == ServiceWorkerVersion::REDUNDANT) { 218 return; // Activation is no longer relevant. 219 } 220 221 // "4. If exitingWorker is not null, 222 if (exiting_version.get()) { 223 DCHECK(!exiting_version->HasControllee()); 224 // TODO(michaeln): should wait for events to be complete 225 // "1. Wait for exitingWorker to finish handling any in-progress requests." 226 // "2. Terminate exitingWorker." 227 exiting_version->StopWorker( 228 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); 229 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and 230 // "redundant" as the arguments." 231 exiting_version->SetStatus(ServiceWorkerVersion::REDUNDANT); 232 } 233 234 // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker." 235 // "6. Set serviceWorkerRegistration.waitingWorker to null." 236 SetActiveVersion(activating_version.get()); 237 238 // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and 239 // "activating" as arguments." 240 activating_version->SetStatus(ServiceWorkerVersion::ACTIVATING); 241 242 // TODO(nhiroki): "8. Fire a simple event named controllerchange..." 243 244 // "9. Queue a task to fire an event named activate..." 245 activating_version->DispatchActivateEvent( 246 base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished, 247 this, activating_version)); 248 } 249 250 void ServiceWorkerRegistration::OnActivateEventFinished( 251 ServiceWorkerVersion* activating_version, 252 ServiceWorkerStatusCode status) { 253 if (!context_ || activating_version != active_version()) 254 return; 255 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is 256 // unexpectedly terminated) we may want to retry sending the event again. 257 if (status != SERVICE_WORKER_OK) { 258 // "11. If activateFailed is true, then:..." 259 UnsetVersion(activating_version); 260 activating_version->Doom(); 261 if (!waiting_version()) { 262 // Delete the records from the db. 263 context_->storage()->DeleteRegistration( 264 id(), pattern().GetOrigin(), 265 base::Bind(&ServiceWorkerRegistration::OnDeleteFinished, this)); 266 // But not from memory if there is a version in the pipeline. 267 if (installing_version()) 268 is_deleted_ = false; 269 } 270 return; 271 } 272 273 // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker 274 // and "activated" as the arguments." 275 activating_version->SetStatus(ServiceWorkerVersion::ACTIVATED); 276 if (context_) { 277 context_->storage()->UpdateToActiveState( 278 this, 279 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); 280 } 281 } 282 283 void ServiceWorkerRegistration::OnDeleteFinished( 284 ServiceWorkerStatusCode status) { 285 // Intentionally empty completion callback, used to prevent 286 // |this| from being deleted until the storage method completes. 287 } 288 289 void ServiceWorkerRegistration::Clear() { 290 is_uninstalling_ = false; 291 is_uninstalled_ = true; 292 if (context_) 293 context_->storage()->NotifyDoneUninstallingRegistration(this); 294 295 ChangedVersionAttributesMask mask; 296 if (installing_version_.get()) { 297 installing_version_->Doom(); 298 installing_version_ = NULL; 299 mask.add(ChangedVersionAttributesMask::INSTALLING_VERSION); 300 } 301 if (waiting_version_.get()) { 302 waiting_version_->Doom(); 303 waiting_version_ = NULL; 304 mask.add(ChangedVersionAttributesMask::WAITING_VERSION); 305 } 306 if (active_version_.get()) { 307 active_version_->Doom(); 308 active_version_->RemoveListener(this); 309 active_version_ = NULL; 310 mask.add(ChangedVersionAttributesMask::ACTIVE_VERSION); 311 } 312 if (mask.changed()) { 313 ServiceWorkerRegistrationInfo info = GetInfo(); 314 FOR_EACH_OBSERVER(Listener, listeners_, 315 OnVersionAttributesChanged(this, mask, info)); 316 } 317 318 FOR_EACH_OBSERVER( 319 Listener, listeners_, OnRegistrationFinishedUninstalling(this)); 320 } 321 322 void ServiceWorkerRegistration::OnRestoreFinished( 323 const StatusCallback& callback, 324 scoped_refptr<ServiceWorkerVersion> version, 325 ServiceWorkerStatusCode status) { 326 if (!context_) { 327 callback.Run(SERVICE_WORKER_ERROR_ABORT); 328 return; 329 } 330 context_->storage()->NotifyDoneInstallingRegistration( 331 this, version.get(), status); 332 callback.Run(status); 333 } 334 335 } // namespace content 336