diff options
Diffstat (limited to 'chromium/content/browser/service_worker/service_worker_registration.cc')
-rw-r--r-- | chromium/content/browser/service_worker/service_worker_registration.cc | 113 |
1 files changed, 91 insertions, 22 deletions
diff --git a/chromium/content/browser/service_worker/service_worker_registration.cc b/chromium/content/browser/service_worker/service_worker_registration.cc index 047e20f82f5..ef587ee2a6b 100644 --- a/chromium/content/browser/service_worker/service_worker_registration.cc +++ b/chromium/content/browser/service_worker/service_worker_registration.cc @@ -6,7 +6,9 @@ #include <vector> +#include "content/browser/service_worker/embedded_worker_status.h" #include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_info.h" #include "content/browser/service_worker/service_worker_metrics.h" #include "content/browser/service_worker/service_worker_register_job.h" @@ -37,7 +39,8 @@ ServiceWorkerRegistration::ServiceWorkerRegistration( is_uninstalled_(false), should_activate_when_ready_(false), resources_total_size_bytes_(0), - context_(context) { + context_(context), + task_runner_(base::ThreadTaskRunnerHandle::Get()) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id); DCHECK(context_); @@ -45,6 +48,11 @@ ServiceWorkerRegistration::ServiceWorkerRegistration( } ServiceWorkerRegistration::~ServiceWorkerRegistration() { + // Can be false during shutdown, in which case the DCHECK_CURRENTLY_ON below + // would cry. + if (!BrowserThread::IsThreadInitialized(BrowserThread::IO)) + return; + DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!listeners_.might_have_observers()); if (context_) @@ -99,10 +107,11 @@ ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() { void ServiceWorkerRegistration::SetActiveVersion( const scoped_refptr<ServiceWorkerVersion>& version) { - should_activate_when_ready_ = false; if (active_version_ == version) return; + should_activate_when_ready_ = false; + ChangedVersionAttributesMask mask; if (version) UnsetVersionInternal(version.get(), &mask); @@ -118,10 +127,11 @@ void ServiceWorkerRegistration::SetActiveVersion( void ServiceWorkerRegistration::SetWaitingVersion( const scoped_refptr<ServiceWorkerVersion>& version) { - should_activate_when_ready_ = false; if (waiting_version_ == version) return; + should_activate_when_ready_ = false; + ChangedVersionAttributesMask mask; if (version) UnsetVersionInternal(version.get(), &mask); @@ -163,6 +173,7 @@ void ServiceWorkerRegistration::UnsetVersionInternal( mask->add(ChangedVersionAttributesMask::INSTALLING_VERSION); } else if (waiting_version_.get() == version) { waiting_version_ = NULL; + should_activate_when_ready_ = false; mask->add(ChangedVersionAttributesMask::WAITING_VERSION); } else if (active_version_.get() == version) { active_version_->RemoveListener(this); @@ -174,10 +185,8 @@ void ServiceWorkerRegistration::UnsetVersionInternal( void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() { DCHECK(waiting_version()); should_activate_when_ready_ = true; - - if (!active_version() || !active_version()->HasControllee() || - waiting_version()->skip_waiting()) - ActivateWaitingVersion(); + if (IsReadyToActivate()) + ActivateWaitingVersion(false /* delay */); } void ServiceWorkerRegistration::ClaimClients() { @@ -244,16 +253,37 @@ void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) { DCHECK_EQ(active_version(), version); if (is_uninstalling_) Clear(); - else if (should_activate_when_ready_) - ActivateWaitingVersion(); - is_uninstalling_ = false; - should_activate_when_ready_ = false; + else if (IsReadyToActivate()) + ActivateWaitingVersion(true /* delay */); } -void ServiceWorkerRegistration::ActivateWaitingVersion() { - DCHECK(context_); +void ServiceWorkerRegistration::OnNoWork(ServiceWorkerVersion* version) { + if (!context_) + return; + DCHECK_EQ(active_version(), version); + if (IsReadyToActivate()) + ActivateWaitingVersion(false /* delay */); +} + +bool ServiceWorkerRegistration::IsReadyToActivate() const { + if (!should_activate_when_ready_) + return false; + DCHECK(waiting_version()); - DCHECK(should_activate_when_ready_); + const ServiceWorkerVersion* active = active_version(); + if (!active) + return true; + if (!active->HasWork() && + (!active->HasControllee() || waiting_version()->skip_waiting())) { + return true; + } + return false; +} + +void ServiceWorkerRegistration::ActivateWaitingVersion(bool delay) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(context_); + DCHECK(IsReadyToActivate()); should_activate_when_ready_ = false; scoped_refptr<ServiceWorkerVersion> activating_version = waiting_version(); scoped_refptr<ServiceWorkerVersion> exiting_version = active_version(); @@ -263,8 +293,11 @@ void ServiceWorkerRegistration::ActivateWaitingVersion() { // "5. If exitingWorker is not null, if (exiting_version.get()) { - // TODO(michaeln): should wait for events to be complete + // TODO(falken): Update the quoted spec comments once + // https://github.com/slightlyoff/ServiceWorker/issues/916 is codified in + // the spec. // "1. Wait for exitingWorker to finish handling any in-progress requests." + // This is already handled by IsReadyToActivate(). // "2. Terminate exitingWorker." exiting_version->StopWorker( base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); @@ -285,6 +318,25 @@ void ServiceWorkerRegistration::ActivateWaitingVersion() { FOR_EACH_OBSERVER(Listener, listeners_, OnSkippedWaiting(this)); // "10. Queue a task to fire an event named activate..." + // The browser could be shutting down. To avoid spurious start worker + // failures, wait a bit before continuing. + if (delay) { + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&ServiceWorkerRegistration::ContinueActivation, + this, activating_version), + base::TimeDelta::FromSeconds(1)); + } else { + ContinueActivation(std::move(activating_version)); + } +} + +void ServiceWorkerRegistration::ContinueActivation( + scoped_refptr<ServiceWorkerVersion> activating_version) { + if (!context_) + return; + if (active_version() != activating_version.get()) + return; + DCHECK_EQ(ServiceWorkerVersion::ACTIVATING, activating_version->status()); activating_version->RunAfterStartWorker( ServiceWorkerMetrics::EventType::ACTIVATE, base::Bind(&ServiceWorkerRegistration::DispatchActivateEvent, this, @@ -333,6 +385,11 @@ void ServiceWorkerRegistration::NotifyRegistrationFinished() { callback.Run(); } +void ServiceWorkerRegistration::SetTaskRunnerForTest( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + task_runner_ = task_runner; +} + void ServiceWorkerRegistration::RegisterRegistrationFinishedCallback( const base::Closure& callback) { // This should only be called if the registration is in progress. @@ -342,14 +399,14 @@ void ServiceWorkerRegistration::RegisterRegistrationFinishedCallback( } void ServiceWorkerRegistration::DispatchActivateEvent( - const scoped_refptr<ServiceWorkerVersion>& activating_version) { + scoped_refptr<ServiceWorkerVersion> activating_version) { if (activating_version != active_version()) { OnActivateEventFinished(activating_version, SERVICE_WORKER_ERROR_FAILED); return; } DCHECK_EQ(ServiceWorkerVersion::ACTIVATING, activating_version->status()); - DCHECK_EQ(ServiceWorkerVersion::RUNNING, activating_version->running_status()) + DCHECK_EQ(EmbeddedWorkerStatus::RUNNING, activating_version->running_status()) << "Worker stopped too soon after it was started."; int request_id = activating_version->StartRequest( ServiceWorkerMetrics::EventType::ACTIVATE, @@ -361,15 +418,26 @@ void ServiceWorkerRegistration::DispatchActivateEvent( } void ServiceWorkerRegistration::OnActivateEventFinished( - const scoped_refptr<ServiceWorkerVersion>& activating_version, + scoped_refptr<ServiceWorkerVersion> activating_version, ServiceWorkerStatusCode status) { + // Activate is prone to failing due to shutdown, because it's triggered when + // tabs close. + bool is_shutdown = + !context_ || context_->wrapper()->process_manager()->IsShutdown(); + ServiceWorkerMetrics::RecordActivateEventStatus(status, is_shutdown); + if (!context_ || activating_version != active_version() || - activating_version->status() != ServiceWorkerVersion::ACTIVATING) + activating_version->status() != ServiceWorkerVersion::ACTIVATING) { return; + } - // |status| is just for UMA. Once we've attempted to dispatch the activate - // event to an installed worker, it's committed to becoming active. - ServiceWorkerMetrics::RecordActivateEventStatus(status); + // Normally, the worker is committed to become activated once we get here, per + // spec. E.g., if the script rejected waitUntil or had an unhandled exception, + // it should still be activated. However, if the failure occurred during + // shutdown, ignore it to give the worker another chance the next time the + // browser starts up. + if (is_shutdown && status != SERVICE_WORKER_OK) + return; // "Run the Update State algorithm passing registration's active worker and // 'activated' as the arguments." @@ -387,6 +455,7 @@ void ServiceWorkerRegistration::OnDeleteFinished( void ServiceWorkerRegistration::Clear() { is_uninstalling_ = false; is_uninstalled_ = true; + should_activate_when_ready_ = false; if (context_) context_->storage()->NotifyDoneUninstallingRegistration(this); |