blob: ff36afe2036776881ea2ffd9c0e9ef6f7df2a1af [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// 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 <utility>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.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/embedded_worker_status.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/service_worker/service_worker_utils.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_type.h"
#include "third_party/blink/public/common/service_worker/service_worker_type_converters.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 defined(OS_ANDROID)
#include "content/browser/android/background_sync_network_observer_android.h"
#endif
namespace content {
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(
BackgroundSyncStatus status,
BackgroundSyncManager::StatusAndRegistrationCallback callback) {
BackgroundSyncMetrics::CountRegisterFailure(status);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), status, nullptr));
}
// Returns nullptr if the browser context cannot be accessed for any reason.
BrowserContext* GetBrowserContextOnUIThread(
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* GetBackgroundSyncControllerOnUIThread(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserContext* browser_context =
GetBrowserContextOnUIThread(std::move(service_worker_context));
if (!browser_context)
return nullptr;
return browser_context->GetBackgroundSyncController();
}
blink::mojom::PermissionStatus GetBackgroundSyncPermissionOnUIThread(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
const url::Origin& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserContext* browser_context =
GetBrowserContextOnUIThread(std::move(service_worker_context));
if (!browser_context)
return blink::mojom::PermissionStatus::DENIED;
PermissionController* permission_controller =
BrowserContext::GetPermissionController(browser_context);
DCHECK(permission_controller);
// The requesting origin always matches the embedding origin.
GURL origin_url = origin.GetURL();
return permission_controller->GetPermissionStatus(
PermissionType::BACKGROUND_SYNC, origin_url, origin_url);
}
void NotifyBackgroundSyncRegisteredOnUIThread(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
const url::Origin& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncControllerOnUIThread(std::move(sw_context_wrapper));
if (!background_sync_controller)
return;
background_sync_controller->NotifyBackgroundSyncRegistered(origin);
}
void RunInBackgroundOnUIThread(
scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper,
bool enabled,
int64_t min_ms) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BackgroundSyncController* background_sync_controller =
GetBackgroundSyncControllerOnUIThread(sw_context_wrapper);
if (background_sync_controller) {
background_sync_controller->RunInBackground(enabled, min_ms);
}
}
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 =
GetBackgroundSyncControllerOnUIThread(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;
}
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));
}
blink::mojom::BackgroundSyncType GetBackgroundSyncType(
const blink::mojom::SyncRegistrationOptions& options) {
return options.min_interval >= 0 ? blink::mojom::BackgroundSyncType::PERIODIC
: blink::mojom::BackgroundSyncType::ONE_SHOT;
}
} // 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,
scoped_refptr<DevToolsBackgroundServicesContext> devtools_context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BackgroundSyncManager* sync_manager = new BackgroundSyncManager(
std::move(service_worker_context), std::move(devtools_context));
sync_manager->Init();
return base::WrapUnique(sync_manager);
}
BackgroundSyncManager::~BackgroundSyncManager() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_->RemoveObserver(this);
}
void BackgroundSyncManager::Register(
int64_t sw_registration_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(callback));
return;
}
if (options.min_interval < 0 &&
options.min_interval != kMinIntervalForOneShotSync) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED,
std::move(callback));
return;
}
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::RegisterCheckIfHasMainFrame,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
std::move(options),
op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::DidResolveRegistration(
int64_t sw_registration_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_)
return;
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::DidResolveRegistrationImpl,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id, tag,
sync_type));
}
void BackgroundSyncManager::GetRegistrations(
int64_t sw_registration_id,
StatusAndRegistrationsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback), BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::vector<std::unique_ptr<BackgroundSyncRegistration>>()));
return;
}
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::GetRegistrationsImpl,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::OnRegistrationDeleted(int64_t sw_registration_id,
const GURL& pattern) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// 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(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::OnRegistrationDeletedImpl,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
MakeEmptyCompletion()));
}
void BackgroundSyncManager::OnStorageWiped() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// 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(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::OnStorageWipedImpl,
weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion()));
}
void BackgroundSyncManager::SetMaxSyncAttemptsForTesting(int max_attempts) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::SetMaxSyncAttemptsImpl,
weak_ptr_factory_.GetWeakPtr(), max_attempts,
MakeEmptyCompletion()));
}
void BackgroundSyncManager::EmulateDispatchSyncEvent(
const std::string& tag,
scoped_refptr<ServiceWorkerVersion> active_version,
bool last_chance,
ServiceWorkerVersion::StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
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::EmulateServiceWorkerOffline(
int64_t service_worker_id,
bool is_offline) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// 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();
}
BackgroundSyncManager::BackgroundSyncManager(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
scoped_refptr<DevToolsBackgroundServicesContext> devtools_context)
: op_scheduler_(CacheStorageSchedulerClient::kBackgroundSync),
service_worker_context_(std::move(service_worker_context)),
devtools_context_(std::move(devtools_context)),
parameters_(std::make_unique<BackgroundSyncParameters>()),
disabled_(false),
num_firing_registrations_(0),
clock_(base::DefaultClock::GetInstance()),
weak_ptr_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(devtools_context_);
service_worker_context_->AddObserver(this);
#if defined(OS_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_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!op_scheduler_.ScheduledOperations());
DCHECK(!disabled_);
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::InitImpl,
weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion()));
}
void BackgroundSyncManager::InitImpl(base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&GetControllerParameters, service_worker_context_,
std::make_unique<BackgroundSyncParameters>(*parameters_)),
base::BindOnce(&BackgroundSyncManager::InitDidGetControllerParameters,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void BackgroundSyncManager::InitDidGetControllerParameters(
base::OnceClosure callback,
std::unique_ptr<BackgroundSyncParameters> updated_parameters) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
parameters_ = std::move(updated_parameters);
if (parameters_->disable) {
disabled_ = true;
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
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_CURRENTLY_ON(BrowserThread::IO);
if (status != blink::ServiceWorkerStatusCode::kOk &&
status != blink::ServiceWorkerStatusCode::kErrorNotFound) {
DisableAndClearManager(std::move(callback));
return;
}
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()) {
blink::mojom::BackgroundSyncType sync_type =
registration_proto.has_periodic_sync_options()
? blink::mojom::BackgroundSyncType::PERIODIC
: blink::mojom::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 == blink::mojom::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_resolved();
}
}
}
FireReadyEvents();
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::RegisterCheckIfHasMainFrame(
int64_t sw_registration_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerRegistration* sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (!sw_registration || !sw_registration->active_version()) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
HasMainFrameProviderHost(
url::Origin::Create(sw_registration->scope().GetOrigin()),
base::BindOnce(&BackgroundSyncManager::RegisterDidCheckIfMainFrame,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
std::move(options), std::move(callback)));
}
void BackgroundSyncManager::RegisterDidCheckIfMainFrame(
int64_t sw_registration_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback,
bool has_main_frame_client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!has_main_frame_client) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED,
std::move(callback));
return;
}
RegisterImpl(sw_registration_id, std::move(options), std::move(callback));
}
void BackgroundSyncManager::RegisterImpl(
int64_t sw_registration_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(callback));
return;
}
if (options.tag.length() > kMaxTagLength) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED,
std::move(callback));
return;
}
ServiceWorkerRegistration* sw_registration =
service_worker_context_->GetLiveRegistration(sw_registration_id);
if (!sw_registration || !sw_registration->active_version()) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&GetBackgroundSyncPermissionOnUIThread,
service_worker_context_,
url::Origin::Create(sw_registration->scope().GetOrigin())),
base::BindOnce(&BackgroundSyncManager::RegisterDidAskForPermission,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
std::move(options), std::move(callback)));
}
void BackgroundSyncManager::RegisterDidAskForPermission(
int64_t sw_registration_id,
blink::mojom::SyncRegistrationOptions options,
StatusAndRegistrationCallback callback,
blink::mojom::PermissionStatus permission_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (permission_status == blink::mojom::PermissionStatus::DENIED) {
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_PERMISSION_DENIED,
std::move(callback));
return;
}
DCHECK(permission_status == blink::mojom::PermissionStatus::GRANTED);
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(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER,
std::move(callback));
return;
}
// TODO(crbug.com/925297): Record Periodic Sync metrics.
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(
&NotifyBackgroundSyncRegisteredOnUIThread, service_worker_context_,
url::Origin::Create(sw_registration->scope().GetOrigin())));
BackgroundSyncRegistration* existing_registration = LookupActiveRegistration(
sw_registration_id, options.tag, GetBackgroundSyncType(options));
if (existing_registration) {
DCHECK(existing_registration->options()->Equals(options));
BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire =
AreOptionConditionsMet()
? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE
: BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE;
BackgroundSyncMetrics::CountRegisterSuccess(
registration_could_fire,
BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE);
if (existing_registration->IsFiring()) {
existing_registration->set_sync_state(
blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK,
std::make_unique<BackgroundSyncRegistration>(
*existing_registration)));
return;
}
BackgroundSyncRegistration new_registration;
*new_registration.options() = std::move(options);
AddActiveRegistration(
sw_registration_id,
url::Origin::Create(sw_registration->scope().GetOrigin()),
new_registration);
StoreRegistrations(
sw_registration_id,
base::BindOnce(&BackgroundSyncManager::RegisterDidStore,
weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
new_registration, std::move(callback)));
}
void BackgroundSyncManager::DisableAndClearManager(base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->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_CURRENTLY_ON(BrowserThread::IO);
if (status != blink::ServiceWorkerStatusCode::kOk || user_data.empty()) {
base::ThreadTaskRunnerHandle::Get()->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_CURRENTLY_ON(BrowserThread::IO);
// The status doesn't matter at this point, there is nothing else to be done.
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(barrier_closure));
}
BackgroundSyncRegistration* BackgroundSyncManager::LookupActiveRegistration(
int64_t sw_registration_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
auto it = active_registrations_.find(sw_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({tag, 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,
ServiceWorkerStorage::StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// 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_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& new_registration,
StatusAndRegistrationCallback callback,
blink::ServiceWorkerStatusCode status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
// The service worker registration is gone.
active_registrations_.erase(sw_registration_id);
RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR,
std::move(callback));
return;
}
if (status != blink::ServiceWorkerStatusCode::kOk) {
BackgroundSyncMetrics::CountRegisterFailure(
BACKGROUND_SYNC_STATUS_STORAGE_ERROR);
DisableAndClearManager(base::BindOnce(
std::move(callback), BACKGROUND_SYNC_STATUS_STORAGE_ERROR, nullptr));
return;
}
BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire =
AreOptionConditionsMet()
? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE
: BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE;
BackgroundSyncMetrics::CountRegisterSuccess(
registration_could_fire,
BackgroundSyncMetrics::REGISTRATION_IS_NOT_DUPLICATE);
// Tell the client that the registration is ready. We won't fire it until the
// client has resolved the registration event.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK,
std::make_unique<BackgroundSyncRegistration>(
new_registration)));
}
void BackgroundSyncManager::DidResolveRegistrationImpl(
int64_t sw_registration_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BackgroundSyncRegistration* registration =
LookupActiveRegistration(sw_registration_id, tag, sync_type);
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();
FireReadyEvents();
op_scheduler_.CompleteOperationAndRunNext();
}
void BackgroundSyncManager::RemoveActiveRegistration(
int64_t sw_registration_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(LookupActiveRegistration(sw_registration_id, tag, sync_type));
BackgroundSyncRegistrations* registrations =
&active_registrations_[sw_registration_id];
registrations->registration_map.erase({tag, sync_type});
}
void BackgroundSyncManager::AddActiveRegistration(
int64_t sw_registration_id,
const url::Origin& origin,
const BackgroundSyncRegistration& sync_registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BackgroundSyncRegistrations* registrations =
&active_registrations_[sw_registration_id];
registrations->origin = origin;
blink::mojom::BackgroundSyncType sync_type =
GetBackgroundSyncType(*sync_registration.options());
registrations
->registration_map[{sync_registration.options()->tag, sync_type}] =
sync_registration;
if (devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC) &&
sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) {
devtools_context_->LogBackgroundServiceEvent(
sw_registration_id, origin, devtools::proto::BACKGROUND_SYNC,
/* event_name= */ "Registered Sync",
/* 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,
ServiceWorkerStorage::StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_->StoreRegistrationUserData(
sw_registration_id, origin.GetURL(), {{backend_key, data}},
std::move(callback));
}
void BackgroundSyncManager::GetDataFromBackend(
const std::string& backend_key,
ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
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_CURRENTLY_ON(BrowserThread::IO);
DCHECK(active_version);
if (active_version->running_status() != EmbeddedWorkerStatus::RUNNING) {
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 repeating_callback =
base::AdaptCallbackForRepeating(std::move(callback));
int request_id = active_version->StartRequestWithCustomTimeout(
ServiceWorkerMetrics::EventType::SYNC, repeating_callback,
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(repeating_callback)));
if (devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC)) {
devtools_context_->LogBackgroundServiceEvent(
active_version->registration_id(), active_version->script_origin(),
devtools::proto::BACKGROUND_SYNC,
/* event_name= */ "Dispatched Sync Event",
/* instance_id= */ tag,
/* event_metadata= */
{{"Last Chance", last_chance ? "Yes" : "No"}});
}
}
void BackgroundSyncManager::ScheduleDelayedTask(base::OnceClosure callback,
base::TimeDelta delay) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, std::move(callback), delay);
}
void BackgroundSyncManager::HasMainFrameProviderHost(const url::Origin& origin,
BoolCallback callback) {
service_worker_context_->HasMainFrameProviderHost(origin.GetURL(),
std::move(callback));
}
void BackgroundSyncManager::GetRegistrationsImpl(
int64_t sw_registration_id,
StatusAndRegistrationsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::vector<std::unique_ptr<BackgroundSyncRegistration>> out_registrations;
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->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;
out_registrations.push_back(
std::make_unique<BackgroundSyncRegistration>(registration));
}
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK,
std::move(out_registrations)));
}
bool BackgroundSyncManager::AreOptionConditionsMet() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return network_observer_->NetworkSufficient();
}
bool BackgroundSyncManager::IsRegistrationReadyToFire(
const BackgroundSyncRegistration& registration,
int64_t service_worker_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Don't fire the registration if the client hasn't yet resolved its
// registration promise.
if (!registration.resolved())
return false;
if (registration.sync_state() != blink::mojom::BackgroundSyncState::PENDING)
return false;
if (clock_->Now() < registration.delay_until())
return false;
if (base::ContainsKey(emulated_offline_sw_, service_worker_id))
return false;
return AreOptionConditionsMet();
}
void BackgroundSyncManager::RunInBackgroundIfNecessary() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::TimeDelta soonest_wakeup_delta = base::TimeDelta::Max();
for (const auto& sw_id_and_registrations : active_registrations_) {
for (const auto& key_and_registration :
sw_id_and_registrations.second.registration_map) {
const BackgroundSyncRegistration& registration =
key_and_registration.second;
if (registration.sync_state() ==
blink::mojom::BackgroundSyncState::PENDING) {
if (clock_->Now() >= registration.delay_until()) {
soonest_wakeup_delta = base::TimeDelta();
} else {
base::TimeDelta delay_delta =
registration.delay_until() - clock_->Now();
if (delay_delta < soonest_wakeup_delta)
soonest_wakeup_delta = delay_delta;
}
}
}
}
// If the browser is closed while firing events, the browser needs a task to
// wake it back up and try again.
if (num_firing_registrations_ > 0 &&
soonest_wakeup_delta > parameters_->min_sync_recovery_time) {
soonest_wakeup_delta = parameters_->min_sync_recovery_time;
}
// Try firing again after the wakeup delta.
if (!soonest_wakeup_delta.is_max() && !soonest_wakeup_delta.is_zero()) {
delayed_sync_task_.Reset(base::Bind(&BackgroundSyncManager::FireReadyEvents,
weak_ptr_factory_.GetWeakPtr()));
ScheduleDelayedTask(delayed_sync_task_.callback(), soonest_wakeup_delta);
}
// In case the browser closes (or to prevent it from closing), call
// RunInBackground to either wake up the browser at the wakeup delta or to
// keep the browser running.
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(
RunInBackgroundOnUIThread, service_worker_context_,
!soonest_wakeup_delta.is_max() /* should run in background */,
soonest_wakeup_delta.InMilliseconds()));
}
void BackgroundSyncManager::FireReadyEvents() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FireReadyEventsThenRunCallback(MakeEmptyCompletion());
}
void BackgroundSyncManager::FireReadyEventsThenRunCallback(
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_)
return;
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::FireReadyEventsImpl,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void BackgroundSyncManager::FireReadyEventsImpl(base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
// Find the registrations that are ready to run.
struct RegistrationsToFire {
RegistrationsToFire(int64_t service_worker_registration_id_,
std::string tag_,
blink::mojom::BackgroundSyncType sync_type_)
: service_worker_registration_id(service_worker_registration_id_),
tag(tag_),
sync_type(sync_type_) {}
int64_t service_worker_registration_id;
std::string tag;
blink::mojom::BackgroundSyncType sync_type;
};
std::vector<RegistrationsToFire> to_fire;
for (auto& sw_id_and_registrations : active_registrations_) {
const int64_t service_worker_registration_id =
sw_id_and_registrations.first;
for (auto& key_and_registration :
sw_id_and_registrations.second.registration_map) {
BackgroundSyncRegistration* registration = &key_and_registration.second;
if (IsRegistrationReadyToFire(*registration,
service_worker_registration_id)) {
to_fire.emplace_back(
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 (to_fire.empty()) {
RunInBackgroundIfNecessary();
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
base::TimeTicks start_time = base::TimeTicks::Now();
// Fire the sync event of the ready registrations and run |callback| once
// they're all done.
base::RepeatingClosure events_fired_barrier_closure = base::BarrierClosure(
to_fire.size(),
base::BindOnce(&BackgroundSyncManager::FireReadyEventsAllEventsFiring,
weak_ptr_factory_.GetWeakPtr(), std::move(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(&OnAllSyncEventsCompleted, start_time,
to_fire.size()));
for (auto& registration_info : to_fire) {
const BackgroundSyncRegistration* registration = LookupActiveRegistration(
registration_info.service_worker_registration_id, registration_info.tag,
registration_info.sync_type);
DCHECK(registration);
service_worker_context_->FindReadyRegistrationForId(
registration_info.service_worker_registration_id,
active_registrations_[registration_info.service_worker_registration_id]
.origin.GetURL(),
base::BindOnce(
&BackgroundSyncManager::FireReadyEventsDidFindRegistration,
weak_ptr_factory_.GetWeakPtr(),
registration_info.service_worker_registration_id,
std::move(registration_info.tag), registration_info.sync_type,
events_fired_barrier_closure, events_completed_barrier_closure));
}
}
void BackgroundSyncManager::FireReadyEventsDidFindRegistration(
int64_t service_worker_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type,
base::OnceClosure event_fired_callback,
base::OnceClosure event_completed_callback,
blink::ServiceWorkerStatusCode service_worker_status,
scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BackgroundSyncRegistration* registration =
LookupActiveRegistration(service_worker_id, tag, sync_type);
if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
if (registration)
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_fired_callback));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_completed_callback));
return;
}
DCHECK_EQ(service_worker_id, service_worker_registration->id());
DCHECK(registration);
// Don't dispatch a sync event if the sync is periodic.
// TODO(crbug.com/925297): Remove this code when we've added the logic to
// dispatch periodic sync events.
if (registration && sync_type == blink::mojom::BackgroundSyncType::PERIODIC) {
RemoveActiveRegistration(service_worker_id, tag, sync_type);
StoreRegistrations(service_worker_id, base::DoNothing());
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_fired_callback));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_completed_callback));
return;
}
const bool option_conditions_met = AreOptionConditionsMet();
UMA_HISTOGRAM_BOOLEAN("BackgroundSync.OptionConditionsChanged",
!option_conditions_met);
// The connectivity was lost before dispatching the sync event, so there is
// no point in going through with it.
if (!option_conditions_met) {
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_fired_callback));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_completed_callback));
return;
}
num_firing_registrations_ += 1;
const bool last_chance =
registration->num_attempts() == parameters_->max_sync_attempts - 1;
HasMainFrameProviderHost(
url::Origin::Create(service_worker_registration->scope().GetOrigin()),
base::BindOnce(&BackgroundSyncMetrics::RecordEventStarted));
DispatchSyncEvent(
registration->options()->tag,
service_worker_registration->active_version(), last_chance,
base::BindOnce(
&BackgroundSyncManager::EventComplete, weak_ptr_factory_.GetWeakPtr(),
service_worker_registration, service_worker_registration->id(), tag,
sync_type, std::move(event_completed_callback)));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(event_fired_callback));
}
void BackgroundSyncManager::FireReadyEventsAllEventsFiring(
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
RunInBackgroundIfNecessary();
base::ThreadTaskRunnerHandle::Get()->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,
int64_t service_worker_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type,
base::OnceClosure callback,
blink::ServiceWorkerStatusCode status_code) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
op_scheduler_.ScheduleOperation(
CacheStorageSchedulerOp::kBackgroundSync,
base::BindOnce(&BackgroundSyncManager::EventCompleteImpl,
weak_ptr_factory_.GetWeakPtr(), service_worker_id, tag,
sync_type, status_code,
op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
}
void BackgroundSyncManager::EventCompleteImpl(
int64_t service_worker_id,
const std::string& tag,
blink::mojom::BackgroundSyncType sync_type,
blink::ServiceWorkerStatusCode status_code,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (disabled_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
num_firing_registrations_ -= 1;
BackgroundSyncRegistration* registration =
LookupActiveRegistration(service_worker_id, tag, sync_type);
if (!registration) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
DCHECK_NE(blink::mojom::BackgroundSyncState::PENDING,
registration->sync_state());
registration->set_num_attempts(registration->num_attempts() + 1);
// The event ran to completion, we should count it, no matter what happens
// from here.
ServiceWorkerRegistration* sw_registration =
service_worker_context_->GetLiveRegistration(service_worker_id);
url::Origin origin =
url::Origin::Create(sw_registration->scope().GetOrigin());
if (sw_registration) {
HasMainFrameProviderHost(
origin,
base::BindOnce(&BackgroundSyncMetrics::RecordEventResult,
status_code == blink::ServiceWorkerStatusCode::kOk));
}
bool registration_completed = true;
bool can_retry =
registration->num_attempts() < parameters_->max_sync_attempts;
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;
} else if (status_code != blink::ServiceWorkerStatusCode::kOk &&
can_retry) { // Sync failed but can retry
registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
base::TimeDelta delay =
parameters_->initial_retry_delay *
pow(parameters_->retry_delay_factor, registration->num_attempts() - 1);
registration->set_delay_until(clock_->Now() + delay);
registration_completed = false;
if (devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC) &&
sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) {
devtools_context_->LogBackgroundServiceEvent(
sw_registration->id(), origin, devtools::proto::BACKGROUND_SYNC,
/* event_name= */ "Sync Event Failed",
/* instance_id= */ tag,
/* event_metadata= */
{{"Next Attempt Delay (ms)",
base::NumberToString(delay.InMilliseconds())}});
}
}
if (registration_completed) {
RemoveActiveRegistration(service_worker_id, tag, sync_type);
if (devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC) &&
sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) {
bool succeded = status_code == blink::ServiceWorkerStatusCode::kOk;
devtools_context_->LogBackgroundServiceEvent(
sw_registration->id(), origin, devtools::proto::BACKGROUND_SYNC,
/* event_name= */ "Sync Complete",
/* instance_id= */ tag,
/* event_metadata= */
{{"Succeeded", succeded ? "Yes" : "No"}});
}
}
StoreRegistrations(
service_worker_id,
base::BindOnce(&BackgroundSyncManager::EventCompleteDidStore,
weak_ptr_factory_.GetWeakPtr(), service_worker_id,
std::move(callback)));
}
void BackgroundSyncManager::EventCompleteDidStore(
int64_t service_worker_id,
base::OnceClosure callback,
blink::ServiceWorkerStatusCode status_code) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (status_code == blink::ServiceWorkerStatusCode::kErrorNotFound) {
// The registration is gone.
active_registrations_.erase(service_worker_id);
base::ThreadTaskRunnerHandle::Get()->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();
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
}
// static
void BackgroundSyncManager::OnAllSyncEventsCompleted(
const base::TimeTicks& start_time,
int number_of_batched_sync_events) {
// Record the combined time taken by all sync events.
BackgroundSyncMetrics::RecordBatchSyncEventComplete(
base::TimeTicks::Now() - start_time, number_of_batched_sync_events);
}
void BackgroundSyncManager::OnRegistrationDeletedImpl(
int64_t sw_registration_id,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// The backend (ServiceWorkerStorage) will delete the data, so just delete the
// memory representation here.
active_registrations_.erase(sw_registration_id);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
}
void BackgroundSyncManager::OnStorageWipedImpl(base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
active_registrations_.clear();
disabled_ = false;
InitImpl(std::move(callback));
}
void BackgroundSyncManager::OnNetworkChanged() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FireReadyEvents();
}
void BackgroundSyncManager::SetMaxSyncAttemptsImpl(int max_attempts,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
parameters_->max_sync_attempts = max_attempts;
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
}
base::OnceClosure BackgroundSyncManager::MakeEmptyCompletion() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return op_scheduler_.WrapCallbackToRunNext(base::DoNothing::Once());
}
blink::ServiceWorkerStatusCode BackgroundSyncManager::CanEmulateSyncEvent(
scoped_refptr<ServiceWorkerVersion> active_version) {
if (!active_version)
return blink::ServiceWorkerStatusCode::kErrorFailed;
if (!network_observer_->NetworkSufficient())
return blink::ServiceWorkerStatusCode::kErrorNetwork;
int64_t registration_id = active_version->registration_id();
if (base::ContainsKey(emulated_offline_sw_, registration_id))
return blink::ServiceWorkerStatusCode::kErrorNetwork;
return blink::ServiceWorkerStatusCode::kOk;
}
} // namespace content