blob: da5efe0aaf78eb0d984d104e72e3686718b2c1da [file] [log] [blame]
asvitkinebccbb862015-09-04 17:17:451// 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
avi9b6f42932015-12-26 22:15:147#include <stddef.h>
8
asvitkine8423d172015-09-28 23:23:449#include <utility>
asvitkinebccbb862015-09-04 17:17:4510#include <vector>
11
12#include "base/logging.h"
asvitkine8423d172015-09-28 23:23:4413#include "base/metrics/field_trial.h"
asvitkinebccbb862015-09-04 17:17:4514#include "base/strings/string_split.h"
asvitkineb2e44d82015-12-01 04:10:2815#include "base/strings/string_util.h"
asvitkinebccbb862015-09-04 17:17:4516
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
asvitkineb2e44d82015-12-01 04:10:2826// 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) {
asvitkine6d31c52e2016-03-22 15:37:5233 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
asvitkineb2e44d82015-12-01 04:10:2834}
35
asvitkinebccbb862015-09-04 17:17:4536} // namespace
37
changwan5b9da192016-03-31 07:36:1938FeatureList::FeatureList()
39 : initialized_(false),
40 initialized_from_command_line_(false) {
41}
asvitkinebccbb862015-09-04 17:17:4542
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()).
asvitkineb2e44d82015-12-01 04:10:2852 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
53 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
changwan5b9da192016-03-31 07:36:1954
55 initialized_from_command_line_ = true;
asvitkinebccbb862015-09-04 17:17:4556}
57
asvitkine8423d172015-09-28 23:23:4458bool 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
asvitkine8423d172015-09-28 23:23:4466void 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
asvitkine86340192015-12-01 00:45:2986void 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
asvitkine6d31c52e2016-03-22 15:37:52107 // 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.
asvitkine86340192015-12-01 00:45:29110 for (const auto& entry : overrides_) {
asvitkineb2e44d82015-12-01 04:10:28111 std::string* target_list = nullptr;
asvitkine86340192015-12-01 00:45:29112 switch (entry.second.overridden_state) {
asvitkine6d31c52e2016-03-22 15:37:52113 case OVERRIDE_USE_DEFAULT:
asvitkine86340192015-12-01 00:45:29114 case OVERRIDE_ENABLE_FEATURE:
asvitkineb2e44d82015-12-01 04:10:28115 target_list = enable_overrides;
asvitkine86340192015-12-01 00:45:29116 break;
117 case OVERRIDE_DISABLE_FEATURE:
asvitkineb2e44d82015-12-01 04:10:28118 target_list = disable_overrides;
asvitkine86340192015-12-01 00:45:29119 break;
120 }
asvitkineb2e44d82015-12-01 04:10:28121
122 if (!target_list->empty())
123 target_list->push_back(',');
asvitkine6d31c52e2016-03-22 15:37:52124 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
125 target_list->push_back('*');
asvitkineb2e44d82015-12-01 04:10:28126 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 }
asvitkine86340192015-12-01 00:45:29131 }
132}
133
asvitkinebccbb862015-09-04 17:17:45134// static
135bool FeatureList::IsEnabled(const Feature& feature) {
136 return GetInstance()->IsFeatureEnabled(feature);
137}
138
139// static
asvitkine03007d02015-10-21 22:50:06140std::vector<std::string> FeatureList::SplitFeatureListString(
141 const std::string& input) {
142 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
143}
144
145// static
asvitkinee6be55d2016-04-04 23:29:50146bool FeatureList::InitializeInstance(const std::string& enable_features,
changwan5b9da192016-03-31 07:36:19147 const std::string& disable_features) {
148 // We want to initialize a new instance here to support command-line features
149 // in testing better. For example, we initialize a dummy instance in
150 // base/test/test_suite.cc, and override it in content/browser/
151 // browser_main_loop.cc.
152 // On the other hand, we want to avoid re-initialization from command line.
153 // For example, we initialize an instance in chrome/browser/
154 // chrome_browser_main.cc and do not override it in content/browser/
155 // browser_main_loop.cc.
asvitkinee6be55d2016-04-04 23:29:50156 bool instance_existed_before = false;
changwan5b9da192016-03-31 07:36:19157 if (g_instance) {
158 if (g_instance->initialized_from_command_line_)
asvitkinee6be55d2016-04-04 23:29:50159 return false;
changwan5b9da192016-03-31 07:36:19160
161 delete g_instance;
162 g_instance = nullptr;
asvitkinee6be55d2016-04-04 23:29:50163 instance_existed_before = true;
changwan5b9da192016-03-31 07:36:19164 }
165
dcheng093de9b2016-04-04 21:25:51166 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
changwan5b9da192016-03-31 07:36:19167 feature_list->InitializeFromCommandLine(enable_features, disable_features);
168 base::FeatureList::SetInstance(std::move(feature_list));
asvitkinee6be55d2016-04-04 23:29:50169 return !instance_existed_before;
asvitkine9d96abf2015-11-02 21:52:08170}
171
172// static
asvitkinebccbb862015-09-04 17:17:45173FeatureList* FeatureList::GetInstance() {
174 return g_instance;
175}
176
177// static
dcheng093de9b2016-04-04 21:25:51178void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
asvitkinebccbb862015-09-04 17:17:45179 DCHECK(!g_instance);
180 instance->FinalizeInitialization();
181
182 // Note: Intentional leak of global singleton.
183 g_instance = instance.release();
184}
185
186// static
187void FeatureList::ClearInstanceForTesting() {
188 delete g_instance;
189 g_instance = nullptr;
190}
191
192void FeatureList::FinalizeInitialization() {
193 DCHECK(!initialized_);
194 initialized_ = true;
195}
196
197bool FeatureList::IsFeatureEnabled(const Feature& feature) {
198 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28199 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45200 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
201
202 auto it = overrides_.find(feature.name);
203 if (it != overrides_.end()) {
204 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44205
206 // Activate the corresponding field trial, if necessary.
207 if (entry.field_trial)
208 entry.field_trial->group();
209
asvitkinebccbb862015-09-04 17:17:45210 // TODO(asvitkine) Expand this section as more support is added.
asvitkine64e9e112016-03-17 17:32:00211
212 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
213 if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
214 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
asvitkinebccbb862015-09-04 17:17:45215 }
216 // Otherwise, return the default state.
217 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
218}
219
asvitkineb2e44d82015-12-01 04:10:28220void FeatureList::RegisterOverridesFromCommandLine(
221 const std::string& feature_list,
222 OverrideState overridden_state) {
223 for (const auto& value : SplitFeatureListString(feature_list)) {
224 StringPiece feature_name(value);
225 base::FieldTrial* trial = nullptr;
226
227 // The entry may be of the form FeatureName<FieldTrialName - in which case,
228 // this splits off the field trial name and associates it with the override.
229 std::string::size_type pos = feature_name.find('<');
230 if (pos != std::string::npos) {
231 feature_name.set(value.data(), pos);
232 trial = base::FieldTrialList::Find(value.substr(pos + 1));
233 }
234
235 RegisterOverride(feature_name, overridden_state, trial);
236 }
237}
238
239void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44240 OverrideState overridden_state,
241 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45242 DCHECK(!initialized_);
asvitkineb2e44d82015-12-01 04:10:28243 if (field_trial) {
244 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
245 << field_trial->trial_name();
246 }
asvitkine6d31c52e2016-03-22 15:37:52247 if (feature_name.starts_with("*")) {
248 feature_name = feature_name.substr(1);
249 overridden_state = OVERRIDE_USE_DEFAULT;
250 }
asvitkineb2e44d82015-12-01 04:10:28251
asvitkine8423d172015-09-28 23:23:44252 // Note: The semantics of insert() is that it does not overwrite the entry if
253 // one already exists for the key. Thus, only the first override for a given
254 // feature name takes effect.
255 overrides_.insert(std::make_pair(
asvitkineb2e44d82015-12-01 04:10:28256 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45257}
258
259bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
260 AutoLock auto_lock(feature_identity_tracker_lock_);
261
262 auto it = feature_identity_tracker_.find(feature.name);
263 if (it == feature_identity_tracker_.end()) {
264 // If it's not tracked yet, register it.
265 feature_identity_tracker_[feature.name] = &feature;
266 return true;
267 }
268 // Compare address of |feature| to the existing tracked entry.
269 return it->second == &feature;
270}
271
asvitkine8423d172015-09-28 23:23:44272FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
273 FieldTrial* field_trial)
274 : overridden_state(overridden_state),
275 field_trial(field_trial),
276 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45277
278} // namespace base