blob: 919d2aa9f16c4cfa26fd7f0986aad1f7a0f99d67 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/background_sync/background_sync_manager.h"
#include <algorithm>
#include <utility>
#include "base/barrier_closure.h"
#include "base/containers/contains.h"
#include "base/debug/crash_logging.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/background_sync/background_sync_metrics.h"
#include "content/browser/background_sync/background_sync_network_observer.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/background_sync_controller.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/render_process_host.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/common/service_worker/embedded_worker_status.h"
#include "third_party/blink/public/common/service_worker/service_worker_type_converters.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
#if BUILDFLAG(IS_ANDROID)
#include "content/browser/android/background_sync_network_observer_android.h"
#include "content/browser/background_sync/background_sync_launcher.h"
#endif
using blink::mojom::BackgroundSyncType;
using blink::mojom::PermissionStatus;
using SyncAndNotificationPermissions =
std::pair<PermissionStatus, PermissionStatus>;
namespace content {
// TODO(crbug.com/40614176): Use blink::mojom::BackgroundSyncError
// directly and eliminate these checks.
#define COMPILE_ASSERT_MATCHING_ENUM(mojo_name, manager_name) \
static_assert(static_cast<int>(blink::mojo_name) == \
static_cast<int>(content::manager_name), \
"mojo and manager enums must match")
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NONE,
BACKGROUND_SYNC_STATUS_OK);
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::STORAGE,
BACKGROUND_SYNC_STATUS_STORAGE_ERROR);
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_FOUND,
BACKGROUND_SYNC_STATUS_NOT_FOUND);
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NO_SERVICE_WORKER,
BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER);
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_ALLOWED,
BACKGROUND_SYNC_STATUS_NOT_ALLOWED);
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::PERMISSION_DENIED,
BACKGROUND_SYNC_STATUS_PERMISSION_DENIED);
COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::MAX,
BACKGROUND_SYNC_STATUS_PERMISSION_DENIED);
namespace {
// The only allowed value of min_interval for one shot Background Sync
// registrations.
constexpr int kMinIntervalForOneShotSync = -1;
// The key used to index the background sync data in ServiceWorkerStorage.
const char kBackgroundSyncUserDataKey[] = "BackgroundSyncUserData";
void RecordFailureAndPostError(
BackgroundSyncType sync_type,
BackgroundSyncStatus status,
BackgroundSyncManager::StatusAndRegistrationCallback callback) {
BackgroundSyncMetrics::CountRegisterFailure(sync_type, status);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), status, nullptr));
}
// Returns nullptr if the browser context cannot be accessed for any reason.
BrowserContext* GetBrowserContext(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!service_worker_context)
return nullptr;
StoragePartitionImpl* storage_partition_impl =
service_worker_context->storage_partition();
if (!storage_partition_impl) // may be null in tests
return nullptr;
return storage_partition_impl->browser_context();
}
// Returns nullptr if the controller cannot be accessed for any reason.
BackgroundSyncController* GetBackgroundSyncController(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserContext* browser_context =
GetBrowserContext(std::move(service_worker_context));
if (!browser_context)
return nullptr;
return browser_context->GetBackgroundSyncController();
}
SyncAndNotificationPermissions GetBackgroundSyncPermission(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
const url::Origin& origin,
RenderProcessHost* render_process_host,
BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserContext* browser_context =
GetBrowserContext(std::move(service_worker_context));
if (!browser_context)
return {PermissionStatus::DENIED, PermissionStatus::DENIED};
PermissionController* permission_controller =
browser_context->GetPermissionController();
DCHECK(permission_controller);
// The requesting origin always matches the embedding origin.
auto sync_permission = permission_controller->GetPermissionStatusForWorker(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
sync_type == BackgroundSyncType::ONE_SHOT
? blink::PermissionType::BACKGROUND_SYNC
: blink::PermissionType::PERIODIC_BACKGROUND_SYNC),
render_process_host, origin);
auto notification_permission =
permission_controller->GetPermissionStatusForWorker(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
blink::PermissionType::NOTIFICATIONS),
render_process_host, origin);
return {sync_permission, notification_permission};
}
void NotifyOneShotBackgroundSyncRegistered(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const url::Origin& origin,
bool can_fire,
bool is_reregistered) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncController(std::move(sw_context_wrapper));
if (!background_sync_controller)
return;
background_sync_controller->NotifyOneShotBackgroundSyncRegistered(
origin, can_fire, is_reregistered);
}
void NotifyPeriodicBackgroundSyncRegistered(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const url::Origin& origin,
int min_interval,
bool is_reregistered) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncController(std::move(sw_context_wrapper));
if (!background_sync_controller)
return;
background_sync_controller->NotifyPeriodicBackgroundSyncRegistered(
origin, min_interval, is_reregistered);
}
void NotifyOneShotBackgroundSyncCompleted(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const url::Origin& origin,
blink::ServiceWorkerStatusCode status_code,
int num_attempts,
int max_attempts) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncController(std::move(sw_context_wrapper));
if (!background_sync_controller)
return;
background_sync_controller->NotifyOneShotBackgroundSyncCompleted(
origin, status_code, num_attempts, max_attempts);
}
void NotifyPeriodicBackgroundSyncCompleted(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const url::Origin& origin,
blink::ServiceWorkerStatusCode status_code,
int num_attempts,
int max_attempts) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncController(std::move(sw_context_wrapper));
if (!background_sync_controller)
return;
background_sync_controller->NotifyPeriodicBackgroundSyncCompleted(
origin, status_code, num_attempts, max_attempts);
}
std::unique_ptr<BackgroundSyncParameters> GetControllerParameters(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
std::unique_ptr<BackgroundSyncParameters> parameters) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncController(sw_context_wrapper);
if (!background_sync_controller) {
// If there is no controller then BackgroundSync can't run in the
// background, disable it.
parameters->disable = true;
return parameters;
}
background_sync_controller->GetParameterOverrides(parameters.get());
return parameters;
}
base::TimeDelta GetNextEventDelay(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const BackgroundSyncRegistration& registration,
std::unique_ptr<BackgroundSyncParameters> parameters,
base::TimeDelta time_till_soonest_scheduled_event_for_origin) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncController(sw_context_wrapper);
if (!background_sync_controller)
return base::TimeDelta::Max();
return background_sync_controller->GetNextEventDelay(
registration, parameters.get(),
time_till_soonest_scheduled_event_for_origin);
}
void OnSyncEventFinished(scoped_refptr<ServiceWorkerVersion> active_version,
int request_id,
ServiceWorkerVersion::StatusCallback callback,
blink::mojom::ServiceWorkerEventStatus status) {
if (!active_version->FinishRequest(
request_id,
status == blink::mojom::ServiceWorkerEventStatus::COMPLETED)) {
return;
}
std::move(callback).Run(
mojo::ConvertTo<blink::ServiceWorkerStatusCode>(status));
}
void DidStartWorkerForSyncEvent(
base::OnceCallback<void(ServiceWorkerVersion::StatusCallback)> task,
ServiceWorkerVersion::StatusCallback callback,
blink::ServiceWorkerStatusCode start_worker_status) {
if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
std::move(callback).Run(start_worker_status);
return;
}
std::move(task).Run(std::move(callback));
}
BackgroundSyncType GetBackgroundSyncType(
const blink::mojom::SyncRegistrationOptions& options) {
return options.min_interval == -1 ? BackgroundSyncType::ONE_SHOT
: BackgroundSyncType::PERIODIC;
}
std::string GetSyncEventName(const BackgroundSyncType sync_type) {
if (sync_type == BackgroundSyncType::ONE_SHOT)
return "sync";
else
return "periodicsync";
}
DevToolsBackgroundService GetDevToolsBackgroundService(
BackgroundSyncType sync_type) {
if (sync_type == BackgroundSyncType::ONE_SHOT)
return DevToolsBackgroundService::kBackgroundSync;
else
return DevToolsBackgroundService::kPeriodicBackgroundSync;
}
std::string GetDelayAsString(base::TimeDelta delay) {
if (delay.is_max())
return "infinite";
return base::NumberToString(delay.InMilliseconds());
}
std::string GetEventStatusString(blink::ServiceWorkerStatusCode status_code) {
// The |status_code| is derived from blink::mojom::ServiceWorkerEventStatus.
switch (status_code) {
case blink::ServiceWorkerStatusCode::kOk:
return "succeeded";
case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
return "waitUntil rejected";
case blink::ServiceWorkerStatusCode::kErrorFailed:
return "failed";
case blink::ServiceWorkerStatusCode::kErrorAbort:
return "aborted";
case blink::ServiceWorkerStatusCode::kErrorTimeout:
return "timeout";
default:
SCOPED_CRASH_KEY_NUMBER("BGSM", "status_code",
static_cast<int>(status_code));
DUMP_WILL_BE_NOTREACHED()
<< "status_code " << static_cast<int>(status_code);
return "unknown error";
}
}
int GetNumAttemptsAfterEvent(BackgroundSyncType sync_type,
int current_num_attempts,
int max_attempts,
blink::mojom::BackgroundSyncState sync_state,
bool succeeded) {
int num_attempts = ++current_num_attempts;
if (sync_type == BackgroundSyncType::PERIODIC) {
if (succeeded)
return 0;
if (num_attempts == max_attempts)
return 0;
}
if (sync_state ==
blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING) {
return 0;
}
return num_attempts;
}
// This prevents the browser process from shutting down when the last browser
// window is closed and there are one-shot Background Sync events ready to fire.
std::unique_ptr<BackgroundSyncController::BackgroundSyncEventKeepAlive>
CreateBackgroundSyncEventKeepAlive(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const blink::mojom::BackgroundSyncRegistrationInfo& registration_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* controller =
GetBackgroundSyncController(sw_context_wrapper);
if (!controller ||
registration_info.sync_type != BackgroundSyncType::ONE_SHOT) {
return nullptr;
}
return controller->CreateBackgroundSyncEventKeepAlive();
}
} // namespace
BackgroundSyncManager::BackgroundSyncRegistrations::
BackgroundSyncRegistrations() = default;
BackgroundSyncManager::BackgroundSyncRegistrations::BackgroundSyncRegistrations(
const BackgroundSyncRegistrations& other) = default;
BackgroundSyncManager::BackgroundSyncRegistrations::
~BackgroundSyncRegistrations() = default;
// static
std::unique_ptr<BackgroundSyncManager> BackgroundSyncManager::Create(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
DevToolsBackgroundServicesContextImpl& devtools_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncManager* sync_manager = new BackgroundSyncManager(
std::move(service_worker_context), devtools_context);
sync_manager->Init();
return base::WrapUnique(sync_manager);
}
BackgroundSyncManager::~BackgroundSyncManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
service_worker_context_->RemoveObserver(this);
}
void BackgroundSyncManager::Register(
int64_t sw_registration_id,
int render_process_host_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(callback));
return;
}
DCHECK(options.min_interval >= 0 ||
options.min_interval == kMinIntervalForOneShotSync);
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::RegisterCheckIfHasMainFrame,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
render_process_host_id, std::move(options),
op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::UnregisterPeriodicSync(
int64_t sw_registration_id,
const std::string& tag,
BackgroundSyncManager::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
BACKGROUND_SYNC_STATUS_STORAGE_ERROR));
return;
}
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::UnregisterPeriodicSyncImpl,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id, tag,
op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::DidResolveRegistration(
blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_)
return;
op_scheduler_.ScheduleOperation(base::BindOnce(
&BackgroundSyncManager::DidResolveRegistrationImpl,
weak_ptr_factory_.GetWeakPtr(), std::move(registration_info)));
}
void BackgroundSyncManager::GetOneShotSyncRegistrations(
int64_t sw_registration_id,
StatusAndRegistrationsCallback callback) {
GetRegistrations(BackgroundSyncType::ONE_SHOT, sw_registration_id,
std::move(callback));
}
void BackgroundSyncManager::GetPeriodicSyncRegistrations(
int64_t sw_registration_id,
StatusAndRegistrationsCallback callback) {
GetRegistrations(BackgroundSyncType::PERIODIC, sw_registration_id,
std::move(callback));
}
void BackgroundSyncManager::UnregisterPeriodicSyncForOrigin(
const url::Origin& origin) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::UnregisterForOriginImpl,
weak_ptr_factory_.GetWeakPtr(), std::move(origin),
MakeEmptyCompletion()));
}
void BackgroundSyncManager::UnregisterForOriginImpl(
const url::Origin& origin,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
std::vector<int64_t> service_worker_registrations_affected;
for (const auto& service_worker_and_registration : active_registrations_) {
const auto registrations = service_worker_and_registration.second;
if (registrations.origin != origin)
continue;
service_worker_registrations_affected.emplace_back(
service_worker_and_registration.first);
}
if (service_worker_registrations_affected.empty()) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
base::RepeatingClosure barrier_closure = base::BarrierClosure(
service_worker_registrations_affected.size(),
base::BindOnce(
&BackgroundSyncManager::UnregisterForOriginScheduleDelayedProcessing,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
for (int64_t service_worker_registration_id :
service_worker_registrations_affected) {
StoreRegistrations(
service_worker_registration_id,
base::BindOnce(&BackgroundSyncManager::UnregisterForOriginDidStore,
weak_ptr_factory_.GetWeakPtr(),
service_worker_registration_id, barrier_closure));
}
}
void BackgroundSyncManager::UnregisterForOriginDidStore(
int64_t service_worker_registration_id_to_remove,
base::OnceClosure done_closure,
blink::ServiceWorkerStatusCode status) {
active_registrations_.erase(service_worker_registration_id_to_remove);
if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
// The service worker registration is gone.
std::move(done_closure).Run();
return;
}
if (status != blink::ServiceWorkerStatusCode::kOk) {
DisableAndClearManager(std::move(done_closure));
return;
}
std::move(done_closure).Run();
}
void BackgroundSyncManager::UnregisterForOriginScheduleDelayedProcessing(
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ScheduleOrCancelDelayedProcessing(BackgroundSyncType::PERIODIC);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::GetRegistrations(
BackgroundSyncType sync_type,
int64_t sw_registration_id,
StatusAndRegistrationsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The renderer should have checked and disallowed the request for fenced
// frames and thrown an exception in blink::SyncManager or
// blink::PeriodicSyncManager. Return a not allowed error if the renderer side
// check didn't happen for some reason.
scoped_refptr<ServiceWorkerRegistration> sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (sw_registration && sw_registration->ancestor_frame_type() ==
blink::mojom::AncestorFrameType::kFencedFrame) {
mojo::ReportBadMessage("Background Sync is not allowed in a fenced frame");
return;
}
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback), BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::vector<std::unique_ptr<BackgroundSyncRegistration>>()));
return;
}
op_scheduler_.ScheduleOperation(base::BindOnce(
&BackgroundSyncManager::GetRegistrationsImpl,
weak_ptr_factory_.GetWeakPtr(), sync_type, sw_registration_id,
op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::OnRegistrationDeleted(
int64_t sw_registration_id,
const GURL& pattern,
const blink::StorageKey& key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Operations already in the queue will either fail when they write to storage
// or return stale results based on registrations loaded in memory. This is
// inconsequential since the service worker is gone.
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::OnRegistrationDeletedImpl,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
MakeEmptyCompletion()));
}
void BackgroundSyncManager::OnStorageWiped() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Operations already in the queue will either fail when they write to storage
// or return stale results based on registrations loaded in memory. This is
// inconsequential since the service workers are gone.
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::OnStorageWipedImpl,
weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion()));
}
void BackgroundSyncManager::EmulateDispatchSyncEvent(
const std::string& tag,
scoped_refptr<ServiceWorkerVersion> active_version,
bool last_chance,
ServiceWorkerVersion::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::ServiceWorkerStatusCode code = CanEmulateSyncEvent(active_version);
if (code != blink::ServiceWorkerStatusCode::kOk) {
std::move(callback).Run(code);
return;
}
DispatchSyncEvent(tag, std::move(active_version), last_chance,
std::move(callback));
}
void BackgroundSyncManager::EmulateDispatchPeriodicSyncEvent(
const std::string& tag,
scoped_refptr<ServiceWorkerVersion> active_version,
ServiceWorkerVersion::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::ServiceWorkerStatusCode code = CanEmulateSyncEvent(active_version);
if (code != blink::ServiceWorkerStatusCode::kOk) {
std::move(callback).Run(code);
return;
}
DispatchPeriodicSyncEvent(tag, std::move(active_version),
std::move(callback));
}
void BackgroundSyncManager::EmulateServiceWorkerOffline(
int64_t service_worker_id,
bool is_offline) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Multiple DevTools sessions may want to set the same SW offline, which
// is supposed to disable the background sync. For consistency with the
// network stack, SW remains offline until all DevTools sessions disable
// the offline mode.
emulated_offline_sw_[service_worker_id] += is_offline ? 1 : -1;
if (emulated_offline_sw_[service_worker_id] > 0)
return;
emulated_offline_sw_.erase(service_worker_id);
FireReadyEvents(BackgroundSyncType::ONE_SHOT, /* reschedule= */ true,
base::DoNothing());
}
BackgroundSyncManager::BackgroundSyncManager(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
DevToolsBackgroundServicesContextImpl& devtools_context)
: op_scheduler_(base::SingleThreadTaskRunner::GetCurrentDefault()),
service_worker_context_(std::move(service_worker_context)),
proxy_(std::make_unique<BackgroundSyncProxy>(service_worker_context_)),
devtools_context_(&devtools_context),
parameters_(std::make_unique<BackgroundSyncParameters>()),
disabled_(false),
num_firing_registrations_one_shot_(0),
num_firing_registrations_periodic_(0),
clock_(base::DefaultClock::GetInstance()) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(devtools_context_);
DCHECK(service_worker_context_);
service_worker_context_->AddObserver(this);
#if BUILDFLAG(IS_ANDROID)
network_observer_ = std::make_unique<BackgroundSyncNetworkObserverAndroid>(
base::BindRepeating(&BackgroundSyncManager::OnNetworkChanged,
weak_ptr_factory_.GetWeakPtr()));
#else
network_observer_ = std::make_unique<BackgroundSyncNetworkObserver>(
base::BindRepeating(&BackgroundSyncManager::OnNetworkChanged,
weak_ptr_factory_.GetWeakPtr()));
#endif
}
void BackgroundSyncManager::Init() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!op_scheduler_.ScheduledOperations());
DCHECK(!disabled_);
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::InitImpl,
weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion()));
}
void BackgroundSyncManager::InitImpl(base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
InitDidGetControllerParameters(
std::move(callback),
GetControllerParameters(
service_worker_context_,
std::make_unique<BackgroundSyncParameters>(*parameters_)));
}
void BackgroundSyncManager::InitDidGetControllerParameters(
base::OnceClosure callback,
std::unique_ptr<BackgroundSyncParameters> updated_parameters) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
parameters_ = std::move(updated_parameters);
if (parameters_->disable) {
disabled_ = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
network_observer_->Init();
GetDataFromBackend(
kBackgroundSyncUserDataKey,
base::BindOnce(&BackgroundSyncManager::InitDidGetDataFromBackend,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void BackgroundSyncManager::InitDidGetDataFromBackend(
base::OnceClosure callback,
const std::vector<std::pair<int64_t, std::string>>& user_data,
blink::ServiceWorkerStatusCode status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status != blink::ServiceWorkerStatusCode::kOk &&
status != blink::ServiceWorkerStatusCode::kErrorNotFound) {
DisableAndClearManager(std::move(callback));
return;
}
std::set<url::Origin> suspended_periodic_sync_origins;
std::set<url::Origin> registered_origins;
for (const std::pair<int64_t, std::string>& data : user_data) {
BackgroundSyncRegistrationsProto registrations_proto;
if (registrations_proto.ParseFromString(data.second)) {
BackgroundSyncRegistrations* registrations =
&active_registrations_[data.first];
registrations->origin =
url::Origin::Create(GURL(registrations_proto.origin()));
for (const auto& registration_proto :
registrations_proto.registration()) {
BackgroundSyncType sync_type =
registration_proto.has_periodic_sync_options()
? BackgroundSyncType::PERIODIC
: BackgroundSyncType::ONE_SHOT;
BackgroundSyncRegistration* registration =
&registrations
->registration_map[{registration_proto.tag(), sync_type}];
blink::mojom::SyncRegistrationOptions* options =
registration->options();
options->tag = registration_proto.tag();
if (sync_type == BackgroundSyncType::PERIODIC) {
options->min_interval =
registration_proto.periodic_sync_options().min_interval();
if (options->min_interval < 0) {
DisableAndClearManager(std::move(callback));
return;
}
} else {
options->min_interval = kMinIntervalForOneShotSync;
}
registration->set_num_attempts(registration_proto.num_attempts());
registration->set_delay_until(
base::Time::FromInternalValue(registration_proto.delay_until()));
registration->set_origin(registrations->origin);
registered_origins.insert(registration->origin());
if (registration->is_suspended()) {
suspended_periodic_sync_origins.insert(registration->origin());
}
registration->set_resolved();
if (registration_proto.has_max_attempts())
registration->set_max_attempts(registration_proto.max_attempts());
else
registration->set_max_attempts(parameters_->max_sync_attempts);
}
}
}
FireReadyEvents(BackgroundSyncType::ONE_SHOT, /* reschedule= */ true,
base::DoNothing());
FireReadyEvents(BackgroundSyncType::PERIODIC, /* reschedule= */ true,
base::DoNothing());
proxy_->SendSuspendedPeriodicSyncOrigins(
std::move(suspended_periodic_sync_origins));
proxy_->SendRegisteredPeriodicSyncOrigins(std::move(registered_origins));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::RegisterCheckIfHasMainFrame(
int64_t sw_registration_id,
int render_process_host_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<ServiceWorkerRegistration> sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (!sw_registration || !sw_registration->active_version()) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
// The renderer should have checked and disallowed the request for fenced
// frames and thrown an exception in blink::SyncManager or
// blink::PeriodicSyncManager. Return a not allowed error if the renderer side
// check didn't happen for some reason.
if (sw_registration->ancestor_frame_type() ==
blink::mojom::AncestorFrameType::kFencedFrame) {
mojo::ReportBadMessage("Background Sync is not allowed in a fenced frame");
return;
}
HasMainFrameWindowClient(
sw_registration->key(),
base::BindOnce(&BackgroundSyncManager::RegisterDidCheckIfMainFrame,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
render_process_host_id, std::move(options),
std::move(callback)));
}
void BackgroundSyncManager::RegisterDidCheckIfMainFrame(
int64_t sw_registration_id,
int render_process_host_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback,
bool has_main_frame_client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!has_main_frame_client) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_NOT_ALLOWED,
std::move(callback));
return;
}
RegisterImpl(sw_registration_id, render_process_host_id, std::move(options),
std::move(callback));
}
void BackgroundSyncManager::RegisterImpl(
int64_t sw_registration_id,
int render_process_host_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(callback));
return;
}
if (options.tag.length() > kMaxTagLength) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_NOT_ALLOWED,
std::move(callback));
return;
}
scoped_refptr<ServiceWorkerRegistration> sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (!sw_registration || !sw_registration->active_version()) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
RenderProcessHost* render_process_host =
RenderProcessHost::FromID(render_process_host_id);
if (!render_process_host) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
BackgroundSyncType sync_type = GetBackgroundSyncType(options);
if (parameters_->skip_permissions_check_for_testing) {
RegisterDidAskForPermission(
sw_registration_id, std::move(options), std::move(callback),
{PermissionStatus::GRANTED, PermissionStatus::GRANTED});
return;
}
SyncAndNotificationPermissions permission = GetBackgroundSyncPermission(
service_worker_context_, sw_registration->key().origin(),
render_process_host, sync_type);
RegisterDidAskForPermission(sw_registration_id, std::move(options),
std::move(callback), permission);
}
void BackgroundSyncManager::RegisterDidAskForPermission(
int64_t sw_registration_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback,
SyncAndNotificationPermissions permission_statuses) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (permission_statuses.first == PermissionStatus::DENIED) {
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_PERMISSION_DENIED,
std::move(callback));
return;
}
DCHECK_EQ(permission_statuses.first, PermissionStatus::GRANTED);
scoped_refptr<ServiceWorkerRegistration> sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (!sw_registration || !sw_registration->active_version()) {
// The service worker was shut down in the interim.
RecordFailureAndPostError(GetBackgroundSyncType(options),
BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
BackgroundSyncRegistration* existing_registration =
LookupActiveRegistration(blink::mojom::BackgroundSyncRegistrationInfo(
sw_registration_id, options.tag, GetBackgroundSyncType(options)));
const url::Origin& origin = sw_registration->key().origin();
if (GetBackgroundSyncType(options) ==
blink::mojom::BackgroundSyncType::ONE_SHOT) {
bool is_reregistered =
existing_registration && existing_registration->IsFiring();
NotifyOneShotBackgroundSyncRegistered(
service_worker_context_, origin,
/* can_fire= */ AreOptionConditionsMet(), is_reregistered);
} else {
NotifyPeriodicBackgroundSyncRegistered(
service_worker_context_, origin, options.min_interval,
/* is_reregistered= */ static_cast<bool>(existing_registration));
}
if (existing_registration) {
DCHECK_EQ(existing_registration->options()->tag, options.tag);
DCHECK_EQ(existing_registration->sync_type(),
GetBackgroundSyncType(options));
if (existing_registration->options()->Equals(options)) {
BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire =
AreOptionConditionsMet()
? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE
: BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE;
BackgroundSyncMetrics::CountRegisterSuccess(
existing_registration->sync_type(), options.min_interval,
registration_could_fire,
BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE);
if (existing_registration->IsFiring()) {
existing_registration->set_sync_state(
blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING);
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK,
std::make_unique<BackgroundSyncRegistration>(
*existing_registration)));
return;
}
}
BackgroundSyncRegistration registration;
registration.set_origin(origin);
*registration.options() = std::move(options);
// TODO(crbug.com/40627578): This section below is really confusing. Add a
// comment explaining what's going on here, or annotate permission_statuses.
registration.set_max_attempts(
permission_statuses.second == PermissionStatus::GRANTED
? parameters_->max_sync_attempts_with_notification_permission
: parameters_->max_sync_attempts);
// Skip the current registration when getting time till next scheduled
// periodic sync event for the origin. This is because we'll be updating the
// schedule time of this registration soon anyway, so considering its
// schedule time would cause us to calculate incorrect delay.
if (registration.sync_type() == BackgroundSyncType::PERIODIC) {
base::TimeDelta delay = GetNextEventDelay(
service_worker_context_, registration,
std::make_unique<BackgroundSyncParameters>(*parameters_),
GetSmallestPeriodicSyncEventDelayForOrigin(
origin, registration.options()->tag));
RegisterDidGetDelay(sw_registration_id, registration, std::move(callback),
delay);
return;
}
RegisterDidGetDelay(sw_registration_id, registration, std::move(callback),
base::TimeDelta());
}
void BackgroundSyncManager::RegisterDidGetDelay(
int64_t sw_registration_id,
BackgroundSyncRegistration registration,
StatusAndRegistrationCallback callback,
base::TimeDelta delay) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// We don't fire periodic Background Sync registrations immediately after
// registration, so set delay_until to override its default value.
if (registration.sync_type() == BackgroundSyncType::PERIODIC)
registration.set_delay_until(clock_->Now() + delay);
scoped_refptr<ServiceWorkerRegistration> sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (!sw_registration || !sw_registration->active_version()) {
// The service worker was shut down in the interim.
RecordFailureAndPostError(registration.sync_type(),
BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
if (registration.sync_type() == BackgroundSyncType::PERIODIC &&
ShouldLogToDevTools(registration.sync_type())) {
devtools_context_->LogBackgroundServiceEvent(
sw_registration_id,
blink::StorageKey::CreateFirstParty(registration.origin()),
DevToolsBackgroundService::kPeriodicBackgroundSync,
/* event_name= */ "Got next event delay",
/* instance_id= */ registration.options()->tag,
{{"Next Attempt Delay (ms)",
GetDelayAsString(registration.delay_until() - clock_->Now())}});
}
AddOrUpdateActiveRegistration(sw_registration_id,
sw_registration->key().origin(), registration);
StoreRegistrations(
sw_registration_id,
base::BindOnce(&BackgroundSyncManager::RegisterDidStore,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
registration, std::move(callback)));
}
void BackgroundSyncManager::UnregisterPeriodicSyncImpl(
int64_t sw_registration_id,
const std::string& tag,
BackgroundSyncManager::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto registration_info = blink::mojom::BackgroundSyncRegistrationInfo(
sw_registration_id, tag, BackgroundSyncType::PERIODIC);
if (!LookupActiveRegistration(registration_info)) {
// It's okay to not find a matching tag.
UnregisterPeriodicSyncDidStore(std::move(callback),
blink::ServiceWorkerStatusCode::kOk);
return;
}
RemoveActiveRegistration(std::move(registration_info));
StoreRegistrations(
sw_registration_id,
base::BindOnce(&BackgroundSyncManager::UnregisterPeriodicSyncDidStore,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void BackgroundSyncManager::UnregisterPeriodicSyncDidStore(
BackgroundSyncManager::StatusCallback callback,
blink::ServiceWorkerStatusCode status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status != blink::ServiceWorkerStatusCode::kOk) {
BackgroundSyncMetrics::CountUnregisterPeriodicSync(
BACKGROUND_SYNC_STATUS_STORAGE_ERROR);
DisableAndClearManager(base::BindOnce(
std::move(callback), BACKGROUND_SYNC_STATUS_STORAGE_ERROR));
return;
}
BackgroundSyncMetrics::CountUnregisterPeriodicSync(BACKGROUND_SYNC_STATUS_OK);
ScheduleOrCancelDelayedProcessing(BackgroundSyncType::PERIODIC);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK));
}
void BackgroundSyncManager::DisableAndClearManager(base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
disabled_ = true;
active_registrations_.clear();
// Delete all backend entries. The memory representation of registered syncs
// may be out of sync with storage (e.g., due to corruption detection on
// loading from storage), so reload the registrations from storage again.
GetDataFromBackend(
kBackgroundSyncUserDataKey,
base::BindOnce(&BackgroundSyncManager::DisableAndClearDidGetRegistrations,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void BackgroundSyncManager::DisableAndClearDidGetRegistrations(
base::OnceClosure callback,
const std::vector<std::pair<int64_t, std::string>>& user_data,
blink::ServiceWorkerStatusCode status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status != blink::ServiceWorkerStatusCode::kOk || user_data.empty()) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
base::RepeatingClosure barrier_closure =
base::BarrierClosure(user_data.size(), std::move(callback));
for (const auto& sw_id_and_regs : user_data) {
service_worker_context_->ClearRegistrationUserData(
sw_id_and_regs.first, {kBackgroundSyncUserDataKey},
base::BindOnce(&BackgroundSyncManager::DisableAndClearManagerClearedOne,
weak_ptr_factory_.GetWeakPtr(), barrier_closure));
}
}
void BackgroundSyncManager::DisableAndClearManagerClearedOne(
base::OnceClosure barrier_closure,
blink::ServiceWorkerStatusCode status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The status doesn't matter at this point, there is nothing else to be done.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(barrier_closure));
}
BackgroundSyncRegistration* BackgroundSyncManager::LookupActiveRegistration(
const blink::mojom::BackgroundSyncRegistrationInfo& registration_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto it = active_registrations_.find(
registration_info.service_worker_registration_id);
if (it == active_registrations_.end())
return nullptr;
BackgroundSyncRegistrations& registrations = it->second;
DCHECK(!registrations.origin.opaque());
auto key_and_registration_iter = registrations.registration_map.find(
{registration_info.tag, registration_info.sync_type});
if (key_and_registration_iter == registrations.registration_map.end())
return nullptr;
return &key_and_registration_iter->second;
}
void BackgroundSyncManager::StoreRegistrations(
int64_t sw_registration_id,
ServiceWorkerRegistry::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Serialize the data.
const BackgroundSyncRegistrations& registrations =
active_registrations_[sw_registration_id];
BackgroundSyncRegistrationsProto registrations_proto;
registrations_proto.set_origin(registrations.origin.Serialize());
for (const auto& key_and_registration : registrations.registration_map) {
const BackgroundSyncRegistration& registration =
key_and_registration.second;
BackgroundSyncRegistrationProto* registration_proto =
registrations_proto.add_registration();
registration_proto->set_tag(registration.options()->tag);
if (registration.options()->min_interval >= 0) {
registration_proto->mutable_periodic_sync_options()->set_min_interval(
registration.options()->min_interval);
}
registration_proto->set_num_attempts(registration.num_attempts());
registration_proto->set_max_attempts(registration.max_attempts());
registration_proto->set_delay_until(
registration.delay_until().ToInternalValue());
}
std::string serialized;
bool success = registrations_proto.SerializeToString(&serialized);
DCHECK(success);
StoreDataInBackend(sw_registration_id, registrations.origin,
kBackgroundSyncUserDataKey, serialized,
std::move(callback));
}
void BackgroundSyncManager::RegisterDidStore(
int64_t sw_registration_id,
const BackgroundSyncRegistration& registration,
StatusAndRegistrationCallback callback,
blink::ServiceWorkerStatusCode status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
// The service worker registration is gone.
active_registrations_.erase(sw_registration_id);
RecordFailureAndPostError(registration.sync_type(),
BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(callback));
return;
}
if (status != blink::ServiceWorkerStatusCode::kOk) {
BackgroundSyncMetrics::CountRegisterFailure(
registration.sync_type(), BACKGROUND_SYNC_STATUS_STORAGE_ERROR);
DisableAndClearManager(base::BindOnce(
std::move(callback), BACKGROUND_SYNC_STATUS_STORAGE_ERROR, nullptr));
return;
}
// Update controller of this new origin.
if (registration.sync_type() == BackgroundSyncType::PERIODIC)
proxy_->AddToTrackedOrigins(registration.origin());
BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire =
AreOptionConditionsMet()
? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE
: BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE;
BackgroundSyncMetrics::CountRegisterSuccess(
registration.sync_type(), registration.options()->min_interval,
registration_could_fire,
BackgroundSyncMetrics::REGISTRATION_IS_NOT_DUPLICATE);
ScheduleOrCancelDelayedProcessing(BackgroundSyncType::PERIODIC);
// Tell the client that the registration is ready. We won't fire it until the
// client has resolved the registration event.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK,
std::make_unique<BackgroundSyncRegistration>(
registration)));
}
void BackgroundSyncManager::DidResolveRegistrationImpl(
blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BackgroundSyncRegistration* registration =
LookupActiveRegistration(*registration_info);
if (!registration) {
// There might not be a registration if the client ack's a registration that
// was a duplicate in the first place and was already firing and finished by
// the time the client acknowledged the second registration.
op_scheduler_.CompleteOperationAndRunNext();
return;
}
registration->set_resolved();
ResolveRegistrationDidCreateKeepAlive(CreateBackgroundSyncEventKeepAlive(
service_worker_context_, std::move(*registration_info)));
}
void BackgroundSyncManager::ResolveRegistrationDidCreateKeepAlive(
std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
FireReadyEvents(BackgroundSyncType::ONE_SHOT, /* reschedule= */ true,
base::DoNothing(), std::move(keepalive));
op_scheduler_.CompleteOperationAndRunNext();
}
void BackgroundSyncManager::RemoveActiveRegistration(
const blink::mojom::BackgroundSyncRegistrationInfo& registration_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(LookupActiveRegistration(registration_info));
BackgroundSyncRegistrations* registrations =
&active_registrations_[registration_info.service_worker_registration_id];
const url::Origin& origin = registrations->origin;
registrations->registration_map.erase(
{registration_info.tag, registration_info.sync_type});
// Update controller's list of registered origin if necessary.
if (registrations->registration_map.empty())
proxy_->RemoveFromTrackedOrigins(origin);
else {
bool no_more_periodic_sync_registrations = true;
for (auto& key_and_registration : registrations->registration_map) {
if (key_and_registration.second.sync_type() ==
BackgroundSyncType::PERIODIC) {
no_more_periodic_sync_registrations = false;
break;
}
}
if (no_more_periodic_sync_registrations)
proxy_->RemoveFromTrackedOrigins(origin);
}
if (registration_info.sync_type == BackgroundSyncType::PERIODIC &&
ShouldLogToDevTools(registration_info.sync_type)) {
devtools_context_->LogBackgroundServiceEvent(
registration_info.service_worker_registration_id,
blink::StorageKey::CreateFirstParty(origin),
DevToolsBackgroundService::kPeriodicBackgroundSync,
/* event_name= */ "Unregistered periodicsync",
/* instance_id= */ registration_info.tag,
/* event_metadata= */ {});
}
}
void BackgroundSyncManager::AddOrUpdateActiveRegistration(
int64_t sw_registration_id,
const url::Origin& origin,
const BackgroundSyncRegistration& sync_registration) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BackgroundSyncRegistrations* registrations =
&active_registrations_[sw_registration_id];
registrations->origin = origin;
BackgroundSyncType sync_type = sync_registration.sync_type();
registrations
->registration_map[{sync_registration.options()->tag, sync_type}] =
sync_registration;
if (ShouldLogToDevTools(sync_registration.sync_type())) {
std::map<std::string, std::string> event_metadata;
if (sync_registration.sync_type() == BackgroundSyncType::PERIODIC) {
event_metadata["minInterval"] =
base::NumberToString(sync_registration.options()->min_interval);
}
devtools_context_->LogBackgroundServiceEvent(
sw_registration_id, blink::StorageKey::CreateFirstParty(origin),
GetDevToolsBackgroundService(sync_type),
/* event_name= */ "Registered " + GetSyncEventName(sync_type),
/* instance_id= */ sync_registration.options()->tag, event_metadata);
}
}
void BackgroundSyncManager::StoreDataInBackend(
int64_t sw_registration_id,
const url::Origin& origin,
const std::string& backend_key,
const std::string& data,
ServiceWorkerRegistry::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
service_worker_context_->StoreRegistrationUserData(
sw_registration_id, blink::StorageKey::CreateFirstParty(origin),
{{backend_key, data}}, std::move(callback));
}
void BackgroundSyncManager::GetDataFromBackend(
const std::string& backend_key,
ServiceWorkerRegistry::GetUserDataForAllRegistrationsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
service_worker_context_->GetUserDataForAllRegistrations(backend_key,
std::move(callback));
}
void BackgroundSyncManager::DispatchSyncEvent(
const std::string& tag,
scoped_refptr<ServiceWorkerVersion> active_version,
bool last_chance,
ServiceWorkerVersion::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(active_version);
if (active_version->running_status() !=
blink::EmbeddedWorkerStatus::kRunning) {
active_version->RunAfterStartWorker(
ServiceWorkerMetrics::EventType::SYNC,
base::BindOnce(&DidStartWorkerForSyncEvent,
base::BindOnce(&BackgroundSyncManager::DispatchSyncEvent,
weak_ptr_factory_.GetWeakPtr(), tag,
active_version, last_chance),
std::move(callback)));
return;
}
auto split_callback = base::SplitOnceCallback(std::move(callback));
int request_id = active_version->StartRequestWithCustomTimeout(
ServiceWorkerMetrics::EventType::SYNC, std::move(split_callback.first),
parameters_->max_sync_event_duration,
ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
active_version->endpoint()->DispatchSyncEvent(
tag, last_chance, parameters_->max_sync_event_duration,
base::BindOnce(&OnSyncEventFinished, active_version, request_id,
std::move(split_callback.second)));
if (devtools_context_->IsRecording(
DevToolsBackgroundService::kBackgroundSync)) {
devtools_context_->LogBackgroundServiceEvent(
active_version->registration_id(), active_version->key(),
DevToolsBackgroundService::kBackgroundSync,
/* event_name= */ "Dispatched sync event",
/* instance_id= */ tag,
/* event_metadata= */
{{"Last Chance", last_chance ? "Yes" : "No"}});
}
}
void BackgroundSyncManager::DispatchPeriodicSyncEvent(
const std::string& tag,
scoped_refptr<ServiceWorkerVersion> active_version,
ServiceWorkerVersion::StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(active_version);
if (active_version->running_status() !=
blink::EmbeddedWorkerStatus::kRunning) {
active_version->RunAfterStartWorker(
ServiceWorkerMetrics::EventType::PERIODIC_SYNC,
base::BindOnce(
&DidStartWorkerForSyncEvent,
base::BindOnce(&BackgroundSyncManager::DispatchPeriodicSyncEvent,
weak_ptr_factory_.GetWeakPtr(), tag, active_version),
std::move(callback)));
return;
}
auto split_callback = base::SplitOnceCallback(std::move(callback));
int request_id = active_version->StartRequestWithCustomTimeout(
ServiceWorkerMetrics::EventType::PERIODIC_SYNC,
std::move(split_callback.first), parameters_->max_sync_event_duration,
ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
active_version->endpoint()->DispatchPeriodicSyncEvent(
tag, parameters_->max_sync_event_duration,
base::BindOnce(&OnSyncEventFinished, active_version, request_id,
std::move(split_callback.second)));
if (devtools_context_->IsRecording(
DevToolsBackgroundService::kPeriodicBackgroundSync)) {
devtools_context_->LogBackgroundServiceEvent(
active_version->registration_id(), active_version->key(),
DevToolsBackgroundService::kPeriodicBackgroundSync,
/* event_name= */ "Dispatched periodicsync event",
/* instance_id= */ tag,
/* event_metadata= */ {});
}
}
void BackgroundSyncManager::HasMainFrameWindowClient(
const blink::StorageKey& key,
BoolCallback callback) {
service_worker_context_->HasMainFrameWindowClient(key, std::move(callback));
}
void BackgroundSyncManager::GetRegistrationsImpl(
BackgroundSyncType sync_type,
int64_t sw_registration_id,
StatusAndRegistrationsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<std::unique_ptr<BackgroundSyncRegistration>> out_registrations;
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(out_registrations)));
return;
}
auto it = active_registrations_.find(sw_registration_id);
if (it != active_registrations_.end()) {
const BackgroundSyncRegistrations& registrations = it->second;
for (const auto& key_and_registration : registrations.registration_map) {
const BackgroundSyncRegistration& registration =
key_and_registration.second;
if (registration.sync_type() != sync_type)
continue;
out_registrations.push_back(
std::make_unique<BackgroundSyncRegistration>(registration));
}
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK,
std::move(out_registrations)));
}
bool BackgroundSyncManager::AreOptionConditionsMet() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return network_observer_->NetworkSufficient();
}
bool BackgroundSyncManager::AllConditionsExceptConnectivitySatisfied(
const BackgroundSyncRegistration& registration,
int64_t service_worker_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Don't fire the registration if the client hasn't yet resolved its
// registration promise.
if (!registration.resolved() &&
registration.sync_type() == BackgroundSyncType::ONE_SHOT) {
return false;
}
if (registration.sync_state() != blink::mojom::BackgroundSyncState::PENDING)
return false;
if (registration.is_suspended())
return false;
if (base::Contains(emulated_offline_sw_, service_worker_id))
return false;
return true;
}
bool BackgroundSyncManager::CanFireAnyRegistrationUponConnectivity(
BackgroundSyncType sync_type) {
for (const auto& sw_reg_id_and_registrations : active_registrations_) {
int64_t service_worker_registration_id = sw_reg_id_and_registrations.first;
for (const auto& key_and_registration :
sw_reg_id_and_registrations.second.registration_map) {
const BackgroundSyncRegistration& registration =
key_and_registration.second;
if (sync_type != registration.sync_type())
continue;
if (AllConditionsExceptConnectivitySatisfied(
registration, service_worker_registration_id)) {
return true;
}
}
}
return false;
}
bool& BackgroundSyncManager::delayed_processing_scheduled(
BackgroundSyncType sync_type) {
if (sync_type == BackgroundSyncType::ONE_SHOT)
return delayed_processing_scheduled_one_shot_sync_;
else
return delayed_processing_scheduled_periodic_sync_;
}
void BackgroundSyncManager::ScheduleOrCancelDelayedProcessing(
BackgroundSyncType sync_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool can_fire_with_connectivity =
CanFireAnyRegistrationUponConnectivity(sync_type);
if (delayed_processing_scheduled(sync_type) && !can_fire_with_connectivity &&
!GetNumFiringRegistrations(sync_type)) {
CancelDelayedProcessingOfRegistrations(sync_type);
delayed_processing_scheduled(sync_type) = false;
} else if (can_fire_with_connectivity ||
GetNumFiringRegistrations(sync_type)) {
ScheduleDelayedProcessingOfRegistrations(sync_type);
delayed_processing_scheduled(sync_type) = true;
}
}
bool BackgroundSyncManager::IsRegistrationReadyToFire(
const BackgroundSyncRegistration& registration,
int64_t service_worker_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (clock_->Now() < registration.delay_until())
return false;
return AllConditionsExceptConnectivitySatisfied(registration,
service_worker_id) &&
AreOptionConditionsMet();
}
int BackgroundSyncManager::GetNumFiringRegistrations(
BackgroundSyncType sync_type) {
if (sync_type == BackgroundSyncType::ONE_SHOT)
return num_firing_registrations_one_shot_;
return num_firing_registrations_periodic_;
}
void BackgroundSyncManager::UpdateNumFiringRegistrationsBy(
BackgroundSyncType sync_type,
int to_add) {
if (sync_type == BackgroundSyncType::ONE_SHOT)
num_firing_registrations_one_shot_ += to_add;
else
num_firing_registrations_periodic_ += to_add;
}
bool BackgroundSyncManager::AllRegistrationsWaitingToBeResolved() const {
for (const auto& active_registration : active_registrations_) {
for (const auto& key_and_registration :
active_registration.second.registration_map) {
const BackgroundSyncRegistration& registration =
key_and_registration.second;
if (registration.resolved())
return false;
}
}
return true;
}
base::TimeDelta BackgroundSyncManager::GetSoonestWakeupDelta(
BackgroundSyncType sync_type,
base::Time last_browser_wakeup_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::TimeDelta soonest_wakeup_delta = base::TimeDelta::Max();
bool need_retries = false;
for (const auto& sw_reg_id_and_registrations : active_registrations_) {
for (const auto& key_and_registration :
sw_reg_id_and_registrations.second.registration_map) {
const BackgroundSyncRegistration& registration =
key_and_registration.second;
if (registration.sync_type() != sync_type)
continue;
if (registration.num_attempts() > 0 &&
registration.num_attempts() < registration.max_attempts()) {
need_retries = true;
}
if (registration.sync_state() ==
blink::mojom::BackgroundSyncState::PENDING) {
if (clock_->Now() >= registration.delay_until()) {
soonest_wakeup_delta = base::TimeDelta();
break;
} else {
base::TimeDelta delay_delta =
registration.delay_until() - clock_->Now();
soonest_wakeup_delta = std::min(delay_delta, soonest_wakeup_delta);
}
}
}
}
// If the browser is closed while firing events, the browser needs a task to
// wake it back up and try again.
if (GetNumFiringRegistrations(sync_type) > 0 &&
soonest_wakeup_delta > parameters_->min_sync_recovery_time) {
soonest_wakeup_delta = parameters_->min_sync_recovery_time;
}
// If we're still waiting for registrations to be resolved, don't schedule
// a wake up task eagerly.
if (sync_type == BackgroundSyncType::ONE_SHOT &&
AllRegistrationsWaitingToBeResolved() &&
soonest_wakeup_delta < parameters_->min_sync_recovery_time) {
soonest_wakeup_delta = parameters_->min_sync_recovery_time;
}
// The browser may impose a hard limit on how often it can be woken up to
// process periodic Background Sync registrations. This excludes retries.
if (sync_type == BackgroundSyncType::PERIODIC && !need_retries) {
soonest_wakeup_delta = MaybeApplyBrowserWakeupCountLimit(
soonest_wakeup_delta, last_browser_wakeup_time);
}
return soonest_wakeup_delta;
}
base::TimeDelta BackgroundSyncManager::MaybeApplyBrowserWakeupCountLimit(
base::TimeDelta soonest_wakeup_delta,
base::Time last_browser_wakeup_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (last_browser_wakeup_time.is_null())
return soonest_wakeup_delta;
base::TimeDelta time_since_last_browser_wakeup =
clock_->Now() - last_browser_wakeup_time;
if (time_since_last_browser_wakeup >=
parameters_->min_periodic_sync_events_interval) {
return soonest_wakeup_delta;
}
base::TimeDelta time_till_next_allowed_browser_wakeup =
parameters_->min_periodic_sync_events_interval -
time_since_last_browser_wakeup;
return std::max(soonest_wakeup_delta, time_till_next_allowed_browser_wakeup);
}
base::TimeDelta
BackgroundSyncManager::GetSmallestPeriodicSyncEventDelayForOrigin(
const url::Origin& origin,
const std::string& tag_to_skip) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Time soonest_wakeup_time = base::Time();
for (const auto& active_registration : active_registrations_) {
if (active_registration.second.origin != origin)
continue;
const auto& tag_and_registrations =
active_registration.second.registration_map;
for (const auto& tag_and_registration : tag_and_registrations) {
if (/* tag= */ tag_and_registration.first.first == tag_to_skip)
continue;
if (/* sync_type= */ tag_and_registration.first.second !=
BackgroundSyncType::PERIODIC) {
continue;
}
if (tag_and_registration.second.delay_until().is_null())
continue;
if (soonest_wakeup_time.is_null() ||
tag_and_registration.second.delay_until() < soonest_wakeup_time) {
soonest_wakeup_time = tag_and_registration.second.delay_until();
}
}
}
if (soonest_wakeup_time.is_null())
return base::TimeDelta::Max();
if (soonest_wakeup_time < clock_->Now())
return base::TimeDelta();
return soonest_wakeup_time - clock_->Now();
}
void BackgroundSyncManager::RevivePeriodicSyncRegistrations(
url::Origin origin) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_)
return;
op_scheduler_.ScheduleOperation(base::BindOnce(
&BackgroundSyncManager::ReviveOriginImpl, weak_ptr_factory_.GetWeakPtr(),
std::move(origin), MakeEmptyCompletion()));
}
void BackgroundSyncManager::ReviveOriginImpl(url::Origin origin,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
// Create a list of registrations to revive.
std::vector<const BackgroundSyncRegistration*> to_revive;
std::map<const BackgroundSyncRegistration*, int64_t>
service_worker_registration_ids;
for (const auto& active_registration : active_registrations_) {
int64_t service_worker_registration_id = active_registration.first;
if (active_registration.second.origin != origin)
continue;
for (const auto& key_and_registration :
active_registration.second.registration_map) {
const BackgroundSyncRegistration* registration =
&key_and_registration.second;
if (!registration->is_suspended())
continue;
to_revive.push_back(registration);
service_worker_registration_ids[registration] =
service_worker_registration_id;
}
}
if (to_revive.empty()) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
base::RepeatingClosure received_new_delays_closure = base::BarrierClosure(
to_revive.size(),
base::BindOnce(
&BackgroundSyncManager::DidReceiveDelaysForSuspendedRegistrations,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
for (const auto* registration : to_revive) {
base::TimeDelta delay = GetNextEventDelay(
service_worker_context_, *registration,
std::make_unique<BackgroundSyncParameters>(*parameters_),
GetSmallestPeriodicSyncEventDelayForOrigin(
origin, registration->options()->tag));
ReviveDidGetNextEventDelay(service_worker_registration_ids[registration],
*registration, received_new_delays_closure,
delay);
}
}
void BackgroundSyncManager::ReviveDidGetNextEventDelay(
int64_t service_worker_registration_id,
BackgroundSyncRegistration registration,
base::OnceClosure done_closure,
base::TimeDelta delay) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (delay.is_max()) {
std::move(done_closure).Run();
return;
}
BackgroundSyncRegistration* active_registration =
LookupActiveRegistration(blink::mojom::BackgroundSyncRegistrationInfo(
service_worker_registration_id, registration.options()->tag,
registration.sync_type()));
if (!active_registration) {
std::move(done_closure).Run();
return;
}
active_registration->set_delay_until(clock_->Now() + delay);
StoreRegistrations(
service_worker_registration_id,
base::BindOnce(&BackgroundSyncManager::ReviveDidStoreRegistration,
weak_ptr_factory_.GetWeakPtr(),
service_worker_registration_id, std::move(done_closure)));
}
void BackgroundSyncManager::ReviveDidStoreRegistration(
int64_t service_worker_registration_id,
base::OnceClosure done_closure,
blink::ServiceWorkerStatusCode status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
// The service worker registration is gone.
active_registrations_.erase(service_worker_registration_id);
std::move(done_closure).Run();
return;
}
if (status != blink::ServiceWorkerStatusCode::kOk) {
DisableAndClearManager(std::move(done_closure));
return;
}
std::move(done_closure).Run();
}
void BackgroundSyncManager::DidReceiveDelaysForSuspendedRegistrations(
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ScheduleOrCancelDelayedProcessing(BackgroundSyncType::PERIODIC);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::ScheduleDelayedProcessingOfRegistrations(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto fire_events_callback = base::BindOnce(
&BackgroundSyncManager::FireReadyEvents, weak_ptr_factory_.GetWeakPtr(),
sync_type, /* reschedule= */ true, base::DoNothing(),
/* keepalive= */ nullptr);
proxy_->ScheduleDelayedProcessing(
sync_type,
GetSoonestWakeupDelta(sync_type,
/* last_browser_wakeup_time= */ base::Time()),
std::move(fire_events_callback));
}
void BackgroundSyncManager::CancelDelayedProcessingOfRegistrations(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
proxy_->CancelDelayedProcessing(sync_type);
}
void BackgroundSyncManager::FireReadyEvents(
blink::mojom::BackgroundSyncType sync_type,
bool reschedule,
base::OnceClosure callback,
std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!reschedule) {
// This invocation has come from scheduled processing of registrations.
// Since this delayed processing is one-off, update internal state.
delayed_processing_scheduled(sync_type) = false;
}
op_scheduler_.ScheduleOperation(
base::BindOnce(&BackgroundSyncManager::FireReadyEventsImpl,
weak_ptr_factory_.GetWeakPtr(), sync_type, reschedule,
std::move(callback), std::move(keepalive)));
}
void BackgroundSyncManager::FireReadyEventsImpl(
blink::mojom::BackgroundSyncType sync_type,
bool reschedule,
base::OnceClosure callback,
std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, op_scheduler_.WrapCallbackToRunNext(std::move(callback)));
return;
}
// Find the registrations that are ready to run.
std::vector<blink::mojom::BackgroundSyncRegistrationInfoPtr> to_fire;
for (auto& sw_reg_id_and_registrations : active_registrations_) {
const int64_t service_worker_registration_id =
sw_reg_id_and_registrations.first;
for (auto& key_and_registration :
sw_reg_id_and_registrations.second.registration_map) {
BackgroundSyncRegistration* registration = &key_and_registration.second;
if (sync_type != registration->sync_type())
continue;
if (IsRegistrationReadyToFire(*registration,
service_worker_registration_id)) {
to_fire.emplace_back(blink::mojom::BackgroundSyncRegistrationInfo::New(
service_worker_registration_id,
/* tag= */ key_and_registration.first.first,
/* sync_type= */ key_and_registration.first.second));
// The state change is not saved to persistent storage because
// if the sync event is killed mid-sync then it should return to
// SYNC_STATE_PENDING.
registration->set_sync_state(blink::mojom::BackgroundSyncState::FIRING);
}
}
}
if (!reschedule) {
// This method has been called from a Chrome wakeup task.
BackgroundSyncMetrics::RecordEventsFiredFromWakeupTask(
sync_type, /* events_fired= */ !to_fire.empty());
}
if (to_fire.empty()) {
// TODO(crbug.com/40641360): Reschedule wakeup after a non-zero delay if
// called from a wakeup task.
if (reschedule)
ScheduleOrCancelDelayedProcessing(sync_type);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, op_scheduler_.WrapCallbackToRunNext(std::move(callback)));
return;
}
base::TimeTicks start_time = base::TimeTicks::Now();
// If we've been called from a wake up task, potentially keep the browser
// awake till all events have completed. If not, we only do so until all
// events have been fired.
// To allow the |op_scheduler_| to process other tasks after sync events
// have been fired, mark this task complete after firing events.
base::OnceClosure events_fired_callback, events_completed_callback;
bool keep_browser_awake_till_events_complete =
!reschedule && parameters_->keep_browser_awake_till_events_complete;
if (keep_browser_awake_till_events_complete) {
events_fired_callback = MakeEmptyCompletion();
events_completed_callback = std::move(callback);
} else {
events_fired_callback =
op_scheduler_.WrapCallbackToRunNext(std::move(callback));
events_completed_callback = base::DoNothing();
}
// Fire the sync event of the ready registrations and run
// |events_fired_closure| once they're all done.
base::RepeatingClosure events_fired_barrier_closure = base::BarrierClosure(
to_fire.size(),
base::BindOnce(&BackgroundSyncManager::FireReadyEventsAllEventsFiring,
weak_ptr_factory_.GetWeakPtr(), sync_type, reschedule,
std::move(events_fired_callback)));
// Record the total time taken after all events have run to completion.
base::RepeatingClosure events_completed_barrier_closure =
base::BarrierClosure(
to_fire.size(),
base::BindOnce(&BackgroundSyncManager::OnAllSyncEventsCompleted,
sync_type, start_time, !reschedule, to_fire.size(),
std::move(events_completed_callback)));
for (auto& registration_info : to_fire) {
const BackgroundSyncRegistration* registration =
LookupActiveRegistration(*registration_info);
DCHECK(registration);
int64_t service_worker_registration_id =
registration_info->service_worker_registration_id;
// If BackgroundSync becomes usable from a 3p context then
// BackgroundSyncRegistrations should be changed to use StorageKey.
service_worker_context_->FindReadyRegistrationForId(
service_worker_registration_id,
blink::StorageKey::CreateFirstParty(
active_registrations_[service_worker_registration_id].origin),
base::BindOnce(
&BackgroundSyncManager::FireReadyEventsDidFindRegistration,
weak_ptr_factory_.GetWeakPtr(), std::move(registration_info),
std::move(keepalive), events_fired_barrier_closure,
events_completed_barrier_closure));
}
}
void BackgroundSyncManager::FireReadyEventsDidFindRegistration(
blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive,
base::OnceClosure event_fired_callback,
base::OnceClosure event_completed_callback,
blink::ServiceWorkerStatusCode service_worker_status,
scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BackgroundSyncRegistration* registration =
LookupActiveRegistration(*registration_info);
if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
if (registration)
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(event_fired_callback));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(event_completed_callback));
return;
}
DCHECK_EQ(registration_info->service_worker_registration_id,
service_worker_registration->id());
DCHECK(registration);
// The connectivity was lost before dispatching the sync event, so there is
// no point in going through with it.
if (!AreOptionConditionsMet()) {
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(event_fired_callback));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(event_completed_callback));
return;
}
auto sync_type = registration_info->sync_type;
UpdateNumFiringRegistrationsBy(sync_type, 1);
const bool last_chance =
registration->num_attempts() == registration->max_attempts() - 1;
HasMainFrameWindowClient(
service_worker_registration->key(),
base::BindOnce(&BackgroundSyncMetrics::RecordEventStarted, sync_type));
if (sync_type == BackgroundSyncType::ONE_SHOT) {
DispatchSyncEvent(
registration->options()->tag,
service_worker_registration->active_version(), last_chance,
base::BindOnce(&BackgroundSyncManager::EventComplete,
weak_ptr_factory_.GetWeakPtr(),
service_worker_registration,
std::move(registration_info), std::move(keepalive),
std::move(event_completed_callback)));
} else {
DispatchPeriodicSyncEvent(
registration->options()->tag,
service_worker_registration->active_version(),
base::BindOnce(&BackgroundSyncManager::EventComplete,
weak_ptr_factory_.GetWeakPtr(),
service_worker_registration,
std::move(registration_info), std::move(keepalive),
std::move(event_completed_callback)));
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(event_fired_callback));
}
void BackgroundSyncManager::FireReadyEventsAllEventsFiring(
BackgroundSyncType sync_type,
bool reschedule,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (reschedule)
ScheduleOrCancelDelayedProcessing(sync_type);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
// |service_worker_registration| is just to keep the registration alive
// while the event is firing.
void BackgroundSyncManager::EventComplete(
scoped_refptr<ServiceWorkerRegistration> service_worker_registration,
blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive,
base::OnceClosure callback,
blink::ServiceWorkerStatusCode status_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
// The event ran to completion, we should count it, no matter what happens
// from here.
const blink::StorageKey& key = service_worker_registration->key();
HasMainFrameWindowClient(
key, base::BindOnce(&BackgroundSyncMetrics::RecordEventResult,
registration_info->sync_type,
status_code == blink::ServiceWorkerStatusCode::kOk));
op_scheduler_.ScheduleOperation(base::BindOnce(
&BackgroundSyncManager::EventCompleteImpl, weak_ptr_factory_.GetWeakPtr(),
std::move(registration_info), std::move(keepalive), status_code,
key.origin(), op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::EventCompleteImpl(
blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive,
blink::ServiceWorkerStatusCode status_code,
const url::Origin& origin,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disabled_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
BackgroundSyncRegistration* registration =
LookupActiveRegistration(*registration_info);
if (!registration) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
DCHECK_NE(blink::mojom::BackgroundSyncState::PENDING,
registration->sync_state());
// It's important to update |num_attempts| before we update |delay_until|.
bool succeeded = status_code == blink::ServiceWorkerStatusCode::kOk;
registration->set_num_attempts(GetNumAttemptsAfterEvent(
registration->sync_type(), registration->num_attempts(),
registration->max_attempts(), registration->sync_state(), succeeded));
// If |delay_until| needs to be updated, get updated delay.
if (registration->sync_type() == BackgroundSyncType::PERIODIC ||
(!succeeded &&
registration->num_attempts() < registration->max_attempts())) {
base::TimeDelta delay = GetNextEventDelay(
service_worker_context_, *registration,
std::make_unique<BackgroundSyncParameters>(*parameters_),
GetSmallestPeriodicSyncEventDelayForOrigin(
origin, registration->options()->tag));
EventCompleteDidGetDelay(std::move(registration_info), status_code, origin,
std::move(callback), delay);
return;
}
EventCompleteDidGetDelay(std::move(registration_info), status_code, origin,
std::move(callback), base::TimeDelta::Max());
}
void BackgroundSyncManager::EventCompleteDidGetDelay(
blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
blink::ServiceWorkerStatusCode status_code,
const url::Origin& origin,
base::OnceClosure callback,
base::TimeDelta delay) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UpdateNumFiringRegistrationsBy(registration_info->sync_type, -1);
const blink::StorageKey storage_key =
blink::StorageKey::CreateFirstParty(origin);
BackgroundSyncRegistration* registration =
LookupActiveRegistration(*registration_info);
if (!registration) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
bool succeeded = status_code == blink::ServiceWorkerStatusCode::kOk;
bool can_retry = registration->num_attempts() < registration->max_attempts();
bool registration_completed = true;
if (registration->sync_state() ==
blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING) {
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
registration->set_num_attempts(0);
registration_completed = false;
if (ShouldLogToDevTools(registration->sync_type())) {
devtools_context_->LogBackgroundServiceEvent(
registration_info->service_worker_registration_id, storage_key,
GetDevToolsBackgroundService(registration->sync_type()),
/* event_name= */ "Sync event reregistered",
/* instance_id= */ registration_info->tag,
/* event_metadata= */ {});
}
} else if ((!succeeded && can_retry) ||
registration->sync_type() == BackgroundSyncType::PERIODIC) {
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
registration_completed = false;
registration->set_delay_until(clock_->Now() + delay);
std::string event_name = GetSyncEventName(registration->sync_type()) +
(succeeded ? " event completed" : " event failed");
base::TimeDelta display_delay =
registration->sync_type() == BackgroundSyncType::ONE_SHOT
? delay
: registration->delay_until() - clock_->Now();
std::map<std::string, std::string> event_metadata = {
{"Next Attempt Delay (ms)", GetDelayAsString(display_delay)}};
if (!succeeded) {
event_metadata.emplace("Failure Reason",
GetEventStatusString(status_code));
}
if (ShouldLogToDevTools(registration->sync_type())) {
devtools_context_->LogBackgroundServiceEvent(
registration_info->service_worker_registration_id, storage_key,
GetDevToolsBackgroundService(registration->sync_type()), event_name,
/* instance_id= */ registration_info->tag, event_metadata);
}
}
if (registration_completed) {
BackgroundSyncMetrics::RecordRegistrationComplete(
succeeded, registration->num_attempts());
if (ShouldLogToDevTools(registration->sync_type())) {
devtools_context_->LogBackgroundServiceEvent(
registration_info->service_worker_registration_id, storage_key,
GetDevToolsBackgroundService(registration->sync_type()),
/* event_name= */ "Sync completed",
/* instance_id= */ registration_info->tag,
{{"Status", GetEventStatusString(status_code)}});
}
if (registration_info->sync_type ==
blink::mojom::BackgroundSyncType::ONE_SHOT) {
NotifyOneShotBackgroundSyncCompleted(
service_worker_context_, origin, status_code,
registration->num_attempts(), registration->max_attempts());
} else {
NotifyPeriodicBackgroundSyncCompleted(
service_worker_context_, origin, status_code,
registration->num_attempts(), registration->max_attempts());
}
RemoveActiveRegistration(*registration_info);
}
StoreRegistrations(
registration_info->service_worker_registration_id,
base::BindOnce(&BackgroundSyncManager::EventCompleteDidStore,
weak_ptr_factory_.GetWeakPtr(),
registration_info->sync_type,
registration_info->service_worker_registration_id,
std::move(callback)));
}
void BackgroundSyncManager::EventCompleteDidStore(
blink::mojom::BackgroundSyncType sync_type,
int64_t service_worker_id,
base::OnceClosure callback,
blink::ServiceWorkerStatusCode status_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status_code == blink::ServiceWorkerStatusCode::kErrorNotFound) {
// The registration is gone.
active_registrations_.erase(service_worker_id);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
if (status_code != blink::ServiceWorkerStatusCode::kOk) {
DisableAndClearManager(std::move(callback));
return;
}
// Fire any ready events and call RunInBackground if anything is waiting.
FireReadyEvents(sync_type, /* reschedule= */ true, base::DoNothing());
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
// static
void BackgroundSyncManager::OnAllSyncEventsCompleted(
BackgroundSyncType sync_type,
const base::TimeTicks& start_time,
bool from_wakeup_task,
int number_of_batched_sync_events,
base::OnceClosure callback) {
// Record the combined time taken by all sync events.
BackgroundSyncMetrics::RecordBatchSyncEventComplete(
sync_type, base::TimeTicks::Now() - start_time, from_wakeup_task,
number_of_batched_sync_events);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::OnRegistrationDeletedImpl(
int64_t sw_registration_id,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The backend (ServiceWorkerStorage) will delete the data, so just delete the
// memory representation here.
active_registrations_.erase(sw_registration_id);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::OnStorageWipedImpl(base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
active_registrations_.clear();
disabled_ = false;
InitImpl(std::move(callback));
}
void BackgroundSyncManager::OnNetworkChanged() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if BUILDFLAG(IS_ANDROID)
if (parameters_->rely_on_android_network_detection)
return;
#endif
if (!AreOptionConditionsMet())
return;
FireReadyEvents(BackgroundSyncType::ONE_SHOT, /* reschedule= */ true,
base::DoNothing());
FireReadyEvents(BackgroundSyncType::PERIODIC, /* reschedule= */ true,
base::DoNothing());
}
base::OnceClosure BackgroundSyncManager::MakeEmptyCompletion() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return op_scheduler_.WrapCallbackToRunNext(base::BindOnce([] {}));
}
blink::ServiceWorkerStatusCode BackgroundSyncManager::CanEmulateSyncEvent(
scoped_refptr<ServiceWorkerVersion> active_version) {
if (!active_version)
return blink::ServiceWorkerStatusCode::kErrorAbort;
if (!network_observer_->NetworkSufficient())
return blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected;
int64_t registration_id = active_version->registration_id();
if (base::Contains(emulated_offline_sw_, registration_id))
return blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected;
return blink::ServiceWorkerStatusCode::kOk;
}
bool BackgroundSyncManager::ShouldLogToDevTools(BackgroundSyncType sync_type) {
return devtools_context_->IsRecording(
GetDevToolsBackgroundService(sync_type));
}
} // namespace content