Commit b86a5cf3 authored by Yannic Bonenberger's avatar Yannic Bonenberger Committed by Commit Bot

[ServiceWorker] Delay update() from workers without controllers

This CL delays the execution of update() for an increasing amount
of time if the calling worker doesn't control a client. The delay
is reset every time a controller is added to the worker, or an
event is dispatched. postMessage from service workers,
|install| and |activate| don't reset the delay.

Bug: 805496
Change-Id: I9c25ba4315ce6a915634ecdf6405db8774c40929
Reviewed-on: https://chromium-review.googlesource.com/900763
Commit-Queue: Yannic Bonenberger <contact@yannic-bonenberger.com>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577875}
parent 7beedc82
......@@ -46,6 +46,9 @@ const char ServiceWorkerConsts::kSetNavigationPreloadHeaderErrorPrefix[] =
const char ServiceWorkerConsts::kShutdownErrorMessage[] =
"The Service Worker system has shutdown.";
const char ServiceWorkerConsts::kUpdateTimeoutErrorMesage[] =
"Service worker self-update limit exceeded.";
const char ServiceWorkerConsts::kUserDeniedPermissionMessage[] =
"The user denied permission to use Service Worker.";
......
......@@ -21,6 +21,7 @@ struct ServiceWorkerConsts {
static const char kNoDocumentURLErrorMessage[];
static const char kSetNavigationPreloadHeaderErrorPrefix[];
static const char kShutdownErrorMessage[];
static const char kUpdateTimeoutErrorMesage[];
static const char kUserDeniedPermissionMessage[];
};
......
......@@ -19,7 +19,7 @@ namespace content {
namespace {
using StatusCallback = base::OnceCallback<void(blink::ServiceWorkerStatusCode)>;
using SetExtendableMessageEventSourceCallback =
using PrepareExtendableMessageEventCallback =
base::OnceCallback<bool(mojom::ExtendableMessageEventPtr*)>;
void DispatchExtendableMessageEventAfterStartWorker(
......@@ -28,7 +28,7 @@ void DispatchExtendableMessageEventAfterStartWorker(
const url::Origin& source_origin,
const base::Optional<base::TimeDelta>& timeout,
StatusCallback callback,
SetExtendableMessageEventSourceCallback set_source_callback,
PrepareExtendableMessageEventCallback prepare_callback,
blink::ServiceWorkerStatusCode start_worker_status) {
if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
std::move(callback).Run(start_worker_status);
......@@ -38,7 +38,7 @@ void DispatchExtendableMessageEventAfterStartWorker(
mojom::ExtendableMessageEventPtr event = mojom::ExtendableMessageEvent::New();
event->message = std::move(message);
event->source_origin = source_origin;
if (!std::move(set_source_callback).Run(&event)) {
if (!std::move(prepare_callback).Run(&event)) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorFailed);
return;
}
......@@ -62,7 +62,7 @@ void StartWorkerToDispatchExtendableMessageEvent(
const url::Origin& source_origin,
const base::Optional<base::TimeDelta>& timeout,
StatusCallback callback,
SetExtendableMessageEventSourceCallback set_source_callback) {
PrepareExtendableMessageEventCallback prepare_callback) {
// If not enough time is left to actually process the event don't even
// bother starting the worker and sending the event.
if (timeout && *timeout < base::TimeDelta::FromMilliseconds(100)) {
......@@ -74,27 +74,42 @@ void StartWorkerToDispatchExtendableMessageEvent(
ServiceWorkerMetrics::EventType::MESSAGE,
base::BindOnce(&DispatchExtendableMessageEventAfterStartWorker, worker,
std::move(message), source_origin, timeout,
std::move(callback), std::move(set_source_callback)));
std::move(callback), std::move(prepare_callback)));
}
bool SetSourceClientInfo(
bool PrepareExtendableMessageEventFromClient(
base::WeakPtr<ServiceWorkerContextCore> context,
int64_t registration_id,
blink::mojom::ServiceWorkerClientInfoPtr source_client_info,
mojom::ExtendableMessageEventPtr* event) {
if (!context) {
return false;
}
DCHECK(source_client_info && !source_client_info->client_uuid.empty());
(*event)->source_info_for_client = std::move(source_client_info);
// Hide the client url if the client has a unique origin.
if ((*event)->source_origin.unique())
(*event)->source_info_for_client->url = GURL();
// Reset |registration->self_update_delay| iff postMessage is coming from a
// client, to prevent workers from postMessage to another version to reset
// the delay (https://crbug.com/805496).
ServiceWorkerRegistration* registration =
context->GetLiveRegistration(registration_id);
DCHECK(registration) << "running workers should have a live registration";
registration->set_self_update_delay(base::TimeDelta());
return true;
}
// The output |event| must be sent over Mojo immediately after this function
// returns. See ServiceWorkerObjectHost::CreateCompleteObjectInfoToSend() for
// details.
bool SetSourceServiceWorkerInfo(scoped_refptr<ServiceWorkerVersion> worker,
base::WeakPtr<ServiceWorkerProviderHost>
source_service_worker_provider_host,
mojom::ExtendableMessageEventPtr* event) {
bool PrepareExtendableMessageEventFromServiceWorker(
scoped_refptr<ServiceWorkerVersion> worker,
base::WeakPtr<ServiceWorkerProviderHost>
source_service_worker_provider_host,
mojom::ExtendableMessageEventPtr* event) {
// The service worker execution context may have been destroyed by the time we
// get here.
if (!source_service_worker_provider_host)
......@@ -121,11 +136,16 @@ bool SetSourceServiceWorkerInfo(scoped_refptr<ServiceWorkerVersion> worker,
}
void DispatchExtendableMessageEventFromClient(
base::WeakPtr<ServiceWorkerContextCore> context,
scoped_refptr<ServiceWorkerVersion> worker,
blink::TransferableMessage message,
const url::Origin& source_origin,
StatusCallback callback,
blink::mojom::ServiceWorkerClientInfoPtr source_client_info) {
if (!context) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort);
return;
}
// |source_client_info| may be null if a client sent the message but its
// info could not be retrieved.
if (!source_client_info) {
......@@ -136,7 +156,8 @@ void DispatchExtendableMessageEventFromClient(
StartWorkerToDispatchExtendableMessageEvent(
worker, std::move(message), source_origin, base::nullopt /* timeout */,
std::move(callback),
base::BindOnce(&SetSourceClientInfo, std::move(source_client_info)));
base::BindOnce(&PrepareExtendableMessageEventFromClient, context,
worker->registration_id(), std::move(source_client_info)));
}
void DispatchExtendableMessageEventFromServiceWorker(
......@@ -156,7 +177,7 @@ void DispatchExtendableMessageEventFromServiceWorker(
source_service_worker_provider_host->provider_type());
StartWorkerToDispatchExtendableMessageEvent(
worker, std::move(message), source_origin, timeout, std::move(callback),
base::BindOnce(&SetSourceServiceWorkerInfo, worker,
base::BindOnce(&PrepareExtendableMessageEventFromServiceWorker, worker,
source_service_worker_provider_host));
}
......@@ -261,8 +282,8 @@ void ServiceWorkerObjectHost::DispatchExtendableMessageEvent(
case blink::mojom::ServiceWorkerProviderType::kForWindow:
service_worker_client_utils::GetClient(
provider_host_,
base::BindOnce(&DispatchExtendableMessageEventFromClient, version_,
std::move(message), provider_origin_,
base::BindOnce(&DispatchExtendableMessageEventFromClient, context_,
version_, std::move(message), provider_origin_,
std::move(callback)));
return;
case blink::mojom::ServiceWorkerProviderType::kForServiceWorker: {
......
......@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/common/content_export.h"
#include "content/common/service_worker/service_worker_types.h"
......@@ -162,6 +163,13 @@ class CONTENT_EXPORT ServiceWorkerRegistration
base::Time last_update_check() const { return last_update_check_; }
void set_last_update_check(base::Time last) { last_update_check_ = last; }
// The delay for self-updating service workers, to prevent them from running
// forever (see https://crbug.com/805496).
base::TimeDelta self_update_delay() const { return self_update_delay_; }
void set_self_update_delay(const base::TimeDelta& delay) {
self_update_delay_ = delay;
}
// Unsets the version and deletes its resources. Also deletes this
// registration from storage if there is no longer a stored version.
void DeleteVersion(const scoped_refptr<ServiceWorkerVersion>& version);
......@@ -227,6 +235,7 @@ class CONTENT_EXPORT ServiceWorkerRegistration
bool should_activate_when_ready_;
blink::mojom::NavigationPreloadState navigation_preload_state_;
base::Time last_update_check_;
base::TimeDelta self_update_delay_;
int64_t resources_total_size_bytes_;
// This registration is the primary owner of these versions.
......
......@@ -4,11 +4,13 @@
#include "content/browser/service_worker/service_worker_registration_object_host.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_consts.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_object_host.h"
#include "content/browser/service_worker/service_worker_provider_host.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "content/public/browser/browser_thread.h"
#include "net/http/http_util.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
......@@ -16,6 +18,9 @@ namespace content {
namespace {
constexpr base::TimeDelta kSelfUpdateDelay = base::TimeDelta::FromSeconds(30);
constexpr base::TimeDelta kMaxSelfUpdateDelay = base::TimeDelta::FromMinutes(3);
// Returns an object info to send over Mojo. The info must be sent immediately.
// See ServiceWorkerObjectHost::CreateCompleteObjectInfoToSend() for details.
blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend(
......@@ -28,6 +33,45 @@ blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend(
return service_worker_object_host->CreateCompleteObjectInfoToSend();
}
void ExecuteUpdate(base::WeakPtr<ServiceWorkerContextCore> context,
int64_t registration_id,
bool force_bypass_cache,
bool skip_script_comparison,
ServiceWorkerContextCore::UpdateCallback callback,
blink::ServiceWorkerStatusCode status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (status != blink::ServiceWorkerStatusCode::kOk) {
// The delay was already very long and update() is rejected immediately.
DCHECK_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, status);
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorTimeout,
ServiceWorkerConsts::kUpdateTimeoutErrorMesage,
registration_id);
return;
}
if (!context) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
ServiceWorkerConsts::kShutdownErrorMessage,
registration_id);
return;
}
ServiceWorkerRegistration* registration =
context->GetLiveRegistration(registration_id);
if (!registration) {
// The service worker is no longer running, so update() won't be rejected.
// We still run the callback so the caller knows.
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorTimeout,
ServiceWorkerConsts::kUpdateTimeoutErrorMesage,
registration_id);
return;
}
context->UpdateServiceWorker(registration, force_bypass_cache,
skip_script_comparison, std::move(callback));
}
} // anonymous namespace
ServiceWorkerRegistrationObjectHost::ServiceWorkerRegistrationObjectHost(
......@@ -116,14 +160,58 @@ void ServiceWorkerRegistrationObjectHost::Update(UpdateCallback callback) {
return;
}
context_->UpdateServiceWorker(
registration_.get(), false /* force_bypass_cache */,
false /* skip_script_comparison */,
base::AdaptCallbackForRepeating(
DelayUpdate(
provider_host_->provider_type(), registration_.get(),
provider_host_->running_hosted_version(),
base::BindOnce(
&ExecuteUpdate, context_, registration_->id(),
false /* force_bypass_cache */, false /* skip_script_comparison */,
base::BindOnce(&ServiceWorkerRegistrationObjectHost::UpdateComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
}
void ServiceWorkerRegistrationObjectHost::DelayUpdate(
blink::mojom::ServiceWorkerProviderType provider_type,
ServiceWorkerRegistration* registration,
ServiceWorkerVersion* version,
StatusCallback update_function) {
DCHECK(registration);
if (provider_type !=
blink::mojom::ServiceWorkerProviderType::kForServiceWorker ||
(version && version->HasControllee())) {
// Don't delay update() if called by non-workers or by workers with
// controllees.
std::move(update_function).Run(blink::ServiceWorkerStatusCode::kOk);
return;
}
base::TimeDelta delay = registration->self_update_delay();
if (delay > kMaxSelfUpdateDelay) {
std::move(update_function)
.Run(blink::ServiceWorkerStatusCode::kErrorTimeout);
return;
}
if (delay < kSelfUpdateDelay) {
registration->set_self_update_delay(kSelfUpdateDelay);
} else {
registration->set_self_update_delay(delay * 2);
}
if (delay < base::TimeDelta::Min()) {
// Only enforce the delay of update() iff |delay| exists.
std::move(update_function).Run(blink::ServiceWorkerStatusCode::kOk);
return;
}
BrowserThread::PostDelayedTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(std::move(update_function),
blink::ServiceWorkerStatusCode::kOk),
delay);
}
void ServiceWorkerRegistrationObjectHost::Unregister(
UnregisterCallback callback) {
if (!CanServeRegistrationObjectHostMethods(
......@@ -302,6 +390,7 @@ void ServiceWorkerRegistrationObjectHost::SetVersionAttributes(
waiting = CreateCompleteObjectInfoToSend(provider_host_, waiting_version);
if (changed_mask.active_changed())
active = CreateCompleteObjectInfoToSend(provider_host_, active_version);
DCHECK(remote_registration_);
remote_registration_->SetVersionAttributes(
changed_mask.changed(), std::move(installing), std::move(waiting),
......
......@@ -17,6 +17,9 @@
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace content {
namespace service_worker_registration_unittest {
class ServiceWorkerRegistrationObjectHostTest;
} // namespace service_worker_registration_unittest
class ServiceWorkerContextCore;
class ServiceWorkerVersion;
......@@ -46,6 +49,12 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost
ServiceWorkerRegistration* registration() { return registration_.get(); }
private:
friend class service_worker_registration_unittest::
ServiceWorkerRegistrationObjectHostTest;
using StatusCallback =
base::OnceCallback<void(blink::ServiceWorkerStatusCode status)>;
// ServiceWorkerRegistration::Listener overrides.
void OnVersionAttributesChanged(
ServiceWorkerRegistration* registration,
......@@ -68,6 +77,20 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost
const std::string& value,
SetNavigationPreloadHeaderCallback callback) override;
// Delays an update if it is called by a worker without controllee, to prevent
// workers from running forever (see https://crbug.com/805496).
// Calls |update_function| with blink::ServiceWorkerStatusCode::kOk if the
// update should procceed, and blink::ServiceWorkerStatusCode::kTimeout
// otherwise.
// If there is no delay, or if the delay is very long, |update_function| is
// executed synchronously (before this method returns).
//
// TODO(falken): See if tests can call |Update| directly, then this separate
// function isn't needed.
static void DelayUpdate(blink::mojom::ServiceWorkerProviderType provider_type,
ServiceWorkerRegistration* registration,
ServiceWorkerVersion* version,
StatusCallback update_function);
// Called back from ServiceWorkerContextCore when an update is complete.
void UpdateComplete(UpdateCallback callback,
blink::ServiceWorkerStatusCode status,
......
......@@ -698,6 +698,27 @@ class ServiceWorkerRegistrationObjectHostTest
return error;
}
blink::ServiceWorkerStatusCode CallDelayUpdate(
blink::mojom::ServiceWorkerProviderType provider_type,
ServiceWorkerRegistration* registration,
ServiceWorkerVersion* version) {
base::Optional<blink::ServiceWorkerStatusCode> status;
base::RunLoop run_loop;
ServiceWorkerRegistrationObjectHost::DelayUpdate(
blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
registration, version,
base::BindOnce(
[](base::Optional<blink::ServiceWorkerStatusCode>* out_status,
base::OnceClosure callback,
blink::ServiceWorkerStatusCode status) {
*out_status = status;
std::move(callback).Run();
},
&status, run_loop.QuitClosure()));
run_loop.Run();
return status.value();
}
blink::mojom::ServiceWorkerErrorType CallUnregister(
blink::mojom::ServiceWorkerRegistrationObjectHost* registration_host) {
blink::mojom::ServiceWorkerErrorType error =
......@@ -730,21 +751,21 @@ class ServiceWorkerRegistrationObjectHostTest
return status.value();
}
int64_t SetUpRegistration(const GURL& scope, const GURL& script_url) {
storage()->LazyInitializeForTest(base::DoNothing());
base::RunLoop().RunUntilIdle();
// Prepare ServiceWorkerRegistration.
scoped_refptr<ServiceWorkerRegistration> CreateRegistration(
const GURL& scope) {
blink::mojom::ServiceWorkerRegistrationOptions options;
options.scope = scope;
scoped_refptr<ServiceWorkerRegistration> registration =
base::MakeRefCounted<ServiceWorkerRegistration>(
options, storage()->NewRegistrationId(), context()->AsWeakPtr());
// Prepare ServiceWorkerVersion.
return base::MakeRefCounted<ServiceWorkerRegistration>(
options, storage()->NewRegistrationId(), context()->AsWeakPtr());
}
scoped_refptr<ServiceWorkerVersion> CreateVersion(
ServiceWorkerRegistration* registration,
const GURL& script_url) {
scoped_refptr<ServiceWorkerVersion> version =
base::MakeRefCounted<ServiceWorkerVersion>(
registration.get(), script_url, storage()->NewVersionId(),
context()->AsWeakPtr());
base::MakeRefCounted<ServiceWorkerVersion>(registration, script_url,
storage()->NewVersionId(),
context()->AsWeakPtr());
std::vector<ServiceWorkerDatabase::ResourceRecord> records;
records.push_back(WriteToDiskCacheSync(
storage(), version->script_url(), storage()->NewResourceId(),
......@@ -755,13 +776,26 @@ class ServiceWorkerRegistrationObjectHostTest
version->set_fetch_handler_existence(
ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
version->SetStatus(ServiceWorkerVersion::INSTALLING);
return version;
}
int64_t SetUpRegistration(const GURL& scope, const GURL& script_url) {
storage()->LazyInitializeForTest(base::DoNothing());
base::RunLoop().RunUntilIdle();
// Prepare ServiceWorkerRegistration and ServiceWorkerVersion.
scoped_refptr<ServiceWorkerRegistration> registration =
CreateRegistration(scope);
scoped_refptr<ServiceWorkerVersion> version =
CreateVersion(registration.get(), script_url);
// Make the registration findable via storage functions.
bool called = false;
blink::ServiceWorkerStatusCode status =
blink::ServiceWorkerStatusCode::kErrorFailed;
storage()->StoreRegistration(registration.get(), version.get(),
base::AdaptCallbackForRepeating(base::BindOnce(
&SaveStatusCallback, &called, &status)));
storage()->StoreRegistration(
registration.get(), version.get(),
base::BindOnce(&SaveStatusCallback, &called, &status));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status);
......@@ -893,6 +927,102 @@ TEST_F(ServiceWorkerRegistrationObjectHostTest,
SetBrowserClientForTesting(old_browser_client);
}
TEST_F(ServiceWorkerRegistrationObjectHostTest, Update_NoDelayFromControllee) {
const GURL kScope("https://www.example.com/");
const GURL kScriptUrl("https://www.example.com/sw.js");
int64_t registration_id = SetUpRegistration(kScope, kScriptUrl);
const int64_t kProviderId = 99; // Dummy value
ServiceWorkerRemoteProviderEndpoint remote_endpoint =
PrepareProviderHost(kProviderId, kScope);
blink::mojom::ServiceWorkerRegistrationObjectHostAssociatedPtr
registration_host_ptr;
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info =
GetRegistrationFromRemote(remote_endpoint.host_ptr()->get(), kScope);
registration_host_ptr.Bind(std::move(info->host_ptr_info));
// Ignore the messages to the registration object, otherwise the callbacks
// issued from |registration_host_ptr| may wait for receiving the messages to
// |info->request|.
info->request = nullptr;
// Get registration and set |self_update_delay| to zero.
ServiceWorkerRegistration* registration =
context()->GetLiveRegistration(registration_id);
ASSERT_TRUE(registration);
registration->set_self_update_delay(base::TimeDelta());
EXPECT_EQ(base::TimeDelta(), registration->self_update_delay());
EXPECT_EQ(blink::mojom::ServiceWorkerErrorType::kNone,
CallUpdate(registration_host_ptr.get()));
EXPECT_EQ(base::TimeDelta(), registration->self_update_delay());
}
TEST_F(ServiceWorkerRegistrationObjectHostTest,
Update_DelayFromWorkerWithoutControllee) {
const GURL kScope("https://www.example.com/");
const GURL kScriptUrl("https://www.example.com/sw.js");
scoped_refptr<ServiceWorkerRegistration> registration =
CreateRegistration(kScope);
scoped_refptr<ServiceWorkerVersion> version =
CreateVersion(registration.get(), kScriptUrl);
// Initially set |self_update_delay| to zero.
registration->set_self_update_delay(base::TimeDelta());
EXPECT_EQ(base::TimeDelta(), registration->self_update_delay());
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
CallDelayUpdate(
blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
registration.get(), version.get()));
EXPECT_LT(base::TimeDelta(), registration->self_update_delay());
// TODO(falken): Add a test verifying that a delayed update will be executed
// eventually.
// Set |self_update_delay| to a time so that update() will reject immediately.
registration->set_self_update_delay(base::TimeDelta::FromMinutes(5));
EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout,
CallDelayUpdate(
blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
registration.get(), version.get()));
EXPECT_LE(base::TimeDelta::FromMinutes(5), registration->self_update_delay());
}
TEST_F(ServiceWorkerRegistrationObjectHostTest,
Update_NoDelayFromWorkerWithControllee) {
const GURL kScope("https://www.example.com/");
const GURL kScriptUrl("https://www.example.com/sw.js");
const int64_t kProviderId = 99; // Dummy value
scoped_refptr<ServiceWorkerRegistration> registration =
CreateRegistration(kScope);
scoped_refptr<ServiceWorkerVersion> version =
CreateVersion(registration.get(), kScriptUrl);
ServiceWorkerRemoteProviderEndpoint remote_endpoint;
std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
helper_->mock_render_process_id(), kProviderId,
true /* is_parent_frame_secure */, context()->AsWeakPtr(),
&remote_endpoint);
host->SetDocumentUrl(kScope);
version->AddControllee(host.get());
// Initially set |self_update_delay| to zero.
registration->set_self_update_delay(base::TimeDelta());
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
CallDelayUpdate(
blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
registration.get(), version.get()));
EXPECT_EQ(base::TimeDelta(), registration->self_update_delay());
// Set |self_update_delay| to a time so that update() will reject immediately
// if the worker doesn't have at least one controlee.
registration->set_self_update_delay(base::TimeDelta::FromMinutes(5));
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
CallDelayUpdate(
blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
registration.get(), version.get()));
EXPECT_EQ(base::TimeDelta::FromMinutes(5), registration->self_update_delay());
}
TEST_F(ServiceWorkerRegistrationObjectHostTest, Unregister_Success) {
const GURL kScope("https://www.example.com/");
const GURL kScriptUrl("https://www.example.com/sw.js");
......
......@@ -538,6 +538,21 @@ int ServiceWorkerVersion::StartRequestWithCustomTimeout(
<< "Event of type " << static_cast<int>(event_type)
<< " can only be dispatched to an active worker: " << status();
if (event_type != ServiceWorkerMetrics::EventType::INSTALL &&
event_type != ServiceWorkerMetrics::EventType::ACTIVATE &&
event_type != ServiceWorkerMetrics::EventType::MESSAGE) {
// Reset the self-update delay iff this is not an event that can triggered
// by a service worker itself. Otherwise, service workers can use update()
// to keep running forever via install and activate events, or postMessage()
// between themselves to reset the delay via message event.
// postMessage() resets the delay in ServiceWorkerObjectHost, iff it didn't
// come from a service worker.
ServiceWorkerRegistration* registration =
context_->GetLiveRegistration(registration_id_);
DCHECK(registration) << "running workers should have a live registration";
registration->set_self_update_delay(base::TimeDelta());
}
auto request = std::make_unique<InflightRequest>(
std::move(error_callback), clock_->Now(), tick_clock_->NowTicks(),
event_type);
......@@ -669,6 +684,12 @@ void ServiceWorkerVersion::AddControllee(
RestartTick(&idle_time_);
ClearTick(&no_controllees_time_);
ServiceWorkerRegistration* registration =
context_->GetLiveRegistration(registration_id_);
if (registration) {
registration->set_self_update_delay(base::TimeDelta());
}
// Notify observers asynchronously for consistency with RemoveControllee.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
......
......@@ -773,6 +773,42 @@ TEST_F(ServiceWorkerVersionTest, StaleUpdate_DoNotDeferTimer) {
EXPECT_EQ(run_time, version_->update_timer_.desired_run_time());
}
// Tests the delay mechanism for self-updating service workers, to prevent
// them from running forever (see https://crbug.com/805496).
TEST_F(ServiceWorkerVersionTest, ResetUpdateDelay) {
const base::TimeDelta kMinute = base::TimeDelta::FromMinutes(1);
const base::TimeDelta kNoDelay = base::TimeDelta();
// Initialize the delay.
version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
registration_->SetActiveVersion(version_);
registration_->set_self_update_delay(kMinute);
// Events that can be triggered by a worker should not reset the delay.
// See the comment in ServiceWorkerVersion::StartRequestWithCustomTimeout.
SimulateDispatchEvent(ServiceWorkerMetrics::EventType::INSTALL);
SimulateDispatchEvent(ServiceWorkerMetrics::EventType::ACTIVATE);
SimulateDispatchEvent(ServiceWorkerMetrics::EventType::MESSAGE);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kMinute, registration_->self_update_delay());
// Events that can only be triggered externally reset the delay.
// Repeat the test for several such events.
SimulateDispatchEvent(ServiceWorkerMetrics::EventType::SYNC);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kNoDelay, registration_->self_update_delay());
registration_->set_self_update_delay(kMinute);
SimulateDispatchEvent(ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kNoDelay, registration_->self_update_delay());
registration_->set_self_update_delay(kMinute);
SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kNoDelay, registration_->self_update_delay());
}
TEST_F(ServiceWorkerVersionTest, UpdateCachedMetadata) {
CachedMetadataUpdateListener listener;
version_->AddObserver(&listener);
......
This is a testharness.js-based test.
FAIL Verify multiple updates from service workers without controllees do not resolve immediately assert_equals: update should not have succeeded expected "failure" but got "success"
Harness: the test ran to completion.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment