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