prerender_field_trial.cc revision 2385ea399aae016c0806a4f9ef3c9cfe3d2a39df
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
21using base::FieldTrial;
22using base::FieldTrialList;
23using base::SplitStringUsingSubstr;
24using base::StringToInt;
25using std::string;
26using std::vector;
27
28namespace prerender {
29
30namespace {
31
32const char kOmniboxTrialName[] = "PrerenderFromOmnibox";
33int g_omnibox_trial_default_group_number = kint32min;
34
35const char kDisabledGroup[] = "Disabled";
36const char kEnabledGroup[] = "Enabled";
37
38const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec";
39const char kLocalPredictorKeyName[] = "LocalPredictor";
40const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist";
41const char kPrerenderLaunchKeyName[] = "PrerenderLaunch";
42const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl";
43const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds";
44const char kPrerenderPriorityHalfLifeTimeKeyName[] =
45    "PrerenderPriorityHalfLifeTimeSeconds";
46const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders";
47const char kSkipFragment[] = "SkipFragment";
48const char kSkipHTTPS[] = "SkipHTTPS";
49const char kSkipWhitelist[] = "SkipWhitelist";
50const char kSkipLoggedIn[] = "SkipLoggedIn";
51const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender";
52
53void 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
71void 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
165void ConfigureOmniboxPrerender();
166
167void 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
224void 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
243bool 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/*
272PrerenderLocalPredictorSpec is a field trial, and its value must have the
273following format:
274key1=value1:key2=value2:key3=value3
275eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled"
276The function below extracts the value corresponding to a key provided from the
277LocalPredictorSpec.
278*/
279string 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
294bool 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
305bool IsLoggedInPredictorEnabled() {
306  return IsLocalPredictorEnabled();
307}
308
309bool IsSideEffectFreeWhitelistEnabled() {
310  return IsLocalPredictorEnabled() &&
311      GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
312      kDisabledGroup;
313}
314
315bool IsLocalPredictorPrerenderLaunchEnabled() {
316  return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
317}
318
319bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
320  return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) ==
321      kEnabledGroup;
322}
323
324int 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
333int 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
343int 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
356bool SkipLocalPredictorFragment() {
357  return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
358}
359
360bool SkipLocalPredictorHTTPS() {
361  return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
362}
363
364bool SkipLocalPredictorWhitelist() {
365  return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
366}
367
368bool SkipLocalPredictorLoggedIn() {
369  return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
370}
371
372bool SkipLocalPredictorDefaultNoPrerender() {
373  return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
374}
375
376}  // namespace prerender
377