Commit d0de86eb authored by Ryan Sturm's avatar Ryan Sturm Committed by Commit Bot

Refactoring parts of the PreviewsBlackList

This CL starts to refactor the PreviewsBlacklist.

The goal is to split Blacklist into it's own component and implement a
PreviewsBlackList as a derived class.

This CL creates core methods and Previews specific methods that can
easily be moved into seperate classes. Further, this cleans the
Blacklist  <-> OptOutStore API to pass a single object that supports
various blacklisting rules. This allows consumers to write their own
OptOutStore (or Blacklist) implementation, while getting all of the
functionality of the Blacklist.

There are TODO's identifying functionality in PreviewsBlackList that
will move to a seperate class (base class) or stay in
PreviewsBlackList (derived class).

Bug: 852593
Change-Id: I684ccb6b1a1b9d0838d34e2db36a8f870aa76fd9
Reviewed-on: https://chromium-review.googlesource.com/1100085
Commit-Queue: Ryan Sturm <ryansturm@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568130}
parent 437b870e
......@@ -44,6 +44,7 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/previews/content/previews_io_data.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_features.h"
#include "components/previews/core/previews_logger.h"
......@@ -201,7 +202,8 @@ class PreviewsInfoBarDelegateUnitTest
previews_ui_service_ = std::make_unique<previews::PreviewsUIService>(
previews_io_data_.get(), base::MessageLoopCurrent::Get()->task_runner(),
nullptr /* previews_opt_out_store */, nullptr /* previews_opt_guide */,
base::Bind(&IsPreviewsEnabled), std::move(previews_logger));
base::BindRepeating(&IsPreviewsEnabled), std::move(previews_logger),
previews::BlacklistData::AllowedTypesAndVersions());
base::RunLoop().RunUntilIdle();
}
......
......@@ -15,6 +15,7 @@
#include "components/previews/content/previews_io_data.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
#include "components/previews/core/previews_opt_out_store.h"
......@@ -75,16 +76,15 @@ int GetPreviewsTypeVersion(previews::PreviewsType type) {
}
// Returns the enabled PreviewsTypes with their version.
std::unique_ptr<previews::PreviewsTypeList> GetEnabledPreviews() {
std::unique_ptr<previews::PreviewsTypeList> enabled_previews(
new previews::PreviewsTypeList());
previews::BlacklistData::AllowedTypesAndVersions GetAllowedPreviews() {
previews::BlacklistData::AllowedTypesAndVersions enabled_previews;
// Loop across all previews types (relies on sequential enum values).
for (int i = static_cast<int>(previews::PreviewsType::NONE) + 1;
i < static_cast<int>(previews::PreviewsType::LAST); ++i) {
previews::PreviewsType type = static_cast<previews::PreviewsType>(i);
if (IsPreviewsTypeEnabled(type))
enabled_previews->push_back({type, GetPreviewsTypeVersion(type)});
enabled_previews.insert({i, GetPreviewsTypeVersion(type)});
}
return enabled_previews;
}
......@@ -115,12 +115,11 @@ void PreviewsService::Initialize(
previews_io_data, io_task_runner,
std::make_unique<previews::PreviewsOptOutStoreSQL>(
io_task_runner, background_task_runner,
profile_path.Append(chrome::kPreviewsOptOutDBFilename),
GetEnabledPreviews()),
profile_path.Append(chrome::kPreviewsOptOutDBFilename)),
optimization_guide_service
? std::make_unique<previews::PreviewsOptimizationGuide>(
optimization_guide_service, io_task_runner)
: nullptr,
base::Bind(&IsPreviewsTypeEnabled),
std::make_unique<previews::PreviewsLogger>());
std::make_unique<previews::PreviewsLogger>(), GetAllowedPreviews());
}
......@@ -19,6 +19,7 @@
#include "components/previews/content/previews_io_data.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_features.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
......@@ -42,7 +43,9 @@ class TestPreviewsIOData : public previews::PreviewsIOData {
base::WeakPtr<previews::PreviewsUIService> previews_ui_service,
std::unique_ptr<previews::PreviewsOptOutStore> previews_opt_out_store,
std::unique_ptr<previews::PreviewsOptimizationGuide> previews_opt_guide,
const previews::PreviewsIsEnabledCallback& is_enabled_callback) override {
const previews::PreviewsIsEnabledCallback& is_enabled_callback,
previews::BlacklistData::AllowedTypesAndVersions allowed_previews)
override {
enabled_callback_ = is_enabled_callback;
}
......
......@@ -31,6 +31,7 @@
#include "chrome/test/base/testing_profile_manager.h"
#include "components/previews/content/previews_io_data.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_features.h"
#include "components/previews/core/previews_logger.h"
#include "components/previews/core/previews_logger_observer.h"
......@@ -220,7 +221,8 @@ class TestPreviewsIOData : public previews::PreviewsIOData {
base::WeakPtr<previews::PreviewsUIService> previews_ui_service,
std::unique_ptr<previews::PreviewsOptOutStore> opt_out_store,
std::unique_ptr<previews::PreviewsOptimizationGuide> previews_opt_guide,
const previews::PreviewsIsEnabledCallback& is_enabled_callback) override {
const previews::PreviewsIsEnabledCallback& is_enabled_callback,
previews::BlacklistData::AllowedTypesAndVersions allowed_types) override {
// Do nothing.
}
};
......@@ -235,7 +237,8 @@ class TestPreviewsUIService : public previews::PreviewsUIService {
nullptr, /* previews_opt_out_store */
nullptr, /* previews_opt_guide */
base::Bind(&MockedPreviewsIsEnabled),
std::move(logger)),
std::move(logger),
previews::BlacklistData::AllowedTypesAndVersions()),
blacklist_ignored_(false) {}
~TestPreviewsUIService() override {}
......
......@@ -103,7 +103,8 @@ void PreviewsIOData::Initialize(
base::WeakPtr<PreviewsUIService> previews_ui_service,
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback) {
const PreviewsIsEnabledCallback& is_enabled_callback,
BlacklistData::AllowedTypesAndVersions allowed_previews) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
is_enabled_callback_ = is_enabled_callback;
previews_ui_service_ = previews_ui_service;
......@@ -111,9 +112,10 @@ void PreviewsIOData::Initialize(
// Set up the IO thread portion of |this|.
io_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PreviewsIOData::InitializeOnIOThread,
base::Unretained(this),
std::move(previews_opt_out_store)));
FROM_HERE,
base::BindOnce(&PreviewsIOData::InitializeOnIOThread,
base::Unretained(this), std::move(previews_opt_out_store),
std::move(allowed_previews)));
}
void PreviewsIOData::OnNewBlacklistedHost(const std::string& host,
......@@ -139,11 +141,12 @@ void PreviewsIOData::OnBlacklistCleared(base::Time time) {
}
void PreviewsIOData::InitializeOnIOThread(
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store) {
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
BlacklistData::AllowedTypesAndVersions allowed_previews) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
previews_black_list_.reset(
new PreviewsBlackList(std::move(previews_opt_out_store),
base::DefaultClock::GetInstance(), this));
previews_black_list_.reset(new PreviewsBlackList(
std::move(previews_opt_out_store), base::DefaultClock::GetInstance(),
this, std::move(allowed_previews)));
ui_task_runner_->PostTask(
FROM_HERE, base::Bind(&PreviewsUIService::SetIOData, previews_ui_service_,
weak_factory_.GetWeakPtr()));
......
......@@ -18,6 +18,7 @@
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_black_list_delegate.h"
#include "components/previews/core/previews_decider.h"
......@@ -59,7 +60,8 @@ class PreviewsIOData : public PreviewsDecider,
base::WeakPtr<PreviewsUIService> previews_ui_service,
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback);
const PreviewsIsEnabledCallback& is_enabled_callback,
BlacklistData::AllowedTypesAndVersions allowed_previews);
// Adds log message of the navigation asynchronously.
void LogPreviewNavigation(const GURL& url,
......@@ -120,7 +122,8 @@ class PreviewsIOData : public PreviewsDecider,
// Posts a task to SetIOData for |previews_ui_service_| on the UI thread with
// a weak pointer to |this|. Virtualized for testing.
virtual void InitializeOnIOThread(
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store);
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
BlacklistData::AllowedTypesAndVersions allowed_previews);
// Sets a blacklist for testing.
void SetPreviewsBlacklistForTesting(
......
......@@ -28,6 +28,7 @@
#include "base/time/time.h"
#include "components/optimization_guide/optimization_guide_service.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_black_list_delegate.h"
#include "components/previews/core/previews_black_list_item.h"
......@@ -86,7 +87,8 @@ class TestPreviewsBlackList : public PreviewsBlackList {
PreviewsBlacklistDelegate* blacklist_delegate)
: PreviewsBlackList(nullptr,
base::DefaultClock::GetInstance(),
blacklist_delegate),
blacklist_delegate,
{}),
status_(status) {}
~TestPreviewsBlackList() override {}
......@@ -145,13 +147,15 @@ class TestPreviewsUIService : public PreviewsUIService {
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback,
std::unique_ptr<PreviewsLogger> logger)
std::unique_ptr<PreviewsLogger> logger,
BlacklistData::AllowedTypesAndVersions allowed_types)
: PreviewsUIService(previews_io_data,
io_task_runner,
std::move(previews_opt_out_store),
std::move(previews_opt_guide),
is_enabled_callback,
std::move(logger)),
std::move(logger),
std::move(allowed_types)),
user_blacklisted_(false),
blacklist_ignored_(false) {}
......@@ -285,21 +289,20 @@ class TestPreviewsIOData : public PreviewsIOData {
private:
// Set |initialized_| to true and use base class functionality.
void InitializeOnIOThread(
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store) override {
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
BlacklistData::AllowedTypesAndVersions allowed_previews) override {
initialized_ = true;
PreviewsIOData::InitializeOnIOThread(std::move(previews_opt_out_store));
PreviewsIOData::InitializeOnIOThread(std::move(previews_opt_out_store),
std::move(allowed_previews));
}
// Whether Initialize was called.
bool initialized_;
};
void RunLoadCallback(
LoadBlackListCallback callback,
std::unique_ptr<BlackListItemMap> black_list_item_map,
std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item) {
callback.Run(std::move(black_list_item_map),
std::move(host_indifferent_black_list_item));
void RunLoadCallback(LoadBlackListCallback callback,
std::unique_ptr<BlacklistData> data) {
std::move(callback).Run(std::move(data));
}
class TestPreviewsOptOutStore : public PreviewsOptOutStore {
......@@ -309,20 +312,16 @@ class TestPreviewsOptOutStore : public PreviewsOptOutStore {
private:
// PreviewsOptOutStore implementation:
void AddPreviewNavigation(bool opt_out,
const std::string& host_name,
PreviewsType type,
base::Time now) override {}
void AddEntry(bool opt_out,
const std::string& host_name,
int type,
base::Time now) override {}
void LoadBlackList(LoadBlackListCallback callback) override {
std::unique_ptr<BlackListItemMap> black_list_item_map(
new BlackListItemMap());
std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item =
PreviewsBlackList::CreateHostIndifferentBlackListItem();
void LoadBlackList(std::unique_ptr<BlacklistData> data,
LoadBlackListCallback callback) override {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RunLoadCallback, callback,
std::move(black_list_item_map),
std::move(host_indifferent_black_list_item)));
FROM_HERE,
base::BindOnce(&RunLoadCallback, std::move(callback), std::move(data)));
}
void ClearBlackList(base::Time begin_time, base::Time end_time) override {}
......@@ -358,6 +357,12 @@ class PreviewsIODataTest : public testing::Test {
}
void InitializeUIServiceWithoutWaitingForBlackList() {
BlacklistData::AllowedTypesAndVersions allowed_types;
allowed_types[static_cast<int>(PreviewsType::OFFLINE)] = 0;
allowed_types[static_cast<int>(PreviewsType::LOFI)] = 0;
allowed_types[static_cast<int>(PreviewsType::LITE_PAGE)] = 0;
allowed_types[static_cast<int>(PreviewsType::NOSCRIPT)] = 0;
allowed_types[static_cast<int>(PreviewsType::AMP_REDIRECTION)] = 0;
ui_service_.reset(new TestPreviewsUIService(
io_data_.get(), scoped_task_environment_.GetMainThreadTaskRunner(),
std::make_unique<TestPreviewsOptOutStore>(),
......@@ -365,7 +370,7 @@ class PreviewsIODataTest : public testing::Test {
&optimization_guide_service_,
scoped_task_environment_.GetMainThreadTaskRunner()),
base::Bind(&IsPreviewFieldTrialEnabled),
std::make_unique<PreviewsLogger>()));
std::make_unique<PreviewsLogger>(), std::move(allowed_types)));
}
void InitializeUIService() {
......
......@@ -16,14 +16,16 @@ PreviewsUIService::PreviewsUIService(
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback,
std::unique_ptr<PreviewsLogger> logger)
std::unique_ptr<PreviewsLogger> logger,
BlacklistData::AllowedTypesAndVersions allowed_previews)
: io_task_runner_(io_task_runner),
logger_(std::move(logger)),
weak_factory_(this) {
DCHECK(logger_);
previews_io_data->Initialize(
weak_factory_.GetWeakPtr(), std::move(previews_opt_out_store),
std::move(previews_opt_guide), is_enabled_callback);
std::move(previews_opt_guide), is_enabled_callback,
std::move(allowed_previews));
}
PreviewsUIService::~PreviewsUIService() {
......
......@@ -14,6 +14,7 @@
#include "base/time/time.h"
#include "components/previews/content/previews_io_data.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
......@@ -38,7 +39,8 @@ class PreviewsUIService {
std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback,
std::unique_ptr<PreviewsLogger> logger);
std::unique_ptr<PreviewsLogger> logger,
BlacklistData::AllowedTypesAndVersions allowed_previews);
virtual ~PreviewsUIService();
// Sets |io_data_| to |io_data| to allow calls from the UI thread to the IO
......
......@@ -11,6 +11,7 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "components/previews/content/previews_io_data.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
......@@ -33,7 +34,8 @@ class TestPreviewsUIService : public PreviewsUIService {
std::move(previews_opt_out_store),
std::move(previews_opt_guide),
PreviewsIsEnabledCallback(),
std::move(logger)),
std::move(logger),
BlacklistData::AllowedTypesAndVersions()),
io_data_set_(false) {}
~TestPreviewsUIService() override {}
......
......@@ -4,6 +4,8 @@
static_library("core") {
sources = [
"blacklist_data.cc",
"blacklist_data.h",
"previews_amp_converter.cc",
"previews_amp_converter.h",
"previews_black_list.cc",
......
// 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/previews/core/blacklist_data.h"
#include "base/memory/ptr_util.h"
namespace previews {
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<PreviewsBlackListItem>(
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<PreviewsBlackListItem>(
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 previews
// 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.
#ifndef COMPONENTS_PREVIEWS_CORE_BLACKLIST_DATA_H_
#define COMPONENTS_PREVIEWS_CORE_BLACKLIST_DATA_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "components/previews/core/previews_black_list_item.h"
namespace previews {
// The various reasons the Blacklist may tell that the user is blacklisted.
enum class BlacklistReason {
// The blacklist may not be loaded very early in the session or when the user
// has cleared the blacklist history (usually by clearing their browsing
// history).
kBlacklistNotLoaded,
kUserOptedOutInSession,
kUserOptedOutInGeneral,
kUserOptedOutOfHost,
kUserOptedOutOfType,
kAllowed
};
// This class describes all of the data used to determine whether an action is
// allowed based on four possible rules: Session: if the user has opted out
// of j of the last k entries this session, the action will be blacklisted for a
// set duration. Persistent: if the user has opted out of j of the last k
// entries, the action will be blacklisted for a set duration. Host: if the user
// has opted out of threshold of the last history entries for a specific host,
// the action will be blacklisted for a set duration. Type: if the user has
// opted out of j of the last k entries for a specific type, the action will be
// blacklisted for a set duration. This is the in-memory version of the black
// list policy. This object is moved from the embedder thread to a background
// thread, It is not safe to access concurrently on two threads.
class BlacklistData {
public:
// A struct describing the general blacklisting pattern used by all of the
// blacklisting rules.
// The most recent |history| entries are looked at and if |threshold| (or
// more) of them are opt outs, new actions are considered blacklisted unless
// the most recent opt out was longer than |duration| ago.
struct Policy {
Policy(base::TimeDelta duration, size_t history, int threshold)
: duration(duration), history(history), threshold(threshold) {}
~Policy() = default;
// Specifies how long the blacklisting rule lasts after the most recent opt
// out.
const base::TimeDelta duration;
// Amount of entries evaluated for the rule.
const size_t history;
// The number of opt outs that will trigger blacklisting for the rule.
const int threshold;
};
// A map of types that are allowed to be used in the blacklist as well as the
// version that those types are in. Versioning allows removals from persistent
// memory at session start.
using AllowedTypesAndVersions = std::map<int, int>;
// |session_policy| if non-null, is the policy that is not persisted across
// sessions and is not specific to host or type. |persistent_policy| if
// non-null, is the policy that is persisted across sessions and is not
// specific to host or type. |host_policy| if non-null, is the policy that is
// persisted across sessions applies at the per-host level. |host_policy| if
// non-null, is the policy that is persisted across sessions and applies at
// the per-type level. |max_hosts| is the maximum number of hosts stored in
// memory. |allowed_types| contains the action types that are allowed in the
// session and their corresponding versions. Conversioning is used to clear
// stale data from the persistent storage.
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);
~BlacklistData();
// Adds a new entry for all rules to use when evaluating blacklisting state.
// |is_from_persistent_storage| is used to delineate between data added from
// this session, and previous sessions.
void AddEntry(const std::string& host_name,
bool opt_out,
int type,
base::Time time,
bool is_from_persistent_storage);
// Whether the user is opted out when considering all enabled rules. if
// |ignore_long_term_black_list_rules| is true, this will only check the
// session rule. For every reason that is checked, but does not trigger
// blacklisting, a new reason will be appended to the end |passed_reasons|.
// |time| is the time that decision should be evaluated at (usually now).
BlacklistReason IsAllowed(const std::string& host_name,
int type,
bool ignore_long_term_black_list_rules,
base::Time time,
std::vector<BlacklistReason>* passed_reasons) const;
// This clears all data in all rules.
void ClearData();
// The allowed types and what version they are. If it is non-empty, it is used
// to remove stale entries from the database and to DCHECK that other methods
// are not using disallowed types.
const AllowedTypesAndVersions& allowed_types() const {
return allowed_types_;
}
// Whether the specific |host_name| is blacklisted based only on the host
// rule.
bool IsHostBlacklisted(const std::string& host_name, base::Time time) const;
// Whether the user is opted out based solely on the persistent blacklist
// rule.
bool IsUserOptedOutInGeneral(base::Time time) const;
// Exposed for logging purposes only.
const std::map<std::string, PreviewsBlackListItem>& black_list_item_host_map()
const {
return black_list_item_host_map_;
}
private:
// Removes the oldest (or safest) host item from |black_list_item_host_map_|.
// Oldest is defined by most recent opt out time, and safest is defined as an
// item with no opt outs.
void EvictOldestHost();
// The session rule policy. If non-null the session rule is enforced.
std::unique_ptr<Policy> session_policy_;
// The session rule history.
std::unique_ptr<PreviewsBlackListItem> session_black_list_item_;
// The persistent rule policy. If non-null the persistent rule is enforced.
std::unique_ptr<Policy> persistent_policy_;
// The persistent rule history.
std::unique_ptr<PreviewsBlackListItem> persistent_black_list_item_;
// The host rule policy. If non-null the host rule is enforced.
std::unique_ptr<Policy> host_policy_;
// The maximum number of hosts allowed in the host blacklist.
size_t max_hosts_;
// The host rule history. Each host is stored as a separate blacklist history.
std::map<std::string, PreviewsBlackListItem> black_list_item_host_map_;
// The type rule policy. If non-null the type rule is enforced.
std::unique_ptr<Policy> type_policy_;
// The type rule history. Each type is stored as a separate blacklist history.
std::map<int, PreviewsBlackListItem> black_list_item_type_map_;
// The allowed types and what version they are. If it is non-empty, it is used
// to remove stale entries from the database and to DCHECK that other methods
// are not using disallowed types.
AllowedTypesAndVersions allowed_types_;
DISALLOW_COPY_AND_ASSIGN(BlacklistData);
};
} // namespace previews
#endif // COMPONENTS_PREVIEWS_CORE_BLACKLIST_DATA_H_
......@@ -7,6 +7,7 @@
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
......@@ -18,6 +19,7 @@
#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/previews/core/blacklist_data.h"
#include "components/previews/core/previews_black_list_delegate.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_opt_out_store.h"
......@@ -29,7 +31,6 @@ class Clock;
}
namespace previews {
class PreviewsBlackListItem;
enum class PreviewsEligibilityReason {
// The preview navigation was allowed.
......@@ -87,7 +88,8 @@ class PreviewsBlackList {
// it is guaranteed to overlive the life time of |this|.
PreviewsBlackList(std::unique_ptr<PreviewsOptOutStore> opt_out_store,
base::Clock* clock,
PreviewsBlacklistDelegate* blacklist_delegate);
PreviewsBlacklistDelegate* blacklist_delegate,
BlacklistData::AllowedTypesAndVersions allowed_types);
virtual ~PreviewsBlackList();
// Asynchronously adds a new navigation to to the in-memory black list and
......@@ -117,25 +119,17 @@ class PreviewsBlackList {
// passed in a null store, resets all history in the in-memory black list.
void ClearBlackList(base::Time begin_time, base::Time end_time);
// Returns a new PreviewsBlackListItem representing |host_name|. Adds the new
// item to |black_list_item_map|.
static PreviewsBlackListItem* GetOrCreateBlackListItemForMap(
BlackListItemMap* black_list_item_map,
const std::string& host_name);
// Returns a new PreviewsBlackListItem for the host indifferent black list
// that does not consider host name when determining eligibility.
static std::unique_ptr<PreviewsBlackListItem>
CreateHostIndifferentBlackListItem();
private:
// Synchronous version of AddPreviewNavigation method. |time| is the time
// Synchronous version of AddEntry method. |time| is the time
// stamp of when the navigation was determined to be an opt-out or non-opt
// out.
void AddPreviewNavigationSync(const GURL& host_name,
bool opt_out,
PreviewsType type,
base::Time time);
void AddEntrySync(const std::string& host_name,
bool opt_out,
int type,
base::Time time);
// Creates the BlacklistData that backs the blacklist.
void Init();
// Synchronous version of ClearBlackList method.
void ClearBlackListSync(base::Time begin_time, base::Time end_time);
......@@ -143,34 +137,102 @@ class PreviewsBlackList {
// Callback passed to the backing store when loading black list information.
// Moves the |black_list_item_map| and |host_indifferent_black_list_item| into
// the in-memory black list and runs any outstanding tasks.
void LoadBlackListDone(
std::unique_ptr<BlackListItemMap> black_list_item_map,
std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item);
void LoadBlackListDone(std::unique_ptr<BlacklistData> blacklist_data);
// Synchronously determines if |host_name| should be allowed to show previews.
// Returns the reason the blacklist disallowed the preview, or
// PreviewsEligibilityReason::ALLOWED if the preview is allowed. Record
// checked reasons in |passed_reasons|.
BlacklistReason IsLoadedAndAllowed(
const std::string& host_name,
int type,
bool ignore_long_term_black_list_rules,
std::vector<BlacklistReason>* passed_reasons) const;
// Whether the session rule should be enabled. |duration| specifies how long a
// user remains blacklisted. |history| specifies how many entries should be
// evaluated; |threshold| specifies how many opt outs would cause
// blacklisting. I.e., the most recent |history| are looked at and if
// |threshold| (or more) of them are opt outs, the user is considered
// blacklisted unless the most recent opt out was longer than |duration| ago.
// This rule only considers entries within this session (it does not use the
// data that was persisted in previous sessions). When the blacklist is
// cleared, this rule is reset as if it were a new session. Queried in Init().
bool ShouldUseSessionPolicy(base::TimeDelta* duration,
size_t* history,
int* threshold) const;
// Whether the persistent rule should be enabled. |duration| specifies how
// long a user remains blacklisted. |history| specifies how many entries
// should be evaluated; |threshold| specifies how many opt outs would cause
// blacklisting. I.e., the most recent |history| are looked at and if
// |threshold| (or more) of them are opt outs, the user is considered
// blacklisted unless the most recent opt out was longer than |duration| ago.
// Queried in Init().
bool ShouldUsePersistentPolicy(base::TimeDelta* duration,
size_t* history,
int* threshold) const;
// Whether the host rule should be enabled. |duration| specifies how long a
// host remains blacklisted. |history| specifies how many entries should be
// evaluated per host; |threshold| specifies how many opt outs would cause
// blacklisting. I.e., the most recent |history| entries per host are looked
// at and if |threshold| (or more) of them are opt outs, the host is
// considered blacklisted unless the most recent opt out was longer than
// |duration| ago. |max_hosts| will limit the number of hosts stored in this
// class when non-zero.
// Queried in Init().
bool ShouldUseHostPolicy(base::TimeDelta* duration,
size_t* history,
int* threshold,
size_t* max_hosts) const;
// Whether the type rule should be enabled. |duration| specifies how long a
// type remains blacklisted. |history| specifies how many entries should be
// evaluated per type; |threshold| specifies how many opt outs would cause
// blacklisting.
// I.e., the most recent |history| entries per type are looked at and if
// |threshold| (or more) of them are opt outs, the type is considered
// blacklisted unless the most recent opt out was longer than |duration| ago.
// Queried in Init().
bool ShouldUseTypePolicy(base::TimeDelta* duration,
size_t* history,
int* threshold) const;
// The allowed types and what version they are. Should be empty unless the
// caller will not be using the blacklist in the session. It is used to remove
// stale entries from the database and to DCHECK that other methods are not
// using disallowed types. Queried in Init().
const BlacklistData::AllowedTypesAndVersions& GetAllowedTypes() const;
// Asynchronously adds a new navigation to to the in-memory black list and
// backing store. |opt_out| is whether the user opted out of the preview or
// navigated away from the page without opting out. If the in memory map has
// reached the max number of hosts allowed, and |url| is a new host, a host
// will be evicted based on recency of the hosts most recent opt out. It
// returns the time used for recording the moment when the navigation is added
// for logging.
base::Time AddEntry(const std::string& host_name, bool opt_out, int type);
// Called while waiting for the black list to be loaded from the backing
// store.
// Enqueues a task to run when when loading black list information has
// completed. Maintains the order that tasks were called in.
void QueuePendingTask(base::Closure callback);
void QueuePendingTask(base::OnceClosure callback);
// Map maintaining the in-memory black list.
std::unique_ptr<BlackListItemMap> black_list_item_map_;
// Host indifferent opt out history.
std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item_;
// An in-memory representation of the various rules of the blacklist. This is
// null while reading from the backing store.
std::unique_ptr<BlacklistData> blacklist_data_;
// Whether the black list is done being loaded from the backing store.
bool loaded_;
// The time of the last opt out for this session.
base::Optional<base::Time> last_opt_out_time_;
// The backing store of the black list information.
std::unique_ptr<PreviewsOptOutStore> opt_out_store_;
// Callbacks to be run after loading information from the backing store has
// completed.
base::queue<base::Closure> pending_callbacks_;
base::queue<base::OnceClosure> pending_callbacks_;
base::Clock* clock_;
......@@ -178,6 +240,8 @@ class PreviewsBlackList {
// guaranteed to overlive |this|.
PreviewsBlacklistDelegate* blacklist_delegate_;
const BlacklistData::AllowedTypesAndVersions allowed_types_;
base::ThreadChecker thread_checker_;
base::WeakPtrFactory<PreviewsBlackList> weak_factory_;
......
......@@ -40,8 +40,7 @@ PreviewsBlackListItem::PreviewsBlackListItem(
PreviewsBlackListItem::~PreviewsBlackListItem() {}
void PreviewsBlackListItem::AddPreviewNavigation(bool opt_out,
base::Time entry_time) {
void PreviewsBlackListItem::AddEntry(bool opt_out, base::Time entry_time) {
DCHECK_LE(opt_out_records_.size(), max_stored_history_length_);
opt_out_records_.emplace(entry_time, opt_out);
......
......@@ -20,8 +20,8 @@
namespace previews {
// Stores the recent black list history for a single host. Stores
// |stored_history_length| of the most recent previews navigations. To determine
// previews eligiblity fewer than |opt_out_black_list_threshold| out of the past
// |stored_history_length| of the most recent actions. To determine action
// eligibility fewer than |opt_out_black_list_threshold| out of the past
// |stored_history_length| navigations must be opt outs. |black_list_duration|
// is the amount of time that elapses until the host is no longer on the black
// list.
......@@ -34,10 +34,9 @@ class PreviewsBlackListItem {
~PreviewsBlackListItem();
// Adds a new navigation at the specified |entry_time|.
void AddPreviewNavigation(bool opt_out, base::Time entry_time);
void AddEntry(bool opt_out, base::Time entry_time);
// Whether the host name corresponding to |this| should be disallowed from
// showing previews.
// Whether the action corresponding to |this| should be disallowed.
bool IsBlackListed(base::Time now) const;
base::Optional<base::Time> most_recent_opt_out_time() const {
......@@ -47,8 +46,8 @@ class PreviewsBlackListItem {
size_t OptOutRecordsSizeForTesting() const;
private:
// A previews navigation to this host is represented by time and whether the
// navigation was an opt out.
// An action to |this| is represented by time and whether the action was an
// opt out.
class OptOutRecord {
public:
OptOutRecord(base::Time entry_time, bool opt_out);
......@@ -81,9 +80,9 @@ class PreviewsBlackListItem {
// The amount of time to black list a domain after the most recent opt out.
const base::TimeDelta max_black_list_duration_;
// The |max_stored_history_length_| most recent previews navigation. Is
// maintained as a priority queue that has high priority for items that should
// be evicted (i.e., they are old).
// The |max_stored_history_length_| most recent action. Is maintained as a
// priority queue that has high priority for items that should be evicted
// (i.e., they are old).
std::priority_queue<OptOutRecord> opt_out_records_;
// Time of the most recent opt out.
......
......@@ -36,10 +36,10 @@ TEST_F(PreviewsBlackListItemTest, BlackListState) {
EXPECT_FALSE(black_list_item.IsBlackListed(later));
EXPECT_FALSE(black_list_item.most_recent_opt_out_time());
black_list_item.AddPreviewNavigation(false, now);
black_list_item.AddEntry(false, now);
EXPECT_FALSE(black_list_item.most_recent_opt_out_time());
black_list_item.AddPreviewNavigation(true, now);
black_list_item.AddEntry(true, now);
EXPECT_TRUE(black_list_item.most_recent_opt_out_time());
EXPECT_EQ(now, black_list_item.most_recent_opt_out_time().value());
// Black list item of size less that |threshold| should report that the host
......@@ -47,42 +47,33 @@ TEST_F(PreviewsBlackListItemTest, BlackListState) {
EXPECT_FALSE(black_list_item.IsBlackListed(now));
EXPECT_FALSE(black_list_item.IsBlackListed(later));
black_list_item.AddPreviewNavigation(true, now + delay_between_entries);
black_list_item.AddEntry(true, now + delay_between_entries);
// Black list item with |threshold| fresh entries should report the host as
// disallowed.
EXPECT_TRUE(black_list_item.IsBlackListed(now));
// Black list item with only entries from longer than |duration| ago should
// report the host is allowed.
EXPECT_FALSE(black_list_item.IsBlackListed(later));
black_list_item.AddPreviewNavigation(true,
later - (delay_between_entries * 2));
black_list_item.AddEntry(true, later - (delay_between_entries * 2));
// Black list item with a fresh opt out and total number of opt outs larger
// than |threshold| should report the host is disallowed.
EXPECT_TRUE(black_list_item.IsBlackListed(later));
// The black list item should maintain entries based on time, so adding
// |history| entries should not push out newer entries.
black_list_item.AddPreviewNavigation(true, later - delay_between_entries * 2);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 3);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 3);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 3);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 3);
black_list_item.AddEntry(true, later - delay_between_entries * 2);
black_list_item.AddEntry(false, later - delay_between_entries * 3);
black_list_item.AddEntry(false, later - delay_between_entries * 3);
black_list_item.AddEntry(false, later - delay_between_entries * 3);
black_list_item.AddEntry(false, later - delay_between_entries * 3);
EXPECT_TRUE(black_list_item.IsBlackListed(later));
// The black list item should maintain entries based on time, so adding
// |history| newer entries should push out older entries.
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 1);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 1);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 1);
black_list_item.AddPreviewNavigation(false,
later - delay_between_entries * 1);
black_list_item.AddEntry(false, later - delay_between_entries * 1);
black_list_item.AddEntry(false, later - delay_between_entries * 1);
black_list_item.AddEntry(false, later - delay_between_entries * 1);
black_list_item.AddEntry(false, later - delay_between_entries * 1);
EXPECT_FALSE(black_list_item.IsBlackListed(later));
}
......
......@@ -9,21 +9,14 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/callback.h"
#include "base/time/time.h"
#include "components/previews/core/previews_black_list_item.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/blacklist_data.h"
namespace previews {
typedef std::unordered_map<std::string, std::unique_ptr<PreviewsBlackListItem>>
BlackListItemMap;
typedef base::Callback<void(std::unique_ptr<BlackListItemMap>,
std::unique_ptr<PreviewsBlackListItem>)>
typedef base::OnceCallback<void(std::unique_ptr<BlacklistData>)>
LoadBlackListCallback;
// PreviewsOptOutStore keeps opt out information for the previews.
......@@ -37,14 +30,15 @@ class PreviewsOptOutStore {
// Adds a new navigation to the store. |opt_out| is whether the user opted out
// of the preview.
virtual void AddPreviewNavigation(bool opt_out,
const std::string& host_name,
PreviewsType type,
base::Time now) = 0;
virtual void AddEntry(bool opt_out,
const std::string& host_name,
int type,
base::Time now) = 0;
// Asynchronously loads a map of host names to PreviewsBlackListItem for that
// host from the store. And runs |callback| once loading is finished.
virtual void LoadBlackList(LoadBlackListCallback callback) = 0;
virtual void LoadBlackList(std::unique_ptr<BlacklistData> blacklist_data,
LoadBlackListCallback callback) = 0;
// Deletes all history in the store between |begin_time| and |end_time|.
virtual void ClearBlackList(base::Time begin_time, base::Time end_time) = 0;
......
......@@ -15,7 +15,6 @@
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_opt_out_store.h"
namespace base {
......@@ -36,17 +35,17 @@ class PreviewsOptOutStoreSQL : public PreviewsOptOutStore {
PreviewsOptOutStoreSQL(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
const base::FilePath& database_dir,
std::unique_ptr<PreviewsTypeList> enabled_previews);
const base::FilePath& database_dir);
~PreviewsOptOutStoreSQL() override;
// PreviewsOptOutStore implementation:
void AddPreviewNavigation(bool opt_out,
const std::string& host_name,
PreviewsType type,
base::Time now) override;
void AddEntry(bool opt_out,
const std::string& host_name,
int type,
base::Time now) override;
void ClearBlackList(base::Time begin_time, base::Time end_time) override;
void LoadBlackList(LoadBlackListCallback callback) override;
void LoadBlackList(std::unique_ptr<BlacklistData> blacklist_data,
LoadBlackListCallback callback) override;
private:
// Thread this object is accessed on.
......@@ -61,9 +60,6 @@ class PreviewsOptOutStoreSQL : public PreviewsOptOutStore {
// SQL connection to the SQLite database.
std::unique_ptr<sql::Connection> db_;
// All enabled previews and versions.
const std::unique_ptr<PreviewsTypeList> enabled_previews_;
DISALLOW_COPY_AND_ASSIGN(PreviewsOptOutStoreSQL);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment