| // 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 <algorithm> |
| #include <utility> |
| |
| #include "base/barrier_closure.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/location.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" |
| #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/932591): 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::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(); |
| } |
| |
| SyncAndNotificationPermissions GetBackgroundSyncPermissionOnUIThread( |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context, |
| const url::Origin& origin, |
| BackgroundSyncType sync_type) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| BrowserContext* browser_context = |
| GetBrowserContextOnUIThread(std::move(service_worker_context)); |
| if (!browser_context) |
| return {PermissionStatus::DENIED, 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(); |
| auto sync_permission = permission_controller->GetPermissionStatus( |
| sync_type == BackgroundSyncType::ONE_SHOT |
| ? PermissionType::BACKGROUND_SYNC |
| : PermissionType::PERIODIC_BACKGROUND_SYNC, |
| origin_url, origin_url); |
| auto notification_permission = permission_controller->GetPermissionStatus( |
| PermissionType::NOTIFICATIONS, origin_url, origin_url); |
| return {sync_permission, notification_permission}; |
| } |
| |
| void NotifyOneShotBackgroundSyncRegisteredOnUIThread( |
| 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 = |
| GetBackgroundSyncControllerOnUIThread(std::move(sw_context_wrapper)); |
| |
| if (!background_sync_controller) |
| return; |
| |
| background_sync_controller->NotifyOneShotBackgroundSyncRegistered( |
| origin, can_fire, is_reregistered); |
| } |
| |
| void NotifyPeriodicBackgroundSyncRegisteredOnUIThread( |
| 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 = |
| GetBackgroundSyncControllerOnUIThread(std::move(sw_context_wrapper)); |
| |
| if (!background_sync_controller) |
| return; |
| |
| background_sync_controller->NotifyPeriodicBackgroundSyncRegistered( |
| origin, min_interval, is_reregistered); |
| } |
| |
| void NotifyOneShotBackgroundSyncCompletedOnUIThread( |
| 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 = |
| GetBackgroundSyncControllerOnUIThread(std::move(sw_context_wrapper)); |
| |
| if (!background_sync_controller) |
| return; |
| |
| background_sync_controller->NotifyOneShotBackgroundSyncCompleted( |
| origin, status_code, num_attempts, max_attempts); |
| } |
| |
| void NotifyPeriodicBackgroundSyncCompletedOnUIThread( |
| 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 = |
| GetBackgroundSyncControllerOnUIThread(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 = |
| 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; |
| } |
| |
| 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 = |
| GetBackgroundSyncControllerOnUIThread(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::kErrorAbort: |
| return "aborted"; |
| case blink::ServiceWorkerStatusCode::kErrorTimeout: |
| return "timeout"; |
| default: |
| NOTREACHED(); |
| 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> |
| CreateBackgroundSyncEventKeepAliveOnUIThread( |
| scoped_refptr<ServiceWorkerContextWrapper> sw_context_wrapper, |
| const blink::mojom::BackgroundSyncRegistrationInfo& registration_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| BackgroundSyncController* controller = |
| GetBackgroundSyncControllerOnUIThread(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, |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| service_worker_context_->RemoveObserver(this); |
| } |
| |
| void BackgroundSyncManager::Register( |
| int64_t sw_registration_id, |
| blink::mojom::SyncRegistrationOptions options, |
| StatusAndRegistrationCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| RecordFailureAndPostError(GetBackgroundSyncType(options), |
| BACKGROUND_SYNC_STATUS_STORAGE_ERROR, |
| std::move(callback)); |
| return; |
| } |
| |
| DCHECK(options.min_interval >= 0 || |
| options.min_interval == kMinIntervalForOneShotSync); |
| |
| if (GetBackgroundSyncType(options) == BackgroundSyncType::ONE_SHOT) { |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce( |
| &BackgroundSyncManager::RegisterCheckIfHasMainFrame, |
| weak_ptr_factory_.GetWeakPtr(), sw_registration_id, |
| std::move(options), |
| op_scheduler_.WrapCallbackToRunNext(id, std::move(callback)))); |
| } else { |
| // Periodic Background Sync events already have a pre-defined cadence which |
| // the user agent decides. Don't block registration if there's no top level |
| // frame at the time of registration. |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce( |
| &BackgroundSyncManager::RegisterImpl, |
| weak_ptr_factory_.GetWeakPtr(), sw_registration_id, |
| std::move(options), |
| op_scheduler_.WrapCallbackToRunNext(id, std::move(callback)))); |
| } |
| } |
| |
| void BackgroundSyncManager::UnregisterPeriodicSync( |
| int64_t sw_registration_id, |
| const std::string& tag, |
| BackgroundSyncManager::StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), |
| BACKGROUND_SYNC_STATUS_STORAGE_ERROR)); |
| return; |
| } |
| |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce( |
| &BackgroundSyncManager::UnregisterPeriodicSyncImpl, |
| weak_ptr_factory_.GetWeakPtr(), sw_registration_id, tag, |
| op_scheduler_.WrapCallbackToRunNext(id, std::move(callback)))); |
| } |
| |
| void BackgroundSyncManager::DidResolveRegistration( |
| blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) |
| return; |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce(&BackgroundSyncManager::DidResolveRegistrationImpl, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(registration_info), id)); |
| } |
| |
| 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::GetRegistrations( |
| BackgroundSyncType sync_type, |
| int64_t sw_registration_id, |
| StatusAndRegistrationsCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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; |
| } |
| |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce( |
| &BackgroundSyncManager::GetRegistrationsImpl, |
| weak_ptr_factory_.GetWeakPtr(), sync_type, sw_registration_id, |
| op_scheduler_.WrapCallbackToRunNext(id, std::move(callback)))); |
| } |
| |
| void BackgroundSyncManager::OnRegistrationDeleted(int64_t sw_registration_id, |
| const GURL& pattern) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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. |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce(&BackgroundSyncManager::OnRegistrationDeletedImpl, |
| weak_ptr_factory_.GetWeakPtr(), sw_registration_id, |
| MakeEmptyCompletion(id))); |
| } |
| |
| void BackgroundSyncManager::OnStorageWiped() { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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. |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce(&BackgroundSyncManager::OnStorageWipedImpl, |
| weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion(id))); |
| } |
| |
| void BackgroundSyncManager::EmulateDispatchSyncEvent( |
| const std::string& tag, |
| scoped_refptr<ServiceWorkerVersion> active_version, |
| bool last_chance, |
| ServiceWorkerVersion::StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| // 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::Once()); |
| } |
| |
| BackgroundSyncManager::BackgroundSyncManager( |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context, |
| scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context) |
| : op_scheduler_(CacheStorageSchedulerClient::kBackgroundSync, |
| base::ThreadTaskRunnerHandle::Get()), |
| service_worker_context_(std::move(service_worker_context)), |
| proxy_(std::make_unique<BackgroundSyncProxy>(service_worker_context_)), |
| devtools_context_(std::move(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(ServiceWorkerContext::GetCoreThreadId()); |
| DCHECK(devtools_context_); |
| DCHECK(service_worker_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(ServiceWorkerContext::GetCoreThreadId()); |
| DCHECK(!op_scheduler_.ScheduledOperations()); |
| DCHECK(!disabled_); |
| |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce(&BackgroundSyncManager::InitImpl, |
| weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion(id))); |
| } |
| |
| void BackgroundSyncManager::InitImpl(base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| std::move(callback)); |
| return; |
| } |
| |
| // TODO(crbug.com/824858): Remove the else branch after the feature is |
| // enabled. Also, try to make a RunOrPostTaskOnThreadAndReplyWithResult() |
| // function so the if/else isn't needed. |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| InitDidGetControllerParameters( |
| std::move(callback), |
| GetControllerParameters( |
| service_worker_context_, |
| std::make_unique<BackgroundSyncParameters>(*parameters_))); |
| } else { |
| base::PostTaskAndReplyWithResult( |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (status != blink::ServiceWorkerStatusCode::kOk && |
| status != blink::ServiceWorkerStatusCode::kErrorNotFound) { |
| DisableAndClearManager(std::move(callback)); |
| return; |
| } |
| |
| std::set<url::Origin> suspended_periodic_sync_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 = |
| ®istrations |
| ->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); |
| 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::Once()); |
| FireReadyEvents(BackgroundSyncType::PERIODIC, /* reschedule= */ true, |
| base::DoNothing::Once()); |
| proxy_->SendSuspendedPeriodicSyncOrigins( |
| std::move(suspended_periodic_sync_origins)); |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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; |
| } |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (!has_main_frame_client) { |
| RecordFailureAndPostError(GetBackgroundSyncType(options), |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| BackgroundSyncType sync_type = GetBackgroundSyncType(options); |
| |
| // TODO(crbug.com/824858): Remove the else branch after the feature is |
| // enabled. Also, try to make a RunOrPostTaskOnThreadAndReplyWithResult() |
| // function so the if/else isn't needed. |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| SyncAndNotificationPermissions permission = |
| GetBackgroundSyncPermissionOnUIThread( |
| service_worker_context_, |
| url::Origin::Create(sw_registration->scope().GetOrigin()), |
| sync_type); |
| RegisterDidAskForPermission(sw_registration_id, std::move(options), |
| std::move(callback), permission); |
| } else { |
| base::PostTaskAndReplyWithResult( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce( |
| &GetBackgroundSyncPermissionOnUIThread, service_worker_context_, |
| url::Origin::Create(sw_registration->scope().GetOrigin()), |
| sync_type), |
| 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, |
| SyncAndNotificationPermissions permission_statuses) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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); |
| |
| 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))); |
| |
| url::Origin origin = |
| url::Origin::Create(sw_registration->scope().GetOrigin()); |
| |
| if (GetBackgroundSyncType(options) == |
| blink::mojom::BackgroundSyncType::ONE_SHOT) { |
| bool is_reregistered = |
| existing_registration && existing_registration->IsFiring(); |
| RunOrPostTaskOnThread( |
| FROM_HERE, BrowserThread::UI, |
| base::BindOnce(&NotifyOneShotBackgroundSyncRegisteredOnUIThread, |
| service_worker_context_, origin, |
| /* can_fire= */ AreOptionConditionsMet(), |
| is_reregistered)); |
| } else { |
| RunOrPostTaskOnThread( |
| FROM_HERE, BrowserThread::UI, |
| base::BindOnce( |
| &NotifyPeriodicBackgroundSyncRegisteredOnUIThread, |
| 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::ThreadTaskRunnerHandle::Get()->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/963487): 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) { |
| // TODO(crbug.com/824858): Remove the else branch after the feature is |
| // enabled. Also, try to make a RunOrPostTaskOnThreadAndReplyWithResult() |
| // function so the if/else isn't needed. |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| 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); |
| } else { |
| base::PostTaskAndReplyWithResult( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce( |
| &GetNextEventDelay, service_worker_context_, registration, |
| std::make_unique<BackgroundSyncParameters>(*parameters_), |
| GetSmallestPeriodicSyncEventDelayForOrigin( |
| origin, registration.options()->tag)), |
| base::BindOnce(&BackgroundSyncManager::RegisterDidGetDelay, |
| weak_ptr_factory_.GetWeakPtr(), sw_registration_id, |
| registration, std::move(callback))); |
| } |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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); |
| |
| 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_->LogBackgroundServiceEventOnCoreThread( |
| sw_registration_id, 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, |
| url::Origin::Create(sw_registration->scope().GetOrigin()), 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(callback), BACKGROUND_SYNC_STATUS_OK)); |
| } |
| |
| void BackgroundSyncManager::DisableAndClearManager(base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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( |
| const blink::mojom::BackgroundSyncRegistrationInfo& registration_info) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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, |
| ServiceWorkerStorage::StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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; |
| } |
| |
| 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::ThreadTaskRunnerHandle::Get()->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, |
| CacheStorageSchedulerId id) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(id); |
| return; |
| } |
| |
| registration->set_resolved(); |
| |
| // TODO(crbug.com/824858): Remove the else branch after the feature is |
| // enabled. Also, try to make a RunOrPostTaskOnThreadAndReplyWithResult() |
| // function so the if/else isn't needed. |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| ResolveRegistrationDidCreateKeepAlive( |
| id, CreateBackgroundSyncEventKeepAliveOnUIThread( |
| service_worker_context_, std::move(*registration_info))); |
| } else { |
| base::PostTaskAndReplyWithResult( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&CreateBackgroundSyncEventKeepAliveOnUIThread, |
| service_worker_context_, std::move(*registration_info)), |
| base::BindOnce( |
| &BackgroundSyncManager::ResolveRegistrationDidCreateKeepAlive, |
| weak_ptr_factory_.GetWeakPtr(), id)); |
| } |
| } |
| |
| void BackgroundSyncManager::ResolveRegistrationDidCreateKeepAlive( |
| CacheStorageSchedulerId id, |
| std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| FireReadyEvents(BackgroundSyncType::ONE_SHOT, /* reschedule= */ true, |
| base::DoNothing::Once(), std::move(keepalive)); |
| op_scheduler_.CompleteOperationAndRunNext(id); |
| } |
| |
| void BackgroundSyncManager::RemoveActiveRegistration( |
| const blink::mojom::BackgroundSyncRegistrationInfo& registration_info) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| 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}); |
| |
| if (registration_info.sync_type == BackgroundSyncType::PERIODIC && |
| ShouldLogToDevTools(registration_info.sync_type)) { |
| devtools_context_->LogBackgroundServiceEventOnCoreThread( |
| registration_info.service_worker_registration_id, 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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_->LogBackgroundServiceEventOnCoreThread( |
| sw_registration_id, 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, |
| ServiceWorkerStorage::StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| 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( |
| DevToolsBackgroundService::kBackgroundSync)) { |
| devtools_context_->LogBackgroundServiceEventOnCoreThread( |
| active_version->registration_id(), active_version->script_origin(), |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| DCHECK(active_version); |
| |
| if (active_version->running_status() != EmbeddedWorkerStatus::RUNNING) { |
| 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 repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| |
| int request_id = active_version->StartRequestWithCustomTimeout( |
| ServiceWorkerMetrics::EventType::PERIODIC_SYNC, repeating_callback, |
| 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(repeating_callback))); |
| |
| if (devtools_context_->IsRecording( |
| DevToolsBackgroundService::kPeriodicBackgroundSync)) { |
| devtools_context_->LogBackgroundServiceEventOnCoreThread( |
| active_version->registration_id(), active_version->script_origin(), |
| DevToolsBackgroundService::kPeriodicBackgroundSync, |
| /* event_name= */ "Dispatched periodicsync event", |
| /* instance_id= */ tag, |
| /* event_metadata= */ {}); |
| } |
| } |
| |
| void BackgroundSyncManager::HasMainFrameProviderHost(const url::Origin& origin, |
| BoolCallback callback) { |
| service_worker_context_->HasMainFrameProviderHost(origin.GetURL(), |
| std::move(callback)); |
| } |
| |
| void BackgroundSyncManager::GetRegistrationsImpl( |
| BackgroundSyncType sync_type, |
| int64_t sw_registration_id, |
| StatusAndRegistrationsCallback callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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; |
| if (registration.sync_type() != sync_type) |
| continue; |
| 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(ServiceWorkerContext::GetCoreThreadId()); |
| return network_observer_->NetworkSufficient(); |
| } |
| |
| bool BackgroundSyncManager::AllConditionsExceptConnectivitySatisfied( |
| const BackgroundSyncRegistration& registration, |
| int64_t service_worker_id) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| bool can_fire_with_connectivity = |
| CanFireAnyRegistrationUponConnectivity(sync_type); |
| |
| if (delayed_processing_scheduled(sync_type) && !can_fire_with_connectivity && |
| !GetNumFiringRegistrations(sync_type)) { |
| // TODO(crbug.com/996166): Split out a path to cancel delayed processing. |
| ScheduleDelayedProcessingOfRegistrations(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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) |
| return; |
| |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce(&BackgroundSyncManager::ReviveOriginImpl, |
| weak_ptr_factory_.GetWeakPtr(), std::move(origin), |
| MakeEmptyCompletion(id))); |
| } |
| |
| void BackgroundSyncManager::ReviveOriginImpl(url::Origin origin, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| base::ThreadTaskRunnerHandle::Get()->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::ThreadTaskRunnerHandle::Get()->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) { |
| // TODO(crbug.com/824858): Remove the else branch after the feature is |
| // enabled. Also, try to make a RunOrPostTaskOnThreadAndReplyWithResult() |
| // function so the if/else isn't needed. |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| 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); |
| } else { |
| base::PostTaskAndReplyWithResult( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce( |
| &GetNextEventDelay, service_worker_context_, *registration, |
| std::make_unique<BackgroundSyncParameters>(*parameters_), |
| GetSmallestPeriodicSyncEventDelayForOrigin( |
| origin, registration->options()->tag)), |
| base::BindOnce(&BackgroundSyncManager::ReviveDidGetNextEventDelay, |
| weak_ptr_factory_.GetWeakPtr(), |
| service_worker_registration_ids[registration], |
| *registration, received_new_delays_closure)); |
| } |
| } |
| } |
| |
| void BackgroundSyncManager::ReviveDidGetNextEventDelay( |
| int64_t service_worker_registration_id, |
| BackgroundSyncRegistration registration, |
| base::OnceClosure done_closure, |
| base::TimeDelta delay) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) { |
| // The service worker registration is gone. |
| active_registrations_.erase(service_worker_registration_id); |
| std::move(done_closure).Run(); |
| } |
| |
| if (status != blink::ServiceWorkerStatusCode::kOk) { |
| DisableAndClearManager(std::move(done_closure)); |
| return; |
| } |
| |
| std::move(done_closure).Run(); |
| } |
| |
| void BackgroundSyncManager::DidReceiveDelaysForSuspendedRegistrations( |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| ScheduleOrCancelDelayedProcessing(BackgroundSyncType::PERIODIC); |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback)); |
| } |
| |
| void BackgroundSyncManager::ScheduleDelayedProcessingOfRegistrations( |
| blink::mojom::BackgroundSyncType sync_type) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| auto fire_events_callback = base::BindOnce( |
| &BackgroundSyncManager::FireReadyEvents, weak_ptr_factory_.GetWeakPtr(), |
| sync_type, /* reschedule= */ true, base::DoNothing::Once(), |
| /* keepalive= */ nullptr); |
| |
| proxy_->ScheduleDelayedProcessing( |
| sync_type, |
| GetSoonestWakeupDelta(sync_type, |
| /* last_browser_wakeup_time= */ base::Time()), |
| std::move(fire_events_callback)); |
| } |
| |
| void BackgroundSyncManager::FireReadyEvents( |
| blink::mojom::BackgroundSyncType sync_type, |
| bool reschedule, |
| base::OnceClosure callback, |
| std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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; |
| } |
| |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce(&BackgroundSyncManager::FireReadyEventsImpl, |
| weak_ptr_factory_.GetWeakPtr(), sync_type, reschedule, id, |
| std::move(callback), std::move(keepalive))); |
| } |
| |
| void BackgroundSyncManager::FireReadyEventsImpl( |
| blink::mojom::BackgroundSyncType sync_type, |
| bool reschedule, |
| int scheduler_id, |
| base::OnceClosure callback, |
| std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| op_scheduler_.WrapCallbackToRunNext(scheduler_id, 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/996166): Reschedule wakeup after a non-zero delay if |
| // called from a wakeup task. |
| if (reschedule) |
| ScheduleOrCancelDelayedProcessing(sync_type); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| op_scheduler_.WrapCallbackToRunNext(scheduler_id, 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(scheduler_id); |
| events_completed_callback = std::move(callback); |
| } else { |
| events_fired_callback = |
| op_scheduler_.WrapCallbackToRunNext(scheduler_id, std::move(callback)); |
| events_completed_callback = base::DoNothing::Once(); |
| } |
| |
| // 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; |
| service_worker_context_->FindReadyRegistrationForId( |
| service_worker_registration_id, |
| active_registrations_[service_worker_registration_id].origin.GetURL(), |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| BackgroundSyncRegistration* registration = |
| LookupActiveRegistration(*registration_info); |
| |
| 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(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::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, std::move(event_fired_callback)); |
| base::ThreadTaskRunnerHandle::Get()->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; |
| |
| HasMainFrameProviderHost( |
| url::Origin::Create(service_worker_registration->scope().GetOrigin()), |
| 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::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, std::move(event_fired_callback)); |
| } |
| |
| void BackgroundSyncManager::FireReadyEventsAllEventsFiring( |
| BackgroundSyncType sync_type, |
| bool reschedule, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (reschedule) |
| ScheduleOrCancelDelayedProcessing(sync_type); |
| |
| 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, |
| blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info, |
| std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive, |
| base::OnceClosure callback, |
| blink::ServiceWorkerStatusCode status_code) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| std::move(callback)); |
| return; |
| } |
| |
| // The event ran to completion, we should count it, no matter what happens |
| // from here. |
| url::Origin origin = |
| url::Origin::Create(service_worker_registration->scope().GetOrigin()); |
| HasMainFrameProviderHost( |
| origin, |
| base::BindOnce(&BackgroundSyncMetrics::RecordEventResult, |
| registration_info->sync_type, |
| status_code == blink::ServiceWorkerStatusCode::kOk)); |
| |
| auto id = op_scheduler_.CreateId(); |
| op_scheduler_.ScheduleOperation( |
| id, CacheStorageSchedulerMode::kExclusive, |
| CacheStorageSchedulerOp::kBackgroundSync, |
| base::BindOnce( |
| &BackgroundSyncManager::EventCompleteImpl, |
| weak_ptr_factory_.GetWeakPtr(), std::move(registration_info), |
| std::move(keepalive), status_code, origin, |
| op_scheduler_.WrapCallbackToRunNext(id, 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| if (disabled_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| std::move(callback)); |
| return; |
| } |
| |
| BackgroundSyncRegistration* registration = |
| LookupActiveRegistration(*registration_info); |
| if (!registration) { |
| base::ThreadTaskRunnerHandle::Get()->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())) { |
| // TODO(crbug.com/824858): Remove the else branch after the feature is |
| // enabled. Also, try to make a RunOrPostTaskOnThreadAndReplyWithResult() |
| // function so the if/else isn't needed. |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| 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); |
| |
| } else { |
| base::PostTaskAndReplyWithResult( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce( |
| &GetNextEventDelay, service_worker_context_, *registration, |
| std::make_unique<BackgroundSyncParameters>(*parameters_), |
| GetSmallestPeriodicSyncEventDelayForOrigin( |
| origin, registration->options()->tag)), |
| base::BindOnce(&BackgroundSyncManager::EventCompleteDidGetDelay, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(registration_info), status_code, origin, |
| std::move(callback))); |
| } |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| UpdateNumFiringRegistrationsBy(registration_info->sync_type, -1); |
| |
| BackgroundSyncRegistration* registration = |
| LookupActiveRegistration(*registration_info); |
| if (!registration) { |
| base::ThreadTaskRunnerHandle::Get()->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_->LogBackgroundServiceEventOnCoreThread( |
| registration_info->service_worker_registration_id, origin, |
| 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_->LogBackgroundServiceEventOnCoreThread( |
| registration_info->service_worker_registration_id, origin, |
| 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_->LogBackgroundServiceEventOnCoreThread( |
| registration_info->service_worker_registration_id, origin, |
| 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) { |
| RunOrPostTaskOnThread( |
| FROM_HERE, BrowserThread::UI, |
| base::BindOnce(&NotifyOneShotBackgroundSyncCompletedOnUIThread, |
| service_worker_context_, origin, status_code, |
| registration->num_attempts(), |
| registration->max_attempts())); |
| } else { |
| RunOrPostTaskOnThread( |
| FROM_HERE, BrowserThread::UI, |
| base::BindOnce(&NotifyPeriodicBackgroundSyncCompletedOnUIThread, |
| 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_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| 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(sync_type, /* reschedule= */ true, base::DoNothing::Once()); |
| |
| base::ThreadTaskRunnerHandle::Get()->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::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback)); |
| } |
| |
| void BackgroundSyncManager::OnRegistrationDeletedImpl( |
| int64_t sw_registration_id, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| // 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(ServiceWorkerContext::GetCoreThreadId()); |
| |
| active_registrations_.clear(); |
| disabled_ = false; |
| InitImpl(std::move(callback)); |
| } |
| |
| void BackgroundSyncManager::OnNetworkChanged() { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| |
| #if defined(OS_ANDROID) |
| if (parameters_->rely_on_android_network_detection) |
| return; |
| #endif |
| |
| if (!AreOptionConditionsMet()) |
| return; |
| |
| FireReadyEvents(BackgroundSyncType::ONE_SHOT, /* reschedule= */ true, |
| base::DoNothing::Once()); |
| FireReadyEvents(BackgroundSyncType::PERIODIC, /* reschedule= */ true, |
| base::DoNothing::Once()); |
| } |
| |
| base::OnceClosure BackgroundSyncManager::MakeEmptyCompletion( |
| CacheStorageSchedulerId id) { |
| DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); |
| return op_scheduler_.WrapCallbackToRunNext(id, base::DoNothing::Once()); |
| } |
| |
| 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 |