summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-02 16:35:19 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-02 16:35:19 +0000
commit8a6ff28d37e1010ef77251f232ba5008b28ddc97 (patch)
treedf509f80047be1cc3dc08971361c071e7ff47b47
parentc25108500d8b37f8e8fe194070e99eb8550cc9a5 (diff)
downloadchromium_src-8a6ff28d37e1010ef77251f232ba5008b28ddc97.zip
chromium_src-8a6ff28d37e1010ef77251f232ba5008b28ddc97.tar.gz
chromium_src-8a6ff28d37e1010ef77251f232ba5008b28ddc97.tar.bz2
Adds ability for about:flags to use a select for choosing one of many
values, and wires this up for instant. BUG=none TEST=none Review URL: http://codereview.chromium.org/5451003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68022 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd17
-rw-r--r--chrome/browser/about_flags.cc235
-rw-r--r--chrome/browser/about_flags.h40
-rw-r--r--chrome/browser/about_flags_unittest.cc91
-rw-r--r--chrome/browser/instant/instant_controller.cc6
-rw-r--r--chrome/browser/resources/flags.html30
-rw-r--r--chrome/common/chrome_switches.cc6
-rw-r--r--chrome/common/chrome_switches.h1
8 files changed, 362 insertions, 64 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3c237d1..31bb302 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4065,11 +4065,20 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_PREDICTIVE_INSTANT_DESCRIPTION" desc="Description of the predictive 'Instant' lab.">
Makes the address bar load urls as you type. Search results are shown for the best match.
</message>
- <message name="IDS_FLAGS_VERBATIM_INSTANT_NAME" desc="Description of the verbatim 'Instant' lab.">
- Verbatim Instant
+ <message name="IDS_FLAGS_INSTANT_TYPE_NAME" desc="Description of the 'Instant Type' lab.">
+ Instant Type
</message>
- <message name="IDS_FLAGS_VERBATIM_INSTANT_DESCRIPTION" desc="Description of the verbatim 'Instant' lab.">
- Makes the address bar load urls as you type. Search results are shown exactly as you typed them.
+ <message name="IDS_FLAGS_INSTANT_TYPE_DESCRIPTION" desc="Description of the 'Instant Type' lab.">
+ Configures the behavior of instant.
+ </message>
+ <message name="IDS_FLAGS_INSTANT_TYPE_PREDICTIVE" desc="Name for 'predictive' instant results">
+ Predictive
+ </message>
+ <message name="IDS_FLAGS_INSTANT_TYPE_PREDICTIVE_NO_AUTO_COMPLETE" desc="Name for 'predictive no auto-complete' instant results">
+ Predictive no auto-complete
+ </message>
+ <message name="IDS_FLAGS_INSTANT_TYPE_VERBATIM" desc="Name for 'verbatim' instant results">
+ Verbatim
</message>
<message name="IDS_FLAGS_REMOTING_NAME" desc="Description of the 'Remoting' lab.">
Remoting
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 38ea7f7..fec86c4 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -12,6 +12,7 @@
#include "app/l10n_util.h"
#include "base/command_line.h"
#include "base/singleton.h"
+#include "base/string_number_conversions.h"
#include "base/values.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/prefs/pref_service.h"
@@ -21,6 +22,12 @@
namespace about_flags {
+// Macros to simplify specifying the type.
+#define SINGLE_VALUE_TYPE(command_line) Experiment::SINGLE_VALUE, \
+ command_line, NULL, 0
+#define MULTI_VALUE_TYPE(choices) Experiment::MULTI_VALUE, "", choices, \
+ arraysize(choices)
+
namespace {
const unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS;
@@ -31,6 +38,15 @@ const char kMediaPlayerExperimentName[] = "media-player";
const char kAdvancedFileSystemExperimentName[] = "advanced-file-system";
const char kVerticalTabsExperimentName[] = "vertical-tabs";
+// If adding a new choice, add it to the end of the list.
+const Experiment::Choice kInstantChoices[] = {
+ { IDS_FLAGS_INSTANT_TYPE_VERBATIM, switches::kEnableVerbatimInstant },
+ { IDS_FLAGS_INSTANT_TYPE_PREDICTIVE, switches::kEnablePredictiveInstant },
+ { IDS_FLAGS_INSTANT_TYPE_PREDICTIVE_NO_AUTO_COMPLETE,
+ switches::kEnablePredictiveNoAutoCompleteInstant
+ },
+};
+
// RECORDING USER METRICS FOR FLAGS:
// -----------------------------------------------------------------------------
// The first line of the experiment is the internal name. If you'd like to
@@ -51,6 +67,16 @@ const char kVerticalTabsExperimentName[] = "vertical-tabs";
// TODO(rsesek): See if there's a way to count per-user, rather than
// per-startup.
+// To add a new experiment add to the end of kExperiments. There are two
+// distinct types of experiments:
+// . SINGLE_VALUE: experiment is either on or off. Use the SINGLE_VALUE_TYPE
+// macro for this type supplying the command line to the macro.
+// . MULTI_VALUE: if enabled the command line of the selected choice is enabled.
+// To specify this type of experiment use the macro MULTI_VALUE_TYPE supplying
+// it the array of choices.
+// See the documentation of Experiment for details on the fields.
+//
+// When adding a new choice, add it to the end of the list.
const Experiment kExperiments[] = {
{
"expose-for-tabs", // FLAGS:RECORD_UMA
@@ -59,9 +85,9 @@ const Experiment kExperiments[] = {
kOsMac,
#if defined(OS_MACOSX)
// The switch exists only on OS X.
- switches::kEnableExposeForTabs
+ SINGLE_VALUE_TYPE(switches::kEnableExposeForTabs)
#else
- ""
+ SINGLE_VALUE_TYPE("")
#endif
},
{
@@ -71,9 +97,9 @@ const Experiment kExperiments[] = {
kOsCrOS,
#if defined(OS_CHROMEOS)
// The switch exists only on Chrome OS.
- switches::kEnableMediaPlayer
+ SINGLE_VALUE_TYPE(switches::kEnableMediaPlayer)
#else
- ""
+ SINGLE_VALUE_TYPE("")
#endif
},
{
@@ -83,9 +109,9 @@ const Experiment kExperiments[] = {
kOsCrOS,
#if defined(OS_CHROMEOS)
// The switch exists only on Chrome OS.
- switches::kEnableAdvancedFileSystem
+ SINGLE_VALUE_TYPE(switches::kEnableAdvancedFileSystem)
#else
- ""
+ SINGLE_VALUE_TYPE("")
#endif
},
{
@@ -93,14 +119,14 @@ const Experiment kExperiments[] = {
IDS_FLAGS_SIDE_TABS_NAME,
IDS_FLAGS_SIDE_TABS_DESCRIPTION,
kOsWin | kOsCrOS,
- switches::kEnableVerticalTabs
+ SINGLE_VALUE_TYPE(switches::kEnableVerticalTabs)
},
{
"tabbed-options", // FLAGS:RECORD_UMA
IDS_FLAGS_TABBED_OPTIONS_NAME,
IDS_FLAGS_TABBED_OPTIONS_DESCRIPTION,
kOsWin | kOsLinux | kOsMac, // Enabled by default on CrOS.
- switches::kEnableTabbedOptions
+ SINGLE_VALUE_TYPE(switches::kEnableTabbedOptions)
},
{
"remoting", // FLAGS:RECORD_UMA
@@ -116,35 +142,35 @@ const Experiment kExperiments[] = {
0,
#endif
kOsWin | kOsLinux | kOsCrOS,
- switches::kEnableRemoting
+ SINGLE_VALUE_TYPE(switches::kEnableRemoting)
},
{
"disable-outdated-plugins", // FLAGS:RECORD_UMA
IDS_FLAGS_DISABLE_OUTDATED_PLUGINS_NAME,
IDS_FLAGS_DISABLE_OUTDATED_PLUGINS_DESCRIPTION,
kOsAll,
- switches::kDisableOutdatedPlugins
+ SINGLE_VALUE_TYPE(switches::kDisableOutdatedPlugins)
},
{
"xss-auditor", // FLAGS:RECORD_UMA
IDS_FLAGS_XSS_AUDITOR_NAME,
IDS_FLAGS_XSS_AUDITOR_DESCRIPTION,
kOsAll,
- switches::kEnableXSSAuditor
+ SINGLE_VALUE_TYPE(switches::kEnableXSSAuditor)
},
{
"background-webapps", // FLAGS:RECORD_UMA
IDS_FLAGS_BACKGROUND_WEBAPPS_NAME,
IDS_FLAGS_BACKGROUND_WEBAPPS_DESCRIPTION,
kOsMac | kOsLinux | kOsCrOS, // Enabled by default on windows
- switches::kEnableBackgroundMode
+ SINGLE_VALUE_TYPE(switches::kEnableBackgroundMode)
},
{
"conflicting-modules-check", // FLAGS:RECORD_UMA
IDS_FLAGS_CONFLICTS_CHECK_NAME,
IDS_FLAGS_CONFLICTS_CHECK_DESCRIPTION,
kOsWin,
- switches::kConflictingModulesCheck
+ SINGLE_VALUE_TYPE(switches::kConflictingModulesCheck)
},
{
"cloud-print-proxy", // FLAGS:RECORD_UMA
@@ -159,21 +185,21 @@ const Experiment kExperiments[] = {
// plug-in could be supplied, we'll keep the lab enabled.
kOsWin,
#endif
- switches::kEnableCloudPrintProxy
+ SINGLE_VALUE_TYPE(switches::kEnableCloudPrintProxy)
},
{
"crxless-web-apps",
IDS_FLAGS_CRXLESS_WEB_APPS_NAME,
IDS_FLAGS_CRXLESS_WEB_APPS_DESCRIPTION,
kOsAll,
- switches::kEnableCrxlessWebApps
+ SINGLE_VALUE_TYPE(switches::kEnableCrxlessWebApps)
},
{
"match-preview", // FLAGS:RECORD_UMA
IDS_FLAGS_PREDICTIVE_INSTANT_NAME,
IDS_FLAGS_PREDICTIVE_INSTANT_DESCRIPTION,
kOsMac,
- switches::kEnablePredictiveInstant
+ SINGLE_VALUE_TYPE(switches::kEnablePredictiveInstant)
},
// FIXME(scheib): Add Flags entry for accelerated Compositing,
// or pull it and the strings in generated_resources.grd by Dec 2010
@@ -182,14 +208,14 @@ const Experiment kExperiments[] = {
// IDS_FLAGS_ACCELERATED_COMPOSITING_NAME,
// IDS_FLAGS_ACCELERATED_COMPOSITING_DESCRIPTION,
// kOsAll,
- // switches::kDisableAcceleratedCompositing
+ // SINGLE_VALUE_TYPE(switches::kDisableAcceleratedCompositing)
// },
{
"gpu-canvas-2d", // FLAGS:RECORD_UMA
IDS_FLAGS_ACCELERATED_CANVAS_2D_NAME,
IDS_FLAGS_ACCELERATED_CANVAS_2D_DESCRIPTION,
kOsWin | kOsLinux | kOsCrOS,
- switches::kEnableAccelerated2dCanvas
+ SINGLE_VALUE_TYPE(switches::kEnableAccelerated2dCanvas)
},
// FIXME(scheib): Add Flags entry for WebGL,
// or pull it and the strings in generated_resources.grd by Dec 2010
@@ -198,71 +224,78 @@ const Experiment kExperiments[] = {
// IDS_FLAGS_WEBGL_NAME,
// IDS_FLAGS_WEBGL_DESCRIPTION,
// kOsAll,
- // switches::kDisableExperimentalWebGL
+ // SINGLE_VALUE_TYPE(switches::kDisableExperimentalWebGL)
// }
{
"print-preview", // FLAGS:RECORD_UMA
IDS_FLAGS_PRINT_PREVIEW_NAME,
IDS_FLAGS_PRINT_PREVIEW_DESCRIPTION,
kOsAll,
- switches::kEnablePrintPreview
+ SINGLE_VALUE_TYPE(switches::kEnablePrintPreview)
},
{
"enable-nacl", // FLAGS:RECORD_UMA
IDS_FLAGS_ENABLE_NACL_NAME,
IDS_FLAGS_ENABLE_NACL_DESCRIPTION,
kOsAll,
- switches::kEnableNaCl
+ SINGLE_VALUE_TYPE(switches::kEnableNaCl)
},
{
"dns-server", // FLAGS:RECORD_UMA
IDS_FLAGS_DNS_SERVER_NAME,
IDS_FLAGS_DNS_SERVER_DESCRIPTION,
kOsLinux,
- switches::kDnsServer
+ SINGLE_VALUE_TYPE(switches::kDnsServer)
},
{
"page-prerender", // FLAGS:RECORD_UMA
IDS_FLAGS_PAGE_PRERENDER_NAME,
IDS_FLAGS_PAGE_PRERENDER_DESCRIPTION,
kOsAll,
- switches::kEnablePagePrerender
+ SINGLE_VALUE_TYPE(switches::kEnablePagePrerender)
},
{
"confirm-to-quit", // FLAGS:RECORD_UMA
IDS_FLAGS_CONFIRM_TO_QUIT_NAME,
IDS_FLAGS_CONFIRM_TO_QUIT_DESCRIPTION,
kOsMac,
- switches::kEnableConfirmToQuit
+ SINGLE_VALUE_TYPE(switches::kEnableConfirmToQuit)
},
{
"extension-apis", // FLAGS:RECORD_UMA
IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_NAME,
IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION,
kOsAll,
- switches::kEnableExperimentalExtensionApis
+ SINGLE_VALUE_TYPE(switches::kEnableExperimentalExtensionApis)
},
{
"click-to-play", // FLAGS:RECORD_UMA
IDS_FLAGS_CLICK_TO_PLAY_NAME,
IDS_FLAGS_CLICK_TO_PLAY_DESCRIPTION,
kOsAll,
- switches::kEnableClickToPlay
+ SINGLE_VALUE_TYPE(switches::kEnableClickToPlay)
},
{
"disable-hyperlink-auditing",
IDS_FLAGS_DISABLE_HYPERLINK_AUDITING_NAME,
IDS_FLAGS_DISABLE_HYPERLINK_AUDITING_DESCRIPTION,
kOsAll,
- switches::kNoPings
+ SINGLE_VALUE_TYPE(switches::kNoPings)
},
{
"experimental-location-features", // FLAGS:RECORD_UMA
IDS_FLAGS_EXPERIMENTAL_LOCATION_FEATURES_NAME,
IDS_FLAGS_EXPERIMENTAL_LOCATION_FEATURES_DESCRIPTION,
kOsMac | kOsWin | kOsLinux, // Currently does nothing on CrOS.
- switches::kExperimentalLocationFeatures
- }
+ SINGLE_VALUE_TYPE(switches::kExperimentalLocationFeatures)
+ },
+ {
+ "instant-type", // FLAGS:RECORD_UMA
+ IDS_FLAGS_INSTANT_TYPE_NAME,
+ IDS_FLAGS_INSTANT_TYPE_DESCRIPTION,
+ kOsWin,
+ MULTI_VALUE_TYPE(kInstantChoices)
+ },
};
const Experiment* experiments = kExperiments;
@@ -347,13 +380,32 @@ void SetEnabledFlags(
}
}
+// Returns the name used in prefs for the choice at the specified index.
+std::string NameForChoice(const Experiment& e, int index) {
+ DCHECK(e.type == Experiment::MULTI_VALUE);
+ DCHECK(index < e.num_choices);
+ return std::string(e.internal_name) + about_flags::testing::kMultiSeparator +
+ base::IntToString(index);
+}
+
+// Adds the internal names for the specified experiment to |names|.
+void AddInternalName(const Experiment& e, std::set<std::string>* names) {
+ if (e.type == Experiment::SINGLE_VALUE) {
+ names->insert(e.internal_name);
+ } else {
+ DCHECK(e.type == Experiment::MULTI_VALUE);
+ for (int i = 0; i < e.num_choices; ++i)
+ names->insert(NameForChoice(e, i));
+ }
+}
+
// Removes all experiments from prefs::kEnabledLabsExperiments that are
// unknown, to prevent this list to become very long as experiments are added
// and removed.
void SanitizeList(PrefService* prefs) {
std::set<std::string> known_experiments;
for (size_t i = 0; i < num_experiments; ++i)
- known_experiments.insert(experiments[i].internal_name);
+ AddInternalName(experiments[i], &known_experiments);
std::set<std::string> enabled_experiments;
GetEnabledFlags(prefs, &enabled_experiments);
@@ -386,7 +438,7 @@ void GetSanitizedEnabledFlagsForCurrentPlatform(
int current_platform = GetCurrentPlatform();
for (size_t i = 0; i < num_experiments; ++i) {
if (experiments[i].supported_platforms & current_platform)
- platform_experiments.insert(experiments[i].internal_name);
+ AddInternalName(experiments[i], &platform_experiments);
}
std::set<std::string> new_enabled_experiments;
@@ -398,6 +450,29 @@ void GetSanitizedEnabledFlagsForCurrentPlatform(
result->swap(new_enabled_experiments);
}
+// Returns the Value representing the choice data in the specified experiment.
+// If one of the choices is enabled |is_one_selected| is set to true.
+Value* CreateChoiceData(const Experiment& experiment,
+ const std::set<std::string>& enabled_experiments,
+ bool* is_one_selected) {
+ DCHECK(experiment.type == Experiment::MULTI_VALUE);
+ ListValue* result = new ListValue;
+ for (int i = 0; i < experiment.num_choices; ++i) {
+ const Experiment::Choice& choice = experiment.choices[i];
+ DictionaryValue* value = new DictionaryValue;
+ std::string name = NameForChoice(experiment, i);
+ value->SetString("description",
+ l10n_util::GetStringUTF16(choice.description_id));
+ value->SetString("internal_name", name);
+ bool is_selected = enabled_experiments.count(name) > 0;
+ if (is_selected)
+ *is_one_selected = true;
+ value->SetBoolean("selected", is_selected);
+ result->Append(value);
+ }
+ return result;
+}
+
} // namespace
void ConvertFlagsToSwitches(PrefService* prefs, CommandLine* command_line) {
@@ -423,9 +498,15 @@ ListValue* GetFlagsExperimentsData(PrefService* prefs) {
data->SetString("description",
l10n_util::GetStringUTF16(
experiment.visible_description_id));
- data->SetBoolean("enabled",
- enabled_experiments.count(experiment.internal_name) > 0);
+ bool enabled = enabled_experiments.count(experiment.internal_name) > 0;
+
+ if (experiment.type == Experiment::MULTI_VALUE) {
+ data->Set("choices", CreateChoiceData(experiment, enabled_experiments,
+ &enabled));
+ }
+
+ data->SetBoolean("enabled", enabled);
experiments_data->Append(data);
}
return experiments_data;
@@ -494,9 +575,17 @@ void FlagsState::ConvertFlagsToSwitches(
GetSanitizedEnabledFlagsForCurrentPlatform(prefs, &enabled_experiments);
- std::map<std::string, const Experiment*> experiment_map;
- for (size_t i = 0; i < num_experiments; ++i)
- experiment_map[experiments[i].internal_name] = &experiments[i];
+ typedef std::map<std::string, std::string> NameToSwitchMap;
+ NameToSwitchMap name_to_switch_map;
+ for (size_t i = 0; i < num_experiments; ++i) {
+ const Experiment& e = experiments[i];
+ if (e.type == Experiment::SINGLE_VALUE) {
+ name_to_switch_map[e.internal_name] = e.command_line;
+ } else {
+ for (int j = 0; j < e.num_choices; ++j)
+ name_to_switch_map[NameForChoice(e, j)] = e.choices[j].command_line;
+ }
+ }
command_line->AppendSwitch(switches::kFlagSwitchesBegin);
flags_switches_.insert(switches::kFlagSwitchesBegin);
@@ -504,14 +593,15 @@ void FlagsState::ConvertFlagsToSwitches(
it != enabled_experiments.end();
++it) {
const std::string& experiment_name = *it;
- std::map<std::string, const Experiment*>::iterator experiment =
- experiment_map.find(experiment_name);
- DCHECK(experiment != experiment_map.end());
- if (experiment == experiment_map.end())
+ NameToSwitchMap::const_iterator name_to_switch_it =
+ name_to_switch_map.find(experiment_name);
+ if (name_to_switch_it == name_to_switch_map.end()) {
+ NOTREACHED();
continue;
+ }
- command_line->AppendSwitch(experiment->second->command_line);
- flags_switches_.insert(experiment->second->command_line);
+ command_line->AppendSwitch(name_to_switch_it->second);
+ flags_switches_.insert(name_to_switch_it->second);
}
command_line->AppendSwitch(switches::kFlagSwitchesEnd);
flags_switches_.insert(switches::kFlagSwitchesEnd);
@@ -525,13 +615,56 @@ void FlagsState::SetExperimentEnabled(
PrefService* prefs, const std::string& internal_name, bool enable) {
needs_restart_ = true;
+ size_t at_index = internal_name.find(about_flags::testing::kMultiSeparator);
+ if (at_index != std::string::npos) {
+ DCHECK(enable);
+ // We're being asked to enable a multi-choice experiment. Disable the
+ // currently selected choice.
+ DCHECK_NE(at_index, 0u);
+ SetExperimentEnabled(prefs, internal_name.substr(0, at_index), false);
+
+ // And enable the new choice.
+ std::set<std::string> enabled_experiments;
+ GetSanitizedEnabledFlags(prefs, &enabled_experiments);
+ enabled_experiments.insert(internal_name);
+ SetEnabledFlags(prefs, enabled_experiments);
+ return;
+ }
+
std::set<std::string> enabled_experiments;
GetSanitizedEnabledFlags(prefs, &enabled_experiments);
- if (enable)
- enabled_experiments.insert(internal_name);
- else
- enabled_experiments.erase(internal_name);
+ const Experiment* e = NULL;
+ for (size_t i = 0; i < num_experiments; ++i) {
+ if (experiments[i].internal_name == internal_name) {
+ e = experiments + i;
+ break;
+ }
+ }
+ DCHECK(e);
+
+ if (e->type == Experiment::SINGLE_VALUE) {
+ if (enable)
+ enabled_experiments.insert(internal_name);
+ else
+ enabled_experiments.erase(internal_name);
+ } else {
+ if (enable) {
+ // Enable the first choice.
+ enabled_experiments.insert(NameForChoice(*e, 0));
+ } else {
+ // Find the currently enabled choice and disable it.
+ for (int i = 0; i < e->num_choices; ++i) {
+ std::string choice_name = NameForChoice(*e, i);
+ if (enabled_experiments.find(choice_name) !=
+ enabled_experiments.end()) {
+ enabled_experiments.erase(choice_name);
+ // Continue on just in case there's a bug and more than one
+ // experiment for this choice was enabled.
+ }
+ }
+ }
+ }
SetEnabledFlags(prefs, enabled_experiments);
}
@@ -553,6 +686,11 @@ void FlagsState::reset() {
} // namespace
namespace testing {
+
+// WARNING: '@' is also used in the html file. If you update this constant you
+// also need to update the html file.
+const char kMultiSeparator[] = "@";
+
void ClearState() {
FlagsState::instance()->reset();
}
@@ -567,6 +705,11 @@ void SetExperiments(const Experiment* e, size_t count) {
}
}
+const Experiment* GetExperiments(size_t* count) {
+ *count = num_experiments;
+ return experiments;
+}
+
} // namespace testing
} // namespace about_flags
diff --git a/chrome/browser/about_flags.h b/chrome/browser/about_flags.h
index fa8c868..daa76df 100644
--- a/chrome/browser/about_flags.h
+++ b/chrome/browser/about_flags.h
@@ -24,6 +24,27 @@ enum { kOsMac = 1 << 0, kOsWin = 1 << 1, kOsLinux = 1 << 2 , kOsCrOS = 1 << 3 };
// for testing).
// This is exposed only for testing.
struct Experiment {
+ enum Type {
+ // An experiment with a single value. This is typically what you want.
+ SINGLE_VALUE,
+
+ // The experiment has multiple values only one of which is ever enabled.
+ // For MULTI_VALUE experiments the command_line of the Experiment is not
+ // used. If the experiment is enabled the command line of the selected
+ // Choice is enabled.
+ MULTI_VALUE,
+ };
+
+ // Used for MULTI_VALUE types to describe one of the possible values the user
+ // can select.
+ struct Choice {
+ // ID of the message containing the choice name.
+ int description_id;
+
+ // Command line to enabled for this choice.
+ const char* command_line;
+ };
+
// The internal name of the experiment. This is never shown to the user.
// It _is_ however stored in the prefs file, so you shouldn't change the
// name of existing flags.
@@ -39,10 +60,21 @@ struct Experiment {
// Needs to be more than a compile-time #ifdef because of profile sync.
unsigned supported_platforms; // bitmask
+ // Type of experiment.
+ Type type;
+
// The commandline parameter that's added when this lab is active. This is
// different from |internal_name| so that the commandline flag can be
// renamed without breaking the prefs file.
+ // This is used if type is SINGLE_VALUE.
const char* command_line;
+
+ // This is used if type is MULTI_VALUE.
+ const Choice* choices;
+
+ // Number of |choices|.
+ // This is used if type is MULTI_VALUE.
+ int num_choices;
};
// Reads the Labs |prefs| (called "Labs" for historical reasons) and adds the
@@ -80,6 +112,14 @@ void ClearState();
// Sets the list of experiments. Pass in NULL to use the default set. This does
// NOT take ownership of the supplied Experiments.
void SetExperiments(const Experiment* e, size_t count);
+
+// Returns the current set of experiments.
+const Experiment* GetExperiments(size_t* count);
+
+// Separator used for multi values. Multi values are represented in prefs as
+// name-of-experiment + kMultiSeparator + selected_index.
+extern const char kMultiSeparator[];
+
} // namespace testing
} // namespace about_flags
diff --git a/chrome/browser/about_flags_unittest.cc b/chrome/browser/about_flags_unittest.cc
index f6b5193..c3bbe95 100644
--- a/chrome/browser/about_flags_unittest.cc
+++ b/chrome/browser/about_flags_unittest.cc
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/about_flags.h"
-
+#include "base/string_number_conversions.h"
#include "base/values.h"
+#include "chrome/browser/about_flags.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_pref_service.h"
@@ -14,37 +14,64 @@
const char kFlags1[] = "flag1";
const char kFlags2[] = "flag2";
const char kFlags3[] = "flag3";
+const char kFlags4[] = "flag4";
const char kSwitch1[] = "switch";
const char kSwitch2[] = "switch2";
const char kSwitch3[] = "switch3";
+const char kMultiSwitch1[] = "multi_switch1";
+const char kMultiSwitch2[] = "multi_switch2";
+
namespace about_flags {
-// The experiments that are set for these tests. The first two experiments are
-// supported on the current platform, but the last is only supported on a
-// platform other than the current.
+const Experiment::Choice kMultiChoices[] = {
+ { IDS_PRODUCT_NAME, kMultiSwitch1 },
+ { IDS_PRODUCT_NAME, kMultiSwitch2 },
+};
+
+// The experiments that are set for these tests. The 3rd experiment is not
+// supported on the current platform, all others are.
static Experiment kExperiments[] = {
{
kFlags1,
IDS_PRODUCT_NAME,
IDS_PRODUCT_NAME,
0, // Ends up being mapped to the current platform.
- kSwitch1
+ Experiment::SINGLE_VALUE,
+ kSwitch1,
+ NULL,
+ 0
},
{
kFlags2,
IDS_PRODUCT_NAME,
IDS_PRODUCT_NAME,
0, // Ends up being mapped to the current platform.
- kSwitch2
+ Experiment::SINGLE_VALUE,
+ kSwitch2,
+ NULL,
+ 0
},
{
kFlags3,
IDS_PRODUCT_NAME,
IDS_PRODUCT_NAME,
0, // This ends up enabling for an OS other than the current.
- kSwitch3
+ Experiment::SINGLE_VALUE,
+ kSwitch3,
+ NULL,
+ 0
+ },
+ {
+ kFlags4,
+ IDS_PRODUCT_NAME,
+ IDS_PRODUCT_NAME,
+ 0, // Ends up being mapped to the current platform.
+ Experiment::MULTI_VALUE,
+ "",
+ kMultiChoices,
+ arraysize(kMultiChoices)
},
};
@@ -176,7 +203,7 @@ TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
// Tests enabling experiments that aren't supported on the current platform.
TEST_F(AboutFlagsTest, PersistAndPrune) {
- // Enable exerpiement 1 and experiment 3.
+ // Enable experiments 1 and 3.
SetExperimentEnabled(&prefs_, kFlags1, true);
SetExperimentEnabled(&prefs_, kFlags3, true);
CommandLine command_line(CommandLine::NO_PROGRAM);
@@ -192,7 +219,51 @@ TEST_F(AboutFlagsTest, PersistAndPrune) {
// Experiment 3 should show still be persisted in preferences though.
scoped_ptr<ListValue> switch_prefs(GetFlagsExperimentsData(&prefs_));
ASSERT_TRUE(switch_prefs.get());
- EXPECT_EQ(2u, switch_prefs->GetSize());
+ EXPECT_EQ(arraysize(kExperiments) - 1, switch_prefs->GetSize());
+}
+
+// Tests enabling multi-value type experiments.
+TEST_F(AboutFlagsTest, MultiValues) {
+ // Enable the multi value experiment, which should enable the first choice.
+ SetExperimentEnabled(&prefs_, kFlags4, true);
+ {
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ ConvertFlagsToSwitches(&prefs_, &command_line);
+ EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch1));
+ EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
+ }
+
+ // Enable the 2nd choice of the multi-value, which should disable the first
+ // choice.
+ SetExperimentEnabled(&prefs_, std::string(kFlags4) +
+ std::string(testing::kMultiSeparator) +
+ base::IntToString(1), true);
+ {
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ ConvertFlagsToSwitches(&prefs_, &command_line);
+ EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
+ EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
+ }
+
+ // Disable the multi-value experiment.
+ SetExperimentEnabled(&prefs_, kFlags4, false);
+ {
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ ConvertFlagsToSwitches(&prefs_, &command_line);
+ EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
+ EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
+ }
+}
+
+// Makes sure there are no separators in any of the experiment names.
+TEST_F(AboutFlagsTest, NoSeparators) {
+ testing::SetExperiments(NULL, 0);
+ size_t count;
+ const Experiment* experiments = testing::GetExperiments(&count);
+ for (size_t i = 0; i < count; ++i) {
+ std::string name = experiments->internal_name;
+ EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
+ }
}
} // namespace about_flags
diff --git a/chrome/browser/instant/instant_controller.cc b/chrome/browser/instant/instant_controller.cc
index fcc55f9..8a71266 100644
--- a/chrome/browser/instant/instant_controller.cc
+++ b/chrome/browser/instant/instant_controller.cc
@@ -514,8 +514,10 @@ bool InstantController::GetType(Profile* profile, Type* type) {
*type = VERBATIM_TYPE;
return true;
}
-
- // There is no switch for PREDICTIVE_NO_AUTO_COMPLETE_TYPE.
+ if (cl->HasSwitch(switches::kEnablePredictiveNoAutoCompleteInstant)) {
+ *type = PREDICTIVE_NO_AUTO_COMPLETE_TYPE;
+ return true;
+ }
// Then prefs.
PrefService* prefs = profile->GetPrefs();
diff --git a/chrome/browser/resources/flags.html b/chrome/browser/resources/flags.html
index 9798c82..106d4a5 100644
--- a/chrome/browser/resources/flags.html
+++ b/chrome/browser/resources/flags.html
@@ -176,7 +176,15 @@ var flagsExperimentsDataFormat = {
'internal_name': 'Experiment ID string',
'name': 'Experiment Name',
'description': 'description',
- 'enabled': true
+ 'enabled': true,
+ /* choices is only set if the experiment has multiple values */
+ 'choices': [
+ {
+ 'internal_name': 'Experiment ID string',
+ 'description': 'description',
+ 'selected': true
+ }
+ ]
}
],
'needsRestart': false
@@ -231,6 +239,17 @@ function handleEnableExperiment(node, enable) {
requestFlagsExperimentsData();
}
+/**
+ * Invoked when the selection of a multi-value choice is changed to the
+ * specified index.
+ */
+function handleSelectChoiceExperiment(node, index) {
+ // Tell the C++ FlagsDOMHandler to enable the selected choice.
+ chrome.send('enableFlagsExperiment',
+ [String(node.internal_name) + "@" + index, "true"]);
+ requestFlagsExperimentsData();
+}
+
// Get data and have it displayed upon loading.
document.addEventListener('DOMContentLoaded', requestFlagsExperimentsData);
@@ -282,6 +301,15 @@ document.addEventListener('DOMContentLoaded', requestFlagsExperimentsData);
<div>
<span dir="ltr" jsvalues=".innerHTML:description">
</div>
+ <div jsdisplay="choices && choices.length > 0">
+ <select jsvalues=".internal_name:internal_name;.disabled:!enabled"
+ onchange="handleSelectChoiceExperiment(this, this.selectedIndex)">
+ <option jsvalues=".selected:selected"
+ jsselect="choices"
+ jscontent="description">NAME
+ </option>
+ </select>
+ </div>
</div>
</div>
<div class="experiment-actions">
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 7c7fb23..33cfe75 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -505,9 +505,13 @@ const char kEnableNaClDebug[] = "enable-nacl-debug";
// Enable Native Web Worker support.
const char kEnableNativeWebWorkers[] = "enable-native-web-workers";
-// Is the predictive varition of instant enabled?
+// Is InstantController::PREDICTIVE_TYPE enabled?
const char kEnablePredictiveInstant[] = "enable-predictive-instant";
+// Is InstantController::PREDICTIVE_NO_AUTO_COMPLETE_TYPE enabled?
+const char kEnablePredictiveNoAutoCompleteInstant[] =
+ "enable-predictive-no-auto-complete-instant";
+
// This applies only when the process type is "service". Enables the
// Chromoting Host Process within the service process.
const char kEnableRemoting[] = "enable-remoting";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 22b930b..26c79bf 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -154,6 +154,7 @@ extern const char kEnablePagePrerender[];
extern const char kEnableSyncNewAutofill[];
extern const char kEnablePreconnect[];
extern const char kEnablePredictiveInstant[];
+extern const char kEnablePredictiveNoAutoCompleteInstant[];
extern const char kEnablePreparsedJsCaching[];
extern const char kEnablePrintPreview[];
extern const char kEnableRemoting[];