Index: base/feature_list.cc |
diff --git a/base/feature_list.cc b/base/feature_list.cc |
index c06ba8b3bfdf6be87e55321f08b52a8c71d2bac9..104bf08539e06b165239eb6ad7442d807fe3b852 100644 |
--- a/base/feature_list.cc |
+++ b/base/feature_list.cc |
@@ -10,6 +10,7 @@ |
#include "base/logging.h" |
#include "base/metrics/field_trial.h" |
#include "base/strings/string_split.h" |
+#include "base/strings/string_util.h" |
namespace base { |
@@ -20,6 +21,16 @@ namespace { |
// have more control over initialization timing. Leaky. |
FeatureList* g_instance = nullptr; |
+// Some characters are not allowed to appear in feature names or the associated |
+// field trial names, as they are used as special characters for command-line |
+// serialization. This function checks that the strings are ASCII (since they |
+// are used in command-line API functions that require ASCII) and whether there |
+// are any reserved characters present, returning true if the string is valid. |
+// Only called in DCHECKs. |
+bool IsValidFeatureOrFieldTrialName(const std::string& name) { |
+ return IsStringASCII(name) && name.find_first_of(",<") == std::string::npos; |
+} |
+ |
} // namespace |
FeatureList::FeatureList() : initialized_(false) {} |
@@ -33,12 +44,8 @@ void FeatureList::InitializeFromCommandLine( |
// Process disabled features first, so that disabled ones take precedence over |
// enabled ones (since RegisterOverride() uses insert()). |
- for (const auto& feature_name : SplitFeatureListString(disable_features)) { |
- RegisterOverride(feature_name, OVERRIDE_DISABLE_FEATURE, nullptr); |
- } |
- for (const auto& feature_name : SplitFeatureListString(enable_features)) { |
- RegisterOverride(feature_name, OVERRIDE_ENABLE_FEATURE, nullptr); |
- } |
+ RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); |
+ RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); |
} |
bool FeatureList::IsFeatureOverriddenFromCommandLine( |
@@ -91,18 +98,23 @@ void FeatureList::GetFeatureOverrides(std::string* enable_overrides, |
disable_overrides->clear(); |
for (const auto& entry : overrides_) { |
+ std::string* target_list = nullptr; |
switch (entry.second.overridden_state) { |
case OVERRIDE_ENABLE_FEATURE: |
- if (!enable_overrides->empty()) |
- enable_overrides->push_back(','); |
- enable_overrides->append(entry.first); |
+ target_list = enable_overrides; |
break; |
case OVERRIDE_DISABLE_FEATURE: |
- if (!disable_overrides->empty()) |
- disable_overrides->push_back(','); |
- disable_overrides->append(entry.first); |
+ target_list = disable_overrides; |
break; |
} |
+ |
+ if (!target_list->empty()) |
+ target_list->push_back(','); |
+ target_list->append(entry.first); |
+ if (entry.second.field_trial) { |
+ target_list->push_back('<'); |
+ target_list->append(entry.second.field_trial->trial_name()); |
+ } |
} |
} |
@@ -151,6 +163,7 @@ void FeatureList::FinalizeInitialization() { |
bool FeatureList::IsFeatureEnabled(const Feature& feature) { |
DCHECK(initialized_); |
+ DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; |
DCHECK(CheckFeatureIdentity(feature)) << feature.name; |
auto it = overrides_.find(feature.name); |
@@ -168,15 +181,39 @@ bool FeatureList::IsFeatureEnabled(const Feature& feature) { |
return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; |
} |
-void FeatureList::RegisterOverride(const std::string& feature_name, |
+void FeatureList::RegisterOverridesFromCommandLine( |
+ const std::string& feature_list, |
+ OverrideState overridden_state) { |
+ for (const auto& value : SplitFeatureListString(feature_list)) { |
+ StringPiece feature_name(value); |
+ base::FieldTrial* trial = nullptr; |
+ |
+ // The entry may be of the form FeatureName<FieldTrialName - in which case, |
+ // this splits off the field trial name and associates it with the override. |
+ std::string::size_type pos = feature_name.find('<'); |
+ if (pos != std::string::npos) { |
+ feature_name.set(value.data(), pos); |
+ trial = base::FieldTrialList::Find(value.substr(pos + 1)); |
+ } |
+ |
+ RegisterOverride(feature_name, overridden_state, trial); |
+ } |
+} |
+ |
+void FeatureList::RegisterOverride(StringPiece feature_name, |
OverrideState overridden_state, |
FieldTrial* field_trial) { |
DCHECK(!initialized_); |
+ if (field_trial) { |
+ DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name())) |
+ << field_trial->trial_name(); |
+ } |
+ |
// Note: The semantics of insert() is that it does not overwrite the entry if |
// one already exists for the key. Thus, only the first override for a given |
// feature name takes effect. |
overrides_.insert(std::make_pair( |
- feature_name, OverrideEntry(overridden_state, field_trial))); |
+ feature_name.as_string(), OverrideEntry(overridden_state, field_trial))); |
} |
bool FeatureList::CheckFeatureIdentity(const Feature& feature) { |