blob: 46732108dd6fcf0004e14b7ad6da06faf3561718 [file] [log] [blame]
asvitkinea3fa4a72015-09-05 02:17:45 +09001// Copyright 2015 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 "base/feature_list.h"
6
avia6a6a682015-12-27 07:15:14 +09007#include <stddef.h>
8
asvitkine15555a22015-09-29 08:23:44 +09009#include <utility>
asvitkinea3fa4a72015-09-05 02:17:45 +090010#include <vector>
11
12#include "base/logging.h"
asvitkine15555a22015-09-29 08:23:44 +090013#include "base/metrics/field_trial.h"
asvitkinea3fa4a72015-09-05 02:17:45 +090014#include "base/strings/string_split.h"
asvitkine9b310192015-12-01 13:10:28 +090015#include "base/strings/string_util.h"
asvitkinea3fa4a72015-09-05 02:17:45 +090016
17namespace base {
18
19namespace {
20
21// Pointer to the FeatureList instance singleton that was set via
22// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
23// have more control over initialization timing. Leaky.
24FeatureList* g_instance = nullptr;
25
asvitkine9b310192015-12-01 13:10:28 +090026// Some characters are not allowed to appear in feature names or the associated
27// field trial names, as they are used as special characters for command-line
28// serialization. This function checks that the strings are ASCII (since they
29// are used in command-line API functions that require ASCII) and whether there
30// are any reserved characters present, returning true if the string is valid.
31// Only called in DCHECKs.
32bool IsValidFeatureOrFieldTrialName(const std::string& name) {
asvitkinedb103122016-03-23 00:37:52 +090033 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
asvitkine9b310192015-12-01 13:10:28 +090034}
35
asvitkinea3fa4a72015-09-05 02:17:45 +090036} // namespace
37
changwan5cd1c292016-03-31 16:36:19 +090038FeatureList::FeatureList()
39 : initialized_(false),
40 initialized_from_command_line_(false) {
41}
asvitkinea3fa4a72015-09-05 02:17:45 +090042
43FeatureList::~FeatureList() {}
44
45void FeatureList::InitializeFromCommandLine(
46 const std::string& enable_features,
47 const std::string& disable_features) {
48 DCHECK(!initialized_);
49
50 // Process disabled features first, so that disabled ones take precedence over
51 // enabled ones (since RegisterOverride() uses insert()).
asvitkine9b310192015-12-01 13:10:28 +090052 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
53 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
changwan5cd1c292016-03-31 16:36:19 +090054
55 initialized_from_command_line_ = true;
asvitkinea3fa4a72015-09-05 02:17:45 +090056}
57
asvitkine15555a22015-09-29 08:23:44 +090058bool FeatureList::IsFeatureOverriddenFromCommandLine(
59 const std::string& feature_name,
60 OverrideState state) const {
61 auto it = overrides_.find(feature_name);
62 return it != overrides_.end() && it->second.overridden_state == state &&
63 !it->second.overridden_by_field_trial;
64}
65
asvitkine15555a22015-09-29 08:23:44 +090066void FeatureList::AssociateReportingFieldTrial(
67 const std::string& feature_name,
68 OverrideState for_overridden_state,
69 FieldTrial* field_trial) {
70 DCHECK(
71 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
72
73 // Only one associated field trial is supported per feature. This is generally
74 // enforced server-side.
75 OverrideEntry* entry = &overrides_.find(feature_name)->second;
76 if (entry->field_trial) {
77 NOTREACHED() << "Feature " << feature_name
78 << " already has trial: " << entry->field_trial->trial_name()
79 << ", associating trial: " << field_trial->trial_name();
80 return;
81 }
82
83 entry->field_trial = field_trial;
84}
85
asvitkine3ab99942015-12-01 09:45:29 +090086void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
87 OverrideState override_state,
88 FieldTrial* field_trial) {
89 DCHECK(field_trial);
90 DCHECK(!ContainsKey(overrides_, feature_name) ||
91 !overrides_.find(feature_name)->second.field_trial)
92 << "Feature " << feature_name
93 << " has conflicting field trial overrides: "
94 << overrides_.find(feature_name)->second.field_trial->trial_name()
95 << " / " << field_trial->trial_name();
96
97 RegisterOverride(feature_name, override_state, field_trial);
98}
99
100void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
101 std::string* disable_overrides) {
102 DCHECK(initialized_);
103
104 enable_overrides->clear();
105 disable_overrides->clear();
106
asvitkinedb103122016-03-23 00:37:52 +0900107 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
108 // order. This not guaranteed to users of this function, but is useful for
109 // tests to assume the order.
asvitkine3ab99942015-12-01 09:45:29 +0900110 for (const auto& entry : overrides_) {
asvitkine9b310192015-12-01 13:10:28 +0900111 std::string* target_list = nullptr;
asvitkine3ab99942015-12-01 09:45:29 +0900112 switch (entry.second.overridden_state) {
asvitkinedb103122016-03-23 00:37:52 +0900113 case OVERRIDE_USE_DEFAULT:
asvitkine3ab99942015-12-01 09:45:29 +0900114 case OVERRIDE_ENABLE_FEATURE:
asvitkine9b310192015-12-01 13:10:28 +0900115 target_list = enable_overrides;
asvitkine3ab99942015-12-01 09:45:29 +0900116 break;
117 case OVERRIDE_DISABLE_FEATURE:
asvitkine9b310192015-12-01 13:10:28 +0900118 target_list = disable_overrides;
asvitkine3ab99942015-12-01 09:45:29 +0900119 break;
120 }
asvitkine9b310192015-12-01 13:10:28 +0900121
122 if (!target_list->empty())
123 target_list->push_back(',');
asvitkinedb103122016-03-23 00:37:52 +0900124 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
125 target_list->push_back('*');
asvitkine9b310192015-12-01 13:10:28 +0900126 target_list->append(entry.first);
127 if (entry.second.field_trial) {
128 target_list->push_back('<');
129 target_list->append(entry.second.field_trial->trial_name());
130 }
asvitkine3ab99942015-12-01 09:45:29 +0900131 }
132}
133
asvitkinea3fa4a72015-09-05 02:17:45 +0900134// static
135bool FeatureList::IsEnabled(const Feature& feature) {
136 return GetInstance()->IsFeatureEnabled(feature);
137}
138
139// static
jwd7ffa58b2016-05-07 05:39:42 +0900140FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
141 return GetInstance()->GetAssociatedFieldTrial(feature);
142}
143
144// static
asvitkinef530e102015-10-22 07:50:06 +0900145std::vector<std::string> FeatureList::SplitFeatureListString(
146 const std::string& input) {
147 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
148}
149
150// static
asvitkine50786eb2016-04-05 08:29:50 +0900151bool FeatureList::InitializeInstance(const std::string& enable_features,
changwan5cd1c292016-03-31 16:36:19 +0900152 const std::string& disable_features) {
153 // We want to initialize a new instance here to support command-line features
154 // in testing better. For example, we initialize a dummy instance in
155 // base/test/test_suite.cc, and override it in content/browser/
156 // browser_main_loop.cc.
157 // On the other hand, we want to avoid re-initialization from command line.
158 // For example, we initialize an instance in chrome/browser/
159 // chrome_browser_main.cc and do not override it in content/browser/
160 // browser_main_loop.cc.
asvitkine50786eb2016-04-05 08:29:50 +0900161 bool instance_existed_before = false;
changwan5cd1c292016-03-31 16:36:19 +0900162 if (g_instance) {
163 if (g_instance->initialized_from_command_line_)
asvitkine50786eb2016-04-05 08:29:50 +0900164 return false;
changwan5cd1c292016-03-31 16:36:19 +0900165
166 delete g_instance;
167 g_instance = nullptr;
asvitkine50786eb2016-04-05 08:29:50 +0900168 instance_existed_before = true;
changwan5cd1c292016-03-31 16:36:19 +0900169 }
170
dchengcc8e4d82016-04-05 06:25:51 +0900171 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
changwan5cd1c292016-03-31 16:36:19 +0900172 feature_list->InitializeFromCommandLine(enable_features, disable_features);
173 base::FeatureList::SetInstance(std::move(feature_list));
asvitkine50786eb2016-04-05 08:29:50 +0900174 return !instance_existed_before;
asvitkineb5f8a1d2015-11-03 06:52:08 +0900175}
176
177// static
asvitkinea3fa4a72015-09-05 02:17:45 +0900178FeatureList* FeatureList::GetInstance() {
179 return g_instance;
180}
181
182// static
dchengcc8e4d82016-04-05 06:25:51 +0900183void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
asvitkinea3fa4a72015-09-05 02:17:45 +0900184 DCHECK(!g_instance);
185 instance->FinalizeInitialization();
186
187 // Note: Intentional leak of global singleton.
188 g_instance = instance.release();
189}
190
191// static
192void FeatureList::ClearInstanceForTesting() {
193 delete g_instance;
194 g_instance = nullptr;
195}
196
197void FeatureList::FinalizeInitialization() {
198 DCHECK(!initialized_);
199 initialized_ = true;
200}
201
202bool FeatureList::IsFeatureEnabled(const Feature& feature) {
203 DCHECK(initialized_);
asvitkine9b310192015-12-01 13:10:28 +0900204 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinea3fa4a72015-09-05 02:17:45 +0900205 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
206
207 auto it = overrides_.find(feature.name);
208 if (it != overrides_.end()) {
209 const OverrideEntry& entry = it->second;
asvitkine15555a22015-09-29 08:23:44 +0900210
211 // Activate the corresponding field trial, if necessary.
212 if (entry.field_trial)
213 entry.field_trial->group();
214
asvitkinea3fa4a72015-09-05 02:17:45 +0900215 // TODO(asvitkine) Expand this section as more support is added.
asvitkine7b0cfef2016-03-18 02:32:00 +0900216
217 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
218 if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
219 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
asvitkinea3fa4a72015-09-05 02:17:45 +0900220 }
221 // Otherwise, return the default state.
222 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
223}
224
jwd7ffa58b2016-05-07 05:39:42 +0900225FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
226 DCHECK(initialized_);
227 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
228 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
229
230 auto it = overrides_.find(feature.name);
231 if (it != overrides_.end()) {
232 const OverrideEntry& entry = it->second;
233 return entry.field_trial;
234 }
235
236 return nullptr;
237}
238
asvitkine9b310192015-12-01 13:10:28 +0900239void FeatureList::RegisterOverridesFromCommandLine(
240 const std::string& feature_list,
241 OverrideState overridden_state) {
242 for (const auto& value : SplitFeatureListString(feature_list)) {
243 StringPiece feature_name(value);
244 base::FieldTrial* trial = nullptr;
245
246 // The entry may be of the form FeatureName<FieldTrialName - in which case,
247 // this splits off the field trial name and associates it with the override.
248 std::string::size_type pos = feature_name.find('<');
249 if (pos != std::string::npos) {
250 feature_name.set(value.data(), pos);
251 trial = base::FieldTrialList::Find(value.substr(pos + 1));
252 }
253
254 RegisterOverride(feature_name, overridden_state, trial);
255 }
256}
257
258void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine15555a22015-09-29 08:23:44 +0900259 OverrideState overridden_state,
260 FieldTrial* field_trial) {
asvitkinea3fa4a72015-09-05 02:17:45 +0900261 DCHECK(!initialized_);
asvitkine9b310192015-12-01 13:10:28 +0900262 if (field_trial) {
263 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
264 << field_trial->trial_name();
265 }
asvitkinedb103122016-03-23 00:37:52 +0900266 if (feature_name.starts_with("*")) {
267 feature_name = feature_name.substr(1);
268 overridden_state = OVERRIDE_USE_DEFAULT;
269 }
asvitkine9b310192015-12-01 13:10:28 +0900270
asvitkine15555a22015-09-29 08:23:44 +0900271 // Note: The semantics of insert() is that it does not overwrite the entry if
272 // one already exists for the key. Thus, only the first override for a given
273 // feature name takes effect.
274 overrides_.insert(std::make_pair(
asvitkine9b310192015-12-01 13:10:28 +0900275 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinea3fa4a72015-09-05 02:17:45 +0900276}
277
278bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
279 AutoLock auto_lock(feature_identity_tracker_lock_);
280
281 auto it = feature_identity_tracker_.find(feature.name);
282 if (it == feature_identity_tracker_.end()) {
283 // If it's not tracked yet, register it.
284 feature_identity_tracker_[feature.name] = &feature;
285 return true;
286 }
287 // Compare address of |feature| to the existing tracked entry.
288 return it->second == &feature;
289}
290
asvitkine15555a22015-09-29 08:23:44 +0900291FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
292 FieldTrial* field_trial)
293 : overridden_state(overridden_state),
294 field_trial(field_trial),
295 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinea3fa4a72015-09-05 02:17:45 +0900296
297} // namespace base