blob: ba4a43c204152b1b54051f2a6bd159a55101a8af [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.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
#include "base/optional.h"
#include "base/strings/stringprintf.h"
#include "base/time/clock.h"
#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
namespace blacklist {
OptOutBlacklist::OptOutBlacklist(std::unique_ptr<OptOutStore> opt_out_store,
base::Clock* clock,
OptOutBlacklistDelegate* blacklist_delegate)
: loaded_(false),
opt_out_store_(std::move(opt_out_store)),
clock_(clock),
blacklist_delegate_(blacklist_delegate),
weak_factory_(this) {
DCHECK(clock_);
DCHECK(blacklist_delegate_);
}
OptOutBlacklist::~OptOutBlacklist() = default;
void OptOutBlacklist::Init() {
DCHECK(!loaded_);
DCHECK(!blacklist_data_);
base::TimeDelta duration;
size_t history = 0;
int threshold = 0;
std::unique_ptr<BlacklistData::Policy> session_policy;
if (ShouldUseSessionPolicy(&duration, &history, &threshold)) {
session_policy =
std::make_unique<BlacklistData::Policy>(duration, history, threshold);
}
std::unique_ptr<BlacklistData::Policy> persistent_policy;
if (ShouldUsePersistentPolicy(&duration, &history, &threshold)) {
persistent_policy =
std::make_unique<BlacklistData::Policy>(duration, history, threshold);
}
size_t max_hosts = 0;
std::unique_ptr<BlacklistData::Policy> host_policy;
if (ShouldUseHostPolicy(&duration, &history, &threshold, &max_hosts)) {
host_policy =
std::make_unique<BlacklistData::Policy>(duration, history, threshold);
}
std::unique_ptr<BlacklistData::Policy> type_policy;
if (ShouldUseTypePolicy(&duration, &history, &threshold)) {
type_policy =
std::make_unique<BlacklistData::Policy>(duration, history, threshold);
}
auto blacklist_data = std::make_unique<BlacklistData>(
std::move(session_policy), std::move(persistent_policy),
std::move(host_policy), std::move(type_policy), max_hosts,
GetAllowedTypes());
if (opt_out_store_) {
opt_out_store_->LoadBlackList(
std::move(blacklist_data),
base::BindOnce(&OptOutBlacklist::LoadBlackListDone,
weak_factory_.GetWeakPtr()));
} else {
LoadBlackListDone(std::move(blacklist_data));
}
}
base::Time OptOutBlacklist::AddEntry(const std::string& host_name,
bool opt_out,
int type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Time now = clock_->Now();
// If the |blacklist_data| has been loaded from |opt_out_store_|, synchronous
// operations will be accurate. Otherwise, queue the task to run
// asynchronously.
if (loaded_) {
AddEntrySync(host_name, opt_out, type, now);
} else {
QueuePendingTask(base::BindOnce(&OptOutBlacklist::AddEntrySync,
base::Unretained(this), host_name, opt_out,
type, now));
}
return now;
}
void OptOutBlacklist::AddEntrySync(const std::string& host_name,
bool opt_out,
int type,
base::Time time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded_);
bool host_was_blacklisted =
blacklist_data_->IsHostBlacklisted(host_name, time);
bool user_was_blacklisted = blacklist_data_->IsUserOptedOutInGeneral(time);
blacklist_data_->AddEntry(host_name, opt_out, type, time, false);
if (!host_was_blacklisted &&
blacklist_data_->IsHostBlacklisted(host_name, time)) {
// Notify |blacklist_delegate_| about a new blacklisted host.
blacklist_delegate_->OnNewBlacklistedHost(host_name, time);
}
if (user_was_blacklisted != blacklist_data_->IsUserOptedOutInGeneral(time)) {
// Notify |blacklist_delegate_| about a new blacklisted host.
blacklist_delegate_->OnUserBlacklistedStatusChange(
blacklist_data_->IsUserOptedOutInGeneral(time));
}
if (!opt_out_store_)
return;
opt_out_store_->AddEntry(opt_out, host_name, type, time);
}
BlacklistReason OptOutBlacklist::IsLoadedAndAllowed(
const std::string& host_name,
int type,
bool ignore_long_term_black_list_rules,
std::vector<BlacklistReason>* passed_reasons) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!loaded_)
return BlacklistReason::kBlacklistNotLoaded;
passed_reasons->push_back(BlacklistReason::kBlacklistNotLoaded);
return blacklist_data_->IsAllowed(host_name, type,
ignore_long_term_black_list_rules,
clock_->Now(), passed_reasons);
}
void OptOutBlacklist::ClearBlackList(base::Time begin_time,
base::Time end_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_LE(begin_time, end_time);
// If the |blacklist_data| has been loaded from |opt_out_store_|,
// synchronous operations will be accurate. Otherwise, queue the task to run
// asynchronously.
if (loaded_) {
ClearBlackListSync(begin_time, end_time);
} else {
QueuePendingTask(base::BindOnce(&OptOutBlacklist::ClearBlackListSync,
base::Unretained(this), begin_time,
end_time));
}
}
void OptOutBlacklist::ClearBlackListSync(base::Time begin_time,
base::Time end_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded_);
DCHECK_LE(begin_time, end_time);
// Clear the in-memory rules entirely.
blacklist_data_->ClearData();
loaded_ = false;
// Notify |blacklist_delegate_| that the blacklist is cleared.
blacklist_delegate_->OnBlacklistCleared(clock_->Now());
// Delete relevant entries and reload the blacklist into memory.
if (opt_out_store_) {
opt_out_store_->ClearBlackList(begin_time, end_time);
opt_out_store_->LoadBlackList(
std::move(blacklist_data_),
base::BindOnce(&OptOutBlacklist::LoadBlackListDone,
weak_factory_.GetWeakPtr()));
} else {
LoadBlackListDone(std::move(blacklist_data_));
}
}
void OptOutBlacklist::QueuePendingTask(base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!loaded_);
DCHECK(!callback.is_null());
pending_callbacks_.push(std::move(callback));
}
void OptOutBlacklist::LoadBlackListDone(
std::unique_ptr<BlacklistData> blacklist_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(blacklist_data);
DCHECK(!loaded_);
DCHECK(!blacklist_data_);
loaded_ = true;
blacklist_data_ = std::move(blacklist_data);
// Notify |blacklist_delegate_| on current user blacklisted status.
blacklist_delegate_->OnUserBlacklistedStatusChange(
blacklist_data_->IsUserOptedOutInGeneral(clock_->Now()));
// Notify the |blacklist_delegate_| on historical blacklisted hosts.
for (const auto& entry : blacklist_data_->black_list_item_host_map()) {
if (blacklist_data_->IsHostBlacklisted(entry.first, clock_->Now())) {
blacklist_delegate_->OnNewBlacklistedHost(
entry.first, entry.second.most_recent_opt_out_time().value());
}
}
// Run all pending tasks. |loaded_| may change if ClearBlackList is queued.
while (pending_callbacks_.size() > 0 && loaded_) {
std::move(pending_callbacks_.front()).Run();
pending_callbacks_.pop();
}
}
} // namespace blacklist