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