blob: ece8c14da8d2ee6bd6ef9cfd3579d8e9ccea3eff [file] [log] [blame]
// Copyright 2025 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/ui/safety_hub/unused_site_permissions_manager.h"
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ui/safety_hub/revoked_permissions_result.h"
#include "chrome/browser/ui/safety_hub/safety_hub_prefs.h"
#include "chrome/browser/ui/safety_hub/safety_hub_result.h"
#include "chrome/browser/ui/safety_hub/safety_hub_util.h"
#include "components/content_settings/core/browser/content_settings_info.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/content_settings_uma_util.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/website_settings_registry.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_constraints.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/permissions/constants.h"
#include "components/permissions/permission_uma_util.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/safety_check/safety_check.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "url/gurl.h"
#include "url/origin.h"
constexpr base::TimeDelta kRevocationThresholdNoDelayForTesting = base::Days(0);
constexpr base::TimeDelta kRevocationThresholdWithDelayForTesting =
base::Minutes(5);
namespace {
constexpr char kUnknownContentSettingsType[] = "unknown";
// Reflects the maximum number of days between a permissions being revoked and
// the time when the user regrants the permission through the unused site
// permission module of Safete Check. The maximum number of days is determined
// by `kRevocationCleanUpThreshold`.
size_t kAllowAgainMetricsExclusiveMaxCount = 31;
// Using a single bucket per day, following the value of
// `kAllowAgainMetricsExclusiveMaxCount`.
size_t kAllowAgainMetricsBuckets = 31;
// Determines the time interval after which sites are considered to be unused
// and their permissions will be revoked.
const base::TimeDelta kUnusedSitePermissionsRevocationThreshold =
base::Days(60);
base::TimeDelta GetRevocationThreshold() {
// TODO(crbug.com/40250875): Clean up no delay revocation after the feature is
// ready. Today, no delay revocation is necessary to enable manual testing.
if (content_settings::features::kSafetyCheckUnusedSitePermissionsNoDelay
.Get()) {
return kRevocationThresholdNoDelayForTesting;
} else if (content_settings::features::
kSafetyCheckUnusedSitePermissionsWithDelay.Get()) {
return kRevocationThresholdWithDelayForTesting;
}
return kUnusedSitePermissionsRevocationThreshold;
}
bool IsContentSetting(ContentSettingsType type) {
auto* content_setting_registry =
content_settings::ContentSettingsRegistry::GetInstance();
return content_setting_registry->Get(type);
}
bool IsWebsiteSetting(ContentSettingsType type) {
auto* website_setting_registry =
content_settings::WebsiteSettingsRegistry::GetInstance();
return website_setting_registry->Get(type);
}
bool IsChooserPermissionSupported() {
return base::FeatureList::IsEnabled(
content_settings::features::
kSafetyCheckUnusedSitePermissionsForSupportedChooserPermissions);
}
const std::set<ContentSettingsType> GetRevokedUnusedSitePermissionTypes(
const std::set<ContentSettingsType> permissions) {
std::set<ContentSettingsType>
permissions_without_revoked_abusive_notification_manager = permissions;
if (permissions_without_revoked_abusive_notification_manager.contains(
ContentSettingsType::NOTIFICATIONS)) {
permissions_without_revoked_abusive_notification_manager.erase(
ContentSettingsType::NOTIFICATIONS);
}
return permissions_without_revoked_abusive_notification_manager;
}
base::Value::List ConvertContentSettingsIntValuesToString(
const base::Value::List& content_settings_values_list,
bool* successful_migration) {
base::Value::List string_value_list;
for (const base::Value& setting_value : content_settings_values_list) {
if (setting_value.is_int()) {
int setting_int = setting_value.GetInt();
auto setting_name =
UnusedSitePermissionsManager::ConvertContentSettingsTypeToKey(
static_cast<ContentSettingsType>(setting_int));
if (setting_name == kUnknownContentSettingsType) {
*successful_migration = false;
string_value_list.Append(setting_value.GetInt());
} else {
string_value_list.Append(setting_name);
}
} else {
CHECK(setting_value.is_string());
// Store string group name values.
string_value_list.Append(setting_value.GetString());
}
}
return string_value_list;
}
base::Value::Dict ConvertChooserContentSettingsIntValuesToString(
const base::Value::Dict& chooser_content_settings_values_dict) {
base::Value::Dict string_keyed_dict;
for (const auto [key, value] : chooser_content_settings_values_dict) {
int number = -1;
base::StringToInt(key, &number);
// If number conversion fails it returns 0 which is not a chooser permission
// enum value so it will not clash with the conversion.
if (number == 0) {
// Store string keyed values as is.
string_keyed_dict.Set(key, value.GetDict().Clone());
} else {
string_keyed_dict.Set(
UnusedSitePermissionsManager::ConvertContentSettingsTypeToKey(
static_cast<ContentSettingsType>(number)),
value.GetDict().Clone());
}
}
return string_keyed_dict;
}
} // namespace
// static
std::unique_ptr<SafetyHubResult>
UnusedSitePermissionsManager::UpdateOnBackgroundThread(
base::Clock* clock,
const scoped_refptr<HostContentSettingsMap> hcsm) {
UnusedSitePermissionsManager::UnusedPermissionMap recently_unused;
const base::Time threshold =
clock->Now() - content_settings::GetCoarseVisitedTimePrecision();
auto* website_setting_registry =
content_settings::WebsiteSettingsRegistry::GetInstance();
for (const content_settings::WebsiteSettingsInfo* info :
*website_setting_registry) {
ContentSettingsType type = info->type();
if (!IsContentSetting(type) && IsWebsiteSetting(type) &&
!IsChooserPermissionSupported()) {
continue;
}
if (!content_settings::CanTrackLastVisit(type)) {
continue;
}
ContentSettingsForOneType settings = hcsm->GetSettingsForOneType(type);
for (const auto& setting : settings) {
// Skip wildcard patterns that don't belong to a single origin. These
// shouldn't track visit timestamps.
if (!setting.primary_pattern.MatchesSingleOrigin()) {
continue;
}
if (setting.metadata.last_visited() != base::Time() &&
setting.metadata.last_visited() < threshold) {
// Converting a primary pattern to an origin is normally an anti-pattern
// but here it is ok since the primary pattern belongs to a single
// origin. Therefore, it has a fully defined URL+scheme+port which makes
// converting primary pattern to origin successful.
url::Origin origin =
ConvertPrimaryPatternToOrigin(setting.primary_pattern);
recently_unused[origin.Serialize()].push_back(
{type, std::move(setting)});
}
}
}
auto result = std::make_unique<RevokedPermissionsResult>();
result->SetRecentlyUnusedPermissions(recently_unused);
return std::move(result);
}
// static
std::string UnusedSitePermissionsManager::ConvertContentSettingsTypeToKey(
ContentSettingsType type) {
auto* website_setting_registry =
content_settings::WebsiteSettingsRegistry::GetInstance();
CHECK(website_setting_registry);
auto* website_settings_info = website_setting_registry->Get(type);
if (!website_settings_info) {
auto integer_type = static_cast<int32_t>(type);
DVLOG(1) << "Couldn't retrieve website settings info entry from the "
"registry for type: "
<< integer_type;
base::UmaHistogramSparse(
"Settings.SafetyCheck.UnusedSitePermissionsMigrationFail",
integer_type);
return kUnknownContentSettingsType;
}
return website_settings_info->name();
}
// static
ContentSettingsType
UnusedSitePermissionsManager::ConvertKeyToContentSettingsType(
const std::string& key) {
auto* website_setting_registry =
content_settings::WebsiteSettingsRegistry::GetInstance();
return website_setting_registry->GetByName(key)->type();
}
// static
url::Origin UnusedSitePermissionsManager::ConvertPrimaryPatternToOrigin(
const ContentSettingsPattern& primary_pattern) {
GURL origin_url = GURL(primary_pattern.ToString());
CHECK(origin_url.is_valid());
return url::Origin::Create(origin_url);
}
UnusedSitePermissionsManager::UnusedSitePermissionsManager(
content::BrowserContext* browser_context,
PrefService* prefs)
: browser_context_(browser_context),
clock_(base::DefaultClock::GetInstance()) {
CHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK(browser_context_);
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
pref_change_registrar_->Init(prefs);
bool migration_completed = pref_change_registrar_->prefs()->GetBoolean(
safety_hub_prefs::kUnusedSitePermissionsRevocationMigrationCompleted);
if (!migration_completed) {
// Convert all integer permission values to string, if there is any
// permission represented by integer stored in disk.
// TODO(crbug.com/415227458): Clean up this migration after some milestones.
UpdateIntegerValuesToGroupName();
}
}
UnusedSitePermissionsManager::~UnusedSitePermissionsManager() = default;
void UnusedSitePermissionsManager::RevokeUnusedPermissions(
std::unique_ptr<SafetyHubResult> result) {
// Set this to true to prevent
// `UnusedSitePermissionsManager::OnContentSettingChanged` from removing
// revoked setting values during auto-revocation.
is_unused_site_revocation_running_ = true;
auto* interim_result = static_cast<RevokedPermissionsResult*>(result.get());
recently_unused_permissions_ = interim_result->GetRecentlyUnusedPermissions();
base::Time threshold = clock_->Now() - GetRevocationThreshold();
for (auto itr = recently_unused_permissions_.begin();
itr != recently_unused_permissions_.end();) {
std::list<ContentSettingEntry>& unused_site_permissions = itr->second;
// All |primary_pattern|s are equal across list items, the same is true for
// |secondary_pattern|s. This property is needed later and checked in the
// loop.
ContentSettingsPattern primary_pattern =
unused_site_permissions.front().source.primary_pattern;
ContentSettingsPattern secondary_pattern =
unused_site_permissions.front().source.secondary_pattern;
std::set<ContentSettingsType> revoked_permissions;
base::Value::Dict chooser_permissions_data;
for (auto permission_itr = unused_site_permissions.begin();
permission_itr != unused_site_permissions.end();) {
const ContentSettingEntry& entry = *permission_itr;
// Check if the current permission can be auto revoked.
if (!content_settings::CanBeAutoRevokedAsUnusedPermission(
/*type=*/entry.type, /*value=*/entry.source.setting_value)) {
permission_itr++;
continue;
}
CHECK_EQ(entry.source.primary_pattern, primary_pattern);
CHECK(entry.source.secondary_pattern ==
ContentSettingsPattern::Wildcard() ||
entry.source.secondary_pattern == entry.source.primary_pattern);
// Reset the permission to default if the site is visited before
// threshold. Also, the secondary pattern should be wildcard.
CHECK_NE(entry.source.metadata.last_visited(), base::Time());
CHECK(entry.type != ContentSettingsType::NOTIFICATIONS);
if (entry.source.metadata.last_visited() < threshold &&
entry.source.secondary_pattern ==
ContentSettingsPattern::Wildcard()) {
permissions::PermissionUmaUtil::ScopedRevocationReporter reporter(
browser_context_.get(), entry.source.primary_pattern,
entry.source.secondary_pattern, entry.type,
permissions::PermissionSourceUI::SAFETY_HUB_AUTO_REVOCATION);
// Record the number of permissions auto-revoked per permission type.
content_settings_uma_util::RecordContentSettingsHistogram(
"Settings.SafetyHub.UnusedSitePermissionsModule.AutoRevoked2",
entry.type);
revoked_permissions.insert(entry.type);
if (IsContentSetting(entry.type)) {
hcsm()->SetContentSettingCustomScope(
entry.source.primary_pattern, entry.source.secondary_pattern,
entry.type, ContentSetting::CONTENT_SETTING_DEFAULT);
} else if (IsChooserPermissionSupported() &&
IsWebsiteSetting(entry.type)) {
chooser_permissions_data.Set(
ConvertContentSettingsTypeToKey(entry.type),
entry.source.setting_value.Clone());
hcsm()->SetWebsiteSettingCustomScope(entry.source.primary_pattern,
entry.source.secondary_pattern,
entry.type, base::Value());
} else {
NOTREACHED()
<< "Unable to find ContentSettingsType in neither "
<< "ContentSettingsRegistry nor WebsiteSettingsRegistry: "
<< ConvertContentSettingsTypeToKey(entry.type);
}
unused_site_permissions.erase(permission_itr++);
} else {
permission_itr++;
}
}
// Store revoked permissions on HCSM.
if (!revoked_permissions.empty()) {
StorePermissionInUnusedSitePermissionSetting(
revoked_permissions, chooser_permissions_data, std::nullopt,
primary_pattern, secondary_pattern);
}
// Handle clean up of recently_unused_permissions_ map after revocation.
if (unused_site_permissions.empty()) {
// Since all unused permissions are revoked, the map should be cleared.
recently_unused_permissions_.erase(itr++);
} else {
// Since there are some permissions that are not revoked, the tracked
// unused permissions should be set to those permissions.
// Note that, currently all permissions belong to a single domain will
// revoked all together, since triggering permission prompt requires a
// page visit. So the timestamp of all granted permissions of the origin
// will be updated. However, this logic will prevent edge cases like
// permission prompt stays open long time, also will provide support for
// revoking permissions separately in the future.
itr++;
}
}
// Set this back to false, so that `OnContentSettingChanged` can cleanup
// revoked settings if necessary.
is_unused_site_revocation_running_ = false;
}
bool UnusedSitePermissionsManager::IsRevocationRunning() {
return is_unused_site_revocation_running_;
}
// Called by TabHelper when a URL was visited.
void UnusedSitePermissionsManager::OnPageVisited(const url::Origin& origin) {
CHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Check if this origin has unused permissions.
auto origin_entry = recently_unused_permissions_.find(origin.Serialize());
if (origin_entry == recently_unused_permissions_.end()) {
return;
}
// See which permissions of the origin actually match the URL and update them.
auto& site_permissions = origin_entry->second;
for (auto it = site_permissions.begin(); it != site_permissions.end();) {
if (it->source.primary_pattern.Matches(origin.GetURL())) {
hcsm()->UpdateLastVisitedTime(it->source.primary_pattern,
it->source.secondary_pattern, it->type);
site_permissions.erase(it++);
} else {
it++;
}
}
// Remove origin entry if all permissions were updated.
if (site_permissions.empty()) {
recently_unused_permissions_.erase(origin_entry);
}
}
void UnusedSitePermissionsManager::RegrantPermissionsForOrigin(
const url::Origin& origin) {
content_settings::SettingInfo info;
base::Value stored_value(hcsm()->GetWebsiteSetting(
origin.GetURL(), origin.GetURL(),
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, &info));
if (!stored_value.is_dict()) {
return;
}
base::Value::List* permission_type_list =
stored_value.GetDict().FindList(permissions::kRevokedKey);
CHECK(permission_type_list);
// Set this to true to prevent `OnContentSettingChanged` from removing
// revoked setting values, since this is set with specific values below.
is_unused_site_revocation_running_ = true;
for (auto& permission_type : *permission_type_list) {
// Look up ContentSettingsRegistry to see if type is content setting
// or website setting.
ContentSettingsType type =
ConvertKeyToContentSettingsType(permission_type.GetString());
if (IsContentSetting(type)) {
// ContentSettingsRegistry-based permissions with ALLOW value were
// revoked; re-grant them by setting ALLOW again.
hcsm()->SetContentSettingCustomScope(
info.primary_pattern, info.secondary_pattern, type,
ContentSetting::CONTENT_SETTING_ALLOW);
} else if (IsChooserPermissionSupported() && IsWebsiteSetting(type)) {
auto* chooser_permissions_data = stored_value.GetDict().FindDict(
permissions::kRevokedChooserPermissionsKey);
// There should be always data attached for a revoked chooser permission.
CHECK(chooser_permissions_data);
// Chooser permissions are WebsiteSettingsRegistry-based, so it is
// re-granted by restoring the previously revoked Website Setting value.
auto* revoked_value = chooser_permissions_data->FindDict(
ConvertContentSettingsTypeToKey(type));
CHECK(revoked_value);
hcsm()->SetWebsiteSettingCustomScope(
info.primary_pattern, info.secondary_pattern, type,
base::Value(std::move(*revoked_value)));
} else {
NOTREACHED() << "Unable to find ContentSettingsType in neither "
<< "ContentSettingsRegistry nor WebsiteSettingsRegistry: "
<< ConvertContentSettingsTypeToKey(type);
}
}
// Set this back to false, so that `OnContentSettingChanged` can cleanup
// revoked settings if necessary.
is_unused_site_revocation_running_ = false;
// Ignore origin from future auto-revocations.
IgnoreOriginForAutoRevocation(origin);
// Remove origin from revoked permissions list.
DeletePatternFromRevokedUnusedSitePermissionList(info.primary_pattern,
info.secondary_pattern);
// Record the days elapsed from auto-revocation to regrant.
base::Time revoked_time =
info.metadata.expiration() -
safety_check::GetUnusedSitePermissionsRevocationCleanUpThreshold();
base::UmaHistogramCustomCounts(
"Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays",
(clock_->Now() - revoked_time).InDays(), 0,
kAllowAgainMetricsExclusiveMaxCount, kAllowAgainMetricsBuckets);
}
void UnusedSitePermissionsManager::UndoRegrantPermissionsForOrigin(
const PermissionsData& permissions_data) {
// If `permissions_data` had abusive notifications revoked, remove the
// `NOTIFICATIONS` setting from the list of permission types to handle below,
// since these were already handled. If there are no unused site permissions
// to handle below, then return. Otherwise, handle them below.
const std::set<ContentSettingsType> unused_site_permission_types =
GetRevokedUnusedSitePermissionTypes(permissions_data.permission_types);
if (unused_site_permission_types.empty()) {
return;
}
// Set this to true to prevent `OnContentSettingChanged` from removing
// revoked setting values, since this is set with specific values below.
is_unused_site_revocation_running_ = true;
for (const auto& permission : unused_site_permission_types) {
if (IsContentSetting(permission)) {
hcsm()->SetContentSettingCustomScope(
permissions_data.primary_pattern, ContentSettingsPattern::Wildcard(),
permission, ContentSetting::CONTENT_SETTING_DEFAULT);
} else if (IsChooserPermissionSupported() && IsWebsiteSetting(permission)) {
hcsm()->SetWebsiteSettingDefaultScope(
permissions_data.primary_pattern.ToRepresentativeUrl(), GURL(),
permission, base::Value());
} else {
NOTREACHED() << "Unable to find ContentSettingsType in neither "
<< "ContentSettingsRegistry nor WebsiteSettingsRegistry: "
<< ConvertContentSettingsTypeToKey(permission);
}
}
// Set this back to false, so that `OnContentSettingChanged` can cleanup
// revoked settings if necessary.
is_unused_site_revocation_running_ = false;
StorePermissionInUnusedSitePermissionSetting(
unused_site_permission_types, permissions_data.chooser_permissions_data,
permissions_data.constraints.Clone(), permissions_data.primary_pattern,
ContentSettingsPattern::Wildcard());
}
void UnusedSitePermissionsManager::
DeletePatternFromRevokedUnusedSitePermissionList(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern) {
hcsm()->SetWebsiteSettingCustomScope(
primary_pattern, secondary_pattern,
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, {});
}
void UnusedSitePermissionsManager::StorePermissionInUnusedSitePermissionSetting(
const std::set<ContentSettingsType>& permissions,
const base::Value::Dict& chooser_permissions_data,
const std::optional<content_settings::ContentSettingConstraints> constraint,
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern) {
// This method only pertains to permissions other than `NOTIFICATIONS`, since
// these permissions are not revoked for unused sites.
const std::set<ContentSettingsType>& unused_site_permission_types =
GetRevokedUnusedSitePermissionTypes(permissions);
GURL url = GURL(primary_pattern.ToString());
// The url should be valid as it is checked that the pattern represents a
// single origin.
CHECK(url.is_valid());
// Get the current value of the setting to append the recently revoked
// permissions.
base::Value cur_value(hcsm()->GetWebsiteSetting(
url, url, ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS));
base::Value::Dict dict = cur_value.is_dict() ? std::move(cur_value.GetDict())
: base::Value::Dict();
base::Value::List permission_type_list =
dict.FindList(permissions::kRevokedKey)
? std::move(*dict.FindList(permissions::kRevokedKey))
: base::Value::List();
for (const auto& permission : unused_site_permission_types) {
// Chooser permissions (not ContentSettingsRegistry-based) should have
// corresponding data to be restored in `chooser_permissions_data`.
CHECK(IsContentSetting(permission) || !IsChooserPermissionSupported() ||
chooser_permissions_data.contains(
ConvertContentSettingsTypeToKey(permission)));
permission_type_list.Append(ConvertContentSettingsTypeToKey(permission));
}
dict.Set(permissions::kRevokedKey,
base::Value::List(std::move(permission_type_list)));
if (IsChooserPermissionSupported() && !chooser_permissions_data.empty()) {
base::Value::Dict existing_chooser_permissions_data =
dict.FindDict(permissions::kRevokedChooserPermissionsKey)
? std::move(
*dict.FindDict(permissions::kRevokedChooserPermissionsKey))
: base::Value::Dict();
for (auto data : chooser_permissions_data) {
// Chooser permissions data should have its permission type included in
// `permissions` set.
CHECK(permissions.contains(ConvertKeyToContentSettingsType(data.first)));
existing_chooser_permissions_data.Set(data.first, data.second.Clone());
}
dict.Set(permissions::kRevokedChooserPermissionsKey,
base::Value::Dict(std::move(existing_chooser_permissions_data)));
}
content_settings::ContentSettingConstraints default_constraint(clock_->Now());
default_constraint.set_lifetime(safety_hub_util::GetCleanUpThreshold());
// Set website setting for the list of recently revoked permissions and
// previously revoked permissions, if exists.
hcsm()->SetWebsiteSettingCustomScope(
primary_pattern, secondary_pattern,
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
base::Value(std::move(dict)),
constraint.has_value() ? constraint.value() : default_constraint);
}
void UnusedSitePermissionsManager::SetClockForTesting(base::Clock* clock) {
clock_ = clock;
}
std::vector<ContentSettingEntry>
UnusedSitePermissionsManager::GetTrackedUnusedPermissionsForTesting() {
std::vector<ContentSettingEntry> result;
for (const auto& list : recently_unused_permissions_) {
for (const auto& entry : list.second) {
result.push_back(entry);
}
}
return result;
}
void UnusedSitePermissionsManager::IgnoreOriginForAutoRevocation(
const url::Origin& origin) {
auto* registry = content_settings::ContentSettingsRegistry::GetInstance();
for (const content_settings::ContentSettingsInfo* info : *registry) {
ContentSettingsType type = info->website_settings_info()->type();
for (const auto& setting : hcsm()->GetSettingsForOneType(type)) {
if (setting.metadata.last_visited() != base::Time() &&
setting.primary_pattern.MatchesSingleOrigin() &&
setting.primary_pattern.Matches(origin.GetURL())) {
hcsm()->ResetLastVisitedTime(setting.primary_pattern,
setting.secondary_pattern, type);
break;
}
}
}
}
void UnusedSitePermissionsManager::UpdateIntegerValuesToGroupName() {
ContentSettingsForOneType settings = hcsm()->GetSettingsForOneType(
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS);
bool successful_migration = true;
for (const auto& revoked_permissions : settings) {
const base::Value& stored_value = revoked_permissions.setting_value;
CHECK(stored_value.is_dict());
base::Value updated_dict(stored_value.Clone());
const base::Value::List* permission_value_list =
stored_value.GetDict().FindList(permissions::kRevokedKey);
if (permission_value_list) {
base::Value::List updated_permission_value_list =
ConvertContentSettingsIntValuesToString(
permission_value_list->Clone(), &successful_migration);
updated_dict.GetDict().Set(permissions::kRevokedKey,
std::move(updated_permission_value_list));
}
const base::Value::Dict* chooser_permission_value_dict =
stored_value.GetDict().FindDict(
permissions::kRevokedChooserPermissionsKey);
if (chooser_permission_value_dict) {
base::Value::Dict updated_chooser_permission_value_dict =
ConvertChooserContentSettingsIntValuesToString(
chooser_permission_value_dict->Clone());
updated_dict.GetDict().Set(
permissions::kRevokedChooserPermissionsKey,
std::move(updated_chooser_permission_value_dict));
}
// Create a new constraint with the old creation time of the original
// exception.
base::Time creation_time = revoked_permissions.metadata.expiration() -
revoked_permissions.metadata.lifetime();
content_settings::ContentSettingConstraints constraints(creation_time);
constraints.set_lifetime(revoked_permissions.metadata.lifetime());
hcsm()->SetWebsiteSettingCustomScope(
revoked_permissions.primary_pattern,
revoked_permissions.secondary_pattern,
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
std::move(updated_dict), constraints);
}
if (successful_migration) {
pref_change_registrar_->prefs()->SetBoolean(
safety_hub_prefs::kUnusedSitePermissionsRevocationMigrationCompleted,
true);
}
}