xref: /aosp_15_r20/external/cronet/base/feature_list.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2015 The Chromium Authors
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 <string>
10 #include <string_view>
11 #include <tuple>
12 
13 #include "base/base_switches.h"
14 #include "base/containers/contains.h"
15 #include "base/containers/span.h"
16 #include "base/debug/crash_logging.h"
17 #include "base/debug/dump_without_crashing.h"
18 #include "base/logging.h"
19 #include "base/memory/ptr_util.h"
20 #include "base/memory/raw_ptr.h"
21 #include "base/metrics/field_trial.h"
22 #include "base/metrics/field_trial_param_associator.h"
23 #include "base/metrics/field_trial_params.h"
24 #include "base/metrics/persistent_memory_allocator.h"
25 #include "base/no_destructor.h"
26 #include "base/notreached.h"
27 #include "base/pickle.h"
28 #include "base/rand_util.h"
29 #include "base/strings/string_split.h"
30 #include "base/strings/string_util.h"
31 #include "base/strings/stringprintf.h"
32 #include "build/build_config.h"
33 #include "build/chromeos_buildflags.h"
34 
35 #if BUILDFLAG(IS_CHROMEOS_ASH)
36 #include "base/feature_visitor.h"
37 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
38 
39 namespace base {
40 
41 namespace {
42 
43 // Pointer to the FeatureList instance singleton that was set via
44 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
45 // have more control over initialization timing. Leaky.
46 FeatureList* g_feature_list_instance = nullptr;
47 
48 // Tracks access to Feature state before FeatureList registration.
49 class EarlyFeatureAccessTracker {
50  public:
GetInstance()51   static EarlyFeatureAccessTracker* GetInstance() {
52     static NoDestructor<EarlyFeatureAccessTracker> instance;
53     return instance.get();
54   }
55 
56   // Invoked when `feature` is accessed before FeatureList registration.
AccessedFeature(const Feature & feature,bool with_feature_allow_list=false)57   void AccessedFeature(const Feature& feature,
58                        bool with_feature_allow_list = false) {
59     AutoLock lock(lock_);
60     if (fail_instantly_) {
61       Fail(&feature, with_feature_allow_list);
62     } else if (!feature_) {
63       feature_ = &feature;
64       feature_had_feature_allow_list_ = with_feature_allow_list;
65     }
66   }
67 
68   // Asserts that no feature was accessed before FeatureList registration.
AssertNoAccess()69   void AssertNoAccess() {
70     AutoLock lock(lock_);
71     if (feature_) {
72       Fail(feature_, feature_had_feature_allow_list_);
73     }
74   }
75 
76   // Makes calls to AccessedFeature() fail instantly.
FailOnFeatureAccessWithoutFeatureList()77   void FailOnFeatureAccessWithoutFeatureList() {
78     AutoLock lock(lock_);
79     if (feature_) {
80       Fail(feature_, feature_had_feature_allow_list_);
81     }
82     fail_instantly_ = true;
83   }
84 
85   // Resets the state of this tracker.
Reset()86   void Reset() {
87     AutoLock lock(lock_);
88     feature_ = nullptr;
89     fail_instantly_ = false;
90   }
91 
GetFeature()92   const Feature* GetFeature() {
93     AutoLock lock(lock_);
94     return feature_.get();
95   }
96 
97  private:
Fail(const Feature * feature,bool with_feature_allow_list)98   void Fail(const Feature* feature, bool with_feature_allow_list) {
99     // TODO(crbug.com/1358639): Enable this check on all platforms.
100 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
101 #if !BUILDFLAG(IS_NACL)
102     // Create a crash key with the name of the feature accessed too early, to
103     // facilitate crash triage.
104     SCOPED_CRASH_KEY_STRING256("FeatureList", "feature-accessed-too-early",
105                                feature->name);
106     SCOPED_CRASH_KEY_BOOL("FeatureList", "early-access-allow-list",
107                           with_feature_allow_list);
108 #endif  // !BUILDFLAG(IS_NACL)
109     CHECK(!feature) << "Accessed feature " << feature->name
110                     << (with_feature_allow_list
111                             ? " which is not on the allow list passed to "
112                               "SetEarlyAccessInstance()."
113                             : " before FeatureList registration.");
114 #endif  // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) &&
115         // !BUILDFLAG(IS_CHROMEOS)
116   }
117 
118   friend class NoDestructor<EarlyFeatureAccessTracker>;
119 
120   EarlyFeatureAccessTracker() = default;
121   ~EarlyFeatureAccessTracker() = default;
122 
123   Lock lock_;
124 
125   // First feature to be accessed before FeatureList registration.
126   raw_ptr<const Feature> feature_ GUARDED_BY(lock_) = nullptr;
127   bool feature_had_feature_allow_list_ GUARDED_BY(lock_) = false;
128 
129   // Whether AccessedFeature() should fail instantly.
130   bool fail_instantly_ GUARDED_BY(lock_) = false;
131 };
132 
133 #if DCHECK_IS_ON()
134 const char* g_reason_overrides_disallowed = nullptr;
135 
DCheckOverridesAllowed()136 void DCheckOverridesAllowed() {
137   const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
138   DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
139 }
140 #else
DCheckOverridesAllowed()141 void DCheckOverridesAllowed() {}
142 #endif
143 
144 // An allocator entry for a feature in shared memory. The FeatureEntry is
145 // followed by a base::Pickle object that contains the feature and trial name.
146 struct FeatureEntry {
147   // SHA1(FeatureEntry): Increment this if structure changes!
148   static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 2;
149 
150   // Expected size for 32/64-bit check.
151   static constexpr size_t kExpectedInstanceSize = 16;
152 
153   // Specifies whether a feature override enables or disables the feature. Same
154   // values as the OverrideState enum in feature_list.h
155   uint32_t override_state;
156 
157   // On e.g. x86, alignof(uint64_t) is 4.  Ensure consistent size and alignment
158   // of `pickle_size` across platforms.
159   uint32_t padding;
160 
161   // Size of the pickled structure, NOT the total size of this entry.
162   uint64_t pickle_size;
163 
164   // Return a pointer to the pickled data area immediately following the entry.
GetPickledDataPtrbase::__anon54ff85260111::FeatureEntry165   uint8_t* GetPickledDataPtr() { return reinterpret_cast<uint8_t*>(this + 1); }
GetPickledDataPtrbase::__anon54ff85260111::FeatureEntry166   const uint8_t* GetPickledDataPtr() const {
167     return reinterpret_cast<const uint8_t*>(this + 1);
168   }
169 
170   // Reads the feature and trial name from the pickle. Calling this is only
171   // valid on an initialized entry that's in shared memory.
GetFeatureAndTrialNamebase::__anon54ff85260111::FeatureEntry172   bool GetFeatureAndTrialName(std::string_view* feature_name,
173                               std::string_view* trial_name) const {
174     Pickle pickle = Pickle::WithUnownedBuffer(
175         span(GetPickledDataPtr(), checked_cast<size_t>(pickle_size)));
176     PickleIterator pickle_iter(pickle);
177     if (!pickle_iter.ReadStringPiece(feature_name)) {
178       return false;
179     }
180     // Return true because we are not guaranteed to have a trial name anyways.
181     std::ignore = pickle_iter.ReadStringPiece(trial_name);
182     return true;
183   }
184 };
185 
186 // Splits |text| into two parts by the |separator| where the first part will be
187 // returned updated in |first| and the second part will be returned as |second|.
188 // This function returns false if there is more than one |separator| in |first|.
189 // If there is no |separator| presented in |first|, this function will not
190 // modify |first| and |second|. It's used for splitting the |enable_features|
191 // flag into feature name, field trial name and feature parameters.
SplitIntoTwo(std::string_view text,std::string_view separator,std::string_view * first,std::string * second)192 bool SplitIntoTwo(std::string_view text,
193                   std::string_view separator,
194                   std::string_view* first,
195                   std::string* second) {
196   std::vector<std::string_view> parts =
197       SplitStringPiece(text, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
198   if (parts.size() == 2) {
199     *second = std::string(parts[1]);
200   } else if (parts.size() > 2) {
201     DLOG(ERROR) << "Only one '" << separator
202                 << "' is allowed but got: " << *first;
203     return false;
204   }
205   *first = parts[0];
206   return true;
207 }
208 
209 // Checks and parses the |enable_features| flag and sets
210 // |parsed_enable_features| to be a comma-separated list of features,
211 // |force_fieldtrials| to be a comma-separated list of field trials that each
212 // feature want to associate with and |force_fieldtrial_params| to be the field
213 // trial parameters for each field trial.
214 // Returns true if |enable_features| is parsable, otherwise false.
ParseEnableFeatures(const std::string & enable_features,std::string * parsed_enable_features,std::string * force_fieldtrials,std::string * force_fieldtrial_params)215 bool ParseEnableFeatures(const std::string& enable_features,
216                          std::string* parsed_enable_features,
217                          std::string* force_fieldtrials,
218                          std::string* force_fieldtrial_params) {
219   std::vector<std::string> enable_features_list;
220   std::vector<std::string> force_fieldtrials_list;
221   std::vector<std::string> force_fieldtrial_params_list;
222   for (const auto& enable_feature :
223        FeatureList::SplitFeatureListString(enable_features)) {
224     std::string feature_name;
225     std::string study;
226     std::string group;
227     std::string feature_params;
228     if (!FeatureList::ParseEnableFeatureString(
229             enable_feature, &feature_name, &study, &group, &feature_params)) {
230       return false;
231     }
232 
233     // If feature params were set but group and study weren't, associate the
234     // feature and its feature params to a synthetic field trial as the
235     // feature params only make sense when it's combined with a field trial.
236     if (!feature_params.empty()) {
237       force_fieldtrials_list.push_back(study + "/" + group);
238       force_fieldtrial_params_list.push_back(study + "." + group + ":" +
239                                              feature_params);
240     }
241     enable_features_list.push_back(
242         study.empty() ? feature_name : (feature_name + "<" + study));
243   }
244 
245   *parsed_enable_features = JoinString(enable_features_list, ",");
246   // Field trial separator is currently a slash. See
247   // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
248   *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
249   *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
250   return true;
251 }
252 
UnpackFeatureCache(uint32_t packed_cache_value)253 std::pair<FeatureList::OverrideState, uint16_t> UnpackFeatureCache(
254     uint32_t packed_cache_value) {
255   return std::make_pair(
256       static_cast<FeatureList::OverrideState>(packed_cache_value >> 24),
257       packed_cache_value & 0xFFFF);
258 }
259 
PackFeatureCache(FeatureList::OverrideState override_state,uint32_t caching_context)260 uint32_t PackFeatureCache(FeatureList::OverrideState override_state,
261                           uint32_t caching_context) {
262   return (static_cast<uint32_t>(override_state) << 24) |
263          (caching_context & 0xFFFF);
264 }
265 
266 // A monotonically increasing id, passed to `FeatureList`s as they are created
267 // to invalidate the cache member of `base::Feature` objects that were queried
268 // with a different `FeatureList` installed.
269 uint16_t g_current_caching_context = 1;
270 
271 }  // namespace
272 
273 #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
274 BASE_FEATURE(kDCheckIsFatalFeature,
275              "DcheckIsFatal",
276              FEATURE_DISABLED_BY_DEFAULT);
277 #endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
278 
FeatureList()279 FeatureList::FeatureList() : caching_context_(g_current_caching_context++) {}
280 
281 FeatureList::~FeatureList() = default;
282 
ScopedDisallowOverrides(const char * reason)283 FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
284     const char* reason)
285 #if DCHECK_IS_ON()
286     : previous_reason_(g_reason_overrides_disallowed) {
287   g_reason_overrides_disallowed = reason;
288 }
289 #else
290 {
291 }
292 #endif
293 
~ScopedDisallowOverrides()294 FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
295 #if DCHECK_IS_ON()
296   g_reason_overrides_disallowed = previous_reason_;
297 #endif
298 }
299 
InitFromCommandLine(const std::string & enable_features,const std::string & disable_features)300 void FeatureList::InitFromCommandLine(const std::string& enable_features,
301                                       const std::string& disable_features) {
302   DCHECK(!initialized_);
303 
304   std::string parsed_enable_features;
305   std::string force_fieldtrials;
306   std::string force_fieldtrial_params;
307   bool parse_enable_features_result =
308       ParseEnableFeatures(enable_features, &parsed_enable_features,
309                           &force_fieldtrials, &force_fieldtrial_params);
310   DCHECK(parse_enable_features_result) << StringPrintf(
311       "The --%s list is unparsable or invalid, please check the format.",
312       ::switches::kEnableFeatures);
313 
314   // Only create field trials when field_trial_list is available. Some tests
315   // don't have field trial list available.
316   if (FieldTrialList::GetInstance()) {
317     bool associate_params_result = AssociateFieldTrialParamsFromString(
318         force_fieldtrial_params, &UnescapeValue);
319     DCHECK(associate_params_result) << StringPrintf(
320         "The field trial parameters part of the --%s list is invalid. Make "
321         "sure "
322         "you %%-encode the following characters in param values: %%:/.,",
323         ::switches::kEnableFeatures);
324 
325     bool create_trials_result =
326         FieldTrialList::CreateTrialsFromString(force_fieldtrials);
327     DCHECK(create_trials_result)
328         << StringPrintf("Invalid field trials are specified in --%s.",
329                         ::switches::kEnableFeatures);
330   }
331 
332   // Process disabled features first, so that disabled ones take precedence over
333   // enabled ones (since RegisterOverride() uses insert()).
334   RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
335   RegisterOverridesFromCommandLine(parsed_enable_features,
336                                    OVERRIDE_ENABLE_FEATURE);
337 
338   initialized_from_command_line_ = true;
339 }
340 
InitFromSharedMemory(PersistentMemoryAllocator * allocator)341 void FeatureList::InitFromSharedMemory(PersistentMemoryAllocator* allocator) {
342   DCHECK(!initialized_);
343 
344   PersistentMemoryAllocator::Iterator iter(allocator);
345   const FeatureEntry* entry;
346   while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
347     OverrideState override_state =
348         static_cast<OverrideState>(entry->override_state);
349 
350     std::string_view feature_name;
351     std::string_view trial_name;
352     if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
353       continue;
354 
355     FieldTrial* trial = FieldTrialList::Find(trial_name);
356     RegisterOverride(feature_name, override_state, trial);
357   }
358 }
359 
IsFeatureOverridden(const std::string & feature_name) const360 bool FeatureList::IsFeatureOverridden(const std::string& feature_name) const {
361   return overrides_.count(feature_name);
362 }
363 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name) const364 bool FeatureList::IsFeatureOverriddenFromCommandLine(
365     const std::string& feature_name) const {
366   auto it = overrides_.find(feature_name);
367   return it != overrides_.end() && !it->second.overridden_by_field_trial;
368 }
369 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name,OverrideState state) const370 bool FeatureList::IsFeatureOverriddenFromCommandLine(
371     const std::string& feature_name,
372     OverrideState state) const {
373   auto it = overrides_.find(feature_name);
374   return it != overrides_.end() && !it->second.overridden_by_field_trial &&
375          it->second.overridden_state == state;
376 }
377 
AssociateReportingFieldTrial(const std::string & feature_name,OverrideState for_overridden_state,FieldTrial * field_trial)378 void FeatureList::AssociateReportingFieldTrial(
379     const std::string& feature_name,
380     OverrideState for_overridden_state,
381     FieldTrial* field_trial) {
382   DCHECK(
383       IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
384 
385   // Only one associated field trial is supported per feature. This is generally
386   // enforced server-side.
387   OverrideEntry* entry = &overrides_.find(feature_name)->second;
388   if (entry->field_trial) {
389     NOTREACHED() << "Feature " << feature_name
390                  << " already has trial: " << entry->field_trial->trial_name()
391                  << ", associating trial: " << field_trial->trial_name();
392     return;
393   }
394 
395   entry->field_trial = field_trial;
396 }
397 
RegisterFieldTrialOverride(const std::string & feature_name,OverrideState override_state,FieldTrial * field_trial)398 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
399                                              OverrideState override_state,
400                                              FieldTrial* field_trial) {
401   DCHECK(field_trial);
402   DCHECK(!HasAssociatedFieldTrialByFeatureName(feature_name))
403       << "Feature " << feature_name << " is overriden multiple times in these "
404       << "trials: "
405       << overrides_.find(feature_name)->second.field_trial->trial_name()
406       << " and " << field_trial->trial_name() << ". "
407       << "Check the trial (study) in (1) the server config, "
408       << "(2) fieldtrial_testing_config.json, (3) about_flags.cc, and "
409       << "(4) client-side field trials.";
410 
411   RegisterOverride(feature_name, override_state, field_trial);
412 }
413 
RegisterExtraFeatureOverrides(const std::vector<FeatureOverrideInfo> & extra_overrides)414 void FeatureList::RegisterExtraFeatureOverrides(
415     const std::vector<FeatureOverrideInfo>& extra_overrides) {
416   for (const FeatureOverrideInfo& override_info : extra_overrides) {
417     RegisterOverride(override_info.first.get().name, override_info.second,
418                      /* field_trial = */ nullptr);
419   }
420 }
421 
AddFeaturesToAllocator(PersistentMemoryAllocator * allocator)422 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
423   DCHECK(initialized_);
424 
425   for (const auto& override : overrides_) {
426     Pickle pickle;
427     pickle.WriteString(override.first);
428     if (override.second.field_trial)
429       pickle.WriteString(override.second.field_trial->trial_name());
430 
431     size_t total_size = sizeof(FeatureEntry) + pickle.size();
432     FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
433     if (!entry)
434       return;
435 
436     entry->override_state = override.second.overridden_state;
437     entry->pickle_size = pickle.size();
438     memcpy(entry->GetPickledDataPtr(), pickle.data(), pickle.size());
439 
440     allocator->MakeIterable(entry);
441   }
442 }
443 
GetFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides,bool include_group_name) const444 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
445                                       std::string* disable_overrides,
446                                       bool include_group_name) const {
447   GetFeatureOverridesImpl(enable_overrides, disable_overrides, false,
448                           include_group_name);
449 }
450 
GetCommandLineFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides) const451 void FeatureList::GetCommandLineFeatureOverrides(
452     std::string* enable_overrides,
453     std::string* disable_overrides) const {
454   GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
455 }
456 
457 // static
IsEnabled(const Feature & feature)458 bool FeatureList::IsEnabled(const Feature& feature) {
459   if (!g_feature_list_instance ||
460       !g_feature_list_instance->AllowFeatureAccess(feature)) {
461     EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(
462         feature, g_feature_list_instance &&
463                      g_feature_list_instance->IsEarlyAccessInstance());
464     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
465   }
466   return g_feature_list_instance->IsFeatureEnabled(feature);
467 }
468 
469 // static
IsValidFeatureOrFieldTrialName(std::string_view name)470 bool FeatureList::IsValidFeatureOrFieldTrialName(std::string_view name) {
471   return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
472 }
473 
474 // static
GetStateIfOverridden(const Feature & feature)475 std::optional<bool> FeatureList::GetStateIfOverridden(const Feature& feature) {
476   if (!g_feature_list_instance ||
477       !g_feature_list_instance->AllowFeatureAccess(feature)) {
478     EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(
479         feature, g_feature_list_instance &&
480                      g_feature_list_instance->IsEarlyAccessInstance());
481     // If there is no feature list, there can be no overrides.
482     return std::nullopt;
483   }
484   return g_feature_list_instance->IsFeatureEnabledIfOverridden(feature);
485 }
486 
487 // static
GetFieldTrial(const Feature & feature)488 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
489   if (!g_feature_list_instance ||
490       !g_feature_list_instance->AllowFeatureAccess(feature)) {
491     EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(
492         feature, g_feature_list_instance &&
493                      g_feature_list_instance->IsEarlyAccessInstance());
494     return nullptr;
495   }
496   return g_feature_list_instance->GetAssociatedFieldTrial(feature);
497 }
498 
499 // static
SplitFeatureListString(std::string_view input)500 std::vector<std::string_view> FeatureList::SplitFeatureListString(
501     std::string_view input) {
502   return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
503 }
504 
505 // static
ParseEnableFeatureString(std::string_view enable_feature,std::string * feature_name,std::string * study_name,std::string * group_name,std::string * params)506 bool FeatureList::ParseEnableFeatureString(std::string_view enable_feature,
507                                            std::string* feature_name,
508                                            std::string* study_name,
509                                            std::string* group_name,
510                                            std::string* params) {
511   std::string_view first;
512   // First, check whether ":" is present. If true, feature parameters were
513   // set for this feature.
514   std::string feature_params;
515   if (!SplitIntoTwo(enable_feature, ":", &first, &feature_params))
516     return false;
517   // Then, check whether "." is present. If true, a group was specified for
518   // this feature.
519   std::string group;
520   if (!SplitIntoTwo(first, ".", &first, &group))
521     return false;
522   // Finally, check whether "<" is present. If true, a study was specified for
523   // this feature.
524   std::string study;
525   if (!SplitIntoTwo(first, "<", &first, &study))
526     return false;
527 
528   std::string enable_feature_name(first);
529   // If feature params were set but group and study weren't, associate the
530   // feature and its feature params to a synthetic field trial as the
531   // feature params only make sense when it's combined with a field trial.
532   if (!feature_params.empty()) {
533     study = study.empty() ? "Study" + enable_feature_name : study;
534     group = group.empty() ? "Group" + enable_feature_name : group;
535   }
536 
537   feature_name->swap(enable_feature_name);
538   study_name->swap(study);
539   group_name->swap(group);
540   params->swap(feature_params);
541   return true;
542 }
543 
544 // static
InitInstance(const std::string & enable_features,const std::string & disable_features)545 bool FeatureList::InitInstance(const std::string& enable_features,
546                                const std::string& disable_features) {
547   return InitInstance(enable_features, disable_features,
548                       std::vector<FeatureOverrideInfo>());
549 }
550 
551 // static
InitInstance(const std::string & enable_features,const std::string & disable_features,const std::vector<FeatureOverrideInfo> & extra_overrides)552 bool FeatureList::InitInstance(
553     const std::string& enable_features,
554     const std::string& disable_features,
555     const std::vector<FeatureOverrideInfo>& extra_overrides) {
556   // We want to initialize a new instance here to support command-line features
557   // in testing better. For example, we initialize a dummy instance in
558   // base/test/test_suite.cc, and override it in content/browser/
559   // browser_main_loop.cc.
560   // On the other hand, we want to avoid re-initialization from command line.
561   // For example, we initialize an instance in chrome/browser/
562   // chrome_browser_main.cc and do not override it in content/browser/
563   // browser_main_loop.cc.
564   // If the singleton was previously initialized from within an accessor, we
565   // want to prevent callers from reinitializing the singleton and masking the
566   // accessor call(s) which likely returned incorrect information.
567   EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
568   bool instance_existed_before = false;
569   if (g_feature_list_instance) {
570     if (g_feature_list_instance->initialized_from_command_line_)
571       return false;
572 
573     delete g_feature_list_instance;
574     g_feature_list_instance = nullptr;
575     instance_existed_before = true;
576   }
577 
578   std::unique_ptr<FeatureList> feature_list(new FeatureList);
579   feature_list->InitFromCommandLine(enable_features, disable_features);
580   feature_list->RegisterExtraFeatureOverrides(extra_overrides);
581   FeatureList::SetInstance(std::move(feature_list));
582   return !instance_existed_before;
583 }
584 
585 // static
GetInstance()586 FeatureList* FeatureList::GetInstance() {
587   return g_feature_list_instance;
588 }
589 
590 // static
SetInstance(std::unique_ptr<FeatureList> instance)591 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
592   DCHECK(!g_feature_list_instance ||
593          g_feature_list_instance->IsEarlyAccessInstance());
594   // If there is an existing early-access instance, release it.
595   if (g_feature_list_instance) {
596     std::unique_ptr<FeatureList> old_instance =
597         WrapUnique(g_feature_list_instance);
598     g_feature_list_instance = nullptr;
599   }
600   instance->FinalizeInitialization();
601 
602   // Note: Intentional leak of global singleton.
603   g_feature_list_instance = instance.release();
604 
605   EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
606 
607   // Don't configure random bytes field trials for a possibly early access
608   // FeatureList instance, as the state of the involved Features might change
609   // with the final FeatureList for this process.
610   if (!g_feature_list_instance->IsEarlyAccessInstance()) {
611 #if !BUILDFLAG(IS_NACL)
612     // Configured first because it takes precedence over the getrandom() trial.
613     internal::ConfigureBoringSSLBackedRandBytesFieldTrial();
614 #endif
615 
616 #if BUILDFLAG(IS_ANDROID)
617     internal::ConfigureRandBytesFieldTrial();
618 #endif
619   }
620 
621 #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
622   // Update the behaviour of LOGGING_DCHECK to match the Feature configuration.
623   // DCHECK is also forced to be FATAL if we are running a death-test.
624   // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
625   // currently run through this codepath, mitigated in
626   // base::TestSuite::Initialize() for now.
627   // TODO(asvitkine): If we find other use-cases that need integrating here
628   // then define a proper API/hook for the purpose.
629   if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
630       CommandLine::ForCurrentProcess()->HasSwitch(
631           "gtest_internal_run_death_test")) {
632     logging::LOGGING_DCHECK = logging::LOGGING_FATAL;
633   } else {
634     logging::LOGGING_DCHECK = logging::LOGGING_ERROR;
635   }
636 #endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
637 }
638 
639 // static
SetEarlyAccessInstance(std::unique_ptr<FeatureList> instance,base::flat_set<std::string> allowed_feature_names)640 void FeatureList::SetEarlyAccessInstance(
641     std::unique_ptr<FeatureList> instance,
642     base::flat_set<std::string> allowed_feature_names) {
643   CHECK(!g_feature_list_instance);
644   CHECK(!allowed_feature_names.empty());
645   instance->allowed_feature_names_ = std::move(allowed_feature_names);
646   SetInstance(std::move(instance));
647 }
648 
649 // static
ClearInstanceForTesting()650 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
651   FeatureList* old_instance = g_feature_list_instance;
652   g_feature_list_instance = nullptr;
653   EarlyFeatureAccessTracker::GetInstance()->Reset();
654   return WrapUnique(old_instance);
655 }
656 
657 // static
RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance)658 void FeatureList::RestoreInstanceForTesting(
659     std::unique_ptr<FeatureList> instance) {
660   DCHECK(!g_feature_list_instance);
661   // Note: Intentional leak of global singleton.
662   g_feature_list_instance = instance.release();
663 }
664 
665 // static
FailOnFeatureAccessWithoutFeatureList()666 void FeatureList::FailOnFeatureAccessWithoutFeatureList() {
667   EarlyFeatureAccessTracker::GetInstance()
668       ->FailOnFeatureAccessWithoutFeatureList();
669 }
670 
671 // static
GetEarlyAccessedFeatureForTesting()672 const Feature* FeatureList::GetEarlyAccessedFeatureForTesting() {
673   return EarlyFeatureAccessTracker::GetInstance()->GetFeature();
674 }
675 
676 // static
ResetEarlyFeatureAccessTrackerForTesting()677 void FeatureList::ResetEarlyFeatureAccessTrackerForTesting() {
678   EarlyFeatureAccessTracker::GetInstance()->Reset();
679 }
680 
AddEarlyAllowedFeatureForTesting(std::string feature_name)681 void FeatureList::AddEarlyAllowedFeatureForTesting(std::string feature_name) {
682   CHECK(IsEarlyAccessInstance());
683   allowed_feature_names_.insert(std::move(feature_name));
684 }
685 
686 #if BUILDFLAG(IS_CHROMEOS_ASH)
687 // static
VisitFeaturesAndParams(FeatureVisitor & visitor)688 void FeatureList::VisitFeaturesAndParams(FeatureVisitor& visitor) {
689   CHECK(g_feature_list_instance);
690   FieldTrialParamAssociator* params_associator =
691       FieldTrialParamAssociator::GetInstance();
692 
693   for (auto& feature_override : g_feature_list_instance->overrides_) {
694     FieldTrial* field_trial = feature_override.second.field_trial;
695 
696     std::string trial_name;
697     std::string group_name;
698     FieldTrialParams params;
699     if (field_trial) {
700       trial_name = field_trial->trial_name();
701       group_name = field_trial->group_name();
702       params_associator->GetFieldTrialParamsWithoutFallback(
703           trial_name, group_name, &params);
704     }
705 
706     visitor.Visit(feature_override.first,
707                   feature_override.second.overridden_state, params, trial_name,
708                   group_name);
709   }
710 }
711 #endif  // BULDFLAG(IS_CHROMEOS_ASH)
712 
FinalizeInitialization()713 void FeatureList::FinalizeInitialization() {
714   DCHECK(!initialized_);
715   // Store the field trial list pointer for DCHECKing.
716   field_trial_list_ = FieldTrialList::GetInstance();
717   initialized_ = true;
718 }
719 
IsFeatureEnabled(const Feature & feature) const720 bool FeatureList::IsFeatureEnabled(const Feature& feature) const {
721   OverrideState overridden_state = GetOverrideState(feature);
722 
723   // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
724   if (overridden_state != OVERRIDE_USE_DEFAULT)
725     return overridden_state == OVERRIDE_ENABLE_FEATURE;
726 
727   return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
728 }
729 
IsFeatureEnabledIfOverridden(const Feature & feature) const730 std::optional<bool> FeatureList::IsFeatureEnabledIfOverridden(
731     const Feature& feature) const {
732   OverrideState overridden_state = GetOverrideState(feature);
733 
734   // If marked as OVERRIDE_USE_DEFAULT, fall through to returning empty.
735   if (overridden_state != OVERRIDE_USE_DEFAULT)
736     return overridden_state == OVERRIDE_ENABLE_FEATURE;
737 
738   return std::nullopt;
739 }
740 
GetOverrideState(const Feature & feature) const741 FeatureList::OverrideState FeatureList::GetOverrideState(
742     const Feature& feature) const {
743   DCHECK(initialized_);
744   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
745   DCHECK(CheckFeatureIdentity(feature))
746       << feature.name
747       << " has multiple definitions. Either it is defined more than once in "
748          "code or (for component builds) the code is built into multiple "
749          "components (shared libraries) without a corresponding export "
750          "statement";
751 
752   uint32_t current_cache_value =
753       feature.cached_value.load(std::memory_order_relaxed);
754 
755   auto unpacked = UnpackFeatureCache(current_cache_value);
756 
757   if (unpacked.second == caching_context_)
758     return unpacked.first;
759 
760   OverrideState state = GetOverrideStateByFeatureName(feature.name);
761   uint32_t new_cache_value = PackFeatureCache(state, caching_context_);
762 
763   // Update the cache with the new value.
764   // In non-test code, this value can be in one of 2 states: either it's unset,
765   // or another thread has updated it to the same value we're about to write.
766   // Because of this, a plain `store` yields the correct result in all cases.
767   // In test code, it's possible for a different thread to have installed a new
768   // `ScopedFeatureList` and written a value that's different than the one we're
769   // about to write, although that would be a thread safety violation already
770   // and such tests should be fixed.
771   feature.cached_value.store(new_cache_value, std::memory_order_relaxed);
772 
773   return state;
774 }
775 
GetOverrideStateByFeatureName(std::string_view feature_name) const776 FeatureList::OverrideState FeatureList::GetOverrideStateByFeatureName(
777     std::string_view feature_name) const {
778   DCHECK(initialized_);
779   DCHECK(IsValidFeatureOrFieldTrialName(feature_name)) << feature_name;
780 
781   auto it = overrides_.find(feature_name);
782   if (it != overrides_.end()) {
783     const OverrideEntry& entry = it->second;
784 
785     // Activate the corresponding field trial, if necessary.
786     if (entry.field_trial)
787       entry.field_trial->Activate();
788 
789     // TODO(asvitkine) Expand this section as more support is added.
790 
791     return entry.overridden_state;
792   }
793   // Otherwise, report that we want to use the default state.
794   return OVERRIDE_USE_DEFAULT;
795 }
796 
GetAssociatedFieldTrial(const Feature & feature) const797 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) const {
798   DCHECK(initialized_);
799   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
800 
801   return GetAssociatedFieldTrialByFeatureName(feature.name);
802 }
803 
804 const base::FeatureList::OverrideEntry*
GetOverrideEntryByFeatureName(std::string_view name) const805 FeatureList::GetOverrideEntryByFeatureName(std::string_view name) const {
806   DCHECK(initialized_);
807   DCHECK(IsValidFeatureOrFieldTrialName(name)) << name;
808 
809   auto it = overrides_.find(name);
810   if (it != overrides_.end()) {
811     const OverrideEntry& entry = it->second;
812     return &entry;
813   }
814   return nullptr;
815 }
816 
GetAssociatedFieldTrialByFeatureName(std::string_view name) const817 FieldTrial* FeatureList::GetAssociatedFieldTrialByFeatureName(
818     std::string_view name) const {
819   DCHECK(initialized_);
820 
821   const base::FeatureList::OverrideEntry* entry =
822       GetOverrideEntryByFeatureName(name);
823   if (entry) {
824     return entry->field_trial;
825   }
826   return nullptr;
827 }
828 
HasAssociatedFieldTrialByFeatureName(std::string_view name) const829 bool FeatureList::HasAssociatedFieldTrialByFeatureName(
830     std::string_view name) const {
831   DCHECK(!initialized_);
832   auto entry = overrides_.find(name);
833   return entry != overrides_.end() && entry->second.field_trial != nullptr;
834 }
835 
GetEnabledFieldTrialByFeatureName(std::string_view name) const836 FieldTrial* FeatureList::GetEnabledFieldTrialByFeatureName(
837     std::string_view name) const {
838   DCHECK(initialized_);
839 
840   const base::FeatureList::OverrideEntry* entry =
841       GetOverrideEntryByFeatureName(name);
842   if (entry &&
843       entry->overridden_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
844     return entry->field_trial;
845   }
846   return nullptr;
847 }
848 
ConstructAccessor()849 std::unique_ptr<FeatureList::Accessor> FeatureList::ConstructAccessor() {
850   if (initialized_) {
851     // This function shouldn't be called after initialization.
852     NOTREACHED();
853     return nullptr;
854   }
855   // Use new and WrapUnique because we want to restrict access to the Accessor's
856   // constructor.
857   return base::WrapUnique(new Accessor(this));
858 }
859 
RegisterOverridesFromCommandLine(const std::string & feature_list,OverrideState overridden_state)860 void FeatureList::RegisterOverridesFromCommandLine(
861     const std::string& feature_list,
862     OverrideState overridden_state) {
863   for (const auto& value : SplitFeatureListString(feature_list)) {
864     std::string_view feature_name = value;
865     FieldTrial* trial = nullptr;
866 
867     // The entry may be of the form FeatureName<FieldTrialName - in which case,
868     // this splits off the field trial name and associates it with the override.
869     std::string::size_type pos = feature_name.find('<');
870     if (pos != std::string::npos) {
871       feature_name = std::string_view(value.data(), pos);
872       trial = FieldTrialList::Find(value.substr(pos + 1));
873 #if !BUILDFLAG(IS_NACL)
874       // If the below DCHECK fires, it means a non-existent trial name was
875       // specified via the "Feature<Trial" command-line syntax.
876       DCHECK(trial) << "trial='" << value.substr(pos + 1) << "' does not exist";
877 #endif  // !BUILDFLAG(IS_NACL)
878     }
879 
880     RegisterOverride(feature_name, overridden_state, trial);
881   }
882 }
883 
RegisterOverride(std::string_view feature_name,OverrideState overridden_state,FieldTrial * field_trial)884 void FeatureList::RegisterOverride(std::string_view feature_name,
885                                    OverrideState overridden_state,
886                                    FieldTrial* field_trial) {
887   DCHECK(!initialized_);
888   DCheckOverridesAllowed();
889   if (field_trial) {
890     DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
891         << field_trial->trial_name();
892   }
893   if (StartsWith(feature_name, "*")) {
894     feature_name = feature_name.substr(1);
895     overridden_state = OVERRIDE_USE_DEFAULT;
896   }
897 
898   // Note: The semantics of emplace() is that it does not overwrite the entry if
899   // one already exists for the key. Thus, only the first override for a given
900   // feature name takes effect.
901   overrides_.emplace(std::string(feature_name),
902                      OverrideEntry(overridden_state, field_trial));
903 }
904 
GetFeatureOverridesImpl(std::string * enable_overrides,std::string * disable_overrides,bool command_line_only,bool include_group_name) const905 void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
906                                           std::string* disable_overrides,
907                                           bool command_line_only,
908                                           bool include_group_name) const {
909   DCHECK(initialized_);
910 
911   // Check that the FieldTrialList this is associated with, if any, is the
912   // active one. If not, it likely indicates that this FeatureList has override
913   // entries from a freed FieldTrial, which may be caused by an incorrect test
914   // set up.
915   if (field_trial_list_)
916     DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
917 
918   enable_overrides->clear();
919   disable_overrides->clear();
920 
921   // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
922   // order. This is not guaranteed to users of this function, but is useful for
923   // tests to assume the order.
924   for (const auto& entry : overrides_) {
925     if (command_line_only &&
926         (entry.second.field_trial != nullptr ||
927          entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
928       continue;
929     }
930 
931     std::string* target_list = nullptr;
932     switch (entry.second.overridden_state) {
933       case OVERRIDE_USE_DEFAULT:
934       case OVERRIDE_ENABLE_FEATURE:
935         target_list = enable_overrides;
936         break;
937       case OVERRIDE_DISABLE_FEATURE:
938         target_list = disable_overrides;
939         break;
940     }
941 
942     if (!target_list->empty())
943       target_list->push_back(',');
944     if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
945       target_list->push_back('*');
946     target_list->append(entry.first);
947     if (entry.second.field_trial) {
948       auto* const field_trial = entry.second.field_trial.get();
949       target_list->push_back('<');
950       target_list->append(field_trial->trial_name());
951       if (include_group_name) {
952         target_list->push_back('.');
953         target_list->append(field_trial->GetGroupNameWithoutActivation());
954       }
955     }
956   }
957 }
958 
CheckFeatureIdentity(const Feature & feature) const959 bool FeatureList::CheckFeatureIdentity(const Feature& feature) const {
960   AutoLock auto_lock(feature_identity_tracker_lock_);
961 
962   auto it = feature_identity_tracker_.find(feature.name);
963   if (it == feature_identity_tracker_.end()) {
964     // If it's not tracked yet, register it.
965     feature_identity_tracker_[feature.name] = &feature;
966     return true;
967   }
968   // Compare address of |feature| to the existing tracked entry.
969   return it->second == &feature;
970 }
971 
IsEarlyAccessInstance() const972 bool FeatureList::IsEarlyAccessInstance() const {
973   return !allowed_feature_names_.empty();
974 }
975 
AllowFeatureAccess(const Feature & feature) const976 bool FeatureList::AllowFeatureAccess(const Feature& feature) const {
977   DCHECK(initialized_);
978   // If this isn't an instance set with SetEarlyAccessInstance all features are
979   // allowed to be checked.
980   if (!IsEarlyAccessInstance()) {
981     return true;
982   }
983   return base::Contains(allowed_feature_names_, feature.name);
984 }
985 
OverrideEntry(OverrideState overridden_state,FieldTrial * field_trial)986 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
987                                           FieldTrial* field_trial)
988     : overridden_state(overridden_state),
989       field_trial(field_trial),
990       overridden_by_field_trial(field_trial != nullptr) {}
991 
Accessor(FeatureList * feature_list)992 FeatureList::Accessor::Accessor(FeatureList* feature_list)
993     : feature_list_(feature_list) {}
994 
GetOverrideStateByFeatureName(std::string_view feature_name)995 FeatureList::OverrideState FeatureList::Accessor::GetOverrideStateByFeatureName(
996     std::string_view feature_name) {
997   return feature_list_->GetOverrideStateByFeatureName(feature_name);
998 }
999 
GetParamsByFeatureName(std::string_view feature_name,std::map<std::string,std::string> * params)1000 bool FeatureList::Accessor::GetParamsByFeatureName(
1001     std::string_view feature_name,
1002     std::map<std::string, std::string>* params) {
1003   base::FieldTrial* trial =
1004       feature_list_->GetAssociatedFieldTrialByFeatureName(feature_name);
1005   return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial,
1006                                                                        params);
1007 }
1008 
1009 }  // namespace base
1010