- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / prerender / prerender_field_trial.cc
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
78
79 void SetupPrefetchFieldTrial() {
80   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
81   if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
82       channel == chrome::VersionInfo::CHANNEL_BETA) {
83     return;
84   }
85
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);
95 }
96
97 void SetupPrerenderFieldTrial() {
98   const FieldTrial::Probability divisor = 1000;
99
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;
104
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;
115     COMPILE_ASSERT(
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);
121
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;
127   } else {
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);
141
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;
147   }
148
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);
167
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);
184   } else {
185     NOTREACHED();
186   }
187 }
188
189 }  // end namespace
190
191 void ConfigureOmniboxPrerender();
192
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,
199   };
200
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);
205
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
213       // means enable.
214       prerender_option = PRERENDER_OPTION_ENABLED;
215     } else if (switch_value ==
216                switches::kPrerenderModeSwitchValuePrefetchOnly) {
217       prerender_option = PRERENDER_OPTION_PREFETCH_ONLY;
218     } else {
219       prerender_option = PRERENDER_OPTION_DISABLED;
220       LOG(ERROR) << "Invalid --prerender option received on command line: "
221                  << switch_value;
222       LOG(ERROR) << "Disabling prerendering!";
223     }
224   }
225
226   switch (prerender_option) {
227     case PRERENDER_OPTION_AUTO:
228       SetupPrefetchFieldTrial();
229       SetupPrerenderFieldTrial();
230       break;
231     case PRERENDER_OPTION_DISABLED:
232       PrerenderManager::SetIsPrefetchEnabled(false);
233       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
234       break;
235     case PRERENDER_OPTION_ENABLED:
236       PrerenderManager::SetIsPrefetchEnabled(true);
237       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
238       break;
239     case PRERENDER_OPTION_PREFETCH_ONLY:
240       PrerenderManager::SetIsPrefetchEnabled(true);
241       PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_DISABLED);
242       break;
243     default:
244       NOTREACHED();
245   }
246
247   ConfigureOmniboxPrerender();
248 }
249
250 void ConfigureOmniboxPrerender() {
251   // Field trial to see if we're enabled.
252   const FieldTrial::Probability kDivisor = 100;
253
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;
259   }
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);
267 }
268
269 bool IsOmniboxEnabled(Profile* profile) {
270   if (!profile)
271     return false;
272
273   if (!PrerenderManager::IsPrerenderingPossible())
274     return false;
275
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);
282
283     if (switch_value == switches::kPrerenderFromOmniboxSwitchValueEnabled)
284       return true;
285
286     if (switch_value == switches::kPrerenderFromOmniboxSwitchValueDisabled)
287       return false;
288
289     DCHECK(switch_value == switches::kPrerenderFromOmniboxSwitchValueAuto);
290   }
291
292   const int group = FieldTrialList::FindValue(kOmniboxTrialName);
293   return group == FieldTrial::kNotFinalized ||
294          group == g_omnibox_trial_default_group_number;
295 }
296
297 /*
298 PrerenderLocalPredictorSpec is a field trial, and its value must have the
299 following format:
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
303 LocalPredictorSpec.
304 */
305 string GetLocalPredictorSpecValue(string spec_key) {
306   vector<string> elements;
307   SplitStringUsingSubstr(
308       FieldTrialList::FindFullName(kLocalPredictorSpecTrialName),
309       ":",
310       &elements);
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)
315       return key_value[1];
316   }
317   return string();
318 }
319
320 bool IsUnencryptedSyncEnabled(Profile* profile) {
321   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
322       GetForProfile(profile);
323   return service && service->GetSessionModelAssociator() &&
324       !service->EncryptEverythingEnabled();
325 }
326
327 // Indicates whether the Local Predictor is enabled based on field trial
328 // selection.
329 bool IsLocalPredictorEnabled() {
330 #if defined(OS_ANDROID) || defined(OS_IOS)
331   return false;
332 #endif
333   if (CommandLine::ForCurrentProcess()->HasSwitch(
334           switches::kDisablePrerenderLocalPredictor)) {
335     return false;
336   }
337   return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup;
338 }
339
340 bool DisableLocalPredictorBasedOnSyncAndConfiguration(Profile* profile) {
341   return
342       GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) ==
343       kEnabledGroup &&
344       !IsUnencryptedSyncEnabled(profile);
345 }
346
347 bool IsLoggedInPredictorEnabled() {
348   return IsLocalPredictorEnabled();
349 }
350
351 bool IsSideEffectFreeWhitelistEnabled() {
352   return IsLocalPredictorEnabled() &&
353       GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) !=
354       kDisabledGroup;
355 }
356
357 bool IsLocalPredictorPrerenderLaunchEnabled() {
358   return GetLocalPredictorSpecValue(kPrerenderLaunchKeyName) != kDisabledGroup;
359 }
360
361 bool IsLocalPredictorPrerenderAlwaysControlEnabled() {
362   return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) ==
363       kEnabledGroup;
364 }
365
366 bool ShouldQueryPrerenderService(Profile* profile) {
367   return IsUnencryptedSyncEnabled(profile) &&
368       GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) ==
369       kEnabledGroup;
370 }
371
372 bool ShouldQueryPrerenderServiceForCurrentURL() {
373   return GetLocalPredictorSpecValue(
374       kPrerenderQueryPrerenderServiceCurrentURLKeyName) != kDisabledGroup;
375 }
376
377 bool ShouldQueryPrerenderServiceForCandidateURLs() {
378   return GetLocalPredictorSpecValue(
379       kPrerenderQueryPrerenderServiceCandidateURLsKeyName) != kDisabledGroup;
380 }
381
382 string GetPrerenderServiceURLPrefix() {
383   string prefix = chrome_variations::GetVariationParamValue(
384       kLocalPredictorSpecTrialName,
385       kPrerenderServiceURLPrefixParameterName);
386   if (prefix.empty())
387     prefix = kDefaultPrerenderServiceURLPrefix;
388   return prefix;
389 }
390
391 int GetPrerenderServiceBehaviorID() {
392   int id;
393   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName),
394               &id);
395   // The behavior ID must be non-negative.
396   if (id < 0)
397     id = 0;
398   return id;
399 }
400
401 int GetPrerenderServiceFetchTimeoutMs() {
402   int result;
403   StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName),
404               &result);
405   // The behavior ID must be non-negative.
406   if (result < kMinPrerenderServiceTimeoutMs ||
407       result > kMaxPrerenderServiceTimeoutMs) {
408     result = kDefaultPrerenderServiceTimeoutMs;
409   }
410   return result;
411 }
412
413 int GetLocalPredictorTTLSeconds() {
414   int ttl;
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)
418     ttl = 180;
419   return ttl;
420 }
421
422 int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds() {
423   int half_life_time;
424   StringToInt(GetLocalPredictorSpecValue(kPrerenderPriorityHalfLifeTimeKeyName),
425               &half_life_time);
426   // Sanity check: Ensure the half life time is non-negative.
427   if (half_life_time < 0)
428     half_life_time = 0;
429   return half_life_time;
430 }
431
432 int GetLocalPredictorMaxConcurrentPrerenders() {
433   int num_prerenders;
434   StringToInt(GetLocalPredictorSpecValue(kMaxConcurrentPrerenderKeyName),
435               &num_prerenders);
436   // Sanity check: Ensure the number of prerenders is at least 1.
437   if (num_prerenders < 1)
438     num_prerenders = 1;
439   // Sanity check: Ensure the number of prerenders is at most 10.
440   if (num_prerenders > 10)
441     num_prerenders = 10;
442   return num_prerenders;
443 };
444
445 bool SkipLocalPredictorFragment() {
446   return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup;
447 }
448
449 bool SkipLocalPredictorHTTPS() {
450   return GetLocalPredictorSpecValue(kSkipHTTPS) == kEnabledGroup;
451 }
452
453 bool SkipLocalPredictorWhitelist() {
454   return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup;
455 }
456
457 bool SkipLocalPredictorServiceWhitelist() {
458   return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup;
459 }
460
461 bool SkipLocalPredictorLoggedIn() {
462   return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup;
463 }
464
465 bool SkipLocalPredictorDefaultNoPrerender() {
466   return GetLocalPredictorSpecValue(kSkipDefaultNoPrerender) == kEnabledGroup;
467 }
468
469 bool SkipLocalPredictorLocalCandidates() {
470   return GetLocalPredictorSpecValue(kSkipPrerenderLocalCanadidates) ==
471       kEnabledGroup;
472 }
473
474 bool SkipLocalPredictorServiceCandidates() {
475   return GetLocalPredictorSpecValue(kSkipPrerenderServiceCanadidates) ==
476       kEnabledGroup;
477 }
478
479 }  // namespace prerender