910e62b5创建于 1月15日历史提交
// 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 "chrome/browser/browsing_data/chrome_browsing_data_model_delegate.h"

#include <memory>
#include <variant>

#include "base/barrier_callback.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/concurrent_callbacks.h"
#include "base/functional/concurrent_closures.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/browsing_topics/browsing_topics_service_factory.h"
#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
#include "chrome/browser/webid/federated_identity_permission_context.h"
#include "chrome/browser/webid/federated_identity_permission_context_factory.h"
#include "components/browsing_topics/browsing_topics_service.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/media_device_salt/media_device_salt_service.h"
#include "components/permissions/permissions_client.h"
#include "components/supervised_user/core/common/features.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "url/origin.h"

#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/web_applications/isolated_web_apps/remove_isolated_web_app_data.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#endif

namespace {

#if !BUILDFLAG(IS_ANDROID)
std::vector<ChromeBrowsingDataModelDelegate::DelegateEntry>
IsolatedWebAppBrowsingDataToDelegateEntries(
    base::flat_map<url::Origin, uint64_t> isolated_web_app_browsing_data) {
  std::vector<ChromeBrowsingDataModelDelegate::DelegateEntry> entries;
  for (auto const& [origin, size] : isolated_web_app_browsing_data) {
    entries.emplace_back(
        origin,
        static_cast<BrowsingDataModel::StorageType>(
            ChromeBrowsingDataModelDelegate::StorageType::kIsolatedWebApp),
        size);
  }
  return entries;
}
#endif  // !BUILDFLAG(IS_ANDROID)

std::vector<ChromeBrowsingDataModelDelegate::DelegateEntry>
FlattenDelegateEntries(
    std::vector<std::vector<ChromeBrowsingDataModelDelegate::DelegateEntry>>
        entries) {
  std::vector<ChromeBrowsingDataModelDelegate::DelegateEntry> flattened_entries;
  for (const auto& vec : entries) {
    flattened_entries.insert(flattened_entries.end(), vec.begin(), vec.end());
  }
  return flattened_entries;
}

}  // namespace

// static
std::unique_ptr<ChromeBrowsingDataModelDelegate>
ChromeBrowsingDataModelDelegate::CreateForProfile(Profile* profile) {
  return base::WrapUnique(new ChromeBrowsingDataModelDelegate(
      profile, profile->GetDefaultStoragePartition()));
}

// static
std::unique_ptr<ChromeBrowsingDataModelDelegate>
ChromeBrowsingDataModelDelegate::CreateForStoragePartition(
    Profile* profile,
    content::StoragePartition* storage_partition) {
  return base::WrapUnique(
      new ChromeBrowsingDataModelDelegate(profile, storage_partition));
}

// static
void ChromeBrowsingDataModelDelegate::BrowsingDataAccessed(
    content::RenderFrameHost* rfh,
    const BrowsingDataModel::DataKey& data_key,
    StorageType storage_type,
    bool blocked) {
  content_settings::PageSpecificContentSettings::BrowsingDataAccessed(
      rfh, data_key, static_cast<BrowsingDataModel::StorageType>(storage_type),
      blocked);
}

ChromeBrowsingDataModelDelegate::ChromeBrowsingDataModelDelegate(
    Profile* profile,
    content::StoragePartition* storage_partition)
    : profile_(profile), storage_partition_(storage_partition) {}

ChromeBrowsingDataModelDelegate::~ChromeBrowsingDataModelDelegate() = default;

void ChromeBrowsingDataModelDelegate::GetAllDataKeys(
    base::OnceCallback<void(std::vector<DelegateEntry>)> callback) {
  base::ConcurrentCallbacks<std::vector<DelegateEntry>> concurrent;

  GetAllFederatedIdentityDataKeys(concurrent.CreateCallback(), {});

#if !BUILDFLAG(IS_ANDROID)
  auto* web_app_provider = web_app::WebAppProvider::GetForWebApps(profile_);
  if (web_app_provider && storage_partition_->GetConfig().is_default()) {
    web_app_provider->scheduler().GetIsolatedWebAppBrowsingData(
        base::BindOnce(&IsolatedWebAppBrowsingDataToDelegateEntries)
            .Then(concurrent.CreateCallback()));
  }
#endif  // !BUILDFLAG(IS_ANDROID)

  GetAllMediaDeviceSaltDataKeys(concurrent.CreateCallback(), {});

  // TODO(crbug.com/40205603): Implement data retrieval for remaining data
  // types.

  std::move(concurrent)
      .Done(base::BindOnce(&FlattenDelegateEntries).Then(std::move(callback)));
}

void ChromeBrowsingDataModelDelegate::RemoveDataKey(
    const BrowsingDataModel::DataKey& data_key,
    BrowsingDataModel::StorageTypeSet storage_types,
    base::OnceClosure callback) {
  base::ConcurrentClosures concurrent;

  if (storage_types.Has(
          static_cast<BrowsingDataModel::StorageType>(StorageType::kTopics))) {
    // Topics can be deleted but not queried from disk as the creating origins
    // are hashed before being saved.
    const url::Origin* origin = std::get_if<url::Origin>(&data_key);
    auto* browsing_topics_service =
        browsing_topics::BrowsingTopicsServiceFactory::GetForProfile(profile_);
    browsing_topics_service->ClearTopicsDataForOrigin(*origin);
  }

  if (storage_types.Has(static_cast<BrowsingDataModel::StorageType>(
          StorageType::kMediaDeviceSalt))) {
    if (const blink::StorageKey* storage_key =
            std::get_if<blink::StorageKey>(&data_key)) {
      RemoveMediaDeviceSalt(*storage_key, concurrent.CreateClosure());
    }
  }

  if (storage_types.Has(static_cast<BrowsingDataModel::StorageType>(
          StorageType::kFederatedIdentity))) {
    if (const webid::FederatedIdentityDataModel::DataKey*
            federated_identity_data_key =
                std::get_if<webid::FederatedIdentityDataModel::DataKey>(
                    &data_key)) {
      RemoveFederatedIdentityData(*federated_identity_data_key,
                                  concurrent.CreateClosure());
    }
  }

#if !BUILDFLAG(IS_ANDROID)
  if (storage_types.Has(static_cast<BrowsingDataModel::StorageType>(
          StorageType::kIsolatedWebApp))) {
    CHECK(std::holds_alternative<url::Origin>(data_key));
    const url::Origin& origin = *std::get_if<url::Origin>(&data_key);

    web_app::RemoveIsolatedWebAppBrowsingData(profile_, origin,
                                              concurrent.CreateClosure());
  }
#endif  // !BUILDFLAG(IS_ANDROID)

  std::move(concurrent).Done(std::move(callback));
}

std::optional<BrowsingDataModel::DataOwner>
ChromeBrowsingDataModelDelegate::GetDataOwner(
    const BrowsingDataModel::DataKey& data_key,
    BrowsingDataModel::StorageType storage_type) const {
  switch (static_cast<StorageType>(storage_type)) {
    case StorageType::kIsolatedWebApp:
      CHECK(std::holds_alternative<url::Origin>(data_key))
          << "Unsupported IWA DataKey type: " << data_key.index();
      return std::get<url::Origin>(data_key);

    case StorageType::kTopics:
      CHECK(std::holds_alternative<url::Origin>(data_key))
          << "Unsupported Topics DataKey type: " << data_key.index();
      return std::get<url::Origin>(data_key).host();

    case StorageType::kMediaDeviceSalt:
      CHECK(std::holds_alternative<blink::StorageKey>(data_key))
          << "Unsupported MediaDeviceSalt DataKey type: " << data_key.index();
      return std::get<blink::StorageKey>(data_key).origin().host();

    case StorageType::kFederatedIdentity:
      CHECK(std::holds_alternative<webid::FederatedIdentityDataModel::DataKey>(
          data_key))
          << "Unsupported FederatedIdentity DataKey type: " << data_key.index();
      return std::get<webid::FederatedIdentityDataModel::DataKey>(data_key)
          .relying_party_embedder()
          .host();

    default:
      return std::nullopt;
  }
}

std::optional<bool> ChromeBrowsingDataModelDelegate::IsStorageTypeCookieLike(
    BrowsingDataModel::StorageType storage_type) const {
  // Values below the first delegate type are handled in the model itself.
  if (static_cast<int>(storage_type) <
      static_cast<int>(StorageType::kFirstType)) {
    return std::nullopt;
  }
  switch (
      static_cast<ChromeBrowsingDataModelDelegate::StorageType>(storage_type)) {
    case StorageType::kTopics:
    case StorageType::kIsolatedWebApp:
    case StorageType::kMediaDeviceSalt:
      return false;
    default:
      NOTREACHED();
  }
}

std::optional<bool>
ChromeBrowsingDataModelDelegate::IsBlockedByThirdPartyCookieBlocking(
    const BrowsingDataModel::DataKey& data_key,
    BrowsingDataModel::StorageType storage_type) const {
  // TODO(crbug.com/40066162): Implement `GetThirdPartyPartitioningSite()` for
  // delegate-specific data keys.
  return IsStorageTypeCookieLike(storage_type);
}

bool ChromeBrowsingDataModelDelegate::IsCookieDeletionDisabled(
    const GURL& url) {
  CHECK(profile_);
  if (profile_->IsChild()) {
    auto* client = permissions::PermissionsClient::Get();
    return client->IsCookieDeletionDisabled(profile_, url);
  }
  return false;
}

base::WeakPtr<BrowsingDataModel::Delegate>
ChromeBrowsingDataModelDelegate::AsWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

void ChromeBrowsingDataModelDelegate::GetAllMediaDeviceSaltDataKeys(
    base::OnceCallback<void(std::vector<DelegateEntry>)> callback,
    std::vector<DelegateEntry> entries) {
  if (auto* service =
          MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext(
              profile_)) {
    service->GetAllStorageKeys(base::BindOnce(
        &ChromeBrowsingDataModelDelegate::GotAllMediaDeviceSaltDataKeys,
        weak_ptr_factory_.GetWeakPtr(), std::move(callback),
        std::move(entries)));
  } else {
    std::move(callback).Run(std::move(entries));
  }
}

void ChromeBrowsingDataModelDelegate::GotAllMediaDeviceSaltDataKeys(
    base::OnceCallback<void(std::vector<DelegateEntry>)> callback,
    std::vector<DelegateEntry> entries,
    std::vector<blink::StorageKey> storage_keys) {
  static constexpr uint64_t kMediaDeviceSaltEntrySize = 100;
  for (const auto& key : storage_keys) {
    entries.emplace_back(key,
                         static_cast<BrowsingDataModel::StorageType>(
                             StorageType::kMediaDeviceSalt),
                         kMediaDeviceSaltEntrySize);
  }
  std::move(callback).Run(std::move(entries));
}

void ChromeBrowsingDataModelDelegate::RemoveMediaDeviceSalt(
    const blink::StorageKey& storage_key,
    base::OnceClosure callback) {
  media_device_salt::MediaDeviceSaltService* service =
      MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext(
          profile_);
  if (service) {
    service->DeleteSalt(storage_key, std::move(callback));
  } else {
    std::move(callback).Run();
  }
}

void ChromeBrowsingDataModelDelegate::GetAllFederatedIdentityDataKeys(
    base::OnceCallback<void(std::vector<DelegateEntry>)> callback,
    std::vector<DelegateEntry> entries) {
  if (auto* context =
          FederatedIdentityPermissionContextFactory::GetForProfile(profile_)) {
    context->GetAllDataKeys(base::BindOnce(
        &ChromeBrowsingDataModelDelegate::GotAllFederatedIdentityDataKeys,
        weak_ptr_factory_.GetWeakPtr(), std::move(callback),
        std::move(entries)));
  } else {
    std::move(callback).Run(std::move(entries));
  }
}

void ChromeBrowsingDataModelDelegate::GotAllFederatedIdentityDataKeys(
    base::OnceCallback<void(std::vector<DelegateEntry>)> callback,
    std::vector<DelegateEntry> entries,
    std::vector<webid::FederatedIdentityDataModel::DataKey> data_keys) {
  static constexpr uint64_t kFederatedIdentityDataEntrySize = 100;
  for (const auto& key : data_keys) {
    entries.emplace_back(key,
                         static_cast<BrowsingDataModel::StorageType>(
                             StorageType::kFederatedIdentity),
                         kFederatedIdentityDataEntrySize);
  }
  std::move(callback).Run(std::move(entries));
}

void ChromeBrowsingDataModelDelegate::RemoveFederatedIdentityData(
    const webid::FederatedIdentityDataModel::DataKey& data_key,
    base::OnceClosure callback) {
  if (auto* context =
          FederatedIdentityPermissionContextFactory::GetForProfile(profile_)) {
    context->RemoveFederatedIdentityDataByDataKey(data_key,
                                                  std::move(callback));
  } else {
    std::move(callback).Run();
  }
}