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_register_job.h" 6 7 #include <vector> 8 9 #include "base/message_loop/message_loop.h" 10 #include "content/browser/service_worker/service_worker_context_core.h" 11 #include "content/browser/service_worker/service_worker_job_coordinator.h" 12 #include "content/browser/service_worker/service_worker_registration.h" 13 #include "content/browser/service_worker/service_worker_storage.h" 14 #include "content/browser/service_worker/service_worker_utils.h" 15 #include "net/base/net_errors.h" 16 17 namespace content { 18 19 namespace { 20 21 void RunSoon(const base::Closure& closure) { 22 base::MessageLoop::current()->PostTask(FROM_HERE, closure); 23 } 24 25 } // namespace 26 27 typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType; 28 29 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( 30 base::WeakPtr<ServiceWorkerContextCore> context, 31 const GURL& pattern, 32 const GURL& script_url) 33 : context_(context), 34 job_type_(REGISTRATION_JOB), 35 pattern_(pattern), 36 script_url_(script_url), 37 phase_(INITIAL), 38 is_promise_resolved_(false), 39 promise_resolved_status_(SERVICE_WORKER_OK), 40 weak_factory_(this) {} 41 42 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( 43 base::WeakPtr<ServiceWorkerContextCore> context, 44 ServiceWorkerRegistration* registration) 45 : context_(context), 46 job_type_(UPDATE_JOB), 47 pattern_(registration->pattern()), 48 script_url_(registration->GetNewestVersion()->script_url()), 49 phase_(INITIAL), 50 is_promise_resolved_(false), 51 promise_resolved_status_(SERVICE_WORKER_OK), 52 weak_factory_(this) { 53 internal_.registration = registration; 54 } 55 56 ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() { 57 DCHECK(!context_ || 58 phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT) 59 << "Jobs should only be interrupted during shutdown."; 60 } 61 62 void ServiceWorkerRegisterJob::AddCallback( 63 const RegistrationCallback& callback, 64 ServiceWorkerProviderHost* provider_host) { 65 if (!is_promise_resolved_) { 66 callbacks_.push_back(callback); 67 if (provider_host) 68 provider_host->AddScopedProcessReferenceToPattern(pattern_); 69 return; 70 } 71 RunSoon(base::Bind( 72 callback, promise_resolved_status_, 73 promise_resolved_registration_, promise_resolved_version_)); 74 } 75 76 void ServiceWorkerRegisterJob::Start() { 77 SetPhase(START); 78 ServiceWorkerStorage::FindRegistrationCallback next_step; 79 if (job_type_ == REGISTRATION_JOB) { 80 next_step = base::Bind( 81 &ServiceWorkerRegisterJob::ContinueWithRegistration, 82 weak_factory_.GetWeakPtr()); 83 } else { 84 next_step = base::Bind( 85 &ServiceWorkerRegisterJob::ContinueWithUpdate, 86 weak_factory_.GetWeakPtr()); 87 } 88 89 scoped_refptr<ServiceWorkerRegistration> registration = 90 context_->storage()->GetUninstallingRegistration(pattern_); 91 if (registration.get()) 92 RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration)); 93 else 94 context_->storage()->FindRegistrationForPattern(pattern_, next_step); 95 } 96 97 void ServiceWorkerRegisterJob::Abort() { 98 SetPhase(ABORT); 99 CompleteInternal(SERVICE_WORKER_ERROR_ABORT); 100 // Don't have to call FinishJob() because the caller takes care of removing 101 // the jobs from the queue. 102 } 103 104 bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) { 105 if (job->GetType() != GetType()) 106 return false; 107 ServiceWorkerRegisterJob* register_job = 108 static_cast<ServiceWorkerRegisterJob*>(job); 109 return register_job->pattern_ == pattern_ && 110 register_job->script_url_ == script_url_; 111 } 112 113 RegistrationJobType ServiceWorkerRegisterJob::GetType() { 114 return job_type_; 115 } 116 117 ServiceWorkerRegisterJob::Internal::Internal() {} 118 119 ServiceWorkerRegisterJob::Internal::~Internal() {} 120 121 void ServiceWorkerRegisterJob::set_registration( 122 const scoped_refptr<ServiceWorkerRegistration>& registration) { 123 DCHECK(phase_ == START || phase_ == REGISTER) << phase_; 124 DCHECK(!internal_.registration.get()); 125 internal_.registration = registration; 126 } 127 128 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() { 129 DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_; 130 return internal_.registration.get(); 131 } 132 133 void ServiceWorkerRegisterJob::set_new_version( 134 ServiceWorkerVersion* version) { 135 DCHECK(phase_ == UPDATE) << phase_; 136 DCHECK(!internal_.new_version.get()); 137 internal_.new_version = version; 138 } 139 140 ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() { 141 DCHECK(phase_ >= UPDATE) << phase_; 142 return internal_.new_version.get(); 143 } 144 145 void ServiceWorkerRegisterJob::set_uninstalling_registration( 146 const scoped_refptr<ServiceWorkerRegistration>& registration) { 147 DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL); 148 internal_.uninstalling_registration = registration; 149 } 150 151 ServiceWorkerRegistration* 152 ServiceWorkerRegisterJob::uninstalling_registration() { 153 DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL); 154 return internal_.uninstalling_registration.get(); 155 } 156 157 void ServiceWorkerRegisterJob::SetPhase(Phase phase) { 158 switch (phase) { 159 case INITIAL: 160 NOTREACHED(); 161 break; 162 case START: 163 DCHECK(phase_ == INITIAL) << phase_; 164 break; 165 case WAIT_FOR_UNINSTALL: 166 DCHECK(phase_ == START) << phase_; 167 break; 168 case REGISTER: 169 DCHECK(phase_ == START || phase_ == WAIT_FOR_UNINSTALL) << phase_; 170 break; 171 case UPDATE: 172 DCHECK(phase_ == START || phase_ == REGISTER) << phase_; 173 break; 174 case INSTALL: 175 DCHECK(phase_ == UPDATE) << phase_; 176 break; 177 case STORE: 178 DCHECK(phase_ == INSTALL) << phase_; 179 break; 180 case COMPLETE: 181 DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_; 182 break; 183 case ABORT: 184 break; 185 } 186 phase_ = phase; 187 } 188 189 // This function corresponds to the steps in [[Register]] following 190 // "Let registration be the result of running the [[GetRegistration]] algorithm. 191 // Throughout this file, comments in quotes are excerpts from the spec. 192 void ServiceWorkerRegisterJob::ContinueWithRegistration( 193 ServiceWorkerStatusCode status, 194 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) { 195 DCHECK_EQ(REGISTRATION_JOB, job_type_); 196 if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) { 197 Complete(status); 198 return; 199 } 200 201 if (!existing_registration.get() || existing_registration->is_uninstalled()) { 202 RegisterAndContinue(SERVICE_WORKER_OK); 203 return; 204 } 205 206 DCHECK(existing_registration->GetNewestVersion()); 207 // "If scriptURL is equal to registration.[[ScriptURL]], then:" 208 if (existing_registration->GetNewestVersion()->script_url() == script_url_) { 209 // "Set registration.[[Uninstalling]] to false." 210 existing_registration->AbortPendingClear(base::Bind( 211 &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl, 212 weak_factory_.GetWeakPtr(), 213 existing_registration)); 214 return; 215 } 216 217 if (existing_registration->is_uninstalling()) { 218 // "Wait until the Record {[[key]], [[value]]} entry of its 219 // [[ScopeToRegistrationMap]] where registation.scope matches entry.[[key]] 220 // is deleted." 221 WaitForUninstall(existing_registration); 222 return; 223 } 224 225 // "Set registration.[[Uninstalling]] to false." 226 DCHECK(!existing_registration->is_uninstalling()); 227 228 // "Return the result of running the [[Update]] algorithm, or its equivalent, 229 // passing registration as the argument." 230 set_registration(existing_registration); 231 UpdateAndContinue(); 232 } 233 234 void ServiceWorkerRegisterJob::ContinueWithUpdate( 235 ServiceWorkerStatusCode status, 236 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) { 237 DCHECK_EQ(UPDATE_JOB, job_type_); 238 if (status != SERVICE_WORKER_OK) { 239 Complete(status); 240 return; 241 } 242 243 if (existing_registration.get() != registration()) { 244 Complete(SERVICE_WORKER_ERROR_NOT_FOUND); 245 return; 246 } 247 248 // A previous job may have unregistered or installed a new version to this 249 // registration. 250 if (registration()->is_uninstalling() || 251 registration()->GetNewestVersion()->script_url() != script_url_) { 252 Complete(SERVICE_WORKER_ERROR_NOT_FOUND); 253 return; 254 } 255 256 // TODO(michaeln): If the last update check was less than 24 hours 257 // ago, depending on the freshness of the cached worker script we 258 // may be able to complete the update job right here. 259 260 UpdateAndContinue(); 261 } 262 263 // Creates a new ServiceWorkerRegistration. 264 void ServiceWorkerRegisterJob::RegisterAndContinue( 265 ServiceWorkerStatusCode status) { 266 SetPhase(REGISTER); 267 if (status != SERVICE_WORKER_OK) { 268 // Abort this registration job. 269 Complete(status); 270 return; 271 } 272 273 set_registration(new ServiceWorkerRegistration( 274 pattern_, context_->storage()->NewRegistrationId(), context_)); 275 AssociateProviderHostsToRegistration(registration()); 276 UpdateAndContinue(); 277 } 278 279 void ServiceWorkerRegisterJob::WaitForUninstall( 280 const scoped_refptr<ServiceWorkerRegistration>& existing_registration) { 281 SetPhase(WAIT_FOR_UNINSTALL); 282 set_uninstalling_registration(existing_registration); 283 uninstalling_registration()->AddListener(this); 284 } 285 286 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl( 287 const scoped_refptr<ServiceWorkerRegistration>& existing_registration, 288 ServiceWorkerStatusCode status) { 289 if (status != SERVICE_WORKER_OK) { 290 Complete(status); 291 return; 292 } 293 set_registration(existing_registration); 294 295 // TODO(falken): Follow the spec: resolve the promise 296 // with the newest version. 297 298 if (!existing_registration->active_version()) { 299 UpdateAndContinue(); 300 return; 301 } 302 303 ResolvePromise(status, 304 existing_registration.get(), 305 existing_registration->active_version()); 306 Complete(SERVICE_WORKER_OK); 307 } 308 309 // This function corresponds to the spec's [[Update]] algorithm. 310 void ServiceWorkerRegisterJob::UpdateAndContinue() { 311 SetPhase(UPDATE); 312 context_->storage()->NotifyInstallingRegistration(registration()); 313 314 // TODO(falken): "If serviceWorkerRegistration.installingWorker is not null.." 315 // then terminate the installing worker. It doesn't make sense to implement 316 // yet since we always activate the worker if install completed, so there can 317 // be no installing worker at this point. 318 319 // "Let serviceWorker be a newly-created ServiceWorker object..." and start 320 // the worker. 321 set_new_version(new ServiceWorkerVersion(registration(), 322 script_url_, 323 context_->storage()->NewVersionId(), 324 context_)); 325 326 bool pause_after_download = job_type_ == UPDATE_JOB; 327 if (pause_after_download) 328 new_version()->embedded_worker()->AddListener(this); 329 new_version()->StartWorker( 330 pause_after_download, 331 base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished, 332 weak_factory_.GetWeakPtr())); 333 } 334 335 void ServiceWorkerRegisterJob::OnStartWorkerFinished( 336 ServiceWorkerStatusCode status) { 337 if (status == SERVICE_WORKER_OK) { 338 InstallAndContinue(); 339 return; 340 } 341 342 // "If serviceWorker fails to start up..." then reject the promise with an 343 // error and abort. When there is a main script network error, the status will 344 // be updated to a more specific one. 345 const net::URLRequestStatus& main_script_status = 346 new_version()->script_cache_map()->main_script_status(); 347 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) { 348 switch (main_script_status.error()) { 349 case net::ERR_INSECURE_RESPONSE: 350 case net::ERR_UNSAFE_REDIRECT: 351 status = SERVICE_WORKER_ERROR_SECURITY; 352 break; 353 case net::ERR_ABORTED: 354 status = SERVICE_WORKER_ERROR_ABORT; 355 break; 356 case net::ERR_FAILED: 357 status = SERVICE_WORKER_ERROR_NETWORK; 358 break; 359 default: 360 NOTREACHED(); 361 } 362 } 363 Complete(status); 364 } 365 366 // This function corresponds to the spec's [[Install]] algorithm. 367 void ServiceWorkerRegisterJob::InstallAndContinue() { 368 SetPhase(INSTALL); 369 370 // "2. Set registration.installingWorker to worker." 371 registration()->SetInstallingVersion(new_version()); 372 373 // "3. Resolve promise with registration." 374 ResolvePromise(SERVICE_WORKER_OK, registration(), new_version()); 375 376 // "4. Run the [[UpdateState]] algorithm passing registration.installingWorker 377 // and "installing" as the arguments." 378 new_version()->SetStatus(ServiceWorkerVersion::INSTALLING); 379 380 // "5. Fire a simple event named updatefound..." 381 registration()->NotifyUpdateFound(); 382 383 // "6. Fire an event named install..." 384 new_version()->DispatchInstallEvent( 385 -1, 386 base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished, 387 weak_factory_.GetWeakPtr())); 388 } 389 390 void ServiceWorkerRegisterJob::OnInstallFinished( 391 ServiceWorkerStatusCode status) { 392 // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is 393 // unexpectedly terminated) we may want to retry sending the event again. 394 if (status != SERVICE_WORKER_OK) { 395 // "8. If installFailed is true, then:..." 396 Complete(status); 397 return; 398 } 399 400 SetPhase(STORE); 401 registration()->set_last_update_check(base::Time::Now()); 402 context_->storage()->StoreRegistration( 403 registration(), 404 new_version(), 405 base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete, 406 weak_factory_.GetWeakPtr())); 407 } 408 409 void ServiceWorkerRegisterJob::OnStoreRegistrationComplete( 410 ServiceWorkerStatusCode status) { 411 if (status != SERVICE_WORKER_OK) { 412 Complete(status); 413 return; 414 } 415 416 // "9. If registration.waitingWorker is not null, then:..." 417 if (registration()->waiting_version()) { 418 // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker 419 // and "redundant" as the arguments." 420 registration()->waiting_version()->SetStatus( 421 ServiceWorkerVersion::REDUNDANT); 422 } 423 424 // "10. Set registration.waitingWorker to registration.installingWorker." 425 // "11. Set registration.installingWorker to null." 426 registration()->SetWaitingVersion(new_version()); 427 428 // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker 429 // and "installed" as the arguments." 430 new_version()->SetStatus(ServiceWorkerVersion::INSTALLED); 431 432 // TODO(michaeln): "13. If activateImmediate is true, then..." 433 434 // "14. Wait until no document is using registration as their 435 // Service Worker registration." 436 registration()->ActivateWaitingVersionWhenReady(); 437 438 Complete(SERVICE_WORKER_OK); 439 } 440 441 void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) { 442 CompleteInternal(status); 443 context_->job_coordinator()->FinishJob(pattern_, this); 444 } 445 446 void ServiceWorkerRegisterJob::CompleteInternal( 447 ServiceWorkerStatusCode status) { 448 SetPhase(COMPLETE); 449 if (status != SERVICE_WORKER_OK) { 450 if (registration()) { 451 if (new_version()) { 452 registration()->UnsetVersion(new_version()); 453 new_version()->Doom(); 454 } 455 if (!registration()->waiting_version() && 456 !registration()->active_version()) { 457 registration()->NotifyRegistrationFailed(); 458 context_->storage()->DeleteRegistration( 459 registration()->id(), 460 registration()->pattern().GetOrigin(), 461 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); 462 } 463 } 464 if (!is_promise_resolved_) 465 ResolvePromise(status, NULL, NULL); 466 } 467 DCHECK(callbacks_.empty()); 468 if (registration()) { 469 context_->storage()->NotifyDoneInstallingRegistration( 470 registration(), new_version(), status); 471 } 472 if (new_version()) 473 new_version()->embedded_worker()->RemoveListener(this); 474 } 475 476 void ServiceWorkerRegisterJob::ResolvePromise( 477 ServiceWorkerStatusCode status, 478 ServiceWorkerRegistration* registration, 479 ServiceWorkerVersion* version) { 480 DCHECK(!is_promise_resolved_); 481 is_promise_resolved_ = true; 482 promise_resolved_status_ = status; 483 promise_resolved_registration_ = registration; 484 promise_resolved_version_ = version; 485 for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin(); 486 it != callbacks_.end(); 487 ++it) { 488 it->Run(status, registration, version); 489 } 490 callbacks_.clear(); 491 } 492 493 void ServiceWorkerRegisterJob::OnPausedAfterDownload() { 494 // This happens prior to OnStartWorkerFinished time. 495 scoped_refptr<ServiceWorkerVersion> most_recent_version = 496 registration()->waiting_version() ? 497 registration()->waiting_version() : 498 registration()->active_version(); 499 DCHECK(most_recent_version.get()); 500 int64 most_recent_script_id = 501 most_recent_version->script_cache_map()->Lookup(script_url_); 502 int64 new_script_id = 503 new_version()->script_cache_map()->Lookup(script_url_); 504 505 // TODO(michaeln): It would be better to compare as the new resource 506 // is being downloaded and to avoid writing it to disk until we know 507 // its needed. 508 context_->storage()->CompareScriptResources( 509 most_recent_script_id, new_script_id, 510 base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete, 511 weak_factory_.GetWeakPtr(), 512 most_recent_version)); 513 } 514 515 bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) { 516 return false; 517 } 518 519 void ServiceWorkerRegisterJob::OnRegistrationFinishedUninstalling( 520 ServiceWorkerRegistration* existing_registration) { 521 DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL); 522 DCHECK_EQ(existing_registration, uninstalling_registration()); 523 existing_registration->RemoveListener(this); 524 set_uninstalling_registration(NULL); 525 RegisterAndContinue(SERVICE_WORKER_OK); 526 } 527 528 void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete( 529 ServiceWorkerVersion* most_recent_version, 530 ServiceWorkerStatusCode status, 531 bool are_equal) { 532 if (are_equal) { 533 // Only bump the last check time when we've bypassed the browser cache. 534 base::TimeDelta time_since_last_check = 535 base::Time::Now() - registration()->last_update_check(); 536 if (time_since_last_check > base::TimeDelta::FromHours(24)) { 537 registration()->set_last_update_check(base::Time::Now()); 538 context_->storage()->UpdateLastUpdateCheckTime(registration()); 539 } 540 541 ResolvePromise(SERVICE_WORKER_OK, registration(), most_recent_version); 542 Complete(SERVICE_WORKER_ERROR_EXISTS); 543 return; 544 } 545 546 // Proceed with really starting the worker. 547 new_version()->embedded_worker()->ResumeAfterDownload(); 548 new_version()->embedded_worker()->RemoveListener(this); 549 } 550 551 void ServiceWorkerRegisterJob::AssociateProviderHostsToRegistration( 552 ServiceWorkerRegistration* registration) { 553 DCHECK(registration); 554 for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it = 555 context_->GetProviderHostIterator(); 556 !it->IsAtEnd(); it->Advance()) { 557 ServiceWorkerProviderHost* host = it->GetProviderHost(); 558 if (ServiceWorkerUtils::ScopeMatches(registration->pattern(), 559 host->document_url())) { 560 if (host->CanAssociateRegistration(registration)) 561 host->AssociateRegistration(registration); 562 } 563 } 564 } 565 566 } // namespace content 567