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/memory/ptr_util.h" 14 #include "base/metrics/field_trial.h" 15 #include "base/pickle.h" 16 #include "base/strings/string_split.h" 17 #include "base/strings/string_util.h" 18 19 namespace base { 20 21 namespace { 22 23 // Pointer to the FeatureList instance singleton that was set via 24 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to 25 // have more control over initialization timing. Leaky. 26 FeatureList* g_instance = nullptr; 27 28 // Tracks whether the FeatureList instance was initialized via an accessor. 29 bool g_initialized_from_accessor = false; 30 31 // An allocator entry for a feature in shared memory. The FeatureEntry is 32 // followed by a base::Pickle object that contains the feature and trial name. 33 struct FeatureEntry { 34 // SHA1(FeatureEntry): Increment this if structure changes! 35 static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1; 36 37 // Expected size for 32/64-bit check. 38 static constexpr size_t kExpectedInstanceSize = 8; 39 40 // Specifies whether a feature override enables or disables the feature. Same 41 // values as the OverrideState enum in feature_list.h 42 uint32_t override_state; 43 44 // Size of the pickled structure, NOT the total size of this entry. 45 uint32_t pickle_size; 46 47 // Reads the feature and trial name from the pickle. Calling this is only 48 // valid on an initialized entry that's in shared memory. 49 bool GetFeatureAndTrialName(StringPiece* feature_name, 50 StringPiece* trial_name) const { 51 const char* src = 52 reinterpret_cast<const char*>(this) + sizeof(FeatureEntry); 53 54 Pickle pickle(src, pickle_size); 55 PickleIterator pickle_iter(pickle); 56 57 if (!pickle_iter.ReadStringPiece(feature_name)) 58 return false; 59 60 // Return true because we are not guaranteed to have a trial name anyways. 61 auto sink = pickle_iter.ReadStringPiece(trial_name); 62 ALLOW_UNUSED_LOCAL(sink); 63 return true; 64 } 65 }; 66 67 // Some characters are not allowed to appear in feature names or the associated 68 // field trial names, as they are used as special characters for command-line 69 // serialization. This function checks that the strings are ASCII (since they 70 // are used in command-line API functions that require ASCII) and whether there 71 // are any reserved characters present, returning true if the string is valid. 72 // Only called in DCHECKs. 73 bool IsValidFeatureOrFieldTrialName(const std::string& name) { 74 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; 75 } 76 77 } // namespace 78 79 FeatureList::FeatureList() {} 80 81 FeatureList::~FeatureList() {} 82 83 void FeatureList::InitializeFromCommandLine( 84 const std::string& enable_features, 85 const std::string& disable_features) { 86 DCHECK(!initialized_); 87 88 // Process disabled features first, so that disabled ones take precedence over 89 // enabled ones (since RegisterOverride() uses insert()). 90 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); 91 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); 92 93 initialized_from_command_line_ = true; 94 } 95 96 void FeatureList::InitializeFromSharedMemory( 97 PersistentMemoryAllocator* allocator) { 98 DCHECK(!initialized_); 99 100 PersistentMemoryAllocator::Iterator iter(allocator); 101 const FeatureEntry* entry; 102 while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) { 103 OverrideState override_state = 104 static_cast<OverrideState>(entry->override_state); 105 106 StringPiece feature_name; 107 StringPiece trial_name; 108 if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name)) 109 continue; 110 111 FieldTrial* trial = FieldTrialList::Find(trial_name.as_string()); 112 RegisterOverride(feature_name, override_state, trial); 113 } 114 } 115 116 bool FeatureList::IsFeatureOverriddenFromCommandLine( 117 const std::string& feature_name, 118 OverrideState state) const { 119 auto it = overrides_.find(feature_name); 120 return it != overrides_.end() && it->second.overridden_state == state && 121 !it->second.overridden_by_field_trial; 122 } 123 124 void FeatureList::AssociateReportingFieldTrial( 125 const std::string& feature_name, 126 OverrideState for_overridden_state, 127 FieldTrial* field_trial) { 128 DCHECK( 129 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state)); 130 131 // Only one associated field trial is supported per feature. This is generally 132 // enforced server-side. 133 OverrideEntry* entry = &overrides_.find(feature_name)->second; 134 if (entry->field_trial) { 135 NOTREACHED() << "Feature " << feature_name 136 << " already has trial: " << entry->field_trial->trial_name() 137 << ", associating trial: " << field_trial->trial_name(); 138 return; 139 } 140 141 entry->field_trial = field_trial; 142 } 143 144 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name, 145 OverrideState override_state, 146 FieldTrial* field_trial) { 147 DCHECK(field_trial); 148 DCHECK(!ContainsKey(overrides_, feature_name) || 149 !overrides_.find(feature_name)->second.field_trial) 150 << "Feature " << feature_name 151 << " has conflicting field trial overrides: " 152 << overrides_.find(feature_name)->second.field_trial->trial_name() 153 << " / " << field_trial->trial_name(); 154 155 RegisterOverride(feature_name, override_state, field_trial); 156 } 157 158 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) { 159 DCHECK(initialized_); 160 161 for (const auto& override : overrides_) { 162 Pickle pickle; 163 pickle.WriteString(override.first); 164 if (override.second.field_trial) 165 pickle.WriteString(override.second.field_trial->trial_name()); 166 167 size_t total_size = sizeof(FeatureEntry) + pickle.size(); 168 FeatureEntry* entry = allocator->New<FeatureEntry>(total_size); 169 if (!entry) 170 return; 171 172 entry->override_state = override.second.overridden_state; 173 entry->pickle_size = pickle.size(); 174 175 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry); 176 memcpy(dst, pickle.data(), pickle.size()); 177 178 allocator->MakeIterable(entry); 179 } 180 } 181 182 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, 183 std::string* disable_overrides) { 184 DCHECK(initialized_); 185 186 enable_overrides->clear(); 187 disable_overrides->clear(); 188 189 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical 190 // order. This not guaranteed to users of this function, but is useful for 191 // tests to assume the order. 192 for (const auto& entry : overrides_) { 193 std::string* target_list = nullptr; 194 switch (entry.second.overridden_state) { 195 case OVERRIDE_USE_DEFAULT: 196 case OVERRIDE_ENABLE_FEATURE: 197 target_list = enable_overrides; 198 break; 199 case OVERRIDE_DISABLE_FEATURE: 200 target_list = disable_overrides; 201 break; 202 } 203 204 if (!target_list->empty()) 205 target_list->push_back(','); 206 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT) 207 target_list->push_back('*'); 208 target_list->append(entry.first); 209 if (entry.second.field_trial) { 210 target_list->push_back('<'); 211 target_list->append(entry.second.field_trial->trial_name()); 212 } 213 } 214 } 215 216 // static 217 bool FeatureList::IsEnabled(const Feature& feature) { 218 if (!g_instance) { 219 g_initialized_from_accessor = true; 220 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; 221 } 222 return g_instance->IsFeatureEnabled(feature); 223 } 224 225 // static 226 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) { 227 return GetInstance()->GetAssociatedFieldTrial(feature); 228 } 229 230 // static 231 std::vector<base::StringPiece> FeatureList::SplitFeatureListString( 232 base::StringPiece input) { 233 return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); 234 } 235 236 // static 237 bool FeatureList::InitializeInstance(const std::string& enable_features, 238 const std::string& disable_features) { 239 // We want to initialize a new instance here to support command-line features 240 // in testing better. For example, we initialize a dummy instance in 241 // base/test/test_suite.cc, and override it in content/browser/ 242 // browser_main_loop.cc. 243 // On the other hand, we want to avoid re-initialization from command line. 244 // For example, we initialize an instance in chrome/browser/ 245 // chrome_browser_main.cc and do not override it in content/browser/ 246 // browser_main_loop.cc. 247 // If the singleton was previously initialized from within an accessor, we 248 // want to prevent callers from reinitializing the singleton and masking the 249 // accessor call(s) which likely returned incorrect information. 250 CHECK(!g_initialized_from_accessor); 251 bool instance_existed_before = false; 252 if (g_instance) { 253 if (g_instance->initialized_from_command_line_) 254 return false; 255 256 delete g_instance; 257 g_instance = nullptr; 258 instance_existed_before = true; 259 } 260 261 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); 262 feature_list->InitializeFromCommandLine(enable_features, disable_features); 263 base::FeatureList::SetInstance(std::move(feature_list)); 264 return !instance_existed_before; 265 } 266 267 // static 268 FeatureList* FeatureList::GetInstance() { 269 return g_instance; 270 } 271 272 // static 273 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) { 274 DCHECK(!g_instance); 275 instance->FinalizeInitialization(); 276 277 // Note: Intentional leak of global singleton. 278 g_instance = instance.release(); 279 } 280 281 // static 282 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() { 283 FeatureList* old_instance = g_instance; 284 g_instance = nullptr; 285 g_initialized_from_accessor = false; 286 return base::WrapUnique(old_instance); 287 } 288 289 // static 290 void FeatureList::RestoreInstanceForTesting( 291 std::unique_ptr<FeatureList> instance) { 292 DCHECK(!g_instance); 293 // Note: Intentional leak of global singleton. 294 g_instance = instance.release(); 295 } 296 297 void FeatureList::FinalizeInitialization() { 298 DCHECK(!initialized_); 299 initialized_ = true; 300 } 301 302 bool FeatureList::IsFeatureEnabled(const Feature& feature) { 303 DCHECK(initialized_); 304 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; 305 DCHECK(CheckFeatureIdentity(feature)) << feature.name; 306 307 auto it = overrides_.find(feature.name); 308 if (it != overrides_.end()) { 309 const OverrideEntry& entry = it->second; 310 311 // Activate the corresponding field trial, if necessary. 312 if (entry.field_trial) 313 entry.field_trial->group(); 314 315 // TODO(asvitkine) Expand this section as more support is added. 316 317 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below. 318 if (entry.overridden_state != OVERRIDE_USE_DEFAULT) 319 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; 320 } 321 // Otherwise, return the default state. 322 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; 323 } 324 325 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) { 326 DCHECK(initialized_); 327 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; 328 DCHECK(CheckFeatureIdentity(feature)) << feature.name; 329 330 auto it = overrides_.find(feature.name); 331 if (it != overrides_.end()) { 332 const OverrideEntry& entry = it->second; 333 return entry.field_trial; 334 } 335 336 return nullptr; 337 } 338 339 void FeatureList::RegisterOverridesFromCommandLine( 340 const std::string& feature_list, 341 OverrideState overridden_state) { 342 for (const auto& value : SplitFeatureListString(feature_list)) { 343 StringPiece feature_name = value; 344 base::FieldTrial* trial = nullptr; 345 346 // The entry may be of the form FeatureName<FieldTrialName - in which case, 347 // this splits off the field trial name and associates it with the override. 348 std::string::size_type pos = feature_name.find('<'); 349 if (pos != std::string::npos) { 350 feature_name.set(value.data(), pos); 351 trial = base::FieldTrialList::Find(value.substr(pos + 1).as_string()); 352 } 353 354 RegisterOverride(feature_name, overridden_state, trial); 355 } 356 } 357 358 void FeatureList::RegisterOverride(StringPiece feature_name, 359 OverrideState overridden_state, 360 FieldTrial* field_trial) { 361 DCHECK(!initialized_); 362 if (field_trial) { 363 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name())) 364 << field_trial->trial_name(); 365 } 366 if (feature_name.starts_with("*")) { 367 feature_name = feature_name.substr(1); 368 overridden_state = OVERRIDE_USE_DEFAULT; 369 } 370 371 // Note: The semantics of insert() is that it does not overwrite the entry if 372 // one already exists for the key. Thus, only the first override for a given 373 // feature name takes effect. 374 overrides_.insert(std::make_pair( 375 feature_name.as_string(), OverrideEntry(overridden_state, field_trial))); 376 } 377 378 bool FeatureList::CheckFeatureIdentity(const Feature& feature) { 379 AutoLock auto_lock(feature_identity_tracker_lock_); 380 381 auto it = feature_identity_tracker_.find(feature.name); 382 if (it == feature_identity_tracker_.end()) { 383 // If it's not tracked yet, register it. 384 feature_identity_tracker_[feature.name] = &feature; 385 return true; 386 } 387 // Compare address of |feature| to the existing tracked entry. 388 return it->second == &feature; 389 } 390 391 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, 392 FieldTrial* field_trial) 393 : overridden_state(overridden_state), 394 field_trial(field_trial), 395 overridden_by_field_trial(field_trial != nullptr) {} 396 397 } // namespace base 398