blob: 8b519016629e94e9c74c701c176aceee0296f47e [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
#include "base/memory/ptr_util.h"
namespace blacklist {
BlacklistData::BlacklistData(std::unique_ptr<Policy> session_policy,
std::unique_ptr<Policy> persistent_policy,
std::unique_ptr<Policy> host_policy,
std::unique_ptr<Policy> type_policy,
size_t max_hosts,
AllowedTypesAndVersions allowed_types)
: session_policy_(std::move(session_policy)),
persistent_policy_(std::move(persistent_policy)),
host_policy_(std::move(host_policy)),
max_hosts_(max_hosts),
type_policy_(std::move(type_policy)),
allowed_types_(std::move(allowed_types)) {
DCHECK_GE(100u, max_hosts);
}
BlacklistData::~BlacklistData() {}
void BlacklistData::ClearData() {
session_black_list_item_.reset();
persistent_black_list_item_.reset();
black_list_item_host_map_.clear();
black_list_item_type_map_.clear();
}
void BlacklistData::AddEntry(const std::string& host_name,
bool opt_out,
int type,
base::Time time,
bool is_from_persistent_storage) {
// Add to the session based rule if it is enabled.
if (session_policy_ && !is_from_persistent_storage) {
if (!session_black_list_item_) {
session_black_list_item_ = std::make_unique<OptOutBlacklistItem>(
session_policy_->history, session_policy_->threshold,
session_policy_->duration);
}
session_black_list_item_->AddEntry(opt_out, time);
}
// Add to the persistent rule if it is enabled.
if (persistent_policy_) {
if (!persistent_black_list_item_) {
persistent_black_list_item_ = std::make_unique<OptOutBlacklistItem>(
persistent_policy_->history, persistent_policy_->threshold,
persistent_policy_->duration);
}
persistent_black_list_item_->AddEntry(opt_out, time);
}
// Add to the host rule if it is enabled. Remove hosts if there are more than
// |max_hosts_| in the map.
if (host_policy_) {
auto item = black_list_item_host_map_.find(host_name);
if (item == black_list_item_host_map_.end()) {
auto value = black_list_item_host_map_.emplace(
std::piecewise_construct, std::forward_as_tuple(host_name),
std::forward_as_tuple(host_policy_->history, host_policy_->threshold,
host_policy_->duration));
DCHECK(value.second);
item = value.first;
}
item->second.AddEntry(opt_out, time);
if (max_hosts_ > 0 && black_list_item_host_map_.size() > max_hosts_)
EvictOldestHost();
}
// Only allowed types should be recorded.
DCHECK(allowed_types_.find(type) != allowed_types_.end());
if (type_policy_) {
auto item = black_list_item_type_map_.find(type);
if (item == black_list_item_type_map_.end()) {
auto value = black_list_item_type_map_.emplace(
std::piecewise_construct, std::forward_as_tuple(type),
std::forward_as_tuple(type_policy_->history, type_policy_->threshold,
type_policy_->duration));
DCHECK(value.second);
item = value.first;
}
item->second.AddEntry(opt_out, time);
}
}
BlacklistReason BlacklistData::IsAllowed(
const std::string& host_name,
int type,
bool ignore_long_term_black_list_rules,
base::Time time,
std::vector<BlacklistReason>* passed_reasons) const {
// Check the session rule.
if (session_policy_) {
if (session_black_list_item_ &&
session_black_list_item_->IsBlackListed(time)) {
return BlacklistReason::kUserOptedOutInSession;
}
passed_reasons->push_back(BlacklistReason::kUserOptedOutInSession);
}
// Check whether the persistent rules should be checked this time.
if (ignore_long_term_black_list_rules)
return BlacklistReason::kAllowed;
// Check the persistent rule.
if (persistent_policy_) {
if (IsUserOptedOutInGeneral(time)) {
return BlacklistReason::kUserOptedOutInGeneral;
}
passed_reasons->push_back(BlacklistReason::kUserOptedOutInGeneral);
}
// Check the host rule.
if (host_policy_) {
if (IsHostBlacklisted(host_name, time))
return BlacklistReason::kUserOptedOutOfHost;
passed_reasons->push_back(BlacklistReason::kUserOptedOutOfHost);
}
// Only allowed types should be recorded.
DCHECK(allowed_types_.find(type) != allowed_types_.end());
// Check the type rule.
if (type_policy_) {
auto item = black_list_item_type_map_.find(type);
if (item != black_list_item_type_map_.end() &&
item->second.IsBlackListed(time)) {
return BlacklistReason::kUserOptedOutOfType;
}
passed_reasons->push_back(BlacklistReason::kUserOptedOutOfType);
}
return BlacklistReason::kAllowed;
}
void BlacklistData::EvictOldestHost() {
DCHECK_LT(max_hosts_, black_list_item_host_map_.size());
base::Optional<base::Time> oldest_opt_out;
std::string key_to_delete;
for (auto& item : black_list_item_host_map_) {
base::Optional<base::Time> most_recent_opt_out =
item.second.most_recent_opt_out_time();
if (!most_recent_opt_out) {
// If there is no opt out time, this is a good choice to evict.
key_to_delete = item.first;
break;
}
if (!oldest_opt_out ||
most_recent_opt_out.value() < oldest_opt_out.value()) {
oldest_opt_out = most_recent_opt_out.value();
key_to_delete = item.first;
}
}
black_list_item_host_map_.erase(key_to_delete);
}
bool BlacklistData::IsHostBlacklisted(const std::string& host_name,
base::Time time) const {
auto item = black_list_item_host_map_.find(host_name);
return item != black_list_item_host_map_.end() &&
item->second.IsBlackListed(time);
}
bool BlacklistData::IsUserOptedOutInGeneral(base::Time time) const {
return persistent_black_list_item_ &&
persistent_black_list_item_->IsBlackListed(time);
}
} // namespace blacklist