• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
23 
24 using base::FieldTrial;
25 using base::FieldTrialList;
26 using base::SplitStringUsingSubstr;
27 using base::StringToInt;
28 using std::string;
29 using std::vector;
30 
31 namespace prerender {
32 
33 namespace {
34 
35 const char kOmniboxTrialName[] = "PrerenderFromOmnibox";
36 int g_omnibox_trial_default_group_number = kint32min;
37 
38 const char kDisabledGroup[] = "Disabled";
39 const char kEnabledGroup[] = "Enabled";
40 
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";
79 
SetupPrerenderFieldTrial()80 void SetupPrerenderFieldTrial() {
81   const FieldTrial::Probability divisor = 1000;
82 
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;
87 
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;
98     COMPILE_ASSERT(
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);
104 
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;
110   } else {
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);
124 
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;
130   }
131 
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);
150 
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);
167   } else {
168     NOTREACHED();
169   }
170 }
171 
172 }  // end namespace
173 
174 void ConfigureOmniboxPrerender();
175 
ConfigurePrerender(const CommandLine & command_line)176 void ConfigurePrerender(const CommandLine& command_line) {
177   enum PrerenderOption {
178     PRERENDER_OPTION_AUTO,
179     PRERENDER_OPTION_DISABLED,
180     PRERENDER_OPTION_ENABLED,
181   };
182 
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);
187 
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
195       // means enable.
196       prerender_option = PRERENDER_OPTION_ENABLED;
197     } else {
198       prerender_option = PRERENDER_OPTION_DISABLED;
199       LOG(ERROR) << "Invalid --prerender option received on command line: "
200                  << switch_value;
201       LOG(ERROR) << "Disabling prerendering!";
202     }
203   }
204 
205   switch (prerender_option) {
206     case PRERENDER_OPTION_AUTO:
207       SetupPrerenderFieldTrial();
208       break;
209     case PRERENDER_OPTION_DISABLED:
210       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
211       break;
212     case PRERENDER_OPTION_ENABLED:
213       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
214       break;
215     default:
216       NOTREACHED();
217   }
218 
219   ConfigureOmniboxPrerender();
220 }
221 
ConfigureOmniboxPrerender()222 void ConfigureOmniboxPrerender() {
223   // Field trial to see if we're enabled.
224   const FieldTrial::Probability kDivisor = 100;
225 
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;
231   }
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);
239 }
240 
IsOmniboxEnabled(Profile * profile)241 bool IsOmniboxEnabled(Profile* profile) {
242   if (!profile)
243     return false;
244 
245   if (!PrerenderManager::IsPrerenderingPossible())
246     return false;
247 
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);
254 
255     if (switch_value == switches::kPrerenderFromOmniboxSwitchValueEnabled)
256       return true;
257 
258     if (switch_value == switches::kPrerenderFromOmniboxSwitchValueDisabled)
259       return false;
260 
261     DCHECK(switch_value == switches::kPrerenderFromOmniboxSwitchValueAuto);
262   }
263 
264   const int group = FieldTrialList::FindValue(kOmniboxTrialName);
265   return group == FieldTrial::kNotFinalized ||
266          group == g_omnibox_trial_default_group_number;
267 }
268 
269 /*
270 PrerenderLocalPredictorSpec is a field trial, and its value must have the
271 following format:
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
275 LocalPredictorSpec.
276 */
GetLocalPredictorSpecValue(string spec_key)277 string GetLocalPredictorSpecValue(string spec_key) {
278   vector<string> elements;
279   SplitStringUsingSubstr(
280       FieldTrialList::FindFullName(kLocalPredictorSpecTrialName),
281       ":",
282       &elements);
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)
287       return key_value[1];
288   }
289   return string();
290 }
291 
IsUnencryptedSyncEnabled(Profile * profile)292 bool IsUnencryptedSyncEnabled(Profile* profile) {
293   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
294       GetForProfile(profile);
295   return service && service->GetOpenTabsUIDelegate() &&
296       !service->EncryptEverythingEnabled();
297 }
298 
299 // Indicates whether the Local Predictor is enabled based on field trial
300 // selection.
IsLocalPredictorEnabled()301 bool IsLocalPredictorEnabled() {
302 #if defined(OS_ANDROID) || defined(OS_IOS)
303   return false;
304 #endif
305   if (CommandLine::ForCurrentProcess()->HasSwitch(
306           switches::kDisablePrerenderLocalPredictor)) {
307     return false;
308   }
309   return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup;
310 }
311 
DisableLocalPredictorBasedOnSyncAndConfiguration(Profile * profile)312 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) {
313   return
314       GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) ==
315       kEnabledGroup &&
316       !IsUnencryptedSyncEnabled(profile);
317 }
318 
IsLoggedInPredictorEnabled()319 bool IsLoggedInPredictorEnabled() {
320   return IsLocalPredictorEnabled();
321 }
322 
IsSideEffectFreeWhitelistEnabled()323 bool IsSideEffectFreeWhitelistEnabled() {
324   return IsLocalPredictorEnabled() &&
325       GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
326       kDisabledGroup;
327 }
328 
IsLocalPredictorPrerenderLaunchEnabled()329 bool IsLocalPredictorPrerenderLaunchEnabled() {
330   return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
331 }
332 
IsLocalPredictorPrerenderAlwaysControlEnabled()333 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
334   return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) ==
335       kEnabledGroup;
336 }
337 
ShouldQueryPrerenderService(Profile * profile)338 bool ShouldQueryPrerenderService(Profile* profile) {
339   return IsUnencryptedSyncEnabled(profile) &&
340       GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) ==
341       kEnabledGroup;
342 }
343 
ShouldQueryPrerenderServiceForCurrentURL()344 bool ShouldQueryPrerenderServiceForCurrentURL() {
345   return GetLocalPredictorSpecValue(
346       kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup;
347 }
348 
ShouldQueryPrerenderServiceForCandidateURLs()349 bool ShouldQueryPrerenderServiceForCandidateURLs() {
350   return GetLocalPredictorSpecValue(
351       kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup;
352 }
353 
GetPrerenderServiceURLPrefix()354 string GetPrerenderServiceURLPrefix() {
355   string prefix = chrome_variations::GetVariationParamValue(
356       kLocalPredictorSpecTrialName,
357       kPrerenderServiceURLPrefixParameterName);
358   if (prefix.empty())
359     prefix = kDefaultPrerenderServiceURLPrefix;
360   return prefix;
361 }
362 
GetPrerenderServiceBehaviorID()363 int GetPrerenderServiceBehaviorID() {
364   int id;
365   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName),
366               &id);
367   // The behavior ID must be non-negative.
368   if (id < 0)
369     id = 0;
370   return id;
371 }
372 
GetPrerenderServiceFetchTimeoutMs()373 int GetPrerenderServiceFetchTimeoutMs() {
374   int result;
375   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName),
376               &result);
377   // The behavior ID must be non-negative.
378   if (result < kMinPrerenderServiceTimeoutMs ||
379       result > kMaxPrerenderServiceTimeoutMs) {
380     result = kDefaultPrerenderServiceTimeoutMs;
381   }
382   return result;
383 }
384 
GetLocalPredictorTTLSeconds()385 int GetLocalPredictorTTLSeconds() {
386   int ttl;
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)
390     ttl = 180;
391   return ttl;
392 }
393 
GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds()394 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
395   int half_life_time;
396   StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName),
397               &half_life_time);
398   // Sanity check: Ensure the half life time is non-negative.
399   if (half_life_time < 0)
400     half_life_time = 0;
401   return half_life_time;
402 }
403 
GetLocalPredictorMaxConcurrentPrerenders()404 int GetLocalPredictorMaxConcurrentPrerenders() {
405   int num_prerenders;
406   StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName),
407               &num_prerenders);
408   // Sanity check: Ensure the number of prerenders is at least 1.
409   if (num_prerenders < 1)
410     num_prerenders = 1;
411   // Sanity check: Ensure the number of prerenders is at most 10.
412   if (num_prerenders > 10)
413     num_prerenders = 10;
414   return num_prerenders;
415 };
416 
SkipLocalPredictorFragment()417 bool SkipLocalPredictorFragment() {
418   return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
419 }
420 
SkipLocalPredictorHTTPS()421 bool SkipLocalPredictorHTTPS() {
422   return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
423 }
424 
SkipLocalPredictorWhitelist()425 bool SkipLocalPredictorWhitelist() {
426   return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
427 }
428 
SkipLocalPredictorServiceWhitelist()429 bool SkipLocalPredictorServiceWhitelist() {
430   return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup;
431 }
432 
SkipLocalPredictorLoggedIn()433 bool SkipLocalPredictorLoggedIn() {
434   return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
435 }
436 
SkipLocalPredictorDefaultNoPrerender()437 bool SkipLocalPredictorDefaultNoPrerender() {
438   return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
439 }
440 
SkipLocalPredictorLocalCandidates()441 bool SkipLocalPredictorLocalCandidates() {
442   return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) ==
443       kEnabledGroup;
444 }
445 
SkipLocalPredictorServiceCandidates()446 bool SkipLocalPredictorServiceCandidates() {
447   return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) ==
448       kEnabledGroup;
449 }
450 
ShouldMergeSessionStorageNamespaces()451 bool ShouldMergeSessionStorageNamespaces() {
452   return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging) !=
453       kDisabledGroup;
454 }
455 
456 }  // namespace prerender
457