blob: a765601ff9737a78b39b742c12cd8b0f2366164e [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"
15#include "chrome/browser/platform_util.h"
16#include "chrome/browser/prefs/pref_service.h"
17#include "chrome/browser/profile.h"
18#include "chrome/common/chrome_switches.h"
19#include "chrome/common/pref_names.h"
20#include "grit/generated_resources.h"
21
22namespace about_labs {
23
24enum { kOsMac = 1 << 0, kOsWin = 1 << 1, kOsLinux = 1 << 2 };
25
[email protected]654151872010-09-13 22:43:0526unsigned kOsAll = kOsMac | kOsWin | kOsLinux;
27
[email protected]ad2a3ded2010-08-27 13:19:0528struct Experiment {
29 // The internal name of the experiment. This is never shown to the user.
30 // It _is_ however stored in the prefs file, so you shouldn't change the
31 // name of existing labs.
32 const char* internal_name;
33
34 // String id of the message containing the experiment's name.
35 int visible_name_id;
36
37 // String id of the message containing the experiment's description.
38 int visible_description_id;
39
40 // The platforms the experiment is available on
41 // Needs to be more than a compile-time #ifdef because of profile sync.
42 unsigned supported_platforms; // bitmask
43
44 // The commandline parameter that's added when this lab is active. This is
45 // different from |internal_name| so that the commandline flag can be
46 // renamed without breaking the prefs file.
47 const char* command_line;
48};
49
50const Experiment kExperiments[] = {
51 {
[email protected]232448fb2010-08-27 18:48:2452 "expose-for-tabs", // Do not change; see above.
[email protected]ad2a3ded2010-08-27 13:19:0553 IDS_LABS_TABPOSE_NAME,
54 IDS_LABS_TABPOSE_DESCRIPTION,
55 kOsMac,
56#if defined(OS_MACOSX)
57 // The switch exists only on OS X.
58 switches::kEnableExposeForTabs
59#else
60 ""
61#endif
62 },
63 {
[email protected]232448fb2010-08-27 18:48:2464 "vertical-tabs", // Do not change; see above.
65 IDS_LABS_SIDE_TABS_NAME,
66 IDS_LABS_SIDE_TABS_DESCRIPTION,
[email protected]ad2a3ded2010-08-27 13:19:0567 kOsWin,
68 switches::kEnableVerticalTabs
[email protected]654151872010-09-13 22:43:0569 },
70 {
71 "tabbed-options", // Do not change; see above.
72 IDS_LABS_TABBED_OPTIONS_NAME,
73 IDS_LABS_TABBED_OPTIONS_DESCRIPTION,
74 kOsAll,
75 switches::kEnableTabbedOptions
76 },
[email protected]bcf91672010-09-16 15:40:2177 {
78 "match-preview", // Do not change; see above.
79 IDS_LABS_INSTANT_NAME,
80 IDS_LABS_INSTANT_DESCRIPTION,
81 kOsWin,
82 switches::kEnableMatchPreview
83 },
[email protected]52fa2d52010-09-25 14:08:5684 {
85 "remoting", // Do not change; see above.
86 IDS_LABS_REMOTING_NAME,
[email protected]4fb2f1192010-09-25 14:56:3187#if defined(OS_WIN)
[email protected]52fa2d52010-09-25 14:08:5688 // Windows only supports host functionality at the moment.
89 IDS_LABS_REMOTING_HOST_DESCRIPTION,
90#elif defined(OS_LINUX)
91 // Linux only supports client functionality at the moment.
92 IDS_LABS_REMOTING_CLIENT_DESCRIPTION,
93#else
[email protected]a6940852010-09-25 14:25:3294 // On other platforms, this lab isn't available at all.
95 0,
[email protected]52fa2d52010-09-25 14:08:5696#endif
97 kOsWin | kOsLinux,
98 switches::kEnableRemoting
99 },
[email protected]a6f03652010-09-28 15:59:07100 {
101 "page-info-bubble", // Do not change; see above.
102 IDS_LABS_PAGE_INFO_BUBBLE_NAME,
103 IDS_LABS_PAGE_INFO_BUBBLE_DESCRIPTION,
104 kOsWin | kOsLinux,
105 switches::kEnableNewPageInfoBubble
106 }
[email protected]57b66d02010-09-30 11:24:51107 {
108 "disable-outdated-plugins", // Do not change; see above.
109 IDS_LABS_DISABLE_OUTDATED_PLUGINS_NAME,
110 IDS_LABS_DISABLE_OUTDATED_PLUGINS_DESCRIPTION,
111 kOsAll,
112 switches::kDisableOutdatedPlugins
113 },
[email protected]ad2a3ded2010-08-27 13:19:05114};
115
116// Extracts the list of enabled lab experiments from a profile and stores them
117// in a set.
118void GetEnabledLabs(const PrefService* prefs, std::set<std::string>* result) {
119 const ListValue* enabled_experiments = prefs->GetList(
120 prefs::kEnabledLabsExperiments);
121 if (!enabled_experiments)
122 return;
123
124 for (ListValue::const_iterator it = enabled_experiments->begin();
125 it != enabled_experiments->end();
126 ++it) {
127 std::string experiment_name;
128 if (!(*it)->GetAsString(&experiment_name)) {
129 LOG(WARNING) << "Invalid entry in " << prefs::kEnabledLabsExperiments;
130 continue;
131 }
132 result->insert(experiment_name);
133 }
134}
135
136// Takes a set of enabled lab experiments
137void SetEnabledLabs(
138 PrefService* prefs, const std::set<std::string>& enabled_experiments) {
139 ListValue* experiments_list = prefs->GetMutableList(
140 prefs::kEnabledLabsExperiments);
141 if (!experiments_list)
142 return;
143
144 experiments_list->Clear();
145 for (std::set<std::string>::const_iterator it = enabled_experiments.begin();
146 it != enabled_experiments.end();
147 ++it) {
148 experiments_list->Append(new StringValue(*it));
149 }
150}
151
152// Removes all experiments from prefs::kEnabledLabsExperiments that are
153// unknown, to prevent this list to become very long as experiments are added
154// and removed.
155void SanitizeList(PrefService* prefs) {
156 std::set<std::string> known_experiments;
157 for (size_t i = 0; i < arraysize(kExperiments); ++i)
158 known_experiments.insert(kExperiments[i].internal_name);
159
160 std::set<std::string> enabled_experiments;
161 GetEnabledLabs(prefs, &enabled_experiments);
162
163 std::set<std::string> new_enabled_experiments;
164 std::set_intersection(
165 known_experiments.begin(), known_experiments.end(),
166 enabled_experiments.begin(), enabled_experiments.end(),
167 std::inserter(new_enabled_experiments, new_enabled_experiments.begin()));
168
169 SetEnabledLabs(prefs, new_enabled_experiments);
170}
171
172void GetSanitizedEnabledLabs(
173 PrefService* prefs, std::set<std::string>* result) {
174 SanitizeList(prefs);
175 GetEnabledLabs(prefs, result);
176}
177
178int GetCurrentPlatform() {
179#if defined(OS_MACOSX)
180 return kOsMac;
181#elif defined(OS_WIN)
182 return kOsWin;
183#elif defined(OS_LINUX)
184 return kOsLinux;
185#else
186#error Unknown platform
187#endif
188}
189
190bool IsEnabled() {
191#if defined(OS_CHROMEOS)
192 // ChromeOS uses a different mechanism for about:labs; integrated with their
193 // dom ui options.
194 return false;
195#elif defined(GOOGLE_CHROME_BUILD)
196 // Don't enable this on the stable channel.
[email protected]a9e8bbe62010-08-27 21:09:07197 return !platform_util::GetVersionStringModifier().empty();
[email protected]ad2a3ded2010-08-27 13:19:05198#else
199 return true;
200#endif
201}
202
203void ConvertLabsToSwitches(Profile* profile, CommandLine* command_line) {
204 // Do not activate labs features on the stable channel.
205 if (!IsEnabled())
206 return;
207
208 std::set<std::string> enabled_experiments;
209 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
210
211 std::map<std::string, const Experiment*> experiments;
212 for (size_t i = 0; i < arraysize(kExperiments); ++i)
213 experiments[kExperiments[i].internal_name] = &kExperiments[i];
214
215 for (std::set<std::string>::iterator it = enabled_experiments.begin();
216 it != enabled_experiments.end();
217 ++it) {
218 const std::string& experiment_name = *it;
219 std::map<std::string, const Experiment*>::iterator experiment =
220 experiments.find(experiment_name);
221 DCHECK(experiment != experiments.end());
222 if (experiment == experiments.end())
223 continue;
224
225 command_line->AppendSwitch(experiment->second->command_line);
226 }
227}
228
229ListValue* GetLabsExperimentsData(Profile* profile) {
230 std::set<std::string> enabled_experiments;
231 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
232
233 int current_platform = GetCurrentPlatform();
234
235 ListValue* experiments_data = new ListValue();
236 for (size_t i = 0; i < arraysize(kExperiments); ++i) {
237 const Experiment& experiment = kExperiments[i];
238 if (!(experiment.supported_platforms & current_platform))
239 continue;
240
241 DictionaryValue* data = new DictionaryValue();
242 data->SetString("internal_name", experiment.internal_name);
243 data->SetString("name",
244 l10n_util::GetStringUTF16(experiment.visible_name_id));
245 data->SetString("description",
246 l10n_util::GetStringUTF16(
247 experiment.visible_description_id));
248 data->SetBoolean("enabled",
249 enabled_experiments.count(experiment.internal_name) > 0);
250
251 experiments_data->Append(data);
252 }
253 return experiments_data;
254}
255
256static bool needs_restart_ = false;
257
258bool IsRestartNeededToCommitChanges() {
259 return needs_restart_;
260}
261
262void SetExperimentEnabled(
263 Profile* profile, const std::string& internal_name, bool enable) {
264 needs_restart_ = true;
265
266 std::set<std::string> enabled_experiments;
267 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
268
269 if (enable)
270 enabled_experiments.insert(internal_name);
271 else
272 enabled_experiments.erase(internal_name);
273
274 SetEnabledLabs(profile->GetPrefs(), enabled_experiments);
275}
276
277} // namespace Labs