Chromium Code Reviews| 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..140256adf39f0fbd85887381bf1cb5f6245e23bd 100644 |
| --- a/components/subresource_filter/core/browser/subresource_filter_features.cc |
| +++ b/components/subresource_filter/core/browser/subresource_filter_features.cc |
| @@ -4,9 +4,14 @@ |
| #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" |
| @@ -14,12 +19,15 @@ |
| #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 for parsing the experimental configuration ------------------------- |
| + |
| std::string TakeVariationParamOrReturnEmpty( |
| std::map<std::string, std::string>* params, |
| const std::string& key) { |
| @@ -69,9 +77,9 @@ ActivationList ParseActivationList(const base::StringPiece activation_lists) { |
| } |
| 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 +87,145 @@ bool ParseBool(const base::StringPiece value) { |
| return base::LowerCaseEqualsASCII(value, "true"); |
| } |
| -Configuration ParseFieldTrialConfiguration() { |
| - Configuration configuration; |
| +std::vector<Configuration> FillEnabledPresetConfigurations( |
| + std::map<std::string, std::string>* params) { |
| + const struct { |
| + const char* name; |
| + Configuration (*factory_method)(); |
| + } kAvailablePresetConfigurations[] = { |
| + {kPresetLiveRunOnPhishingSites, |
| + &Configuration::MakePresetForLiveRunOnPhishingSites}, |
| + {kPresetPerformanceTestingDryRunOnAllSites, |
| + &Configuration::MakePresetForPerformanceTestingDryRunOnAllSites}}; |
| + |
| + std::vector<Configuration> enabled_configurations; |
| + const std::string enabled_presets_list = |
| + TakeVariationParamOrReturnEmpty(params, kEnablePresetsParameterName); |
| + for (const base::StringPiece enabled_preset : |
| + base::SplitStringPiece(enabled_presets_list, ",", base::TRIM_WHITESPACE, |
| + base::SPLIT_WANT_NONEMPTY)) { |
| + for (const auto& available_preset : kAvailablePresetConfigurations) { |
| + if (base::LowerCaseEqualsASCII(enabled_preset, available_preset.name)) { |
| + enabled_configurations.push_back(available_preset.factory_method()); |
| + break; |
| + } |
| + } |
| + } |
| + return enabled_configurations; |
| +} |
| - std::map<std::string, std::string> params; |
| - base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, ¶ms); |
| +Configuration ParseExperimentalConfiguration( |
| + std::map<std::string, std::string>* params) { |
| + Configuration configuration; |
| - configuration.activation_level = ParseActivationLevel( |
| - TakeVariationParamOrReturnEmpty(¶ms, kActivationLevelParameterName)); |
| + // ActivationConditions: |
| + configuration.activation_conditions.activation_scope = ParseActivationScope( |
| + TakeVariationParamOrReturnEmpty(params, kActivationScopeParameterName)); |
| - configuration.activation_scope = ParseActivationScope( |
| - TakeVariationParamOrReturnEmpty(¶ms, kActivationScopeParameterName)); |
| + configuration.activation_conditions.activation_list = ParseActivationList( |
| + TakeVariationParamOrReturnEmpty(params, kActivationListsParameterName)); |
| - 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)); |
|
pkalinnikov
2017/05/04 12:04:22
So, we add an experimental config regardless of wh
engedy
2017/05/05 12:25:42
Yes, but it is mostly only needed because otherwis
|
| + |
| + return configs; |
| +} |
| + |
| +std::vector<Configuration> MakeConfigVector(Configuration config) { |
| + std::vector<Configuration> configs; |
| + configs.push_back(std::move(config)); |
| + return configs; |
| +} |
| + |
| +template <class T> |
| +std::string StreamToString(const T& value) { |
| + std::ostringstream oss; |
| + oss << value; |
| + return oss.str(); |
| +} |
| + |
| +// Helpers for calculating configuration priorities --------------------------- |
| + |
|
Charlie Harrison
2017/05/03 14:52:17
A comment here about what the priority levels mean
engedy
2017/05/05 12:25:42
Done.
|
| +size_t GetActivationLevelPriority(ActivationLevel activation_level) { |
| + switch (activation_level) { |
| + case ActivationLevel::DISABLED: |
| + return 1; |
| + case ActivationLevel::DRYRUN: |
| + return 2; |
| + case ActivationLevel::ENABLED: |
| + return 3; |
| + } |
| + NOTREACHED(); |
| + return 0; |
| +} |
| + |
| +size_t GetActivationScopePriority(ActivationScope activation_scope) { |
| + // The more specific ACTIVATION_LIST trumps the blanket scopes. |
| + switch (activation_scope) { |
| + case ActivationScope::NO_SITES: |
|
pkalinnikov
2017/05/04 12:04:22
Hm, I am confused by the term "more specific". My
engedy
2017/05/05 12:25:42
Removed this, N/A anymore.
|
| + return 1; |
| + case ActivationScope::ALL_SITES: |
| + return 2; |
| + case ActivationScope::ACTIVATION_LIST: |
| + return 3; |
| + } |
| + NOTREACHED(); |
| + return 0; |
| +} |
| + |
| +// Returns an opaque value that represents the priority of the given |config|, |
| +// where a greater value indicates higher priority. If it falls onto the |
| +// |activation_list| to break a tie, the more recently implemented list has a |
| +// higher priority. |
| +std::tuple<size_t, size_t, size_t> GetConfigurationPriority( |
| + const Configuration& config) { |
| + // std::tuple implements lexicographical compare. |
| + return std::make_tuple( |
| + GetActivationLevelPriority(config.activation_options.activation_level), |
| + GetActivationScopePriority(config.activation_conditions.activation_scope), |
| + static_cast<size_t>(config.activation_conditions.activation_list)); |
| +} |
| + |
| +std::vector<Configuration> SortConfigsByDecreasingPriority( |
| + std::vector<Configuration> configs) { |
| + auto comp = [](const Configuration& a, const Configuration& b) { |
| + return GetConfigurationPriority(a) > GetConfigurationPriority(b); |
| + }; |
| + std::sort(configs.begin(), configs.end(), comp); |
| + return configs; |
| +} |
| + |
| +// Globals -------------------------------------------------------------------- |
| + |
| base::LazyInstance<base::Lock>::Leaky g_active_configurations_lock = |
| LAZY_INSTANCE_INITIALIZER; |
| @@ -120,6 +234,8 @@ base::LazyInstance<scoped_refptr<ConfigurationList>>::Leaky |
| } // namespace |
| +// Constant definitions ------------------------------------------------------- |
| + |
| const base::Feature kSafeBrowsingSubresourceFilter{ |
| "SubresourceFilter", base::FEATURE_DISABLED_BY_DEFAULT}; |
| @@ -143,35 +259,114 @@ const char kActivationListSocialEngineeringAdsInterstitial[] = |
| const char kActivationListPhishingInterstitial[] = "phishing_interstitial"; |
| const char kActivationListSubresourceFilter[] = "subresource_filter"; |
| -const char kRulesetFlavorParameterName[] = "ruleset_flavor"; |
| - |
| 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 kPresetLiveRunOnPhishingSites[] = "liverun_on_phishing_sites"; |
| +const char kPresetPerformanceTestingDryRunOnAllSites[] = |
| + "performance_testing_dryrun_on_all_sites"; |
| + |
| +// Configuration -------------------------------------------------------------- |
| + |
| +// static |
| +Configuration Configuration::MakePresetForLiveRunOnPhishingSites() { |
| + return Configuration(ActivationLevel::ENABLED, |
| + ActivationScope::ACTIVATION_LIST, |
| + ActivationList::PHISHING_INTERSTITIAL); |
| +} |
| + |
| +// static |
| +Configuration Configuration::MakePresetForPerformanceTestingDryRunOnAllSites() { |
| + Configuration config(ActivationLevel::DRYRUN, ActivationScope::ALL_SITES); |
| + config.activation_options.performance_measurement_rate = 1.0; |
| + 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; |
| +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_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) { |
|
Charlie Harrison
2017/05/03 14:52:18
This reminds me, with all these configurations I t
pkalinnikov
2017/05/04 12:04:23
+1
Does trace work well with multi-line strings?
engedy
2017/05/05 12:25:42
As clarified in our offline discussion, we are tal
engedy
2017/05/05 12:25:42
To clarify, are we talking about SCOPED_TRACE's he
|
| + base::DictionaryValue dict; |
| + dict.SetString("activation_scope", |
| + StreamToString(config.activation_conditions.activation_scope)); |
| + dict.SetString("activation_list", |
| + StreamToString(config.activation_conditions.activation_list)); |
| + 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))) {} |
| ConfigurationList::ConfigurationList(Configuration config) |
| - : config_(std::move(config)) {} |
| + : ConfigurationList(MakeConfigVector(std::move(config))) {} |
|
pkalinnikov
2017/05/04 12:04:22
nit1: Can this be just {std::move(config)} instead
engedy
2017/05/05 12:25:42
Initializer lists don't take rvalues, so there wil
|
| ConfigurationList::~ConfigurationList() = default; |
| -scoped_refptr<ConfigurationList> GetActiveConfigurations() { |
| +std::string ConfigurationList::GetMostSpecificRulesetFlavor() const { |
|
pkalinnikov
2017/05/04 12:04:22
Since the ConfigurationList is immutable, could we
engedy
2017/05/05 12:25:42
Done. It's more consistent either way.
|
| + std::string most_specific_flavor; |
|
Charlie Harrison
2017/05/03 14:52:17
Can it be const ref so we don't copy the underlyin
pkalinnikov
2017/05/04 12:04:22
+1, const ptr.
engedy
2017/05/05 12:25:42
Done.
engedy
2017/05/05 12:25:42
Well we cannot re-assign a const&, but StringPiece
|
| + for (const auto& config : configs_by_decreasing_priority_) { |
| + const std::string& flavor = config.general_settings.ruleset_flavor; |
| + if (flavor.size() > most_specific_flavor.size() || |
| + (flavor.size() == most_specific_flavor.size() && |
| + flavor > most_specific_flavor)) { |
| + most_specific_flavor = flavor; |
| + } |
| + } |
| + return most_specific_flavor; |
| +} |
| + |
| +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(); |
| } |