summaryrefslogtreecommitdiff
path: root/chromium/content/browser/service_worker/service_worker_registration.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/service_worker/service_worker_registration.cc')
-rw-r--r--chromium/content/browser/service_worker/service_worker_registration.cc113
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);