[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / omnibox / omnibox_field_trial.cc
blob31d8e84a28a885639e6759b97c0cd36a226f6baa
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/omnibox/omnibox_field_trial.h"
7 #include <cmath>
8 #include <string>
10 #include "base/metrics/field_trial.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/autocomplete/autocomplete_input.h"
17 #include "chrome/browser/search/search.h"
18 #include "chrome/common/metrics/variations/variation_ids.h"
19 #include "components/variations/active_field_trials.h"
20 #include "components/variations/metrics_util.h"
21 #include "components/variations/variations_associated_data.h"
23 namespace {
25 typedef std::map<std::string, std::string> VariationParams;
26 typedef HUPScoringParams::ScoreBuckets ScoreBuckets;
28 // Field trial names.
29 const char kHUPCullRedirectsFieldTrialName[] = "OmniboxHUPCullRedirects";
30 const char kHUPCreateShorterMatchFieldTrialName[] =
31 "OmniboxHUPCreateShorterMatch";
32 const char kStopTimerFieldTrialName[] = "OmniboxStopTimer";
34 // In dynamic field trials, we use these group names to switch between
35 // different zero suggest implementations.
36 const char kEnableZeroSuggestGroupPrefix[] = "EnableZeroSuggest";
37 const char kEnableZeroSuggestMostVisitedGroupPrefix[] =
38 "EnableZeroSuggestMostVisited";
39 const char kEnableZeroSuggestAfterTypingGroupPrefix[] =
40 "EnableZeroSuggestAfterTyping";
41 const char kEnableZeroSuggestPersonalizedGroupPrefix[] =
42 "EnableZeroSuggestPersonalized";
44 // The autocomplete dynamic field trial name prefix. Each field trial is
45 // configured dynamically and is retrieved automatically by Chrome during
46 // the startup.
47 const char kAutocompleteDynamicFieldTrialPrefix[] = "AutocompleteDynamicTrial_";
48 // The maximum number of the autocomplete dynamic field trials (aka layers).
49 const int kMaxAutocompleteDynamicFieldTrials = 5;
51 // Field trial experiment probabilities.
53 // For HistoryURL provider cull redirects field trial, put 0% ( = 0/100 )
54 // of the users in the don't-cull-redirects experiment group.
55 // TODO(mpearson): Remove this field trial and the code it uses once I'm
56 // sure it's no longer needed.
57 const base::FieldTrial::Probability kHUPCullRedirectsFieldTrialDivisor = 100;
58 const base::FieldTrial::Probability
59 kHUPCullRedirectsFieldTrialExperimentFraction = 0;
61 // For HistoryURL provider create shorter match field trial, put 0%
62 // ( = 25/100 ) of the users in the don't-create-a-shorter-match
63 // experiment group.
64 // TODO(mpearson): Remove this field trial and the code it uses once I'm
65 // sure it's no longer needed.
66 const base::FieldTrial::Probability
67 kHUPCreateShorterMatchFieldTrialDivisor = 100;
68 const base::FieldTrial::Probability
69 kHUPCreateShorterMatchFieldTrialExperimentFraction = 0;
71 // Field trial IDs.
72 // Though they are not literally "const", they are set only once, in
73 // ActivateStaticTrials() below.
75 // Whether the static field trials have been initialized by
76 // ActivateStaticTrials() method.
77 bool static_field_trials_initialized = false;
79 // Field trial ID for the HistoryURL provider cull redirects experiment group.
80 int hup_dont_cull_redirects_experiment_group = 0;
82 // Field trial ID for the HistoryURL provider create shorter match
83 // experiment group.
84 int hup_dont_create_shorter_match_experiment_group = 0;
87 // Concatenates the autocomplete dynamic field trial prefix with a field trial
88 // ID to form a complete autocomplete field trial name.
89 std::string DynamicFieldTrialName(int id) {
90 return base::StringPrintf("%s%d", kAutocompleteDynamicFieldTrialPrefix, id);
93 void InitializeScoreBuckets(const VariationParams& params,
94 const char* relevance_cap_param,
95 const char* half_life_param,
96 const char* score_buckets_param,
97 ScoreBuckets* score_buckets) {
98 VariationParams::const_iterator it = params.find(relevance_cap_param);
99 if (it != params.end()) {
100 int relevance_cap;
101 if (base::StringToInt(it->second, &relevance_cap))
102 score_buckets->set_relevance_cap(relevance_cap);
105 it = params.find(half_life_param);
106 if (it != params.end()) {
107 int half_life_days;
108 if (base::StringToInt(it->second, &half_life_days))
109 score_buckets->set_half_life_days(half_life_days);
112 it = params.find(score_buckets_param);
113 if (it != params.end()) {
114 // The value of the score bucket is a comma-separated list of
115 // {DecayedCount + ":" + MaxRelevance}.
116 base::StringPairs kv_pairs;
117 if (base::SplitStringIntoKeyValuePairs(it->second, ':', ',', &kv_pairs)) {
118 for (base::StringPairs::const_iterator it = kv_pairs.begin();
119 it != kv_pairs.end(); ++it) {
120 ScoreBuckets::CountMaxRelevance bucket;
121 base::StringToDouble(it->first, &bucket.first);
122 base::StringToInt(it->second, &bucket.second);
123 score_buckets->buckets().push_back(bucket);
125 std::sort(score_buckets->buckets().begin(),
126 score_buckets->buckets().end(),
127 std::greater<ScoreBuckets::CountMaxRelevance>());
132 } // namespace
134 HUPScoringParams::ScoreBuckets::ScoreBuckets()
135 : relevance_cap_(-1),
136 half_life_days_(-1) {
139 HUPScoringParams::ScoreBuckets::~ScoreBuckets() {
142 double HUPScoringParams::ScoreBuckets::HalfLifeTimeDecay(
143 const base::TimeDelta& elapsed_time) const {
144 double time_ms;
145 if ((half_life_days_ <= 0) ||
146 ((time_ms = elapsed_time.InMillisecondsF()) <= 0))
147 return 1.0;
149 const double half_life_intervals =
150 time_ms / base::TimeDelta::FromDays(half_life_days_).InMillisecondsF();
151 return pow(2.0, -half_life_intervals);
154 void OmniboxFieldTrial::ActivateStaticTrials() {
155 DCHECK(!static_field_trials_initialized);
157 // Create the HistoryURL provider cull redirects field trial.
158 // Make it expire on March 1, 2013.
159 scoped_refptr<base::FieldTrial> trial(
160 base::FieldTrialList::FactoryGetFieldTrial(
161 kHUPCullRedirectsFieldTrialName, kHUPCullRedirectsFieldTrialDivisor,
162 "Standard", 2013, 3, 1, base::FieldTrial::ONE_TIME_RANDOMIZED, NULL));
163 hup_dont_cull_redirects_experiment_group =
164 trial->AppendGroup("DontCullRedirects",
165 kHUPCullRedirectsFieldTrialExperimentFraction);
167 // Create the HistoryURL provider create shorter match field trial.
168 // Make it expire on March 1, 2013.
169 trial = base::FieldTrialList::FactoryGetFieldTrial(
170 kHUPCreateShorterMatchFieldTrialName,
171 kHUPCreateShorterMatchFieldTrialDivisor, "Standard", 2013, 3, 1,
172 base::FieldTrial::ONE_TIME_RANDOMIZED, NULL);
173 hup_dont_create_shorter_match_experiment_group =
174 trial->AppendGroup("DontCreateShorterMatch",
175 kHUPCreateShorterMatchFieldTrialExperimentFraction);
177 static_field_trials_initialized = true;
180 void OmniboxFieldTrial::ActivateDynamicTrials() {
181 // Initialize all autocomplete dynamic field trials. This method may be
182 // called multiple times.
183 for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i)
184 base::FieldTrialList::FindValue(DynamicFieldTrialName(i));
187 int OmniboxFieldTrial::GetDisabledProviderTypes() {
188 // Make sure that Autocomplete dynamic field trials are activated. It's OK to
189 // call this method multiple times.
190 ActivateDynamicTrials();
192 // Look for group names in form of "DisabledProviders_<mask>" where "mask"
193 // is a bitmap of disabled provider types (AutocompleteProvider::Type).
194 int provider_types = 0;
195 for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i) {
196 std::string group_name = base::FieldTrialList::FindFullName(
197 DynamicFieldTrialName(i));
198 const char kDisabledProviders[] = "DisabledProviders_";
199 if (!StartsWithASCII(group_name, kDisabledProviders, true))
200 continue;
201 int types = 0;
202 if (!base::StringToInt(base::StringPiece(
203 group_name.substr(strlen(kDisabledProviders))), &types))
204 continue;
205 provider_types |= types;
207 return provider_types;
210 void OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(
211 std::vector<uint32>* field_trial_hashes) {
212 field_trial_hashes->clear();
213 for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i) {
214 const std::string& trial_name = DynamicFieldTrialName(i);
215 if (base::FieldTrialList::TrialExists(trial_name))
216 field_trial_hashes->push_back(metrics::HashName(trial_name));
218 if (base::FieldTrialList::TrialExists(kBundledExperimentFieldTrialName)) {
219 field_trial_hashes->push_back(
220 metrics::HashName(kBundledExperimentFieldTrialName));
224 bool OmniboxFieldTrial::InHUPCullRedirectsFieldTrial() {
225 return base::FieldTrialList::TrialExists(kHUPCullRedirectsFieldTrialName);
228 bool OmniboxFieldTrial::InHUPCullRedirectsFieldTrialExperimentGroup() {
229 if (!base::FieldTrialList::TrialExists(kHUPCullRedirectsFieldTrialName))
230 return false;
232 // Return true if we're in the experiment group.
233 const int group = base::FieldTrialList::FindValue(
234 kHUPCullRedirectsFieldTrialName);
235 return group == hup_dont_cull_redirects_experiment_group;
238 bool OmniboxFieldTrial::InHUPCreateShorterMatchFieldTrial() {
239 return
240 base::FieldTrialList::TrialExists(kHUPCreateShorterMatchFieldTrialName);
243 bool OmniboxFieldTrial::InHUPCreateShorterMatchFieldTrialExperimentGroup() {
244 if (!base::FieldTrialList::TrialExists(kHUPCreateShorterMatchFieldTrialName))
245 return false;
247 // Return true if we're in the experiment group.
248 const int group = base::FieldTrialList::FindValue(
249 kHUPCreateShorterMatchFieldTrialName);
250 return group == hup_dont_create_shorter_match_experiment_group;
253 base::TimeDelta OmniboxFieldTrial::StopTimerFieldTrialDuration() {
254 int stop_timer_ms;
255 if (base::StringToInt(
256 base::FieldTrialList::FindFullName(kStopTimerFieldTrialName),
257 &stop_timer_ms))
258 return base::TimeDelta::FromMilliseconds(stop_timer_ms);
259 return base::TimeDelta::FromMilliseconds(1500);
262 bool OmniboxFieldTrial::HasDynamicFieldTrialGroupPrefix(
263 const char* group_prefix) {
264 // Make sure that Autocomplete dynamic field trials are activated. It's OK to
265 // call this method multiple times.
266 ActivateDynamicTrials();
268 // Look for group names starting with |group_prefix|.
269 for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i) {
270 const std::string& group_name = base::FieldTrialList::FindFullName(
271 DynamicFieldTrialName(i));
272 if (StartsWithASCII(group_name, group_prefix, true))
273 return true;
275 return false;
278 bool OmniboxFieldTrial::InZeroSuggestFieldTrial() {
279 return HasDynamicFieldTrialGroupPrefix(kEnableZeroSuggestGroupPrefix) ||
280 chrome_variations::GetVariationParamValue(
281 kBundledExperimentFieldTrialName, kZeroSuggestRule) == "true";
284 bool OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial() {
285 return HasDynamicFieldTrialGroupPrefix(
286 kEnableZeroSuggestMostVisitedGroupPrefix) ||
287 chrome_variations::GetVariationParamValue(
288 kBundledExperimentFieldTrialName,
289 kZeroSuggestVariantRule) == "MostVisited";
292 bool OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial() {
293 return HasDynamicFieldTrialGroupPrefix(
294 kEnableZeroSuggestAfterTypingGroupPrefix) ||
295 chrome_variations::GetVariationParamValue(
296 kBundledExperimentFieldTrialName,
297 kZeroSuggestVariantRule) == "AfterTyping";
300 bool OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() {
301 return HasDynamicFieldTrialGroupPrefix(
302 kEnableZeroSuggestPersonalizedGroupPrefix) ||
303 chrome_variations::GetVariationParamValue(
304 kBundledExperimentFieldTrialName,
305 kZeroSuggestVariantRule) == "Personalized";
308 bool OmniboxFieldTrial::ShortcutsScoringMaxRelevance(
309 AutocompleteInput::PageClassification current_page_classification,
310 int* max_relevance) {
311 // The value of the rule is a string that encodes an integer containing
312 // the max relevance.
313 const std::string& max_relevance_str =
314 OmniboxFieldTrial::GetValueForRuleInContext(
315 kShortcutsScoringMaxRelevanceRule, current_page_classification);
316 if (max_relevance_str.empty())
317 return false;
318 if (!base::StringToInt(max_relevance_str, max_relevance))
319 return false;
320 return true;
323 bool OmniboxFieldTrial::SearchHistoryPreventInlining(
324 AutocompleteInput::PageClassification current_page_classification) {
325 return OmniboxFieldTrial::GetValueForRuleInContext(
326 kSearchHistoryRule, current_page_classification) == "PreventInlining";
329 bool OmniboxFieldTrial::SearchHistoryDisable(
330 AutocompleteInput::PageClassification current_page_classification) {
331 return OmniboxFieldTrial::GetValueForRuleInContext(
332 kSearchHistoryRule, current_page_classification) == "Disable";
335 void OmniboxFieldTrial::GetDemotionsByType(
336 AutocompleteInput::PageClassification current_page_classification,
337 DemotionMultipliers* demotions_by_type) {
338 demotions_by_type->clear();
339 std::string demotion_rule = OmniboxFieldTrial::GetValueForRuleInContext(
340 kDemoteByTypeRule, current_page_classification);
341 // If there is no demotion rule for this context, then use the default
342 // value for that context. At the moment the default value is non-empty
343 // only for the fakebox-focus context.
344 if (demotion_rule.empty() &&
345 (current_page_classification ==
346 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS))
347 demotion_rule = "1:61,2:61,3:61,4:61,12:61";
349 // The value of the DemoteByType rule is a comma-separated list of
350 // {ResultType + ":" + Number} where ResultType is an AutocompleteMatchType::
351 // Type enum represented as an integer and Number is an integer number
352 // between 0 and 100 inclusive. Relevance scores of matches of that result
353 // type are multiplied by Number / 100. 100 means no change.
354 base::StringPairs kv_pairs;
355 if (base::SplitStringIntoKeyValuePairs(demotion_rule, ':', ',', &kv_pairs)) {
356 for (base::StringPairs::const_iterator it = kv_pairs.begin();
357 it != kv_pairs.end(); ++it) {
358 // This is a best-effort conversion; we trust the hand-crafted parameters
359 // downloaded from the server to be perfect. There's no need to handle
360 // errors smartly.
361 int k, v;
362 base::StringToInt(it->first, &k);
363 base::StringToInt(it->second, &v);
364 (*demotions_by_type)[static_cast<AutocompleteMatchType::Type>(k)] =
365 static_cast<float>(v) / 100.0f;
370 void OmniboxFieldTrial::GetExperimentalHUPScoringParams(
371 HUPScoringParams* scoring_params) {
372 scoring_params->experimental_scoring_enabled = false;
374 VariationParams params;
375 if (!chrome_variations::GetVariationParams(kBundledExperimentFieldTrialName,
376 &params))
377 return;
379 VariationParams::const_iterator it = params.find(kHUPNewScoringEnabledParam);
380 if (it != params.end()) {
381 int enabled = 0;
382 if (base::StringToInt(it->second, &enabled))
383 scoring_params->experimental_scoring_enabled = (enabled != 0);
386 InitializeScoreBuckets(params, kHUPNewScoringTypedCountRelevanceCapParam,
387 kHUPNewScoringTypedCountHalfLifeTimeParam,
388 kHUPNewScoringTypedCountScoreBucketsParam,
389 &scoring_params->typed_count_buckets);
390 InitializeScoreBuckets(params, kHUPNewScoringVisitedCountRelevanceCapParam,
391 kHUPNewScoringVisitedCountHalfLifeTimeParam,
392 kHUPNewScoringVisitedCountScoreBucketsParam,
393 &scoring_params->visited_count_buckets);
396 int OmniboxFieldTrial::HQPBookmarkValue() {
397 std::string bookmark_value_str = chrome_variations::
398 GetVariationParamValue(kBundledExperimentFieldTrialName,
399 kHQPBookmarkValueRule);
400 if (bookmark_value_str.empty())
401 return 1;
402 // This is a best-effort conversion; we trust the hand-crafted parameters
403 // downloaded from the server to be perfect. There's no need for handle
404 // errors smartly.
405 int bookmark_value;
406 base::StringToInt(bookmark_value_str, &bookmark_value);
407 return bookmark_value;
410 bool OmniboxFieldTrial::HQPAllowMatchInTLDValue() {
411 return chrome_variations::GetVariationParamValue(
412 kBundledExperimentFieldTrialName,
413 kHQPAllowMatchInTLDRule) == "true";
416 bool OmniboxFieldTrial::HQPAllowMatchInSchemeValue() {
417 return chrome_variations::GetVariationParamValue(
418 kBundledExperimentFieldTrialName,
419 kHQPAllowMatchInSchemeRule) == "true";
422 bool OmniboxFieldTrial::BookmarksIndexURLsValue() {
423 return chrome_variations::GetVariationParamValue(
424 kBundledExperimentFieldTrialName,
425 kBookmarksIndexURLsRule) == "true";
428 const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] =
429 "OmniboxBundledExperimentV1";
430 const char OmniboxFieldTrial::kShortcutsScoringMaxRelevanceRule[] =
431 "ShortcutsScoringMaxRelevance";
432 const char OmniboxFieldTrial::kSearchHistoryRule[] = "SearchHistory";
433 const char OmniboxFieldTrial::kDemoteByTypeRule[] = "DemoteByType";
434 const char OmniboxFieldTrial::kHQPBookmarkValueRule[] =
435 "HQPBookmarkValue";
436 const char OmniboxFieldTrial::kHQPAllowMatchInTLDRule[] = "HQPAllowMatchInTLD";
437 const char OmniboxFieldTrial::kHQPAllowMatchInSchemeRule[] =
438 "HQPAllowMatchInScheme";
439 const char OmniboxFieldTrial::kZeroSuggestRule[] = "ZeroSuggest";
440 const char OmniboxFieldTrial::kZeroSuggestVariantRule[] = "ZeroSuggestVariant";
441 const char OmniboxFieldTrial::kBookmarksIndexURLsRule[] = "BookmarksIndexURLs";
443 const char OmniboxFieldTrial::kHUPNewScoringEnabledParam[] =
444 "HUPExperimentalScoringEnabled";
445 const char OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam[] =
446 "TypedCountRelevanceCap";
447 const char OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam[] =
448 "TypedCountHalfLifeTime";
449 const char OmniboxFieldTrial::kHUPNewScoringTypedCountScoreBucketsParam[] =
450 "TypedCountScoreBuckets";
451 const char OmniboxFieldTrial::kHUPNewScoringVisitedCountRelevanceCapParam[] =
452 "VisitedCountRelevanceCap";
453 const char OmniboxFieldTrial::kHUPNewScoringVisitedCountHalfLifeTimeParam[] =
454 "VisitedCountHalfLifeTime";
455 const char OmniboxFieldTrial::kHUPNewScoringVisitedCountScoreBucketsParam[] =
456 "VisitedCountScoreBuckets";
458 // Background and implementation details:
460 // Each experiment group in any field trial can come with an optional set of
461 // parameters (key-value pairs). In the bundled omnibox experiment
462 // (kBundledExperimentFieldTrialName), each experiment group comes with a
463 // list of parameters in the form:
464 // key=<Rule>:
465 // <AutocompleteInput::PageClassification (as an int)>:
466 // <whether Instant Extended is enabled (as a 1 or 0)>
467 // (note that there are no linebreaks in keys; this format is for
468 // presentation only>
469 // value=<arbitrary string>
470 // Both the AutocompleteInput::PageClassification and the Instant Extended
471 // entries can be "*", which means this rule applies for all values of the
472 // matching portion of the context.
473 // One example parameter is
474 // key=SearchHistory:6:1
475 // value=PreventInlining
476 // This means in page classification context 6 (a search result page doing
477 // search term replacement) with Instant Extended enabled, the SearchHistory
478 // experiment should PreventInlining.
480 // When an exact match to the rule in the current context is missing, we
481 // give preference to a wildcard rule that matches the instant extended
482 // context over a wildcard rule that matches the page classification
483 // context. Hopefully, though, users will write their field trial configs
484 // so as not to rely on this fall back order.
486 // In short, this function tries to find the value associated with key
487 // |rule|:|page_classification|:|instant_extended|, failing that it looks up
488 // |rule|:*:|instant_extended|, failing that it looks up
489 // |rule|:|page_classification|:*, failing that it looks up |rule|:*:*,
490 // and failing that it returns the empty string.
491 std::string OmniboxFieldTrial::GetValueForRuleInContext(
492 const std::string& rule,
493 AutocompleteInput::PageClassification page_classification) {
494 VariationParams params;
495 if (!chrome_variations::GetVariationParams(kBundledExperimentFieldTrialName,
496 &params)) {
497 return std::string();
499 const std::string page_classification_str =
500 base::IntToString(static_cast<int>(page_classification));
501 const std::string instant_extended =
502 chrome::IsInstantExtendedAPIEnabled() ? "1" : "0";
503 // Look up rule in this exact context.
504 VariationParams::const_iterator it = params.find(
505 rule + ":" + page_classification_str + ":" + instant_extended);
506 if (it != params.end())
507 return it->second;
508 // Fall back to the global page classification context.
509 it = params.find(rule + ":*:" + instant_extended);
510 if (it != params.end())
511 return it->second;
512 // Fall back to the global instant extended context.
513 it = params.find(rule + ":" + page_classification_str + ":*");
514 if (it != params.end())
515 return it->second;
516 // Look up rule in the global context.
517 it = params.find(rule + ":*:*");
518 return (it != params.end()) ? it->second : std::string();