[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chrome/browser/labs.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <iterator> |
| 9 | #include <map> |
| 10 | #include <set> |
| 11 | |
| 12 | #include "app/l10n_util.h" |
| 13 | #include "base/command_line.h" |
| 14 | #include "base/values.h" |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 15 | #include "chrome/browser/prefs/pref_service.h" |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 16 | #include "chrome/common/chrome_switches.h" |
| 17 | #include "chrome/common/pref_names.h" |
| 18 | #include "grit/generated_resources.h" |
| 19 | |
| 20 | namespace about_labs { |
| 21 | |
| 22 | enum { kOsMac = 1 << 0, kOsWin = 1 << 1, kOsLinux = 1 << 2 }; |
| 23 | |
[email protected] | 65415187 | 2010-09-13 22:43:05 | [diff] [blame] | 24 | unsigned kOsAll = kOsMac | kOsWin | kOsLinux; |
| 25 | |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 26 | struct Experiment { |
| 27 | // The internal name of the experiment. This is never shown to the user. |
| 28 | // It _is_ however stored in the prefs file, so you shouldn't change the |
| 29 | // name of existing labs. |
| 30 | const char* internal_name; |
| 31 | |
| 32 | // String id of the message containing the experiment's name. |
| 33 | int visible_name_id; |
| 34 | |
| 35 | // String id of the message containing the experiment's description. |
| 36 | int visible_description_id; |
| 37 | |
| 38 | // The platforms the experiment is available on |
| 39 | // Needs to be more than a compile-time #ifdef because of profile sync. |
| 40 | unsigned supported_platforms; // bitmask |
| 41 | |
| 42 | // The commandline parameter that's added when this lab is active. This is |
| 43 | // different from |internal_name| so that the commandline flag can be |
| 44 | // renamed without breaking the prefs file. |
| 45 | const char* command_line; |
| 46 | }; |
| 47 | |
| 48 | const Experiment kExperiments[] = { |
| 49 | { |
[email protected] | 232448fb | 2010-08-27 18:48:24 | [diff] [blame] | 50 | "expose-for-tabs", // Do not change; see above. |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 51 | IDS_LABS_TABPOSE_NAME, |
| 52 | IDS_LABS_TABPOSE_DESCRIPTION, |
| 53 | kOsMac, |
| 54 | #if defined(OS_MACOSX) |
| 55 | // The switch exists only on OS X. |
| 56 | switches::kEnableExposeForTabs |
| 57 | #else |
| 58 | "" |
| 59 | #endif |
| 60 | }, |
| 61 | { |
[email protected] | 232448fb | 2010-08-27 18:48:24 | [diff] [blame] | 62 | "vertical-tabs", // Do not change; see above. |
| 63 | IDS_LABS_SIDE_TABS_NAME, |
| 64 | IDS_LABS_SIDE_TABS_DESCRIPTION, |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 65 | kOsWin, |
| 66 | switches::kEnableVerticalTabs |
[email protected] | 65415187 | 2010-09-13 22:43:05 | [diff] [blame] | 67 | }, |
| 68 | { |
| 69 | "tabbed-options", // Do not change; see above. |
| 70 | IDS_LABS_TABBED_OPTIONS_NAME, |
| 71 | IDS_LABS_TABBED_OPTIONS_DESCRIPTION, |
| 72 | kOsAll, |
| 73 | switches::kEnableTabbedOptions |
| 74 | }, |
[email protected] | bcf9167 | 2010-09-16 15:40:21 | [diff] [blame] | 75 | { |
[email protected] | 52fa2d5 | 2010-09-25 14:08:56 | [diff] [blame] | 76 | "remoting", // Do not change; see above. |
| 77 | IDS_LABS_REMOTING_NAME, |
[email protected] | 4fb2f119 | 2010-09-25 14:56:31 | [diff] [blame] | 78 | #if defined(OS_WIN) |
[email protected] | 52fa2d5 | 2010-09-25 14:08:56 | [diff] [blame] | 79 | // Windows only supports host functionality at the moment. |
| 80 | IDS_LABS_REMOTING_HOST_DESCRIPTION, |
| 81 | #elif defined(OS_LINUX) |
| 82 | // Linux only supports client functionality at the moment. |
| 83 | IDS_LABS_REMOTING_CLIENT_DESCRIPTION, |
| 84 | #else |
[email protected] | a694085 | 2010-09-25 14:25:32 | [diff] [blame] | 85 | // On other platforms, this lab isn't available at all. |
| 86 | 0, |
[email protected] | 52fa2d5 | 2010-09-25 14:08:56 | [diff] [blame] | 87 | #endif |
| 88 | kOsWin | kOsLinux, |
| 89 | switches::kEnableRemoting |
| 90 | }, |
[email protected] | a6f0365 | 2010-09-28 15:59:07 | [diff] [blame] | 91 | { |
[email protected] | 57b66d0 | 2010-09-30 11:24:51 | [diff] [blame] | 92 | "disable-outdated-plugins", // Do not change; see above. |
| 93 | IDS_LABS_DISABLE_OUTDATED_PLUGINS_NAME, |
| 94 | IDS_LABS_DISABLE_OUTDATED_PLUGINS_DESCRIPTION, |
| 95 | kOsAll, |
| 96 | switches::kDisableOutdatedPlugins |
| 97 | }, |
[email protected] | b3ce30ea | 2010-10-01 09:33:53 | [diff] [blame] | 98 | { |
| 99 | "xss-auditor", // Do not change; see above. |
| 100 | IDS_LABS_XSS_AUDITOR_NAME, |
| 101 | IDS_LABS_XSS_AUDITOR_DESCRIPTION, |
| 102 | kOsAll, |
| 103 | switches::kEnableXSSAuditor |
| 104 | }, |
[email protected] | aea2ff4 | 2010-10-04 18:04:19 | [diff] [blame] | 105 | { |
| 106 | "background-webapps", // Do not change; see above |
| 107 | IDS_LABS_BACKGROUND_WEBAPPS_NAME, |
| 108 | IDS_LABS_BACKGROUND_WEBAPPS_DESCRIPTION, |
| 109 | kOsAll, |
| 110 | switches::kEnableBackgroundMode |
[email protected] | 7bee0b2 | 2010-10-05 17:00:47 | [diff] [blame] | 111 | }, |
| 112 | { |
| 113 | "cloud-print-proxy", // Do not change; see above. |
| 114 | IDS_LABS_CLOUD_PRINT_PROXY_NAME, |
| 115 | IDS_LABS_CLOUD_PRINT_PROXY_DESCRIPTION, |
[email protected] | 65736d4 | 2010-10-08 16:47:58 | [diff] [blame] | 116 | #if defined(GOOGLE_CHROME_BUILD) |
| 117 | // For a Chrome build, we know we have a PDF plug-in, and so we'll |
| 118 | // enable by platform as we get things working. |
| 119 | 0, |
| 120 | #else |
| 121 | // Otherwise, where we know it could be working if a viable PDF |
| 122 | // plug-in could be supplied, we'll keep the lab enabled. |
[email protected] | 7bee0b2 | 2010-10-05 17:00:47 | [diff] [blame] | 123 | kOsWin, |
[email protected] | 65736d4 | 2010-10-08 16:47:58 | [diff] [blame] | 124 | #endif |
[email protected] | 7bee0b2 | 2010-10-05 17:00:47 | [diff] [blame] | 125 | switches::kEnableCloudPrintProxy |
[email protected] | 8b6588a | 2010-10-12 02:39:42 | [diff] [blame] | 126 | }, |
[email protected] | 580939a | 2010-10-12 18:54:37 | [diff] [blame^] | 127 | { |
| 128 | "match-preview", // Do not change; see above. |
| 129 | IDS_LABS_INSTANT_NAME, |
| 130 | IDS_LABS_INSTANT_DESCRIPTION, |
| 131 | kOsMac, |
| 132 | switches::kEnableMatchPreview |
| 133 | }, |
[email protected] | 8b6588a | 2010-10-12 02:39:42 | [diff] [blame] | 134 | // FIXME(scheib): Add Labs entry for accelerated Compositing, |
| 135 | // or pull it and the strings in generated_resources.grd by Dec 2010 |
| 136 | //{ |
| 137 | // "gpu-compositing", // Do not change; see above |
| 138 | // IDS_LABS_ACCELERATED_COMPOSITING_NAME, |
| 139 | // IDS_LABS_ACCELERATED_COMPOSITING_DESCRIPTION, |
| 140 | // kOsAll, |
| 141 | // switches::kDisableAcceleratedCompositing |
| 142 | //}, |
| 143 | { |
| 144 | "gpu-canvas-2d", // Do not change; see above |
| 145 | IDS_LABS_ACCELERATED_CANVAS_2D_NAME, |
| 146 | IDS_LABS_ACCELERATED_CANVAS_2D_DESCRIPTION, |
| 147 | kOsWin | kOsLinux, |
| 148 | switches::kEnableAccelerated2dCanvas |
[email protected] | aea2ff4 | 2010-10-04 18:04:19 | [diff] [blame] | 149 | } |
[email protected] | 8b6588a | 2010-10-12 02:39:42 | [diff] [blame] | 150 | // FIXME(scheib): Add Labs entry for WebGL, |
| 151 | // or pull it and the strings in generated_resources.grd by Dec 2010 |
| 152 | //{ |
| 153 | // "webgl", // Do not change; see above |
| 154 | // IDS_LABS_WEBGL_NAME, |
| 155 | // IDS_LABS_WEBGL_DESCRIPTION, |
| 156 | // kOsAll, |
| 157 | // switches::kDisableExperimentalWebGL |
| 158 | //} |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 159 | }; |
| 160 | |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 161 | // Extracts the list of enabled lab experiments from preferences and stores them |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 162 | // in a set. |
| 163 | void GetEnabledLabs(const PrefService* prefs, std::set<std::string>* result) { |
| 164 | const ListValue* enabled_experiments = prefs->GetList( |
| 165 | prefs::kEnabledLabsExperiments); |
| 166 | if (!enabled_experiments) |
| 167 | return; |
| 168 | |
| 169 | for (ListValue::const_iterator it = enabled_experiments->begin(); |
| 170 | it != enabled_experiments->end(); |
| 171 | ++it) { |
| 172 | std::string experiment_name; |
| 173 | if (!(*it)->GetAsString(&experiment_name)) { |
| 174 | LOG(WARNING) << "Invalid entry in " << prefs::kEnabledLabsExperiments; |
| 175 | continue; |
| 176 | } |
| 177 | result->insert(experiment_name); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | // Takes a set of enabled lab experiments |
| 182 | void SetEnabledLabs( |
| 183 | PrefService* prefs, const std::set<std::string>& enabled_experiments) { |
| 184 | ListValue* experiments_list = prefs->GetMutableList( |
| 185 | prefs::kEnabledLabsExperiments); |
| 186 | if (!experiments_list) |
| 187 | return; |
| 188 | |
| 189 | experiments_list->Clear(); |
| 190 | for (std::set<std::string>::const_iterator it = enabled_experiments.begin(); |
| 191 | it != enabled_experiments.end(); |
| 192 | ++it) { |
| 193 | experiments_list->Append(new StringValue(*it)); |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | // Removes all experiments from prefs::kEnabledLabsExperiments that are |
| 198 | // unknown, to prevent this list to become very long as experiments are added |
| 199 | // and removed. |
| 200 | void SanitizeList(PrefService* prefs) { |
| 201 | std::set<std::string> known_experiments; |
| 202 | for (size_t i = 0; i < arraysize(kExperiments); ++i) |
| 203 | known_experiments.insert(kExperiments[i].internal_name); |
| 204 | |
| 205 | std::set<std::string> enabled_experiments; |
| 206 | GetEnabledLabs(prefs, &enabled_experiments); |
| 207 | |
| 208 | std::set<std::string> new_enabled_experiments; |
| 209 | std::set_intersection( |
| 210 | known_experiments.begin(), known_experiments.end(), |
| 211 | enabled_experiments.begin(), enabled_experiments.end(), |
| 212 | std::inserter(new_enabled_experiments, new_enabled_experiments.begin())); |
| 213 | |
| 214 | SetEnabledLabs(prefs, new_enabled_experiments); |
| 215 | } |
| 216 | |
| 217 | void GetSanitizedEnabledLabs( |
| 218 | PrefService* prefs, std::set<std::string>* result) { |
| 219 | SanitizeList(prefs); |
| 220 | GetEnabledLabs(prefs, result); |
| 221 | } |
| 222 | |
| 223 | int GetCurrentPlatform() { |
| 224 | #if defined(OS_MACOSX) |
| 225 | return kOsMac; |
| 226 | #elif defined(OS_WIN) |
| 227 | return kOsWin; |
| 228 | #elif defined(OS_LINUX) |
| 229 | return kOsLinux; |
| 230 | #else |
| 231 | #error Unknown platform |
| 232 | #endif |
| 233 | } |
| 234 | |
| 235 | bool IsEnabled() { |
| 236 | #if defined(OS_CHROMEOS) |
| 237 | // ChromeOS uses a different mechanism for about:labs; integrated with their |
| 238 | // dom ui options. |
[email protected] | 9bbf970 | 2010-10-02 00:25:38 | [diff] [blame] | 239 | // TODO(thakis): Port about:labs to chromeos -- http://crbug.com/57634 |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 240 | return false; |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 241 | #else |
| 242 | return true; |
| 243 | #endif |
| 244 | } |
| 245 | |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 246 | void ConvertLabsToSwitches(PrefService* prefs, CommandLine* command_line) { |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 247 | if (!IsEnabled()) |
| 248 | return; |
| 249 | |
| 250 | std::set<std::string> enabled_experiments; |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 251 | GetSanitizedEnabledLabs(prefs, &enabled_experiments); |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 252 | |
| 253 | std::map<std::string, const Experiment*> experiments; |
| 254 | for (size_t i = 0; i < arraysize(kExperiments); ++i) |
| 255 | experiments[kExperiments[i].internal_name] = &kExperiments[i]; |
| 256 | |
| 257 | for (std::set<std::string>::iterator it = enabled_experiments.begin(); |
| 258 | it != enabled_experiments.end(); |
| 259 | ++it) { |
| 260 | const std::string& experiment_name = *it; |
| 261 | std::map<std::string, const Experiment*>::iterator experiment = |
| 262 | experiments.find(experiment_name); |
| 263 | DCHECK(experiment != experiments.end()); |
| 264 | if (experiment == experiments.end()) |
| 265 | continue; |
| 266 | |
| 267 | command_line->AppendSwitch(experiment->second->command_line); |
| 268 | } |
| 269 | } |
| 270 | |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 271 | ListValue* GetLabsExperimentsData(PrefService* prefs) { |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 272 | std::set<std::string> enabled_experiments; |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 273 | GetSanitizedEnabledLabs(prefs, &enabled_experiments); |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 274 | |
| 275 | int current_platform = GetCurrentPlatform(); |
| 276 | |
| 277 | ListValue* experiments_data = new ListValue(); |
| 278 | for (size_t i = 0; i < arraysize(kExperiments); ++i) { |
| 279 | const Experiment& experiment = kExperiments[i]; |
| 280 | if (!(experiment.supported_platforms & current_platform)) |
| 281 | continue; |
| 282 | |
| 283 | DictionaryValue* data = new DictionaryValue(); |
| 284 | data->SetString("internal_name", experiment.internal_name); |
| 285 | data->SetString("name", |
| 286 | l10n_util::GetStringUTF16(experiment.visible_name_id)); |
| 287 | data->SetString("description", |
| 288 | l10n_util::GetStringUTF16( |
| 289 | experiment.visible_description_id)); |
| 290 | data->SetBoolean("enabled", |
| 291 | enabled_experiments.count(experiment.internal_name) > 0); |
| 292 | |
| 293 | experiments_data->Append(data); |
| 294 | } |
| 295 | return experiments_data; |
| 296 | } |
| 297 | |
| 298 | static bool needs_restart_ = false; |
| 299 | |
| 300 | bool IsRestartNeededToCommitChanges() { |
| 301 | return needs_restart_; |
| 302 | } |
| 303 | |
| 304 | void SetExperimentEnabled( |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 305 | PrefService* prefs, const std::string& internal_name, bool enable) { |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 306 | needs_restart_ = true; |
| 307 | |
| 308 | std::set<std::string> enabled_experiments; |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 309 | GetSanitizedEnabledLabs(prefs, &enabled_experiments); |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 310 | |
| 311 | if (enable) |
| 312 | enabled_experiments.insert(internal_name); |
| 313 | else |
| 314 | enabled_experiments.erase(internal_name); |
| 315 | |
[email protected] | c7b7800a | 2010-10-07 18:51:35 | [diff] [blame] | 316 | SetEnabledLabs(prefs, enabled_experiments); |
[email protected] | ad2a3ded | 2010-08-27 13:19:05 | [diff] [blame] | 317 | } |
| 318 | |
| 319 | } // namespace Labs |