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 24using base::FieldTrial; 25using base::FieldTrialList; 26using base::SplitStringUsingSubstr; 27using base::StringToInt; 28using std::string; 29using std::vector; 30 31namespace prerender { 32 33namespace { 34 35const char kOmniboxTrialName[] = "PrerenderFromOmnibox"; 36int g_omnibox_trial_default_group_number = kint32min; 37 38const char kDisabledGroup[] = "Disabled"; 39const char kEnabledGroup[] = "Enabled"; 40 41const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec"; 42const char kLocalPredictorKeyName[] = "LocalPredictor"; 43const char kLocalPredictorUnencryptedSyncOnlyKeyName[] = 44 "LocalPredictorUnencryptedSyncOnly"; 45const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist"; 46const char kPrerenderLaunchKeyName[] = "PrerenderLaunch"; 47const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl"; 48const char kPrerenderQueryPrerenderServiceKeyName[] = 49 "PrerenderQueryPrerenderService"; 50const char kPrerenderQueryPrerenderServiceCurrentURLKeyName[] = 51 "PrerenderQueryPrerenderServiceCurrentURL"; 52const char kPrerenderQueryPrerenderServiceCandidateURLsKeyName[] = 53 "PrerenderQueryPrerenderServiceCandidateURLs"; 54const char kPrerenderServiceBehaviorIDKeyName[] = "PrerenderServiceBehaviorID"; 55const char kPrerenderServiceFetchTimeoutKeyName[] = 56 "PrerenderServiceFetchTimeoutMs"; 57const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds"; 58const char kPrerenderPriorityHalfLifeTimeKeyName[] = 59 "PrerenderPriorityHalfLifeTimeSeconds"; 60const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders"; 61const char kSkipFragment[] = "SkipFragment"; 62const char kSkipHTTPS[] = "SkipHTTPS"; 63const char kSkipWhitelist[] = "SkipWhitelist"; 64const char kSkipServiceWhitelist[] = "SkipServiceWhitelist"; 65const char kSkipLoggedIn[] = "SkipLoggedIn"; 66const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender"; 67const char kPrerenderServiceURLPrefixParameterName[] = 68 "PrerenderServiceURLPrefix"; 69const char kDefaultPrerenderServiceURLPrefix[] = 70 "https://clients4.google.com/prerenderservice/?q="; 71const int kMinPrerenderServiceTimeoutMs = 1; 72const int kMaxPrerenderServiceTimeoutMs = 10000; 73const int kDefaultPrerenderServiceTimeoutMs = 1000; 74const char kSkipPrerenderLocalCanadidates[] = "SkipPrerenderLocalCandidates"; 75const char kSkipPrerenderServiceCanadidates[] = 76 "SkipPrerenderServiceCandidates"; 77const char kDisableSessionStorageNamespaceMerging[] = 78 "DisableSessionStorageNamespaceMerging"; 79 80void 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 174void ConfigureOmniboxPrerender(); 175 176void 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 222void 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 241bool 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/* 270PrerenderLocalPredictorSpec is a field trial, and its value must have the 271following format: 272key1=value1:key2=value2:key3=value3 273eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled" 274The function below extracts the value corresponding to a key provided from the 275LocalPredictorSpec. 276*/ 277string 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 292bool 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. 301bool 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 312bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) { 313 return 314 GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) == 315 kEnabledGroup && 316 !IsUnencryptedSyncEnabled(profile); 317} 318 319bool IsLoggedInPredictorEnabled() { 320 return IsLocalPredictorEnabled(); 321} 322 323bool IsSideEffectFreeWhitelistEnabled() { 324 return IsLocalPredictorEnabled() && 325 GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) != 326 kDisabledGroup; 327} 328 329bool IsLocalPredictorPrerenderLaunchEnabled() { 330 return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup; 331} 332 333bool IsLocalPredictorPrerenderAlwaysControlEnabled() { 334 return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) == 335 kEnabledGroup; 336} 337 338bool ShouldQueryPrerenderService(Profile* profile) { 339 return IsUnencryptedSyncEnabled(profile) && 340 GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) == 341 kEnabledGroup; 342} 343 344bool ShouldQueryPrerenderServiceForCurrentURL() { 345 return GetLocalPredictorSpecValue( 346 kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup; 347} 348 349bool ShouldQueryPrerenderServiceForCandidateURLs() { 350 return GetLocalPredictorSpecValue( 351 kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup; 352} 353 354string GetPrerenderServiceURLPrefix() { 355 string prefix = chrome_variations::GetVariationParamValue( 356 kLocalPredictorSpecTrialName, 357 kPrerenderServiceURLPrefixParameterName); 358 if (prefix.empty()) 359 prefix = kDefaultPrerenderServiceURLPrefix; 360 return prefix; 361} 362 363int 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 373int 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 385int 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 394int 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 404int 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 417bool SkipLocalPredictorFragment() { 418 return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup; 419} 420 421bool SkipLocalPredictorHTTPS() { 422 return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup; 423} 424 425bool SkipLocalPredictorWhitelist() { 426 return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup; 427} 428 429bool SkipLocalPredictorServiceWhitelist() { 430 return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup; 431} 432 433bool SkipLocalPredictorLoggedIn() { 434 return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup; 435} 436 437bool SkipLocalPredictorDefaultNoPrerender() { 438 return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup; 439} 440 441bool SkipLocalPredictorLocalCandidates() { 442 return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) == 443 kEnabledGroup; 444} 445 446bool SkipLocalPredictorServiceCandidates() { 447 return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) == 448 kEnabledGroup; 449} 450 451bool ShouldMergeSessionStorageNamespaces() { 452 return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging) != 453 kDisabledGroup; 454} 455 456} // namespace prerender 457