• 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/predictors/autocomplete_action_predictor.h"
15 #include "chrome/browser/prerender/prerender_manager.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/sync/profile_sync_service_factory.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "components/metrics/metrics_service.h"
22 #include "components/variations/variations_associated_data.h"
23 
24 using base::FieldTrial;
25 using base::FieldTrialList;
26 using base::StringToInt;
27 using std::string;
28 using std::vector;
29 
30 namespace prerender {
31 
32 namespace {
33 
34 const char kOmniboxTrialName[] = "PrerenderFromOmnibox";
35 int g_omnibox_trial_default_group_number = kint32min;
36 
37 const char kDisabledGroup[] = "Disabled";
38 const char kEnabledGroup[] = "Enabled";
39 
40 const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec";
41 const char kLocalPredictorKeyName[] = "LocalPredictor";
42 const char kLocalPredictorUnencryptedSyncOnlyKeyName[] =
43     "LocalPredictorUnencryptedSyncOnly";
44 const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist";
45 const char kPrerenderLaunchKeyName[] = "PrerenderLaunch";
46 const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl";
47 const char kPrerenderPrefetchKeyName[] = "PrerenderPrefetch";
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 kMaxLaunchPrerenderKeyName[] = "MaxLaunchPrerenders";
62 const char kSkipFragment[] = "SkipFragment";
63 const char kSkipHTTPS[] = "SkipHTTPS";
64 const char kSkipWhitelist[] = "SkipWhitelist";
65 const char kSkipServiceWhitelist[] = "SkipServiceWhitelist";
66 const char kSkipLoggedIn[] = "SkipLoggedIn";
67 const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender";
68 const char kPrerenderServiceURLPrefixParameterName[] =
69     "PrerenderServiceURLPrefix";
70 const char kDefaultPrerenderServiceURLPrefix[] =
71     "https://clients4.google.com/prerenderservice/?q=";
72 const int kMinPrerenderServiceTimeoutMs = 1;
73 const int kMaxPrerenderServiceTimeoutMs = 10000;
74 const int kDefaultPrerenderServiceTimeoutMs = 1000;
75 const char kSkipPrerenderLocalCanadidates[] = "SkipPrerenderLocalCandidates";
76 const char kSkipPrerenderServiceCanadidates[] =
77     "SkipPrerenderServiceCandidates";
78 const char kDisableSessionStorageNamespaceMerging[] =
79     "DisableSessionStorageNamespaceMerging";
80 const char kPrerenderCookieStore[] = "PrerenderCookieStore";
81 
SetupPrerenderFieldTrial()82 void SetupPrerenderFieldTrial() {
83   const FieldTrial::Probability divisor = 1000;
84 
85   FieldTrial::Probability control_probability;
86   FieldTrial::Probability experiment_multi_prerender_probability;
87   FieldTrial::Probability experiment_15min_ttl_probability;
88   FieldTrial::Probability experiment_no_use_probability;
89 
90   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
91   if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
92       channel == chrome::VersionInfo::CHANNEL_BETA) {
93     // Use very conservatives and stable settings in beta and stable.
94     const FieldTrial::Probability release_prerender_enabled_probability = 980;
95     const FieldTrial::Probability release_control_probability = 10;
96     const FieldTrial::Probability
97         release_experiment_multi_prerender_probability = 0;
98     const FieldTrial::Probability release_experiment_15min_ttl_probability = 10;
99     const FieldTrial::Probability release_experiment_no_use_probability = 0;
100     COMPILE_ASSERT(
101         release_prerender_enabled_probability + release_control_probability +
102         release_experiment_multi_prerender_probability +
103         release_experiment_15min_ttl_probability +
104         release_experiment_no_use_probability == divisor,
105         release_experiment_probabilities_must_equal_divisor);
106 
107     control_probability = release_control_probability;
108     experiment_multi_prerender_probability =
109         release_experiment_multi_prerender_probability;
110     experiment_15min_ttl_probability = release_experiment_15min_ttl_probability;
111     experiment_no_use_probability = release_experiment_no_use_probability;
112   } else {
113     // In testing channels, use more experiments and a larger control group to
114     // improve quality of data.
115     const FieldTrial::Probability dev_prerender_enabled_probability = 250;
116     const FieldTrial::Probability dev_control_probability = 250;
117     const FieldTrial::Probability
118         dev_experiment_multi_prerender_probability = 250;
119     const FieldTrial::Probability dev_experiment_15min_ttl_probability = 125;
120     const FieldTrial::Probability dev_experiment_no_use_probability = 125;
121     COMPILE_ASSERT(dev_prerender_enabled_probability + dev_control_probability +
122                    dev_experiment_multi_prerender_probability +
123                    dev_experiment_15min_ttl_probability +
124                    dev_experiment_no_use_probability == divisor,
125                    dev_experiment_probabilities_must_equal_divisor);
126 
127     control_probability = dev_control_probability;
128     experiment_multi_prerender_probability =
129         dev_experiment_multi_prerender_probability;
130     experiment_15min_ttl_probability = dev_experiment_15min_ttl_probability;
131     experiment_no_use_probability = dev_experiment_no_use_probability;
132   }
133 
134   int prerender_enabled_group = -1;
135   scoped_refptr<FieldTrial> trial(
136       FieldTrialList::FactoryGetFieldTrial(
137           "Prerender", divisor, "PrerenderEnabled",
138           2014, 12, 31, FieldTrial::SESSION_RANDOMIZED,
139           &prerender_enabled_group));
140   const int control_group =
141       trial->AppendGroup("PrerenderControl",
142                          control_probability);
143   const int experiment_multi_prerender_group =
144       trial->AppendGroup("PrerenderMulti",
145                          experiment_multi_prerender_probability);
146   const int experiment_15_min_TTL_group =
147       trial->AppendGroup("Prerender15minTTL",
148                          experiment_15min_ttl_probability);
149   const int experiment_no_use_group =
150       trial->AppendGroup("PrerenderNoUse",
151                          experiment_no_use_probability);
152 
153   const int trial_group = trial->group();
154   if (trial_group == prerender_enabled_group) {
155     PrerenderManager::SetMode(
156         PrerenderManager::PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP);
157   } else if (trial_group == control_group) {
158     PrerenderManager::SetMode(
159         PrerenderManager::PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP);
160   } else if (trial_group == experiment_multi_prerender_group) {
161     PrerenderManager::SetMode(
162         PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP);
163   } else if (trial_group == experiment_15_min_TTL_group) {
164     PrerenderManager::SetMode(
165         PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP);
166   } else if (trial_group == experiment_no_use_group) {
167     PrerenderManager::SetMode(
168         PrerenderManager::PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP);
169   } else {
170     NOTREACHED();
171   }
172 }
173 
174 }  // end namespace
175 
176 void ConfigureOmniboxPrerender();
177 
ConfigurePrerender(const CommandLine & command_line)178 void ConfigurePrerender(const CommandLine& command_line) {
179   enum PrerenderOption {
180     PRERENDER_OPTION_AUTO,
181     PRERENDER_OPTION_DISABLED,
182     PRERENDER_OPTION_ENABLED,
183   };
184 
185   PrerenderOption prerender_option = PRERENDER_OPTION_AUTO;
186   if (command_line.HasSwitch(switches::kPrerenderMode)) {
187     const string switch_value =
188         command_line.GetSwitchValueASCII(switches::kPrerenderMode);
189 
190     if (switch_value == switches::kPrerenderModeSwitchValueAuto) {
191       prerender_option = PRERENDER_OPTION_AUTO;
192     } else if (switch_value == switches::kPrerenderModeSwitchValueDisabled) {
193       prerender_option = PRERENDER_OPTION_DISABLED;
194     } else if (switch_value.empty() ||
195                switch_value == switches::kPrerenderModeSwitchValueEnabled) {
196       // The empty string means the option was provided with no value, and that
197       // means enable.
198       prerender_option = PRERENDER_OPTION_ENABLED;
199     } else {
200       prerender_option = PRERENDER_OPTION_DISABLED;
201       LOG(ERROR) << "Invalid --prerender option received on command line: "
202                  << switch_value;
203       LOG(ERROR) << "Disabling prerendering!";
204     }
205   }
206 
207   switch (prerender_option) {
208     case PRERENDER_OPTION_AUTO:
209       SetupPrerenderFieldTrial();
210       break;
211     case PRERENDER_OPTION_DISABLED:
212       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
213       break;
214     case PRERENDER_OPTION_ENABLED:
215       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
216       break;
217     default:
218       NOTREACHED();
219   }
220 
221   ConfigureOmniboxPrerender();
222 }
223 
ConfigureOmniboxPrerender()224 void ConfigureOmniboxPrerender() {
225   // Field trial to see if we're enabled.
226   const FieldTrial::Probability kDivisor = 100;
227 
228   FieldTrial::Probability kDisabledProbability = 10;
229   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
230   if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
231       channel == chrome::VersionInfo::CHANNEL_BETA) {
232     kDisabledProbability = 1;
233   }
234   scoped_refptr<FieldTrial> omnibox_prerender_trial(
235       FieldTrialList::FactoryGetFieldTrial(
236           kOmniboxTrialName, kDivisor, "OmniboxPrerenderEnabled",
237           2014, 12, 31, FieldTrial::SESSION_RANDOMIZED,
238           &g_omnibox_trial_default_group_number));
239   omnibox_prerender_trial->AppendGroup("OmniboxPrerenderDisabled",
240                                        kDisabledProbability);
241 }
242 
IsOmniboxEnabled(Profile * profile)243 bool IsOmniboxEnabled(Profile* profile) {
244   if (!profile)
245     return false;
246 
247   if (!PrerenderManager::IsPrerenderingPossible())
248     return false;
249 
250   // Override any field trial groups if the user has set a command line flag.
251   if (CommandLine::ForCurrentProcess()->HasSwitch(
252       switches::kPrerenderFromOmnibox)) {
253     const string switch_value =
254         CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
255             switches::kPrerenderFromOmnibox);
256 
257     if (switch_value == switches::kPrerenderFromOmniboxSwitchValueEnabled)
258       return true;
259 
260     if (switch_value == switches::kPrerenderFromOmniboxSwitchValueDisabled)
261       return false;
262 
263     DCHECK_EQ(switches::kPrerenderFromOmniboxSwitchValueAuto, switch_value);
264   }
265 
266   const int group = FieldTrialList::FindValue(kOmniboxTrialName);
267   return group == FieldTrial::kNotFinalized ||
268          group == g_omnibox_trial_default_group_number;
269 }
270 
271 /*
272 PrerenderLocalPredictorSpec is a field trial, and its value must have the
273 following format:
274 key1=value1:key2=value2:key3=value3
275 eg "LocalPredictor=Enabled:SideEffectFreeWhitelist=Enabled"
276 The function below extracts the value corresponding to a key provided from the
277 LocalPredictorSpec.
278 */
GetLocalPredictorSpecValue(string spec_key)279 string GetLocalPredictorSpecValue(string spec_key) {
280   vector<string> elements;
281   base::SplitString(FieldTrialList::FindFullName(kLocalPredictorSpecTrialName),
282                     ':', &elements);
283   for (int i = 0; i < static_cast<int>(elements.size()); i++) {
284     vector<string> key_value;
285     base::SplitString(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   return
306       !CommandLine::ForCurrentProcess()->HasSwitch(
307           switches::kDisablePrerenderLocalPredictor) &&
308       GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup;
309 }
310 
DisableLocalPredictorBasedOnSyncAndConfiguration(Profile * profile)311 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) {
312   return
313       GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) ==
314           kEnabledGroup &&
315       !IsUnencryptedSyncEnabled(profile);
316 }
317 
IsLoggedInPredictorEnabled()318 bool IsLoggedInPredictorEnabled() {
319   return IsLocalPredictorEnabled();
320 }
321 
IsSideEffectFreeWhitelistEnabled()322 bool IsSideEffectFreeWhitelistEnabled() {
323   return IsLocalPredictorEnabled() &&
324       GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
325       kDisabledGroup;
326 }
327 
IsLocalPredictorPrerenderLaunchEnabled()328 bool IsLocalPredictorPrerenderLaunchEnabled() {
329   return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
330 }
331 
IsLocalPredictorPrerenderAlwaysControlEnabled()332 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
333   // If we prefetch rather than prerender, we automatically also prerender
334   // as a control group only.
335   return (GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) ==
336           kEnabledGroup) || IsLocalPredictorPrerenderPrefetchEnabled();
337 }
338 
IsLocalPredictorPrerenderPrefetchEnabled()339 bool IsLocalPredictorPrerenderPrefetchEnabled() {
340   return GetLocalPredictorSpecValue(kPrerenderPrefetchKeyName) ==
341       kEnabledGroup;
342 }
343 
ShouldQueryPrerenderService(Profile * profile)344 bool ShouldQueryPrerenderService(Profile* profile) {
345   return IsUnencryptedSyncEnabled(profile) &&
346       GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) ==
347       kEnabledGroup;
348 }
349 
ShouldQueryPrerenderServiceForCurrentURL()350 bool ShouldQueryPrerenderServiceForCurrentURL() {
351   return GetLocalPredictorSpecValue(
352       kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup;
353 }
354 
ShouldQueryPrerenderServiceForCandidateURLs()355 bool ShouldQueryPrerenderServiceForCandidateURLs() {
356   return GetLocalPredictorSpecValue(
357       kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup;
358 }
359 
GetPrerenderServiceURLPrefix()360 string GetPrerenderServiceURLPrefix() {
361   string prefix = chrome_variations::GetVariationParamValue(
362       kLocalPredictorSpecTrialName,
363       kPrerenderServiceURLPrefixParameterName);
364   return prefix.empty() ? kDefaultPrerenderServiceURLPrefix : prefix;
365 }
366 
GetPrerenderServiceBehaviorID()367 int GetPrerenderServiceBehaviorID() {
368   int id;
369   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName),
370               &id);
371   // The behavior ID must be non-negative.
372   return std::max(id, 0);
373 }
374 
GetPrerenderServiceFetchTimeoutMs()375 int GetPrerenderServiceFetchTimeoutMs() {
376   int result;
377   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName),
378               &result);
379   // If the value is outside the valid range, use the default value.
380   return (result < kMinPrerenderServiceTimeoutMs ||
381           result > kMaxPrerenderServiceTimeoutMs) ?
382       kDefaultPrerenderServiceTimeoutMs : 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   return (ttl < 10 || ttl > 600) ? 180 : ttl;
390 }
391 
GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds()392 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
393   int half_life_time;
394   StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName),
395               &half_life_time);
396   // Sanity check: Ensure the half life time is non-negative.
397   return std::max(half_life_time, 0);
398 }
399 
GetLocalPredictorMaxConcurrentPrerenders()400 int GetLocalPredictorMaxConcurrentPrerenders() {
401   int num_prerenders;
402   StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName),
403               &num_prerenders);
404   // Sanity check: Ensure the number of prerenders is between 1 and 10.
405   return std::min(std::max(num_prerenders, 1), 10);
406 }
407 
GetLocalPredictorMaxLaunchPrerenders()408 int GetLocalPredictorMaxLaunchPrerenders() {
409   int num_prerenders;
410   StringToInt(GetLocalPredictorSpecValue(kMaxLaunchPrerenderKeyName),
411               &num_prerenders);
412   // Sanity check: Ensure the number of prerenders is between 1 and 10.
413   return std::min(std::max(num_prerenders, 1), 10);
414 }
415 
SkipLocalPredictorFragment()416 bool SkipLocalPredictorFragment() {
417   return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
418 }
419 
SkipLocalPredictorHTTPS()420 bool SkipLocalPredictorHTTPS() {
421   return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
422 }
423 
SkipLocalPredictorWhitelist()424 bool SkipLocalPredictorWhitelist() {
425   return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
426 }
427 
SkipLocalPredictorServiceWhitelist()428 bool SkipLocalPredictorServiceWhitelist() {
429   return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup;
430 }
431 
SkipLocalPredictorLoggedIn()432 bool SkipLocalPredictorLoggedIn() {
433   return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
434 }
435 
SkipLocalPredictorDefaultNoPrerender()436 bool SkipLocalPredictorDefaultNoPrerender() {
437   return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
438 }
439 
SkipLocalPredictorLocalCandidates()440 bool SkipLocalPredictorLocalCandidates() {
441   return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) ==
442       kEnabledGroup;
443 }
444 
SkipLocalPredictorServiceCandidates()445 bool SkipLocalPredictorServiceCandidates() {
446   return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) ==
447       kEnabledGroup;
448 }
449 
ShouldMergeSessionStorageNamespaces()450 bool ShouldMergeSessionStorageNamespaces() {
451   return GetLocalPredictorSpecValue(kDisableSessionStorageNamespaceMerging) !=
452       kDisabledGroup;
453 }
454 
IsPrerenderCookieStoreEnabled()455 bool IsPrerenderCookieStoreEnabled() {
456   return GetLocalPredictorSpecValue(kPrerenderCookieStore) != kDisabledGroup &&
457       FieldTrialList::FindFullName(kPrerenderCookieStore) != kDisabledGroup;
458 }
459 
460 }  // namespace prerender
461