blob: ddf05203ef9342c2dd3bf9e90269fa9c75c8adc3 [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,
87#if defined(OS_WINDOWS)
88 // 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]ad2a3ded2010-08-27 13:19:05100};
101
102// Extracts the list of enabled lab experiments from a profile and stores them
103// in a set.
104void GetEnabledLabs(const PrefService* prefs, std::set<std::string>* result) {
105 const ListValue* enabled_experiments = prefs->GetList(
106 prefs::kEnabledLabsExperiments);
107 if (!enabled_experiments)
108 return;
109
110 for (ListValue::const_iterator it = enabled_experiments->begin();
111 it != enabled_experiments->end();
112 ++it) {
113 std::string experiment_name;
114 if (!(*it)->GetAsString(&experiment_name)) {
115 LOG(WARNING) << "Invalid entry in " << prefs::kEnabledLabsExperiments;
116 continue;
117 }
118 result->insert(experiment_name);
119 }
120}
121
122// Takes a set of enabled lab experiments
123void SetEnabledLabs(
124 PrefService* prefs, const std::set<std::string>& enabled_experiments) {
125 ListValue* experiments_list = prefs->GetMutableList(
126 prefs::kEnabledLabsExperiments);
127 if (!experiments_list)
128 return;
129
130 experiments_list->Clear();
131 for (std::set<std::string>::const_iterator it = enabled_experiments.begin();
132 it != enabled_experiments.end();
133 ++it) {
134 experiments_list->Append(new StringValue(*it));
135 }
136}
137
138// Removes all experiments from prefs::kEnabledLabsExperiments that are
139// unknown, to prevent this list to become very long as experiments are added
140// and removed.
141void SanitizeList(PrefService* prefs) {
142 std::set<std::string> known_experiments;
143 for (size_t i = 0; i < arraysize(kExperiments); ++i)
144 known_experiments.insert(kExperiments[i].internal_name);
145
146 std::set<std::string> enabled_experiments;
147 GetEnabledLabs(prefs, &enabled_experiments);
148
149 std::set<std::string> new_enabled_experiments;
150 std::set_intersection(
151 known_experiments.begin(), known_experiments.end(),
152 enabled_experiments.begin(), enabled_experiments.end(),
153 std::inserter(new_enabled_experiments, new_enabled_experiments.begin()));
154
155 SetEnabledLabs(prefs, new_enabled_experiments);
156}
157
158void GetSanitizedEnabledLabs(
159 PrefService* prefs, std::set<std::string>* result) {
160 SanitizeList(prefs);
161 GetEnabledLabs(prefs, result);
162}
163
164int GetCurrentPlatform() {
165#if defined(OS_MACOSX)
166 return kOsMac;
167#elif defined(OS_WIN)
168 return kOsWin;
169#elif defined(OS_LINUX)
170 return kOsLinux;
171#else
172#error Unknown platform
173#endif
174}
175
176bool IsEnabled() {
177#if defined(OS_CHROMEOS)
178 // ChromeOS uses a different mechanism for about:labs; integrated with their
179 // dom ui options.
180 return false;
181#elif defined(GOOGLE_CHROME_BUILD)
182 // Don't enable this on the stable channel.
[email protected]a9e8bbe62010-08-27 21:09:07183 return !platform_util::GetVersionStringModifier().empty();
[email protected]ad2a3ded2010-08-27 13:19:05184#else
185 return true;
186#endif
187}
188
189void ConvertLabsToSwitches(Profile* profile, CommandLine* command_line) {
190 // Do not activate labs features on the stable channel.
191 if (!IsEnabled())
192 return;
193
194 std::set<std::string> enabled_experiments;
195 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
196
197 std::map<std::string, const Experiment*> experiments;
198 for (size_t i = 0; i < arraysize(kExperiments); ++i)
199 experiments[kExperiments[i].internal_name] = &kExperiments[i];
200
201 for (std::set<std::string>::iterator it = enabled_experiments.begin();
202 it != enabled_experiments.end();
203 ++it) {
204 const std::string& experiment_name = *it;
205 std::map<std::string, const Experiment*>::iterator experiment =
206 experiments.find(experiment_name);
207 DCHECK(experiment != experiments.end());
208 if (experiment == experiments.end())
209 continue;
210
211 command_line->AppendSwitch(experiment->second->command_line);
212 }
213}
214
215ListValue* GetLabsExperimentsData(Profile* profile) {
216 std::set<std::string> enabled_experiments;
217 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
218
219 int current_platform = GetCurrentPlatform();
220
221 ListValue* experiments_data = new ListValue();
222 for (size_t i = 0; i < arraysize(kExperiments); ++i) {
223 const Experiment& experiment = kExperiments[i];
224 if (!(experiment.supported_platforms & current_platform))
225 continue;
226
227 DictionaryValue* data = new DictionaryValue();
228 data->SetString("internal_name", experiment.internal_name);
229 data->SetString("name",
230 l10n_util::GetStringUTF16(experiment.visible_name_id));
231 data->SetString("description",
232 l10n_util::GetStringUTF16(
233 experiment.visible_description_id));
234 data->SetBoolean("enabled",
235 enabled_experiments.count(experiment.internal_name) > 0);
236
237 experiments_data->Append(data);
238 }
239 return experiments_data;
240}
241
242static bool needs_restart_ = false;
243
244bool IsRestartNeededToCommitChanges() {
245 return needs_restart_;
246}
247
248void SetExperimentEnabled(
249 Profile* profile, const std::string& internal_name, bool enable) {
250 needs_restart_ = true;
251
252 std::set<std::string> enabled_experiments;
253 GetSanitizedEnabledLabs(profile->GetPrefs(), &enabled_experiments);
254
255 if (enable)
256 enabled_experiments.insert(internal_name);
257 else
258 enabled_experiments.erase(internal_name);
259
260 SetEnabledLabs(profile->GetPrefs(), enabled_experiments);
261}
262
263} // namespace Labs