blob: 51951ab1e0fdfc55be5084e715158074db48f5f8 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chrome_browser_field_trials.h"
#include <string>
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/auto_launch_trial.h"
#include "chrome/browser/autocomplete/autocomplete_field_trial.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/gpu/chrome_gpu_util.h"
#include "chrome/browser/metrics/variations/variations_service.h"
#include "chrome/browser/prerender/prerender_field_trial.h"
#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
#include "chrome/browser/ui/sync/one_click_signin_helper.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/metrics/variations/variations_util.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/spdy/spdy_session.h"
#include "ui/base/layout.h"
#if defined(OS_WIN)
#include "net/socket/tcp_client_socket_win.h"
#include "ui/base/win/dpi.h" // For DisableNewTabFieldTrialIfNecesssary.
#endif // defined(OS_WIN)
namespace {
// Set up a uniformity field trial. |one_time_randomized| indicates if the
// field trial is one-time randomized or session-randomized. |trial_name_string|
// must contain a "%d" since the percentage of the group will be inserted in
// the trial name. |num_trial_groups| must be a divisor of 100 (e.g. 5, 20)
void SetupSingleUniformityFieldTrial(
bool one_time_randomized,
const std::string& trial_name_string,
const chrome_variations::VariationID trial_base_id,
int num_trial_groups) {
// Probability per group remains constant for all uniformity trials, what
// changes is the probability divisor.
static const base::FieldTrial::Probability kProbabilityPerGroup = 1;
const std::string kDefaultGroupName = "default";
const base::FieldTrial::Probability divisor = num_trial_groups;
DCHECK_EQ(100 % num_trial_groups, 0);
const int group_percent = 100 / num_trial_groups;
const std::string trial_name = StringPrintf(trial_name_string.c_str(),
group_percent);
DVLOG(1) << "Trial name = " << trial_name;
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
trial_name, divisor, kDefaultGroupName, 2015, 1, 1, NULL));
if (one_time_randomized)
trial->UseOneTimeRandomization();
chrome_variations::AssociateGoogleVariationID(
chrome_variations::GOOGLE_WEB_PROPERTIES, trial_name, kDefaultGroupName,
trial_base_id);
// Loop starts with group 1 because the field trial automatically creates a
// default group, which would be group 0.
for (int group_number = 1; group_number < num_trial_groups; ++group_number) {
const std::string group_name = StringPrintf("group_%02d", group_number);
DVLOG(1) << " Group name = " << group_name;
trial->AppendGroup(group_name, kProbabilityPerGroup);
chrome_variations::AssociateGoogleVariationID(
chrome_variations::GOOGLE_WEB_PROPERTIES, trial_name, group_name,
static_cast<chrome_variations::VariationID>(trial_base_id +
group_number));
}
// Now that all groups have been appended, call group() on the trial to
// ensure that our trial is registered. This resolves an off-by-one issue
// where the default group never gets chosen if we don't "use" the trial.
const int chosen_group = trial->group();
DVLOG(1) << "Chosen Group: " << chosen_group;
}
// Setup a 50% uniformity trial for new installs only. This is accomplished by
// disabling the trial on clients that were installed before a specified date.
void SetupNewInstallUniformityTrial(const base::Time& install_date) {
const base::Time::Exploded kStartDate = {
2012, 11, 0, 6, // Nov 6, 2012
0, 0, 0, 0 // 00:00:00.000
};
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"UMA-New-Install-Uniformity-Trial", 100, "Disabled",
2015, 1, 1, NULL));
trial->UseOneTimeRandomization();
trial->AppendGroup("Control", 50);
trial->AppendGroup("Experiment", 50);
const base::Time start_date = base::Time::FromLocalExploded(kStartDate);
if (install_date < start_date)
trial->Disable();
else
trial->group();
}
void SetSocketReusePolicy(int warmest_socket_trial_group,
const int socket_policy[],
int num_groups) {
const int* result = std::find(socket_policy, socket_policy + num_groups,
warmest_socket_trial_group);
DCHECK_NE(result, socket_policy + num_groups)
<< "Not a valid socket reuse policy group";
net::SetSocketReusePolicy(result - socket_policy);
}
} // namespace
ChromeBrowserFieldTrials::ChromeBrowserFieldTrials(
const CommandLine& parsed_command_line) :
parsed_command_line_(parsed_command_line) {
}
ChromeBrowserFieldTrials::~ChromeBrowserFieldTrials() {
}
void ChromeBrowserFieldTrials::SetupFieldTrials(
const base::Time& install_time) {
prerender::ConfigurePrefetchAndPrerender(parsed_command_line_);
SpdyFieldTrial();
WarmConnectionFieldTrial();
AutoLaunchChromeFieldTrial();
gpu_util::InitializeCompositingFieldTrial();
SetupUniformityFieldTrials(install_time);
AutocompleteFieldTrial::Activate();
DisableNewTabFieldTrialIfNecesssary();
SetUpInfiniteCacheFieldTrial();
SetUpCacheSensitivityAnalysisFieldTrial();
WindowsOverlappedTCPReadsFieldTrial();
#if defined(ENABLE_ONE_CLICK_SIGNIN)
OneClickSigninHelper::InitializeFieldTrial();
#endif
InstantiateDynamicTrials();
}
// When --use-spdy not set, users will be in A/B test for spdy.
// group A (npn_with_spdy): this means npn and spdy are enabled. In case server
// supports spdy, browser will use spdy.
// group B (npn_with_http): this means npn is enabled but spdy won't be used.
// Http is still used for all requests.
// default group: no npn or spdy is involved. The "old" non-spdy
// chrome behavior.
void ChromeBrowserFieldTrials::SpdyFieldTrial() {
// Setup SPDY CWND Field trial.
const base::FieldTrial::Probability kSpdyCwndDivisor = 100;
const base::FieldTrial::Probability kSpdyCwnd16 = 20; // fixed at 16
const base::FieldTrial::Probability kSpdyCwnd10 = 20; // fixed at 10
const base::FieldTrial::Probability kSpdyCwndMin16 = 20; // no less than 16
const base::FieldTrial::Probability kSpdyCwndMin10 = 20; // no less than 10
// After June 30, 2013 builds, it will always be in default group
// (cwndDynamic).
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"SpdyCwnd", kSpdyCwndDivisor, "cwndDynamic", 2013, 6, 30, NULL));
trial->AppendGroup("cwnd10", kSpdyCwnd10);
trial->AppendGroup("cwnd16", kSpdyCwnd16);
trial->AppendGroup("cwndMin16", kSpdyCwndMin16);
trial->AppendGroup("cwndMin10", kSpdyCwndMin10);
}
// If --socket-reuse-policy is not specified, run an A/B test for choosing the
// warmest socket.
void ChromeBrowserFieldTrials::WarmConnectionFieldTrial() {
const CommandLine& command_line = parsed_command_line_;
if (command_line.HasSwitch(switches::kSocketReusePolicy)) {
std::string socket_reuse_policy_str = command_line.GetSwitchValueASCII(
switches::kSocketReusePolicy);
int policy = -1;
base::StringToInt(socket_reuse_policy_str, &policy);
const int policy_list[] = { 0, 1, 2 };
VLOG(1) << "Setting socket_reuse_policy = " << policy;
SetSocketReusePolicy(policy, policy_list, arraysize(policy_list));
return;
}
const base::FieldTrial::Probability kWarmSocketDivisor = 100;
const base::FieldTrial::Probability kWarmSocketProbability = 33;
// Default value is USE_LAST_ACCESSED_SOCKET.
int last_accessed_socket = -1;
// After January 30, 2013 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> warmest_socket_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"WarmSocketImpact", kWarmSocketDivisor, "last_accessed_socket",
2013, 1, 30, &last_accessed_socket));
const int warmest_socket = warmest_socket_trial->AppendGroup(
"warmest_socket", kWarmSocketProbability);
const int warm_socket = warmest_socket_trial->AppendGroup(
"warm_socket", kWarmSocketProbability);
const int warmest_socket_trial_group = warmest_socket_trial->group();
const int policy_list[] = { warmest_socket, warm_socket,
last_accessed_socket };
SetSocketReusePolicy(warmest_socket_trial_group, policy_list,
arraysize(policy_list));
}
void ChromeBrowserFieldTrials::AutoLaunchChromeFieldTrial() {
std::string brand;
google_util::GetBrand(&brand);
// Create a 100% field trial based on the brand code.
if (auto_launch_trial::IsInExperimentGroup(brand)) {
base::FieldTrialList::CreateFieldTrial(kAutoLaunchTrialName,
kAutoLaunchTrialAutoLaunchGroup);
} else if (auto_launch_trial::IsInControlGroup(brand)) {
base::FieldTrialList::CreateFieldTrial(kAutoLaunchTrialName,
kAutoLaunchTrialControlGroup);
}
}
void ChromeBrowserFieldTrials::SetupUniformityFieldTrials(
const base::Time& install_date) {
// One field trial will be created for each entry in this array. The i'th
// field trial will have |trial_sizes[i]| groups in it, including the default
// group. Each group will have a probability of 1/|trial_sizes[i]|.
const int num_trial_groups[] = { 100, 20, 10, 5, 2 };
// Declare our variation ID bases along side this array so we can loop over it
// and assign the IDs appropriately. So for example, the 1 percent experiments
// should have a size of 100 (100/100 = 1).
const chrome_variations::VariationID trial_base_ids[] = {
chrome_variations::UNIFORMITY_1_PERCENT_BASE,
chrome_variations::UNIFORMITY_5_PERCENT_BASE,
chrome_variations::UNIFORMITY_10_PERCENT_BASE,
chrome_variations::UNIFORMITY_20_PERCENT_BASE,
chrome_variations::UNIFORMITY_50_PERCENT_BASE
};
const std::string kOneTimeRandomizedTrialName =
"UMA-Uniformity-Trial-%d-Percent";
for (size_t i = 0; i < arraysize(num_trial_groups); ++i) {
SetupSingleUniformityFieldTrial(true, kOneTimeRandomizedTrialName,
trial_base_ids[i], num_trial_groups[i]);
}
// Setup a 5% session-randomized uniformity trial.
const std::string kSessionRandomizedTrialName =
"UMA-Session-Randomized-Uniformity-Trial-%d-Percent";
SetupSingleUniformityFieldTrial(false, kSessionRandomizedTrialName,
chrome_variations::UNIFORMITY_SESSION_RANDOMIZED_5_PERCENT_BASE, 20);
SetupNewInstallUniformityTrial(install_date);
}
void ChromeBrowserFieldTrials::DisableNewTabFieldTrialIfNecesssary() {
// The new tab button field trial will get created in variations_service.cc
// through the variations server. However, since there are no HiDPI assets
// for it, disable it for non-desktop layouts.
base::FieldTrial* trial = base::FieldTrialList::Find("NewTabButton");
if (trial) {
bool using_hidpi_assets = false;
#if defined(ENABLE_HIDPI) && defined(OS_WIN)
// Mirrors logic in resource_bundle_win.cc.
using_hidpi_assets = ui::GetDPIScale() > 1.5;
#endif
if (ui::GetDisplayLayout() != ui::LAYOUT_DESKTOP || using_hidpi_assets)
trial->Disable();
}
}
void ChromeBrowserFieldTrials::SetUpInfiniteCacheFieldTrial() {
const base::FieldTrial::Probability kDivisor = 100;
base::FieldTrial::Probability infinite_cache_probability = 0;
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial("InfiniteCache", kDivisor,
"No", 2013, 12, 31, NULL));
trial->UseOneTimeRandomization();
trial->AppendGroup("Yes", infinite_cache_probability);
trial->AppendGroup("Control", infinite_cache_probability);
}
void ChromeBrowserFieldTrials::SetUpCacheSensitivityAnalysisFieldTrial() {
const base::FieldTrial::Probability kDivisor = 100;
base::FieldTrial::Probability sensitivity_analysis_probability = 0;
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial("CacheSensitivityAnalysis",
kDivisor, "No",
2012, 12, 31, NULL));
trial->AppendGroup("ControlA", sensitivity_analysis_probability);
trial->AppendGroup("ControlB", sensitivity_analysis_probability);
trial->AppendGroup("100A", sensitivity_analysis_probability);
trial->AppendGroup("100B", sensitivity_analysis_probability);
trial->AppendGroup("200A", sensitivity_analysis_probability);
trial->AppendGroup("200B", sensitivity_analysis_probability);
trial->AppendGroup("400A", sensitivity_analysis_probability);
trial->AppendGroup("400B", sensitivity_analysis_probability);
}
void ChromeBrowserFieldTrials::WindowsOverlappedTCPReadsFieldTrial() {
#if defined(OS_WIN)
if (parsed_command_line_.HasSwitch(switches::kOverlappedRead)) {
std::string option =
parsed_command_line_.GetSwitchValueASCII(switches::kOverlappedRead);
if (LowerCaseEqualsASCII(option, "off"))
net::TCPClientSocketWin::DisableOverlappedReads();
} else {
const base::FieldTrial::Probability kDivisor = 2; // 1 in 2 chance
const base::FieldTrial::Probability kOverlappedReadProbability = 1;
scoped_refptr<base::FieldTrial> overlapped_reads_trial(
base::FieldTrialList::FactoryGetFieldTrial("OverlappedReadImpact",
kDivisor, "OverlappedReadEnabled", 2013, 6, 1, NULL));
int overlapped_reads_disabled_group =
overlapped_reads_trial->AppendGroup("OverlappedReadDisabled",
kOverlappedReadProbability);
int assigned_group = overlapped_reads_trial->group();
if (assigned_group == overlapped_reads_disabled_group)
net::TCPClientSocketWin::DisableOverlappedReads();
}
#endif
}
void ChromeBrowserFieldTrials::InstantiateDynamicTrials() {
// Call |FindValue()| on the trials below, which may come from the server, to
// ensure they get marked as "used" for the purposes of data reporting.
base::FieldTrialList::FindValue("UMA-Dynamic-Binary-Uniformity-Trial");
base::FieldTrialList::FindValue("UMA-Dynamic-Uniformity-Trial");
base::FieldTrialList::FindValue("InstantDummy");
base::FieldTrialList::FindValue("InstantChannel");
base::FieldTrialList::FindValue("Test0PercentDefault");
}