blob: 49dbd3f05c951731b37ef851f34988b73b9d4a7c [file] [log] [blame]
[email protected]3828d6f2011-02-24 18:32:211// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]ad2a3ded2010-08-27 13:19:052// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]1a47d7e2010-10-15 00:37:245#include "chrome/browser/about_flags.h"
[email protected]ad2a3ded2010-08-27 13:19:056
7#include <algorithm>
8#include <iterator>
9#include <map>
10#include <set>
11
[email protected]ad2a3ded2010-08-27 13:19:0512#include "base/command_line.h"
[email protected]3b63f8f42011-03-28 01:54:1513#include "base/memory/singleton.h"
[email protected]8a6ff28d2010-12-02 16:35:1914#include "base/string_number_conversions.h"
[email protected]d208f4d82011-05-23 21:52:0315#include "base/utf_string_conversions.h"
[email protected]ad2a3ded2010-08-27 13:19:0516#include "base/values.h"
[email protected]ad2a3ded2010-08-27 13:19:0517#include "chrome/browser/prefs/pref_service.h"
[email protected]1bc78422011-03-31 08:41:3818#include "chrome/browser/prefs/scoped_user_pref_update.h"
[email protected]d208f4d82011-05-23 21:52:0319#include "chrome/common/chrome_content_client.h"
[email protected]ad2a3ded2010-08-27 13:19:0520#include "chrome/common/chrome_switches.h"
21#include "chrome/common/pref_names.h"
[email protected]afd1e522011-04-27 23:29:5922#include "content/browser/user_metrics.h"
[email protected]ad2a3ded2010-08-27 13:19:0523#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1724#include "ui/base/l10n/l10n_util.h"
[email protected]8ff7f342011-05-25 01:49:4725#include "ui/gfx/gl/gl_switches.h"
[email protected]ad2a3ded2010-08-27 13:19:0526
[email protected]1a47d7e2010-10-15 00:37:2427namespace about_flags {
[email protected]ad2a3ded2010-08-27 13:19:0528
[email protected]8a6ff28d2010-12-02 16:35:1929// Macros to simplify specifying the type.
[email protected]a82744532011-02-11 16:15:5330#define SINGLE_VALUE_TYPE_AND_VALUE(command_line_switch, switch_value) \
31 Experiment::SINGLE_VALUE, command_line_switch, switch_value, NULL, 0
32#define SINGLE_VALUE_TYPE(command_line_switch) \
33 SINGLE_VALUE_TYPE_AND_VALUE(command_line_switch, "")
34#define MULTI_VALUE_TYPE(choices) \
[email protected]0e6f56d2011-02-12 23:45:1535 Experiment::MULTI_VALUE, "", "", choices, arraysize(choices)
[email protected]8a6ff28d2010-12-02 16:35:1936
[email protected]e2ddbc92010-10-15 20:02:0737namespace {
38
[email protected]a314ee5a2010-10-26 21:23:2839const unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS;
[email protected]ad2a3ded2010-08-27 13:19:0540
[email protected]ba8164242010-11-16 21:31:0041// Names for former Chrome OS Labs experiments, shared with prefs migration
42// code.
43const char kMediaPlayerExperimentName[] = "media-player";
44const char kAdvancedFileSystemExperimentName[] = "advanced-file-system";
45const char kVerticalTabsExperimentName[] = "vertical-tabs";
46
[email protected]4bc5050c2010-11-18 17:55:5447// RECORDING USER METRICS FOR FLAGS:
48// -----------------------------------------------------------------------------
49// The first line of the experiment is the internal name. If you'd like to
50// gather statistics about the usage of your flag, you should append a marker
51// comment to the end of the feature name, like so:
52// "my-special-feature", // FLAGS:RECORD_UMA
53//
54// After doing that, run //chrome/tools/extract_actions.py (see instructions at
55// the top of that file for details) to update the chromeactions.txt file, which
56// will enable UMA to record your feature flag.
57//
58// After your feature has shipped under a flag, you can locate the metrics
59// under the action name AboutFlags_internal-action-name. Actions are recorded
60// once per startup, so you should divide this number by AboutFlags_StartupTick
61// to get a sense of usage. Note that this will not be the same as number of
62// users with a given feature enabled because users can quit and relaunch
63// the application multiple times over a given time interval.
64// TODO(rsesek): See if there's a way to count per-user, rather than
65// per-startup.
66
[email protected]8a6ff28d2010-12-02 16:35:1967// To add a new experiment add to the end of kExperiments. There are two
68// distinct types of experiments:
69// . SINGLE_VALUE: experiment is either on or off. Use the SINGLE_VALUE_TYPE
70// macro for this type supplying the command line to the macro.
[email protected]28e35af2011-02-09 12:56:2271// . MULTI_VALUE: a list of choices, the first of which should correspond to a
72// deactivated state for this lab (i.e. no command line option). To specify
73// this type of experiment use the macro MULTI_VALUE_TYPE supplying it the
74// array of choices.
[email protected]8a6ff28d2010-12-02 16:35:1975// See the documentation of Experiment for details on the fields.
76//
77// When adding a new choice, add it to the end of the list.
[email protected]ad2a3ded2010-08-27 13:19:0578const Experiment kExperiments[] = {
79 {
[email protected]aac169d2011-03-18 19:53:0380 "expose-for-tabs", // FLAGS:RECORD_UMA
81 IDS_FLAGS_TABPOSE_NAME,
82 IDS_FLAGS_TABPOSE_DESCRIPTION,
83 kOsMac,
84#if defined(OS_MACOSX)
85 // The switch exists only on OS X.
86 SINGLE_VALUE_TYPE(switches::kEnableExposeForTabs)
87#else
88 SINGLE_VALUE_TYPE("")
89#endif
90 },
91 {
[email protected]4bc5050c2010-11-18 17:55:5492 "vertical-tabs", // FLAGS:RECORD_UMA
[email protected]9486c1f2010-10-14 19:52:1293 IDS_FLAGS_SIDE_TABS_NAME,
94 IDS_FLAGS_SIDE_TABS_DESCRIPTION,
[email protected]ba8164242010-11-16 21:31:0095 kOsWin | kOsCrOS,
[email protected]8a6ff28d2010-12-02 16:35:1996 SINGLE_VALUE_TYPE(switches::kEnableVerticalTabs)
[email protected]654151872010-09-13 22:43:0597 },
98 {
[email protected]4bc5050c2010-11-18 17:55:5499 "conflicting-modules-check", // FLAGS:RECORD_UMA
[email protected]c1bbaa82010-11-08 11:17:05100 IDS_FLAGS_CONFLICTS_CHECK_NAME,
101 IDS_FLAGS_CONFLICTS_CHECK_DESCRIPTION,
102 kOsWin,
[email protected]8a6ff28d2010-12-02 16:35:19103 SINGLE_VALUE_TYPE(switches::kConflictingModulesCheck)
[email protected]c1bbaa82010-11-08 11:17:05104 },
105 {
[email protected]4bc5050c2010-11-18 17:55:54106 "cloud-print-proxy", // FLAGS:RECORD_UMA
[email protected]9486c1f2010-10-14 19:52:12107 IDS_FLAGS_CLOUD_PRINT_PROXY_NAME,
108 IDS_FLAGS_CLOUD_PRINT_PROXY_DESCRIPTION,
[email protected]9f8872b32011-03-04 19:44:45109 // For a Chrome build, we know we have a PDF plug-in on Windows, so it's
[email protected]4fa24bf2011-08-20 02:15:22110 // fully enabled.
[email protected]5d10d57d2011-07-22 22:16:31111 // Otherwise, where we know Windows could be working if a viable PDF
[email protected]4fa24bf2011-08-20 02:15:22112 // plug-in could be supplied, we'll keep the lab enabled. Mac and Linux
113 // always have PDF rasterization available, so no flag needed there.
114#if !defined(GOOGLE_CHROME_BUILD)
115 kOsWin,
116#else
117 0,
[email protected]fa6d2a2f2010-11-30 21:47:19118#endif
[email protected]8a6ff28d2010-12-02 16:35:19119 SINGLE_VALUE_TYPE(switches::kEnableCloudPrintProxy)
[email protected]8b6588a2010-10-12 02:39:42120 },
[email protected]580939a2010-10-12 18:54:37121 {
[email protected]bb461532010-11-26 21:50:23122 "crxless-web-apps",
123 IDS_FLAGS_CRXLESS_WEB_APPS_NAME,
124 IDS_FLAGS_CRXLESS_WEB_APPS_DESCRIPTION,
125 kOsAll,
[email protected]8a6ff28d2010-12-02 16:35:19126 SINGLE_VALUE_TYPE(switches::kEnableCrxlessWebApps)
[email protected]bb461532010-11-26 21:50:23127 },
128 {
[email protected]96c6f4c2011-05-18 19:36:22129 "ignore-gpu-blacklist",
130 IDS_FLAGS_IGNORE_GPU_BLACKLIST_NAME,
131 IDS_FLAGS_IGNORE_GPU_BLACKLIST_DESCRIPTION,
132 kOsAll,
133 SINGLE_VALUE_TYPE(switches::kIgnoreGpuBlacklist)
134 },
135 {
[email protected]0110cf112011-07-02 00:39:43136 "force-compositing-mode-2",
[email protected]96c6f4c2011-05-18 19:36:22137 IDS_FLAGS_FORCE_COMPOSITING_MODE_NAME,
138 IDS_FLAGS_FORCE_COMPOSITING_MODE_DESCRIPTION,
139 kOsAll,
140 SINGLE_VALUE_TYPE(switches::kForceCompositingMode)
141 },
142 {
[email protected]5963b772011-02-09 22:55:38143 "composited-layer-borders",
144 IDS_FLAGS_COMPOSITED_LAYER_BORDERS,
145 IDS_FLAGS_COMPOSITED_LAYER_BORDERS_DESCRIPTION,
146 kOsAll,
147 SINGLE_VALUE_TYPE(switches::kShowCompositedLayerBorders)
148 },
149 {
[email protected]a8f1eaa2011-03-07 19:00:58150 "show-fps-counter",
151 IDS_FLAGS_SHOW_FPS_COUNTER,
152 IDS_FLAGS_SHOW_FPS_COUNTER_DESCRIPTION,
153 kOsAll,
154 SINGLE_VALUE_TYPE(switches::kShowFPSCounter)
155 },
[email protected]8ff7f342011-05-25 01:49:47156 {
157 "disable-gpu-vsync",
158 IDS_FLAGS_DISABLE_GPU_VSYNC_NAME,
159 IDS_FLAGS_DISABLE_GPU_VSYNC_DESCRIPTION,
160 kOsAll,
161 SINGLE_VALUE_TYPE(switches::kDisableGpuVsync)
162 },
[email protected]9de0ec12011-08-24 21:57:50163 // Exposed on all platforms until there is a workaround for easy access to
164 // the native print dialog for users that need it. Once that's done, revert
165 // back to:
166 // Only expose this for Chromium builds where users may not have the PDF
167 // plugin. Do not give Google Chrome users the option to disable it here.
168 // Also expose it for Chrome OS where print preview is still experimental.
[email protected]8d260e52010-10-13 01:03:05169 {
[email protected]4bc5050c2010-11-18 17:55:54170 "print-preview", // FLAGS:RECORD_UMA
[email protected]9486c1f2010-10-14 19:52:12171 IDS_FLAGS_PRINT_PREVIEW_NAME,
172 IDS_FLAGS_PRINT_PREVIEW_DESCRIPTION,
[email protected]b579afd2011-07-13 00:01:27173 kOsAll,
[email protected]8a6ff28d2010-12-02 16:35:19174 SINGLE_VALUE_TYPE(switches::kEnablePrintPreview)
[email protected]2fe15fcb2010-10-21 20:39:53175 },
[email protected]313bbf5e2011-08-24 03:31:15176 {
177 "cloud-print", // FLAGS:RECORD_UMA
178 IDS_FLAGS_CLOUD_PRINT_NAME,
179 IDS_FLAGS_CLOUD_PRINT_DESCRIPTION,
180 // Define this for all platforms except Chrome OS.
181 // It makes no sense to disable it on Chrome OS.
182 kOsMac | kOsWin | kOsLinux,
183 SINGLE_VALUE_TYPE(switches::kEnableCloudPrint)
184 },
[email protected]d208f4d82011-05-23 21:52:03185 // TODO(dspringer): When NaCl is on by default, remove this flag entry.
[email protected]2fe15fcb2010-10-21 20:39:53186 {
[email protected]e3791ce92011-08-09 01:03:32187 "enable-nacl", // FLAGS:RECORD_UMA
[email protected]4ff87d202010-11-06 01:28:40188 IDS_FLAGS_ENABLE_NACL_NAME,
189 IDS_FLAGS_ENABLE_NACL_DESCRIPTION,
190 kOsAll,
[email protected]8a6ff28d2010-12-02 16:35:19191 SINGLE_VALUE_TYPE(switches::kEnableNaCl)
[email protected]4ff87d202010-11-06 01:28:40192 },
193 {
[email protected]4bc5050c2010-11-18 17:55:54194 "extension-apis", // FLAGS:RECORD_UMA
[email protected]11dd68cd52010-11-12 01:15:32195 IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_NAME,
196 IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION,
197 kOsAll,
[email protected]8a6ff28d2010-12-02 16:35:19198 SINGLE_VALUE_TYPE(switches::kEnableExperimentalExtensionApis)
[email protected]11dd68cd52010-11-12 01:15:32199 },
[email protected]3627b06d2010-11-12 16:36:16200 {
[email protected]cbe224d2011-08-04 22:12:49201 "apps-new-install-bubble",
202 IDS_FLAGS_APPS_NEW_INSTALL_BUBBLE_NAME,
203 IDS_FLAGS_APPS_NEW_INSTALL_BUBBLE_DESCRIPTION,
204 kOsAll,
205 SINGLE_VALUE_TYPE(switches::kAppsNewInstallBubble)
206 },
207 {
[email protected]4bc5050c2010-11-18 17:55:54208 "click-to-play", // FLAGS:RECORD_UMA
[email protected]3627b06d2010-11-12 16:36:16209 IDS_FLAGS_CLICK_TO_PLAY_NAME,
210 IDS_FLAGS_CLICK_TO_PLAY_DESCRIPTION,
211 kOsAll,
[email protected]8a6ff28d2010-12-02 16:35:19212 SINGLE_VALUE_TYPE(switches::kEnableClickToPlay)
[email protected]3627b06d2010-11-12 16:36:16213 },
[email protected]80bd24e2010-11-30 09:34:38214 {
215 "disable-hyperlink-auditing",
216 IDS_FLAGS_DISABLE_HYPERLINK_AUDITING_NAME,
217 IDS_FLAGS_DISABLE_HYPERLINK_AUDITING_DESCRIPTION,
218 kOsAll,
[email protected]8a6ff28d2010-12-02 16:35:19219 SINGLE_VALUE_TYPE(switches::kNoPings)
[email protected]feb28fef2010-12-01 10:52:51220 },
221 {
222 "experimental-location-features", // FLAGS:RECORD_UMA
223 IDS_FLAGS_EXPERIMENTAL_LOCATION_FEATURES_NAME,
224 IDS_FLAGS_EXPERIMENTAL_LOCATION_FEATURES_DESCRIPTION,
225 kOsMac | kOsWin | kOsLinux, // Currently does nothing on CrOS.
[email protected]8a6ff28d2010-12-02 16:35:19226 SINGLE_VALUE_TYPE(switches::kExperimentalLocationFeatures)
227 },
228 {
[email protected]4c6452d2011-01-12 08:47:57229 "block-reading-third-party-cookies",
230 IDS_FLAGS_BLOCK_ALL_THIRD_PARTY_COOKIES_NAME,
231 IDS_FLAGS_BLOCK_ALL_THIRD_PARTY_COOKIES_DESCRIPTION,
232 kOsAll,
233 SINGLE_VALUE_TYPE(switches::kBlockReadingThirdPartyCookies)
234 },
[email protected]04227962011-01-20 02:03:09235 {
236 "disable-interactive-form-validation",
237 IDS_FLAGS_DISABLE_INTERACTIVE_FORM_VALIDATION_NAME,
238 IDS_FLAGS_DISABLE_INTERACTIVE_FORM_VALIDATION_DESCRIPTION,
239 kOsAll,
240 SINGLE_VALUE_TYPE(switches::kDisableInteractiveFormValidation)
241 },
[email protected]8df51192011-01-22 20:05:03242 {
[email protected]07d490bc2011-03-07 17:05:26243 "focus-existing-tab-on-open", // FLAGS:RECORD_UMA
244 IDS_FLAGS_FOCUS_EXISTING_TAB_ON_OPEN_NAME,
245 IDS_FLAGS_FOCUS_EXISTING_TAB_ON_OPEN_DESCRIPTION,
246 kOsAll,
247 SINGLE_VALUE_TYPE(switches::kFocusExistingTabOnOpen)
248 },
[email protected]d5dcfb32011-03-19 00:49:24249 {
[email protected]5aea1862011-03-23 23:55:39250 "tab-groups-context-menu",
251 IDS_FLAGS_TAB_GROUPS_CONTEXT_MENU_NAME,
252 IDS_FLAGS_TAB_GROUPS_CONTEXT_MENU_DESCRIPTION,
253 kOsWin,
254 SINGLE_VALUE_TYPE(switches::kEnableTabGroupsContextMenu)
255 },
[email protected]e9bf9d92011-03-31 20:57:15256 {
257 "ppapi-flash-in-process",
258 IDS_FLAGS_PPAPI_FLASH_IN_PROCESS_NAME,
259 IDS_FLAGS_PPAPI_FLASH_IN_PROCESS_DESCRIPTION,
[email protected]f2d3ce0d2011-04-01 18:19:30260 kOsAll,
[email protected]e9bf9d92011-03-31 20:57:15261 SINGLE_VALUE_TYPE(switches::kPpapiFlashInProcess)
262 },
[email protected]ce5737142011-04-14 23:35:09263 {
[email protected]20b5df962011-04-18 22:07:43264 "multi-profiles",
265 IDS_FLAGS_MULTI_PROFILES_NAME,
266 IDS_FLAGS_MULTI_PROFILES_DESCRIPTION,
[email protected]0d76f6a2011-07-26 17:10:21267 kOsWin | kOsMac | kOsLinux, // This switch is not available in CrOS.
[email protected]20b5df962011-04-18 22:07:43268 SINGLE_VALUE_TYPE(switches::kMultiProfiles)
269 },
[email protected]cc057b02011-04-21 17:19:04270 {
[email protected]08338b862011-05-05 20:22:23271 "restrict-instant-to-search",
272 IDS_FLAGS_RESTRICT_INSTANT_TO_SEARCH_NAME,
273 IDS_FLAGS_RESTRICT_INSTANT_TO_SEARCH_DESCRIPTION,
274 kOsAll,
275 SINGLE_VALUE_TYPE(switches::kRestrictInstantToSearch)
276 },
[email protected]b652fc82011-05-23 07:53:05277 {
[email protected]f2557bd2011-06-01 02:33:07278 "preload-instant-search",
279 IDS_FLAGS_PRELOAD_INSTANT_SEARCH_NAME,
280 IDS_FLAGS_PRELOAD_INSTANT_SEARCH_DESCRIPTION,
281 kOsAll,
282 SINGLE_VALUE_TYPE(switches::kPreloadInstantSearch)
283 },
[email protected]354e33b2011-06-15 00:29:10284 {
285 "static-ip-config",
286 IDS_FLAGS_STATIC_IP_CONFIG_NAME,
287 IDS_FLAGS_STATIC_IP_CONFIG_DESCRIPTION,
288 kOsCrOS,
289#if defined(OS_CHROMEOS)
290 // This switch exists only on Chrome OS.
291 SINGLE_VALUE_TYPE(switches::kEnableStaticIPConfig)
292#else
293 SINGLE_VALUE_TYPE("")
294#endif
295 },
[email protected]3eb5728c2011-06-20 22:32:24296 {
297 "show-autofill-type-predictions",
298 IDS_FLAGS_SHOW_AUTOFILL_TYPE_PREDICTIONS_NAME,
299 IDS_FLAGS_SHOW_AUTOFILL_TYPE_PREDICTIONS_DESCRIPTION,
300 kOsAll,
301 SINGLE_VALUE_TYPE(switches::kShowAutofillTypePredictions)
302 },
[email protected]bff4d3e2011-06-21 23:58:52303 {
[email protected]ebde4ca2011-08-12 10:44:45304 "sync-oauth",
305 IDS_FLAGS_SYNC_OAUTH_NAME,
306 IDS_FLAGS_SYNC_OAUTH_DESCRIPTION,
307 kOsAll,
308 SINGLE_VALUE_TYPE(switches::kEnableSyncOAuth)
309 },
310 {
[email protected]5b0ec7f2011-08-11 02:17:17311 "sync-sessions",
312 IDS_FLAGS_SYNC_SESSIONS_NAME,
313 IDS_FLAGS_SYNC_SESSIONS_DESCRIPTION,
314 kOsAll,
315 SINGLE_VALUE_TYPE(switches::kEnableSyncSessions)
316 },
317 {
[email protected]a22ebd812011-06-23 00:05:39318 "enable-smooth-scrolling", // FLAGS:RECORD_UMA
319 IDS_FLAGS_ENABLE_SMOOTH_SCROLLING_NAME,
320 IDS_FLAGS_ENABLE_SMOOTH_SCROLLING_DESCRIPTION,
321 // Can't expose the switch unless the code is compiled in.
322#if defined(ENABLE_SMOOTH_SCROLLING)
323 kOsAll,
324#else
325 0,
326#endif
327 SINGLE_VALUE_TYPE(switches::kEnableSmoothScrolling)
328 },
[email protected]81a6b0b2011-06-24 17:55:40329 {
[email protected]ae1eb29a2011-08-17 17:50:57330 "prerender-from-omnibox", // FLAGS:RECORD_UMA
[email protected]81a6b0b2011-06-24 17:55:40331 IDS_FLAGS_PRERENDER_FROM_OMNIBOX_NAME,
332 IDS_FLAGS_PRERENDER_FROM_OMNIBOX_DESCRIPTION,
333 kOsAll,
334 SINGLE_VALUE_TYPE(switches::kPrerenderFromOmnibox)
335 },
[email protected]40916632011-07-02 01:11:01336 {
[email protected]f6f1d942011-07-29 00:21:11337 "enable-autofill-feedback",
338 IDS_FLAGS_ENABLE_ADDITIONAL_AUTOFILL_FEEDBACK_NAME,
339 IDS_FLAGS_ENABLE_ADDITIONAL_AUTOFILL_FEEDBACK_DESCRIPTION,
340 kOsAll,
341 SINGLE_VALUE_TYPE(switches::kEnableAutofillFeedback)
342 },
343 {
[email protected]73fb1fc52011-07-09 00:06:54344 "panels",
345 IDS_FLAGS_ENABLE_PANELS_NAME,
346 IDS_FLAGS_ENABLE_PANELS_DESCRIPTION,
347 kOsAll,
348 SINGLE_VALUE_TYPE(switches::kEnablePanels)
349 },
[email protected]e3749d12011-07-25 22:22:12350 {
351 "enable-shortcuts-provider",
352 IDS_FLAGS_ENABLE_SHORTCUTS_PROVIDER,
353 IDS_FLAGS_ENABLE_SHORTCUTS_PROVIDER_DESCRIPTION,
354 kOsAll,
355 SINGLE_VALUE_TYPE(switches::kEnableShortcutsProvider)
356 },
[email protected]1a2966b92011-08-09 06:50:19357#if defined(OS_CHROMEOS)
358 {
359 "webui-login",
360 IDS_SYNC_SETUP_TITLE,
361 IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_ADVANCED_BUTTON,
362 kOsCrOS,
363 SINGLE_VALUE_TYPE(switches::kWebUILogin)
364 },
365#endif
[email protected]80eb4262011-08-03 16:10:41366 {
367 "memory-widget",
368 IDS_FLAGS_MEMORY_WIDGET_NAME,
369 IDS_FLAGS_MEMORY_WIDGET_DESCRIPTION,
370 kOsCrOS,
371#if defined(OS_CHROMEOS)
372 // This switch exists only on Chrome OS.
373 SINGLE_VALUE_TYPE(switches::kMemoryWidget)
374#else
375 SINGLE_VALUE_TYPE("")
376#endif
[email protected]a0e4b072011-08-17 01:47:07377 },
378 {
379 "downloads-new-ui", // FLAGS:RECORD_UMA
380 IDS_FLAGS_DOWNLOADS_NEW_UI_NAME,
381 IDS_FLAGS_DOWNLOADS_NEW_UI_DESCRIPTION,
382 kOsAll,
383 SINGLE_VALUE_TYPE(switches::kDownloadsNewUI)
384 },
385};
[email protected]ad2a3ded2010-08-27 13:19:05386
[email protected]a314ee5a2010-10-26 21:23:28387const Experiment* experiments = kExperiments;
388size_t num_experiments = arraysize(kExperiments);
389
[email protected]e2ddbc92010-10-15 20:02:07390// Stores and encapsulates the little state that about:flags has.
391class FlagsState {
392 public:
393 FlagsState() : needs_restart_(false) {}
394 void ConvertFlagsToSwitches(PrefService* prefs, CommandLine* command_line);
395 bool IsRestartNeededToCommitChanges();
396 void SetExperimentEnabled(
[email protected]a314ee5a2010-10-26 21:23:28397 PrefService* prefs, const std::string& internal_name, bool enable);
[email protected]e2ddbc92010-10-15 20:02:07398 void RemoveFlagsSwitches(
399 std::map<std::string, CommandLine::StringType>* switch_list);
400 void reset();
401
402 // Returns the singleton instance of this class
[email protected]8e8bb6d2010-12-13 08:18:55403 static FlagsState* GetInstance() {
[email protected]e2ddbc92010-10-15 20:02:07404 return Singleton<FlagsState>::get();
405 }
406
407 private:
408 bool needs_restart_;
[email protected]a82744532011-02-11 16:15:53409 std::map<std::string, std::string> flags_switches_;
[email protected]e2ddbc92010-10-15 20:02:07410
411 DISALLOW_COPY_AND_ASSIGN(FlagsState);
412};
413
[email protected]c7b7800a2010-10-07 18:51:35414// Extracts the list of enabled lab experiments from preferences and stores them
[email protected]ad2a3ded2010-08-27 13:19:05415// in a set.
[email protected]1a47d7e2010-10-15 00:37:24416void GetEnabledFlags(const PrefService* prefs, std::set<std::string>* result) {
[email protected]ad2a3ded2010-08-27 13:19:05417 const ListValue* enabled_experiments = prefs->GetList(
418 prefs::kEnabledLabsExperiments);
419 if (!enabled_experiments)
420 return;
421
422 for (ListValue::const_iterator it = enabled_experiments->begin();
423 it != enabled_experiments->end();
424 ++it) {
425 std::string experiment_name;
426 if (!(*it)->GetAsString(&experiment_name)) {
427 LOG(WARNING) << "Invalid entry in " << prefs::kEnabledLabsExperiments;
428 continue;
429 }
430 result->insert(experiment_name);
431 }
432}
433
434// Takes a set of enabled lab experiments
[email protected]1a47d7e2010-10-15 00:37:24435void SetEnabledFlags(
[email protected]ad2a3ded2010-08-27 13:19:05436 PrefService* prefs, const std::set<std::string>& enabled_experiments) {
[email protected]1bc78422011-03-31 08:41:38437 ListPrefUpdate update(prefs, prefs::kEnabledLabsExperiments);
438 ListValue* experiments_list = update.Get();
[email protected]ad2a3ded2010-08-27 13:19:05439
440 experiments_list->Clear();
441 for (std::set<std::string>::const_iterator it = enabled_experiments.begin();
442 it != enabled_experiments.end();
443 ++it) {
444 experiments_list->Append(new StringValue(*it));
445 }
446}
447
[email protected]8a6ff28d2010-12-02 16:35:19448// Returns the name used in prefs for the choice at the specified index.
449std::string NameForChoice(const Experiment& e, int index) {
[email protected]2ce9c89752011-02-25 18:24:34450 DCHECK_EQ(Experiment::MULTI_VALUE, e.type);
451 DCHECK_LT(index, e.num_choices);
[email protected]8a6ff28d2010-12-02 16:35:19452 return std::string(e.internal_name) + about_flags::testing::kMultiSeparator +
453 base::IntToString(index);
454}
455
456// Adds the internal names for the specified experiment to |names|.
457void AddInternalName(const Experiment& e, std::set<std::string>* names) {
458 if (e.type == Experiment::SINGLE_VALUE) {
459 names->insert(e.internal_name);
460 } else {
[email protected]2ce9c89752011-02-25 18:24:34461 DCHECK_EQ(Experiment::MULTI_VALUE, e.type);
[email protected]8a6ff28d2010-12-02 16:35:19462 for (int i = 0; i < e.num_choices; ++i)
463 names->insert(NameForChoice(e, i));
464 }
465}
466
[email protected]28e35af2011-02-09 12:56:22467// Confirms that an experiment is valid, used in a DCHECK in
468// SanitizeList below.
469bool ValidateExperiment(const Experiment& e) {
470 switch (e.type) {
471 case Experiment::SINGLE_VALUE:
472 DCHECK_EQ(0, e.num_choices);
473 DCHECK(!e.choices);
474 break;
475 case Experiment::MULTI_VALUE:
476 DCHECK_GT(e.num_choices, 0);
477 DCHECK(e.choices);
[email protected]a82744532011-02-11 16:15:53478 DCHECK(e.choices[0].command_line_switch);
479 DCHECK_EQ('\0', e.choices[0].command_line_switch[0]);
[email protected]28e35af2011-02-09 12:56:22480 break;
481 default:
482 NOTREACHED();
483 }
484 return true;
485}
486
[email protected]ad2a3ded2010-08-27 13:19:05487// Removes all experiments from prefs::kEnabledLabsExperiments that are
488// unknown, to prevent this list to become very long as experiments are added
489// and removed.
490void SanitizeList(PrefService* prefs) {
491 std::set<std::string> known_experiments;
[email protected]28e35af2011-02-09 12:56:22492 for (size_t i = 0; i < num_experiments; ++i) {
493 DCHECK(ValidateExperiment(experiments[i]));
[email protected]8a6ff28d2010-12-02 16:35:19494 AddInternalName(experiments[i], &known_experiments);
[email protected]28e35af2011-02-09 12:56:22495 }
[email protected]ad2a3ded2010-08-27 13:19:05496
497 std::set<std::string> enabled_experiments;
[email protected]1a47d7e2010-10-15 00:37:24498 GetEnabledFlags(prefs, &enabled_experiments);
[email protected]ad2a3ded2010-08-27 13:19:05499
500 std::set<std::string> new_enabled_experiments;
501 std::set_intersection(
502 known_experiments.begin(), known_experiments.end(),
503 enabled_experiments.begin(), enabled_experiments.end(),
504 std::inserter(new_enabled_experiments, new_enabled_experiments.begin()));
505
[email protected]1a47d7e2010-10-15 00:37:24506 SetEnabledFlags(prefs, new_enabled_experiments);
[email protected]ad2a3ded2010-08-27 13:19:05507}
508
[email protected]1a47d7e2010-10-15 00:37:24509void GetSanitizedEnabledFlags(
[email protected]ad2a3ded2010-08-27 13:19:05510 PrefService* prefs, std::set<std::string>* result) {
511 SanitizeList(prefs);
[email protected]1a47d7e2010-10-15 00:37:24512 GetEnabledFlags(prefs, result);
[email protected]ad2a3ded2010-08-27 13:19:05513}
514
[email protected]a314ee5a2010-10-26 21:23:28515// Variant of GetSanitizedEnabledFlags that also removes any flags that aren't
516// enabled on the current platform.
517void GetSanitizedEnabledFlagsForCurrentPlatform(
518 PrefService* prefs, std::set<std::string>* result) {
519 GetSanitizedEnabledFlags(prefs, result);
520
521 // Filter out any experiments that aren't enabled on the current platform. We
522 // don't remove these from prefs else syncing to a platform with a different
523 // set of experiments would be lossy.
524 std::set<std::string> platform_experiments;
525 int current_platform = GetCurrentPlatform();
526 for (size_t i = 0; i < num_experiments; ++i) {
527 if (experiments[i].supported_platforms & current_platform)
[email protected]8a6ff28d2010-12-02 16:35:19528 AddInternalName(experiments[i], &platform_experiments);
[email protected]a314ee5a2010-10-26 21:23:28529 }
530
531 std::set<std::string> new_enabled_experiments;
532 std::set_intersection(
533 platform_experiments.begin(), platform_experiments.end(),
534 result->begin(), result->end(),
535 std::inserter(new_enabled_experiments, new_enabled_experiments.begin()));
536
537 result->swap(new_enabled_experiments);
[email protected]ad2a3ded2010-08-27 13:19:05538}
539
[email protected]8a6ff28d2010-12-02 16:35:19540// Returns the Value representing the choice data in the specified experiment.
[email protected]8a6ff28d2010-12-02 16:35:19541Value* CreateChoiceData(const Experiment& experiment,
[email protected]28e35af2011-02-09 12:56:22542 const std::set<std::string>& enabled_experiments) {
[email protected]2ce9c89752011-02-25 18:24:34543 DCHECK_EQ(Experiment::MULTI_VALUE, experiment.type);
[email protected]8a6ff28d2010-12-02 16:35:19544 ListValue* result = new ListValue;
545 for (int i = 0; i < experiment.num_choices; ++i) {
546 const Experiment::Choice& choice = experiment.choices[i];
547 DictionaryValue* value = new DictionaryValue;
548 std::string name = NameForChoice(experiment, i);
549 value->SetString("description",
550 l10n_util::GetStringUTF16(choice.description_id));
551 value->SetString("internal_name", name);
[email protected]28e35af2011-02-09 12:56:22552 value->SetBoolean("selected", enabled_experiments.count(name) > 0);
[email protected]8a6ff28d2010-12-02 16:35:19553 result->Append(value);
554 }
555 return result;
556}
557
[email protected]e2ddbc92010-10-15 20:02:07558} // namespace
559
[email protected]1a47d7e2010-10-15 00:37:24560void ConvertFlagsToSwitches(PrefService* prefs, CommandLine* command_line) {
[email protected]8e8bb6d2010-12-13 08:18:55561 FlagsState::GetInstance()->ConvertFlagsToSwitches(prefs, command_line);
[email protected]ad2a3ded2010-08-27 13:19:05562}
563
[email protected]1a47d7e2010-10-15 00:37:24564ListValue* GetFlagsExperimentsData(PrefService* prefs) {
[email protected]ad2a3ded2010-08-27 13:19:05565 std::set<std::string> enabled_experiments;
[email protected]1a47d7e2010-10-15 00:37:24566 GetSanitizedEnabledFlags(prefs, &enabled_experiments);
[email protected]ad2a3ded2010-08-27 13:19:05567
568 int current_platform = GetCurrentPlatform();
569
570 ListValue* experiments_data = new ListValue();
[email protected]a314ee5a2010-10-26 21:23:28571 for (size_t i = 0; i < num_experiments; ++i) {
572 const Experiment& experiment = experiments[i];
[email protected]ad2a3ded2010-08-27 13:19:05573 if (!(experiment.supported_platforms & current_platform))
574 continue;
575
576 DictionaryValue* data = new DictionaryValue();
577 data->SetString("internal_name", experiment.internal_name);
578 data->SetString("name",
579 l10n_util::GetStringUTF16(experiment.visible_name_id));
580 data->SetString("description",
581 l10n_util::GetStringUTF16(
582 experiment.visible_description_id));
[email protected]ad2a3ded2010-08-27 13:19:05583
[email protected]28e35af2011-02-09 12:56:22584 switch (experiment.type) {
585 case Experiment::SINGLE_VALUE:
586 data->SetBoolean(
587 "enabled",
588 enabled_experiments.count(experiment.internal_name) > 0);
589 break;
590 case Experiment::MULTI_VALUE:
591 data->Set("choices", CreateChoiceData(experiment, enabled_experiments));
592 break;
593 default:
594 NOTREACHED();
[email protected]8a6ff28d2010-12-02 16:35:19595 }
596
[email protected]ad2a3ded2010-08-27 13:19:05597 experiments_data->Append(data);
598 }
599 return experiments_data;
600}
601
[email protected]ad2a3ded2010-08-27 13:19:05602bool IsRestartNeededToCommitChanges() {
[email protected]8e8bb6d2010-12-13 08:18:55603 return FlagsState::GetInstance()->IsRestartNeededToCommitChanges();
[email protected]ad2a3ded2010-08-27 13:19:05604}
605
606void SetExperimentEnabled(
[email protected]c7b7800a2010-10-07 18:51:35607 PrefService* prefs, const std::string& internal_name, bool enable) {
[email protected]8e8bb6d2010-12-13 08:18:55608 FlagsState::GetInstance()->SetExperimentEnabled(prefs, internal_name, enable);
[email protected]e2ddbc92010-10-15 20:02:07609}
610
611void RemoveFlagsSwitches(
612 std::map<std::string, CommandLine::StringType>* switch_list) {
[email protected]8e8bb6d2010-12-13 08:18:55613 FlagsState::GetInstance()->RemoveFlagsSwitches(switch_list);
[email protected]e2ddbc92010-10-15 20:02:07614}
615
[email protected]a314ee5a2010-10-26 21:23:28616int GetCurrentPlatform() {
617#if defined(OS_MACOSX)
618 return kOsMac;
619#elif defined(OS_WIN)
620 return kOsWin;
621#elif defined(OS_CHROMEOS) // Needs to be before the OS_LINUX check.
622 return kOsCrOS;
623#elif defined(OS_LINUX)
624 return kOsLinux;
625#else
626#error Unknown platform
627#endif
628}
629
[email protected]4bc5050c2010-11-18 17:55:54630void RecordUMAStatistics(const PrefService* prefs) {
631 std::set<std::string> flags;
632 GetEnabledFlags(prefs, &flags);
633 for (std::set<std::string>::iterator it = flags.begin(); it != flags.end();
634 ++it) {
635 std::string action("AboutFlags_");
636 action += *it;
637 UserMetrics::RecordComputedAction(action);
638 }
639 // Since flag metrics are recorded every startup, add a tick so that the
640 // stats can be made meaningful.
641 if (flags.size())
642 UserMetrics::RecordAction(UserMetricsAction("AboutFlags_StartupTick"));
[email protected]970b2fd2011-01-22 18:06:45643 UserMetrics::RecordAction(UserMetricsAction("StartupTick"));
[email protected]4bc5050c2010-11-18 17:55:54644}
645
[email protected]e2ddbc92010-10-15 20:02:07646//////////////////////////////////////////////////////////////////////////////
647// FlagsState implementation.
648
649namespace {
650
651void FlagsState::ConvertFlagsToSwitches(
652 PrefService* prefs, CommandLine* command_line) {
[email protected]e2ddbc92010-10-15 20:02:07653 if (command_line->HasSwitch(switches::kNoExperiments))
654 return;
655
656 std::set<std::string> enabled_experiments;
[email protected]ba8164242010-11-16 21:31:00657
[email protected]a314ee5a2010-10-26 21:23:28658 GetSanitizedEnabledFlagsForCurrentPlatform(prefs, &enabled_experiments);
[email protected]e2ddbc92010-10-15 20:02:07659
[email protected]a82744532011-02-11 16:15:53660 typedef std::map<std::string, std::pair<std::string, std::string> >
661 NameToSwitchAndValueMap;
662 NameToSwitchAndValueMap name_to_switch_map;
[email protected]8a6ff28d2010-12-02 16:35:19663 for (size_t i = 0; i < num_experiments; ++i) {
664 const Experiment& e = experiments[i];
665 if (e.type == Experiment::SINGLE_VALUE) {
[email protected]a82744532011-02-11 16:15:53666 name_to_switch_map[e.internal_name] =
667 std::pair<std::string, std::string>(e.command_line_switch,
668 e.command_line_value);
[email protected]8a6ff28d2010-12-02 16:35:19669 } else {
670 for (int j = 0; j < e.num_choices; ++j)
[email protected]a82744532011-02-11 16:15:53671 name_to_switch_map[NameForChoice(e, j)] =
672 std::pair<std::string, std::string>(
673 e.choices[j].command_line_switch,
674 e.choices[j].command_line_value);
[email protected]8a6ff28d2010-12-02 16:35:19675 }
676 }
[email protected]e2ddbc92010-10-15 20:02:07677
678 command_line->AppendSwitch(switches::kFlagSwitchesBegin);
[email protected]a82744532011-02-11 16:15:53679 flags_switches_.insert(
680 std::pair<std::string, std::string>(switches::kFlagSwitchesBegin,
681 std::string()));
[email protected]e2ddbc92010-10-15 20:02:07682 for (std::set<std::string>::iterator it = enabled_experiments.begin();
683 it != enabled_experiments.end();
684 ++it) {
685 const std::string& experiment_name = *it;
[email protected]a82744532011-02-11 16:15:53686 NameToSwitchAndValueMap::const_iterator name_to_switch_it =
[email protected]8a6ff28d2010-12-02 16:35:19687 name_to_switch_map.find(experiment_name);
688 if (name_to_switch_it == name_to_switch_map.end()) {
689 NOTREACHED();
[email protected]e2ddbc92010-10-15 20:02:07690 continue;
[email protected]8a6ff28d2010-12-02 16:35:19691 }
[email protected]e2ddbc92010-10-15 20:02:07692
[email protected]a82744532011-02-11 16:15:53693 const std::pair<std::string, std::string>&
694 switch_and_value_pair = name_to_switch_it->second;
695
696 command_line->AppendSwitchASCII(switch_and_value_pair.first,
697 switch_and_value_pair.second);
698 flags_switches_[switch_and_value_pair.first] = switch_and_value_pair.second;
[email protected]e2ddbc92010-10-15 20:02:07699 }
700 command_line->AppendSwitch(switches::kFlagSwitchesEnd);
[email protected]a82744532011-02-11 16:15:53701 flags_switches_.insert(
702 std::pair<std::string, std::string>(switches::kFlagSwitchesEnd,
703 std::string()));
[email protected]e2ddbc92010-10-15 20:02:07704}
705
706bool FlagsState::IsRestartNeededToCommitChanges() {
707 return needs_restart_;
708}
709
710void FlagsState::SetExperimentEnabled(
711 PrefService* prefs, const std::string& internal_name, bool enable) {
[email protected]ad2a3ded2010-08-27 13:19:05712 needs_restart_ = true;
713
[email protected]8a6ff28d2010-12-02 16:35:19714 size_t at_index = internal_name.find(about_flags::testing::kMultiSeparator);
715 if (at_index != std::string::npos) {
716 DCHECK(enable);
717 // We're being asked to enable a multi-choice experiment. Disable the
718 // currently selected choice.
719 DCHECK_NE(at_index, 0u);
[email protected]28e35af2011-02-09 12:56:22720 const std::string experiment_name = internal_name.substr(0, at_index);
721 SetExperimentEnabled(prefs, experiment_name, false);
[email protected]8a6ff28d2010-12-02 16:35:19722
[email protected]28e35af2011-02-09 12:56:22723 // And enable the new choice, if it is not the default first choice.
724 if (internal_name != experiment_name + "@0") {
725 std::set<std::string> enabled_experiments;
726 GetSanitizedEnabledFlags(prefs, &enabled_experiments);
727 enabled_experiments.insert(internal_name);
728 SetEnabledFlags(prefs, enabled_experiments);
729 }
[email protected]8a6ff28d2010-12-02 16:35:19730 return;
731 }
732
[email protected]ad2a3ded2010-08-27 13:19:05733 std::set<std::string> enabled_experiments;
[email protected]1a47d7e2010-10-15 00:37:24734 GetSanitizedEnabledFlags(prefs, &enabled_experiments);
[email protected]ad2a3ded2010-08-27 13:19:05735
[email protected]8a6ff28d2010-12-02 16:35:19736 const Experiment* e = NULL;
737 for (size_t i = 0; i < num_experiments; ++i) {
738 if (experiments[i].internal_name == internal_name) {
739 e = experiments + i;
740 break;
741 }
742 }
743 DCHECK(e);
744
745 if (e->type == Experiment::SINGLE_VALUE) {
[email protected]8a2713682011-08-19 10:36:59746 if (enable)
[email protected]8a6ff28d2010-12-02 16:35:19747 enabled_experiments.insert(internal_name);
[email protected]8a2713682011-08-19 10:36:59748 else
[email protected]8a6ff28d2010-12-02 16:35:19749 enabled_experiments.erase(internal_name);
750 } else {
751 if (enable) {
752 // Enable the first choice.
753 enabled_experiments.insert(NameForChoice(*e, 0));
754 } else {
755 // Find the currently enabled choice and disable it.
756 for (int i = 0; i < e->num_choices; ++i) {
757 std::string choice_name = NameForChoice(*e, i);
758 if (enabled_experiments.find(choice_name) !=
759 enabled_experiments.end()) {
760 enabled_experiments.erase(choice_name);
761 // Continue on just in case there's a bug and more than one
762 // experiment for this choice was enabled.
763 }
764 }
765 }
766 }
[email protected]ad2a3ded2010-08-27 13:19:05767
[email protected]1a47d7e2010-10-15 00:37:24768 SetEnabledFlags(prefs, enabled_experiments);
[email protected]ad2a3ded2010-08-27 13:19:05769}
770
[email protected]e2ddbc92010-10-15 20:02:07771void FlagsState::RemoveFlagsSwitches(
772 std::map<std::string, CommandLine::StringType>* switch_list) {
[email protected]a82744532011-02-11 16:15:53773 for (std::map<std::string, std::string>::const_iterator
774 it = flags_switches_.begin(); it != flags_switches_.end(); ++it) {
775 switch_list->erase(it->first);
[email protected]e2ddbc92010-10-15 20:02:07776 }
777}
778
779void FlagsState::reset() {
780 needs_restart_ = false;
781 flags_switches_.clear();
782}
783
[email protected]38e46812011-05-09 20:49:22784} // namespace
[email protected]e2ddbc92010-10-15 20:02:07785
786namespace testing {
[email protected]8a6ff28d2010-12-02 16:35:19787
788// WARNING: '@' is also used in the html file. If you update this constant you
789// also need to update the html file.
790const char kMultiSeparator[] = "@";
791
[email protected]e2ddbc92010-10-15 20:02:07792void ClearState() {
[email protected]8e8bb6d2010-12-13 08:18:55793 FlagsState::GetInstance()->reset();
[email protected]e2ddbc92010-10-15 20:02:07794}
[email protected]a314ee5a2010-10-26 21:23:28795
796void SetExperiments(const Experiment* e, size_t count) {
797 if (!e) {
798 experiments = kExperiments;
799 num_experiments = arraysize(kExperiments);
800 } else {
801 experiments = e;
802 num_experiments = count;
803 }
804}
805
[email protected]8a6ff28d2010-12-02 16:35:19806const Experiment* GetExperiments(size_t* count) {
807 *count = num_experiments;
808 return experiments;
809}
810
[email protected]e2ddbc92010-10-15 20:02:07811} // namespace testing
812
[email protected]1a47d7e2010-10-15 00:37:24813} // namespace about_flags