Home | History | Annotate | Download | only in prerender
      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/predictors/autocomplete_action_predictor.h"
     15 #include "chrome/browser/prerender/prerender_manager.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/sync/profile_sync_service.h"
     18 #include "chrome/browser/sync/profile_sync_service_factory.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/common/chrome_version_info.h"
     21 #include "components/metrics/metrics_service.h"
     22 #include "components/variations/variations_associated_data.h"
     23 
     24 using base::FieldTrial;
     25 using base::FieldTrialList;
     26 using base::StringToInt;
     27 using std::string;
     28 using std::vector;
     29 
     30 namespace prerender {
     31 
     32 namespace {
     33 
     34 const char kOmniboxTrialName[] = "PrerenderFromOmnibox";
     35 int g_omnibox_trial_default_group_number = kint32min;
     36 
     37 const char kDisabledGroup[] = "Disabled";
     38 const char kEnabledGroup[] = "Enabled";
     39 
     40 const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec";
     41 const char kLocalPredictorKeyName[] = "LocalPredictor";
     42 const char kLocalPredictorUnencryptedSyncOnlyKeyName[] =
     43     "LocalPredictorUnencryptedSyncOnly";
     44 const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist";
     45 const char kPrerenderLaunchKeyName[] = "PrerenderLaunch";
     46 const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl";
     47 const char kPrerenderPrefetchKeyName[] = "PrerenderPrefetch";
     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 kMaxLaunchPrerenderKeyName[] = "MaxLaunchPrerenders";
     62 const char kSkipFragment[] = "SkipFragment";
     63 const char kSkipHTTPS[] = "SkipHTTPS";
     64 const char kSkipWhitelist[] = "SkipWhitelist";
     65 const char kSkipServiceWhitelist[] = "SkipServiceWhitelist";
     66 const char kSkipLoggedIn[] = "SkipLoggedIn";
     67 const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender";
     68 const char kPrerenderServiceURLPrefixParameterName[] =
     69     "PrerenderServiceURLPrefix";
     70 const char kDefaultPrerenderServiceURLPrefix[] =
     71     "https://clients4.google.com/prerenderservice/?q=";
     72 const int kMinPrerenderServiceTimeoutMs = 1;
     73 const int kMaxPrerenderServiceTimeoutMs = 10000;
     74 const int kDefaultPrerenderServiceTimeoutMs = 1000;
     75 const char kSkipPrerenderLocalCanadidates[] = "SkipPrerenderLocalCandidates";
     76 const char kSkipPrerenderServiceCanadidates[] =
     77     "SkipPrerenderServiceCandidates";
     78 const char kDisableSessionStorageNamespaceMerging[] =
     79     "DisableSessionStorageNamespaceMerging";
     80 const char kPrerenderCookieStore[] = "PrerenderCookieStore";
     81 
     82 void SetupPrerenderFieldTrial() {
     83   const FieldTrial::Probability divisor = 1000;
     84 
     85   FieldTrial::Probability control_probability;
     86   FieldTrial::Probability experiment_multi_prerender_probability;
     87   FieldTrial::Probability experiment_15min_ttl_probability;
     88   FieldTrial::Probability experiment_no_use_probability;
     89 
     90   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
     91   if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
     92       channel == chrome::VersionInfo::CHANNEL_BETA) {
     93     // Use very conservatives and stable settings in beta and stable.
     94     const FieldTrial::Probability release_prerender_enabled_probability = 980;
     95     const FieldTrial::Probability release_control_probability = 10;
     96     const FieldTrial::Probability
     97         release_experiment_multi_prerender_probability = 0;
     98     const FieldTrial::Probability release_experiment_15min_ttl_probability = 10;
     99     const FieldTrial::Probability release_experiment_no_use_probability = 0;
    100     COMPILE_ASSERT(
    101         release_prerender_enabled_probability + release_control_probability +
    102         release_experiment_multi_prerender_probability +
    103         release_experiment_15min_ttl_probability +
    104         release_experiment_no_use_probability == divisor,
    105         release_experiment_probabilities_must_equal_divisor);
    106 
    107     control_probability = release_control_probability;
    108     experiment_multi_prerender_probability =
    109         release_experiment_multi_prerender_probability;
    110     experiment_15min_ttl_probability = release_experiment_15min_ttl_probability;
    111     experiment_no_use_probability = release_experiment_no_use_probability;
    112   } else {
    113     // In testing channels, use more experiments and a larger control group to
    114     // improve quality of data.
    115     const FieldTrial::Probability dev_prerender_enabled_probability = 250;
    116     const FieldTrial::Probability dev_control_probability = 250;
    117     const FieldTrial::Probability
    118         dev_experiment_multi_prerender_probability = 250;
    119     const FieldTrial::Probability dev_experiment_15min_ttl_probability = 125;
    120     const FieldTrial::Probability dev_experiment_no_use_probability = 125;
    121     COMPILE_ASSERT(dev_prerender_enabled_probability + dev_control_probability +
    122                    dev_experiment_multi_prerender_probability +
    123                    dev_experiment_15min_ttl_probability +
    124                    dev_experiment_no_use_probability == divisor,
    125                    dev_experiment_probabilities_must_equal_divisor);
    126 
    127     control_probability = dev_control_probability;
    128     experiment_multi_prerender_probability =
    129         dev_experiment_multi_prerender_probability;
    130     experiment_15min_ttl_probability = dev_experiment_15min_ttl_probability;
    131     experiment_no_use_probability = dev_experiment_no_use_probability;
    132   }
    133 
    134   int prerender_enabled_group = -1;
    135   scoped_refptr<FieldTrial> trial(
    136       FieldTrialList::FactoryGetFieldTrial(
    137           "Prerender", divisor, "PrerenderEnabled",
    138           2014, 12, 31, FieldTrial::SESSION_RANDOMIZED,
    139           &prerender_enabled_group));
    140   const int control_group =
    141       trial->AppendGroup("PrerenderControl",
    142                          control_probability);
    143   const int experiment_multi_prerender_group =
    144       trial->AppendGroup("PrerenderMulti",
    145                          experiment_multi_prerender_probability);
    146   const int experiment_15_min_TTL_group =
    147       trial->AppendGroup("Prerender15minTTL",
    148                          experiment_15min_ttl_probability);
    149   const int experiment_no_use_group =
    150       trial->AppendGroup("PrerenderNoUse",
    151                          experiment_no_use_probability);
    152 
    153   const int trial_group = trial->group();
    154   if (trial_group == prerender_enabled_group) {
    155     PrerenderManager::SetMode(
    156         PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP);
    157   } else if (trial_group == control_group) {
    158     PrerenderManager::SetMode(
    159         PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP);
    160   } else if (trial_group == experiment_multi_prerender_group) {
    161     PrerenderManager::SetMode(
    162         PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP);
    163   } else if (trial_group == experiment_15_min_TTL_group) {
    164     PrerenderManager::SetMode(
    165         PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP);
    166   } else if (trial_group == experiment_no_use_group) {
    167     PrerenderManager::SetMode(
    168         PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP);
    169   } else {
    170     NOTREACHED();
    171   }
    172 }
    173 
    174 }  // end namespace
    175 
    176 void ConfigureOmniboxPrerender();
    177 
    178 void ConfigurePrerender(const CommandLine& command_line) {
    179   enum PrerenderOption {
    180     PRERENDER_OPTION_AUTO,
    181     PRERENDER_OPTION_DISABLED,
    182     PRERENDER_OPTION_ENABLED,
    183   };
    184 
    185   PrerenderOption prerender_option = PRERENDER_OPTION_AUTO;
    186   if (command_line.HasSwitch(switches::kPrerenderMode)) {
    187     const string switch_value =
    188         command_line.GetSwitchValueASCII(switches::kPrerenderMode);
    189 
    190     if (switch_value == switches::kPrerenderModeSwitchValueAuto) {
    191       prerender_option = PRERENDER_OPTION_AUTO;
    192     } else if (switch_value == switches::kPrerenderModeSwitchValueDisabled) {
    193       prerender_option = PRERENDER_OPTION_DISABLED;
    194     } else if (switch_value.empty() ||
    195                switch_value == switches::kPrerenderModeSwitchValueEnabled) {
    196       // The empty string means the option was provided with no value, and that
    197       // means enable.
    198       prerender_option = PRERENDER_OPTION_ENABLED;
    199     } else {
    200       prerender_option = PRERENDER_OPTION_DISABLED;
    201       LOG(ERROR) << "Invalid --prerender option received on command line: "
    202                  << switch_value;
    203       LOG(ERROR) << "Disabling prerendering!";
    204     }
    205   }
    206 
    207   switch (prerender_option) {
    208     case PRERENDER_OPTION_AUTO:
    209       SetupPrerenderFieldTrial();
    210       break;
    211     case PRERENDER_OPTION_DISABLED:
    212       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
    213       break;
    214     case PRERENDER_OPTION_ENABLED:
    215       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
    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           2014, 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_EQ(switches::kPrerenderFromOmniboxSwitchValueAuto, switch_value);
    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   base::SplitString(FieldTrialList::FindFullName(kLocalPredictorSpecTrialName),
    282                     ':', &elements);
    283   for (int i = 0; i < static_cast<int>(elements.size()); i++) {
    284     vector<string> key_value;
    285     base::SplitString(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   return
    306       !CommandLine::ForCurrentProcess()->HasSwitch(
    307           switches::kDisablePrerenderLocalPredictor) &&
    308       GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup;
    309 }
    310 
    311 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) {
    312   return
    313       GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) ==
    314           kEnabledGroup &&
    315       !IsUnencryptedSyncEnabled(profile);
    316 }
    317 
    318 bool IsLoggedInPredictorEnabled() {
    319   return IsLocalPredictorEnabled();
    320 }
    321 
    322 bool IsSideEffectFreeWhitelistEnabled() {
    323   return IsLocalPredictorEnabled() &&
    324       GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
    325       kDisabledGroup;
    326 }
    327 
    328 bool IsLocalPredictorPrerenderLaunchEnabled() {
    329   return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
    330 }
    331 
    332 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
    333   // If we prefetch rather than prerender, we automatically also prerender
    334   // as a control group only.
    335   return (GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) ==
    336           kEnabledGroup) || IsLocalPredictorPrerenderPrefetchEnabled();
    337 }
    338 
    339 bool IsLocalPredictorPrerenderPrefetchEnabled() {
    340   return GetLocalPredictorSpecValue(kPrerenderPrefetchKeyName) ==
    341       kEnabledGroup;
    342 }
    343 
    344 bool ShouldQueryPrerenderService(Profile* profile) {
    345   return IsUnencryptedSyncEnabled(profile) &&
    346       GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) ==
    347       kEnabledGroup;
    348 }
    349 
    350 bool ShouldQueryPrerenderServiceForCurrentURL() {
    351   return GetLocalPredictorSpecValue(
    352       kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup;
    353 }
    354 
    355 bool ShouldQueryPrerenderServiceForCandidateURLs() {
    356   return GetLocalPredictorSpecValue(
    357       kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup;
    358 }
    359 
    360 string GetPrerenderServiceURLPrefix() {
    361   string prefix = chrome_variations::GetVariationParamValue(
    362       kLocalPredictorSpecTrialName,
    363       kPrerenderServiceURLPrefixParameterName);
    364   return prefix.empty() ? kDefaultPrerenderServiceURLPrefix : prefix;
    365 }
    366 
    367 int GetPrerenderServiceBehaviorID() {
    368   int id;
    369   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName),
    370               &id);
    371   // The behavior ID must be non-negative.
    372   return std::max(id, 0);
    373 }
    374 
    375 int GetPrerenderServiceFetchTimeoutMs() {
    376   int result;
    377   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName),
    378               &result);
    379   // If the value is outside the valid range, use the default value.
    380   return (result < kMinPrerenderServiceTimeoutMs ||
    381           result > kMaxPrerenderServiceTimeoutMs) ?
    382       kDefaultPrerenderServiceTimeoutMs : 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   return (ttl < 10 || ttl > 600) ? 180 : ttl;
    390 }
    391 
    392 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
    393   int half_life_time;
    394   StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName),
    395               &half_life_time);
    396   // Sanity check: Ensure the half life time is non-negative.
    397   return std::max(half_life_time, 0);
    398 }
    399 
    400 int GetLocalPredictorMaxConcurrentPrerenders() {
    401   int num_prerenders;
    402   StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName),
    403               &num_prerenders);
    404   // Sanity check: Ensure the number of prerenders is between 1 and 10.
    405   return std::min(std::max(num_prerenders, 1), 10);
    406 }
    407 
    408 int GetLocalPredictorMaxLaunchPrerenders() {
    409   int num_prerenders;
    410   StringToInt(GetLocalPredictorSpecValue(kMaxLaunchPrerenderKeyName),
    411               &num_prerenders);
    412   // Sanity check: Ensure the number of prerenders is between 1 and 10.
    413   return std::min(std::max(num_prerenders, 1), 10);
    414 }
    415 
    416 bool SkipLocalPredictorFragment() {
    417   return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
    418 }
    419 
    420 bool SkipLocalPredictorHTTPS() {
    421   return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
    422 }
    423 
    424 bool SkipLocalPredictorWhitelist() {
    425   return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
    426 }
    427 
    428 bool SkipLocalPredictorServiceWhitelist() {
    429   return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup;
    430 }
    431 
    432 bool SkipLocalPredictorLoggedIn() {
    433   return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
    434 }
    435 
    436 bool SkipLocalPredictorDefaultNoPrerender() {
    437   return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
    438 }
    439 
    440 bool SkipLocalPredictorLocalCandidates() {
    441   return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) ==
    442       kEnabledGroup;
    443 }
    444 
    445 bool SkipLocalPredictorServiceCandidates() {
    446   return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) ==
    447       kEnabledGroup;
    448 }
    449 
    450 bool ShouldMergeSessionStorageNamespaces() {
    451   return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging) !=
    452       kDisabledGroup;
    453 }
    454 
    455 bool IsPrerenderCookieStoreEnabled() {
    456   return GetLocalPredictorSpecValue(kPrerenderCookieStore) != kDisabledGroup &&
    457       FieldTrialList::FindFullName(kPrerenderCookieStore) != kDisabledGroup;
    458 }
    459 
    460 }  // namespace prerender
    461