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