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/base_switches.h"
13 #include "base/debug/alias.h"
14 #include "base/debug/stack_trace.h"
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/pickle.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "build/build_config.h"
23 
24 namespace base {
25 
26 namespace {
27 
28 // Pointer to the FeatureList instance singleton that was set via
29 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
30 // have more control over initialization timing. Leaky.
31 FeatureList* g_feature_list_instance = nullptr;
32 
33 // Tracks whether the FeatureList instance was initialized via an accessor, and
34 // which Feature that accessor was for, if so.
35 const Feature* g_initialized_from_accessor = nullptr;
36 
37 #if DCHECK_IS_ON()
38 const char* g_reason_overrides_disallowed = nullptr;
39 
DCheckOverridesAllowed()40 void DCheckOverridesAllowed() {
41   const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
42   DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
43 }
44 #else
DCheckOverridesAllowed()45 void DCheckOverridesAllowed() {}
46 #endif
47 
48 // An allocator entry for a feature in shared memory. The FeatureEntry is
49 // followed by a base::Pickle object that contains the feature and trial name.
50 struct FeatureEntry {
51   // SHA1(FeatureEntry): Increment this if structure changes!
52   static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
53 
54   // Expected size for 32/64-bit check.
55   static constexpr size_t kExpectedInstanceSize = 8;
56 
57   // Specifies whether a feature override enables or disables the feature. Same
58   // values as the OverrideState enum in feature_list.h
59   uint32_t override_state;
60 
61   // Size of the pickled structure, NOT the total size of this entry.
62   uint32_t pickle_size;
63 
64   // Reads the feature and trial name from the pickle. Calling this is only
65   // valid on an initialized entry that's in shared memory.
GetFeatureAndTrialNamebase::__anon53668f4a0111::FeatureEntry66   bool GetFeatureAndTrialName(StringPiece* feature_name,
67                               StringPiece* trial_name) const {
68     const char* src =
69         reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
70 
71     Pickle pickle(src, pickle_size);
72     PickleIterator pickle_iter(pickle);
73 
74     if (!pickle_iter.ReadStringPiece(feature_name))
75       return false;
76 
77     // Return true because we are not guaranteed to have a trial name anyways.
78     auto sink = pickle_iter.ReadStringPiece(trial_name);
79     ALLOW_UNUSED_LOCAL(sink);
80     return true;
81   }
82 };
83 
84 // Some characters are not allowed to appear in feature names or the associated
85 // field trial names, as they are used as special characters for command-line
86 // serialization. This function checks that the strings are ASCII (since they
87 // are used in command-line API functions that require ASCII) and whether there
88 // are any reserved characters present, returning true if the string is valid.
89 // Only called in DCHECKs.
IsValidFeatureOrFieldTrialName(const std::string & name)90 bool IsValidFeatureOrFieldTrialName(const std::string& name) {
91   return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
92 }
93 
94 // Splits |first| into two parts by the |separator| where the first part will be
95 // returned updated in |first| and the second part will be returned as |second|.
96 // This function returns false if there is more than one |separator| in |first|.
97 // If there is no |separator| presented in |first|, this function will not
98 // modify |first| and |second|. It's used for splitting the |enable_features|
99 // flag into feature name, field trial name and feature parameters.
SplitIntoTwo(const std::string & separator,StringPiece * first,std::string * second)100 bool SplitIntoTwo(const std::string& separator,
101                   StringPiece* first,
102                   std::string* second) {
103   std::vector<StringPiece> parts =
104       SplitStringPiece(*first, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
105   if (parts.size() == 2) {
106     *second = parts[1].as_string();
107   } else if (parts.size() > 2) {
108     DLOG(ERROR) << "Only one '" << separator
109                 << "' is allowed but got: " << first->as_string();
110     return false;
111   }
112   *first = parts[0];
113   return true;
114 }
115 
116 // Checks and parses the |enable_features| flag and sets
117 // |parsed_enable_features| to be a comma-separated list of features,
118 // |force_fieldtrials| to be a comma-separated list of field trials that each
119 // feature want to associate with and |force_fieldtrial_params| to be the field
120 // trial parameters for each field trial.
121 // 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)122 bool ParseEnableFeatures(const std::string& enable_features,
123                          std::string* parsed_enable_features,
124                          std::string* force_fieldtrials,
125                          std::string* force_fieldtrial_params) {
126   std::vector<std::string> enable_features_list;
127   std::vector<std::string> force_fieldtrials_list;
128   std::vector<std::string> force_fieldtrial_params_list;
129   for (auto& enable_feature :
130        FeatureList::SplitFeatureListString(enable_features)) {
131     // First, check whether ":" is present. If true, feature parameters were
132     // set for this feature.
133     std::string feature_params;
134     if (!SplitIntoTwo(":", &enable_feature, &feature_params))
135       return false;
136     // Then, check whether "." is present. If true, a group was specified for
137     // this feature.
138     std::string group;
139     if (!SplitIntoTwo(".", &enable_feature, &group))
140       return false;
141     // Finally, check whether "<" is present. If true, a study was specified for
142     // this feature.
143     std::string study;
144     if (!SplitIntoTwo("<", &enable_feature, &study))
145       return false;
146 
147     const std::string feature_name = enable_feature.as_string();
148     // If feature params were set but group and study weren't, associate the
149     // feature and its feature params to a synthetic field trial as the
150     // feature params only make sense when it's combined with a field trial.
151     if (!feature_params.empty()) {
152       study = study.empty() ? "Study" + feature_name : study;
153       group = group.empty() ? "Group" + feature_name : group;
154       force_fieldtrials_list.push_back(study + "/" + group);
155       force_fieldtrial_params_list.push_back(study + "." + group + ":" +
156                                              feature_params);
157     }
158     enable_features_list.push_back(
159         study.empty() ? feature_name : (feature_name + "<" + study));
160   }
161 
162   *parsed_enable_features = JoinString(enable_features_list, ",");
163   // Field trial separator is currently a slash. See
164   // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
165   *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
166   *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
167   return true;
168 }
169 
170 }  // namespace
171 
172 #if defined(DCHECK_IS_CONFIGURABLE)
173 const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
174                                     FEATURE_DISABLED_BY_DEFAULT};
175 #endif  // defined(DCHECK_IS_CONFIGURABLE)
176 
177 FeatureList::FeatureList() = default;
178 
179 FeatureList::~FeatureList() = default;
180 
ScopedDisallowOverrides(const char * reason)181 FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
182     const char* reason)
183 #if DCHECK_IS_ON()
184     : previous_reason_(g_reason_overrides_disallowed) {
185   g_reason_overrides_disallowed = reason;
186 }
187 #else
188 {
189 }
190 #endif
191 
~ScopedDisallowOverrides()192 FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
193 #if DCHECK_IS_ON()
194   g_reason_overrides_disallowed = previous_reason_;
195 #endif
196 }
197 
InitializeFromCommandLine(const std::string & enable_features,const std::string & disable_features)198 void FeatureList::InitializeFromCommandLine(
199     const std::string& enable_features,
200     const std::string& disable_features) {
201   DCHECK(!initialized_);
202 
203   std::string parsed_enable_features;
204   std::string force_fieldtrials;
205   std::string force_fieldtrial_params;
206   bool parse_enable_features_result =
207       ParseEnableFeatures(enable_features, &parsed_enable_features,
208                           &force_fieldtrials, &force_fieldtrial_params);
209   DCHECK(parse_enable_features_result) << StringPrintf(
210       "The --%s list is unparsable or invalid, please check the format.",
211       ::switches::kEnableFeatures);
212 
213   // Only create field trials when field_trial_list is available. Some tests
214   // don't have field trial list available.
215   if (FieldTrialList::GetInstance()) {
216     bool associate_params_result = AssociateFieldTrialParamsFromString(
217         force_fieldtrial_params, &UnescapeValue);
218     DCHECK(associate_params_result) << StringPrintf(
219         "The field trial parameters part of the --%s list is invalid. Make "
220         "sure "
221         "you %%-encode the following characters in param values: %%:/.,",
222         ::switches::kEnableFeatures);
223 
224     bool create_trials_result =
225         FieldTrialList::CreateTrialsFromString(force_fieldtrials);
226     DCHECK(create_trials_result)
227         << StringPrintf("Invalid field trials are specified in --%s.",
228                         ::switches::kEnableFeatures);
229   }
230 
231   // Process disabled features first, so that disabled ones take precedence over
232   // enabled ones (since RegisterOverride() uses insert()).
233   RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
234   RegisterOverridesFromCommandLine(parsed_enable_features,
235                                    OVERRIDE_ENABLE_FEATURE);
236 
237   initialized_from_command_line_ = true;
238 }
239 
InitializeFromSharedMemory(PersistentMemoryAllocator * allocator)240 void FeatureList::InitializeFromSharedMemory(
241     PersistentMemoryAllocator* allocator) {
242   DCHECK(!initialized_);
243 
244   PersistentMemoryAllocator::Iterator iter(allocator);
245   const FeatureEntry* entry;
246   while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
247     OverrideState override_state =
248         static_cast<OverrideState>(entry->override_state);
249 
250     StringPiece feature_name;
251     StringPiece trial_name;
252     if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
253       continue;
254 
255     FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
256     RegisterOverride(feature_name, override_state, trial);
257   }
258 }
259 
IsFeatureOverridden(const std::string & feature_name) const260 bool FeatureList::IsFeatureOverridden(const std::string& feature_name) const {
261   return overrides_.count(feature_name);
262 }
263 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name) const264 bool FeatureList::IsFeatureOverriddenFromCommandLine(
265     const std::string& feature_name) const {
266   auto it = overrides_.find(feature_name);
267   return it != overrides_.end() && !it->second.overridden_by_field_trial;
268 }
269 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name,OverrideState state) const270 bool FeatureList::IsFeatureOverriddenFromCommandLine(
271     const std::string& feature_name,
272     OverrideState state) const {
273   auto it = overrides_.find(feature_name);
274   return it != overrides_.end() && !it->second.overridden_by_field_trial &&
275          it->second.overridden_state == state;
276 }
277 
AssociateReportingFieldTrial(const std::string & feature_name,OverrideState for_overridden_state,FieldTrial * field_trial)278 void FeatureList::AssociateReportingFieldTrial(
279     const std::string& feature_name,
280     OverrideState for_overridden_state,
281     FieldTrial* field_trial) {
282   DCHECK(
283       IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
284 
285   // Only one associated field trial is supported per feature. This is generally
286   // enforced server-side.
287   OverrideEntry* entry = &overrides_.find(feature_name)->second;
288   if (entry->field_trial) {
289     NOTREACHED() << "Feature " << feature_name
290                  << " already has trial: " << entry->field_trial->trial_name()
291                  << ", associating trial: " << field_trial->trial_name();
292     return;
293   }
294 
295   entry->field_trial = field_trial;
296 }
297 
RegisterFieldTrialOverride(const std::string & feature_name,OverrideState override_state,FieldTrial * field_trial)298 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
299                                              OverrideState override_state,
300                                              FieldTrial* field_trial) {
301   DCHECK(field_trial);
302   DCHECK(!Contains(overrides_, feature_name) ||
303          !overrides_.find(feature_name)->second.field_trial)
304       << "Feature " << feature_name
305       << " has conflicting field trial overrides: "
306       << overrides_.find(feature_name)->second.field_trial->trial_name()
307       << " / " << field_trial->trial_name()
308       << ". Please make sure that the trial (study) name is consistent across:"
309       << " (1)The server config, (2)The fieldtrial_testing_config, and"
310       << " (3) The about_flags.cc";
311 
312   RegisterOverride(feature_name, override_state, field_trial);
313 }
314 
RegisterExtraFeatureOverrides(const std::vector<FeatureOverrideInfo> & extra_overrides)315 void FeatureList::RegisterExtraFeatureOverrides(
316     const std::vector<FeatureOverrideInfo>& extra_overrides) {
317   for (const FeatureOverrideInfo& override_info : extra_overrides) {
318     RegisterOverride(override_info.first.get().name, override_info.second,
319                      /* field_trial = */ nullptr);
320   }
321 }
322 
AddFeaturesToAllocator(PersistentMemoryAllocator * allocator)323 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
324   DCHECK(initialized_);
325 
326   for (const auto& override : overrides_) {
327     Pickle pickle;
328     pickle.WriteString(override.first);
329     if (override.second.field_trial)
330       pickle.WriteString(override.second.field_trial->trial_name());
331 
332     size_t total_size = sizeof(FeatureEntry) + pickle.size();
333     FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
334     if (!entry)
335       return;
336 
337     entry->override_state = override.second.overridden_state;
338     entry->pickle_size = pickle.size();
339 
340     char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
341     memcpy(dst, pickle.data(), pickle.size());
342 
343     allocator->MakeIterable(entry);
344   }
345 }
346 
GetFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides)347 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
348                                       std::string* disable_overrides) {
349   GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
350 }
351 
GetCommandLineFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides)352 void FeatureList::GetCommandLineFeatureOverrides(
353     std::string* enable_overrides,
354     std::string* disable_overrides) {
355   GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
356 }
357 
358 // static
IsEnabled(const Feature & feature)359 bool FeatureList::IsEnabled(const Feature& feature) {
360   if (!g_feature_list_instance) {
361     g_initialized_from_accessor = &feature;
362     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
363   }
364   return g_feature_list_instance->IsFeatureEnabled(feature);
365 }
366 
367 // static
GetFieldTrial(const Feature & feature)368 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
369   if (!g_feature_list_instance) {
370     g_initialized_from_accessor = &feature;
371     return nullptr;
372   }
373   return g_feature_list_instance->GetAssociatedFieldTrial(feature);
374 }
375 
376 // static
SplitFeatureListString(StringPiece input)377 std::vector<StringPiece> FeatureList::SplitFeatureListString(
378     StringPiece input) {
379   return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
380 }
381 
382 // static
InitializeInstance(const std::string & enable_features,const std::string & disable_features)383 bool FeatureList::InitializeInstance(const std::string& enable_features,
384                                      const std::string& disable_features) {
385   return InitializeInstance(enable_features, disable_features,
386                             std::vector<FeatureOverrideInfo>());
387 }
388 
389 // static
InitializeInstance(const std::string & enable_features,const std::string & disable_features,const std::vector<FeatureOverrideInfo> & extra_overrides)390 bool FeatureList::InitializeInstance(
391     const std::string& enable_features,
392     const std::string& disable_features,
393     const std::vector<FeatureOverrideInfo>& extra_overrides) {
394   // We want to initialize a new instance here to support command-line features
395   // in testing better. For example, we initialize a dummy instance in
396   // base/test/test_suite.cc, and override it in content/browser/
397   // browser_main_loop.cc.
398   // On the other hand, we want to avoid re-initialization from command line.
399   // For example, we initialize an instance in chrome/browser/
400   // chrome_browser_main.cc and do not override it in content/browser/
401   // browser_main_loop.cc.
402   // If the singleton was previously initialized from within an accessor, we
403   // want to prevent callers from reinitializing the singleton and masking the
404   // accessor call(s) which likely returned incorrect information.
405   if (g_initialized_from_accessor) {
406     DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
407     CHECK(!g_initialized_from_accessor);
408   }
409   bool instance_existed_before = false;
410   if (g_feature_list_instance) {
411     if (g_feature_list_instance->initialized_from_command_line_)
412       return false;
413 
414     delete g_feature_list_instance;
415     g_feature_list_instance = nullptr;
416     instance_existed_before = true;
417   }
418 
419   std::unique_ptr<FeatureList> feature_list(new FeatureList);
420   feature_list->InitializeFromCommandLine(enable_features, disable_features);
421   feature_list->RegisterExtraFeatureOverrides(extra_overrides);
422   FeatureList::SetInstance(std::move(feature_list));
423   return !instance_existed_before;
424 }
425 
426 // static
GetInstance()427 FeatureList* FeatureList::GetInstance() {
428   return g_feature_list_instance;
429 }
430 
431 // static
SetInstance(std::unique_ptr<FeatureList> instance)432 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
433   DCHECK(!g_feature_list_instance);
434   instance->FinalizeInitialization();
435 
436   // Note: Intentional leak of global singleton.
437   g_feature_list_instance = instance.release();
438 
439 #if defined(DCHECK_IS_CONFIGURABLE)
440   // Update the behaviour of LOGGING_DCHECK to match the Feature configuration.
441   // DCHECK is also forced to be FATAL if we are running a death-test.
442   // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
443   // currently run through this codepath, mitigated in
444   // base::TestSuite::Initialize() for now.
445   // TODO(asvitkine): If we find other use-cases that need integrating here
446   // then define a proper API/hook for the purpose.
447   if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
448       CommandLine::ForCurrentProcess()->HasSwitch(
449           "gtest_internal_run_death_test")) {
450     logging::LOGGING_DCHECK = logging::LOG_FATAL;
451   } else {
452     logging::LOGGING_DCHECK = logging::LOG_INFO;
453   }
454 #endif  // defined(DCHECK_IS_CONFIGURABLE)
455 }
456 
457 // static
ClearInstanceForTesting()458 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
459   FeatureList* old_instance = g_feature_list_instance;
460   g_feature_list_instance = nullptr;
461   g_initialized_from_accessor = nullptr;
462   return WrapUnique(old_instance);
463 }
464 
465 // static
RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance)466 void FeatureList::RestoreInstanceForTesting(
467     std::unique_ptr<FeatureList> instance) {
468   DCHECK(!g_feature_list_instance);
469   // Note: Intentional leak of global singleton.
470   g_feature_list_instance = instance.release();
471 }
472 
FinalizeInitialization()473 void FeatureList::FinalizeInitialization() {
474   DCHECK(!initialized_);
475   // Store the field trial list pointer for DCHECKing.
476   field_trial_list_ = FieldTrialList::GetInstance();
477   initialized_ = true;
478 }
479 
IsFeatureEnabled(const Feature & feature)480 bool FeatureList::IsFeatureEnabled(const Feature& feature) {
481   DCHECK(initialized_);
482   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
483   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
484 
485   auto it = overrides_.find(feature.name);
486   if (it != overrides_.end()) {
487     const OverrideEntry& entry = it->second;
488 
489     // Activate the corresponding field trial, if necessary.
490     if (entry.field_trial)
491       entry.field_trial->group();
492 
493     // TODO(asvitkine) Expand this section as more support is added.
494 
495     // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
496     if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
497       return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
498   }
499   // Otherwise, return the default state.
500   return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
501 }
502 
GetAssociatedFieldTrial(const Feature & feature)503 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
504   DCHECK(initialized_);
505   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
506   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
507 
508   auto it = overrides_.find(feature.name);
509   if (it != overrides_.end()) {
510     const OverrideEntry& entry = it->second;
511     return entry.field_trial;
512   }
513 
514   return nullptr;
515 }
516 
RegisterOverridesFromCommandLine(const std::string & feature_list,OverrideState overridden_state)517 void FeatureList::RegisterOverridesFromCommandLine(
518     const std::string& feature_list,
519     OverrideState overridden_state) {
520   for (const auto& value : SplitFeatureListString(feature_list)) {
521     StringPiece feature_name = value;
522     FieldTrial* trial = nullptr;
523 
524     // The entry may be of the form FeatureName<FieldTrialName - in which case,
525     // this splits off the field trial name and associates it with the override.
526     std::string::size_type pos = feature_name.find('<');
527     if (pos != std::string::npos) {
528       feature_name = StringPiece(value.data(), pos);
529       trial = FieldTrialList::Find(value.substr(pos + 1).as_string());
530 #if !defined(OS_NACL)
531       // If the below DCHECK fires, it means a non-existent trial name was
532       // specified via the "Feature<Trial" command-line syntax.
533       DCHECK(trial) << "trial=" << value.substr(pos + 1);
534 #endif  // !defined(OS_NACL)
535     }
536 
537     RegisterOverride(feature_name, overridden_state, trial);
538   }
539 }
540 
RegisterOverride(StringPiece feature_name,OverrideState overridden_state,FieldTrial * field_trial)541 void FeatureList::RegisterOverride(StringPiece feature_name,
542                                    OverrideState overridden_state,
543                                    FieldTrial* field_trial) {
544   DCHECK(!initialized_);
545   DCheckOverridesAllowed();
546   if (field_trial) {
547     DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
548         << field_trial->trial_name();
549   }
550   if (StartsWith(feature_name, "*")) {
551     feature_name = feature_name.substr(1);
552     overridden_state = OVERRIDE_USE_DEFAULT;
553   }
554 
555   // Note: The semantics of insert() is that it does not overwrite the entry if
556   // one already exists for the key. Thus, only the first override for a given
557   // feature name takes effect.
558   overrides_.insert(std::make_pair(
559       feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
560 }
561 
GetFeatureOverridesImpl(std::string * enable_overrides,std::string * disable_overrides,bool command_line_only)562 void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
563                                           std::string* disable_overrides,
564                                           bool command_line_only) {
565   DCHECK(initialized_);
566 
567   // Check that the FieldTrialList this is associated with, if any, is the
568   // active one. If not, it likely indicates that this FeatureList has override
569   // entries from a freed FieldTrial, which may be caused by an incorrect test
570   // set up.
571   if (field_trial_list_)
572     DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
573 
574   enable_overrides->clear();
575   disable_overrides->clear();
576 
577   // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
578   // order. This is not guaranteed to users of this function, but is useful for
579   // tests to assume the order.
580   for (const auto& entry : overrides_) {
581     if (command_line_only &&
582         (entry.second.field_trial != nullptr ||
583          entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
584       continue;
585     }
586 
587     std::string* target_list = nullptr;
588     switch (entry.second.overridden_state) {
589       case OVERRIDE_USE_DEFAULT:
590       case OVERRIDE_ENABLE_FEATURE:
591         target_list = enable_overrides;
592         break;
593       case OVERRIDE_DISABLE_FEATURE:
594         target_list = disable_overrides;
595         break;
596     }
597 
598     if (!target_list->empty())
599       target_list->push_back(',');
600     if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
601       target_list->push_back('*');
602     target_list->append(entry.first);
603     if (entry.second.field_trial) {
604       target_list->push_back('<');
605       target_list->append(entry.second.field_trial->trial_name());
606     }
607   }
608 }
609 
CheckFeatureIdentity(const Feature & feature)610 bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
611   AutoLock auto_lock(feature_identity_tracker_lock_);
612 
613   auto it = feature_identity_tracker_.find(feature.name);
614   if (it == feature_identity_tracker_.end()) {
615     // If it's not tracked yet, register it.
616     feature_identity_tracker_[feature.name] = &feature;
617     return true;
618   }
619   // Compare address of |feature| to the existing tracked entry.
620   return it->second == &feature;
621 }
622 
OverrideEntry(OverrideState overridden_state,FieldTrial * field_trial)623 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
624                                           FieldTrial* field_trial)
625     : overridden_state(overridden_state),
626       field_trial(field_trial),
627       overridden_by_field_trial(field_trial != nullptr) {}
628 
629 }  // namespace base
630