blob: 104bf08539e06b165239eb6ad7442d807fe3b852 [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
asvitkine8423d172015-09-28 23:23:447#include <utility>
asvitkinebccbb862015-09-04 17:17:458#include <vector>
9
10#include "base/logging.h"
asvitkine8423d172015-09-28 23:23:4411#include "base/metrics/field_trial.h"
asvitkinebccbb862015-09-04 17:17:4512#include "base/strings/string_split.h"
asvitkineb2e44d82015-12-01 04:10:2813#include "base/strings/string_util.h"
asvitkinebccbb862015-09-04 17:17:4514
15namespace base {
16
17namespace {
18
19// Pointer to the FeatureList instance singleton that was set via
20// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
21// have more control over initialization timing. Leaky.
22FeatureList* g_instance = nullptr;
23
asvitkineb2e44d82015-12-01 04:10:2824// Some characters are not allowed to appear in feature names or the associated
25// field trial names, as they are used as special characters for command-line
26// serialization. This function checks that the strings are ASCII (since they
27// are used in command-line API functions that require ASCII) and whether there
28// are any reserved characters present, returning true if the string is valid.
29// Only called in DCHECKs.
30bool IsValidFeatureOrFieldTrialName(const std::string& name) {
31 return IsStringASCII(name) && name.find_first_of(",<") == std::string::npos;
32}
33
asvitkinebccbb862015-09-04 17:17:4534} // namespace
35
36FeatureList::FeatureList() : initialized_(false) {}
37
38FeatureList::~FeatureList() {}
39
40void FeatureList::InitializeFromCommandLine(
41 const std::string& enable_features,
42 const std::string& disable_features) {
43 DCHECK(!initialized_);
44
45 // Process disabled features first, so that disabled ones take precedence over
46 // enabled ones (since RegisterOverride() uses insert()).
asvitkineb2e44d82015-12-01 04:10:2847 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
48 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
asvitkinebccbb862015-09-04 17:17:4549}
50
asvitkine8423d172015-09-28 23:23:4451bool FeatureList::IsFeatureOverriddenFromCommandLine(
52 const std::string& feature_name,
53 OverrideState state) const {
54 auto it = overrides_.find(feature_name);
55 return it != overrides_.end() && it->second.overridden_state == state &&
56 !it->second.overridden_by_field_trial;
57}
58
asvitkine8423d172015-09-28 23:23:4459void FeatureList::AssociateReportingFieldTrial(
60 const std::string& feature_name,
61 OverrideState for_overridden_state,
62 FieldTrial* field_trial) {
63 DCHECK(
64 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
65
66 // Only one associated field trial is supported per feature. This is generally
67 // enforced server-side.
68 OverrideEntry* entry = &overrides_.find(feature_name)->second;
69 if (entry->field_trial) {
70 NOTREACHED() << "Feature " << feature_name
71 << " already has trial: " << entry->field_trial->trial_name()
72 << ", associating trial: " << field_trial->trial_name();
73 return;
74 }
75
76 entry->field_trial = field_trial;
77}
78
asvitkine86340192015-12-01 00:45:2979void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
80 OverrideState override_state,
81 FieldTrial* field_trial) {
82 DCHECK(field_trial);
83 DCHECK(!ContainsKey(overrides_, feature_name) ||
84 !overrides_.find(feature_name)->second.field_trial)
85 << "Feature " << feature_name
86 << " has conflicting field trial overrides: "
87 << overrides_.find(feature_name)->second.field_trial->trial_name()
88 << " / " << field_trial->trial_name();
89
90 RegisterOverride(feature_name, override_state, field_trial);
91}
92
93void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
94 std::string* disable_overrides) {
95 DCHECK(initialized_);
96
97 enable_overrides->clear();
98 disable_overrides->clear();
99
100 for (const auto& entry : overrides_) {
asvitkineb2e44d82015-12-01 04:10:28101 std::string* target_list = nullptr;
asvitkine86340192015-12-01 00:45:29102 switch (entry.second.overridden_state) {
103 case OVERRIDE_ENABLE_FEATURE:
asvitkineb2e44d82015-12-01 04:10:28104 target_list = enable_overrides;
asvitkine86340192015-12-01 00:45:29105 break;
106 case OVERRIDE_DISABLE_FEATURE:
asvitkineb2e44d82015-12-01 04:10:28107 target_list = disable_overrides;
asvitkine86340192015-12-01 00:45:29108 break;
109 }
asvitkineb2e44d82015-12-01 04:10:28110
111 if (!target_list->empty())
112 target_list->push_back(',');
113 target_list->append(entry.first);
114 if (entry.second.field_trial) {
115 target_list->push_back('<');
116 target_list->append(entry.second.field_trial->trial_name());
117 }
asvitkine86340192015-12-01 00:45:29118 }
119}
120
asvitkinebccbb862015-09-04 17:17:45121// static
122bool FeatureList::IsEnabled(const Feature& feature) {
123 return GetInstance()->IsFeatureEnabled(feature);
124}
125
126// static
asvitkine03007d02015-10-21 22:50:06127std::vector<std::string> FeatureList::SplitFeatureListString(
128 const std::string& input) {
129 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
130}
131
132// static
asvitkine9d96abf2015-11-02 21:52:08133void FeatureList::InitializeInstance() {
134 if (g_instance)
135 return;
136 SetInstance(make_scoped_ptr(new FeatureList));
137}
138
139// static
asvitkinebccbb862015-09-04 17:17:45140FeatureList* FeatureList::GetInstance() {
141 return g_instance;
142}
143
144// static
145void FeatureList::SetInstance(scoped_ptr<FeatureList> instance) {
146 DCHECK(!g_instance);
147 instance->FinalizeInitialization();
148
149 // Note: Intentional leak of global singleton.
150 g_instance = instance.release();
151}
152
153// static
154void FeatureList::ClearInstanceForTesting() {
155 delete g_instance;
156 g_instance = nullptr;
157}
158
159void FeatureList::FinalizeInitialization() {
160 DCHECK(!initialized_);
161 initialized_ = true;
162}
163
164bool FeatureList::IsFeatureEnabled(const Feature& feature) {
165 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28166 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45167 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
168
169 auto it = overrides_.find(feature.name);
170 if (it != overrides_.end()) {
171 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44172
173 // Activate the corresponding field trial, if necessary.
174 if (entry.field_trial)
175 entry.field_trial->group();
176
asvitkinebccbb862015-09-04 17:17:45177 // TODO(asvitkine) Expand this section as more support is added.
178 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
179 }
180 // Otherwise, return the default state.
181 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
182}
183
asvitkineb2e44d82015-12-01 04:10:28184void FeatureList::RegisterOverridesFromCommandLine(
185 const std::string& feature_list,
186 OverrideState overridden_state) {
187 for (const auto& value : SplitFeatureListString(feature_list)) {
188 StringPiece feature_name(value);
189 base::FieldTrial* trial = nullptr;
190
191 // The entry may be of the form FeatureName<FieldTrialName - in which case,
192 // this splits off the field trial name and associates it with the override.
193 std::string::size_type pos = feature_name.find('<');
194 if (pos != std::string::npos) {
195 feature_name.set(value.data(), pos);
196 trial = base::FieldTrialList::Find(value.substr(pos + 1));
197 }
198
199 RegisterOverride(feature_name, overridden_state, trial);
200 }
201}
202
203void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44204 OverrideState overridden_state,
205 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45206 DCHECK(!initialized_);
asvitkineb2e44d82015-12-01 04:10:28207 if (field_trial) {
208 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
209 << field_trial->trial_name();
210 }
211
asvitkine8423d172015-09-28 23:23:44212 // Note: The semantics of insert() is that it does not overwrite the entry if
213 // one already exists for the key. Thus, only the first override for a given
214 // feature name takes effect.
215 overrides_.insert(std::make_pair(
asvitkineb2e44d82015-12-01 04:10:28216 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45217}
218
219bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
220 AutoLock auto_lock(feature_identity_tracker_lock_);
221
222 auto it = feature_identity_tracker_.find(feature.name);
223 if (it == feature_identity_tracker_.end()) {
224 // If it's not tracked yet, register it.
225 feature_identity_tracker_[feature.name] = &feature;
226 return true;
227 }
228 // Compare address of |feature| to the existing tracked entry.
229 return it->second == &feature;
230}
231
asvitkine8423d172015-09-28 23:23:44232FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
233 FieldTrial* field_trial)
234 : overridden_state(overridden_state),
235 field_trial(field_trial),
236 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45237
238} // namespace base