blob: 4929e0506036d687785ffd176d289d125f23863f [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/page_image_service/image_service_consent_helper.h"
#include "base/metrics/histogram_functions.h"
#include "components/page_image_service/metrics_util.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_service_utils.h"
namespace page_image_service {
namespace {
PageImageServiceConsentStatus ConsentStatusToUmaStatus(
std::optional<bool> consent_status) {
if (!consent_status) {
return PageImageServiceConsentStatus::kTimedOut;
}
return consent_status.value() ? PageImageServiceConsentStatus::kSuccess
: PageImageServiceConsentStatus::kFailure;
}
} // namespace
ImageServiceConsentHelper::ImageServiceConsentHelper(
syncer::SyncService* sync_service,
syncer::DataType data_type)
: sync_service_(sync_service),
data_type_(data_type),
timeout_duration_(base::Seconds(10)) {
// `sync_service` can be null, for example when disabled via flags.
if (sync_service) {
sync_service_observer_.Observe(sync_service);
}
}
ImageServiceConsentHelper::~ImageServiceConsentHelper() = default;
void ImageServiceConsentHelper::EnqueueRequest(
base::OnceCallback<void(PageImageServiceConsentStatus)> callback,
mojom::ClientId client_id) {
base::UmaHistogramBoolean("PageImageService.ConsentStatusRequestCount", true);
std::optional<bool> consent_status = GetConsentStatus();
if (consent_status.has_value()) {
std::move(callback).Run(*consent_status
? PageImageServiceConsentStatus::kSuccess
: PageImageServiceConsentStatus::kFailure);
return;
}
enqueued_request_callbacks_.emplace_back(std::move(callback), client_id);
if (!request_processing_timer_.IsRunning()) {
request_processing_timer_.Start(
FROM_HERE, timeout_duration_,
base::BindOnce(&ImageServiceConsentHelper::OnTimeoutExpired,
weak_ptr_factory_.GetWeakPtr()));
}
}
void ImageServiceConsentHelper::OnStateChanged(
syncer::SyncService* sync_service) {
CHECK_EQ(sync_service_, sync_service);
std::optional<bool> consent_status = GetConsentStatus();
if (!consent_status.has_value()) {
return;
}
request_processing_timer_.Stop();
// The request callbacks can modify the vector while running. Swap the vector
// onto the stack to prevent crashing. https://crbug.com/1472360.
std::vector<std::pair<base::OnceCallback<void(PageImageServiceConsentStatus)>,
mojom::ClientId>>
callbacks;
std::swap(callbacks, enqueued_request_callbacks_);
for (auto& request_callback_with_client_id : callbacks) {
std::move(request_callback_with_client_id.first)
.Run(*consent_status ? PageImageServiceConsentStatus::kSuccess
: PageImageServiceConsentStatus::kFailure);
}
}
void ImageServiceConsentHelper::OnSyncShutdown(
syncer::SyncService* sync_service) {
CHECK_EQ(sync_service_, sync_service);
sync_service_observer_.Reset();
sync_service_ = nullptr;
}
std::optional<bool> ImageServiceConsentHelper::GetConsentStatus() {
if (!sync_service_) {
return false;
}
// If upload of the given DataType is disabled (or inactive due to an
// error), then consent must be assumed to be NOT given.
// Note that the "INITIALIZING" state is good enough: It means the data
// type is enabled in principle, Sync just hasn't fully finished
// initializing yet. This case is handled by the DownloadStatus check
// below.
if (syncer::GetUploadToGoogleState(sync_service_, data_type_) ==
syncer::UploadState::NOT_ACTIVE) {
return false;
}
// Ensure Sync has downloaded all relevant updates (i.e. any deletions from
// other devices are known).
syncer::SyncService::DataTypeDownloadStatus download_status =
sync_service_->GetDownloadStatusFor(data_type_);
switch (download_status) {
case syncer::SyncService::DataTypeDownloadStatus::kWaitingForUpdates:
return std::nullopt;
case syncer::SyncService::DataTypeDownloadStatus::kUpToDate:
return true;
case syncer::SyncService::DataTypeDownloadStatus::kError:
return false;
}
}
void ImageServiceConsentHelper::OnTimeoutExpired() {
// The request callbacks can modify the vector while running. Swap the vector
// onto the stack to prevent crashing. https://crbug.com/1472360.
std::vector<std::pair<base::OnceCallback<void(PageImageServiceConsentStatus)>,
mojom::ClientId>>
callbacks;
std::swap(callbacks, enqueued_request_callbacks_);
for (auto& request_callback_with_client_id : callbacks) {
// Report consent status on timeout for each request to compare against the
// number of all requests.
PageImageServiceConsentStatus consent_status =
ConsentStatusToUmaStatus(GetConsentStatus());
base::UmaHistogramEnumeration("PageImageService.ConsentStatusOnTimeout",
consent_status);
std::move(request_callback_with_client_id.first).Run(consent_status);
}
}
} // namespace page_image_service