| 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 |