summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-08 10:37:23 +0000
committerhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-08 10:39:32 +0000
commit4c583b681190c262b5a95d0a3f9e6a44641925f9 (patch)
treef90d564759639886b2f373fac42bbd1b1572f2ec /components
parente93710a4dc1ac82407acabe799a37a24f7820df0 (diff)
downloadchromium_src-4c583b681190c262b5a95d0a3f9e6a44641925f9.zip
chromium_src-4c583b681190c262b5a95d0a3f9e6a44641925f9.tar.gz
chromium_src-4c583b681190c262b5a95d0a3f9e6a44641925f9.tar.bz2
Move OmniboxFieldTrial to components/omnibox
Move omnibox_field_trial{.cc,.h,_unittest.cc} to components/omnibox. Add components/omnibox/omnibox_switches.{cc,h} BUG=371538 TBR=sky@chromium.org for include fix under chrome/browser. Review URL: https://codereview.chromium.org/447183003 Cr-Commit-Position: refs/heads/master@{#288315} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288315 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/omnibox.gypi6
-rw-r--r--components/omnibox/DEPS2
-rw-r--r--components/omnibox/omnibox_field_trial.cc442
-rw-r--r--components/omnibox/omnibox_field_trial.h347
-rw-r--r--components/omnibox/omnibox_field_trial_unittest.cc380
-rw-r--r--components/omnibox/omnibox_switches.cc15
-rw-r--r--components/omnibox/omnibox_switches.h15
8 files changed, 1208 insertions, 0 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index e8d7f9d..8c8d01f 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -138,6 +138,7 @@
'omaha_query_params/omaha_query_params_unittest.cc',
'omnibox/autocomplete_input_unittest.cc',
'omnibox/autocomplete_match_unittest.cc',
+ 'omnibox/omnibox_field_trial_unittest.cc',
'os_crypt/ie7_password_win_unittest.cc',
'os_crypt/keychain_password_mac_unittest.mm',
'os_crypt/os_crypt_unittest.cc',
diff --git a/components/omnibox.gypi b/components/omnibox.gypi
index 6721110..63b399f 100644
--- a/components/omnibox.gypi
+++ b/components/omnibox.gypi
@@ -12,7 +12,9 @@
'../base/base.gyp:base',
'../net/net.gyp:net',
'../url/url.gyp:url_lib',
+ 'search',
'search_engines',
+ 'variations',
'component_metrics_proto',
'components_resources.gyp:components_resources',
'url_fixer',
@@ -32,6 +34,10 @@
'omnibox/autocomplete_provider.h',
'omnibox/autocomplete_provider_listener.h',
'omnibox/autocomplete_scheme_classifier.h',
+ 'omnibox/omnibox_field_trial.cc',
+ 'omnibox/omnibox_field_trial.h',
+ 'omnibox/omnibox_switches.cc',
+ 'omnibox/omnibox_switches.h',
'omnibox/search_suggestion_parser.cc',
'omnibox/search_suggestion_parser.h',
'omnibox/url_prefix.cc',
diff --git a/components/omnibox/DEPS b/components/omnibox/DEPS
index a919c94..86542c8 100644
--- a/components/omnibox/DEPS
+++ b/components/omnibox/DEPS
@@ -1,7 +1,9 @@
include_rules = [
"+components/metrics/proto",
+ "+components/search",
"+components/search_engines",
"+components/url_fixer",
+ "+components/variations",
"+grit",
"+net",
"+url",
diff --git a/components/omnibox/omnibox_field_trial.cc b/components/omnibox/omnibox_field_trial.cc
new file mode 100644
index 0000000..943b5dc
--- /dev/null
+++ b/components/omnibox/omnibox_field_trial.cc
@@ -0,0 +1,442 @@
+// Copyright 2014 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/omnibox/omnibox_field_trial.h"
+
+#include <cmath>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/omnibox_switches.h"
+#include "components/search/search.h"
+#include "components/variations/active_field_trials.h"
+#include "components/variations/metrics_util.h"
+#include "components/variations/variations_associated_data.h"
+
+using metrics::OmniboxEventProto;
+
+namespace {
+
+typedef std::map<std::string, std::string> VariationParams;
+typedef HUPScoringParams::ScoreBuckets ScoreBuckets;
+
+// Field trial names.
+const char kStopTimerFieldTrialName[] = "OmniboxStopTimer";
+
+// The autocomplete dynamic field trial name prefix. Each field trial is
+// configured dynamically and is retrieved automatically by Chrome during
+// the startup.
+const char kAutocompleteDynamicFieldTrialPrefix[] = "AutocompleteDynamicTrial_";
+// The maximum number of the autocomplete dynamic field trials (aka layers).
+const int kMaxAutocompleteDynamicFieldTrials = 5;
+
+
+// Concatenates the autocomplete dynamic field trial prefix with a field trial
+// ID to form a complete autocomplete field trial name.
+std::string DynamicFieldTrialName(int id) {
+ return base::StringPrintf("%s%d", kAutocompleteDynamicFieldTrialPrefix, id);
+}
+
+void InitializeScoreBuckets(const VariationParams& params,
+ const char* relevance_cap_param,
+ const char* half_life_param,
+ const char* score_buckets_param,
+ ScoreBuckets* score_buckets) {
+ VariationParams::const_iterator it = params.find(relevance_cap_param);
+ if (it != params.end()) {
+ int relevance_cap;
+ if (base::StringToInt(it->second, &relevance_cap))
+ score_buckets->set_relevance_cap(relevance_cap);
+ }
+
+ it = params.find(half_life_param);
+ if (it != params.end()) {
+ int half_life_days;
+ if (base::StringToInt(it->second, &half_life_days))
+ score_buckets->set_half_life_days(half_life_days);
+ }
+
+ it = params.find(score_buckets_param);
+ if (it != params.end()) {
+ // The value of the score bucket is a comma-separated list of
+ // {DecayedCount + ":" + MaxRelevance}.
+ base::StringPairs kv_pairs;
+ if (base::SplitStringIntoKeyValuePairs(it->second, ':', ',', &kv_pairs)) {
+ for (base::StringPairs::const_iterator it = kv_pairs.begin();
+ it != kv_pairs.end(); ++it) {
+ ScoreBuckets::CountMaxRelevance bucket;
+ base::StringToDouble(it->first, &bucket.first);
+ base::StringToInt(it->second, &bucket.second);
+ score_buckets->buckets().push_back(bucket);
+ }
+ std::sort(score_buckets->buckets().begin(),
+ score_buckets->buckets().end(),
+ std::greater<ScoreBuckets::CountMaxRelevance>());
+ }
+ }
+}
+
+} // namespace
+
+HUPScoringParams::ScoreBuckets::ScoreBuckets()
+ : relevance_cap_(-1),
+ half_life_days_(-1) {
+}
+
+HUPScoringParams::ScoreBuckets::~ScoreBuckets() {
+}
+
+double HUPScoringParams::ScoreBuckets::HalfLifeTimeDecay(
+ const base::TimeDelta& elapsed_time) const {
+ double time_ms;
+ if ((half_life_days_ <= 0) ||
+ ((time_ms = elapsed_time.InMillisecondsF()) <= 0))
+ return 1.0;
+
+ const double half_life_intervals =
+ time_ms / base::TimeDelta::FromDays(half_life_days_).InMillisecondsF();
+ return pow(2.0, -half_life_intervals);
+}
+
+void OmniboxFieldTrial::ActivateDynamicTrials() {
+ // Initialize all autocomplete dynamic field trials. This method may be
+ // called multiple times.
+ for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i)
+ base::FieldTrialList::FindValue(DynamicFieldTrialName(i));
+}
+
+int OmniboxFieldTrial::GetDisabledProviderTypes() {
+ // Make sure that Autocomplete dynamic field trials are activated. It's OK to
+ // call this method multiple times.
+ ActivateDynamicTrials();
+
+ // Look for group names in form of "DisabledProviders_<mask>" where "mask"
+ // is a bitmap of disabled provider types (AutocompleteProvider::Type).
+ int provider_types = 0;
+ for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i) {
+ std::string group_name = base::FieldTrialList::FindFullName(
+ DynamicFieldTrialName(i));
+ const char kDisabledProviders[] = "DisabledProviders_";
+ if (!StartsWithASCII(group_name, kDisabledProviders, true))
+ continue;
+ int types = 0;
+ if (!base::StringToInt(base::StringPiece(
+ group_name.substr(strlen(kDisabledProviders))), &types))
+ continue;
+ provider_types |= types;
+ }
+ return provider_types;
+}
+
+void OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(
+ std::vector<uint32>* field_trial_hashes) {
+ field_trial_hashes->clear();
+ for (int i = 0; i < kMaxAutocompleteDynamicFieldTrials; ++i) {
+ const std::string& trial_name = DynamicFieldTrialName(i);
+ if (base::FieldTrialList::TrialExists(trial_name))
+ field_trial_hashes->push_back(metrics::HashName(trial_name));
+ }
+ if (base::FieldTrialList::TrialExists(kBundledExperimentFieldTrialName)) {
+ field_trial_hashes->push_back(
+ metrics::HashName(kBundledExperimentFieldTrialName));
+ }
+}
+
+base::TimeDelta OmniboxFieldTrial::StopTimerFieldTrialDuration() {
+ int stop_timer_ms;
+ if (base::StringToInt(
+ base::FieldTrialList::FindFullName(kStopTimerFieldTrialName),
+ &stop_timer_ms))
+ return base::TimeDelta::FromMilliseconds(stop_timer_ms);
+ return base::TimeDelta::FromMilliseconds(1500);
+}
+
+bool OmniboxFieldTrial::InZeroSuggestFieldTrial() {
+ if (variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName, kZeroSuggestRule) == "true")
+ return true;
+ if (variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName, kZeroSuggestRule) == "false")
+ return false;
+#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \
+ (defined(OS_MACOSX) && !defined(OS_IOS))
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kZeroSuggestVariantRule) == "MostVisited";
+}
+
+bool OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kZeroSuggestVariantRule) == "AfterTyping";
+}
+
+bool OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kZeroSuggestVariantRule) == "Personalized";
+}
+
+bool OmniboxFieldTrial::ShortcutsScoringMaxRelevance(
+ OmniboxEventProto::PageClassification current_page_classification,
+ int* max_relevance) {
+ // The value of the rule is a string that encodes an integer containing
+ // the max relevance.
+ const std::string& max_relevance_str =
+ OmniboxFieldTrial::GetValueForRuleInContext(
+ kShortcutsScoringMaxRelevanceRule, current_page_classification);
+ if (max_relevance_str.empty())
+ return false;
+ if (!base::StringToInt(max_relevance_str, max_relevance))
+ return false;
+ return true;
+}
+
+bool OmniboxFieldTrial::SearchHistoryPreventInlining(
+ OmniboxEventProto::PageClassification current_page_classification) {
+ return OmniboxFieldTrial::GetValueForRuleInContext(
+ kSearchHistoryRule, current_page_classification) == "PreventInlining";
+}
+
+bool OmniboxFieldTrial::SearchHistoryDisable(
+ OmniboxEventProto::PageClassification current_page_classification) {
+ return OmniboxFieldTrial::GetValueForRuleInContext(
+ kSearchHistoryRule, current_page_classification) == "Disable";
+}
+
+void OmniboxFieldTrial::GetDemotionsByType(
+ OmniboxEventProto::PageClassification current_page_classification,
+ DemotionMultipliers* demotions_by_type) {
+ demotions_by_type->clear();
+ std::string demotion_rule = OmniboxFieldTrial::GetValueForRuleInContext(
+ kDemoteByTypeRule, current_page_classification);
+ // If there is no demotion rule for this context, then use the default
+ // value for that context. At the moment the default value is non-empty
+ // only for the fakebox-focus context.
+ if (demotion_rule.empty() &&
+ (current_page_classification ==
+ OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS))
+ demotion_rule = "1:61,2:61,3:61,4:61,16:61";
+
+ // The value of the DemoteByType rule is a comma-separated list of
+ // {ResultType + ":" + Number} where ResultType is an AutocompleteMatchType::
+ // Type enum represented as an integer and Number is an integer number
+ // between 0 and 100 inclusive. Relevance scores of matches of that result
+ // type are multiplied by Number / 100. 100 means no change.
+ base::StringPairs kv_pairs;
+ if (base::SplitStringIntoKeyValuePairs(demotion_rule, ':', ',', &kv_pairs)) {
+ for (base::StringPairs::const_iterator it = kv_pairs.begin();
+ it != kv_pairs.end(); ++it) {
+ // This is a best-effort conversion; we trust the hand-crafted parameters
+ // downloaded from the server to be perfect. There's no need to handle
+ // errors smartly.
+ int k, v;
+ base::StringToInt(it->first, &k);
+ base::StringToInt(it->second, &v);
+ (*demotions_by_type)[static_cast<AutocompleteMatchType::Type>(k)] =
+ static_cast<float>(v) / 100.0f;
+ }
+ }
+}
+
+void OmniboxFieldTrial::GetExperimentalHUPScoringParams(
+ HUPScoringParams* scoring_params) {
+ scoring_params->experimental_scoring_enabled = false;
+
+ VariationParams params;
+ if (!variations::GetVariationParams(kBundledExperimentFieldTrialName,
+ &params))
+ return;
+
+ VariationParams::const_iterator it = params.find(kHUPNewScoringEnabledParam);
+ if (it != params.end()) {
+ int enabled = 0;
+ if (base::StringToInt(it->second, &enabled))
+ scoring_params->experimental_scoring_enabled = (enabled != 0);
+ }
+
+ InitializeScoreBuckets(params, kHUPNewScoringTypedCountRelevanceCapParam,
+ kHUPNewScoringTypedCountHalfLifeTimeParam,
+ kHUPNewScoringTypedCountScoreBucketsParam,
+ &scoring_params->typed_count_buckets);
+ InitializeScoreBuckets(params, kHUPNewScoringVisitedCountRelevanceCapParam,
+ kHUPNewScoringVisitedCountHalfLifeTimeParam,
+ kHUPNewScoringVisitedCountScoreBucketsParam,
+ &scoring_params->visited_count_buckets);
+}
+
+int OmniboxFieldTrial::HQPBookmarkValue() {
+ std::string bookmark_value_str =
+ variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
+ kHQPBookmarkValueRule);
+ if (bookmark_value_str.empty())
+ return 10;
+ // This is a best-effort conversion; we trust the hand-crafted parameters
+ // downloaded from the server to be perfect. There's no need for handle
+ // errors smartly.
+ int bookmark_value;
+ base::StringToInt(bookmark_value_str, &bookmark_value);
+ return bookmark_value;
+}
+
+bool OmniboxFieldTrial::HQPAllowMatchInTLDValue() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kHQPAllowMatchInTLDRule) == "true";
+}
+
+bool OmniboxFieldTrial::HQPAllowMatchInSchemeValue() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kHQPAllowMatchInSchemeRule) == "true";
+}
+
+bool OmniboxFieldTrial::BookmarksIndexURLsValue() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kBookmarksIndexURLsRule) == "true";
+}
+
+bool OmniboxFieldTrial::DisableInlining() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kDisableInliningRule) == "true";
+}
+
+bool OmniboxFieldTrial::EnableAnswersInSuggest() {
+ const CommandLine* cl = CommandLine::ForCurrentProcess();
+ if (cl->HasSwitch(switches::kDisableAnswersInSuggest))
+ return false;
+ if (cl->HasSwitch(switches::kEnableAnswersInSuggest))
+ return true;
+
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kAnswersInSuggestRule) == "true";
+}
+
+bool OmniboxFieldTrial::AddUWYTMatchEvenIfPromotedURLs() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kAddUWYTMatchEvenIfPromotedURLsRule) == "true";
+}
+
+bool OmniboxFieldTrial::DisplayHintTextWhenPossible() {
+ return variations::GetVariationParamValue(
+ kBundledExperimentFieldTrialName,
+ kDisplayHintTextWhenPossibleRule) == "true";
+}
+
+const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] =
+ "OmniboxBundledExperimentV1";
+const char OmniboxFieldTrial::kShortcutsScoringMaxRelevanceRule[] =
+ "ShortcutsScoringMaxRelevance";
+const char OmniboxFieldTrial::kSearchHistoryRule[] = "SearchHistory";
+const char OmniboxFieldTrial::kDemoteByTypeRule[] = "DemoteByType";
+const char OmniboxFieldTrial::kHQPBookmarkValueRule[] =
+ "HQPBookmarkValue";
+const char OmniboxFieldTrial::kHQPAllowMatchInTLDRule[] = "HQPAllowMatchInTLD";
+const char OmniboxFieldTrial::kHQPAllowMatchInSchemeRule[] =
+ "HQPAllowMatchInScheme";
+const char OmniboxFieldTrial::kZeroSuggestRule[] = "ZeroSuggest";
+const char OmniboxFieldTrial::kZeroSuggestVariantRule[] = "ZeroSuggestVariant";
+const char OmniboxFieldTrial::kBookmarksIndexURLsRule[] = "BookmarksIndexURLs";
+const char OmniboxFieldTrial::kDisableInliningRule[] = "DisableInlining";
+const char OmniboxFieldTrial::kAnswersInSuggestRule[] = "AnswersInSuggest";
+const char OmniboxFieldTrial::kAddUWYTMatchEvenIfPromotedURLsRule[] =
+ "AddUWYTMatchEvenIfPromotedURLs";
+const char OmniboxFieldTrial::kDisplayHintTextWhenPossibleRule[] =
+ "DisplayHintTextWhenPossible";
+
+const char OmniboxFieldTrial::kHUPNewScoringEnabledParam[] =
+ "HUPExperimentalScoringEnabled";
+const char OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam[] =
+ "TypedCountRelevanceCap";
+const char OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam[] =
+ "TypedCountHalfLifeTime";
+const char OmniboxFieldTrial::kHUPNewScoringTypedCountScoreBucketsParam[] =
+ "TypedCountScoreBuckets";
+const char OmniboxFieldTrial::kHUPNewScoringVisitedCountRelevanceCapParam[] =
+ "VisitedCountRelevanceCap";
+const char OmniboxFieldTrial::kHUPNewScoringVisitedCountHalfLifeTimeParam[] =
+ "VisitedCountHalfLifeTime";
+const char OmniboxFieldTrial::kHUPNewScoringVisitedCountScoreBucketsParam[] =
+ "VisitedCountScoreBuckets";
+
+// Background and implementation details:
+//
+// Each experiment group in any field trial can come with an optional set of
+// parameters (key-value pairs). In the bundled omnibox experiment
+// (kBundledExperimentFieldTrialName), each experiment group comes with a
+// list of parameters in the form:
+// key=<Rule>:
+// <OmniboxEventProto::PageClassification (as an int)>:
+// <whether Instant Extended is enabled (as a 1 or 0)>
+// (note that there are no linebreaks in keys; this format is for
+// presentation only>
+// value=<arbitrary string>
+// Both the OmniboxEventProto::PageClassification and the Instant Extended
+// entries can be "*", which means this rule applies for all values of the
+// matching portion of the context.
+// One example parameter is
+// key=SearchHistory:6:1
+// value=PreventInlining
+// This means in page classification context 6 (a search result page doing
+// search term replacement) with Instant Extended enabled, the SearchHistory
+// experiment should PreventInlining.
+//
+// When an exact match to the rule in the current context is missing, we
+// give preference to a wildcard rule that matches the instant extended
+// context over a wildcard rule that matches the page classification
+// context. Hopefully, though, users will write their field trial configs
+// so as not to rely on this fall back order.
+//
+// In short, this function tries to find the value associated with key
+// |rule|:|page_classification|:|instant_extended|, failing that it looks up
+// |rule|:*:|instant_extended|, failing that it looks up
+// |rule|:|page_classification|:*, failing that it looks up |rule|:*:*,
+// and failing that it returns the empty string.
+std::string OmniboxFieldTrial::GetValueForRuleInContext(
+ const std::string& rule,
+ OmniboxEventProto::PageClassification page_classification) {
+ VariationParams params;
+ if (!variations::GetVariationParams(kBundledExperimentFieldTrialName,
+ &params)) {
+ return std::string();
+ }
+ const std::string page_classification_str =
+ base::IntToString(static_cast<int>(page_classification));
+ const std::string instant_extended =
+ chrome::IsInstantExtendedAPIEnabled() ? "1" : "0";
+ // Look up rule in this exact context.
+ VariationParams::const_iterator it = params.find(
+ rule + ":" + page_classification_str + ":" + instant_extended);
+ if (it != params.end())
+ return it->second;
+ // Fall back to the global page classification context.
+ it = params.find(rule + ":*:" + instant_extended);
+ if (it != params.end())
+ return it->second;
+ // Fall back to the global instant extended context.
+ it = params.find(rule + ":" + page_classification_str + ":*");
+ if (it != params.end())
+ return it->second;
+ // Look up rule in the global context.
+ it = params.find(rule + ":*:*");
+ return (it != params.end()) ? it->second : std::string();
+}
diff --git a/components/omnibox/omnibox_field_trial.h b/components/omnibox/omnibox_field_trial.h
new file mode 100644
index 0000000..cc4440d
--- /dev/null
+++ b/components/omnibox/omnibox_field_trial.h
@@ -0,0 +1,347 @@
+// Copyright 2014 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_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_
+#define COMPONENTS_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/autocomplete_match_type.h"
+
+namespace base {
+class TimeDelta;
+}
+
+// The set of parameters customizing the HUP scoring.
+struct HUPScoringParams {
+ // A set of parameters describing how to cap a given count score. First,
+ // we apply a half-life based decay of the given count and then find the
+ // maximum relevance score in the corresponding bucket list.
+ class ScoreBuckets {
+ public:
+ // (decayed_count, max_relevance) pair.
+ typedef std::pair<double, int> CountMaxRelevance;
+
+ ScoreBuckets();
+ ~ScoreBuckets();
+
+ // Computes a half-life time decay given the |elapsed_time|.
+ double HalfLifeTimeDecay(const base::TimeDelta& elapsed_time) const;
+
+ int relevance_cap() const { return relevance_cap_; }
+ void set_relevance_cap(int relevance_cap) {
+ relevance_cap_ = relevance_cap;
+ }
+
+ int half_life_days() const { return half_life_days_; }
+ void set_half_life_days(int half_life_days) {
+ half_life_days_ = half_life_days;
+ }
+
+ std::vector<CountMaxRelevance>& buckets() { return buckets_; }
+ const std::vector<CountMaxRelevance>& buckets() const { return buckets_; }
+
+ private:
+ // History matches with relevance score greater or equal to |relevance_cap_|
+ // are not affected by this experiment.
+ // Set to -1, if there is no relevance cap in place and all matches are
+ // subject to demotion.
+ int relevance_cap_;
+
+ // Half life time for a decayed count as measured since the last visit.
+ // Set to -1 if not used.
+ int half_life_days_;
+
+ // The relevance score caps for given decayed count values.
+ // Each pair (decayed_count, max_score) indicates what the maximum relevance
+ // score is of a decayed count equal or greater than decayed_count.
+ //
+ // Consider this example:
+ // [(1, 1000), (0.5, 500), (0, 100)]
+ // If decayed count is 2 (which is >= 1), the corresponding match's maximum
+ // relevance will be capped at 1000. In case of 0.5, the score is capped
+ // at 500. Anything below 0.5 is capped at 100.
+ //
+ // This list is sorted by the pair's first element in descending order.
+ std::vector<CountMaxRelevance> buckets_;
+ };
+
+ HUPScoringParams() : experimental_scoring_enabled(false) {}
+
+ bool experimental_scoring_enabled;
+
+ ScoreBuckets typed_count_buckets;
+
+ // Used only when the typed count is 0.
+ ScoreBuckets visited_count_buckets;
+};
+
+// This class manages the Omnibox field trials.
+class OmniboxFieldTrial {
+ public:
+ // A mapping that contains multipliers indicating that matches of the
+ // specified type should have their relevance score multiplied by the
+ // given number. Omitted types are assumed to have multipliers of 1.0.
+ typedef std::map<AutocompleteMatchType::Type, float> DemotionMultipliers;
+
+ // Activates all dynamic field trials. The main difference between
+ // the autocomplete dynamic and static field trials is that the former
+ // don't require any code changes on the Chrome side as they are controlled
+ // on the server side. Chrome binary simply propagates all necessary
+ // information through the X-Client-Data header.
+ // This method may be called multiple times.
+ static void ActivateDynamicTrials();
+
+ // Returns a bitmap containing AutocompleteProvider::Type values
+ // that should be disabled in AutocompleteController.
+ // This method simply goes over all autocomplete dynamic field trial groups
+ // and looks for group names like "ProvidersDisabled_NNN" where NNN is
+ // an integer corresponding to a bitmap mask. All extracted bitmaps
+ // are OR-ed together and returned as the final result.
+ static int GetDisabledProviderTypes();
+
+ // Returns whether the user is in any dynamic field trial where the
+ // group has a the prefix |group_prefix|.
+ static bool HasDynamicFieldTrialGroupPrefix(const char *group_prefix);
+
+ // ---------------------------------------------------------
+ // For the suggest field trial.
+
+ // Populates |field_trial_hash| with hashes of the active suggest field trial
+ // names, if any.
+ static void GetActiveSuggestFieldTrialHashes(
+ std::vector<uint32>* field_trial_hash);
+
+ // ---------------------------------------------------------
+ // For the AutocompleteController "stop timer" field trial.
+
+ // Returns the duration to be used for the AutocompleteController's stop
+ // timer. Returns the default value of 1.5 seconds if the stop timer
+ // override experiment isn't active or if parsing the experiment-provided
+ // duration fails.
+ static base::TimeDelta StopTimerFieldTrialDuration();
+
+ // ---------------------------------------------------------
+ // For the ZeroSuggestProvider field trial.
+
+ // Returns whether the user is in any field trial where the
+ // ZeroSuggestProvider should be used to get suggestions when the
+ // user clicks on the omnibox but has not typed anything yet.
+ static bool InZeroSuggestFieldTrial();
+
+ // Returns whether the user is in a ZeroSuggest field trial, but should
+ // show most visited URL instead. This is used to compare metrics of
+ // ZeroSuggest and most visited suggestions.
+ static bool InZeroSuggestMostVisitedFieldTrial();
+
+ // Returns whether the user is in a ZeroSuggest field trial and URL-based
+ // suggestions can continue to appear after the user has started typing.
+ static bool InZeroSuggestAfterTypingFieldTrial();
+
+ // Returns whether the user is in a ZeroSuggest field trial, but should
+ // show recently searched-for queries instead.
+ static bool InZeroSuggestPersonalizedFieldTrial();
+
+ // ---------------------------------------------------------
+ // For the ShortcutsScoringMaxRelevance experiment that's part of the
+ // bundled omnibox field trial.
+
+ // If the user is in an experiment group that, given the provided
+ // |current_page_classification| context, changes the maximum relevance
+ // ShortcutsProvider::CalculateScore() is supposed to assign, extract
+ // that maximum relevance score and put in in |max_relevance|. Returns
+ // true on a successful extraction. CalculateScore()'s return value is
+ // a product of this maximum relevance score and some attenuating factors
+ // that are all between 0 and 1. (Note that Shortcuts results may have
+ // their scores reduced later if the assigned score is higher than allowed
+ // for non-inlineable results. Shortcuts results are not allowed to be
+ // inlined.)
+ static bool ShortcutsScoringMaxRelevance(
+ metrics::OmniboxEventProto::PageClassification
+ current_page_classification,
+ int* max_relevance);
+
+ // ---------------------------------------------------------
+ // For the SearchHistory experiment that's part of the bundled omnibox
+ // field trial.
+
+ // Returns true if the user is in the experiment group that, given the
+ // provided |current_page_classification| context, scores search history
+ // query suggestions less aggressively so that they don't inline.
+ static bool SearchHistoryPreventInlining(
+ metrics::OmniboxEventProto::PageClassification
+ current_page_classification);
+
+ // Returns true if the user is in the experiment group that, given the
+ // provided |current_page_classification| context, disables all query
+ // suggestions from search history.
+ static bool SearchHistoryDisable(
+ metrics::OmniboxEventProto::PageClassification
+ current_page_classification);
+
+ // ---------------------------------------------------------
+ // For the DemoteByType experiment that's part of the bundled omnibox field
+ // trial.
+
+ // If the user is in an experiment group that, in the provided
+ // |current_page_classification| context, demotes the relevance scores
+ // of certain types of matches, populates the |demotions_by_type| map
+ // appropriately. Otherwise, sets |demotions_by_type| to its default
+ // value based on the context.
+ static void GetDemotionsByType(
+ metrics::OmniboxEventProto::PageClassification
+ current_page_classification,
+ DemotionMultipliers* demotions_by_type);
+
+ // ---------------------------------------------------------
+ // For the HistoryURL provider new scoring experiment that is part of the
+ // bundled omnibox field trial.
+
+ // Initializes the HUP |scoring_params| based on the active HUP scoring
+ // experiment. If there is no such experiment, this function simply sets
+ // |scoring_params|->experimental_scoring_enabled to false.
+ static void GetExperimentalHUPScoringParams(HUPScoringParams* scoring_params);
+
+ // For the HQPBookmarkValue experiment that's part of the
+ // bundled omnibox field trial.
+
+ // Returns the value an untyped visit to a bookmark should receive.
+ // Compare this value with the default of 1 for non-bookmarked untyped
+ // visits to pages and the default of 20 for typed visits. Returns
+ // 10 if the bookmark value experiment isn't active.
+ static int HQPBookmarkValue();
+
+ // ---------------------------------------------------------
+ // For the HQPAllowMatchInTLD experiment that's part of the
+ // bundled omnibox field trial.
+
+ // Returns true if HQP should allow an input term to match in the
+ // top level domain (e.g., .com) of a URL. Returns false if the
+ // allow match in TLD experiment isn't active.
+ static bool HQPAllowMatchInTLDValue();
+
+ // ---------------------------------------------------------
+ // For the HQPAllowMatchInScheme experiment that's part of the
+ // bundled omnibox field trial.
+
+ // Returns true if HQP should allow an input term to match in the
+ // scheme (e.g., http://) of a URL. Returns false if the allow
+ // match in scheme experiment isn't active.
+ static bool HQPAllowMatchInSchemeValue();
+
+ // ---------------------------------------------------------
+ // For the BookmarksIndexURLs experiment that's part of the
+ // bundled omnibox field trial.
+
+ // Returns true if BookmarkIndex should index the URL of bookmarks
+ // (not only the titles) and search for / mark matches in the URLs,
+ // and BookmarkProvider should score bookmarks based on both the
+ // matches in bookmark title and URL. Returns false if the bookmarks
+ // index URLs experiment isn't active.
+ static bool BookmarksIndexURLsValue();
+
+ // ---------------------------------------------------------
+ // For the DisableInlining experiment that's part of the bundled omnibox
+ // field trial.
+
+ // Returns true if AutocompleteResult should prevent any suggestion with
+ // a non-empty |inline_autocomplete| from being the default match. In
+ // other words, prevent an inline autocompletion from appearing as the
+ // top suggestion / within the omnibox itself, reordering matches as
+ // necessary to make this true. Returns false if the experiment isn't
+ // active.
+ static bool DisableInlining();
+
+ // ---------------------------------------------------------
+ // For the AnswersInSuggest experiment that's part of the bundled omnibox
+ // field trial.
+
+ // Returns true if the AnswersInSuggest feature should be enabled causing
+ // query responses such as current weather conditions or stock quotes
+ // to be provided in the Omnibox suggestion list. Considers both the
+ // field trial state as well as the overriding command-line flags.
+ static bool EnableAnswersInSuggest();
+
+ // ---------------------------------------------------------
+ // For the AddUWYTMatchEvenIfPromotedURLs experiment that's part of the
+ // bundled omnibox field trial.
+
+ // Returns true if HistoryURL Provider should add the URL-what-you-typed match
+ // (if valid and reasonable) even if the provider has good inline
+ // autocompletions to offer. Normally HistoryURL does not add the UWYT match
+ // if there are good inline autocompletions, as the user could simply hit
+ // backspace to delete the completion and get the what-you-typed match.
+ // However, for the disabling inlining experiment we want to have the UWYT
+ // always explicitly displayed at an option if possible. Returns false if
+ // the experiment isn't active.
+ static bool AddUWYTMatchEvenIfPromotedURLs();
+
+ // ---------------------------------------------------------
+ // For the DisplayHintTextWhenPossible experiment that's part of the
+ // bundled omnibox field trial.
+
+ // Returns true if the omnibox should display hint text (Search
+ // <search engine> or type URL) when possible (i.e., the omnibox
+ // is otherwise non-empty).
+ static bool DisplayHintTextWhenPossible();
+
+ // ---------------------------------------------------------
+ // Exposed publicly for the sake of unittests.
+ static const char kBundledExperimentFieldTrialName[];
+ // Rule names used by the bundled experiment.
+ static const char kShortcutsScoringMaxRelevanceRule[];
+ static const char kSearchHistoryRule[];
+ static const char kDemoteByTypeRule[];
+ static const char kHQPBookmarkValueRule[];
+ static const char kHQPDiscountFrecencyWhenFewVisitsRule[];
+ static const char kHQPAllowMatchInTLDRule[];
+ static const char kHQPAllowMatchInSchemeRule[];
+ static const char kZeroSuggestRule[];
+ static const char kZeroSuggestVariantRule[];
+ static const char kBookmarksIndexURLsRule[];
+ static const char kDisableInliningRule[];
+ static const char kAnswersInSuggestRule[];
+ static const char kAddUWYTMatchEvenIfPromotedURLsRule[];
+ static const char kDisplayHintTextWhenPossibleRule[];
+
+ // Parameter names used by the HUP new scoring experiments.
+ static const char kHUPNewScoringEnabledParam[];
+ static const char kHUPNewScoringTypedCountRelevanceCapParam[];
+ static const char kHUPNewScoringTypedCountHalfLifeTimeParam[];
+ static const char kHUPNewScoringTypedCountScoreBucketsParam[];
+ static const char kHUPNewScoringVisitedCountRelevanceCapParam[];
+ static const char kHUPNewScoringVisitedCountHalfLifeTimeParam[];
+ static const char kHUPNewScoringVisitedCountScoreBucketsParam[];
+
+ private:
+ friend class OmniboxFieldTrialTest;
+
+ // The bundled omnibox experiment comes with a set of parameters
+ // (key-value pairs). Each key indicates a certain rule that applies in
+ // a certain context. The value indicates what the consequences of
+ // applying the rule are. For example, the value of a SearchHistory rule
+ // in the context of a search results page might indicate that we should
+ // prevent search history matches from inlining.
+ //
+ // This function returns the value associated with the |rule| that applies
+ // in the current context (which currently consists of |page_classification|
+ // and whether Instant Extended is enabled). If no such rule exists in the
+ // current context, fall back to the rule in various wildcard contexts and
+ // return its value if found. If the rule remains unfound in the global
+ // context, returns the empty string. For more details, including how we
+ // prioritize different wildcard contexts, see the implementation. How to
+ // interpret the value is left to the caller; this is rule-dependent.
+ static std::string GetValueForRuleInContext(
+ const std::string& rule,
+ metrics::OmniboxEventProto::PageClassification page_classification);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(OmniboxFieldTrial);
+};
+
+#endif // COMPONENTS_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_
diff --git a/components/omnibox/omnibox_field_trial_unittest.cc b/components/omnibox/omnibox_field_trial_unittest.cc
new file mode 100644
index 0000000..9a78a67
--- /dev/null
+++ b/components/omnibox/omnibox_field_trial_unittest.cc
@@ -0,0 +1,380 @@
+// Copyright 2014 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/omnibox/omnibox_field_trial.h"
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string16.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/search/search.h"
+#include "components/variations/entropy_provider.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using metrics::OmniboxEventProto;
+
+class OmniboxFieldTrialTest : public testing::Test {
+ public:
+ OmniboxFieldTrialTest() {
+ ResetFieldTrialList();
+ }
+
+ void ResetFieldTrialList() {
+ // Destroy the existing FieldTrialList before creating a new one to avoid
+ // a DCHECK.
+ field_trial_list_.reset();
+ field_trial_list_.reset(new base::FieldTrialList(
+ new metrics::SHA1EntropyProvider("foo")));
+ variations::testing::ClearAllVariationParams();
+ OmniboxFieldTrial::ActivateDynamicTrials();
+ }
+
+ // Creates and activates a field trial.
+ static base::FieldTrial* CreateTestTrial(const std::string& name,
+ const std::string& group_name) {
+ base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
+ name, group_name);
+ trial->group();
+ return trial;
+ }
+
+ // Add a field trial disabling ZeroSuggest.
+ static void CreateDisableZeroSuggestTrial() {
+ std::map<std::string, std::string> params;
+ params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = "false";
+ variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+ }
+
+ // EXPECTS that demotions[match_type] exists with value expected_value.
+ static void VerifyDemotion(
+ const OmniboxFieldTrial::DemotionMultipliers& demotions,
+ AutocompleteMatchType::Type match_type,
+ float expected_value);
+
+ // EXPECT()s that OmniboxFieldTrial::GetValueForRuleInContext(|rule|,
+ // |page_classification|) returns |rule_value|.
+ static void ExpectRuleValue(
+ const std::string& rule_value,
+ const std::string& rule,
+ OmniboxEventProto::PageClassification page_classification);
+
+ private:
+ scoped_ptr<base::FieldTrialList> field_trial_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(OmniboxFieldTrialTest);
+};
+
+// static
+void OmniboxFieldTrialTest::VerifyDemotion(
+ const OmniboxFieldTrial::DemotionMultipliers& demotions,
+ AutocompleteMatchType::Type match_type,
+ float expected_value) {
+ OmniboxFieldTrial::DemotionMultipliers::const_iterator demotion_it =
+ demotions.find(match_type);
+ ASSERT_TRUE(demotion_it != demotions.end());
+ EXPECT_FLOAT_EQ(expected_value, demotion_it->second);
+}
+
+// static
+void OmniboxFieldTrialTest::ExpectRuleValue(
+ const std::string& rule_value,
+ const std::string& rule,
+ OmniboxEventProto::PageClassification page_classification) {
+ EXPECT_EQ(rule_value,
+ OmniboxFieldTrial::GetValueForRuleInContext(
+ rule, page_classification));
+}
+
+// Test if GetDisabledProviderTypes() properly parses various field trial
+// group names.
+TEST_F(OmniboxFieldTrialTest, GetDisabledProviderTypes) {
+ EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
+
+ {
+ SCOPED_TRACE("Invalid groups");
+ CreateTestTrial("AutocompleteDynamicTrial_0", "DisabledProviders_");
+ EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
+ ResetFieldTrialList();
+ CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_XXX");
+ EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
+ ResetFieldTrialList();
+ CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_12abc");
+ EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
+ }
+
+ {
+ SCOPED_TRACE("Valid group name, unsupported trial name.");
+ ResetFieldTrialList();
+ CreateTestTrial("UnsupportedTrialName", "DisabledProviders_20");
+ EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
+ }
+
+ {
+ SCOPED_TRACE("Valid field and group name.");
+ ResetFieldTrialList();
+ CreateTestTrial("AutocompleteDynamicTrial_2", "DisabledProviders_3");
+ EXPECT_EQ(3, OmniboxFieldTrial::GetDisabledProviderTypes());
+ // Two groups should be OR-ed together.
+ CreateTestTrial("AutocompleteDynamicTrial_3", "DisabledProviders_6");
+ EXPECT_EQ(7, OmniboxFieldTrial::GetDisabledProviderTypes());
+ }
+}
+
+// Test if InZeroSuggestFieldTrial() properly parses various field trial
+// group names.
+TEST_F(OmniboxFieldTrialTest, ZeroSuggestFieldTrial) {
+ // Default ZeroSuggest setting depends on OS.
+#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \
+ (defined(OS_MACOSX) && !defined(OS_IOS))
+ EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
+#else
+ EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
+#endif
+
+ {
+ SCOPED_TRACE("Disable ZeroSuggest.");
+ ResetFieldTrialList();
+ CreateDisableZeroSuggestTrial();
+ EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
+ }
+
+ {
+ SCOPED_TRACE("Bundled field trial parameters.");
+ ResetFieldTrialList();
+ std::map<std::string, std::string> params;
+ params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = "true";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+ EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
+ EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial());
+ EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial());
+
+ ResetFieldTrialList();
+ params[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule)] =
+ "MostVisited";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+ EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
+ EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial());
+ EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial());
+
+ ResetFieldTrialList();
+ params[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule)] =
+ "AfterTyping";
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
+ EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
+ EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial());
+ EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial());
+ }
+}
+
+TEST_F(OmniboxFieldTrialTest, GetDemotionsByTypeWithFallback) {
+ {
+ std::map<std::string, std::string> params;
+ params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":1:*"] =
+ "1:50,2:0";
+ params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] =
+ "5:100";
+ params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":*:*"] = "1:25";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
+ }
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+ OmniboxFieldTrial::DemotionMultipliers demotions_by_type;
+ OmniboxFieldTrial::GetDemotionsByType(
+ OmniboxEventProto::NTP, &demotions_by_type);
+ ASSERT_EQ(2u, demotions_by_type.size());
+ VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.5);
+ VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_TITLE, 0.0);
+ OmniboxFieldTrial::GetDemotionsByType(
+ OmniboxEventProto::HOME_PAGE, &demotions_by_type);
+ ASSERT_EQ(1u, demotions_by_type.size());
+ VerifyDemotion(demotions_by_type, AutocompleteMatchType::NAVSUGGEST, 1.0);
+ OmniboxFieldTrial::GetDemotionsByType(
+ OmniboxEventProto::BLANK, &demotions_by_type);
+ ASSERT_EQ(1u, demotions_by_type.size());
+ VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.25);
+}
+
+TEST_F(OmniboxFieldTrialTest, GetValueForRuleInContext) {
+ {
+ std::map<std::string, std::string> params;
+ // Rule 1 has some exact matches and fallbacks at every level.
+ params["rule1:1:0"] = "rule1-1-0-value"; // NTP
+ params["rule1:3:0"] = "rule1-3-0-value"; // HOME_PAGE
+ params["rule1:4:1"] = "rule1-4-1-value"; // OTHER
+ params["rule1:4:*"] = "rule1-4-*-value"; // OTHER
+ params["rule1:*:1"] = "rule1-*-1-value"; // global
+ params["rule1:*:*"] = "rule1-*-*-value"; // global
+ // Rule 2 has no exact matches but has fallbacks.
+ params["rule2:*:0"] = "rule2-*-0-value"; // global
+ params["rule2:1:*"] = "rule2-1-*-value"; // NTP
+ params["rule2:*:*"] = "rule2-*-*-value"; // global
+ // Rule 3 has only a global fallback.
+ params["rule3:*:*"] = "rule3-*-*-value"; // global
+ // Rule 4 has an exact match but no fallbacks.
+ params["rule4:4:0"] = "rule4-4-0-value"; // OTHER
+ // Add a malformed rule to make sure it doesn't screw things up.
+ params["unrecognized"] = "unrecognized-value";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
+ }
+
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+
+ if (chrome::IsInstantExtendedAPIEnabled()) {
+ // Tests with Instant Extended enabled.
+ // Tests for rule 1.
+ ExpectRuleValue("rule1-4-1-value",
+ "rule1", OmniboxEventProto::OTHER); // exact match
+ ExpectRuleValue("rule1-*-1-value",
+ "rule1", OmniboxEventProto::BLANK); // partial fallback
+ ExpectRuleValue("rule1-*-1-value",
+ "rule1",
+ OmniboxEventProto::NTP); // partial fallback
+
+ // Tests for rule 2.
+ ExpectRuleValue("rule2-1-*-value",
+ "rule2",
+ OmniboxEventProto::NTP); // partial fallback
+ ExpectRuleValue("rule2-*-*-value",
+ "rule2", OmniboxEventProto::OTHER); // global fallback
+
+ // Tests for rule 3.
+ ExpectRuleValue("rule3-*-*-value",
+ "rule3",
+ OmniboxEventProto::HOME_PAGE); // global fallback
+ ExpectRuleValue("rule3-*-*-value",
+ "rule3",
+ OmniboxEventProto::OTHER); // global fallback
+
+ // Tests for rule 4.
+ ExpectRuleValue("",
+ "rule4",
+ OmniboxEventProto::BLANK); // no global fallback
+ ExpectRuleValue("",
+ "rule4",
+ OmniboxEventProto::HOME_PAGE); // no global fallback
+
+ // Tests for rule 5 (a missing rule).
+ ExpectRuleValue("",
+ "rule5", OmniboxEventProto::OTHER); // no rule at all
+ } else {
+ // Tests for rule 1.
+ ExpectRuleValue("rule1-1-0-value",
+ "rule1", OmniboxEventProto::NTP); // exact match
+ ExpectRuleValue("rule1-1-0-value",
+ "rule1", OmniboxEventProto::NTP); // exact match
+ ExpectRuleValue("rule1-*-*-value",
+ "rule1", OmniboxEventProto::BLANK); // fallback to global
+ ExpectRuleValue("rule1-3-0-value",
+ "rule1",
+ OmniboxEventProto::HOME_PAGE); // exact match
+ ExpectRuleValue("rule1-4-*-value",
+ "rule1", OmniboxEventProto::OTHER); // partial fallback
+ ExpectRuleValue("rule1-*-*-value",
+ "rule1",
+ OmniboxEventProto:: // fallback to global
+ SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT);
+ // Tests for rule 2.
+ ExpectRuleValue("rule2-*-0-value",
+ "rule2",
+ OmniboxEventProto::HOME_PAGE); // partial fallback
+ ExpectRuleValue("rule2-*-0-value",
+ "rule2", OmniboxEventProto::OTHER); // partial fallback
+
+ // Tests for rule 3.
+ ExpectRuleValue("rule3-*-*-value",
+ "rule3",
+ OmniboxEventProto::HOME_PAGE); // fallback to global
+ ExpectRuleValue("rule3-*-*-value",
+ "rule3", OmniboxEventProto::OTHER); // fallback to global
+
+ // Tests for rule 4.
+ ExpectRuleValue("",
+ "rule4", OmniboxEventProto::BLANK); // no global fallback
+ ExpectRuleValue("",
+ "rule4",
+ OmniboxEventProto::HOME_PAGE); // no global fallback
+ ExpectRuleValue("rule4-4-0-value",
+ "rule4", OmniboxEventProto::OTHER); // exact match
+
+ // Tests for rule 5 (a missing rule).
+ ExpectRuleValue("",
+ "rule5", OmniboxEventProto::OTHER); // no rule at all
+ }
+}
+
+TEST_F(OmniboxFieldTrialTest, HUPNewScoringFieldTrial) {
+ {
+ std::map<std::string, std::string> params;
+ params[std::string(OmniboxFieldTrial::kHUPNewScoringEnabledParam)] = "1";
+ params[std::string(
+ OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam)] = "56";
+ params[std::string(
+ OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam)] = "77";
+ params[std::string(
+ OmniboxFieldTrial::kHUPNewScoringTypedCountScoreBucketsParam)] =
+ "0.2:25,0.1:1001,2.3:777";
+ params[std::string(
+ OmniboxFieldTrial::kHUPNewScoringVisitedCountRelevanceCapParam)] = "11";
+ params[std::string(
+ OmniboxFieldTrial::kHUPNewScoringVisitedCountHalfLifeTimeParam)] = "31";
+ params[std::string(
+ OmniboxFieldTrial::kHUPNewScoringVisitedCountScoreBucketsParam)] =
+ "5:300,0:200";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
+ }
+ base::FieldTrialList::CreateFieldTrial(
+ OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+
+ HUPScoringParams scoring_params;
+ OmniboxFieldTrial::GetExperimentalHUPScoringParams(&scoring_params);
+ EXPECT_TRUE(scoring_params.experimental_scoring_enabled);
+ EXPECT_EQ(56, scoring_params.typed_count_buckets.relevance_cap());
+ EXPECT_EQ(77, scoring_params.typed_count_buckets.half_life_days());
+ ASSERT_EQ(3u, scoring_params.typed_count_buckets.buckets().size());
+ EXPECT_EQ(std::make_pair(2.3, 777),
+ scoring_params.typed_count_buckets.buckets()[0]);
+ EXPECT_EQ(std::make_pair(0.2, 25),
+ scoring_params.typed_count_buckets.buckets()[1]);
+ EXPECT_EQ(std::make_pair(0.1, 1001),
+ scoring_params.typed_count_buckets.buckets()[2]);
+ EXPECT_EQ(11, scoring_params.visited_count_buckets.relevance_cap());
+ EXPECT_EQ(31, scoring_params.visited_count_buckets.half_life_days());
+ ASSERT_EQ(2u, scoring_params.visited_count_buckets.buckets().size());
+ EXPECT_EQ(std::make_pair(5.0, 300),
+ scoring_params.visited_count_buckets.buckets()[0]);
+ EXPECT_EQ(std::make_pair(0.0, 200),
+ scoring_params.visited_count_buckets.buckets()[1]);
+}
+
+TEST_F(OmniboxFieldTrialTest, HalfLifeTimeDecay) {
+ HUPScoringParams::ScoreBuckets buckets;
+
+ // No decay by default.
+ EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(7)));
+
+ buckets.set_half_life_days(7);
+ EXPECT_EQ(0.5, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(7)));
+ EXPECT_EQ(0.25, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(14)));
+ EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(0)));
+ EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(-1)));
+}
diff --git a/components/omnibox/omnibox_switches.cc b/components/omnibox/omnibox_switches.cc
new file mode 100644
index 0000000..ce498be
--- /dev/null
+++ b/components/omnibox/omnibox_switches.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 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/omnibox/omnibox_switches.h"
+
+namespace switches {
+
+// Disables the experimental Answers in Suggest feature.
+const char kDisableAnswersInSuggest[] = "disable-answers-in-suggest";
+
+// Enables the experimental Answers in Suggest feature.
+const char kEnableAnswersInSuggest[] = "enable-answers-in-suggest";
+
+} // namespace switches
diff --git a/components/omnibox/omnibox_switches.h b/components/omnibox/omnibox_switches.h
new file mode 100644
index 0000000..7cd73e2
--- /dev/null
+++ b/components/omnibox/omnibox_switches.h
@@ -0,0 +1,15 @@
+// Copyright 2014 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_OMNIBOX_OMNIBOX_SWITCHES_H_
+#define COMPONENTS_OMNIBOX_OMNIBOX_SWITCHES_H_
+
+namespace switches {
+
+extern const char kDisableAnswersInSuggest[];
+extern const char kEnableAnswersInSuggest[];
+
+} // namespace switches
+
+#endif // COMPONENTS_OMNIBOX_OMNIBOX_SWITCHES_H_