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.
5 #include "chrome/browser/prerender/prerender_field_trial.h"
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/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 "components/variations/variations_associated_data.h"
24 using base::FieldTrial;
25 using base::FieldTrialList;
26 using base::SplitStringUsingSubstr;
27 using base::StringToInt;
35 const char kOmniboxTrialName[] = "PrerenderFromOmnibox";
36 int g_omnibox_trial_default_group_number = kint32min;
38 const char kDisabledGroup[] = "Disabled";
39 const char kEnabledGroup[] = "Enabled";
41 const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec";
42 const char kLocalPredictorKeyName[] = "LocalPredictor";
43 const char kLocalPredictorUnencryptedSyncOnlyKeyName[] =
44 "LocalPredictorUnencryptedSyncOnly";
45 const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist";
46 const char kPrerenderLaunchKeyName[] = "PrerenderLaunch";
47 const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl";
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 kSkipFragment[] = "SkipFragment";
62 const char kSkipHTTPS[] = "SkipHTTPS";
63 const char kSkipWhitelist[] = "SkipWhitelist";
64 const char kSkipServiceWhitelist[] = "SkipServiceWhitelist";
65 const char kSkipLoggedIn[] = "SkipLoggedIn";
66 const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender";
67 const char kPrerenderServiceURLPrefixParameterName[] =
68 "PrerenderServiceURLPrefix";
69 const char kDefaultPrerenderServiceURLPrefix[] =
70 "https://clients4.google.com/prerenderservice/?q=";
71 const int kMinPrerenderServiceTimeoutMs = 1;
72 const int kMaxPrerenderServiceTimeoutMs = 10000;
73 const int kDefaultPrerenderServiceTimeoutMs = 1000;
74 const char kSkipPrerenderLocalCanadidates[] = "SkipPrerenderLocalCandidates";
75 const char kSkipPrerenderServiceCanadidates[] =
76 "SkipPrerenderServiceCandidates";
79 void SetupPrefetchFieldTrial() {
80 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
81 if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
82 channel == chrome::VersionInfo::CHANNEL_BETA) {
86 const FieldTrial::Probability divisor = 1000;
87 const FieldTrial::Probability prefetch_probability = 500;
88 scoped_refptr<FieldTrial> trial(
89 FieldTrialList::FactoryGetFieldTrial(
90 "Prefetch", divisor, "ContentPrefetchPrefetchOff",
91 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED, NULL));
92 const int kPrefetchOnGroup = trial->AppendGroup("ContentPrefetchPrefetchOn",
93 prefetch_probability);
94 PrerenderManager::SetIsPrefetchEnabled(trial->group() == kPrefetchOnGroup);
97 void SetupPrerenderFieldTrial() {
98 const FieldTrial::Probability divisor = 1000;
100 FieldTrial::Probability control_probability;
101 FieldTrial::Probability experiment_multi_prerender_probability;
102 FieldTrial::Probability experiment_15min_ttl_probability;
103 FieldTrial::Probability experiment_no_use_probability;
105 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
106 if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
107 channel == chrome::VersionInfo::CHANNEL_BETA) {
108 // Use very conservatives and stable settings in beta and stable.
109 const FieldTrial::Probability release_prerender_enabled_probability = 980;
110 const FieldTrial::Probability release_control_probability = 10;
111 const FieldTrial::Probability
112 release_experiment_multi_prerender_probability = 0;
113 const FieldTrial::Probability release_experiment_15min_ttl_probability = 10;
114 const FieldTrial::Probability release_experiment_no_use_probability = 0;
116 release_prerender_enabled_probability + release_control_probability +
117 release_experiment_multi_prerender_probability +
118 release_experiment_15min_ttl_probability +
119 release_experiment_no_use_probability == divisor,
120 release_experiment_probabilities_must_equal_divisor);
122 control_probability = release_control_probability;
123 experiment_multi_prerender_probability =
124 release_experiment_multi_prerender_probability;
125 experiment_15min_ttl_probability = release_experiment_15min_ttl_probability;
126 experiment_no_use_probability = release_experiment_no_use_probability;
128 // In testing channels, use more experiments and a larger control group to
129 // improve quality of data.
130 const FieldTrial::Probability dev_prerender_enabled_probability = 250;
131 const FieldTrial::Probability dev_control_probability = 250;
132 const FieldTrial::Probability
133 dev_experiment_multi_prerender_probability = 250;
134 const FieldTrial::Probability dev_experiment_15min_ttl_probability = 125;
135 const FieldTrial::Probability dev_experiment_no_use_probability = 125;
136 COMPILE_ASSERT(dev_prerender_enabled_probability + dev_control_probability +
137 dev_experiment_multi_prerender_probability +
138 dev_experiment_15min_ttl_probability +
139 dev_experiment_no_use_probability == divisor,
140 dev_experiment_probabilities_must_equal_divisor);
142 control_probability = dev_control_probability;
143 experiment_multi_prerender_probability =
144 dev_experiment_multi_prerender_probability;
145 experiment_15min_ttl_probability = dev_experiment_15min_ttl_probability;
146 experiment_no_use_probability = dev_experiment_no_use_probability;
149 int prerender_enabled_group = -1;
150 scoped_refptr<FieldTrial> trial(
151 FieldTrialList::FactoryGetFieldTrial(
152 "Prerender", divisor, "PrerenderEnabled",
153 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED,
154 &prerender_enabled_group));
155 const int control_group =
156 trial->AppendGroup("PrerenderControl",
157 control_probability);
158 const int experiment_multi_prerender_group =
159 trial->AppendGroup("PrerenderMulti",
160 experiment_multi_prerender_probability);
161 const int experiment_15_min_TTL_group =
162 trial->AppendGroup("Prerender15minTTL",
163 experiment_15min_ttl_probability);
164 const int experiment_no_use_group =
165 trial->AppendGroup("PrerenderNoUse",
166 experiment_no_use_probability);
168 const int trial_group = trial->group();
169 if (trial_group == prerender_enabled_group) {
170 PrerenderManager::SetMode(
171 PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP);
172 } else if (trial_group == control_group) {
173 PrerenderManager::SetMode(
174 PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP);
175 } else if (trial_group == experiment_multi_prerender_group) {
176 PrerenderManager::SetMode(
177 PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP);
178 } else if (trial_group == experiment_15_min_TTL_group) {
179 PrerenderManager::SetMode(
180 PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP);
181 } else if (trial_group == experiment_no_use_group) {
182 PrerenderManager::SetMode(
183 PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP);
191 void ConfigureOmniboxPrerender();
193 void ConfigurePrefetchAndPrerender(const CommandLine& command_line) {
194 enum PrerenderOption {
195 PRERENDER_OPTION_AUTO,
196 PRERENDER_OPTION_DISABLED,
197 PRERENDER_OPTION_ENABLED,
198 PRERENDER_OPTION_PREFETCH_ONLY,
201 PrerenderOption prerender_option = PRERENDER_OPTION_AUTO;
202 if (command_line.HasSwitch(switches::kPrerenderMode)) {
203 const string switch_value =
204 command_line.GetSwitchValueASCII(switches::kPrerenderMode);
206 if (switch_value == switches::kPrerenderModeSwitchValueAuto) {
207 prerender_option = PRERENDER_OPTION_AUTO;
208 } else if (switch_value == switches::kPrerenderModeSwitchValueDisabled) {
209 prerender_option = PRERENDER_OPTION_DISABLED;
210 } else if (switch_value.empty() ||
211 switch_value == switches::kPrerenderModeSwitchValueEnabled) {
212 // The empty string means the option was provided with no value, and that
214 prerender_option = PRERENDER_OPTION_ENABLED;
215 } else if (switch_value ==
216 switches::kPrerenderModeSwitchValuePrefetchOnly) {
217 prerender_option = PRERENDER_OPTION_PREFETCH_ONLY;
219 prerender_option = PRERENDER_OPTION_DISABLED;
220 LOG(ERROR) << "Invalid --prerender option received on command line: "
222 LOG(ERROR) << "Disabling prerendering!";
226 switch (prerender_option) {
227 case PRERENDER_OPTION_AUTO:
228 SetupPrefetchFieldTrial();
229 SetupPrerenderFieldTrial();
231 case PRERENDER_OPTION_DISABLED:
232 PrerenderManager::SetIsPrefetchEnabled(false);
233 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
235 case PRERENDER_OPTION_ENABLED:
236 PrerenderManager::SetIsPrefetchEnabled(true);
237 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
239 case PRERENDER_OPTION_PREFETCH_ONLY:
240 PrerenderManager::SetIsPrefetchEnabled(true);
241 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
247 ConfigureOmniboxPrerender();
250 void ConfigureOmniboxPrerender() {
251 // Field trial to see if we're enabled.
252 const FieldTrial::Probability kDivisor = 100;
254 FieldTrial::Probability kDisabledProbability = 10;
255 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
256 if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
257 channel == chrome::VersionInfo::CHANNEL_BETA) {
258 kDisabledProbability = 1;
260 scoped_refptr<FieldTrial> omnibox_prerender_trial(
261 FieldTrialList::FactoryGetFieldTrial(
262 kOmniboxTrialName, kDivisor, "OmniboxPrerenderEnabled",
263 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED,
264 &g_omnibox_trial_default_group_number));
265 omnibox_prerender_trial->AppendGroup("OmniboxPrerenderDisabled",
266 kDisabledProbability);
269 bool IsOmniboxEnabled(Profile* profile) {
273 if (!PrerenderManager::IsPrerenderingPossible())
276 // Override any field trial groups if the user has set a command line flag.
277 if (CommandLine::ForCurrentProcess()->HasSwitch(
278 switches::kPrerenderFromOmnibox)) {
279 const string switch_value =
280 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
281 switches::kPrerenderFromOmnibox);
283 if (switch_value == switches::kPrerenderFromOmniboxSwitchValueEnabled)
286 if (switch_value == switches::kPrerenderFromOmniboxSwitchValueDisabled)
289 DCHECK(switch_value == switches::kPrerenderFromOmniboxSwitchValueAuto);
292 const int group = FieldTrialList::FindValue(kOmniboxTrialName);
293 return group == FieldTrial::kNotFinalized ||
294 group == g_omnibox_trial_default_group_number;
298 PrerenderLocalPredictorSpec is a field trial, and its value must have the
300 key1=value1:key2=value2:key3=value3
301 eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled"
302 The function below extracts the value corresponding to a key provided from the
305 string GetLocalPredictorSpecValue(string spec_key) {
306 vector<string> elements;
307 SplitStringUsingSubstr(
308 FieldTrialList::FindFullName(kLocalPredictorSpecTrialName),
311 for (int i = 0; i < static_cast<int>(elements.size()); i++) {
312 vector<string> key_value;
313 SplitStringUsingSubstr(elements[i], "=", &key_value);
314 if (key_value.size() == 2 && key_value[0] == spec_key)
320 bool IsUnencryptedSyncEnabled(Profile* profile) {
321 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
322 GetForProfile(profile);
323 return service && service->GetSessionModelAssociator() &&
324 !service->EncryptEverythingEnabled();
327 // Indicates whether the Local Predictor is enabled based on field trial
329 bool IsLocalPredictorEnabled() {
330 #if defined(OS_ANDROID) || defined(OS_IOS)
333 if (CommandLine::ForCurrentProcess()->HasSwitch(
334 switches::kDisablePrerenderLocalPredictor)) {
337 return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup;
340 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) {
342 GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) ==
344 !IsUnencryptedSyncEnabled(profile);
347 bool IsLoggedInPredictorEnabled() {
348 return IsLocalPredictorEnabled();
351 bool IsSideEffectFreeWhitelistEnabled() {
352 return IsLocalPredictorEnabled() &&
353 GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
357 bool IsLocalPredictorPrerenderLaunchEnabled() {
358 return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
361 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
362 return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) ==
366 bool ShouldQueryPrerenderService(Profile* profile) {
367 return IsUnencryptedSyncEnabled(profile) &&
368 GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) ==
372 bool ShouldQueryPrerenderServiceForCurrentURL() {
373 return GetLocalPredictorSpecValue(
374 kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup;
377 bool ShouldQueryPrerenderServiceForCandidateURLs() {
378 return GetLocalPredictorSpecValue(
379 kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup;
382 string GetPrerenderServiceURLPrefix() {
383 string prefix = chrome_variations::GetVariationParamValue(
384 kLocalPredictorSpecTrialName,
385 kPrerenderServiceURLPrefixParameterName);
387 prefix = kDefaultPrerenderServiceURLPrefix;
391 int GetPrerenderServiceBehaviorID() {
393 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName),
395 // The behavior ID must be non-negative.
401 int GetPrerenderServiceFetchTimeoutMs() {
403 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName),
405 // The behavior ID must be non-negative.
406 if (result < kMinPrerenderServiceTimeoutMs ||
407 result > kMaxPrerenderServiceTimeoutMs) {
408 result = kDefaultPrerenderServiceTimeoutMs;
413 int GetLocalPredictorTTLSeconds() {
415 StringToInt(GetLocalPredictorSpecValue(kPrerenderTTLKeyName), &ttl);
416 // If the value is outside of 10s or 600s, use a default value of 180s.
417 if (ttl < 10 || ttl > 600)
422 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
424 StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName),
426 // Sanity check: Ensure the half life time is non-negative.
427 if (half_life_time < 0)
429 return half_life_time;
432 int GetLocalPredictorMaxConcurrentPrerenders() {
434 StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName),
436 // Sanity check: Ensure the number of prerenders is at least 1.
437 if (num_prerenders < 1)
439 // Sanity check: Ensure the number of prerenders is at most 10.
440 if (num_prerenders > 10)
442 return num_prerenders;
445 bool SkipLocalPredictorFragment() {
446 return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
449 bool SkipLocalPredictorHTTPS() {
450 return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
453 bool SkipLocalPredictorWhitelist() {
454 return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
457 bool SkipLocalPredictorServiceWhitelist() {
458 return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup;
461 bool SkipLocalPredictorLoggedIn() {
462 return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
465 bool SkipLocalPredictorDefaultNoPrerender() {
466 return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
469 bool SkipLocalPredictorLocalCandidates() {
470 return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) ==
474 bool SkipLocalPredictorServiceCandidates() {
475 return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) ==
479 } // namespace prerender