#include "extensions/browser/api/declarative_net_request/prefs_helper.h"
#include <string>
#include <string_view>
#include "base/containers/contains.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/constants.h"
#include "services/preferences/public/cpp/scoped_pref_update.h"
namespace extensions::declarative_net_request {
namespace {
constexpr std::string_view kChecksumKey = "checksum";
constexpr std::string_view kDisabledStaticRuleIds =
"dnr_disabled_static_rule_ids";
constexpr std::string_view kEnabledStaticRulesetIDs = "dnr_enabled_ruleset_ids";
constexpr std::string_view kExtensionRulesAllocated =
"dnr_extension_rules_allocated";
constexpr std::string_view kIgnoreRulesetKey = "ignore_ruleset";
constexpr std::string_view kKeepExcessAllocation = "dnr_keep_excess_allocation";
constexpr std::string_view kUseActionCountAsBadgeText =
"dnr_use_action_count_as_badge_text";
constexpr std::string_view kDynamicRulesetPref = "dnr_dynamic_ruleset";
base::flat_set<int> GetDisabledStaticRuleIdsFromDict(
const base::Value::Dict* disabled_rule_ids_dict,
RulesetID ruleset_id) {
if (!disabled_rule_ids_dict) {
return {};
}
const base::Value::List* disabled_rule_id_list =
disabled_rule_ids_dict->FindList(
base::NumberToString(ruleset_id.value()));
if (!disabled_rule_id_list) {
return {};
}
base::flat_set<int> disabled_rule_ids;
for (const base::Value& disabled_rule_id : *disabled_rule_id_list) {
if (!disabled_rule_id.is_int()) {
return {};
}
disabled_rule_ids.insert(disabled_rule_id.GetInt());
}
return disabled_rule_ids;
}
size_t CountDisabledRules(const base::Value::Dict* disabled_rule_ids_dict) {
if (!disabled_rule_ids_dict) {
return 0;
}
size_t count = 0;
for (const auto [key, value] : *disabled_rule_ids_dict) {
if (!value.is_list()) {
continue;
}
count += value.GetList().size();
}
return count;
}
bool ReadPrefAsBooleanAndReturn(const ExtensionPrefs& prefs,
const ExtensionId& extension_id,
std::string_view key) {
bool value = false;
if (prefs.ReadPrefAsBoolean(extension_id, key, &value)) {
return value;
}
return false;
}
}
PrefsHelper::PrefsHelper(ExtensionPrefs& prefs)
: prefs_(prefs) {}
PrefsHelper::~PrefsHelper() = default;
PrefsHelper::RuleIdsToUpdate::RuleIdsToUpdate(
const std::optional<std::vector<int>>& ids_to_disable,
const std::optional<std::vector<int>>& ids_to_enable) {
if (ids_to_disable) {
this->ids_to_disable.insert(ids_to_disable->begin(), ids_to_disable->end());
}
if (ids_to_enable) {
for (int id : *ids_to_enable) {
if (base::Contains(this->ids_to_disable, id)) {
continue;
}
this->ids_to_enable.insert(id);
}
}
}
PrefsHelper::RuleIdsToUpdate::RuleIdsToUpdate(
RuleIdsToUpdate&& other) = default;
PrefsHelper::RuleIdsToUpdate::~RuleIdsToUpdate() = default;
PrefsHelper::UpdateDisabledStaticRulesResult::
UpdateDisabledStaticRulesResult() = default;
PrefsHelper::UpdateDisabledStaticRulesResult::
UpdateDisabledStaticRulesResult(UpdateDisabledStaticRulesResult&& other) =
default;
PrefsHelper::UpdateDisabledStaticRulesResult::
~UpdateDisabledStaticRulesResult() = default;
const base::Value::Dict*
PrefsHelper::GetDisabledRuleIdsDict(
const ExtensionId& extension_id) const {
return prefs_->ReadPrefAsDict(
extension_id,
ExtensionPrefs::JoinPrefs(
{ExtensionPrefs::kDNRStaticRulesetPref, kDisabledStaticRuleIds}));
}
base::flat_set<int> PrefsHelper::GetDisabledStaticRuleIds(
const ExtensionId& extension_id,
RulesetID ruleset_id) const {
return GetDisabledStaticRuleIdsFromDict(GetDisabledRuleIdsDict(extension_id),
ruleset_id);
}
size_t PrefsHelper::GetDisabledStaticRuleCount(
const ExtensionId& extension_id) const {
return CountDisabledRules(GetDisabledRuleIdsDict(extension_id));
}
void PrefsHelper::SetDisabledStaticRuleIds(
const ExtensionId& extension_id,
RulesetID ruleset_id,
const base::flat_set<int>& disabled_rule_ids) {
std::string key = ExtensionPrefs::JoinPrefs(
{ExtensionPrefs::kDNRStaticRulesetPref, kDisabledStaticRuleIds});
ExtensionPrefs::ScopedDictionaryUpdate update(&*prefs_, extension_id, key);
if (disabled_rule_ids.empty()) {
std::unique_ptr<prefs::DictionaryValueUpdate> disabled_rule_ids_dict =
update.Get();
if (disabled_rule_ids_dict) {
disabled_rule_ids_dict->Remove(base::NumberToString(ruleset_id.value()));
}
return;
}
std::unique_ptr<prefs::DictionaryValueUpdate> disabled_rule_ids_dict =
update.Create();
base::Value::List ids_list;
ids_list.reserve(disabled_rule_ids.size());
for (int id : disabled_rule_ids) {
ids_list.Append(id);
}
disabled_rule_ids_dict->Set(base::NumberToString(ruleset_id.value()),
base::Value(std::move(ids_list)));
}
PrefsHelper::UpdateDisabledStaticRulesResult
PrefsHelper::UpdateDisabledStaticRules(
const ExtensionId& extension_id,
RulesetID ruleset_id,
const RuleIdsToUpdate& rule_ids_to_update) {
UpdateDisabledStaticRulesResult result;
const base::Value::Dict* disabled_rule_ids_dict =
GetDisabledRuleIdsDict(extension_id);
base::flat_set<int> old_disabled_rule_ids(
GetDisabledStaticRuleIdsFromDict(disabled_rule_ids_dict, ruleset_id));
for (int id : old_disabled_rule_ids) {
if (base::Contains(rule_ids_to_update.ids_to_enable, id)) {
result.changed = true;
continue;
}
result.disabled_rule_ids_after_update.insert(id);
}
for (int id : rule_ids_to_update.ids_to_disable) {
auto pair = result.disabled_rule_ids_after_update.insert(id);
if (pair.second) {
result.changed = true;
}
}
if (!result.changed) {
result.disabled_rule_ids_after_update.clear();
return result;
}
int count_before = old_disabled_rule_ids.size();
int count_after = result.disabled_rule_ids_after_update.size();
int new_count =
CountDisabledRules(disabled_rule_ids_dict) + count_after - count_before;
DCHECK_GE(new_count, 0);
if (new_count > GetDisabledStaticRuleLimit()) {
result.error = kDisabledStaticRuleCountExceeded;
result.changed = false;
result.disabled_rule_ids_after_update.clear();
return result;
}
SetDisabledStaticRuleIds(extension_id, ruleset_id,
result.disabled_rule_ids_after_update);
return result;
}
bool PrefsHelper::GetStaticRulesetChecksum(
const ExtensionId& extension_id,
declarative_net_request::RulesetID ruleset_id,
int& checksum) const {
std::string pref = ExtensionPrefs::JoinPrefs(
{ExtensionPrefs::kDNRStaticRulesetPref,
base::NumberToString(ruleset_id.value()), kChecksumKey});
return prefs_->ReadPrefAsInteger(extension_id, pref, &checksum);
}
void PrefsHelper::SetStaticRulesetChecksum(
const ExtensionId& extension_id,
declarative_net_request::RulesetID ruleset_id,
int checksum) {
std::string pref = ExtensionPrefs::JoinPrefs(
{ExtensionPrefs::kDNRStaticRulesetPref,
base::NumberToString(ruleset_id.value()), kChecksumKey});
prefs_->UpdateExtensionPref(extension_id, pref, base::Value(checksum));
}
bool PrefsHelper::GetDynamicRulesetChecksum(const ExtensionId& extension_id,
int& checksum) const {
std::string pref =
ExtensionPrefs::JoinPrefs({kDynamicRulesetPref, kChecksumKey});
return prefs_->ReadPrefAsInteger(extension_id, pref, &checksum);
}
void PrefsHelper::SetDynamicRulesetChecksum(const ExtensionId& extension_id,
int checksum) {
std::string pref =
ExtensionPrefs::JoinPrefs({kDynamicRulesetPref, kChecksumKey});
prefs_->UpdateExtensionPref(extension_id, pref, base::Value(checksum));
}
std::optional<std::set<RulesetID>> PrefsHelper::GetEnabledStaticRulesets(
const ExtensionId& extension_id) const {
std::set<RulesetID> ids;
const base::Value::List* ids_value =
prefs_->ReadPrefAsList(extension_id, kEnabledStaticRulesetIDs);
if (!ids_value) {
return std::nullopt;
}
for (const base::Value& id_value : *ids_value) {
if (!id_value.is_int()) {
return std::nullopt;
}
ids.insert(RulesetID(id_value.GetInt()));
}
return ids;
}
void PrefsHelper::SetEnabledStaticRulesets(const ExtensionId& extension_id,
const std::set<RulesetID>& ids) {
base::Value::List ids_list;
for (const auto& id : ids) {
ids_list.Append(id.value());
}
prefs_->UpdateExtensionPref(extension_id, kEnabledStaticRulesetIDs,
base::Value(std::move(ids_list)));
}
bool PrefsHelper::GetUseActionCountAsBadgeText(
const ExtensionId& extension_id) const {
return ReadPrefAsBooleanAndReturn(*prefs_, extension_id,
kUseActionCountAsBadgeText);
}
void PrefsHelper::SetUseActionCountAsBadgeText(
const ExtensionId& extension_id,
bool use_action_count_as_badge_text) {
prefs_->UpdateExtensionPref(extension_id, kUseActionCountAsBadgeText,
base::Value(use_action_count_as_badge_text));
}
bool PrefsHelper::ShouldIgnoreRuleset(const ExtensionId& extension_id,
RulesetID ruleset_id) const {
std::string pref = ExtensionPrefs::JoinPrefs(
{ExtensionPrefs::kDNRStaticRulesetPref,
base::NumberToString(ruleset_id.value()), kIgnoreRulesetKey});
return ReadPrefAsBooleanAndReturn(*prefs_, extension_id, pref);
}
bool PrefsHelper::GetAllocatedGlobalRuleCount(const ExtensionId& extension_id,
int& rule_count) const {
if (!prefs_->ReadPrefAsInteger(extension_id, kExtensionRulesAllocated,
&rule_count)) {
return false;
}
DCHECK_GT(rule_count, 0);
return true;
}
void PrefsHelper::SetAllocatedGlobalRuleCount(const ExtensionId& extension_id,
int rule_count) {
DCHECK_LE(rule_count, GetGlobalStaticRuleLimit());
std::optional<base::Value> pref_value;
if (rule_count > 0) {
pref_value = base::Value(rule_count);
}
prefs_->UpdateExtensionPref(extension_id, kExtensionRulesAllocated,
std::move(pref_value));
}
bool PrefsHelper::GetKeepExcessAllocation(
const ExtensionId& extension_id) const {
return ReadPrefAsBooleanAndReturn(*prefs_, extension_id,
kKeepExcessAllocation);
}
void PrefsHelper::SetKeepExcessAllocation(const ExtensionId& extension_id,
bool keep_excess_allocation) {
std::optional<base::Value> pref_value;
if (keep_excess_allocation) {
pref_value = base::Value(true);
}
prefs_->UpdateExtensionPref(extension_id, kKeepExcessAllocation,
std::move(pref_value));
}
}