| Index: components/subresource_filter/core/browser/subresource_filter_features.cc
|
| diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
|
| index 06b06f1b3a6f2be15c0e3ddf5aa1321fe1380e7e..5ce505d10bf931e2536b9ee84c3ce012ceb22adf 100644
|
| --- a/components/subresource_filter/core/browser/subresource_filter_features.cc
|
| +++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
|
| @@ -4,22 +4,53 @@
|
|
|
| #include "components/subresource_filter/core/browser/subresource_filter_features.h"
|
|
|
| +#include <map>
|
| +#include <ostream>
|
| +#include <sstream>
|
| #include <string>
|
| +#include <tuple>
|
| #include <utility>
|
|
|
| +#include "base/json/json_writer.h"
|
| #include "base/lazy_instance.h"
|
| #include "base/metrics/field_trial_params.h"
|
| #include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_piece.h"
|
| #include "base/strings/string_split.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/synchronization/lock.h"
|
| +#include "base/values.h"
|
| #include "components/variations/variations_associated_data.h"
|
|
|
| namespace subresource_filter {
|
|
|
| namespace {
|
|
|
| +// Helpers --------------------------------------------------------------------
|
| +
|
| +class CommaSeparatedStrings {
|
| + public:
|
| + CommaSeparatedStrings(std::string comma_separated_strings)
|
| + : backing_string_(comma_separated_strings),
|
| + pieces_(base::SplitStringPiece(backing_string_,
|
| + ",",
|
| + base::TRIM_WHITESPACE,
|
| + base::SPLIT_WANT_NONEMPTY)) {}
|
| +
|
| + bool CaseInsensitiveContains(base::StringPiece lowercase_key) const {
|
| + const auto predicate = [lowercase_key](base::StringPiece element) {
|
| + return base::LowerCaseEqualsASCII(element, lowercase_key);
|
| + };
|
| + return std::find_if(pieces_.begin(), pieces_.end(), predicate) !=
|
| + pieces_.end();
|
| + }
|
| +
|
| + private:
|
| + const std::string backing_string_;
|
| + const std::vector<base::StringPiece> pieces_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(CommaSeparatedStrings);
|
| +};
|
| +
|
| std::string TakeVariationParamOrReturnEmpty(
|
| std::map<std::string, std::string>* params,
|
| const std::string& key) {
|
| @@ -48,30 +79,26 @@ ActivationScope ParseActivationScope(const base::StringPiece activation_scope) {
|
| return ActivationScope::NO_SITES;
|
| }
|
|
|
| -ActivationList ParseActivationList(const base::StringPiece activation_lists) {
|
| +ActivationList ParseActivationList(std::string activation_lists_string) {
|
| ActivationList activation_list_type = ActivationList::NONE;
|
| - for (const base::StringPiece& activation_list :
|
| - base::SplitStringPiece(activation_lists, ",", base::TRIM_WHITESPACE,
|
| - base::SPLIT_WANT_NONEMPTY)) {
|
| - if (base::LowerCaseEqualsASCII(activation_list,
|
| - kActivationListPhishingInterstitial)) {
|
| - return ActivationList::PHISHING_INTERSTITIAL;
|
| - } else if (base::LowerCaseEqualsASCII(
|
| - activation_list,
|
| - kActivationListSocialEngineeringAdsInterstitial)) {
|
| - activation_list_type = ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL;
|
| - } else if (base::LowerCaseEqualsASCII(activation_list,
|
| - kActivationListSubresourceFilter)) {
|
| - activation_list_type = ActivationList::SUBRESOURCE_FILTER;
|
| - }
|
| + CommaSeparatedStrings activation_lists(std::move(activation_lists_string));
|
| + if (activation_lists.CaseInsensitiveContains(
|
| + kActivationListPhishingInterstitial)) {
|
| + return ActivationList::PHISHING_INTERSTITIAL;
|
| + } else if (activation_lists.CaseInsensitiveContains(
|
| + kActivationListSocialEngineeringAdsInterstitial)) {
|
| + activation_list_type = ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL;
|
| + } else if (activation_lists.CaseInsensitiveContains(
|
| + kActivationListSubresourceFilter)) {
|
| + activation_list_type = ActivationList::SUBRESOURCE_FILTER;
|
| }
|
| return activation_list_type;
|
| }
|
|
|
| double ParsePerformanceMeasurementRate(const std::string& rate) {
|
| - double value = 0;
|
| + double value = 0.0;
|
| if (!base::StringToDouble(rate, &value) || value < 0)
|
| - return 0;
|
| + return 0.0;
|
| return value < 1 ? value : 1;
|
| }
|
|
|
| @@ -79,39 +106,120 @@ bool ParseBool(const base::StringPiece value) {
|
| return base::LowerCaseEqualsASCII(value, "true");
|
| }
|
|
|
| -Configuration ParseFieldTrialConfiguration() {
|
| +int ParseInt(const base::StringPiece value) {
|
| + int result = 0;
|
| + base::StringToInt(value, &result);
|
| + return result;
|
| +}
|
| +
|
| +std::vector<Configuration> FillEnabledPresetConfigurations(
|
| + std::map<std::string, std::string>* params) {
|
| + const struct {
|
| + const char* name;
|
| + bool enabled_by_default;
|
| + Configuration (*factory_method)();
|
| + } kAvailablePresetConfigurations[] = {
|
| + {kPresetLiveRunOnPhishingSites, false,
|
| + &Configuration::MakePresetForLiveRunOnPhishingSites},
|
| + {kPresetPerformanceTestingDryRunOnAllSites, false,
|
| + &Configuration::MakePresetForPerformanceTestingDryRunOnAllSites}};
|
| +
|
| + CommaSeparatedStrings enabled_presets(
|
| + TakeVariationParamOrReturnEmpty(params, kEnablePresetsParameterName));
|
| + CommaSeparatedStrings disabled_presets(
|
| + TakeVariationParamOrReturnEmpty(params, kDisablePresetsParameterName));
|
| +
|
| + std::vector<Configuration> enabled_configurations;
|
| + for (const auto& available_preset : kAvailablePresetConfigurations) {
|
| + if ((enabled_presets.CaseInsensitiveContains(available_preset.name) ||
|
| + available_preset.enabled_by_default) &&
|
| + !disabled_presets.CaseInsensitiveContains(available_preset.name)) {
|
| + enabled_configurations.push_back(available_preset.factory_method());
|
| + }
|
| + }
|
| +
|
| + return enabled_configurations;
|
| +}
|
| +
|
| +Configuration ParseExperimentalConfiguration(
|
| + std::map<std::string, std::string>* params) {
|
| Configuration configuration;
|
|
|
| - std::map<std::string, std::string> params;
|
| - base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, ¶ms);
|
| + // ActivationConditions:
|
| + configuration.activation_conditions.activation_scope = ParseActivationScope(
|
| + TakeVariationParamOrReturnEmpty(params, kActivationScopeParameterName));
|
|
|
| - configuration.activation_level = ParseActivationLevel(
|
| - TakeVariationParamOrReturnEmpty(¶ms, kActivationLevelParameterName));
|
| + configuration.activation_conditions.activation_list = ParseActivationList(
|
| + TakeVariationParamOrReturnEmpty(params, kActivationListsParameterName));
|
|
|
| - configuration.activation_scope = ParseActivationScope(
|
| - TakeVariationParamOrReturnEmpty(¶ms, kActivationScopeParameterName));
|
| + configuration.activation_conditions.priority =
|
| + ParseInt(TakeVariationParamOrReturnEmpty(
|
| + params, kActivationPriorityParameterName));
|
|
|
| - configuration.activation_list = ParseActivationList(
|
| - TakeVariationParamOrReturnEmpty(¶ms, kActivationListsParameterName));
|
| + // ActivationOptions:
|
| + configuration.activation_options.activation_level = ParseActivationLevel(
|
| + TakeVariationParamOrReturnEmpty(params, kActivationLevelParameterName));
|
|
|
| - configuration.performance_measurement_rate =
|
| + configuration.activation_options.performance_measurement_rate =
|
| ParsePerformanceMeasurementRate(TakeVariationParamOrReturnEmpty(
|
| - ¶ms, kPerformanceMeasurementRateParameterName));
|
| + params, kPerformanceMeasurementRateParameterName));
|
|
|
| - configuration.should_suppress_notifications =
|
| + configuration.activation_options.should_suppress_notifications =
|
| ParseBool(TakeVariationParamOrReturnEmpty(
|
| - ¶ms, kSuppressNotificationsParameterName));
|
| -
|
| - configuration.ruleset_flavor =
|
| - TakeVariationParamOrReturnEmpty(¶ms, kRulesetFlavorParameterName);
|
| + params, kSuppressNotificationsParameterName));
|
|
|
| - configuration.should_whitelist_site_on_reload =
|
| + configuration.activation_options.should_whitelist_site_on_reload =
|
| ParseBool(TakeVariationParamOrReturnEmpty(
|
| - ¶ms, kWhitelistSiteOnReloadParameterName));
|
| + params, kWhitelistSiteOnReloadParameterName));
|
| +
|
| + // GeneralSettings:
|
| + configuration.general_settings.ruleset_flavor =
|
| + TakeVariationParamOrReturnEmpty(params, kRulesetFlavorParameterName);
|
|
|
| return configuration;
|
| }
|
|
|
| +std::vector<Configuration> ParseEnabledConfigurations() {
|
| + std::map<std::string, std::string> params;
|
| + base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, ¶ms);
|
| +
|
| + std::vector<Configuration> configs = FillEnabledPresetConfigurations(¶ms);
|
| +
|
| + Configuration experimental_config = ParseExperimentalConfiguration(¶ms);
|
| + configs.push_back(std::move(experimental_config));
|
| +
|
| + return configs;
|
| +}
|
| +
|
| +template <class T>
|
| +std::string StreamToString(const T& value) {
|
| + std::ostringstream oss;
|
| + oss << value;
|
| + return oss.str();
|
| +}
|
| +
|
| +std::vector<Configuration> SortConfigsByDecreasingPriority(
|
| + std::vector<Configuration> configs) {
|
| + auto comp = [](const Configuration& a, const Configuration& b) {
|
| + return a.activation_conditions.priority > b.activation_conditions.priority;
|
| + };
|
| + std::sort(configs.begin(), configs.end(), comp);
|
| + return configs;
|
| +}
|
| +
|
| +base::StringPiece GetLexicographicallyGreatestRulesetFlavor(
|
| + const std::vector<Configuration>& configs) {
|
| + base::StringPiece greatest_flavor;
|
| + for (const auto& config : configs) {
|
| + base::StringPiece flavor = config.general_settings.ruleset_flavor;
|
| + if (flavor > greatest_flavor)
|
| + greatest_flavor = flavor;
|
| + }
|
| + return greatest_flavor;
|
| +}
|
| +
|
| +// Globals --------------------------------------------------------------------
|
| +
|
| base::LazyInstance<base::Lock>::Leaky g_active_configurations_lock =
|
| LAZY_INSTANCE_INITIALIZER;
|
|
|
| @@ -120,6 +228,8 @@ base::LazyInstance<scoped_refptr<ConfigurationList>>::Leaky
|
|
|
| } // namespace
|
|
|
| +// Constant definitions -------------------------------------------------------
|
| +
|
| const base::Feature kSafeBrowsingSubresourceFilter{
|
| "SubresourceFilter", base::FEATURE_DISABLED_BY_DEFAULT};
|
|
|
| @@ -143,35 +253,110 @@ const char kActivationListSocialEngineeringAdsInterstitial[] =
|
| const char kActivationListPhishingInterstitial[] = "phishing_interstitial";
|
| const char kActivationListSubresourceFilter[] = "subresource_filter";
|
|
|
| -const char kRulesetFlavorParameterName[] = "ruleset_flavor";
|
| +const char kActivationPriorityParameterName[] = "activation_priority";
|
|
|
| const char kPerformanceMeasurementRateParameterName[] =
|
| "performance_measurement_rate";
|
| -
|
| const char kSuppressNotificationsParameterName[] = "suppress_notifications";
|
| -
|
| const char kWhitelistSiteOnReloadParameterName[] = "whitelist_site_on_reload";
|
|
|
| +const char kRulesetFlavorParameterName[] = "ruleset_flavor";
|
| +
|
| +const char kEnablePresetsParameterName[] = "enable_presets";
|
| +const char kDisablePresetsParameterName[] = "disable_presets";
|
| +const char kPresetLiveRunOnPhishingSites[] = "liverun_on_phishing_sites";
|
| +const char kPresetPerformanceTestingDryRunOnAllSites[] =
|
| + "performance_testing_dryrun_on_all_sites";
|
| +
|
| +// Configuration --------------------------------------------------------------
|
| +
|
| +// static
|
| +Configuration Configuration::MakePresetForLiveRunOnPhishingSites() {
|
| + Configuration config(ActivationLevel::ENABLED,
|
| + ActivationScope::ACTIVATION_LIST,
|
| + ActivationList::PHISHING_INTERSTITIAL);
|
| + config.activation_conditions.priority = 1000;
|
| + return config;
|
| +}
|
| +
|
| +// static
|
| +Configuration Configuration::MakePresetForPerformanceTestingDryRunOnAllSites() {
|
| + Configuration config(ActivationLevel::DRYRUN, ActivationScope::ALL_SITES);
|
| + config.activation_options.performance_measurement_rate = 1.0;
|
| + config.activation_conditions.priority = 500;
|
| + return config;
|
| +}
|
| +
|
| Configuration::Configuration() = default;
|
| Configuration::Configuration(ActivationLevel activation_level,
|
| ActivationScope activation_scope,
|
| - ActivationList activation_list)
|
| - : activation_level(activation_level),
|
| - activation_scope(activation_scope),
|
| - activation_list(activation_list) {}
|
| + ActivationList activation_list) {
|
| + activation_options.activation_level = activation_level;
|
| + activation_conditions.activation_scope = activation_scope;
|
| + activation_conditions.activation_list = activation_list;
|
| +}
|
| +Configuration::Configuration(const Configuration&) = default;
|
| Configuration::Configuration(Configuration&&) = default;
|
| Configuration::~Configuration() = default;
|
| +Configuration& Configuration::operator=(const Configuration&) = default;
|
| Configuration& Configuration::operator=(Configuration&&) = default;
|
|
|
| -ConfigurationList::ConfigurationList(Configuration config)
|
| - : config_(std::move(config)) {}
|
| +bool Configuration::operator==(const Configuration& rhs) const {
|
| + const auto tie = [](const Configuration& config) {
|
| + return std::tie(config.activation_conditions.activation_scope,
|
| + config.activation_conditions.activation_list,
|
| + config.activation_conditions.priority,
|
| + config.activation_options.activation_level,
|
| + config.activation_options.performance_measurement_rate,
|
| + config.activation_options.should_whitelist_site_on_reload,
|
| + config.activation_options.should_suppress_notifications,
|
| + config.general_settings.ruleset_flavor);
|
| + };
|
| + return tie(*this) == tie(rhs);
|
| +}
|
| +
|
| +bool Configuration::operator!=(const Configuration& rhs) const {
|
| + return !(*this == rhs);
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& os, const Configuration& config) {
|
| + base::DictionaryValue dict;
|
| + dict.SetString("activation_scope",
|
| + StreamToString(config.activation_conditions.activation_scope));
|
| + dict.SetString("activation_list",
|
| + StreamToString(config.activation_conditions.activation_list));
|
| + dict.SetInteger("priority", config.activation_conditions.priority);
|
| + dict.SetString("activation_level",
|
| + StreamToString(config.activation_options.activation_level));
|
| + dict.SetDouble("performance_measurement_rate",
|
| + config.activation_options.performance_measurement_rate);
|
| + dict.SetBoolean("should_suppress_notifications",
|
| + config.activation_options.should_suppress_notifications);
|
| + dict.SetBoolean("should_whitelist_site_on_reload",
|
| + config.activation_options.should_whitelist_site_on_reload);
|
| + dict.SetString("ruleset_flavor",
|
| + StreamToString(config.general_settings.ruleset_flavor));
|
| + std::string json;
|
| + base::JSONWriter::WriteWithOptions(
|
| + dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
|
| + return os << json;
|
| +}
|
| +
|
| +// ConfigurationList ----------------------------------------------------------
|
| +
|
| +ConfigurationList::ConfigurationList(std::vector<Configuration> configs)
|
| + : configs_by_decreasing_priority_(
|
| + SortConfigsByDecreasingPriority(std::move(configs))),
|
| + lexicographically_greatest_ruleset_flavor_(
|
| + GetLexicographicallyGreatestRulesetFlavor(
|
| + configs_by_decreasing_priority_)) {}
|
| ConfigurationList::~ConfigurationList() = default;
|
|
|
| -scoped_refptr<ConfigurationList> GetActiveConfigurations() {
|
| +scoped_refptr<ConfigurationList> GetEnabledConfigurations() {
|
| base::AutoLock lock(g_active_configurations_lock.Get());
|
| if (!g_active_configurations.Get()) {
|
| g_active_configurations.Get() =
|
| - base::MakeShared<ConfigurationList>(ParseFieldTrialConfiguration());
|
| + base::MakeShared<ConfigurationList>(ParseEnabledConfigurations());
|
| }
|
| return g_active_configurations.Get();
|
| }
|
|
|