blob: d279e46e84d3243b96643b3359f15b75d0ca7077 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/background_sync/background_sync_scheduler.h"
#include <algorithm>
#include "base/memory/scoped_refptr.h"
#include "build/build_config.h"
#include "content/browser/browser_context_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/background_sync_controller.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
namespace content {
using DelayedProcessingInfoMap =
std::map<StoragePartitionImpl*, std::unique_ptr<base::OneShotTimer>>;
// static
BackgroundSyncScheduler* BackgroundSyncScheduler::GetFor(
BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context);
return BrowserContextImpl::From(browser_context)->background_sync_scheduler();
}
BackgroundSyncScheduler::BackgroundSyncScheduler() = default;
BackgroundSyncScheduler::~BackgroundSyncScheduler() {
for (auto& one_shot_processing_info : delayed_processing_info_one_shot_)
one_shot_processing_info.second->Stop();
for (auto& periodic_processing_info : delayed_processing_info_periodic_)
periodic_processing_info.second->Stop();
}
void BackgroundSyncScheduler::ScheduleDelayedProcessing(
StoragePartitionImpl* storage_partition,
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay,
base::OnceClosure delayed_task) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(storage_partition);
// CancelDelayedProcessing should be called in this case.
DCHECK(!delay.is_max());
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
delayed_processing_info.emplace(storage_partition,
std::make_unique<base::OneShotTimer>());
if (!delay.is_zero()) {
delayed_processing_info[storage_partition]->Start(
FROM_HERE, delay,
base::BindOnce(&BackgroundSyncScheduler::RunDelayedTaskAndPruneInfoMap,
weak_ptr_factory_.GetWeakPtr(), sync_type,
storage_partition, std::move(delayed_task)));
}
#if BUILDFLAG(IS_ANDROID)
ScheduleOrCancelBrowserWakeupForSyncType(sync_type, storage_partition);
#endif
}
void BackgroundSyncScheduler::CancelDelayedProcessing(
StoragePartitionImpl* storage_partition,
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(storage_partition);
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
if (delayed_processing_info.count(storage_partition)) {
base::TimeTicks run_time =
delayed_processing_info[storage_partition]->desired_run_time();
// If this storage partition was scheduling the next wakeup, reset the
// wakeup time for |sync_type|.
if (scheduled_wakeup_time_[sync_type] == run_time)
scheduled_wakeup_time_[sync_type] = base::TimeTicks::Max();
delayed_processing_info.erase(storage_partition);
}
#if BUILDFLAG(IS_ANDROID)
ScheduleOrCancelBrowserWakeupForSyncType(sync_type, storage_partition);
#endif
}
DelayedProcessingInfoMap& BackgroundSyncScheduler::GetDelayedProcessingInfoMap(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT)
return delayed_processing_info_one_shot_;
else
return delayed_processing_info_periodic_;
}
void BackgroundSyncScheduler::RunDelayedTaskAndPruneInfoMap(
blink::mojom::BackgroundSyncType sync_type,
StoragePartitionImpl* storage_partition,
base::OnceClosure delayed_task) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::move(delayed_task).Run();
CancelDelayedProcessing(storage_partition, sync_type);
}
#if BUILDFLAG(IS_ANDROID)
void BackgroundSyncScheduler::ScheduleOrCancelBrowserWakeupForSyncType(
blink::mojom::BackgroundSyncType sync_type,
StoragePartitionImpl* storage_partition) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* browser_context = storage_partition->browser_context();
DCHECK(browser_context);
auto* controller = browser_context->GetBackgroundSyncController();
DCHECK(controller);
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
// If no more scheduled tasks remain, cancel browser wakeup.
// Canceling when there's no task scheduled is a no-op.
if (delayed_processing_info.empty()) {
scheduled_wakeup_time_[sync_type] = base::TimeTicks::Max();
controller->CancelBrowserWakeup(sync_type);
return;
}
// Schedule browser wakeup with the smallest delay required.
auto& min_info = *std::min_element(
delayed_processing_info.begin(), delayed_processing_info.end(),
[](auto& lhs, auto& rhs) {
return (lhs.second->desired_run_time() - base::TimeTicks::Now()) <
(rhs.second->desired_run_time() - base::TimeTicks::Now());
});
base::TimeTicks next_time = min_info.second->desired_run_time();
if (next_time >= scheduled_wakeup_time_[sync_type]) {
// There's an earlier wakeup time scheduled, no need to inform the
// scheduler.
return;
}
scheduled_wakeup_time_[sync_type] = next_time;
controller->ScheduleBrowserWakeUpWithDelay(
sync_type, min_info.second->desired_run_time() - base::TimeTicks::Now());
}
#endif
} // namespace content