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