blob: 8ffd32eeddc023a873bce1a2a2ddee1d7b27f420 [file] [log] [blame]
[email protected]ad2a3ded2010-08-27 13:19:051// 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]ad2a3ded2010-08-27 13:19:0515#include "chrome/browser/prefs/pref_service.h"
16#include "chrome/browser/profile.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/common/pref_names.h"
19#include "grit/generated_resources.h"
20
21namespace about_labs {
22
23enum { kOsMac = 1 << 0, kOsWin = 1 << 1, kOsLinux = 1 << 2 };
24
[email protected]654151872010-09-13 22:43:0525unsigned kOsAll = kOsMac | kOsWin | kOsLinux;
26
[email protected]ad2a3ded2010-08-27 13:19:0527struct Experiment {
28 // The internal name of the experiment. This is never shown to the user.
29 // It _is_ however stored in the prefs file, so you shouldn't change the
30 // name of existing labs.
31 const char* internal_name;
32
33 // String id of the message containing the experiment's name.
34 int visible_name_id;
35
36 // String id of the message containing the experiment's description.
37 int visible_description_id;
38
39 // The platforms the experiment is available on
40 // Needs to be more than a compile-time #ifdef because of profile sync.
41 unsigned supported_platforms; // bitmask
42
43 // The commandline parameter that's added when this lab is active. This is
44 // different from |internal_name| so that the commandline flag can be
45 // renamed without breaking the prefs file.
46 const char* command_line;
47};
48
49const Experiment kExperiments[] = {
50 {
[email protected]232448fb2010-08-27 18:48:2451 "expose-for-tabs", // Do not change; see above.
[email protected]ad2a3ded2010-08-27 13:19:0552 IDS_LABS_TABPOSE_NAME,
53 IDS_LABS_TABPOSE_DESCRIPTION,
54 kOsMac,
55#if defined(OS_MACOSX)
56 // The switch exists only on OS X.
57 switches::kEnableExposeForTabs
58#else
59 ""
60#endif
61 },
62 {
[email protected]232448fb2010-08-27 18:48:2463 "vertical-tabs", // Do not change; see above.
64 IDS_LABS_SIDE_TABS_NAME,
65 IDS_LABS_SIDE_TABS_DESCRIPTION,
[email protected]ad2a3ded2010-08-27 13:19:0566 kOsWin,
67 switches::kEnableVerticalTabs
[email protected]654151872010-09-13 22:43:0568 },
69 {
70 "tabbed-options", // Do not change; see above.
71 IDS_LABS_TABBED_OPTIONS_NAME,
72 IDS_LABS_TABBED_OPTIONS_DESCRIPTION,
73 kOsAll,
74 switches::kEnableTabbedOptions
75 },
[email protected]bcf91672010-09-16 15:40:2176 {
77 "match-preview", // Do not change; see above.
78 IDS_LABS_INSTANT_NAME,
79 IDS_LABS_INSTANT_DESCRIPTION,
80 kOsWin,
81 switches::kEnableMatchPreview
82 },
[email protected]52fa2d52010-09-25 14:08:5683 {
84 "remoting", // Do not change; see above.
85 IDS_LABS_REMOTING_NAME,
[email protected]4fb2f1192010-09-25 14:56:3186#if defined(OS_WIN)
[email protected]52fa2d52010-09-25 14:08:5687 // Windows only supports host functionality at the moment.
88 IDS_LABS_REMOTING_HOST_DESCRIPTION,
89#elif defined(OS_LINUX)
90 // Linux only supports client functionality at the moment.
91 IDS_LABS_REMOTING_CLIENT_DESCRIPTION,
92#else
[email protected]a6940852010-09-25 14:25:3293 // On other platforms, this lab isn't available at all.
94 0,
[email protected]52fa2d52010-09-25 14:08:5695#endif
96 kOsWin | kOsLinux,
97 switches::kEnableRemoting
98 },
[email protected]a6f03652010-09-28 15:59:0799 {
100 "page-info-bubble", // Do not change; see above.
101 IDS_LABS_PAGE_INFO_BUBBLE_NAME,
102 IDS_LABS_PAGE_INFO_BUBBLE_DESCRIPTION,
[email protected]440cb532010-09-30 17:32:28103 kOsAll,
[email protected]a6f03652010-09-28 15:59:07104 switches::kEnableNewPageInfoBubble
[email protected]2e0af762010-09-30 11:34:32105 },
[email protected]57b66d02010-09-30 11:24:51106 {
107 "disable-outdated-plugins", // Do not change; see above.
108 IDS_LABS_DISABLE_OUTDATED_PLUGINS_NAME,
109 IDS_LABS_DISABLE_OUTDATED_PLUGINS_DESCRIPTION,
110 kOsAll,
111 switches::kDisableOutdatedPlugins
112 },
[email protected]b3ce30ea2010-10-01 09:33:53113 {
114 "xss-auditor", // Do not change; see above.
115 IDS_LABS_XSS_AUDITOR_NAME,
116 IDS_LABS_XSS_AUDITOR_DESCRIPTION,
117 kOsAll,
118 switches::kEnableXSSAuditor
119 },
[email protected]aea2ff42010-10-04 18:04:19120 {
121 "background-webapps", // Do not change; see above
122 IDS_LABS_BACKGROUND_WEBAPPS_NAME,
123 IDS_LABS_BACKGROUND_WEBAPPS_DESCRIPTION,
124 kOsAll,
125 switches::kEnableBackgroundMode
126 }
[email protected]ad2a3ded2010-08-27 13:19:05127};
128
129// Extracts the list of enabled lab experiments from a profile and stores them
130// in a set.
131void GetEnabledLabs(const PrefService* prefs, std::set<std::string>* result) {
132 const ListValue* enabled_experiments = prefs->GetList(
133 prefs::kEnabledLabsExperiments);
134 if (!enabled_experiments)
135 return;
136
137 for (ListValue::const_iterator it = enabled_experiments->begin();
138 it != enabled_experiments->end();
139 ++it) {
140 std::string experiment_name;
141 if (!(*it)->GetAsString(&experiment_name)) {
142 LOG(WARNING) << "Invalid entry in " << prefs::kEnabledLabsExperiments;
143 continue;
144 }
145 result->insert(experiment_name);
146 }
147}
148
149// Takes a set of enabled lab experiments
150void SetEnabledLabs(
151 PrefService* prefs, const std::set<std::string>& enabled_experiments) {
152 ListValue* experiments_list = prefs->GetMutableList(
153 prefs::kEnabledLabsExperiments);
154 if (!experiments_list)
155 return;
156
157 experiments_list->Clear();
158 for (std::set<std::string>::const_iterator it = enabled_experiments.begin();
159 it != enabled_experiments.end();
160 ++it) {
161 experiments_list->Append(new StringValue(*it));
162 }
163}
164
165// Removes all experiments from prefs::kEnabledLabsExperiments that are
166// unknown, to prevent this list to become very long as experiments are added
167// and removed.
168void SanitizeList(PrefService* prefs) {
169 std::set<std::string> known_experiments;
170 for (size_t i = 0; i < arraysize(kExperiments); ++i)
171 known_experiments.insert(kExperiments[i].internal_name);
172
173 std::set<std::string> enabled_experiments;
174 GetEnabledLabs(prefs, &enabled_experiments);
175
176 std::set<std::string> new_enabled_experiments;
177 std::set_intersection(
178 known_experiments.begin(), known_experiments.end(),
179 enabled_experiments.begin(), enabled_experiments.end(),
180 std::inserter(new_enabled_experiments, new_enabled_experiments.begin()));
181
182 SetEnabledLabs(prefs, new_enabled_experiments);
183}
184
185void GetSanitizedEnabledLabs(
186 PrefService* prefs, std::set<std::string>* result) {
187 SanitizeList(prefs);
188 GetEnabledLabs(prefs, result);
189}
190
191int GetCurrentPlatform() {
192#if defined(OS_MACOSX)
193 return kOsMac;
194#elif defined(OS_WIN)
195 return kOsWin;
196#elif defined(OS_LINUX)
197 return kOsLinux;
198#else
199#error Unknown platform
200#endif
201}
202
203bool IsEnabled() {
204#if defined(OS_CHROMEOS)
205 // ChromeOS uses a different mechanism for about:labs; integrated with their
206 // dom ui options.
[email protected]9bbf9702010-10-02 00:25:38207 // TODO(thakis): Port about:labs to chromeos -- http://crbug.com/57634
[email protected]ad2a3ded2010-08-27 13:19:05208 return false;
[email protected]ad2a3ded2010-08-27 13:19:05209#else
210 return true;
211#endif
212}
213
214void ConvertLabsToSwitches(Profile* profile, CommandLine* command_line) {
[email protected]ad2a3ded2010-08-27 13:19:05215 if (!IsEnabled())
216 return;
217
218 std::set<std::string> enabled_experiments;
219 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
220
221 std::map<std::string, const Experiment*> experiments;
222 for (size_t i = 0; i < arraysize(kExperiments); ++i)
223 experiments[kExperiments[i].internal_name] = &kExperiments[i];
224
225 for (std::set<std::string>::iterator it = enabled_experiments.begin();
226 it != enabled_experiments.end();
227 ++it) {
228 const std::string& experiment_name = *it;
229 std::map<std::string, const Experiment*>::iterator experiment =
230 experiments.find(experiment_name);
231 DCHECK(experiment != experiments.end());
232 if (experiment == experiments.end())
233 continue;
234
235 command_line->AppendSwitch(experiment->second->command_line);
236 }
237}
238
239ListValue* GetLabsExperimentsData(Profile* profile) {
240 std::set<std::string> enabled_experiments;
241 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
242
243 int current_platform = GetCurrentPlatform();
244
245 ListValue* experiments_data = new ListValue();
246 for (size_t i = 0; i < arraysize(kExperiments); ++i) {
247 const Experiment& experiment = kExperiments[i];
248 if (!(experiment.supported_platforms & current_platform))
249 continue;
250
251 DictionaryValue* data = new DictionaryValue();
252 data->SetString("internal_name", experiment.internal_name);
253 data->SetString("name",
254 l10n_util::GetStringUTF16(experiment.visible_name_id));
255 data->SetString("description",
256 l10n_util::GetStringUTF16(
257 experiment.visible_description_id));
258 data->SetBoolean("enabled",
259 enabled_experiments.count(experiment.internal_name) > 0);
260
261 experiments_data->Append(data);
262 }
263 return experiments_data;
264}
265
266static bool needs_restart_ = false;
267
268bool IsRestartNeededToCommitChanges() {
269 return needs_restart_;
270}
271
272void SetExperimentEnabled(
273 Profile* profile, const std::string& internal_name, bool enable) {
274 needs_restart_ = true;
275
276 std::set<std::string> enabled_experiments;
277 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
278
279 if (enable)
280 enabled_experiments.insert(internal_name);
281 else
282 enabled_experiments.erase(internal_name);
283
284 SetEnabledLabs(profile->GetPrefs(), enabled_experiments);
285}
286
287} // namespace Labs