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 #include "chrome/browser/prerender/prerender_field_trial.h" 6 7 #include "base/command_line.h" 8 #include "base/logging.h" 9 #include "base/metrics/field_trial.h" 10 #include "base/metrics/histogram.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_split.h" 14 #include "chrome/browser/metrics/metrics_service.h" 15 #include "chrome/browser/predictors/autocomplete_action_predictor.h" 16 #include "chrome/browser/prerender/prerender_manager.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "chrome/common/chrome_version_info.h" 20 21 using base::FieldTrial; 22 using base::FieldTrialList; 23 using base::SplitStringUsingSubstr; 24 using base::StringToInt; 25 using std::string; 26 using std::vector; 27 28 namespace prerender { 29 30 namespace { 31 32 const char kOmniboxTrialName[] = "PrerenderFromOmnibox"; 33 int g_omnibox_trial_default_group_number = kint32min; 34 35 const char kDisabledGroup[] = "Disabled"; 36 const char kEnabledGroup[] = "Enabled"; 37 38 const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec"; 39 const char kLocalPredictorKeyName[] = "LocalPredictor"; 40 const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist"; 41 const char kPrerenderLaunchKeyName[] = "PrerenderLaunch"; 42 const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl"; 43 const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds"; 44 const char kPrerenderPriorityHalfLifeTimeKeyName[] = 45 "PrerenderPriorityHalfLifeTimeSeconds"; 46 const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders"; 47 const char kSkipFragment[] = "SkipFragment"; 48 const char kSkipHTTPS[] = "SkipHTTPS"; 49 const char kSkipWhitelist[] = "SkipWhitelist"; 50 const char kSkipLoggedIn[] = "SkipLoggedIn"; 51 const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender"; 52 53 void SetupPrefetchFieldTrial() { 54 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 55 if (channel == chrome::VersionInfo::CHANNEL_STABLE || 56 channel == chrome::VersionInfo::CHANNEL_BETA) { 57 return; 58 } 59 60 const FieldTrial::Probability divisor = 1000; 61 const FieldTrial::Probability prefetch_probability = 500; 62 scoped_refptr<FieldTrial> trial( 63 FieldTrialList::FactoryGetFieldTrial( 64 "Prefetch", divisor, "ContentPrefetchPrefetchOff", 65 2013, 12, 31, FieldTrial::SESSION_RANDOMIZED, NULL)); 66 const int kPrefetchOnGroup = trial->AppendGroup("ContentPrefetchPrefetchOn", 67 prefetch_probability); 68 PrerenderManager::SetIsPrefetchEnabled(trial->group() == kPrefetchOnGroup); 69 } 70 71 void SetupPrerenderFieldTrial() { 72 const FieldTrial::Probability divisor = 1000; 73 74 FieldTrial::Probability control_probability; 75 FieldTrial::Probability experiment_multi_prerender_probability; 76 FieldTrial::Probability experiment_15min_ttl_probability; 77 FieldTrial::Probability experiment_no_use_probability; 78 79 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 80 if (channel == chrome::VersionInfo::CHANNEL_STABLE || 81 channel == chrome::VersionInfo::CHANNEL_BETA) { 82 // Use very conservatives and stable settings in beta and stable. 83 const FieldTrial::Probability release_prerender_enabled_probability = 980; 84 const FieldTrial::Probability release_control_probability = 10; 85 const FieldTrial::Probability 86 release_experiment_multi_prerender_probability = 0; 87 const FieldTrial::Probability release_experiment_15min_ttl_probability = 10; 88 const FieldTrial::Probability release_experiment_no_use_probability = 0; 89 COMPILE_ASSERT( 90 release_prerender_enabled_probability + release_control_probability + 91 release_experiment_multi_prerender_probability + 92 release_experiment_15min_ttl_probability + 93 release_experiment_no_use_probability == divisor, 94 release_experiment_probabilities_must_equal_divisor); 95 96 control_probability = release_control_probability; 97 experiment_multi_prerender_probability = 98 release_experiment_multi_prerender_probability; 99 experiment_15min_ttl_probability = release_experiment_15min_ttl_probability; 100 experiment_no_use_probability = release_experiment_no_use_probability; 101 } else { 102 // In testing channels, use more experiments and a larger control group to 103 // improve quality of data. 104 const FieldTrial::Probability dev_prerender_enabled_probability = 250; 105 const FieldTrial::Probability dev_control_probability = 250; 106 const FieldTrial::Probability 107 dev_experiment_multi_prerender_probability = 250; 108 const FieldTrial::Probability dev_experiment_15min_ttl_probability = 125; 109 const FieldTrial::Probability dev_experiment_no_use_probability = 125; 110 COMPILE_ASSERT(dev_prerender_enabled_probability + dev_control_probability + 111 dev_experiment_multi_prerender_probability + 112 dev_experiment_15min_ttl_probability + 113 dev_experiment_no_use_probability == divisor, 114 dev_experiment_probabilities_must_equal_divisor); 115 116 control_probability = dev_control_probability; 117 experiment_multi_prerender_probability = 118 dev_experiment_multi_prerender_probability; 119 experiment_15min_ttl_probability = dev_experiment_15min_ttl_probability; 120 experiment_no_use_probability = dev_experiment_no_use_probability; 121 } 122 123 int prerender_enabled_group = -1; 124 scoped_refptr<FieldTrial> trial( 125 FieldTrialList::FactoryGetFieldTrial( 126 "Prerender", divisor, "PrerenderEnabled", 127 2013, 12, 31, FieldTrial::SESSION_RANDOMIZED, 128 &prerender_enabled_group)); 129 const int control_group = 130 trial->AppendGroup("PrerenderControl", 131 control_probability); 132 const int experiment_multi_prerender_group = 133 trial->AppendGroup("PrerenderMulti", 134 experiment_multi_prerender_probability); 135 const int experiment_15_min_TTL_group = 136 trial->AppendGroup("Prerender15minTTL", 137 experiment_15min_ttl_probability); 138 const int experiment_no_use_group = 139 trial->AppendGroup("PrerenderNoUse", 140 experiment_no_use_probability); 141 142 const int trial_group = trial->group(); 143 if (trial_group == prerender_enabled_group) { 144 PrerenderManager::SetMode( 145 PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP); 146 } else if (trial_group == control_group) { 147 PrerenderManager::SetMode( 148 PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP); 149 } else if (trial_group == experiment_multi_prerender_group) { 150 PrerenderManager::SetMode( 151 PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP); 152 } else if (trial_group == experiment_15_min_TTL_group) { 153 PrerenderManager::SetMode( 154 PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP); 155 } else if (trial_group == experiment_no_use_group) { 156 PrerenderManager::SetMode( 157 PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP); 158 } else { 159 NOTREACHED(); 160 } 161 } 162 163 } // end namespace 164 165 void ConfigureOmniboxPrerender(); 166 167 void ConfigurePrefetchAndPrerender(const CommandLine& command_line) { 168 enum PrerenderOption { 169 PRERENDER_OPTION_AUTO, 170 PRERENDER_OPTION_DISABLED, 171 PRERENDER_OPTION_ENABLED, 172 PRERENDER_OPTION_PREFETCH_ONLY, 173 }; 174 175 PrerenderOption prerender_option = PRERENDER_OPTION_AUTO; 176 if (command_line.HasSwitch(switches::kPrerenderMode)) { 177 const string switch_value = 178 command_line.GetSwitchValueASCII(switches::kPrerenderMode); 179 180 if (switch_value == switches::kPrerenderModeSwitchValueAuto) { 181 prerender_option = PRERENDER_OPTION_AUTO; 182 } else if (switch_value == switches::kPrerenderModeSwitchValueDisabled) { 183 prerender_option = PRERENDER_OPTION_DISABLED; 184 } else if (switch_value.empty() || 185 switch_value == switches::kPrerenderModeSwitchValueEnabled) { 186 // The empty string means the option was provided with no value, and that 187 // means enable. 188 prerender_option = PRERENDER_OPTION_ENABLED; 189 } else if (switch_value == 190 switches::kPrerenderModeSwitchValuePrefetchOnly) { 191 prerender_option = PRERENDER_OPTION_PREFETCH_ONLY; 192 } else { 193 prerender_option = PRERENDER_OPTION_DISABLED; 194 LOG(ERROR) << "Invalid --prerender option received on command line: " 195 << switch_value; 196 LOG(ERROR) << "Disabling prerendering!"; 197 } 198 } 199 200 switch (prerender_option) { 201 case PRERENDER_OPTION_AUTO: 202 SetupPrefetchFieldTrial(); 203 SetupPrerenderFieldTrial(); 204 break; 205 case PRERENDER_OPTION_DISABLED: 206 PrerenderManager::SetIsPrefetchEnabled(false); 207 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED); 208 break; 209 case PRERENDER_OPTION_ENABLED: 210 PrerenderManager::SetIsPrefetchEnabled(true); 211 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED); 212 break; 213 case PRERENDER_OPTION_PREFETCH_ONLY: 214 PrerenderManager::SetIsPrefetchEnabled(true); 215 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED); 216 break; 217 default: 218 NOTREACHED(); 219 } 220 221 ConfigureOmniboxPrerender(); 222 } 223 224 void ConfigureOmniboxPrerender() { 225 // Field trial to see if we're enabled. 226 const FieldTrial::Probability kDivisor = 100; 227 228 FieldTrial::Probability kDisabledProbability = 10; 229 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 230 if (channel == chrome::VersionInfo::CHANNEL_STABLE || 231 channel == chrome::VersionInfo::CHANNEL_BETA) { 232 kDisabledProbability = 1; 233 } 234 scoped_refptr<FieldTrial> omnibox_prerender_trial( 235 FieldTrialList::FactoryGetFieldTrial( 236 kOmniboxTrialName, kDivisor, "OmniboxPrerenderEnabled", 237 2013, 12, 31, FieldTrial::SESSION_RANDOMIZED, 238 &g_omnibox_trial_default_group_number)); 239 omnibox_prerender_trial->AppendGroup("OmniboxPrerenderDisabled", 240 kDisabledProbability); 241 } 242 243 bool IsOmniboxEnabled(Profile* profile) { 244 if (!profile) 245 return false; 246 247 if (!PrerenderManager::IsPrerenderingPossible()) 248 return false; 249 250 // Override any field trial groups if the user has set a command line flag. 251 if (CommandLine::ForCurrentProcess()->HasSwitch( 252 switches::kPrerenderFromOmnibox)) { 253 const string switch_value = 254 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 255 switches::kPrerenderFromOmnibox); 256 257 if (switch_value == switches::kPrerenderFromOmniboxSwitchValueEnabled) 258 return true; 259 260 if (switch_value == switches::kPrerenderFromOmniboxSwitchValueDisabled) 261 return false; 262 263 DCHECK(switch_value == switches::kPrerenderFromOmniboxSwitchValueAuto); 264 } 265 266 const int group = FieldTrialList::FindValue(kOmniboxTrialName); 267 return group == FieldTrial::kNotFinalized || 268 group == g_omnibox_trial_default_group_number; 269 } 270 271 /* 272 PrerenderLocalPredictorSpec is a field trial, and its value must have the 273 following format: 274 key1=value1:key2=value2:key3=value3 275 eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled" 276 The function below extracts the value corresponding to a key provided from the 277 LocalPredictorSpec. 278 */ 279 string GetLocalPredictorSpecValue(string spec_key) { 280 vector<string> elements; 281 SplitStringUsingSubstr( 282 FieldTrialList::FindFullName(kLocalPredictorSpecTrialName), 283 ":", 284 &elements); 285 for (int i = 0; i < static_cast<int>(elements.size()); i++) { 286 vector<string> key_value; 287 SplitStringUsingSubstr(elements[i], "=", &key_value); 288 if (key_value.size() == 2 && key_value[0] == spec_key) 289 return key_value[1]; 290 } 291 return string(); 292 } 293 294 bool IsLocalPredictorEnabled() { 295 #if defined(OS_ANDROID) || defined(OS_IOS) 296 return false; 297 #endif 298 if (CommandLine::ForCurrentProcess()->HasSwitch( 299 switches::kDisablePrerenderLocalPredictor)) { 300 return false; 301 } 302 return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup; 303 } 304 305 bool IsLoggedInPredictorEnabled() { 306 return IsLocalPredictorEnabled(); 307 } 308 309 bool IsSideEffectFreeWhitelistEnabled() { 310 return IsLocalPredictorEnabled() && 311 GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) != 312 kDisabledGroup; 313 } 314 315 bool IsLocalPredictorPrerenderLaunchEnabled() { 316 return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup; 317 } 318 319 bool IsLocalPredictorPrerenderAlwaysControlEnabled() { 320 return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) == 321 kEnabledGroup; 322 } 323 324 int GetLocalPredictorTTLSeconds() { 325 int ttl; 326 StringToInt(GetLocalPredictorSpecValue(kPrerenderTTLKeyName), &ttl); 327 // If the value is outside of 10s or 600s, use a default value of 180s. 328 if (ttl < 10 || ttl > 600) 329 ttl = 180; 330 return ttl; 331 } 332 333 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() { 334 int half_life_time; 335 StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName), 336 &half_life_time); 337 // Sanity check: Ensure the half life time is non-negative. 338 if (half_life_time < 0) 339 half_life_time = 0; 340 return half_life_time; 341 } 342 343 int GetLocalPredictorMaxConcurrentPrerenders() { 344 int num_prerenders; 345 StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName), 346 &num_prerenders); 347 // Sanity check: Ensure the number of prerenders is at least 1. 348 if (num_prerenders < 1) 349 num_prerenders = 1; 350 // Sanity check: Ensure the number of prerenders is at most 10. 351 if (num_prerenders > 10) 352 num_prerenders = 10; 353 return num_prerenders; 354 }; 355 356 bool SkipLocalPredictorFragment() { 357 return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup; 358 } 359 360 bool SkipLocalPredictorHTTPS() { 361 return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup; 362 } 363 364 bool SkipLocalPredictorWhitelist() { 365 return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup; 366 } 367 368 bool SkipLocalPredictorLoggedIn() { 369 return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup; 370 } 371 372 bool SkipLocalPredictorDefaultNoPrerender() { 373 return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup; 374 } 375 376 } // namespace prerender 377