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/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