| // 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 |