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/browser/sync/profile_sync_service.h" 19 #include "chrome/browser/sync/profile_sync_service_factory.h" 20 #include "chrome/common/chrome_switches.h" 21 #include "chrome/common/chrome_version_info.h" 22 #include "components/variations/variations_associated_data.h" 23 24 using base::FieldTrial; 25 using base::FieldTrialList; 26 using base::SplitStringUsingSubstr; 27 using base::StringToInt; 28 using std::string; 29 using std::vector; 30 31 namespace prerender { 32 33 namespace { 34 35 const char kOmniboxTrialName[] = "PrerenderFromOmnibox"; 36 int g_omnibox_trial_default_group_number = kint32min; 37 38 const char kDisabledGroup[] = "Disabled"; 39 const char kEnabledGroup[] = "Enabled"; 40 41 const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec"; 42 const char kLocalPredictorKeyName[] = "LocalPredictor"; 43 const char kLocalPredictorUnencryptedSyncOnlyKeyName[] = 44 "LocalPredictorUnencryptedSyncOnly"; 45 const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist"; 46 const char kPrerenderLaunchKeyName[] = "PrerenderLaunch"; 47 const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl"; 48 const char kPrerenderQueryPrerenderServiceKeyName[] = 49 "PrerenderQueryPrerenderService"; 50 const char kPrerenderQueryPrerenderServiceCurrentURLKeyName[] = 51 "PrerenderQueryPrerenderServiceCurrentURL"; 52 const char kPrerenderQueryPrerenderServiceCandidateURLsKeyName[] = 53 "PrerenderQueryPrerenderServiceCandidateURLs"; 54 const char kPrerenderServiceBehaviorIDKeyName[] = "PrerenderServiceBehaviorID"; 55 const char kPrerenderServiceFetchTimeoutKeyName[] = 56 "PrerenderServiceFetchTimeoutMs"; 57 const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds"; 58 const char kPrerenderPriorityHalfLifeTimeKeyName[] = 59 "PrerenderPriorityHalfLifeTimeSeconds"; 60 const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders"; 61 const char kSkipFragment[] = "SkipFragment"; 62 const char kSkipHTTPS[] = "SkipHTTPS"; 63 const char kSkipWhitelist[] = "SkipWhitelist"; 64 const char kSkipServiceWhitelist[] = "SkipServiceWhitelist"; 65 const char kSkipLoggedIn[] = "SkipLoggedIn"; 66 const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender"; 67 const char kPrerenderServiceURLPrefixParameterName[] = 68 "PrerenderServiceURLPrefix"; 69 const char kDefaultPrerenderServiceURLPrefix[] = 70 "https://clients4.google.com/prerenderservice/?q="; 71 const int kMinPrerenderServiceTimeoutMs = 1; 72 const int kMaxPrerenderServiceTimeoutMs = 10000; 73 const int kDefaultPrerenderServiceTimeoutMs = 1000; 74 const char kSkipPrerenderLocalCanadidates[] = "SkipPrerenderLocalCandidates"; 75 const char kSkipPrerenderServiceCanadidates[] = 76 "SkipPrerenderServiceCandidates"; 77 const char kDisableSessionStorageNamespaceMerging[] = 78 "DisableSessionStorageNamespaceMerging"; 79 80 void SetupPrerenderFieldTrial() { 81 const FieldTrial::Probability divisor = 1000; 82 83 FieldTrial::Probability control_probability; 84 FieldTrial::Probability experiment_multi_prerender_probability; 85 FieldTrial::Probability experiment_15min_ttl_probability; 86 FieldTrial::Probability experiment_no_use_probability; 87 88 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 89 if (channel == chrome::VersionInfo::CHANNEL_STABLE || 90 channel == chrome::VersionInfo::CHANNEL_BETA) { 91 // Use very conservatives and stable settings in beta and stable. 92 const FieldTrial::Probability release_prerender_enabled_probability = 980; 93 const FieldTrial::Probability release_control_probability = 10; 94 const FieldTrial::Probability 95 release_experiment_multi_prerender_probability = 0; 96 const FieldTrial::Probability release_experiment_15min_ttl_probability = 10; 97 const FieldTrial::Probability release_experiment_no_use_probability = 0; 98 COMPILE_ASSERT( 99 release_prerender_enabled_probability + release_control_probability + 100 release_experiment_multi_prerender_probability + 101 release_experiment_15min_ttl_probability + 102 release_experiment_no_use_probability == divisor, 103 release_experiment_probabilities_must_equal_divisor); 104 105 control_probability = release_control_probability; 106 experiment_multi_prerender_probability = 107 release_experiment_multi_prerender_probability; 108 experiment_15min_ttl_probability = release_experiment_15min_ttl_probability; 109 experiment_no_use_probability = release_experiment_no_use_probability; 110 } else { 111 // In testing channels, use more experiments and a larger control group to 112 // improve quality of data. 113 const FieldTrial::Probability dev_prerender_enabled_probability = 250; 114 const FieldTrial::Probability dev_control_probability = 250; 115 const FieldTrial::Probability 116 dev_experiment_multi_prerender_probability = 250; 117 const FieldTrial::Probability dev_experiment_15min_ttl_probability = 125; 118 const FieldTrial::Probability dev_experiment_no_use_probability = 125; 119 COMPILE_ASSERT(dev_prerender_enabled_probability + dev_control_probability + 120 dev_experiment_multi_prerender_probability + 121 dev_experiment_15min_ttl_probability + 122 dev_experiment_no_use_probability == divisor, 123 dev_experiment_probabilities_must_equal_divisor); 124 125 control_probability = dev_control_probability; 126 experiment_multi_prerender_probability = 127 dev_experiment_multi_prerender_probability; 128 experiment_15min_ttl_probability = dev_experiment_15min_ttl_probability; 129 experiment_no_use_probability = dev_experiment_no_use_probability; 130 } 131 132 int prerender_enabled_group = -1; 133 scoped_refptr<FieldTrial> trial( 134 FieldTrialList::FactoryGetFieldTrial( 135 "Prerender", divisor, "PrerenderEnabled", 136 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED, 137 &prerender_enabled_group)); 138 const int control_group = 139 trial->AppendGroup("PrerenderControl", 140 control_probability); 141 const int experiment_multi_prerender_group = 142 trial->AppendGroup("PrerenderMulti", 143 experiment_multi_prerender_probability); 144 const int experiment_15_min_TTL_group = 145 trial->AppendGroup("Prerender15minTTL", 146 experiment_15min_ttl_probability); 147 const int experiment_no_use_group = 148 trial->AppendGroup("PrerenderNoUse", 149 experiment_no_use_probability); 150 151 const int trial_group = trial->group(); 152 if (trial_group == prerender_enabled_group) { 153 PrerenderManager::SetMode( 154 PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP); 155 } else if (trial_group == control_group) { 156 PrerenderManager::SetMode( 157 PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP); 158 } else if (trial_group == experiment_multi_prerender_group) { 159 PrerenderManager::SetMode( 160 PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP); 161 } else if (trial_group == experiment_15_min_TTL_group) { 162 PrerenderManager::SetMode( 163 PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP); 164 } else if (trial_group == experiment_no_use_group) { 165 PrerenderManager::SetMode( 166 PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP); 167 } else { 168 NOTREACHED(); 169 } 170 } 171 172 } // end namespace 173 174 void ConfigureOmniboxPrerender(); 175 176 void ConfigurePrerender(const CommandLine& command_line) { 177 enum PrerenderOption { 178 PRERENDER_OPTION_AUTO, 179 PRERENDER_OPTION_DISABLED, 180 PRERENDER_OPTION_ENABLED, 181 }; 182 183 PrerenderOption prerender_option = PRERENDER_OPTION_AUTO; 184 if (command_line.HasSwitch(switches::kPrerenderMode)) { 185 const string switch_value = 186 command_line.GetSwitchValueASCII(switches::kPrerenderMode); 187 188 if (switch_value == switches::kPrerenderModeSwitchValueAuto) { 189 prerender_option = PRERENDER_OPTION_AUTO; 190 } else if (switch_value == switches::kPrerenderModeSwitchValueDisabled) { 191 prerender_option = PRERENDER_OPTION_DISABLED; 192 } else if (switch_value.empty() || 193 switch_value == switches::kPrerenderModeSwitchValueEnabled) { 194 // The empty string means the option was provided with no value, and that 195 // means enable. 196 prerender_option = PRERENDER_OPTION_ENABLED; 197 } else { 198 prerender_option = PRERENDER_OPTION_DISABLED; 199 LOG(ERROR) << "Invalid --prerender option received on command line: " 200 << switch_value; 201 LOG(ERROR) << "Disabling prerendering!"; 202 } 203 } 204 205 switch (prerender_option) { 206 case PRERENDER_OPTION_AUTO: 207 SetupPrerenderFieldTrial(); 208 break; 209 case PRERENDER_OPTION_DISABLED: 210 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED); 211 break; 212 case PRERENDER_OPTION_ENABLED: 213 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED); 214 break; 215 default: 216 NOTREACHED(); 217 } 218 219 ConfigureOmniboxPrerender(); 220 } 221 222 void ConfigureOmniboxPrerender() { 223 // Field trial to see if we're enabled. 224 const FieldTrial::Probability kDivisor = 100; 225 226 FieldTrial::Probability kDisabledProbability = 10; 227 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 228 if (channel == chrome::VersionInfo::CHANNEL_STABLE || 229 channel == chrome::VersionInfo::CHANNEL_BETA) { 230 kDisabledProbability = 1; 231 } 232 scoped_refptr<FieldTrial> omnibox_prerender_trial( 233 FieldTrialList::FactoryGetFieldTrial( 234 kOmniboxTrialName, kDivisor, "OmniboxPrerenderEnabled", 235 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED, 236 &g_omnibox_trial_default_group_number)); 237 omnibox_prerender_trial->AppendGroup("OmniboxPrerenderDisabled", 238 kDisabledProbability); 239 } 240 241 bool IsOmniboxEnabled(Profile* profile) { 242 if (!profile) 243 return false; 244 245 if (!PrerenderManager::IsPrerenderingPossible()) 246 return false; 247 248 // Override any field trial groups if the user has set a command line flag. 249 if (CommandLine::ForCurrentProcess()->HasSwitch( 250 switches::kPrerenderFromOmnibox)) { 251 const string switch_value = 252 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 253 switches::kPrerenderFromOmnibox); 254 255 if (switch_value == switches::kPrerenderFromOmniboxSwitchValueEnabled) 256 return true; 257 258 if (switch_value == switches::kPrerenderFromOmniboxSwitchValueDisabled) 259 return false; 260 261 DCHECK(switch_value == switches::kPrerenderFromOmniboxSwitchValueAuto); 262 } 263 264 const int group = FieldTrialList::FindValue(kOmniboxTrialName); 265 return group == FieldTrial::kNotFinalized || 266 group == g_omnibox_trial_default_group_number; 267 } 268 269 /* 270 PrerenderLocalPredictorSpec is a field trial, and its value must have the 271 following format: 272 key1=value1:key2=value2:key3=value3 273 eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled" 274 The function below extracts the value corresponding to a key provided from the 275 LocalPredictorSpec. 276 */ 277 string GetLocalPredictorSpecValue(string spec_key) { 278 vector<string> elements; 279 SplitStringUsingSubstr( 280 FieldTrialList::FindFullName(kLocalPredictorSpecTrialName), 281 ":", 282 &elements); 283 for (int i = 0; i < static_cast<int>(elements.size()); i++) { 284 vector<string> key_value; 285 SplitStringUsingSubstr(elements[i], "=", &key_value); 286 if (key_value.size() == 2 && key_value[0] == spec_key) 287 return key_value[1]; 288 } 289 return string(); 290 } 291 292 bool IsUnencryptedSyncEnabled(Profile* profile) { 293 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()-> 294 GetForProfile(profile); 295 return service && service->GetOpenTabsUIDelegate() && 296 !service->EncryptEverythingEnabled(); 297 } 298 299 // Indicates whether the Local Predictor is enabled based on field trial 300 // selection. 301 bool IsLocalPredictorEnabled() { 302 #if defined(OS_ANDROID) || defined(OS_IOS) 303 return false; 304 #endif 305 if (CommandLine::ForCurrentProcess()->HasSwitch( 306 switches::kDisablePrerenderLocalPredictor)) { 307 return false; 308 } 309 return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup; 310 } 311 312 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) { 313 return 314 GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) == 315 kEnabledGroup && 316 !IsUnencryptedSyncEnabled(profile); 317 } 318 319 bool IsLoggedInPredictorEnabled() { 320 return IsLocalPredictorEnabled(); 321 } 322 323 bool IsSideEffectFreeWhitelistEnabled() { 324 return IsLocalPredictorEnabled() && 325 GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) != 326 kDisabledGroup; 327 } 328 329 bool IsLocalPredictorPrerenderLaunchEnabled() { 330 return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup; 331 } 332 333 bool IsLocalPredictorPrerenderAlwaysControlEnabled() { 334 return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) == 335 kEnabledGroup; 336 } 337 338 bool ShouldQueryPrerenderService(Profile* profile) { 339 return IsUnencryptedSyncEnabled(profile) && 340 GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) == 341 kEnabledGroup; 342 } 343 344 bool ShouldQueryPrerenderServiceForCurrentURL() { 345 return GetLocalPredictorSpecValue( 346 kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup; 347 } 348 349 bool ShouldQueryPrerenderServiceForCandidateURLs() { 350 return GetLocalPredictorSpecValue( 351 kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup; 352 } 353 354 string GetPrerenderServiceURLPrefix() { 355 string prefix = chrome_variations::GetVariationParamValue( 356 kLocalPredictorSpecTrialName, 357 kPrerenderServiceURLPrefixParameterName); 358 if (prefix.empty()) 359 prefix = kDefaultPrerenderServiceURLPrefix; 360 return prefix; 361 } 362 363 int GetPrerenderServiceBehaviorID() { 364 int id; 365 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName), 366 &id); 367 // The behavior ID must be non-negative. 368 if (id < 0) 369 id = 0; 370 return id; 371 } 372 373 int GetPrerenderServiceFetchTimeoutMs() { 374 int result; 375 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName), 376 &result); 377 // The behavior ID must be non-negative. 378 if (result < kMinPrerenderServiceTimeoutMs || 379 result > kMaxPrerenderServiceTimeoutMs) { 380 result = kDefaultPrerenderServiceTimeoutMs; 381 } 382 return result; 383 } 384 385 int GetLocalPredictorTTLSeconds() { 386 int ttl; 387 StringToInt(GetLocalPredictorSpecValue(kPrerenderTTLKeyName), &ttl); 388 // If the value is outside of 10s or 600s, use a default value of 180s. 389 if (ttl < 10 || ttl > 600) 390 ttl = 180; 391 return ttl; 392 } 393 394 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() { 395 int half_life_time; 396 StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName), 397 &half_life_time); 398 // Sanity check: Ensure the half life time is non-negative. 399 if (half_life_time < 0) 400 half_life_time = 0; 401 return half_life_time; 402 } 403 404 int GetLocalPredictorMaxConcurrentPrerenders() { 405 int num_prerenders; 406 StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName), 407 &num_prerenders); 408 // Sanity check: Ensure the number of prerenders is at least 1. 409 if (num_prerenders < 1) 410 num_prerenders = 1; 411 // Sanity check: Ensure the number of prerenders is at most 10. 412 if (num_prerenders > 10) 413 num_prerenders = 10; 414 return num_prerenders; 415 }; 416 417 bool SkipLocalPredictorFragment() { 418 return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup; 419 } 420 421 bool SkipLocalPredictorHTTPS() { 422 return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup; 423 } 424 425 bool SkipLocalPredictorWhitelist() { 426 return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup; 427 } 428 429 bool SkipLocalPredictorServiceWhitelist() { 430 return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup; 431 } 432 433 bool SkipLocalPredictorLoggedIn() { 434 return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup; 435 } 436 437 bool SkipLocalPredictorDefaultNoPrerender() { 438 return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup; 439 } 440 441 bool SkipLocalPredictorLocalCandidates() { 442 return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) == 443 kEnabledGroup; 444 } 445 446 bool SkipLocalPredictorServiceCandidates() { 447 return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) == 448 kEnabledGroup; 449 } 450 451 bool ShouldMergeSessionStorageNamespaces() { 452 return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging) != 453 kDisabledGroup; 454 } 455 456 } // namespace prerender 457