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/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 "components/metrics/metrics_service.h"
23 #include "components/variations/variations_associated_data.h"
25 using base::FieldTrial
;
26 using base::FieldTrialList
;
27 using base::StringToInt
;
35 const char kDisabledGroup
[] = "Disabled";
36 const char kEnabledGroup
[] = "Enabled";
38 const char kLocalPredictorSpecTrialName
[] = "PrerenderLocalPredictorSpec";
39 const char kLocalPredictorKeyName
[] = "LocalPredictor";
40 const char kLocalPredictorUnencryptedSyncOnlyKeyName
[] =
41 "LocalPredictorUnencryptedSyncOnly";
42 const char kLocalPredictorNetworkPredictionEnabledOnly
[] =
43 "LocalPredictorNetworkPredictionEnabledOnly";
44 const char kLocalPredictorOnCellularOnly
[] = "LocalPredictorOnCellularOnly";
45 const char kSideEffectFreeWhitelistKeyName
[] = "SideEffectFreeWhitelist";
46 const char kPrerenderLaunchKeyName
[] = "PrerenderLaunch";
47 const char kPrerenderAlwaysControlKeyName
[] = "PrerenderAlwaysControl";
48 const char kPrerenderPrefetchKeyName
[] = "PrerenderPrefetch";
49 const char kPrerenderQueryPrerenderServiceKeyName
[] =
50 "PrerenderQueryPrerenderService";
51 const char kPrerenderQueryPrerenderServiceCurrentURLKeyName
[] =
52 "PrerenderQueryPrerenderServiceCurrentURL";
53 const char kPrerenderQueryPrerenderServiceCandidateURLsKeyName
[] =
54 "PrerenderQueryPrerenderServiceCandidateURLs";
55 const char kPrerenderServiceBehaviorIDKeyName
[] = "PrerenderServiceBehaviorID";
56 const char kPrerenderServiceFetchTimeoutKeyName
[] =
57 "PrerenderServiceFetchTimeoutMs";
58 const char kPrefetchListTimeoutKeyName
[] = "PrefetchListTimeoutSeconds";
59 const char kPrerenderTTLKeyName
[] = "PrerenderTTLSeconds";
60 const char kPrerenderPriorityHalfLifeTimeKeyName
[] =
61 "PrerenderPriorityHalfLifeTimeSeconds";
62 const char kMaxConcurrentPrerenderKeyName
[] = "MaxConcurrentPrerenders";
63 const char kMaxLaunchPrerenderKeyName
[] = "MaxLaunchPrerenders";
64 const char kSkipFragment
[] = "SkipFragment";
65 const char kSkipHTTPS
[] = "SkipHTTPS";
66 const char kSkipWhitelist
[] = "SkipWhitelist";
67 const char kSkipServiceWhitelist
[] = "SkipServiceWhitelist";
68 const char kSkipLoggedIn
[] = "SkipLoggedIn";
69 const char kSkipDefaultNoPrerender
[] = "SkipDefaultNoPrerender";
70 const char kPrerenderServiceURLPrefixParameterName
[] =
71 "PrerenderServiceURLPrefix";
72 const char kDefaultPrerenderServiceURLPrefix
[] =
73 "https://clients4.google.com/prerenderservice/?q=";
74 const int kMinPrerenderServiceTimeoutMs
= 1;
75 const int kMaxPrerenderServiceTimeoutMs
= 10000;
76 const int kDefaultPrerenderServiceTimeoutMs
= 1000;
77 const int kMinPrefetchListTimeoutSeconds
= 1;
78 const int kMaxPrefetchListTimeoutSeconds
= 1800;
79 const int kDefaultPrefetchListTimeoutSeconds
= 300;
80 const char kSkipPrerenderLocalCanadidates
[] = "SkipPrerenderLocalCandidates";
81 const char kSkipPrerenderServiceCanadidates
[] =
82 "SkipPrerenderServiceCandidates";
83 const char kPrerenderCookieStore
[] = "PrerenderCookieStore";
85 void SetupPrerenderFieldTrial() {
86 const FieldTrial::Probability divisor
= 1000;
88 FieldTrial::Probability control_probability
;
89 FieldTrial::Probability experiment_multi_prerender_probability
;
90 FieldTrial::Probability experiment_15min_ttl_probability
;
91 FieldTrial::Probability experiment_no_use_probability
;
92 FieldTrial::Probability experiment_match_complete_probability
;
94 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
95 if (channel
== chrome::VersionInfo::CHANNEL_STABLE
||
96 channel
== chrome::VersionInfo::CHANNEL_BETA
) {
97 // Use very conservatives and stable settings in beta and stable.
98 const FieldTrial::Probability release_prerender_enabled_probability
= 970;
99 const FieldTrial::Probability release_control_probability
= 10;
100 const FieldTrial::Probability
101 release_experiment_multi_prerender_probability
= 0;
102 const FieldTrial::Probability release_experiment_15min_ttl_probability
= 10;
103 const FieldTrial::Probability release_experiment_no_use_probability
= 0;
104 const FieldTrial::Probability
105 release_experiment_match_complete_probability
= 10;
107 release_prerender_enabled_probability
+
108 release_control_probability
+
109 release_experiment_multi_prerender_probability
+
110 release_experiment_15min_ttl_probability
+
111 release_experiment_no_use_probability
+
112 release_experiment_match_complete_probability
== divisor
,
113 "release experiment probabilities must equal divisor");
115 control_probability
= release_control_probability
;
116 experiment_multi_prerender_probability
=
117 release_experiment_multi_prerender_probability
;
118 experiment_15min_ttl_probability
= release_experiment_15min_ttl_probability
;
119 experiment_no_use_probability
= release_experiment_no_use_probability
;
120 experiment_match_complete_probability
=
121 release_experiment_match_complete_probability
;
123 // In testing channels, use more experiments and a larger control group to
124 // improve quality of data.
125 const FieldTrial::Probability dev_prerender_enabled_probability
= 200;
126 const FieldTrial::Probability dev_control_probability
= 200;
127 const FieldTrial::Probability
128 dev_experiment_multi_prerender_probability
= 200;
129 const FieldTrial::Probability dev_experiment_15min_ttl_probability
= 100;
130 const FieldTrial::Probability dev_experiment_no_use_probability
= 100;
131 const FieldTrial::Probability
132 dev_experiment_match_complete_probability
= 200;
133 static_assert(dev_prerender_enabled_probability
+
134 dev_control_probability
+
135 dev_experiment_multi_prerender_probability
+
136 dev_experiment_15min_ttl_probability
+
137 dev_experiment_no_use_probability
+
138 dev_experiment_match_complete_probability
== divisor
,
139 "dev experiment probabilities must equal divisor");
141 control_probability
= dev_control_probability
;
142 experiment_multi_prerender_probability
=
143 dev_experiment_multi_prerender_probability
;
144 experiment_15min_ttl_probability
= dev_experiment_15min_ttl_probability
;
145 experiment_no_use_probability
= dev_experiment_no_use_probability
;
146 experiment_match_complete_probability
=
147 dev_experiment_match_complete_probability
;
150 int prerender_enabled_group
= -1;
151 scoped_refptr
<FieldTrial
> trial(
152 FieldTrialList::FactoryGetFieldTrial(
153 "Prerender", divisor
, "PrerenderEnabled",
154 2014, 12, 31, FieldTrial::SESSION_RANDOMIZED
,
155 &prerender_enabled_group
));
156 const int control_group
=
157 trial
->AppendGroup("PrerenderControl",
158 control_probability
);
159 const int experiment_multi_prerender_group
=
160 trial
->AppendGroup("PrerenderMulti",
161 experiment_multi_prerender_probability
);
162 const int experiment_15_min_TTL_group
=
163 trial
->AppendGroup("Prerender15minTTL",
164 experiment_15min_ttl_probability
);
165 const int experiment_no_use_group
=
166 trial
->AppendGroup("PrerenderNoUse",
167 experiment_no_use_probability
);
168 const int experiment_match_complete_group
=
169 trial
->AppendGroup("MatchComplete",
170 experiment_match_complete_probability
);
172 const int trial_group
= trial
->group();
173 if (trial_group
== prerender_enabled_group
) {
174 PrerenderManager::SetMode(
175 PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP
);
176 } else if (trial_group
== control_group
) {
177 PrerenderManager::SetMode(
178 PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP
);
179 } else if (trial_group
== experiment_multi_prerender_group
) {
180 PrerenderManager::SetMode(
181 PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP
);
182 } else if (trial_group
== experiment_15_min_TTL_group
) {
183 PrerenderManager::SetMode(
184 PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP
);
185 } else if (trial_group
== experiment_no_use_group
) {
186 PrerenderManager::SetMode(
187 PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP
);
188 } else if (trial_group
== experiment_match_complete_group
) {
189 PrerenderManager::SetMode(
190 PrerenderManager::PRERENDER_MODE_EXPERIMENT_MATCH_COMPLETE_GROUP
);
198 void ConfigurePrerender(const base::CommandLine
& command_line
) {
199 enum PrerenderOption
{
200 PRERENDER_OPTION_AUTO
,
201 PRERENDER_OPTION_DISABLED
,
202 PRERENDER_OPTION_ENABLED
,
205 PrerenderOption prerender_option
= PRERENDER_OPTION_AUTO
;
206 if (command_line
.HasSwitch(switches::kPrerenderMode
)) {
207 const string switch_value
=
208 command_line
.GetSwitchValueASCII(switches::kPrerenderMode
);
210 if (switch_value
== switches::kPrerenderModeSwitchValueAuto
) {
211 prerender_option
= PRERENDER_OPTION_AUTO
;
212 } else if (switch_value
== switches::kPrerenderModeSwitchValueDisabled
) {
213 prerender_option
= PRERENDER_OPTION_DISABLED
;
214 } else if (switch_value
.empty() ||
215 switch_value
== switches::kPrerenderModeSwitchValueEnabled
) {
216 // The empty string means the option was provided with no value, and that
218 prerender_option
= PRERENDER_OPTION_ENABLED
;
220 prerender_option
= PRERENDER_OPTION_DISABLED
;
221 LOG(ERROR
) << "Invalid --prerender option received on command line: "
223 LOG(ERROR
) << "Disabling prerendering!";
227 switch (prerender_option
) {
228 case PRERENDER_OPTION_AUTO
:
229 SetupPrerenderFieldTrial();
231 case PRERENDER_OPTION_DISABLED
:
232 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED
);
234 case PRERENDER_OPTION_ENABLED
:
235 PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED
);
242 bool IsOmniboxEnabled(Profile
* profile
) {
246 if (!PrerenderManager::IsPrerenderingPossible())
249 // Override any field trial groups if the user has set a command line flag.
250 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
251 switches::kPrerenderFromOmnibox
)) {
252 const string switch_value
=
253 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
254 switches::kPrerenderFromOmnibox
);
256 if (switch_value
== switches::kPrerenderFromOmniboxSwitchValueEnabled
)
259 if (switch_value
== switches::kPrerenderFromOmniboxSwitchValueDisabled
)
262 DCHECK_EQ(switches::kPrerenderFromOmniboxSwitchValueAuto
, switch_value
);
265 return (FieldTrialList::FindFullName("PrerenderFromOmnibox") !=
266 "OmniboxPrerenderDisabled");
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 base::SplitString(FieldTrialList::FindFullName(kLocalPredictorSpecTrialName
),
281 for (int i
= 0; i
< static_cast<int>(elements
.size()); i
++) {
282 vector
<string
> key_value
;
283 base::SplitString(elements
[i
], '=', &key_value
);
284 if (key_value
.size() == 2 && key_value
[0] == spec_key
)
290 bool IsUnencryptedSyncEnabled(Profile
* profile
) {
291 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
292 GetForProfile(profile
);
293 return service
&& service
->GetOpenTabsUIDelegate() &&
294 !service
->EncryptEverythingEnabled();
297 // Indicates whether the Local Predictor is enabled based on field trial
299 bool IsLocalPredictorEnabled() {
300 #if defined(OS_ANDROID) || defined(OS_IOS)
303 return !base::CommandLine::ForCurrentProcess()->HasSwitch(
304 switches::kDisablePrerenderLocalPredictor
) &&
305 GetLocalPredictorSpecValue(kLocalPredictorKeyName
) == kEnabledGroup
;
308 bool ShouldDisableLocalPredictorBasedOnSyncAndConfiguration(Profile
* profile
) {
310 GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName
) ==
312 !IsUnencryptedSyncEnabled(profile
);
315 bool ShouldDisableLocalPredictorDueToPreferencesAndNetwork(Profile
* profile
) {
317 net::NetworkChangeNotifier::IsConnectionCellular(
318 net::NetworkChangeNotifier::GetConnectionType());
319 // If the user is not on a cellular connection, but we require a cellular
320 // connection, we must temporarily disable our local predictions.
322 GetLocalPredictorSpecValue(kLocalPredictorOnCellularOnly
) ==
327 // If we don't care whether or not network prediction will actually be
328 // exercised, we do not need to temporarily disable our predictions.
329 if (GetLocalPredictorSpecValue(kLocalPredictorNetworkPredictionEnabledOnly
) !=
334 // We should temporarily disable iff the predictive network action would
337 return !chrome_browser_net::CanPrefetchAndPrerenderUI(profile
->GetPrefs());
340 bool IsLoggedInPredictorEnabled() {
341 return IsLocalPredictorEnabled();
344 bool IsSideEffectFreeWhitelistEnabled() {
345 return IsLocalPredictorEnabled() &&
346 GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName
) !=
350 bool IsLocalPredictorPrerenderLaunchEnabled() {
351 return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName
) != kDisabledGroup
;
354 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
355 // If we prefetch rather than prerender, we automatically also prerender
356 // as a control group only.
357 return (GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName
) ==
358 kEnabledGroup
) || IsLocalPredictorPrerenderPrefetchEnabled();
361 bool IsLocalPredictorPrerenderPrefetchEnabled() {
362 return GetLocalPredictorSpecValue(kPrerenderPrefetchKeyName
) ==
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
= variations::GetVariationParamValue(
384 kLocalPredictorSpecTrialName
,
385 kPrerenderServiceURLPrefixParameterName
);
386 return prefix
.empty() ? kDefaultPrerenderServiceURLPrefix
: prefix
;
389 int GetPrerenderServiceBehaviorID() {
391 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName
),
393 // The behavior ID must be non-negative.
394 return std::max(id
, 0);
397 int GetPrerenderServiceFetchTimeoutMs() {
399 StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName
),
401 // If the value is outside the valid range, use the default value.
402 return (result
< kMinPrerenderServiceTimeoutMs
||
403 result
> kMaxPrerenderServiceTimeoutMs
) ?
404 kDefaultPrerenderServiceTimeoutMs
: result
;
407 int GetPrerenderPrefetchListTimeoutSeconds() {
409 StringToInt(GetLocalPredictorSpecValue(kPrefetchListTimeoutKeyName
), &result
);
410 // If the value is outside the valid range, use the default value.
411 return (result
< kMinPrefetchListTimeoutSeconds
||
412 result
> kMaxPrefetchListTimeoutSeconds
) ?
413 kDefaultPrefetchListTimeoutSeconds
: result
;
416 int GetLocalPredictorTTLSeconds() {
418 StringToInt(GetLocalPredictorSpecValue(kPrerenderTTLKeyName
), &ttl
);
419 // If the value is outside of 10s or 600s, use a default value of 180s.
420 return (ttl
< 10 || ttl
> 600) ? 180 : ttl
;
423 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
425 StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName
),
427 // Sanity check: Ensure the half life time is non-negative.
428 return std::max(half_life_time
, 0);
431 int GetLocalPredictorMaxConcurrentPrerenders() {
433 StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName
),
435 // Sanity check: Ensure the number of prerenders is between 1 and 10.
436 return std::min(std::max(num_prerenders
, 1), 10);
439 int GetLocalPredictorMaxLaunchPrerenders() {
441 StringToInt(GetLocalPredictorSpecValue(kMaxLaunchPrerenderKeyName
),
443 // Sanity check: Ensure the number of prerenders is between 1 and 10.
444 return std::min(std::max(num_prerenders
, 1), 10);
447 bool SkipLocalPredictorFragment() {
448 return GetLocalPredictorSpecValue(kSkipFragment
) == kEnabledGroup
;
451 bool SkipLocalPredictorHTTPS() {
452 return GetLocalPredictorSpecValue(kSkipHTTPS
) == kEnabledGroup
;
455 bool SkipLocalPredictorWhitelist() {
456 return GetLocalPredictorSpecValue(kSkipWhitelist
) == kEnabledGroup
;
459 bool SkipLocalPredictorServiceWhitelist() {
460 return GetLocalPredictorSpecValue(kSkipServiceWhitelist
) == kEnabledGroup
;
463 bool SkipLocalPredictorLoggedIn() {
464 return GetLocalPredictorSpecValue(kSkipLoggedIn
) == kEnabledGroup
;
467 bool SkipLocalPredictorDefaultNoPrerender() {
468 return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender
) == kEnabledGroup
;
471 bool SkipLocalPredictorLocalCandidates() {
472 return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates
) ==
476 bool SkipLocalPredictorServiceCandidates() {
477 return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates
) ==
481 bool IsPrerenderCookieStoreEnabled() {
482 return GetLocalPredictorSpecValue(kPrerenderCookieStore
) != kDisabledGroup
&&
483 FieldTrialList::FindFullName(kPrerenderCookieStore
) != kDisabledGroup
;
486 } // namespace prerender