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 3 // found in the LICENSE file. 4 5 #include "base/feature_list.h" 6 7 #include <stddef.h> 8 9 #include <utility> 10 #include <vector> 11 12 #include "base/logging.h" 13 #include "base/metrics/field_trial.h" 14 #include "base/strings/string_split.h" 15 #include "base/strings/string_util.h" 16 17 namespace base { 18 19 namespace { 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. 24 FeatureList* g_instance = nullptr; 25 26 // Tracks whether the FeatureList instance was initialized via an accessor. 27 bool g_initialized_from_accessor = false; 28 29 // Some characters are not allowed to appear in feature names or the associated 30 // field trial names, as they are used as special characters for command-line 31 // serialization. This function checks that the strings are ASCII (since they 32 // are used in command-line API functions that require ASCII) and whether there 33 // are any reserved characters present, returning true if the string is valid. 34 // Only called in DCHECKs. 35 bool IsValidFeatureOrFieldTrialName(const std::string& name) { 36 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; 37 } 38 39 } // namespace 40 41 FeatureList::FeatureList() {} 42 43 FeatureList::~FeatureList() {} 44 45 void 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()). 52 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); 53 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); 54 55 initialized_from_command_line_ = true; 56 } 57 58 bool 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 66 void 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 86 void 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 100 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, 101 std::string* disable_overrides) { 102 DCHECK(initialized_); 103 104 enable_overrides->clear(); 105 disable_overrides->clear(); 106 107 // 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. 110 for (const auto& entry : overrides_) { 111 std::string* target_list = nullptr; 112 switch (entry.second.overridden_state) { 113 case OVERRIDE_USE_DEFAULT: 114 case OVERRIDE_ENABLE_FEATURE: 115 target_list = enable_overrides; 116 break; 117 case OVERRIDE_DISABLE_FEATURE: 118 target_list = disable_overrides; 119 break; 120 } 121 122 if (!target_list->empty()) 123 target_list->push_back(','); 124 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT) 125 target_list->push_back('*'); 126 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 } 131 } 132 } 133 134 // static 135 bool FeatureList::IsEnabled(const Feature& feature) { 136 if (!g_instance) { 137 g_initialized_from_accessor = true; 138 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; 139 } 140 return g_instance->IsFeatureEnabled(feature); 141 } 142 143 // static 144 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) { 145 return GetInstance()->GetAssociatedFieldTrial(feature); 146 } 147 148 // static 149 std::vector<std::string> FeatureList::SplitFeatureListString( 150 const std::string& input) { 151 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); 152 } 153 154 // static 155 bool FeatureList::InitializeInstance(const std::string& enable_features, 156 const std::string& disable_features) { 157 // We want to initialize a new instance here to support command-line features 158 // in testing better. For example, we initialize a dummy instance in 159 // base/test/test_suite.cc, and override it in content/browser/ 160 // browser_main_loop.cc. 161 // On the other hand, we want to avoid re-initialization from command line. 162 // For example, we initialize an instance in chrome/browser/ 163 // chrome_browser_main.cc and do not override it in content/browser/ 164 // browser_main_loop.cc. 165 // If the singleton was previously initialized from within an accessor, we 166 // want to prevent callers from reinitializing the singleton and masking the 167 // accessor call(s) which likely returned incorrect information. 168 CHECK(!g_initialized_from_accessor); 169 bool instance_existed_before = false; 170 if (g_instance) { 171 if (g_instance->initialized_from_command_line_) 172 return false; 173 174 delete g_instance; 175 g_instance = nullptr; 176 instance_existed_before = true; 177 } 178 179 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); 180 feature_list->InitializeFromCommandLine(enable_features, disable_features); 181 base::FeatureList::SetInstance(std::move(feature_list)); 182 return !instance_existed_before; 183 } 184 185 // static 186 FeatureList* FeatureList::GetInstance() { 187 return g_instance; 188 } 189 190 // static 191 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) { 192 DCHECK(!g_instance); 193 instance->FinalizeInitialization(); 194 195 // Note: Intentional leak of global singleton. 196 g_instance = instance.release(); 197 } 198 199 // static 200 void FeatureList::ClearInstanceForTesting() { 201 delete g_instance; 202 g_instance = nullptr; 203 g_initialized_from_accessor = false; 204 } 205 206 void FeatureList::FinalizeInitialization() { 207 DCHECK(!initialized_); 208 initialized_ = true; 209 } 210 211 bool FeatureList::IsFeatureEnabled(const Feature& feature) { 212 DCHECK(initialized_); 213 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; 214 DCHECK(CheckFeatureIdentity(feature)) << feature.name; 215 216 auto it = overrides_.find(feature.name); 217 if (it != overrides_.end()) { 218 const OverrideEntry& entry = it->second; 219 220 // Activate the corresponding field trial, if necessary. 221 if (entry.field_trial) 222 entry.field_trial->group(); 223 224 // TODO(asvitkine) Expand this section as more support is added. 225 226 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below. 227 if (entry.overridden_state != OVERRIDE_USE_DEFAULT) 228 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; 229 } 230 // Otherwise, return the default state. 231 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; 232 } 233 234 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) { 235 DCHECK(initialized_); 236 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; 237 DCHECK(CheckFeatureIdentity(feature)) << feature.name; 238 239 auto it = overrides_.find(feature.name); 240 if (it != overrides_.end()) { 241 const OverrideEntry& entry = it->second; 242 return entry.field_trial; 243 } 244 245 return nullptr; 246 } 247 248 void FeatureList::RegisterOverridesFromCommandLine( 249 const std::string& feature_list, 250 OverrideState overridden_state) { 251 for (const auto& value : SplitFeatureListString(feature_list)) { 252 StringPiece feature_name(value); 253 base::FieldTrial* trial = nullptr; 254 255 // The entry may be of the form FeatureName<FieldTrialName - in which case, 256 // this splits off the field trial name and associates it with the override. 257 std::string::size_type pos = feature_name.find('<'); 258 if (pos != std::string::npos) { 259 feature_name.set(value.data(), pos); 260 trial = base::FieldTrialList::Find(value.substr(pos + 1)); 261 } 262 263 RegisterOverride(feature_name, overridden_state, trial); 264 } 265 } 266 267 void FeatureList::RegisterOverride(StringPiece feature_name, 268 OverrideState overridden_state, 269 FieldTrial* field_trial) { 270 DCHECK(!initialized_); 271 if (field_trial) { 272 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name())) 273 << field_trial->trial_name(); 274 } 275 if (feature_name.starts_with("*")) { 276 feature_name = feature_name.substr(1); 277 overridden_state = OVERRIDE_USE_DEFAULT; 278 } 279 280 // Note: The semantics of insert() is that it does not overwrite the entry if 281 // one already exists for the key. Thus, only the first override for a given 282 // feature name takes effect. 283 overrides_.insert(std::make_pair( 284 feature_name.as_string(), OverrideEntry(overridden_state, field_trial))); 285 } 286 287 bool FeatureList::CheckFeatureIdentity(const Feature& feature) { 288 AutoLock auto_lock(feature_identity_tracker_lock_); 289 290 auto it = feature_identity_tracker_.find(feature.name); 291 if (it == feature_identity_tracker_.end()) { 292 // If it's not tracked yet, register it. 293 feature_identity_tracker_[feature.name] = &feature; 294 return true; 295 } 296 // Compare address of |feature| to the existing tracked entry. 297 return it->second == &feature; 298 } 299 300 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, 301 FieldTrial* field_trial) 302 : overridden_state(overridden_state), 303 field_trial(field_trial), 304 overridden_by_field_trial(field_trial != nullptr) {} 305 306 } // namespace base 307