blob: dcb76f54e66e860205d869365de97f0af9b4bd9e [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
changwan5b9da192016-03-31 07:36:19146void FeatureList::InitializeInstance(const std::string& enable_features,
147 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.
156 if (g_instance) {
157 if (g_instance->initialized_from_command_line_)
158 return;
159
160 delete g_instance;
161 g_instance = nullptr;
162 }
163
dcheng093de9b2016-04-04 21:25:51164 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
changwan5b9da192016-03-31 07:36:19165 feature_list->InitializeFromCommandLine(enable_features, disable_features);
166 base::FeatureList::SetInstance(std::move(feature_list));
asvitkine9d96abf2015-11-02 21:52:08167}
168
169// static
asvitkinebccbb862015-09-04 17:17:45170FeatureList* FeatureList::GetInstance() {
171 return g_instance;
172}
173
174// static
dcheng093de9b2016-04-04 21:25:51175void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
asvitkinebccbb862015-09-04 17:17:45176 DCHECK(!g_instance);
177 instance->FinalizeInitialization();
178
179 // Note: Intentional leak of global singleton.
180 g_instance = instance.release();
181}
182
183// static
184void FeatureList::ClearInstanceForTesting() {
185 delete g_instance;
186 g_instance = nullptr;
187}
188
189void FeatureList::FinalizeInitialization() {
190 DCHECK(!initialized_);
191 initialized_ = true;
192}
193
194bool FeatureList::IsFeatureEnabled(const Feature& feature) {
195 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28196 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45197 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
198
199 auto it = overrides_.find(feature.name);
200 if (it != overrides_.end()) {
201 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44202
203 // Activate the corresponding field trial, if necessary.
204 if (entry.field_trial)
205 entry.field_trial->group();
206
asvitkinebccbb862015-09-04 17:17:45207 // TODO(asvitkine) Expand this section as more support is added.
asvitkine64e9e112016-03-17 17:32:00208
209 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
210 if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
211 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
asvitkinebccbb862015-09-04 17:17:45212 }
213 // Otherwise, return the default state.
214 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
215}
216
asvitkineb2e44d82015-12-01 04:10:28217void FeatureList::RegisterOverridesFromCommandLine(
218 const std::string& feature_list,
219 OverrideState overridden_state) {
220 for (const auto& value : SplitFeatureListString(feature_list)) {
221 StringPiece feature_name(value);
222 base::FieldTrial* trial = nullptr;
223
224 // The entry may be of the form FeatureName<FieldTrialName - in which case,
225 // this splits off the field trial name and associates it with the override.
226 std::string::size_type pos = feature_name.find('<');
227 if (pos != std::string::npos) {
228 feature_name.set(value.data(), pos);
229 trial = base::FieldTrialList::Find(value.substr(pos + 1));
230 }
231
232 RegisterOverride(feature_name, overridden_state, trial);
233 }
234}
235
236void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44237 OverrideState overridden_state,
238 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45239 DCHECK(!initialized_);
asvitkineb2e44d82015-12-01 04:10:28240 if (field_trial) {
241 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
242 << field_trial->trial_name();
243 }
asvitkine6d31c52e2016-03-22 15:37:52244 if (feature_name.starts_with("*")) {
245 feature_name = feature_name.substr(1);
246 overridden_state = OVERRIDE_USE_DEFAULT;
247 }
asvitkineb2e44d82015-12-01 04:10:28248
asvitkine8423d172015-09-28 23:23:44249 // Note: The semantics of insert() is that it does not overwrite the entry if
250 // one already exists for the key. Thus, only the first override for a given
251 // feature name takes effect.
252 overrides_.insert(std::make_pair(
asvitkineb2e44d82015-12-01 04:10:28253 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45254}
255
256bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
257 AutoLock auto_lock(feature_identity_tracker_lock_);
258
259 auto it = feature_identity_tracker_.find(feature.name);
260 if (it == feature_identity_tracker_.end()) {
261 // If it's not tracked yet, register it.
262 feature_identity_tracker_[feature.name] = &feature;
263 return true;
264 }
265 // Compare address of |feature| to the existing tracked entry.
266 return it->second == &feature;
267}
268
asvitkine8423d172015-09-28 23:23:44269FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
270 FieldTrial* field_trial)
271 : overridden_state(overridden_state),
272 field_trial(field_trial),
273 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45274
275} // namespace base