prerender_field_trial.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
26using base::FieldTrial;
27using base::FieldTrialList;
28using base::StringToInt;
29using std::string;
30using std::vector;
31
32namespace prerender {
33
34namespace {
35
36const char kOmniboxTrialName[] = "PrerenderFromOmnibox";
37int g_omnibox_trial_default_group_number = kint32min;
38
39const char kDisabledGroup[] = "Disabled";
40const char kEnabledGroup[] = "Enabled";
41
42const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec";
43const char kLocalPredictorKeyName[] = "LocalPredictor";
44const char kLocalPredictorUnencryptedSyncOnlyKeyName[] =
45    "LocalPredictorUnencryptedSyncOnly";
46const char kLocalPredictorNetworkPredictionEnabledOnly[] =
47    "LocalPredictorNetworkPredictionEnabledOnly";
48const char kLocalPredictorOnCellularOnly[] = "LocalPredictorOnCellularOnly";
49const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist";
50const char kPrerenderLaunchKeyName[] = "PrerenderLaunch";
51const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl";
52const char kPrerenderPrefetchKeyName[] = "PrerenderPrefetch";
53const char kPrerenderQueryPrerenderServiceKeyName[] =
54    "PrerenderQueryPrerenderService";
55const char kPrerenderQueryPrerenderServiceCurrentURLKeyName[] =
56    "PrerenderQueryPrerenderServiceCurrentURL";
57const char kPrerenderQueryPrerenderServiceCandidateURLsKeyName[] =
58    "PrerenderQueryPrerenderServiceCandidateURLs";
59const char kPrerenderServiceBehaviorIDKeyName[] = "PrerenderServiceBehaviorID";
60const char kPrerenderServiceFetchTimeoutKeyName[] =
61    "PrerenderServiceFetchTimeoutMs";
62const char kPrefetchListTimeoutKeyName[] = "PrefetchListTimeoutSeconds";
63const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds";
64const char kPrerenderPriorityHalfLifeTimeKeyName[] =
65    "PrerenderPriorityHalfLifeTimeSeconds";
66const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders";
67const char kMaxLaunchPrerenderKeyName[] = "MaxLaunchPrerenders";
68const char kSkipFragment[] = "SkipFragment";
69const char kSkipHTTPS[] = "SkipHTTPS";
70const char kSkipWhitelist[] = "SkipWhitelist";
71const char kSkipServiceWhitelist[] = "SkipServiceWhitelist";
72const char kSkipLoggedIn[] = "SkipLoggedIn";
73const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender";
74const char kPrerenderServiceURLPrefixParameterName[] =
75    "PrerenderServiceURLPrefix";
76const char kDefaultPrerenderServiceURLPrefix[] =
77    "https://clients4.google.com/prerenderservice/?q=";
78const int kMinPrerenderServiceTimeoutMs = 1;
79const int kMaxPrerenderServiceTimeoutMs = 10000;
80const int kDefaultPrerenderServiceTimeoutMs = 1000;
81const int kMinPrefetchListTimeoutSeconds = 1;
82const int kMaxPrefetchListTimeoutSeconds = 1800;
83const int kDefaultPrefetchListTimeoutSeconds = 300;
84const char kSkipPrerenderLocalCanadidates[] = "SkipPrerenderLocalCandidates";
85const char kSkipPrerenderServiceCanadidates[] =
86    "SkipPrerenderServiceCandidates";
87const char kDisableSessionStorageNamespaceMerging[] =
88    "DisableSessionStorageNamespaceMerging";
89const char kPrerenderCookieStore[] = "PrerenderCookieStore";
90
91void 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
204void ConfigureOmniboxPrerender();
205
206void 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
252void 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
271bool 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/*
300PrerenderLocalPredictorSpec is a field trial, and its value must have the
301following format:
302key1=value1:key2=value2:key3=value3
303eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled"
304The function below extracts the value corresponding to a key provided from the
305LocalPredictorSpec.
306*/
307string 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
320bool 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.
329bool 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
339bool ShouldDisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) {
340  return
341      GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) ==
342          kEnabledGroup &&
343      !IsUnencryptedSyncEnabled(profile);
344}
345
346bool 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
371bool IsLoggedInPredictorEnabled() {
372  return IsLocalPredictorEnabled();
373}
374
375bool IsSideEffectFreeWhitelistEnabled() {
376  return IsLocalPredictorEnabled() &&
377      GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
378      kDisabledGroup;
379}
380
381bool IsLocalPredictorPrerenderLaunchEnabled() {
382  return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
383}
384
385bool 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
392bool IsLocalPredictorPrerenderPrefetchEnabled() {
393  return GetLocalPredictorSpecValue(kPrerenderPrefetchKeyName) ==
394      kEnabledGroup;
395}
396
397bool ShouldQueryPrerenderService(Profile* profile) {
398  return IsUnencryptedSyncEnabled(profile) &&
399      GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) ==
400      kEnabledGroup;
401}
402
403bool ShouldQueryPrerenderServiceForCurrentURL() {
404  return GetLocalPredictorSpecValue(
405      kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup;
406}
407
408bool ShouldQueryPrerenderServiceForCandidateURLs() {
409  return GetLocalPredictorSpecValue(
410      kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup;
411}
412
413string GetPrerenderServiceURLPrefix() {
414  string prefix = variations::GetVariationParamValue(
415      kLocalPredictorSpecTrialName,
416      kPrerenderServiceURLPrefixParameterName);
417  return prefix.empty() ? kDefaultPrerenderServiceURLPrefix : prefix;
418}
419
420int 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
428int 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
438int 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
447int 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
454int 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
462int 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
470int 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
478bool SkipLocalPredictorFragment() {
479  return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
480}
481
482bool SkipLocalPredictorHTTPS() {
483  return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
484}
485
486bool SkipLocalPredictorWhitelist() {
487  return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
488}
489
490bool SkipLocalPredictorServiceWhitelist() {
491  return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup;
492}
493
494bool SkipLocalPredictorLoggedIn() {
495  return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
496}
497
498bool SkipLocalPredictorDefaultNoPrerender() {
499  return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
500}
501
502bool SkipLocalPredictorLocalCandidates() {
503  return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) ==
504      kEnabledGroup;
505}
506
507bool SkipLocalPredictorServiceCandidates() {
508  return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) ==
509      kEnabledGroup;
510}
511
512bool ShouldMergeSessionStorageNamespaces() {
513  return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging) !=
514      kDisabledGroup;
515}
516
517bool IsPrerenderCookieStoreEnabled() {
518  return GetLocalPredictorSpecValue(kPrerenderCookieStore) != kDisabledGroup &&
519      FieldTrialList::FindFullName(kPrerenderCookieStore) != kDisabledGroup;
520}
521
522}  // namespace prerender
523