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. 4 5 #ifndef CHROME_BROWSER_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ 6 #define CHROME_BROWSER_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ 7 8 #include <map> 9 #include <string> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/gtest_prod_util.h" 14 #include "chrome/common/autocomplete_match_type.h" 15 #include "components/metrics/proto/omnibox_event.pb.h" 16 17 namespace base { 18 class TimeDelta; 19 } 20 21 // The set of parameters customizing the HUP scoring. 22 struct HUPScoringParams { 23 // A set of parameters describing how to cap a given count score. First, 24 // we apply a half-life based decay of the given count and then find the 25 // maximum relevance score in the corresponding bucket list. 26 class ScoreBuckets { 27 public: 28 // (decayed_count, max_relevance) pair. 29 typedef std::pair<double, int> CountMaxRelevance; 30 31 ScoreBuckets(); 32 ~ScoreBuckets(); 33 34 // Computes a half-life time decay given the |elapsed_time|. 35 double HalfLifeTimeDecay(const base::TimeDelta& elapsed_time) const; 36 37 int relevance_cap() const { return relevance_cap_; } 38 void set_relevance_cap(int relevance_cap) { 39 relevance_cap_ = relevance_cap; 40 } 41 42 int half_life_days() const { return half_life_days_; } 43 void set_half_life_days(int half_life_days) { 44 half_life_days_ = half_life_days; 45 } 46 47 std::vector<CountMaxRelevance>& buckets() { return buckets_; } 48 const std::vector<CountMaxRelevance>& buckets() const { return buckets_; } 49 50 private: 51 // History matches with relevance score greater or equal to |relevance_cap_| 52 // are not affected by this experiment. 53 // Set to -1, if there is no relevance cap in place and all matches are 54 // subject to demotion. 55 int relevance_cap_; 56 57 // Half life time for a decayed count as measured since the last visit. 58 // Set to -1 if not used. 59 int half_life_days_; 60 61 // The relevance score caps for given decayed count values. 62 // Each pair (decayed_count, max_score) indicates what the maximum relevance 63 // score is of a decayed count equal or greater than decayed_count. 64 // 65 // Consider this example: 66 // [(1, 1000), (0.5, 500), (0, 100)] 67 // If decayed count is 2 (which is >= 1), the corresponding match's maximum 68 // relevance will be capped at 1000. In case of 0.5, the score is capped 69 // at 500. Anything below 0.5 is capped at 100. 70 // 71 // This list is sorted by the pair's first element in descending order. 72 std::vector<CountMaxRelevance> buckets_; 73 }; 74 75 HUPScoringParams() : experimental_scoring_enabled(false) {} 76 77 bool experimental_scoring_enabled; 78 79 ScoreBuckets typed_count_buckets; 80 81 // Used only when the typed count is 0. 82 ScoreBuckets visited_count_buckets; 83 }; 84 85 // This class manages the Omnibox field trials. 86 class OmniboxFieldTrial { 87 public: 88 // A mapping that contains multipliers indicating that matches of the 89 // specified type should have their relevance score multiplied by the 90 // given number. Omitted types are assumed to have multipliers of 1.0. 91 typedef std::map<AutocompleteMatchType::Type, float> DemotionMultipliers; 92 93 // Creates the static field trial groups. 94 // *** MUST NOT BE CALLED MORE THAN ONCE. *** 95 static void ActivateStaticTrials(); 96 97 // Activates all dynamic field trials. The main difference between 98 // the autocomplete dynamic and static field trials is that the former 99 // don't require any code changes on the Chrome side as they are controlled 100 // on the server side. Chrome binary simply propagates all necessary 101 // information through the X-Client-Data header. 102 // This method, unlike ActivateStaticTrials(), may be called multiple times. 103 static void ActivateDynamicTrials(); 104 105 // Returns a bitmap containing AutocompleteProvider::Type values 106 // that should be disabled in AutocompleteController. 107 // This method simply goes over all autocomplete dynamic field trial groups 108 // and looks for group names like "ProvidersDisabled_NNN" where NNN is 109 // an integer corresponding to a bitmap mask. All extracted bitmaps 110 // are OR-ed together and returned as the final result. 111 static int GetDisabledProviderTypes(); 112 113 // Returns whether the user is in any dynamic field trial where the 114 // group has a the prefix |group_prefix|. 115 static bool HasDynamicFieldTrialGroupPrefix(const char *group_prefix); 116 117 // --------------------------------------------------------- 118 // For the suggest field trial. 119 120 // Populates |field_trial_hash| with hashes of the active suggest field trial 121 // names, if any. 122 static void GetActiveSuggestFieldTrialHashes( 123 std::vector<uint32>* field_trial_hash); 124 125 // --------------------------------------------------------- 126 // For the HistoryURL provider disable culling redirects field trial. 127 128 // Returns whether the user is in any group for this field trial. 129 // (Should always be true unless initialization went wrong.) 130 static bool InHUPCullRedirectsFieldTrial(); 131 132 // Returns whether we should disable culling of redirects in 133 // HistoryURL provider. 134 static bool InHUPCullRedirectsFieldTrialExperimentGroup(); 135 136 // --------------------------------------------------------- 137 // For the HistoryURL provider disable creating a shorter match 138 // field trial. 139 140 // Returns whether the user is in any group for this field trial. 141 // (Should always be true unless initialization went wrong.) 142 static bool InHUPCreateShorterMatchFieldTrial(); 143 144 // Returns whether we should disable creating a shorter match in 145 // HistoryURL provider. 146 static bool InHUPCreateShorterMatchFieldTrialExperimentGroup(); 147 148 // --------------------------------------------------------- 149 // For the AutocompleteController "stop timer" field trial. 150 151 // Returns the duration to be used for the AutocompleteController's stop 152 // timer. Returns the default value of 1.5 seconds if the stop timer 153 // override experiment isn't active or if parsing the experiment-provided 154 // duration fails. 155 static base::TimeDelta StopTimerFieldTrialDuration(); 156 157 // --------------------------------------------------------- 158 // For the ZeroSuggestProvider field trial. 159 160 // Returns whether the user is in any field trial where the 161 // ZeroSuggestProvider should be used to get suggestions when the 162 // user clicks on the omnibox but has not typed anything yet. 163 static bool InZeroSuggestFieldTrial(); 164 165 // Returns whether the user is in a ZeroSuggest field trial, but should 166 // show most visited URL instead. This is used to compare metrics of 167 // ZeroSuggest and most visited suggestions. 168 static bool InZeroSuggestMostVisitedFieldTrial(); 169 170 // Returns whether the user is in a ZeroSuggest field trial and URL-based 171 // suggestions can continue to appear after the user has started typing. 172 static bool InZeroSuggestAfterTypingFieldTrial(); 173 174 // Returns whether the user is in a ZeroSuggest field trial, but should 175 // show recently searched-for queries instead. 176 static bool InZeroSuggestPersonalizedFieldTrial(); 177 178 // --------------------------------------------------------- 179 // For the ShortcutsScoringMaxRelevance experiment that's part of the 180 // bundled omnibox field trial. 181 182 // If the user is in an experiment group that, given the provided 183 // |current_page_classification| context, changes the maximum relevance 184 // ShortcutsProvider::CalculateScore() is supposed to assign, extract 185 // that maximum relevance score and put in in |max_relevance|. Returns 186 // true on a successful extraction. CalculateScore()'s return value is 187 // a product of this maximum relevance score and some attenuating factors 188 // that are all between 0 and 1. (Note that Shortcuts results may have 189 // their scores reduced later if the assigned score is higher than allowed 190 // for non-inlineable results. Shortcuts results are not allowed to be 191 // inlined.) 192 static bool ShortcutsScoringMaxRelevance( 193 metrics::OmniboxEventProto::PageClassification 194 current_page_classification, 195 int* max_relevance); 196 197 // --------------------------------------------------------- 198 // For the SearchHistory experiment that's part of the bundled omnibox 199 // field trial. 200 201 // Returns true if the user is in the experiment group that, given the 202 // provided |current_page_classification| context, scores search history 203 // query suggestions less aggressively so that they don't inline. 204 static bool SearchHistoryPreventInlining( 205 metrics::OmniboxEventProto::PageClassification 206 current_page_classification); 207 208 // Returns true if the user is in the experiment group that, given the 209 // provided |current_page_classification| context, disables all query 210 // suggestions from search history. 211 static bool SearchHistoryDisable( 212 metrics::OmniboxEventProto::PageClassification 213 current_page_classification); 214 215 // --------------------------------------------------------- 216 // For the DemoteByType experiment that's part of the bundled omnibox field 217 // trial. 218 219 // If the user is in an experiment group that, in the provided 220 // |current_page_classification| context, demotes the relevance scores 221 // of certain types of matches, populates the |demotions_by_type| map 222 // appropriately. Otherwise, sets |demotions_by_type| to its default 223 // value based on the context. 224 static void GetDemotionsByType( 225 metrics::OmniboxEventProto::PageClassification 226 current_page_classification, 227 DemotionMultipliers* demotions_by_type); 228 229 // --------------------------------------------------------- 230 // For the HistoryURL provider new scoring experiment that is part of the 231 // bundled omnibox field trial. 232 233 // Initializes the HUP |scoring_params| based on the active HUP scoring 234 // experiment. If there is no such experiment, this function simply sets 235 // |scoring_params|->experimental_scoring_enabled to false. 236 static void GetExperimentalHUPScoringParams(HUPScoringParams* scoring_params); 237 238 // For the HQPBookmarkValue experiment that's part of the 239 // bundled omnibox field trial. 240 241 // Returns the value an untyped visit to a bookmark should receive. 242 // Compare this value with the default of 1 for non-bookmarked untyped 243 // visits to pages and the default of 20 for typed visits. Returns 244 // 10 if the bookmark value experiment isn't active. 245 static int HQPBookmarkValue(); 246 247 // --------------------------------------------------------- 248 // For the HQPAllowMatchInTLD experiment that's part of the 249 // bundled omnibox field trial. 250 251 // Returns true if HQP should allow an input term to match in the 252 // top level domain (e.g., .com) of a URL. Returns false if the 253 // allow match in TLD experiment isn't active. 254 static bool HQPAllowMatchInTLDValue(); 255 256 // --------------------------------------------------------- 257 // For the HQPAllowMatchInScheme experiment that's part of the 258 // bundled omnibox field trial. 259 260 // Returns true if HQP should allow an input term to match in the 261 // scheme (e.g., http://) of a URL. Returns false if the allow 262 // match in scheme experiment isn't active. 263 static bool HQPAllowMatchInSchemeValue(); 264 265 // --------------------------------------------------------- 266 // For the BookmarksIndexURLs experiment that's part of the 267 // bundled omnibox field trial. 268 269 // Returns true if BookmarkIndex should index the URL of bookmarks 270 // (not only the titles) and search for / mark matches in the URLs, 271 // and BookmarkProvider should score bookmarks based on both the 272 // matches in bookmark title and URL. Returns false if the bookmarks 273 // index URLs experiment isn't active. 274 static bool BookmarksIndexURLsValue(); 275 276 // --------------------------------------------------------- 277 // For the DisableInlining experiment that's part of the bundled omnibox 278 // field trial. 279 280 // Returns true if AutocompleteResult should prevent any suggestion with 281 // a non-empty |inline_autocomplete| from being the default match. In 282 // other words, prevent an inline autocompletion from appearing as the 283 // top suggestion / within the omnibox itself, reordering matches as 284 // necessary to make this true. Returns false if the experiment isn't 285 // active. 286 static bool DisableInlining(); 287 288 // --------------------------------------------------------- 289 // For the AnswersInSuggest experiment that's part of the bundled omnibox 290 // field trial. 291 292 // Returns true if the AnswersInSuggest feature should be enabled causing 293 // query responses such as current weather conditions or stock quotes 294 // to be provided in the Omnibox suggestion list. Considers both the 295 // field trial state as well as the overriding command-line flags. 296 static bool EnableAnswersInSuggest(); 297 298 // --------------------------------------------------------- 299 // For the AddUWYTMatchEvenIfPromotedURLs experiment that's part of the 300 // bundled omnibox field trial. 301 302 // Returns true if HistoryURL Provider should add the URL-what-you-typed match 303 // (if valid and reasonable) even if the provider has good inline 304 // autocompletions to offer. Normally HistoryURL does not add the UWYT match 305 // if there are good inline autocompletions, as the user could simply hit 306 // backspace to delete the completion and get the what-you-typed match. 307 // However, in the disable inlining experiment this interaction is a lot more 308 // difficult. The user will have to select a not-inlined suggestion and 309 // backspace (possibly a lot) to get back to the what-you-typed match. 310 // This mode is intended to alleviate the pain by always ensuring that 311 // the UWYT match appears somewhere on the list of suggestions. Returns 312 // false if the experiment isn't active. 313 static bool AddUWYTMatchEvenIfPromotedURLs(); 314 315 // --------------------------------------------------------- 316 // Exposed publicly for the sake of unittests. 317 static const char kBundledExperimentFieldTrialName[]; 318 // Rule names used by the bundled experiment. 319 static const char kShortcutsScoringMaxRelevanceRule[]; 320 static const char kSearchHistoryRule[]; 321 static const char kDemoteByTypeRule[]; 322 static const char kHQPBookmarkValueRule[]; 323 static const char kHQPDiscountFrecencyWhenFewVisitsRule[]; 324 static const char kHQPAllowMatchInTLDRule[]; 325 static const char kHQPAllowMatchInSchemeRule[]; 326 static const char kZeroSuggestRule[]; 327 static const char kZeroSuggestVariantRule[]; 328 static const char kBookmarksIndexURLsRule[]; 329 static const char kDisableInliningRule[]; 330 static const char kAnswersInSuggestRule[]; 331 static const char kAddUWYTMatchEvenIfPromotedURLsRule[]; 332 333 // Parameter names used by the HUP new scoring experiments. 334 static const char kHUPNewScoringEnabledParam[]; 335 static const char kHUPNewScoringTypedCountRelevanceCapParam[]; 336 static const char kHUPNewScoringTypedCountHalfLifeTimeParam[]; 337 static const char kHUPNewScoringTypedCountScoreBucketsParam[]; 338 static const char kHUPNewScoringVisitedCountRelevanceCapParam[]; 339 static const char kHUPNewScoringVisitedCountHalfLifeTimeParam[]; 340 static const char kHUPNewScoringVisitedCountScoreBucketsParam[]; 341 342 private: 343 friend class OmniboxFieldTrialTest; 344 345 // The bundled omnibox experiment comes with a set of parameters 346 // (key-value pairs). Each key indicates a certain rule that applies in 347 // a certain context. The value indicates what the consequences of 348 // applying the rule are. For example, the value of a SearchHistory rule 349 // in the context of a search results page might indicate that we should 350 // prevent search history matches from inlining. 351 // 352 // This function returns the value associated with the |rule| that applies 353 // in the current context (which currently consists of |page_classification| 354 // and whether Instant Extended is enabled). If no such rule exists in the 355 // current context, fall back to the rule in various wildcard contexts and 356 // return its value if found. If the rule remains unfound in the global 357 // context, returns the empty string. For more details, including how we 358 // prioritize different wildcard contexts, see the implementation. How to 359 // interpret the value is left to the caller; this is rule-dependent. 360 static std::string GetValueForRuleInContext( 361 const std::string& rule, 362 metrics::OmniboxEventProto::PageClassification page_classification); 363 364 DISALLOW_IMPLICIT_CONSTRUCTORS(OmniboxFieldTrial); 365 }; 366 367 #endif // CHROME_BROWSER_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ 368