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