Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: components/subresource_filter/core/browser/subresource_filter_features.cc

Issue 2844063002: Add support for multiple simultaneous subresource_filter::Configurations. (Closed)
Patch Set: Polish + rebase. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/subresource_filter/core/browser/subresource_filter_features .h" 5 #include "components/subresource_filter/core/browser/subresource_filter_features .h"
6 6
7 #include <map>
8 #include <ostream>
9 #include <sstream>
7 #include <string> 10 #include <string>
11 #include <tuple>
8 #include <utility> 12 #include <utility>
9 13
14 #include "base/json/json_writer.h"
10 #include "base/lazy_instance.h" 15 #include "base/lazy_instance.h"
11 #include "base/metrics/field_trial_params.h" 16 #include "base/metrics/field_trial_params.h"
12 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h" 18 #include "base/strings/string_piece.h"
14 #include "base/strings/string_split.h" 19 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h" 20 #include "base/strings/string_util.h"
16 #include "base/synchronization/lock.h" 21 #include "base/synchronization/lock.h"
22 #include "base/values.h"
17 #include "components/variations/variations_associated_data.h" 23 #include "components/variations/variations_associated_data.h"
18 24
19 namespace subresource_filter { 25 namespace subresource_filter {
20 26
21 namespace { 27 namespace {
22 28
29 // Helpers for parsing the experimental configuration -------------------------
30
23 std::string TakeVariationParamOrReturnEmpty( 31 std::string TakeVariationParamOrReturnEmpty(
24 std::map<std::string, std::string>* params, 32 std::map<std::string, std::string>* params,
25 const std::string& key) { 33 const std::string& key) {
26 auto it = params->find(key); 34 auto it = params->find(key);
27 if (it == params->end()) 35 if (it == params->end())
28 return std::string(); 36 return std::string();
29 std::string value = std::move(it->second); 37 std::string value = std::move(it->second);
30 params->erase(it); 38 params->erase(it);
31 return value; 39 return value;
32 } 40 }
(...skipping 29 matching lines...) Expand all
62 activation_list_type = ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL; 70 activation_list_type = ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL;
63 } else if (base::LowerCaseEqualsASCII(activation_list, 71 } else if (base::LowerCaseEqualsASCII(activation_list,
64 kActivationListSubresourceFilter)) { 72 kActivationListSubresourceFilter)) {
65 activation_list_type = ActivationList::SUBRESOURCE_FILTER; 73 activation_list_type = ActivationList::SUBRESOURCE_FILTER;
66 } 74 }
67 } 75 }
68 return activation_list_type; 76 return activation_list_type;
69 } 77 }
70 78
71 double ParsePerformanceMeasurementRate(const std::string& rate) { 79 double ParsePerformanceMeasurementRate(const std::string& rate) {
72 double value = 0; 80 double value = 0.0;
73 if (!base::StringToDouble(rate, &value) || value < 0) 81 if (!base::StringToDouble(rate, &value) || value < 0)
74 return 0; 82 return 0.0;
75 return value < 1 ? value : 1; 83 return value < 1 ? value : 1;
76 } 84 }
77 85
78 bool ParseBool(const base::StringPiece value) { 86 bool ParseBool(const base::StringPiece value) {
79 return base::LowerCaseEqualsASCII(value, "true"); 87 return base::LowerCaseEqualsASCII(value, "true");
80 } 88 }
81 89
82 Configuration ParseFieldTrialConfiguration() { 90 std::vector<Configuration> FillEnabledPresetConfigurations(
91 std::map<std::string, std::string>* params) {
92 const struct {
93 const char* name;
94 Configuration (*factory_method)();
95 } kAvailablePresetConfigurations[] = {
96 {kPresetLiveRunOnPhishingSites,
97 &Configuration::MakePresetForLiveRunOnPhishingSites},
98 {kPresetPerformanceTestingDryRunOnAllSites,
99 &Configuration::MakePresetForPerformanceTestingDryRunOnAllSites}};
100
101 std::vector<Configuration> enabled_configurations;
102 const std::string enabled_presets_list =
103 TakeVariationParamOrReturnEmpty(params, kEnablePresetsParameterName);
104 for (const base::StringPiece enabled_preset :
105 base::SplitStringPiece(enabled_presets_list, ",", base::TRIM_WHITESPACE,
106 base::SPLIT_WANT_NONEMPTY)) {
107 for (const auto& available_preset : kAvailablePresetConfigurations) {
108 if (base::LowerCaseEqualsASCII(enabled_preset, available_preset.name)) {
109 enabled_configurations.push_back(available_preset.factory_method());
110 break;
111 }
112 }
113 }
114 return enabled_configurations;
115 }
116
117 Configuration ParseExperimentalConfiguration(
118 std::map<std::string, std::string>* params) {
83 Configuration configuration; 119 Configuration configuration;
84 120
85 std::map<std::string, std::string> params; 121 // ActivationConditions:
86 base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, &params); 122 configuration.activation_conditions.activation_scope = ParseActivationScope(
123 TakeVariationParamOrReturnEmpty(params, kActivationScopeParameterName));
87 124
88 configuration.activation_level = ParseActivationLevel( 125 configuration.activation_conditions.activation_list = ParseActivationList(
89 TakeVariationParamOrReturnEmpty(&params, kActivationLevelParameterName)); 126 TakeVariationParamOrReturnEmpty(params, kActivationListsParameterName));
90 127
91 configuration.activation_scope = ParseActivationScope( 128 // ActivationOptions:
92 TakeVariationParamOrReturnEmpty(&params, kActivationScopeParameterName)); 129 configuration.activation_options.activation_level = ParseActivationLevel(
130 TakeVariationParamOrReturnEmpty(params, kActivationLevelParameterName));
93 131
94 configuration.activation_list = ParseActivationList( 132 configuration.activation_options.performance_measurement_rate =
95 TakeVariationParamOrReturnEmpty(&params, kActivationListsParameterName)); 133 ParsePerformanceMeasurementRate(TakeVariationParamOrReturnEmpty(
134 params, kPerformanceMeasurementRateParameterName));
96 135
97 configuration.performance_measurement_rate = 136 configuration.activation_options.should_suppress_notifications =
98 ParsePerformanceMeasurementRate(TakeVariationParamOrReturnEmpty( 137 ParseBool(TakeVariationParamOrReturnEmpty(
99 &params, kPerformanceMeasurementRateParameterName)); 138 params, kSuppressNotificationsParameterName));
100 139
101 configuration.should_suppress_notifications = 140 configuration.activation_options.should_whitelist_site_on_reload =
102 ParseBool(TakeVariationParamOrReturnEmpty( 141 ParseBool(TakeVariationParamOrReturnEmpty(
103 &params, kSuppressNotificationsParameterName)); 142 params, kWhitelistSiteOnReloadParameterName));
104 143
105 configuration.ruleset_flavor = 144 // GeneralSettings:
106 TakeVariationParamOrReturnEmpty(&params, kRulesetFlavorParameterName); 145 configuration.general_settings.ruleset_flavor =
107 146 TakeVariationParamOrReturnEmpty(params, kRulesetFlavorParameterName);
108 configuration.should_whitelist_site_on_reload =
109 ParseBool(TakeVariationParamOrReturnEmpty(
110 &params, kWhitelistSiteOnReloadParameterName));
111 147
112 return configuration; 148 return configuration;
113 } 149 }
114 150
151 std::vector<Configuration> ParseEnabledConfigurations() {
152 std::map<std::string, std::string> params;
153 base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, &params);
154
155 std::vector<Configuration> configs = FillEnabledPresetConfigurations(&params);
156
157 Configuration experimental_config = ParseExperimentalConfiguration(&params);
158 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
159
160 return configs;
161 }
162
163 std::vector<Configuration> MakeConfigVector(Configuration config) {
164 std::vector<Configuration> configs;
165 configs.push_back(std::move(config));
166 return configs;
167 }
168
169 template <class T>
170 std::string StreamToString(const T& value) {
171 std::ostringstream oss;
172 oss << value;
173 return oss.str();
174 }
175
176 // Helpers for calculating configuration priorities ---------------------------
177
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.
178 size_t GetActivationLevelPriority(ActivationLevel activation_level) {
179 switch (activation_level) {
180 case ActivationLevel::DISABLED:
181 return 1;
182 case ActivationLevel::DRYRUN:
183 return 2;
184 case ActivationLevel::ENABLED:
185 return 3;
186 }
187 NOTREACHED();
188 return 0;
189 }
190
191 size_t GetActivationScopePriority(ActivationScope activation_scope) {
192 // The more specific ACTIVATION_LIST trumps the blanket scopes.
193 switch (activation_scope) {
194 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.
195 return 1;
196 case ActivationScope::ALL_SITES:
197 return 2;
198 case ActivationScope::ACTIVATION_LIST:
199 return 3;
200 }
201 NOTREACHED();
202 return 0;
203 }
204
205 // Returns an opaque value that represents the priority of the given |config|,
206 // where a greater value indicates higher priority. If it falls onto the
207 // |activation_list| to break a tie, the more recently implemented list has a
208 // higher priority.
209 std::tuple<size_t, size_t, size_t> GetConfigurationPriority(
210 const Configuration& config) {
211 // std::tuple implements lexicographical compare.
212 return std::make_tuple(
213 GetActivationLevelPriority(config.activation_options.activation_level),
214 GetActivationScopePriority(config.activation_conditions.activation_scope),
215 static_cast<size_t>(config.activation_conditions.activation_list));
216 }
217
218 std::vector<Configuration> SortConfigsByDecreasingPriority(
219 std::vector<Configuration> configs) {
220 auto comp = [](const Configuration& a, const Configuration& b) {
221 return GetConfigurationPriority(a) > GetConfigurationPriority(b);
222 };
223 std::sort(configs.begin(), configs.end(), comp);
224 return configs;
225 }
226
227 // Globals --------------------------------------------------------------------
228
115 base::LazyInstance<base::Lock>::Leaky g_active_configurations_lock = 229 base::LazyInstance<base::Lock>::Leaky g_active_configurations_lock =
116 LAZY_INSTANCE_INITIALIZER; 230 LAZY_INSTANCE_INITIALIZER;
117 231
118 base::LazyInstance<scoped_refptr<ConfigurationList>>::Leaky 232 base::LazyInstance<scoped_refptr<ConfigurationList>>::Leaky
119 g_active_configurations = LAZY_INSTANCE_INITIALIZER; 233 g_active_configurations = LAZY_INSTANCE_INITIALIZER;
120 234
121 } // namespace 235 } // namespace
122 236
237 // Constant definitions -------------------------------------------------------
238
123 const base::Feature kSafeBrowsingSubresourceFilter{ 239 const base::Feature kSafeBrowsingSubresourceFilter{
124 "SubresourceFilter", base::FEATURE_DISABLED_BY_DEFAULT}; 240 "SubresourceFilter", base::FEATURE_DISABLED_BY_DEFAULT};
125 241
126 const base::Feature kSafeBrowsingSubresourceFilterExperimentalUI{ 242 const base::Feature kSafeBrowsingSubresourceFilterExperimentalUI{
127 "SubresourceFilterExperimentalUI", base::FEATURE_DISABLED_BY_DEFAULT}; 243 "SubresourceFilterExperimentalUI", base::FEATURE_DISABLED_BY_DEFAULT};
128 244
129 // Legacy name `activation_state` is used in variation parameters. 245 // Legacy name `activation_state` is used in variation parameters.
130 const char kActivationLevelParameterName[] = "activation_state"; 246 const char kActivationLevelParameterName[] = "activation_state";
131 const char kActivationLevelDryRun[] = "dryrun"; 247 const char kActivationLevelDryRun[] = "dryrun";
132 const char kActivationLevelEnabled[] = "enabled"; 248 const char kActivationLevelEnabled[] = "enabled";
133 const char kActivationLevelDisabled[] = "disabled"; 249 const char kActivationLevelDisabled[] = "disabled";
134 250
135 const char kActivationScopeParameterName[] = "activation_scope"; 251 const char kActivationScopeParameterName[] = "activation_scope";
136 const char kActivationScopeAllSites[] = "all_sites"; 252 const char kActivationScopeAllSites[] = "all_sites";
137 const char kActivationScopeActivationList[] = "activation_list"; 253 const char kActivationScopeActivationList[] = "activation_list";
138 const char kActivationScopeNoSites[] = "no_sites"; 254 const char kActivationScopeNoSites[] = "no_sites";
139 255
140 const char kActivationListsParameterName[] = "activation_lists"; 256 const char kActivationListsParameterName[] = "activation_lists";
141 const char kActivationListSocialEngineeringAdsInterstitial[] = 257 const char kActivationListSocialEngineeringAdsInterstitial[] =
142 "social_engineering_ads_interstitial"; 258 "social_engineering_ads_interstitial";
143 const char kActivationListPhishingInterstitial[] = "phishing_interstitial"; 259 const char kActivationListPhishingInterstitial[] = "phishing_interstitial";
144 const char kActivationListSubresourceFilter[] = "subresource_filter"; 260 const char kActivationListSubresourceFilter[] = "subresource_filter";
145 261
262 const char kPerformanceMeasurementRateParameterName[] =
263 "performance_measurement_rate";
264 const char kSuppressNotificationsParameterName[] = "suppress_notifications";
265 const char kWhitelistSiteOnReloadParameterName[] = "whitelist_site_on_reload";
266
146 const char kRulesetFlavorParameterName[] = "ruleset_flavor"; 267 const char kRulesetFlavorParameterName[] = "ruleset_flavor";
147 268
148 const char kPerformanceMeasurementRateParameterName[] = 269 const char kEnablePresetsParameterName[] = "enable_presets";
149 "performance_measurement_rate"; 270 const char kPresetLiveRunOnPhishingSites[] = "liverun_on_phishing_sites";
271 const char kPresetPerformanceTestingDryRunOnAllSites[] =
272 "performance_testing_dryrun_on_all_sites";
150 273
151 const char kSuppressNotificationsParameterName[] = "suppress_notifications"; 274 // Configuration --------------------------------------------------------------
152 275
153 const char kWhitelistSiteOnReloadParameterName[] = "whitelist_site_on_reload"; 276 // static
277 Configuration Configuration::MakePresetForLiveRunOnPhishingSites() {
278 return Configuration(ActivationLevel::ENABLED,
279 ActivationScope::ACTIVATION_LIST,
280 ActivationList::PHISHING_INTERSTITIAL);
281 }
282
283 // static
284 Configuration Configuration::MakePresetForPerformanceTestingDryRunOnAllSites() {
285 Configuration config(ActivationLevel::DRYRUN, ActivationScope::ALL_SITES);
286 config.activation_options.performance_measurement_rate = 1.0;
287 return config;
288 }
154 289
155 Configuration::Configuration() = default; 290 Configuration::Configuration() = default;
156 Configuration::Configuration(ActivationLevel activation_level, 291 Configuration::Configuration(ActivationLevel activation_level,
157 ActivationScope activation_scope, 292 ActivationScope activation_scope,
158 ActivationList activation_list) 293 ActivationList activation_list) {
159 : activation_level(activation_level), 294 activation_options.activation_level = activation_level;
160 activation_scope(activation_scope), 295 activation_conditions.activation_scope = activation_scope;
161 activation_list(activation_list) {} 296 activation_conditions.activation_list = activation_list;
297 }
298 Configuration::Configuration(const Configuration&) = default;
162 Configuration::Configuration(Configuration&&) = default; 299 Configuration::Configuration(Configuration&&) = default;
163 Configuration::~Configuration() = default; 300 Configuration::~Configuration() = default;
301 Configuration& Configuration::operator=(const Configuration&) = default;
164 Configuration& Configuration::operator=(Configuration&&) = default; 302 Configuration& Configuration::operator=(Configuration&&) = default;
165 303
304 bool Configuration::operator==(const Configuration& rhs) const {
305 const auto tie = [](const Configuration& config) {
306 return std::tie(config.activation_conditions.activation_scope,
307 config.activation_conditions.activation_list,
308 config.activation_options.activation_level,
309 config.activation_options.performance_measurement_rate,
310 config.activation_options.should_whitelist_site_on_reload,
311 config.activation_options.should_suppress_notifications,
312 config.general_settings.ruleset_flavor);
313 };
314 return tie(*this) == tie(rhs);
315 }
316
317 bool Configuration::operator!=(const Configuration& rhs) const {
318 return !(*this == rhs);
319 }
320
321 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
322 base::DictionaryValue dict;
323 dict.SetString("activation_scope",
324 StreamToString(config.activation_conditions.activation_scope));
325 dict.SetString("activation_list",
326 StreamToString(config.activation_conditions.activation_list));
327 dict.SetString("activation_level",
328 StreamToString(config.activation_options.activation_level));
329 dict.SetDouble("performance_measurement_rate",
330 config.activation_options.performance_measurement_rate);
331 dict.SetBoolean("should_suppress_notifications",
332 config.activation_options.should_suppress_notifications);
333 dict.SetBoolean("should_whitelist_site_on_reload",
334 config.activation_options.should_whitelist_site_on_reload);
335 dict.SetString("ruleset_flavor",
336 StreamToString(config.general_settings.ruleset_flavor));
337 std::string json;
338 base::JSONWriter::WriteWithOptions(
339 dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
340 return os << json;
341 }
342
343 // ConfigurationList ----------------------------------------------------------
344
345 ConfigurationList::ConfigurationList(std::vector<Configuration> configs)
346 : configs_by_decreasing_priority_(
347 SortConfigsByDecreasingPriority(std::move(configs))) {}
166 ConfigurationList::ConfigurationList(Configuration config) 348 ConfigurationList::ConfigurationList(Configuration config)
167 : config_(std::move(config)) {} 349 : 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
168 ConfigurationList::~ConfigurationList() = default; 350 ConfigurationList::~ConfigurationList() = default;
169 351
170 scoped_refptr<ConfigurationList> GetActiveConfigurations() { 352 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.
353 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
354 for (const auto& config : configs_by_decreasing_priority_) {
355 const std::string& flavor = config.general_settings.ruleset_flavor;
356 if (flavor.size() > most_specific_flavor.size() ||
357 (flavor.size() == most_specific_flavor.size() &&
358 flavor > most_specific_flavor)) {
359 most_specific_flavor = flavor;
360 }
361 }
362 return most_specific_flavor;
363 }
364
365 scoped_refptr<ConfigurationList> GetEnabledConfigurations() {
171 base::AutoLock lock(g_active_configurations_lock.Get()); 366 base::AutoLock lock(g_active_configurations_lock.Get());
172 if (!g_active_configurations.Get()) { 367 if (!g_active_configurations.Get()) {
173 g_active_configurations.Get() = 368 g_active_configurations.Get() =
174 base::MakeShared<ConfigurationList>(ParseFieldTrialConfiguration()); 369 base::MakeShared<ConfigurationList>(ParseEnabledConfigurations());
175 } 370 }
176 return g_active_configurations.Get(); 371 return g_active_configurations.Get();
177 } 372 }
178 373
179 namespace testing { 374 namespace testing {
180 375
181 scoped_refptr<ConfigurationList> GetAndSetActivateConfigurations( 376 scoped_refptr<ConfigurationList> GetAndSetActivateConfigurations(
182 scoped_refptr<ConfigurationList> new_configs) { 377 scoped_refptr<ConfigurationList> new_configs) {
183 base::AutoLock lock(g_active_configurations_lock.Get()); 378 base::AutoLock lock(g_active_configurations_lock.Get());
184 auto old_configs = std::move(g_active_configurations.Get()); 379 auto old_configs = std::move(g_active_configurations.Get());
185 g_active_configurations.Get() = std::move(new_configs); 380 g_active_configurations.Get() = std::move(new_configs);
186 return old_configs; 381 return old_configs;
187 } 382 }
188 383
189 } // namespace testing 384 } // namespace testing
190 385
191 } // namespace subresource_filter 386 } // namespace subresource_filter
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698