Extract ChromeBrowserFieldTrials out of ChromeBrowserMainParts.

BUG=none
TEST=compiles

Review URL: https://chromiumcodereview.appspot.com/10827080

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149216 0039d316-1c4b-4281-b951-d872f2087c98
parent 5fe3e1a2
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/automation/automation_provider_list.h"
#include "chrome/browser/background/background_mode_manager.h" #include "chrome/browser/background/background_mode_manager.h"
#include "chrome/browser/browser_trial.h"
#include "chrome/browser/chrome_browser_main.h" #include "chrome/browser/chrome_browser_main.h"
#include "chrome/browser/chrome_content_browser_client.h" #include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/chrome_plugin_service_filter.h" #include "chrome/browser/chrome_plugin_service_filter.h"
......
// Copyright (c) 2006-2008 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/browser_trial.h"
// The following is a sample of the lines that should be listed here.
// // A test to determine the impact of kCase1 vs kCase2 pruning algorithms.
// const wchar_t* BrowserTrial::kPruningAlgorithmFieldTrial =
// L"pruning_algorithm";
// Copyright (c) 2006-2008 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.
// BrowserTrial contains methods specific to field trials (using FieldTrial
// functionality) for browser tests.
#ifndef CHROME_BROWSER_BROWSER_TRIAL_H_
#define CHROME_BROWSER_BROWSER_TRIAL_H_
#include "base/basictypes.h"
// Currently we use this as a name space, to hold static shared constants which
// define current and past trials.
class BrowserTrial {
public:
// The following is a sample line for what should be listed in this file.
// static const wchar_t* kPruningAlgorithmFieldTrial;
private:
DISALLOW_COPY_AND_ASSIGN(BrowserTrial);
};
#endif // CHROME_BROWSER_BROWSER_TRIAL_H_
// 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/extensions/default_apps_trial.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/gpu_util.h"
#include "chrome/browser/net/predictor.h"
#include "chrome/browser/prerender/prerender_field_trial.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/metrics/experiments_helper.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_stream_factory.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "ui/base/layout.h"
#if defined(OS_WIN)
#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();
experiments_helper::AssociateGoogleVariationID(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);
experiments_helper::AssociateGoogleVariationID(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;
}
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(bool proxy_policy_is_set) {
// Note: make sure to call ConnectionFieldTrial() before
// ProxyConnectionsFieldTrial().
ConnectionFieldTrial();
SocketTimeoutFieldTrial();
// If a policy is defining the number of active connections this field test
// shoud not be performed.
if (!proxy_policy_is_set)
ProxyConnectionsFieldTrial();
prerender::ConfigurePrefetchAndPrerender(parsed_command_line_);
SpdyFieldTrial();
ConnectBackupJobsFieldTrial();
WarmConnectionFieldTrial();
PredictorFieldTrial();
DefaultAppsFieldTrial();
AutoLaunchChromeFieldTrial();
gpu_util::InitializeForceCompositingModeFieldTrial();
SetupUniformityFieldTrials();
AutocompleteFieldTrial::Activate();
DisableNewTabFieldTrialIfNecesssary();
ChannelIDFieldTrial();
}
// This is an A/B test for the maximum number of persistent connections per
// host. Currently Chrome, Firefox, and IE8 have this value set at 6. Safari
// uses 4, and Fasterfox (a plugin for Firefox that supposedly configures it to
// run faster) uses 8. We would like to see how much of an effect this value has
// on browsing. Too large a value might cause us to run into SYN flood detection
// mechanisms.
void ChromeBrowserFieldTrials::ConnectionFieldTrial() {
const base::FieldTrial::Probability kConnectDivisor = 100;
const base::FieldTrial::Probability kConnectProbability = 1; // 1% prob.
// This (6) is the current default value. Having this group declared here
// makes it straightforward to modify |kConnectProbability| such that the same
// probability value will be assigned to all the other groups, while
// preserving the remainder of the of probability space to the default value.
int connect_6 = -1;
// After June 30, 2011 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> connect_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"ConnCountImpact", kConnectDivisor, "conn_count_6", 2011, 6, 30,
&connect_6));
const int connect_5 = connect_trial->AppendGroup("conn_count_5",
kConnectProbability);
const int connect_7 = connect_trial->AppendGroup("conn_count_7",
kConnectProbability);
const int connect_8 = connect_trial->AppendGroup("conn_count_8",
kConnectProbability);
const int connect_9 = connect_trial->AppendGroup("conn_count_9",
kConnectProbability);
const int connect_trial_group = connect_trial->group();
int max_sockets = 0;
if (connect_trial_group == connect_5) {
max_sockets = 5;
} else if (connect_trial_group == connect_6) {
max_sockets = 6;
} else if (connect_trial_group == connect_7) {
max_sockets = 7;
} else if (connect_trial_group == connect_8) {
max_sockets = 8;
} else if (connect_trial_group == connect_9) {
max_sockets = 9;
} else {
NOTREACHED();
}
net::ClientSocketPoolManager::set_max_sockets_per_group(
net::HttpNetworkSession::NORMAL_SOCKET_POOL, max_sockets);
}
// A/B test for determining a value for unused socket timeout. Currently the
// timeout defaults to 10 seconds. Having this value set too low won't allow us
// to take advantage of idle sockets. Setting it to too high could possibly
// result in more ERR_CONNECTION_RESETs, since some servers will kill a socket
// before we time it out. Since these are "unused" sockets, we won't retry the
// connection and instead show an error to the user. So we need to be
// conservative here. We've seen that some servers will close the socket after
// as short as 10 seconds. See http://crbug.com/84313 for more details.
void ChromeBrowserFieldTrials::SocketTimeoutFieldTrial() {
const base::FieldTrial::Probability kIdleSocketTimeoutDivisor = 100;
// 1% probability for all experimental settings.
const base::FieldTrial::Probability kSocketTimeoutProbability = 1;
// After June 30, 2011 builds, it will always be in default group.
int socket_timeout_10 = -1;
scoped_refptr<base::FieldTrial> socket_timeout_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"IdleSktToImpact", kIdleSocketTimeoutDivisor, "idle_timeout_10",
2011, 6, 30, &socket_timeout_10));
const int socket_timeout_5 =
socket_timeout_trial->AppendGroup("idle_timeout_5",
kSocketTimeoutProbability);
const int socket_timeout_20 =
socket_timeout_trial->AppendGroup("idle_timeout_20",
kSocketTimeoutProbability);
const int idle_to_trial_group = socket_timeout_trial->group();
if (idle_to_trial_group == socket_timeout_5) {
net::ClientSocketPool::set_unused_idle_socket_timeout(
base::TimeDelta::FromSeconds(5));
} else if (idle_to_trial_group == socket_timeout_10) {
net::ClientSocketPool::set_unused_idle_socket_timeout(
base::TimeDelta::FromSeconds(10));
} else if (idle_to_trial_group == socket_timeout_20) {
net::ClientSocketPool::set_unused_idle_socket_timeout(
base::TimeDelta::FromSeconds(20));
} else {
NOTREACHED();
}
}
void ChromeBrowserFieldTrials::ProxyConnectionsFieldTrial() {
const base::FieldTrial::Probability kProxyConnectionsDivisor = 100;
// 25% probability
const base::FieldTrial::Probability kProxyConnectionProbability = 1;
// This (32 connections per proxy server) is the current default value.
// Declaring it here allows us to easily re-assign the probability space while
// maintaining that the default group always has the remainder of the "share",
// which allows for cleaner and quicker changes down the line if needed.
int proxy_connections_32 = -1;
// After June 30, 2011 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> proxy_connection_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"ProxyConnectionImpact", kProxyConnectionsDivisor,
"proxy_connections_32", 2011, 6, 30, &proxy_connections_32));
// The number of max sockets per group cannot be greater than the max number
// of sockets per proxy server. We tried using 8, and it can easily
// lead to total browser stalls.
const int proxy_connections_16 =
proxy_connection_trial->AppendGroup("proxy_connections_16",
kProxyConnectionProbability);
const int proxy_connections_64 =
proxy_connection_trial->AppendGroup("proxy_connections_64",
kProxyConnectionProbability);
const int proxy_connections_trial_group = proxy_connection_trial->group();
int max_sockets = 0;
if (proxy_connections_trial_group == proxy_connections_16) {
max_sockets = 16;
} else if (proxy_connections_trial_group == proxy_connections_32) {
max_sockets = 32;
} else if (proxy_connections_trial_group == proxy_connections_64) {
max_sockets = 64;
} else {
NOTREACHED();
}
net::ClientSocketPoolManager::set_max_sockets_per_proxy_server(
net::HttpNetworkSession::NORMAL_SOCKET_POOL, max_sockets);
}
// 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() {
bool use_field_trial = true;
if (parsed_command_line_.HasSwitch(switches::kUseSpdy)) {
std::string spdy_mode =
parsed_command_line_.GetSwitchValueASCII(switches::kUseSpdy);
net::HttpNetworkLayer::EnableSpdy(spdy_mode);
use_field_trial = false;
}
if (parsed_command_line_.HasSwitch(switches::kEnableSpdy3)) {
net::HttpStreamFactory::EnableNpnSpdy3();
use_field_trial = false;
} else if (parsed_command_line_.HasSwitch(switches::kEnableNpn)) {
net::HttpStreamFactory::EnableNpnSpdy();
use_field_trial = false;
} else if (parsed_command_line_.HasSwitch(switches::kEnableNpnHttpOnly)) {
net::HttpStreamFactory::EnableNpnHttpOnly();
use_field_trial = false;
}
if (use_field_trial) {
const base::FieldTrial::Probability kSpdyDivisor = 100;
// Enable SPDY/3 for 95% of the users, HTTP (no SPDY) for 1% of the users
// and SPDY/2 for 4% of the users.
base::FieldTrial::Probability npnhttp_probability = 1;
base::FieldTrial::Probability spdy3_probability = 95;
#if defined(OS_CHROMEOS)
// Always enable SPDY (spdy/2 or spdy/3) on Chrome OS
npnhttp_probability = 0;
#endif // !defined(OS_CHROMEOS)
// NPN with spdy support is the default.
int npn_spdy_grp = -1;
// After June 30, 2013 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"SpdyImpact", kSpdyDivisor, "npn_with_spdy", 2013, 6, 30,
&npn_spdy_grp));
// NPN with only http support, no spdy.
int npn_http_grp = trial->AppendGroup("npn_with_http", npnhttp_probability);
// NPN with http/1.1, spdy/2, and spdy/3 support.
int spdy3_grp = trial->AppendGroup("spdy3", spdy3_probability);
int trial_grp = trial->group();
if (trial_grp == npn_spdy_grp) {
net::HttpStreamFactory::EnableNpnSpdy();
} else if (trial_grp == npn_http_grp) {
net::HttpStreamFactory::EnableNpnHttpOnly();
} else if (trial_grp == spdy3_grp) {
net::HttpStreamFactory::EnableNpnSpdy3();
} else {
NOTREACHED();
}
}
// 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 (parsed_command_line_.HasSwitch(switches::kMaxSpdyConcurrentStreams)) {
int value = 0;
base::StringToInt(parsed_command_line_.GetSwitchValueASCII(
switches::kMaxSpdyConcurrentStreams),
&value);
if (value > 0)
net::SpdySession::set_max_concurrent_streams(value);
}
}
// 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));
}
// If neither --enable-connect-backup-jobs or --disable-connect-backup-jobs is
// specified, run an A/B test for automatically establishing backup TCP
// connections when a certain timeout value is exceeded.
void ChromeBrowserFieldTrials::ConnectBackupJobsFieldTrial() {
if (parsed_command_line_.HasSwitch(switches::kEnableConnectBackupJobs)) {
net::internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
true);
} else if (parsed_command_line_.HasSwitch(
switches::kDisableConnectBackupJobs)) {
net::internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
false);
} else {
const base::FieldTrial::Probability kConnectBackupJobsDivisor = 100;
// 1% probability.
const base::FieldTrial::Probability kConnectBackupJobsProbability = 1;
// After June 30, 2011 builds, it will always be in default group.
int connect_backup_jobs_enabled = -1;
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial("ConnnectBackupJobs",
kConnectBackupJobsDivisor, "ConnectBackupJobsEnabled",
2011, 6, 30, &connect_backup_jobs_enabled));
trial->AppendGroup("ConnectBackupJobsDisabled",
kConnectBackupJobsProbability);
const int trial_group = trial->group();
net::internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
trial_group == connect_backup_jobs_enabled);
}
}
void ChromeBrowserFieldTrials::PredictorFieldTrial() {
const base::FieldTrial::Probability kDivisor = 1000;
// For each option (i.e., non-default), we have a fixed probability.
// 0.1% probability.
const base::FieldTrial::Probability kProbabilityPerGroup = 1;
// After June 30, 2011 builds, it will always be in default group
// (default_enabled_prefetch).
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"DnsImpact", kDivisor, "default_enabled_prefetch", 2011, 10, 30,
NULL));
// First option is to disable prefetching completely.
int disabled_prefetch = trial->AppendGroup("disabled_prefetch",
kProbabilityPerGroup);
// We're running two experiments at the same time. The first set of trials
// modulates the delay-time until we declare a congestion event (and purge
// our queue). The second modulates the number of concurrent resolutions
// we do at any time. Users are in exactly one trial (or the default) during
// any one run, and hence only one experiment at a time.
// Experiment 1:
// Set congestion detection at 250, 500, or 750ms, rather than the 1 second
// default.
int max_250ms_prefetch = trial->AppendGroup("max_250ms_queue_prefetch",
kProbabilityPerGroup);
int max_500ms_prefetch = trial->AppendGroup("max_500ms_queue_prefetch",
kProbabilityPerGroup);
int max_750ms_prefetch = trial->AppendGroup("max_750ms_queue_prefetch",
kProbabilityPerGroup);
// Set congestion detection at 2 seconds instead of the 1 second default.
int max_2s_prefetch = trial->AppendGroup("max_2s_queue_prefetch",
kProbabilityPerGroup);
// Experiment 2:
// Set max simultaneous resoultions to 2, 4, or 6, and scale the congestion
// limit proportionally (so we don't impact average probability of asserting
// congesion very much).
int max_2_concurrent_prefetch = trial->AppendGroup(
"max_2 concurrent_prefetch", kProbabilityPerGroup);
int max_4_concurrent_prefetch = trial->AppendGroup(
"max_4 concurrent_prefetch", kProbabilityPerGroup);
int max_6_concurrent_prefetch = trial->AppendGroup(
"max_6 concurrent_prefetch", kProbabilityPerGroup);
if (trial->group() != disabled_prefetch) {
// Initialize the DNS prefetch system.
size_t max_parallel_resolves =
chrome_browser_net::Predictor::kMaxSpeculativeParallelResolves;
int max_queueing_delay_ms =
chrome_browser_net::Predictor::kMaxSpeculativeResolveQueueDelayMs;
if (trial->group() == max_2_concurrent_prefetch)
max_parallel_resolves = 2;
else if (trial->group() == max_4_concurrent_prefetch)
max_parallel_resolves = 4;
else if (trial->group() == max_6_concurrent_prefetch)
max_parallel_resolves = 6;
chrome_browser_net::Predictor::set_max_parallel_resolves(
max_parallel_resolves);
if (trial->group() == max_250ms_prefetch) {
max_queueing_delay_ms =
(250 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
} else if (trial->group() == max_500ms_prefetch) {
max_queueing_delay_ms =
(500 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
} else if (trial->group() == max_750ms_prefetch) {
max_queueing_delay_ms =
(750 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
} else if (trial->group() == max_2s_prefetch) {
max_queueing_delay_ms =
(2000 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
}
chrome_browser_net::Predictor::set_max_queueing_delay(
max_queueing_delay_ms);
}
}
void ChromeBrowserFieldTrials::DefaultAppsFieldTrial() {
std::string brand;
google_util::GetBrand(&brand);
// Create a 100% field trial based on the brand code.
if (LowerCaseEqualsASCII(brand, "ecdb")) {
base::FieldTrialList::CreateFieldTrial(kDefaultAppsTrialName,
kDefaultAppsTrialNoAppsGroup);
} else if (LowerCaseEqualsASCII(brand, "ecda")) {
base::FieldTrialList::CreateFieldTrial(kDefaultAppsTrialName,
kDefaultAppsTrialWithAppsGroup);
}
}
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() {
// 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::kUniformity1PercentBase,
chrome_variations::kUniformity5PercentBase,
chrome_variations::kUniformity10PercentBase,
chrome_variations::kUniformity20PercentBase,
chrome_variations::kUniformity50PercentBase
};
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::kUniformitySessionRandomized5PercentBase, 20);
}
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::ChannelIDFieldTrial() {
chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
if (channel == chrome::VersionInfo::CHANNEL_CANARY) {
net::SSLConfigService::EnableChannelIDTrial();
} else if (channel == chrome::VersionInfo::CHANNEL_DEV &&
base::FieldTrialList::IsOneTimeRandomizationEnabled()) {
const base::FieldTrial::Probability kDivisor = 100;
// 10% probability of being in the enabled group.
const base::FieldTrial::Probability kEnableProbability = 10;
scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::FactoryGetFieldTrial(
"ChannelID", kDivisor, "disable", 2012, 8, 23, NULL);
trial->UseOneTimeRandomization();
int enable_group = trial->AppendGroup("enable", kEnableProbability);
if (trial->group() == enable_group)
net::SSLConfigService::EnableChannelIDTrial();
}
}
// 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.
#ifndef CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_H_
#define CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_H_
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/gtest_prod_util.h"
class ChromeBrowserFieldTrials {
public:
explicit ChromeBrowserFieldTrials(const CommandLine& command_line);
~ChromeBrowserFieldTrials();
// Add an invocation of your field trial init function to this method.
void SetupFieldTrials(bool proxy_policy_is_set);
private:
FRIEND_TEST_ALL_PREFIXES(BrowserMainTest,
WarmConnectionFieldTrial_WarmestSocket);
FRIEND_TEST_ALL_PREFIXES(BrowserMainTest, WarmConnectionFieldTrial_Random);
FRIEND_TEST_ALL_PREFIXES(BrowserMainTest, WarmConnectionFieldTrial_Invalid);
// A/B test for the maximum number of persistent connections per host.
void ConnectionFieldTrial();
// A/B test for determining a value for unused socket timeout.
void SocketTimeoutFieldTrial();
// A/B test for the maximum number of connections per proxy server.
void ProxyConnectionsFieldTrial();
// A/B test for spdy when --use-spdy not set.
void SpdyFieldTrial();
// A/B test for warmest socket vs. most recently used socket.
void WarmConnectionFieldTrial();
// A/B test for automatically establishing a backup TCP connection when a
// specified timeout value is reached.
void ConnectBackupJobsFieldTrial();
// Field trial to see what disabling DNS pre-resolution does to
// latency of page loads.
void PredictorFieldTrial();
// Field trial to see what effect installing defaults in the NTP apps pane
// has on retention and general apps/webstore usage.
void DefaultAppsFieldTrial();
// A field trial to see what effects launching Chrome automatically on
// computer startup has on retention and usage of Chrome.
void AutoLaunchChromeFieldTrial();
// A collection of one-time-randomized and session-randomized field trials
// intended to test the uniformity and correctness of the field trial control,
// bucketing and reporting systems.
void SetupUniformityFieldTrials();
// Disables the new tab field trial if not running in desktop mode.
void DisableNewTabFieldTrialIfNecesssary();
// Field trial for testing TLS channel id.
void ChannelIDFieldTrial();
const CommandLine& parsed_command_line_;
DISALLOW_COPY_AND_ASSIGN(ChromeBrowserFieldTrials);
};
#endif // CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_H_
...@@ -23,8 +23,6 @@ ...@@ -23,8 +23,6 @@
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "base/string_piece.h" #include "base/string_piece.h"
#include "base/string_split.h" #include "base/string_split.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/sys_string_conversions.h" #include "base/sys_string_conversions.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
#include "base/time.h" #include "base/time.h"
...@@ -32,14 +30,11 @@ ...@@ -32,14 +30,11 @@
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/about_flags.h" #include "chrome/browser/about_flags.h"
#include "chrome/browser/auto_launch_trial.h"
#include "chrome/browser/autocomplete/autocomplete_field_trial.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_impl.h" #include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/browser_shutdown.h" #include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/chrome_browser_main_extra_parts.h" #include "chrome/browser/chrome_browser_main_extra_parts.h"
#include "chrome/browser/defaults.h" #include "chrome/browser/defaults.h"
#include "chrome/browser/extensions/default_apps_trial.h"
#include "chrome/browser/extensions/extension_protocols.h" #include "chrome/browser/extensions/extension_protocols.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extensions_startup.h" #include "chrome/browser/extensions/extensions_startup.h"
...@@ -47,7 +42,6 @@ ...@@ -47,7 +42,6 @@
#include "chrome/browser/google/google_search_counter.h" #include "chrome/browser/google/google_search_counter.h"
#include "chrome/browser/google/google_util.h" #include "chrome/browser/google/google_util.h"
#include "chrome/browser/gpu_blacklist.h" #include "chrome/browser/gpu_blacklist.h"
#include "chrome/browser/gpu_util.h"
#include "chrome/browser/jankometer.h" #include "chrome/browser/jankometer.h"
#include "chrome/browser/language_usage_metrics.h" #include "chrome/browser/language_usage_metrics.h"
#include "chrome/browser/managed_mode.h" #include "chrome/browser/managed_mode.h"
...@@ -59,7 +53,6 @@ ...@@ -59,7 +53,6 @@
#include "chrome/browser/metrics/variations_service.h" #include "chrome/browser/metrics/variations_service.h"
#include "chrome/browser/nacl_host/nacl_process_host.h" #include "chrome/browser/nacl_host/nacl_process_host.h"
#include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/chrome_net_log.h"
#include "chrome/browser/net/predictor.h"
#include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/notifications/desktop_notification_service_factory.h" #include "chrome/browser/notifications/desktop_notification_service_factory.h"
#include "chrome/browser/page_cycler/page_cycler.h" #include "chrome/browser/page_cycler/page_cycler.h"
...@@ -67,7 +60,6 @@ ...@@ -67,7 +60,6 @@
#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_value_store.h" #include "chrome/browser/prefs/pref_value_store.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/prerender/prerender_field_trial.h"
#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h" #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
#include "chrome/browser/process_singleton.h" #include "chrome/browser/process_singleton.h"
...@@ -93,7 +85,6 @@ ...@@ -93,7 +85,6 @@
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_result_codes.h" #include "chrome/common/chrome_result_codes.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/env_vars.h" #include "chrome/common/env_vars.h"
#include "chrome/common/json_pref_store.h" #include "chrome/common/json_pref_store.h"
#include "chrome/common/jstemplate_builder.h" #include "chrome/common/jstemplate_builder.h"
...@@ -117,11 +108,7 @@ ...@@ -117,11 +108,7 @@
#include "net/base/sdch_manager.h" #include "net/base/sdch_manager.h"
#include "net/base/ssl_config_service.h" #include "net/base/ssl_config_service.h"
#include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_monster.h"
#include "net/http/http_basic_stream.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_stream_factory.h" #include "net/http/http_stream_factory.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_session_pool.h"
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
...@@ -149,7 +136,6 @@ ...@@ -149,7 +136,6 @@
#if defined(OS_WIN) #if defined(OS_WIN)
#include "base/environment.h" // For PreRead experiment. #include "base/environment.h" // For PreRead experiment.
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
#include "chrome/browser/browser_trial.h"
#include "chrome/browser/browser_util_win.h" #include "chrome/browser/browser_util_win.h"
#include "chrome/browser/chrome_browser_main_win.h" #include "chrome/browser/chrome_browser_main_win.h"
#include "chrome/browser/first_run/try_chrome_dialog_view.h" #include "chrome/browser/first_run/try_chrome_dialog_view.h"
...@@ -437,16 +423,6 @@ OSStatus KeychainCallback(SecKeychainEvent keychain_event, ...@@ -437,16 +423,6 @@ OSStatus KeychainCallback(SecKeychainEvent keychain_event,
} }
#endif #endif
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);
}
// This code is specific to the Windows-only PreReadExperiment field-trial. // This code is specific to the Windows-only PreReadExperiment field-trial.
void AddPreReadHistogramTime(const char* name, base::TimeDelta time) { void AddPreReadHistogramTime(const char* name, base::TimeDelta time) {
const base::TimeDelta kMin(base::TimeDelta::FromMilliseconds(1)); const base::TimeDelta kMin(base::TimeDelta::FromMilliseconds(1));
...@@ -498,53 +474,6 @@ bool HasImportSwitch(const CommandLine& command_line) { ...@@ -498,53 +474,6 @@ bool HasImportSwitch(const CommandLine& command_line) {
command_line.HasSwitch(switches::kImportFromFile)); command_line.HasSwitch(switches::kImportFromFile));
} }
// 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();
experiments_helper::AssociateGoogleVariationID(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);
experiments_helper::AssociateGoogleVariationID(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;
}
} // namespace } // namespace
namespace chrome_browser { namespace chrome_browser {
...@@ -568,6 +497,7 @@ ChromeBrowserMainParts::ChromeBrowserMainParts( ...@@ -568,6 +497,7 @@ ChromeBrowserMainParts::ChromeBrowserMainParts(
result_code_(content::RESULT_CODE_NORMAL_EXIT), result_code_(content::RESULT_CODE_NORMAL_EXIT),
startup_watcher_(new StartupTimeBomb()), startup_watcher_(new StartupTimeBomb()),
shutdown_watcher_(new ShutdownWatcherHelper()), shutdown_watcher_(new ShutdownWatcherHelper()),
browser_field_trials_(parameters.command_line),
record_search_engine_(false), record_search_engine_(false),
translate_manager_(NULL), translate_manager_(NULL),
profile_(NULL), profile_(NULL),
...@@ -631,8 +561,8 @@ void ChromeBrowserMainParts::SetupMetricsAndFieldTrials() { ...@@ -631,8 +561,8 @@ void ChromeBrowserMainParts::SetupMetricsAndFieldTrials() {
browser_process_->variations_service(); browser_process_->variations_service();
variations_service->CreateTrialsFromSeed(browser_process_->local_state()); variations_service->CreateTrialsFromSeed(browser_process_->local_state());
SetupFieldTrials(local_state_->IsManagedPreference( browser_field_trials_.SetupFieldTrials(
prefs::kMaxConnectionsPerProxy)); local_state_->IsManagedPreference(prefs::kMaxConnectionsPerProxy));
SetupPlatformFieldTrials(); SetupPlatformFieldTrials();
...@@ -643,507 +573,8 @@ void ChromeBrowserMainParts::SetupMetricsAndFieldTrials() { ...@@ -643,507 +573,8 @@ void ChromeBrowserMainParts::SetupMetricsAndFieldTrials() {
field_trial_synchronizer_ = new FieldTrialSynchronizer(); field_trial_synchronizer_ = new FieldTrialSynchronizer();
} }
// This is an A/B test for the maximum number of persistent connections per
// host. Currently Chrome, Firefox, and IE8 have this value set at 6. Safari
// uses 4, and Fasterfox (a plugin for Firefox that supposedly configures it to
// run faster) uses 8. We would like to see how much of an effect this value has
// on browsing. Too large a value might cause us to run into SYN flood detection
// mechanisms.
void ChromeBrowserMainParts::ConnectionFieldTrial() {
const base::FieldTrial::Probability kConnectDivisor = 100;
const base::FieldTrial::Probability kConnectProbability = 1; // 1% prob.
// This (6) is the current default value. Having this group declared here
// makes it straightforward to modify |kConnectProbability| such that the same
// probability value will be assigned to all the other groups, while
// preserving the remainder of the of probability space to the default value.
int connect_6 = -1;
// After June 30, 2011 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> connect_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"ConnCountImpact", kConnectDivisor, "conn_count_6", 2011, 6, 30,
&connect_6));
const int connect_5 = connect_trial->AppendGroup("conn_count_5",
kConnectProbability);
const int connect_7 = connect_trial->AppendGroup("conn_count_7",
kConnectProbability);
const int connect_8 = connect_trial->AppendGroup("conn_count_8",
kConnectProbability);
const int connect_9 = connect_trial->AppendGroup("conn_count_9",
kConnectProbability);
const int connect_trial_group = connect_trial->group();
int max_sockets = 0;
if (connect_trial_group == connect_5) {
max_sockets = 5;
} else if (connect_trial_group == connect_6) {
max_sockets = 6;
} else if (connect_trial_group == connect_7) {
max_sockets = 7;
} else if (connect_trial_group == connect_8) {
max_sockets = 8;
} else if (connect_trial_group == connect_9) {
max_sockets = 9;
} else {
NOTREACHED();
}
net::ClientSocketPoolManager::set_max_sockets_per_group(
net::HttpNetworkSession::NORMAL_SOCKET_POOL, max_sockets);
}
// A/B test for determining a value for unused socket timeout. Currently the
// timeout defaults to 10 seconds. Having this value set too low won't allow us
// to take advantage of idle sockets. Setting it to too high could possibly
// result in more ERR_CONNECTION_RESETs, since some servers will kill a socket
// before we time it out. Since these are "unused" sockets, we won't retry the
// connection and instead show an error to the user. So we need to be
// conservative here. We've seen that some servers will close the socket after
// as short as 10 seconds. See http://crbug.com/84313 for more details.
void ChromeBrowserMainParts::SocketTimeoutFieldTrial() {
const base::FieldTrial::Probability kIdleSocketTimeoutDivisor = 100;
// 1% probability for all experimental settings.
const base::FieldTrial::Probability kSocketTimeoutProbability = 1;
// After June 30, 2011 builds, it will always be in default group.
int socket_timeout_10 = -1;
scoped_refptr<base::FieldTrial> socket_timeout_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"IdleSktToImpact", kIdleSocketTimeoutDivisor, "idle_timeout_10",
2011, 6, 30, &socket_timeout_10));
const int socket_timeout_5 =
socket_timeout_trial->AppendGroup("idle_timeout_5",
kSocketTimeoutProbability);
const int socket_timeout_20 =
socket_timeout_trial->AppendGroup("idle_timeout_20",
kSocketTimeoutProbability);
const int idle_to_trial_group = socket_timeout_trial->group();
if (idle_to_trial_group == socket_timeout_5) {
net::ClientSocketPool::set_unused_idle_socket_timeout(
base::TimeDelta::FromSeconds(5));
} else if (idle_to_trial_group == socket_timeout_10) {
net::ClientSocketPool::set_unused_idle_socket_timeout(
base::TimeDelta::FromSeconds(10));
} else if (idle_to_trial_group == socket_timeout_20) {
net::ClientSocketPool::set_unused_idle_socket_timeout(
base::TimeDelta::FromSeconds(20));
} else {
NOTREACHED();
}
}
void ChromeBrowserMainParts::ProxyConnectionsFieldTrial() {
const base::FieldTrial::Probability kProxyConnectionsDivisor = 100;
// 25% probability
const base::FieldTrial::Probability kProxyConnectionProbability = 1;
// This (32 connections per proxy server) is the current default value.
// Declaring it here allows us to easily re-assign the probability space while
// maintaining that the default group always has the remainder of the "share",
// which allows for cleaner and quicker changes down the line if needed.
int proxy_connections_32 = -1;
// After June 30, 2011 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> proxy_connection_trial(
base::FieldTrialList::FactoryGetFieldTrial(
"ProxyConnectionImpact", kProxyConnectionsDivisor,
"proxy_connections_32", 2011, 6, 30, &proxy_connections_32));
// The number of max sockets per group cannot be greater than the max number
// of sockets per proxy server. We tried using 8, and it can easily
// lead to total browser stalls.
const int proxy_connections_16 =
proxy_connection_trial->AppendGroup("proxy_connections_16",
kProxyConnectionProbability);
const int proxy_connections_64 =
proxy_connection_trial->AppendGroup("proxy_connections_64",
kProxyConnectionProbability);
const int proxy_connections_trial_group = proxy_connection_trial->group();
int max_sockets = 0;
if (proxy_connections_trial_group == proxy_connections_16) {
max_sockets = 16;
} else if (proxy_connections_trial_group == proxy_connections_32) {
max_sockets = 32;
} else if (proxy_connections_trial_group == proxy_connections_64) {
max_sockets = 64;
} else {
NOTREACHED();
}
net::ClientSocketPoolManager::set_max_sockets_per_proxy_server(
net::HttpNetworkSession::NORMAL_SOCKET_POOL, max_sockets);
}
// 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 ChromeBrowserMainParts::SpdyFieldTrial() {
bool use_field_trial = true;
if (parsed_command_line().HasSwitch(switches::kUseSpdy)) {
std::string spdy_mode =
parsed_command_line().GetSwitchValueASCII(switches::kUseSpdy);
net::HttpNetworkLayer::EnableSpdy(spdy_mode);
use_field_trial = false;
}
if (parsed_command_line().HasSwitch(switches::kEnableSpdy3)) {
net::HttpStreamFactory::EnableNpnSpdy3();
use_field_trial = false;
} else if (parsed_command_line().HasSwitch(switches::kEnableNpn)) {
net::HttpStreamFactory::EnableNpnSpdy();
use_field_trial = false;
} else if (parsed_command_line().HasSwitch(switches::kEnableNpnHttpOnly)) {
net::HttpStreamFactory::EnableNpnHttpOnly();
use_field_trial = false;
}
if (use_field_trial) {
const base::FieldTrial::Probability kSpdyDivisor = 100;
// Enable SPDY/3 for 95% of the users, HTTP (no SPDY) for 1% of the users
// and SPDY/2 for 4% of the users.
base::FieldTrial::Probability npnhttp_probability = 1;
base::FieldTrial::Probability spdy3_probability = 95;
#if defined(OS_CHROMEOS)
// Always enable SPDY (spdy/2 or spdy/3) on Chrome OS
npnhttp_probability = 0;
#endif // !defined(OS_CHROMEOS)
// NPN with spdy support is the default.
int npn_spdy_grp = -1;
// After June 30, 2013 builds, it will always be in default group.
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"SpdyImpact", kSpdyDivisor, "npn_with_spdy", 2013, 6, 30,
&npn_spdy_grp));
// NPN with only http support, no spdy.
int npn_http_grp = trial->AppendGroup("npn_with_http", npnhttp_probability);
// NPN with http/1.1, spdy/2, and spdy/3 support.
int spdy3_grp = trial->AppendGroup("spdy3", spdy3_probability);
int trial_grp = trial->group();
if (trial_grp == npn_spdy_grp) {
net::HttpStreamFactory::EnableNpnSpdy();
} else if (trial_grp == npn_http_grp) {
net::HttpStreamFactory::EnableNpnHttpOnly();
} else if (trial_grp == spdy3_grp) {
net::HttpStreamFactory::EnableNpnSpdy3();
} else {
NOTREACHED();
}
}
// 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 (parsed_command_line().HasSwitch(switches::kMaxSpdyConcurrentStreams)) {
int value = 0;
base::StringToInt(parsed_command_line().GetSwitchValueASCII(
switches::kMaxSpdyConcurrentStreams),
&value);
if (value > 0)
net::SpdySession::set_max_concurrent_streams(value);
}
}
// If --socket-reuse-policy is not specified, run an A/B test for choosing the
// warmest socket.
void ChromeBrowserMainParts::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));
}
// If neither --enable-connect-backup-jobs or --disable-connect-backup-jobs is
// specified, run an A/B test for automatically establishing backup TCP
// connections when a certain timeout value is exceeded.
void ChromeBrowserMainParts::ConnectBackupJobsFieldTrial() {
if (parsed_command_line().HasSwitch(switches::kEnableConnectBackupJobs)) {
net::internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
true);
} else if (parsed_command_line().HasSwitch(
switches::kDisableConnectBackupJobs)) {
net::internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
false);
} else {
const base::FieldTrial::Probability kConnectBackupJobsDivisor = 100;
// 1% probability.
const base::FieldTrial::Probability kConnectBackupJobsProbability = 1;
// After June 30, 2011 builds, it will always be in default group.
int connect_backup_jobs_enabled = -1;
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial("ConnnectBackupJobs",
kConnectBackupJobsDivisor, "ConnectBackupJobsEnabled",
2011, 6, 30, &connect_backup_jobs_enabled));
trial->AppendGroup("ConnectBackupJobsDisabled",
kConnectBackupJobsProbability);
const int trial_group = trial->group();
net::internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
trial_group == connect_backup_jobs_enabled);
}
}
void ChromeBrowserMainParts::PredictorFieldTrial() {
const base::FieldTrial::Probability kDivisor = 1000;
// For each option (i.e., non-default), we have a fixed probability.
// 0.1% probability.
const base::FieldTrial::Probability kProbabilityPerGroup = 1;
// After June 30, 2011 builds, it will always be in default group
// (default_enabled_prefetch).
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"DnsImpact", kDivisor, "default_enabled_prefetch", 2011, 10, 30,
NULL));
// First option is to disable prefetching completely.
int disabled_prefetch = trial->AppendGroup("disabled_prefetch",
kProbabilityPerGroup);
// We're running two experiments at the same time. The first set of trials
// modulates the delay-time until we declare a congestion event (and purge
// our queue). The second modulates the number of concurrent resolutions
// we do at any time. Users are in exactly one trial (or the default) during
// any one run, and hence only one experiment at a time.
// Experiment 1:
// Set congestion detection at 250, 500, or 750ms, rather than the 1 second
// default.
int max_250ms_prefetch = trial->AppendGroup("max_250ms_queue_prefetch",
kProbabilityPerGroup);
int max_500ms_prefetch = trial->AppendGroup("max_500ms_queue_prefetch",
kProbabilityPerGroup);
int max_750ms_prefetch = trial->AppendGroup("max_750ms_queue_prefetch",
kProbabilityPerGroup);
// Set congestion detection at 2 seconds instead of the 1 second default.
int max_2s_prefetch = trial->AppendGroup("max_2s_queue_prefetch",
kProbabilityPerGroup);
// Experiment 2:
// Set max simultaneous resoultions to 2, 4, or 6, and scale the congestion
// limit proportionally (so we don't impact average probability of asserting
// congesion very much).
int max_2_concurrent_prefetch = trial->AppendGroup(
"max_2 concurrent_prefetch", kProbabilityPerGroup);
int max_4_concurrent_prefetch = trial->AppendGroup(
"max_4 concurrent_prefetch", kProbabilityPerGroup);
int max_6_concurrent_prefetch = trial->AppendGroup(
"max_6 concurrent_prefetch", kProbabilityPerGroup);
if (trial->group() != disabled_prefetch) {
// Initialize the DNS prefetch system.
size_t max_parallel_resolves =
chrome_browser_net::Predictor::kMaxSpeculativeParallelResolves;
int max_queueing_delay_ms =
chrome_browser_net::Predictor::kMaxSpeculativeResolveQueueDelayMs;
if (trial->group() == max_2_concurrent_prefetch)
max_parallel_resolves = 2;
else if (trial->group() == max_4_concurrent_prefetch)
max_parallel_resolves = 4;
else if (trial->group() == max_6_concurrent_prefetch)
max_parallel_resolves = 6;
chrome_browser_net::Predictor::set_max_parallel_resolves(
max_parallel_resolves);
if (trial->group() == max_250ms_prefetch) {
max_queueing_delay_ms =
(250 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
} else if (trial->group() == max_500ms_prefetch) {
max_queueing_delay_ms =
(500 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
} else if (trial->group() == max_750ms_prefetch) {
max_queueing_delay_ms =
(750 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
} else if (trial->group() == max_2s_prefetch) {
max_queueing_delay_ms =
(2000 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) /
max_parallel_resolves;
}
chrome_browser_net::Predictor::set_max_queueing_delay(
max_queueing_delay_ms);
}
}
void ChromeBrowserMainParts::DefaultAppsFieldTrial() {
std::string brand;
google_util::GetBrand(&brand);
// Create a 100% field trial based on the brand code.
if (LowerCaseEqualsASCII(brand, "ecdb")) {
base::FieldTrialList::CreateFieldTrial(kDefaultAppsTrialName,
kDefaultAppsTrialNoAppsGroup);
} else if (LowerCaseEqualsASCII(brand, "ecda")) {
base::FieldTrialList::CreateFieldTrial(kDefaultAppsTrialName,
kDefaultAppsTrialWithAppsGroup);
}
}
void ChromeBrowserMainParts::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 ChromeBrowserMainParts::SetupUniformityFieldTrials() {
// 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::kUniformity1PercentBase,
chrome_variations::kUniformity5PercentBase,
chrome_variations::kUniformity10PercentBase,
chrome_variations::kUniformity20PercentBase,
chrome_variations::kUniformity50PercentBase
};
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::kUniformitySessionRandomized5PercentBase, 20);
}
void ChromeBrowserMainParts::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 ChromeBrowserMainParts::ChannelIDFieldTrial() {
chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
if (channel == chrome::VersionInfo::CHANNEL_CANARY) {
net::SSLConfigService::EnableChannelIDTrial();
} else if (channel == chrome::VersionInfo::CHANNEL_DEV &&
base::FieldTrialList::IsOneTimeRandomizationEnabled()) {
const base::FieldTrial::Probability kDivisor = 100;
// 10% probability of being in the enabled group.
const base::FieldTrial::Probability kEnableProbability = 10;
scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::FactoryGetFieldTrial(
"ChannelID", kDivisor, "disable", 2012, 8, 23, NULL);
trial->UseOneTimeRandomization();
int enable_group = trial->AppendGroup("enable", kEnableProbability);
if (trial->group() == enable_group)
net::SSLConfigService::EnableChannelIDTrial();
}
}
// ChromeBrowserMainParts: |SetupMetricsAndFieldTrials()| related -------------- // ChromeBrowserMainParts: |SetupMetricsAndFieldTrials()| related --------------
void ChromeBrowserMainParts::SetupFieldTrials(bool proxy_policy_is_set) {
// Note: make sure to call ConnectionFieldTrial() before
// ProxyConnectionsFieldTrial().
ConnectionFieldTrial();
SocketTimeoutFieldTrial();
// If a policy is defining the number of active connections this field test
// shoud not be performed.
if (!proxy_policy_is_set)
ProxyConnectionsFieldTrial();
prerender::ConfigurePrefetchAndPrerender(parsed_command_line());
SpdyFieldTrial();
ConnectBackupJobsFieldTrial();
WarmConnectionFieldTrial();
PredictorFieldTrial();
DefaultAppsFieldTrial();
AutoLaunchChromeFieldTrial();
gpu_util::InitializeForceCompositingModeFieldTrial();
SetupUniformityFieldTrials();
AutocompleteFieldTrial::Activate();
DisableNewTabFieldTrialIfNecesssary();
ChannelIDFieldTrial();
}
void ChromeBrowserMainParts::StartMetricsRecording() { void ChromeBrowserMainParts::StartMetricsRecording() {
MetricsService* metrics = g_browser_process->metrics_service(); MetricsService* metrics = g_browser_process->metrics_service();
if (parsed_command_line_.HasSwitch(switches::kMetricsRecordingOnly) || if (parsed_command_line_.HasSwitch(switches::kMetricsRecordingOnly) ||
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/memory/scoped_vector.h" #include "base/memory/scoped_vector.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/tracked_objects.h" #include "base/tracked_objects.h"
#include "chrome/browser/chrome_browser_field_trials.h"
#include "chrome/browser/first_run/first_run.h" #include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/process_singleton.h" #include "chrome/browser/process_singleton.h"
#include "chrome/browser/task_profiler/auto_tracking.h" #include "chrome/browser/task_profiler/auto_tracking.h"
...@@ -97,49 +98,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ...@@ -97,49 +98,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
const PrefService* local_state() const { return local_state_; } const PrefService* local_state() const { return local_state_; }
private: private:
// Methods for |EarlyInitialization()| ---------------------------------------
// A/B test for the maximum number of persistent connections per host.
void ConnectionFieldTrial();
// A/B test for determining a value for unused socket timeout.
void SocketTimeoutFieldTrial();
// A/B test for the maximum number of connections per proxy server.
void ProxyConnectionsFieldTrial();
// A/B test for spdy when --use-spdy not set.
void SpdyFieldTrial();
// A/B test for warmest socket vs. most recently used socket.
void WarmConnectionFieldTrial();
// A/B test for automatically establishing a backup TCP connection when a
// specified timeout value is reached.
void ConnectBackupJobsFieldTrial();
// Field trial to see what disabling DNS pre-resolution does to
// latency of page loads.
void PredictorFieldTrial();
// Field trial to see what effect installing defaults in the NTP apps pane
// has on retention and general apps/webstore usage.
void DefaultAppsFieldTrial();
// A field trial to see what effects launching Chrome automatically on
// computer startup has on retention and usage of Chrome.
void AutoLaunchChromeFieldTrial();
// A collection of one-time-randomized and session-randomized field trials
// intended to test the uniformity and correctness of the field trial control,
// bucketing and reporting systems.
void SetupUniformityFieldTrials();
// Disables the new tab field trial if not running in desktop mode.
void DisableNewTabFieldTrialIfNecesssary();
// Field trial for testing TLS channel id.
void ChannelIDFieldTrial();
// Methods for |SetupMetricsAndFieldTrials()| -------------------------------- // Methods for |SetupMetricsAndFieldTrials()| --------------------------------
...@@ -148,9 +106,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ...@@ -148,9 +106,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// switches. // switches.
void SetupMetricsAndFieldTrials(); void SetupMetricsAndFieldTrials();
// Add an invocation of your field trial init function to this method.
void SetupFieldTrials(bool proxy_policy_is_set);
// Starts recording of metrics. This can only be called after we have a file // Starts recording of metrics. This can only be called after we have a file
// thread. // thread.
void StartMetricsRecording(); void StartMetricsRecording();
...@@ -186,6 +141,8 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ...@@ -186,6 +141,8 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// SetupMetricsAndFieldTrials is called. // SetupMetricsAndFieldTrials is called.
scoped_ptr<base::FieldTrialList> field_trial_list_; scoped_ptr<base::FieldTrialList> field_trial_list_;
ChromeBrowserFieldTrials browser_field_trials_;
// Vector of additional ChromeBrowserMainExtraParts. // Vector of additional ChromeBrowserMainExtraParts.
// Parts are deleted in the inverse order they are added. // Parts are deleted in the inverse order they are added.
std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_; std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_;
......
...@@ -38,7 +38,7 @@ TEST_F(BrowserMainTest, WarmConnectionFieldTrial_WarmestSocket) { ...@@ -38,7 +38,7 @@ TEST_F(BrowserMainTest, WarmConnectionFieldTrial_WarmestSocket) {
ChromeBrowserMainParts* cbw = static_cast<ChromeBrowserMainParts*>(bw.get()); ChromeBrowserMainParts* cbw = static_cast<ChromeBrowserMainParts*>(bw.get());
EXPECT_TRUE(cbw); EXPECT_TRUE(cbw);
if (cbw) { if (cbw) {
cbw->WarmConnectionFieldTrial(); cbw->browser_field_trials_.WarmConnectionFieldTrial();
EXPECT_EQ(0, net::GetSocketReusePolicy()); EXPECT_EQ(0, net::GetSocketReusePolicy());
} }
} }
...@@ -53,7 +53,7 @@ TEST_F(BrowserMainTest, WarmConnectionFieldTrial_Random) { ...@@ -53,7 +53,7 @@ TEST_F(BrowserMainTest, WarmConnectionFieldTrial_Random) {
if (cbw) { if (cbw) {
const int kNumRuns = 1000; const int kNumRuns = 1000;
for (int i = 0; i < kNumRuns; i++) { for (int i = 0; i < kNumRuns; i++) {
cbw->WarmConnectionFieldTrial(); cbw->browser_field_trials_.WarmConnectionFieldTrial();
int val = net::GetSocketReusePolicy(); int val = net::GetSocketReusePolicy();
EXPECT_LE(val, 2); EXPECT_LE(val, 2);
EXPECT_GE(val, 0); EXPECT_GE(val, 0);
...@@ -80,10 +80,10 @@ TEST_F(BrowserMainTest, WarmConnectionFieldTrial_Invalid) { ...@@ -80,10 +80,10 @@ TEST_F(BrowserMainTest, WarmConnectionFieldTrial_Invalid) {
EXPECT_TRUE(cbw); EXPECT_TRUE(cbw);
if (cbw) { if (cbw) {
#if defined(NDEBUG) && defined(DCHECK_ALWAYS_ON) #if defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
EXPECT_DEATH(cbw->WarmConnectionFieldTrial(), EXPECT_DEATH(cbw->browser_field_trials_.WarmConnectionFieldTrial(),
"Not a valid socket reuse policy group"); "Not a valid socket reuse policy group");
#else #else
EXPECT_DEBUG_DEATH(cbw->WarmConnectionFieldTrial(), EXPECT_DEBUG_DEATH(cbw->browser_field_trials_.WarmConnectionFieldTrial(),
"Not a valid socket reuse policy group"); "Not a valid socket reuse policy group");
#endif #endif
} }
......
...@@ -345,8 +345,6 @@ ...@@ -345,8 +345,6 @@
'browser/browser_process_impl.h', 'browser/browser_process_impl.h',
'browser/browser_shutdown.cc', 'browser/browser_shutdown.cc',
'browser/browser_shutdown.h', 'browser/browser_shutdown.h',
'browser/browser_trial.cc',
'browser/browser_trial.h',
'browser/browser_util_win.cc', 'browser/browser_util_win.cc',
'browser/browser_util_win.h', 'browser/browser_util_win.h',
'browser/browsing_data/browsing_data_appcache_helper.cc', 'browser/browsing_data/browsing_data_appcache_helper.cc',
...@@ -399,6 +397,8 @@ ...@@ -399,6 +397,8 @@
'browser/chrome_benchmarking_message_filter.h', 'browser/chrome_benchmarking_message_filter.h',
'browser/chrome_browser_application_mac.h', 'browser/chrome_browser_application_mac.h',
'browser/chrome_browser_application_mac.mm', 'browser/chrome_browser_application_mac.mm',
'browser/chrome_browser_field_trials.cc',
'browser/chrome_browser_field_trials.h',
'browser/chrome_browser_main.cc', 'browser/chrome_browser_main.cc',
'browser/chrome_browser_main.h', 'browser/chrome_browser_main.h',
'browser/chrome_browser_main_extra_parts.h', 'browser/chrome_browser_main_extra_parts.h',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment