OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/feature_list.h" | 5 #include "base/feature_list.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/metrics/field_trial.h" | 11 #include "base/metrics/field_trial.h" |
12 #include "base/strings/string_split.h" | 12 #include "base/strings/string_split.h" |
| 13 #include "base/strings/string_util.h" |
13 | 14 |
14 namespace base { | 15 namespace base { |
15 | 16 |
16 namespace { | 17 namespace { |
17 | 18 |
18 // Pointer to the FeatureList instance singleton that was set via | 19 // Pointer to the FeatureList instance singleton that was set via |
19 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to | 20 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to |
20 // have more control over initialization timing. Leaky. | 21 // have more control over initialization timing. Leaky. |
21 FeatureList* g_instance = nullptr; | 22 FeatureList* g_instance = nullptr; |
22 | 23 |
| 24 // 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. |
| 30 bool IsValidFeatureOrFieldTrialName(const std::string& name) { |
| 31 return IsStringASCII(name) && name.find_first_of(",<") == std::string::npos; |
| 32 } |
| 33 |
23 } // namespace | 34 } // namespace |
24 | 35 |
25 FeatureList::FeatureList() : initialized_(false) {} | 36 FeatureList::FeatureList() : initialized_(false) {} |
26 | 37 |
27 FeatureList::~FeatureList() {} | 38 FeatureList::~FeatureList() {} |
28 | 39 |
29 void FeatureList::InitializeFromCommandLine( | 40 void FeatureList::InitializeFromCommandLine( |
30 const std::string& enable_features, | 41 const std::string& enable_features, |
31 const std::string& disable_features) { | 42 const std::string& disable_features) { |
32 DCHECK(!initialized_); | 43 DCHECK(!initialized_); |
33 | 44 |
34 // Process disabled features first, so that disabled ones take precedence over | 45 // Process disabled features first, so that disabled ones take precedence over |
35 // enabled ones (since RegisterOverride() uses insert()). | 46 // enabled ones (since RegisterOverride() uses insert()). |
36 for (const auto& feature_name : SplitFeatureListString(disable_features)) { | 47 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); |
37 RegisterOverride(feature_name, OVERRIDE_DISABLE_FEATURE, nullptr); | 48 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); |
38 } | |
39 for (const auto& feature_name : SplitFeatureListString(enable_features)) { | |
40 RegisterOverride(feature_name, OVERRIDE_ENABLE_FEATURE, nullptr); | |
41 } | |
42 } | 49 } |
43 | 50 |
44 bool FeatureList::IsFeatureOverriddenFromCommandLine( | 51 bool FeatureList::IsFeatureOverriddenFromCommandLine( |
45 const std::string& feature_name, | 52 const std::string& feature_name, |
46 OverrideState state) const { | 53 OverrideState state) const { |
47 auto it = overrides_.find(feature_name); | 54 auto it = overrides_.find(feature_name); |
48 return it != overrides_.end() && it->second.overridden_state == state && | 55 return it != overrides_.end() && it->second.overridden_state == state && |
49 !it->second.overridden_by_field_trial; | 56 !it->second.overridden_by_field_trial; |
50 } | 57 } |
51 | 58 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 } | 91 } |
85 | 92 |
86 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, | 93 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, |
87 std::string* disable_overrides) { | 94 std::string* disable_overrides) { |
88 DCHECK(initialized_); | 95 DCHECK(initialized_); |
89 | 96 |
90 enable_overrides->clear(); | 97 enable_overrides->clear(); |
91 disable_overrides->clear(); | 98 disable_overrides->clear(); |
92 | 99 |
93 for (const auto& entry : overrides_) { | 100 for (const auto& entry : overrides_) { |
| 101 std::string* target_list = nullptr; |
94 switch (entry.second.overridden_state) { | 102 switch (entry.second.overridden_state) { |
95 case OVERRIDE_ENABLE_FEATURE: | 103 case OVERRIDE_ENABLE_FEATURE: |
96 if (!enable_overrides->empty()) | 104 target_list = enable_overrides; |
97 enable_overrides->push_back(','); | |
98 enable_overrides->append(entry.first); | |
99 break; | 105 break; |
100 case OVERRIDE_DISABLE_FEATURE: | 106 case OVERRIDE_DISABLE_FEATURE: |
101 if (!disable_overrides->empty()) | 107 target_list = disable_overrides; |
102 disable_overrides->push_back(','); | |
103 disable_overrides->append(entry.first); | |
104 break; | 108 break; |
105 } | 109 } |
| 110 |
| 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 } |
106 } | 118 } |
107 } | 119 } |
108 | 120 |
109 // static | 121 // static |
110 bool FeatureList::IsEnabled(const Feature& feature) { | 122 bool FeatureList::IsEnabled(const Feature& feature) { |
111 return GetInstance()->IsFeatureEnabled(feature); | 123 return GetInstance()->IsFeatureEnabled(feature); |
112 } | 124 } |
113 | 125 |
114 // static | 126 // static |
115 std::vector<std::string> FeatureList::SplitFeatureListString( | 127 std::vector<std::string> FeatureList::SplitFeatureListString( |
(...skipping 28 matching lines...) Expand all Loading... |
144 g_instance = nullptr; | 156 g_instance = nullptr; |
145 } | 157 } |
146 | 158 |
147 void FeatureList::FinalizeInitialization() { | 159 void FeatureList::FinalizeInitialization() { |
148 DCHECK(!initialized_); | 160 DCHECK(!initialized_); |
149 initialized_ = true; | 161 initialized_ = true; |
150 } | 162 } |
151 | 163 |
152 bool FeatureList::IsFeatureEnabled(const Feature& feature) { | 164 bool FeatureList::IsFeatureEnabled(const Feature& feature) { |
153 DCHECK(initialized_); | 165 DCHECK(initialized_); |
| 166 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; |
154 DCHECK(CheckFeatureIdentity(feature)) << feature.name; | 167 DCHECK(CheckFeatureIdentity(feature)) << feature.name; |
155 | 168 |
156 auto it = overrides_.find(feature.name); | 169 auto it = overrides_.find(feature.name); |
157 if (it != overrides_.end()) { | 170 if (it != overrides_.end()) { |
158 const OverrideEntry& entry = it->second; | 171 const OverrideEntry& entry = it->second; |
159 | 172 |
160 // Activate the corresponding field trial, if necessary. | 173 // Activate the corresponding field trial, if necessary. |
161 if (entry.field_trial) | 174 if (entry.field_trial) |
162 entry.field_trial->group(); | 175 entry.field_trial->group(); |
163 | 176 |
164 // TODO(asvitkine) Expand this section as more support is added. | 177 // TODO(asvitkine) Expand this section as more support is added. |
165 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; | 178 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; |
166 } | 179 } |
167 // Otherwise, return the default state. | 180 // Otherwise, return the default state. |
168 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; | 181 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; |
169 } | 182 } |
170 | 183 |
171 void FeatureList::RegisterOverride(const std::string& feature_name, | 184 void 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 |
| 203 void FeatureList::RegisterOverride(StringPiece feature_name, |
172 OverrideState overridden_state, | 204 OverrideState overridden_state, |
173 FieldTrial* field_trial) { | 205 FieldTrial* field_trial) { |
174 DCHECK(!initialized_); | 206 DCHECK(!initialized_); |
| 207 if (field_trial) { |
| 208 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name())) |
| 209 << field_trial->trial_name(); |
| 210 } |
| 211 |
175 // Note: The semantics of insert() is that it does not overwrite the entry if | 212 // Note: The semantics of insert() is that it does not overwrite the entry if |
176 // one already exists for the key. Thus, only the first override for a given | 213 // one already exists for the key. Thus, only the first override for a given |
177 // feature name takes effect. | 214 // feature name takes effect. |
178 overrides_.insert(std::make_pair( | 215 overrides_.insert(std::make_pair( |
179 feature_name, OverrideEntry(overridden_state, field_trial))); | 216 feature_name.as_string(), OverrideEntry(overridden_state, field_trial))); |
180 } | 217 } |
181 | 218 |
182 bool FeatureList::CheckFeatureIdentity(const Feature& feature) { | 219 bool FeatureList::CheckFeatureIdentity(const Feature& feature) { |
183 AutoLock auto_lock(feature_identity_tracker_lock_); | 220 AutoLock auto_lock(feature_identity_tracker_lock_); |
184 | 221 |
185 auto it = feature_identity_tracker_.find(feature.name); | 222 auto it = feature_identity_tracker_.find(feature.name); |
186 if (it == feature_identity_tracker_.end()) { | 223 if (it == feature_identity_tracker_.end()) { |
187 // If it's not tracked yet, register it. | 224 // If it's not tracked yet, register it. |
188 feature_identity_tracker_[feature.name] = &feature; | 225 feature_identity_tracker_[feature.name] = &feature; |
189 return true; | 226 return true; |
190 } | 227 } |
191 // Compare address of |feature| to the existing tracked entry. | 228 // Compare address of |feature| to the existing tracked entry. |
192 return it->second == &feature; | 229 return it->second == &feature; |
193 } | 230 } |
194 | 231 |
195 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, | 232 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, |
196 FieldTrial* field_trial) | 233 FieldTrial* field_trial) |
197 : overridden_state(overridden_state), | 234 : overridden_state(overridden_state), |
198 field_trial(field_trial), | 235 field_trial(field_trial), |
199 overridden_by_field_trial(field_trial != nullptr) {} | 236 overridden_by_field_trial(field_trial != nullptr) {} |
200 | 237 |
201 } // namespace base | 238 } // namespace base |
OLD | NEW |