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[] = ...@@ -46,6 +46,9 @@ const char ServiceWorkerConsts::kSetNavigationPreloadHeaderErrorPrefix[] =
const char ServiceWorkerConsts::kShutdownErrorMessage[] = const char ServiceWorkerConsts::kShutdownErrorMessage[] =
"The Service Worker system has shutdown."; "The Service Worker system has shutdown.";
const char ServiceWorkerConsts::kUpdateTimeoutErrorMesage[] =
"Service worker self-update limit exceeded.";
const char ServiceWorkerConsts::kUserDeniedPermissionMessage[] = const char ServiceWorkerConsts::kUserDeniedPermissionMessage[] =
"The user denied permission to use Service Worker."; "The user denied permission to use Service Worker.";
......
...@@ -21,6 +21,7 @@ struct ServiceWorkerConsts { ...@@ -21,6 +21,7 @@ struct ServiceWorkerConsts {
static const char kNoDocumentURLErrorMessage[]; static const char kNoDocumentURLErrorMessage[];
static const char kSetNavigationPreloadHeaderErrorPrefix[]; static const char kSetNavigationPreloadHeaderErrorPrefix[];
static const char kShutdownErrorMessage[]; static const char kShutdownErrorMessage[];
static const char kUpdateTimeoutErrorMesage[];
static const char kUserDeniedPermissionMessage[]; static const char kUserDeniedPermissionMessage[];
}; };
......
...@@ -19,7 +19,7 @@ namespace content { ...@@ -19,7 +19,7 @@ namespace content {
namespace { namespace {
using StatusCallback = base::OnceCallback<void(blink::ServiceWorkerStatusCode)>; using StatusCallback = base::OnceCallback<void(blink::ServiceWorkerStatusCode)>;
using SetExtendableMessageEventSourceCallback = using PrepareExtendableMessageEventCallback =
base::OnceCallback<bool(mojom::ExtendableMessageEventPtr*)>; base::OnceCallback<bool(mojom::ExtendableMessageEventPtr*)>;
void DispatchExtendableMessageEventAfterStartWorker( void DispatchExtendableMessageEventAfterStartWorker(
...@@ -28,7 +28,7 @@ void DispatchExtendableMessageEventAfterStartWorker( ...@@ -28,7 +28,7 @@ void DispatchExtendableMessageEventAfterStartWorker(
const url::Origin& source_origin, const url::Origin& source_origin,
const base::Optional<base::TimeDelta>& timeout, const base::Optional<base::TimeDelta>& timeout,
StatusCallback callback, StatusCallback callback,
SetExtendableMessageEventSourceCallback set_source_callback, PrepareExtendableMessageEventCallback prepare_callback,
blink::ServiceWorkerStatusCode start_worker_status) { blink::ServiceWorkerStatusCode start_worker_status) {
if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) { if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
std::move(callback).Run(start_worker_status); std::move(callback).Run(start_worker_status);
...@@ -38,7 +38,7 @@ void DispatchExtendableMessageEventAfterStartWorker( ...@@ -38,7 +38,7 @@ void DispatchExtendableMessageEventAfterStartWorker(
mojom::ExtendableMessageEventPtr event = mojom::ExtendableMessageEvent::New(); mojom::ExtendableMessageEventPtr event = mojom::ExtendableMessageEvent::New();
event->message = std::move(message); event->message = std::move(message);
event->source_origin = source_origin; 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); std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorFailed);
return; return;
} }
...@@ -62,7 +62,7 @@ void StartWorkerToDispatchExtendableMessageEvent( ...@@ -62,7 +62,7 @@ void StartWorkerToDispatchExtendableMessageEvent(
const url::Origin& source_origin, const url::Origin& source_origin,
const base::Optional<base::TimeDelta>& timeout, const base::Optional<base::TimeDelta>& timeout,
StatusCallback callback, StatusCallback callback,
SetExtendableMessageEventSourceCallback set_source_callback) { PrepareExtendableMessageEventCallback prepare_callback) {
// If not enough time is left to actually process the event don't even // If not enough time is left to actually process the event don't even
// bother starting the worker and sending the event. // bother starting the worker and sending the event.
if (timeout && *timeout < base::TimeDelta::FromMilliseconds(100)) { if (timeout && *timeout < base::TimeDelta::FromMilliseconds(100)) {
...@@ -74,24 +74,39 @@ void StartWorkerToDispatchExtendableMessageEvent( ...@@ -74,24 +74,39 @@ void StartWorkerToDispatchExtendableMessageEvent(
ServiceWorkerMetrics::EventType::MESSAGE, ServiceWorkerMetrics::EventType::MESSAGE,
base::BindOnce(&DispatchExtendableMessageEventAfterStartWorker, worker, base::BindOnce(&DispatchExtendableMessageEventAfterStartWorker, worker,
std::move(message), source_origin, timeout, 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, blink::mojom::ServiceWorkerClientInfoPtr source_client_info,
mojom::ExtendableMessageEventPtr* event) { mojom::ExtendableMessageEventPtr* event) {
if (!context) {
return false;
}
DCHECK(source_client_info && !source_client_info->client_uuid.empty()); DCHECK(source_client_info && !source_client_info->client_uuid.empty());
(*event)->source_info_for_client = std::move(source_client_info); (*event)->source_info_for_client = std::move(source_client_info);
// Hide the client url if the client has a unique origin. // Hide the client url if the client has a unique origin.
if ((*event)->source_origin.unique()) if ((*event)->source_origin.unique())
(*event)->source_info_for_client->url = GURL(); (*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; return true;
} }
// The output |event| must be sent over Mojo immediately after this function // The output |event| must be sent over Mojo immediately after this function
// returns. See ServiceWorkerObjectHost::CreateCompleteObjectInfoToSend() for // returns. See ServiceWorkerObjectHost::CreateCompleteObjectInfoToSend() for
// details. // details.
bool SetSourceServiceWorkerInfo(scoped_refptr<ServiceWorkerVersion> worker, bool PrepareExtendableMessageEventFromServiceWorker(
scoped_refptr<ServiceWorkerVersion> worker,
base::WeakPtr<ServiceWorkerProviderHost> base::WeakPtr<ServiceWorkerProviderHost>
source_service_worker_provider_host, source_service_worker_provider_host,
mojom::ExtendableMessageEventPtr* event) { mojom::ExtendableMessageEventPtr* event) {
...@@ -121,11 +136,16 @@ bool SetSourceServiceWorkerInfo(scoped_refptr<ServiceWorkerVersion> worker, ...@@ -121,11 +136,16 @@ bool SetSourceServiceWorkerInfo(scoped_refptr<ServiceWorkerVersion> worker,
} }
void DispatchExtendableMessageEventFromClient( void DispatchExtendableMessageEventFromClient(
base::WeakPtr<ServiceWorkerContextCore> context,
scoped_refptr<ServiceWorkerVersion> worker, scoped_refptr<ServiceWorkerVersion> worker,
blink::TransferableMessage message, blink::TransferableMessage message,
const url::Origin& source_origin, const url::Origin& source_origin,
StatusCallback callback, StatusCallback callback,
blink::mojom::ServiceWorkerClientInfoPtr source_client_info) { 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 // |source_client_info| may be null if a client sent the message but its
// info could not be retrieved. // info could not be retrieved.
if (!source_client_info) { if (!source_client_info) {
...@@ -136,7 +156,8 @@ void DispatchExtendableMessageEventFromClient( ...@@ -136,7 +156,8 @@ void DispatchExtendableMessageEventFromClient(
StartWorkerToDispatchExtendableMessageEvent( StartWorkerToDispatchExtendableMessageEvent(
worker, std::move(message), source_origin, base::nullopt /* timeout */, worker, std::move(message), source_origin, base::nullopt /* timeout */,
std::move(callback), 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( void DispatchExtendableMessageEventFromServiceWorker(
...@@ -156,7 +177,7 @@ void DispatchExtendableMessageEventFromServiceWorker( ...@@ -156,7 +177,7 @@ void DispatchExtendableMessageEventFromServiceWorker(
source_service_worker_provider_host->provider_type()); source_service_worker_provider_host->provider_type());
StartWorkerToDispatchExtendableMessageEvent( StartWorkerToDispatchExtendableMessageEvent(
worker, std::move(message), source_origin, timeout, std::move(callback), worker, std::move(message), source_origin, timeout, std::move(callback),
base::BindOnce(&SetSourceServiceWorkerInfo, worker, base::BindOnce(&PrepareExtendableMessageEventFromServiceWorker, worker,
source_service_worker_provider_host)); source_service_worker_provider_host));
} }
...@@ -261,8 +282,8 @@ void ServiceWorkerObjectHost::DispatchExtendableMessageEvent( ...@@ -261,8 +282,8 @@ void ServiceWorkerObjectHost::DispatchExtendableMessageEvent(
case blink::mojom::ServiceWorkerProviderType::kForWindow: case blink::mojom::ServiceWorkerProviderType::kForWindow:
service_worker_client_utils::GetClient( service_worker_client_utils::GetClient(
provider_host_, provider_host_,
base::BindOnce(&DispatchExtendableMessageEventFromClient, version_, base::BindOnce(&DispatchExtendableMessageEventFromClient, context_,
std::move(message), provider_origin_, version_, std::move(message), provider_origin_,
std::move(callback))); std::move(callback)));
return; return;
case blink::mojom::ServiceWorkerProviderType::kForServiceWorker: { case blink::mojom::ServiceWorkerProviderType::kForServiceWorker: {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_version.h" #include "content/browser/service_worker/service_worker_version.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/common/service_worker/service_worker_types.h" #include "content/common/service_worker/service_worker_types.h"
...@@ -162,6 +163,13 @@ class CONTENT_EXPORT ServiceWorkerRegistration ...@@ -162,6 +163,13 @@ class CONTENT_EXPORT ServiceWorkerRegistration
base::Time last_update_check() const { return last_update_check_; } base::Time last_update_check() const { return last_update_check_; }
void set_last_update_check(base::Time last) { last_update_check_ = last; } 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 // Unsets the version and deletes its resources. Also deletes this
// registration from storage if there is no longer a stored version. // registration from storage if there is no longer a stored version.
void DeleteVersion(const scoped_refptr<ServiceWorkerVersion>& version); void DeleteVersion(const scoped_refptr<ServiceWorkerVersion>& version);
...@@ -227,6 +235,7 @@ class CONTENT_EXPORT ServiceWorkerRegistration ...@@ -227,6 +235,7 @@ class CONTENT_EXPORT ServiceWorkerRegistration
bool should_activate_when_ready_; bool should_activate_when_ready_;
blink::mojom::NavigationPreloadState navigation_preload_state_; blink::mojom::NavigationPreloadState navigation_preload_state_;
base::Time last_update_check_; base::Time last_update_check_;
base::TimeDelta self_update_delay_;
int64_t resources_total_size_bytes_; int64_t resources_total_size_bytes_;
// This registration is the primary owner of these versions. // This registration is the primary owner of these versions.
......
...@@ -4,11 +4,13 @@ ...@@ -4,11 +4,13 @@
#include "content/browser/service_worker/service_worker_registration_object_host.h" #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_consts.h"
#include "content/browser/service_worker/service_worker_context_core.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_object_host.h"
#include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/service_worker/service_worker_provider_host.h"
#include "content/common/service_worker/service_worker_utils.h" #include "content/common/service_worker/service_worker_utils.h"
#include "content/public/browser/browser_thread.h"
#include "net/http/http_util.h" #include "net/http/http_util.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
...@@ -16,6 +18,9 @@ namespace content { ...@@ -16,6 +18,9 @@ namespace content {
namespace { 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. // Returns an object info to send over Mojo. The info must be sent immediately.
// See ServiceWorkerObjectHost::CreateCompleteObjectInfoToSend() for details. // See ServiceWorkerObjectHost::CreateCompleteObjectInfoToSend() for details.
blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend( blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend(
...@@ -28,6 +33,45 @@ blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend( ...@@ -28,6 +33,45 @@ blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend(
return service_worker_object_host->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 } // anonymous namespace
ServiceWorkerRegistrationObjectHost::ServiceWorkerRegistrationObjectHost( ServiceWorkerRegistrationObjectHost::ServiceWorkerRegistrationObjectHost(
...@@ -116,14 +160,58 @@ void ServiceWorkerRegistrationObjectHost::Update(UpdateCallback callback) { ...@@ -116,14 +160,58 @@ void ServiceWorkerRegistrationObjectHost::Update(UpdateCallback callback) {
return; return;
} }
context_->UpdateServiceWorker( DelayUpdate(
registration_.get(), false /* force_bypass_cache */, provider_host_->provider_type(), registration_.get(),
false /* skip_script_comparison */, provider_host_->running_hosted_version(),
base::AdaptCallbackForRepeating( base::BindOnce(
&ExecuteUpdate, context_, registration_->id(),
false /* force_bypass_cache */, false /* skip_script_comparison */,
base::BindOnce(&ServiceWorkerRegistrationObjectHost::UpdateComplete, base::BindOnce(&ServiceWorkerRegistrationObjectHost::UpdateComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)))); 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( void ServiceWorkerRegistrationObjectHost::Unregister(
UnregisterCallback callback) { UnregisterCallback callback) {
if (!CanServeRegistrationObjectHostMethods( if (!CanServeRegistrationObjectHostMethods(
...@@ -302,6 +390,7 @@ void ServiceWorkerRegistrationObjectHost::SetVersionAttributes( ...@@ -302,6 +390,7 @@ void ServiceWorkerRegistrationObjectHost::SetVersionAttributes(
waiting = CreateCompleteObjectInfoToSend(provider_host_, waiting_version); waiting = CreateCompleteObjectInfoToSend(provider_host_, waiting_version);
if (changed_mask.active_changed()) if (changed_mask.active_changed())
active = CreateCompleteObjectInfoToSend(provider_host_, active_version); active = CreateCompleteObjectInfoToSend(provider_host_, active_version);
DCHECK(remote_registration_); DCHECK(remote_registration_);
remote_registration_->SetVersionAttributes( remote_registration_->SetVersionAttributes(
changed_mask.changed(), std::move(installing), std::move(waiting), changed_mask.changed(), std::move(installing), std::move(waiting),
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace content { namespace content {
namespace service_worker_registration_unittest {
class ServiceWorkerRegistrationObjectHostTest;
} // namespace service_worker_registration_unittest
class ServiceWorkerContextCore; class ServiceWorkerContextCore;
class ServiceWorkerVersion; class ServiceWorkerVersion;
...@@ -46,6 +49,12 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost ...@@ -46,6 +49,12 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost
ServiceWorkerRegistration* registration() { return registration_.get(); } ServiceWorkerRegistration* registration() { return registration_.get(); }
private: private:
friend class service_worker_registration_unittest::
ServiceWorkerRegistrationObjectHostTest;
using StatusCallback =
base::OnceCallback<void(blink::ServiceWorkerStatusCode status)>;
// ServiceWorkerRegistration::Listener overrides. // ServiceWorkerRegistration::Listener overrides.
void OnVersionAttributesChanged( void OnVersionAttributesChanged(
ServiceWorkerRegistration* registration, ServiceWorkerRegistration* registration,
...@@ -68,6 +77,20 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost ...@@ -68,6 +77,20 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost
const std::string& value, const std::string& value,
SetNavigationPreloadHeaderCallback callback) override; 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. // Called back from ServiceWorkerContextCore when an update is complete.
void UpdateComplete(UpdateCallback callback, void UpdateComplete(UpdateCallback callback,
blink::ServiceWorkerStatusCode status, blink::ServiceWorkerStatusCode status,
......
...@@ -698,6 +698,27 @@ class ServiceWorkerRegistrationObjectHostTest ...@@ -698,6 +698,27 @@ class ServiceWorkerRegistrationObjectHostTest
return error; 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::ServiceWorkerErrorType CallUnregister(
blink::mojom::ServiceWorkerRegistrationObjectHost* registration_host) { blink::mojom::ServiceWorkerRegistrationObjectHost* registration_host) {
blink::mojom::ServiceWorkerErrorType error = blink::mojom::ServiceWorkerErrorType error =
...@@ -730,20 +751,20 @@ class ServiceWorkerRegistrationObjectHostTest ...@@ -730,20 +751,20 @@ class ServiceWorkerRegistrationObjectHostTest
return status.value(); return status.value();
} }
int64_t SetUpRegistration(const GURL& scope, const GURL& script_url) { scoped_refptr<ServiceWorkerRegistration> CreateRegistration(
storage()->LazyInitializeForTest(base::DoNothing()); const GURL& scope) {
base::RunLoop().RunUntilIdle();
// Prepare ServiceWorkerRegistration.
blink::mojom::ServiceWorkerRegistrationOptions options; blink::mojom::ServiceWorkerRegistrationOptions options;
options.scope = scope; options.scope = scope;
scoped_refptr<ServiceWorkerRegistration> registration = return base::MakeRefCounted<ServiceWorkerRegistration>(
base::MakeRefCounted<ServiceWorkerRegistration>(
options, storage()->NewRegistrationId(), context()->AsWeakPtr()); options, storage()->NewRegistrationId(), context()->AsWeakPtr());
// Prepare ServiceWorkerVersion. }
scoped_refptr<ServiceWorkerVersion> CreateVersion(
ServiceWorkerRegistration* registration,
const GURL& script_url) {
scoped_refptr<ServiceWorkerVersion> version = scoped_refptr<ServiceWorkerVersion> version =
base::MakeRefCounted<ServiceWorkerVersion>( base::MakeRefCounted<ServiceWorkerVersion>(registration, script_url,
registration.get(), script_url, storage()->NewVersionId(), storage()->NewVersionId(),
context()->AsWeakPtr()); context()->AsWeakPtr());
std::vector<ServiceWorkerDatabase::ResourceRecord> records; std::vector<ServiceWorkerDatabase::ResourceRecord> records;
records.push_back(WriteToDiskCacheSync( records.push_back(WriteToDiskCacheSync(
...@@ -755,13 +776,26 @@ class ServiceWorkerRegistrationObjectHostTest ...@@ -755,13 +776,26 @@ class ServiceWorkerRegistrationObjectHostTest
version->set_fetch_handler_existence( version->set_fetch_handler_existence(
ServiceWorkerVersion::FetchHandlerExistence::EXISTS); ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
version->SetStatus(ServiceWorkerVersion::INSTALLING); 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. // Make the registration findable via storage functions.
bool called = false; bool called = false;
blink::ServiceWorkerStatusCode status = blink::ServiceWorkerStatusCode status =
blink::ServiceWorkerStatusCode::kErrorFailed; blink::ServiceWorkerStatusCode::kErrorFailed;
storage()->StoreRegistration(registration.get(), version.get(), storage()->StoreRegistration(
base::AdaptCallbackForRepeating(base::BindOnce( registration.get(), version.get(),
&SaveStatusCallback, &called, &status))); base::BindOnce(&SaveStatusCallback, &called, &status));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status); EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status);
...@@ -893,6 +927,102 @@ TEST_F(ServiceWorkerRegistrationObjectHostTest, ...@@ -893,6 +927,102 @@ TEST_F(ServiceWorkerRegistrationObjectHostTest,
SetBrowserClientForTesting(old_browser_client); 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) { TEST_F(ServiceWorkerRegistrationObjectHostTest, Unregister_Success) {
const GURL kScope("https://www.example.com/"); const GURL kScope("https://www.example.com/");
const GURL kScriptUrl("https://www.example.com/sw.js"); const GURL kScriptUrl("https://www.example.com/sw.js");
......
...@@ -538,6 +538,21 @@ int ServiceWorkerVersion::StartRequestWithCustomTimeout( ...@@ -538,6 +538,21 @@ int ServiceWorkerVersion::StartRequestWithCustomTimeout(
<< "Event of type " << static_cast<int>(event_type) << "Event of type " << static_cast<int>(event_type)
<< " can only be dispatched to an active worker: " << status(); << " 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>( auto request = std::make_unique<InflightRequest>(
std::move(error_callback), clock_->Now(), tick_clock_->NowTicks(), std::move(error_callback), clock_->Now(), tick_clock_->NowTicks(),
event_type); event_type);
...@@ -669,6 +684,12 @@ void ServiceWorkerVersion::AddControllee( ...@@ -669,6 +684,12 @@ void ServiceWorkerVersion::AddControllee(
RestartTick(&idle_time_); RestartTick(&idle_time_);
ClearTick(&no_controllees_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. // Notify observers asynchronously for consistency with RemoveControllee.
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, FROM_HERE,
......
...@@ -773,6 +773,42 @@ TEST_F(ServiceWorkerVersionTest, StaleUpdate_DoNotDeferTimer) { ...@@ -773,6 +773,42 @@ TEST_F(ServiceWorkerVersionTest, StaleUpdate_DoNotDeferTimer) {
EXPECT_EQ(run_time, version_->update_timer_.desired_run_time()); 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) { TEST_F(ServiceWorkerVersionTest, UpdateCachedMetadata) {
CachedMetadataUpdateListener listener; CachedMetadataUpdateListener listener;
version_->AddObserver(&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