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

Splitting the PreviewsBlackList into an interface

OptOutBlacklist is the interface now backing PreviewsBlackList. This
class handles set up, management of rules, synchronous/asynchronous
behavior, etc. PreviewsBlackList supplies params and type/URL friendly
interfaces to its consumers. PreviewsBlackList also records UMA.

Bug: 852593
Change-Id: I8dee819a4d6ab37b58f3ce2914b09cbbcf21d1d5
Reviewed-on: https://chromium-review.googlesource.com/1108479
Commit-Queue: Ryan Sturm <ryansturm@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569047}
parent e34efffb
......@@ -6,6 +6,8 @@ static_library("core") {
sources = [
"blacklist_data.cc",
"blacklist_data.h",
"opt_out_blacklist.cc",
"opt_out_blacklist.h",
"previews_amp_converter.cc",
"previews_amp_converter.h",
"previews_black_list.cc",
......@@ -44,6 +46,7 @@ static_library("core") {
source_set("unit_tests") {
testonly = true
sources = [
"opt_out_blacklist_unittest.cc",
"previews_amp_converter_unittest.cc",
"previews_black_list_item_unittest.cc",
"previews_black_list_unittest.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/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/previews/core/previews_black_list_delegate.h"
#include "components/previews/core/previews_black_list_item.h"
#include "components/previews/core/previews_opt_out_store.h"
#include "url/gurl.h"
namespace previews {
OptOutBlacklist::OptOutBlacklist(
std::unique_ptr<PreviewsOptOutStore> opt_out_store,
base::Clock* clock,
PreviewsBlacklistDelegate* blacklist_delegate)
: loaded_(false),
opt_out_store_(std::move(opt_out_store)),
clock_(clock),
blacklist_delegate_(blacklist_delegate),
weak_factory_(this) {
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 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_OPT_OUT_BLACKLIST_H_
#define COMPONENTS_PREVIEWS_CORE_OPT_OUT_BLACKLIST_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/previews/core/blacklist_data.h"
namespace base {
class Clock;
}
namespace previews {
class BlacklistData;
class PreviewsBlacklistDelegate;
class PreviewsOptOutStore;
class OptOutBlacklist {
public:
// |opt_out_store| is the backing store to retrieve and store blacklist
// information, and can be null. When |opt_out_store| is null, the in-memory
// data will be immediately loaded to empty. If |opt_out_store| is non-null,
// it will be used to load the in-memory map asynchronously.
// |blacklist_delegate| is a single object listening for blacklist events, and
// it is guaranteed to outlive the life time of |this|.
OptOutBlacklist(std::unique_ptr<PreviewsOptOutStore> opt_out_store,
base::Clock* clock,
PreviewsBlacklistDelegate* blacklist_delegate);
virtual ~OptOutBlacklist();
// Creates the BlacklistData that backs the blacklist.
void Init();
// Asynchronously deletes all entries in the in-memory blacklist. Informs
// the backing store to delete entries between |begin_time| and |end_time|,
// and reloads entries into memory from the backing store. If the embedder
// passed in a null store, resets all history in the in-memory blacklist.
void ClearBlackList(base::Time begin_time, base::Time end_time);
// Asynchronously adds a new navigation to to the in-memory blacklist and
// backing store. |opt_out| is whether the user opted out of the action. If
// the in memory map has reached the max number of hosts allowed, and
// |host_name| 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);
// Synchronously determines if the action should be allowed for |host_name|
// and |type|. Returns the reason the blacklist disallowed the action, or
// kAllowed if the preview is allowed. Record checked reasons in
// |passed_reasons|. |ignore_long_term_black_list_rules| will cause session,
// type, and host rules, but the session rule will still be queried.
BlacklistReason IsLoadedAndAllowed(
const std::string& host_name,
int type,
bool ignore_long_term_black_list_rules,
std::vector<BlacklistReason>* passed_reasons) const;
protected:
// 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().
virtual bool ShouldUseSessionPolicy(base::TimeDelta* duration,
size_t* history,
int* threshold) const = 0;
// 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().
virtual bool ShouldUsePersistentPolicy(base::TimeDelta* duration,
size_t* history,
int* threshold) const = 0;
// 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().
virtual bool ShouldUseHostPolicy(base::TimeDelta* duration,
size_t* history,
int* threshold,
size_t* max_hosts) const = 0;
// 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().
virtual bool ShouldUseTypePolicy(base::TimeDelta* duration,
size_t* history,
int* threshold) const = 0;
// 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().
virtual BlacklistData::AllowedTypesAndVersions GetAllowedTypes() const = 0;
private:
// 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 AddEntrySync(const std::string& host_name,
bool opt_out,
int type,
base::Time time);
// Synchronous version of ClearBlackList method.
void ClearBlackListSync(base::Time begin_time, base::Time end_time);
// Callback passed to the backing store when loading black list information.
// Takes ownership of |blacklist_data|.
void LoadBlackListDone(std::unique_ptr<BlacklistData> blacklist_data);
// Called while waiting for the blacklist to be loaded from the backing
// store.
// Enqueues a task to run when when loading blacklist information has
// completed. Maintains the order that tasks were called in.
void QueuePendingTask(base::OnceClosure callback);
// 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 blacklist is done being loaded from the backing store.
bool loaded_;
// The backing store of the blacklist information.
std::unique_ptr<PreviewsOptOutStore> opt_out_store_;
// Callbacks to be run after loading information from the backing store has
// completed.
base::queue<base::OnceClosure> pending_callbacks_;
base::Clock* clock_;
// The delegate listening to this blacklist. |blacklist_delegate_| lifetime is
// guaranteed to outlive |this|.
PreviewsBlacklistDelegate* blacklist_delegate_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<OptOutBlacklist> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(OptOutBlacklist);
};
} // namespace previews
#endif // COMPONENTS_PREVIEWS_CORE_OPT_OUT_BLACKLIST_H_
This diff is collapsed.
......@@ -13,13 +13,10 @@
#include <vector>
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#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/opt_out_blacklist.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"
......@@ -78,19 +75,13 @@ enum class PreviewsEligibilityReason {
// browsing history), domains are reported as black listed. The list stores no
// more than previews::params::MaxInMemoryHostsInBlackList hosts in-memory,
// which defaults to 100.
class PreviewsBlackList {
class PreviewsBlackList : public OptOutBlacklist {
public:
// |opt_out_store| is the backing store to retrieve and store black list
// information, and can be null. When |opt_out_store| is null, the in-memory
// map will be immediately loaded to empty. If |opt_out_store| is non-null,
// it will be used to load the in-memory map asynchronously.
// |blacklist_delegate| is a single object listening for blacklist events, and
// it is guaranteed to overlive the life time of |this|.
PreviewsBlackList(std::unique_ptr<PreviewsOptOutStore> opt_out_store,
base::Clock* clock,
PreviewsBlacklistDelegate* blacklist_delegate,
BlacklistData::AllowedTypesAndVersions allowed_types);
virtual ~PreviewsBlackList();
~PreviewsBlackList() override;
// 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
......@@ -113,139 +104,26 @@ class PreviewsBlackList {
bool ignore_long_term_black_list_rules,
std::vector<PreviewsEligibilityReason>* passed_reasons) const;
// Asynchronously deletes all entries in the in-memory black list. Informs
// the backing store to delete entries between |begin_time| and |end_time|,
// and reloads entries into memory from the backing store. If the embedder
// passed in a null store, resets all history in the in-memory black list.
void ClearBlackList(base::Time begin_time, base::Time end_time);
private:
// 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 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);
// 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<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().
protected:
// OptOutBlacklist (virtual for testing):
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().
int* threshold) const override;
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().
int* threshold) const override;
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().
size_t* max_hosts) const override;
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::OnceClosure callback);
// 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 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::OnceClosure> pending_callbacks_;
base::Clock* clock_;
// The delegate listening to this blacklist. |blacklist_delegate_| lifetime is
// guaranteed to overlive |this|.
PreviewsBlacklistDelegate* blacklist_delegate_;
int* threshold) const override;
BlacklistData::AllowedTypesAndVersions GetAllowedTypes() const override;
private:
const BlacklistData::AllowedTypesAndVersions allowed_types_;
base::ThreadChecker thread_checker_;
base::WeakPtrFactory<PreviewsBlackList> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(PreviewsBlackList);
};
......
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