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";
77 const char kDisableSessionStorageNamespaceMerging
[] =
78 "DisableSessionStorageNamespaceMerging";
80 void SetupPrerenderFieldTrial() {
81 const FieldTrial::Probability divisor
= 1000;
83 FieldTrial::Probability control_probability
;
84 FieldTrial::Probability experiment_multi_prerender_probability
;
85 FieldTrial::Probability experiment_15min_ttl_probability
;
86 FieldTrial::Probability experiment_no_use_probability
;
88 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
89 if (channel
== chrome::VersionInfo::CHANNEL_STABLE
||
90 channel
== chrome::VersionInfo::CHANNEL_BETA
) {
91 // Use very conservatives and stable settings in beta and stable.
92 const FieldTrial::Probability release_prerender_enabled_probability
= 980;
93 const FieldTrial::Probability release_control_probability
= 10;
94 const FieldTrial::Probability
95 release_experiment_multi_prerender_probability
= 0;
96 const FieldTrial::Probability release_experiment_15min_ttl_probability
= 10;
97 const FieldTrial::Probability release_experiment_no_use_probability
= 0;
99 release_prerender_enabled_probability
+ release_control_probability
+
100 release_experiment_multi_prerender_probability
+
101 release_experiment_15min_ttl_probability
+
102 release_experiment_no_use_probability
== divisor
,
103 release_experiment_probabilities_must_equal_divisor
);
105 control_probability
= release_control_probability
;
106 experiment_multi_prerender_probability
=
107 release_experiment_multi_prerender_probability
;
108 experiment_15min_ttl_probability
= release_experiment_15min_ttl_probability
;
109 experiment_no_use_probability
= release_experiment_no_use_probability
;
111 // In testing channels, use more experiments and a larger control group to
112 // improve quality of data.
113 const FieldTrial::Probability dev_prerender_enabled_probability
= 250;
114 const FieldTrial::Probability dev_control_probability
= 250;
115 const FieldTrial::Probability
116 dev_experiment_multi_prerender_probability
= 250;
117 const FieldTrial::Probability dev_experiment_15min_ttl_probability
= 125;
118 const FieldTrial::Probability dev_experiment_no_use_probability
= 125;
119 COMPILE_ASSERT(dev_prerender_enabled_probability
+ dev_control_probability
+
120 dev_experiment_multi_prerender_probability
+
121 dev_experiment_15min_ttl_probability
+
122 dev_experiment_no_use_probability
== divisor
,
123 dev_experiment_probabilities_must_equal_divisor
);
125 control_probability
= dev_control_probability
;
126 experiment_multi_prerender_probability
=
127 dev_experiment_multi_prerender_probability
;
128 experiment_15min_ttl_probability
= dev_experiment_15min_ttl_probability
;
129 experiment_no_use_probability
= dev_experiment_no_use_probability
;
132 int prerender_enabled_group
= -1;
133 scoped_refptr
<FieldTrial
> trial(
134 FieldTrialList::FactoryGetFieldTrial(
135 "Prerender", divisor
, "PrerenderEnabled",
136 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED
,
137 &prerender_enabled_group
));
138 const int control_group
=
139 trial
->AppendGroup("PrerenderControl",
140 control_probability
);
141 const int experiment_multi_prerender_group
=
142 trial
->AppendGroup("PrerenderMulti",
143 experiment_multi_prerender_probability
);
144 const int experiment_15_min_TTL_group
=
145 trial
->AppendGroup("Prerender15minTTL",
146 experiment_15min_ttl_probability
);
147 const int experiment_no_use_group
=
148 trial
->AppendGroup("PrerenderNoUse",
149 experiment_no_use_probability
);
151 const int trial_group
= trial
->group();
152 if (trial_group
== prerender_enabled_group
) {
153 PrerenderManager::SetMode(
154 PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP
);
155 } else if (trial_group
== control_group
) {
156 PrerenderManager::SetMode(
157 PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP
);
158 } else if (trial_group
== experiment_multi_prerender_group
) {
159 PrerenderManager::SetMode(
160 PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP
);
161 } else if (trial_group
== experiment_15_min_TTL_group
) {
162 PrerenderManager::SetMode(
163 PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP
);
164 } else if (trial_group
== experiment_no_use_group
) {
165 PrerenderManager::SetMode(
166 PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP
);
174 void ConfigureOmniboxPrerender();
176 void ConfigurePrerender(const CommandLine
& command_line
) {
177 enum PrerenderOption
{
178 PRERENDER_OPTION_AUTO
,
179 PRERENDER_OPTION_DISABLED
,
180 PRERENDER_OPTION_ENABLED
,
183 PrerenderOption prerender_option
= PRERENDER_OPTION_AUTO
;
184 if (command_line
.HasSwitch(switches::kPrerenderMode
)) {
185 const string switch_value
=
186 command_line
.GetSwitchValueASCII(switches::kPrerenderMode
);
188 if (switch_value
== switches::kPrerenderModeSwitchValueAuto
) {
189 prerender_option
= PRERENDER_OPTION_AUTO
;
190 } else if (switch_value
== switches::kPrerenderModeSwitchValueDisabled
) {
191 prerender_option
= PRERENDER_OPTION_DISABLED
;
192 } else if (switch_value
.empty() ||
193 switch_value
== switches::kPrerenderModeSwitchValueEnabled
) {
194 // The empty string means the option was provided with no value, and that
196 prerender_option
= PRERENDER_OPTION_ENABLED
;
198 prerender_option
= PRERENDER_OPTION_DISABLED
;
199 LOG(ERROR
) << "Invalid --prerender option received on command line: "
201 LOG(ERROR
) << "Disabling prerendering!";
205 switch (prerender_option
) {
206 case PRERENDER_OPTION_AUTO
:
207 SetupPrerenderFieldTrial();
209 case PRERENDER_OPTION_DISABLED
:
210 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED
);
212 case PRERENDER_OPTION_ENABLED
:
213 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED
);
219 ConfigureOmniboxPrerender();
222 void ConfigureOmniboxPrerender() {
223 // Field trial to see if we're enabled.
224 const FieldTrial::Probability kDivisor
= 100;
226 FieldTrial::Probability kDisabledProbability
= 10;
227 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
228 if (channel
== chrome::VersionInfo::CHANNEL_STABLE
||
229 channel
== chrome::VersionInfo::CHANNEL_BETA
) {
230 kDisabledProbability
= 1;
232 scoped_refptr
<FieldTrial
> omnibox_prerender_trial(
233 FieldTrialList::FactoryGetFieldTrial(
234 kOmniboxTrialName
, kDivisor
, "OmniboxPrerenderEnabled",
235 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED
,
236 &g_omnibox_trial_default_group_number
));
237 omnibox_prerender_trial
->AppendGroup("OmniboxPrerenderDisabled",
238 kDisabledProbability
);
241 bool IsOmniboxEnabled(Profile
* profile
) {
245 if (!PrerenderManager::IsPrerenderingPossible())
248 // Override any field trial groups if the user has set a command line flag.
249 if (CommandLine::ForCurrentProcess()->HasSwitch(
250 switches::kPrerenderFromOmnibox
)) {
251 const string switch_value
=
252 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
253 switches::kPrerenderFromOmnibox
);
255 if (switch_value
== switches::kPrerenderFromOmniboxSwitchValueEnabled
)
258 if (switch_value
== switches::kPrerenderFromOmniboxSwitchValueDisabled
)
261 DCHECK(switch_value
== switches::kPrerenderFromOmniboxSwitchValueAuto
);
264 const int group
= FieldTrialList::FindValue(kOmniboxTrialName
);
265 return group
== FieldTrial::kNotFinalized
||
266 group
== g_omnibox_trial_default_group_number
;
270 PrerenderLocalPredictorSpec is a field trial, and its value must have the
272 key1=value1:key2=value2:key3=value3
273 eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled"
274 The function below extracts the value corresponding to a key provided from the
277 string
GetLocalPredictorSpecValue(string spec_key
) {
278 vector
<string
> elements
;
279 SplitStringUsingSubstr(
280 FieldTrialList::FindFullName(kLocalPredictorSpecTrialName
),
283 for (int i
= 0; i
< static_cast<int>(elements
.size()); i
++) {
284 vector
<string
> key_value
;
285 SplitStringUsingSubstr(elements
[i
], "=", &key_value
);
286 if (key_value
.size() == 2 && key_value
[0] == spec_key
)
292 bool IsUnencryptedSyncEnabled(Profile
* profile
) {
293 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
294 GetForProfile(profile
);
295 return service
&& service
->GetOpenTabsUIDelegate() &&
296 !service
->EncryptEverythingEnabled();
299 // Indicates whether the Local Predictor is enabled based on field trial
301 bool IsLocalPredictorEnabled() {
302 #if defined(OS_ANDROID) || defined(OS_IOS)
305 if (CommandLine::ForCurrentProcess()->HasSwitch(
306 switches::kDisablePrerenderLocalPredictor
)) {
309 return GetLocalPredictorSpecValue(kLocalPredictorKeyName
) == kEnabledGroup
;
312 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile
* profile
) {
314 GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName
) ==
316 !IsUnencryptedSyncEnabled(profile
);
319 bool IsLoggedInPredictorEnabled() {
320 return IsLocalPredictorEnabled();
323 bool IsSideEffectFreeWhitelistEnabled() {
324 return IsLocalPredictorEnabled() &&
325 GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName
) !=
329 bool IsLocalPredictorPrerenderLaunchEnabled() {
330 return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName
) != kDisabledGroup
;
333 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
334 return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName
) ==
338 bool ShouldQueryPrerenderService(Profile
* profile
) {
339 return IsUnencryptedSyncEnabled(profile
) &&
340 GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName
) ==
344 bool ShouldQueryPrerenderServiceForCurrentURL() {
345 return GetLocalPredictorSpecValue(
346 kPrerenderQueryPrerenderServiceCurrentURLKeyName
) != kDisabledGroup
;
349 bool ShouldQueryPrerenderServiceForCandidateURLs() {
350 return GetLocalPredictorSpecValue(
351 kPrerenderQueryPrerenderServiceCandidateURLsKeyName
) != kDisabledGroup
;
354 string
GetPrerenderServiceURLPrefix() {
355 string prefix
= chrome_variations::GetVariationParamValue(
356 kLocalPredictorSpecTrialName
,
357 kPrerenderServiceURLPrefixParameterName
);
359 prefix
= kDefaultPrerenderServiceURLPrefix
;
363 int GetPrerenderServiceBehaviorID() {
365 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName
),
367 // The behavior ID must be non-negative.
373 int GetPrerenderServiceFetchTimeoutMs() {
375 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName
),
377 // The behavior ID must be non-negative.
378 if (result
< kMinPrerenderServiceTimeoutMs
||
379 result
> kMaxPrerenderServiceTimeoutMs
) {
380 result
= kDefaultPrerenderServiceTimeoutMs
;
385 int GetLocalPredictorTTLSeconds() {
387 StringToInt(GetLocalPredictorSpecValue(kPrerenderTTLKeyName
), &ttl
);
388 // If the value is outside of 10s or 600s, use a default value of 180s.
389 if (ttl
< 10 || ttl
> 600)
394 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
396 StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName
),
398 // Sanity check: Ensure the half life time is non-negative.
399 if (half_life_time
< 0)
401 return half_life_time
;
404 int GetLocalPredictorMaxConcurrentPrerenders() {
406 StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName
),
408 // Sanity check: Ensure the number of prerenders is at least 1.
409 if (num_prerenders
< 1)
411 // Sanity check: Ensure the number of prerenders is at most 10.
412 if (num_prerenders
> 10)
414 return num_prerenders
;
417 bool SkipLocalPredictorFragment() {
418 return GetLocalPredictorSpecValue(kSkipFragment
) == kEnabledGroup
;
421 bool SkipLocalPredictorHTTPS() {
422 return GetLocalPredictorSpecValue(kSkipHTTPS
) == kEnabledGroup
;
425 bool SkipLocalPredictorWhitelist() {
426 return GetLocalPredictorSpecValue(kSkipWhitelist
) == kEnabledGroup
;
429 bool SkipLocalPredictorServiceWhitelist() {
430 return GetLocalPredictorSpecValue(kSkipServiceWhitelist
) == kEnabledGroup
;
433 bool SkipLocalPredictorLoggedIn() {
434 return GetLocalPredictorSpecValue(kSkipLoggedIn
) == kEnabledGroup
;
437 bool SkipLocalPredictorDefaultNoPrerender() {
438 return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender
) == kEnabledGroup
;
441 bool SkipLocalPredictorLocalCandidates() {
442 return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates
) ==
446 bool SkipLocalPredictorServiceCandidates() {
447 return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates
) ==
451 bool ShouldMergeSessionStorageNamespaces() {
452 return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging
) !=
456 } // namespace prerender