910e62b5创建于 1月15日历史提交
// 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