blob: 565aeac641acca598971e65b870828ee59abae21 [file] [log] [blame]
asvitkinebccbb862015-09-04 17:17:451// 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
avi9b6f42932015-12-26 22:15:147#include <stddef.h>
8
asvitkine8423d172015-09-28 23:23:449#include <utility>
asvitkinebccbb862015-09-04 17:17:4510#include <vector>
11
Wez1dc3d68b2019-12-10 06:25:4612#include "base/debug/alias.h"
asvitkinebccbb862015-09-04 17:17:4513#include "base/logging.h"
asvitkine9499b8d2016-08-09 05:37:0714#include "base/memory/ptr_util.h"
asvitkine8423d172015-09-28 23:23:4415#include "base/metrics/field_trial.h"
lawrencewu5e03cd32016-12-05 16:23:2816#include "base/pickle.h"
asvitkinebccbb862015-09-04 17:17:4517#include "base/strings/string_split.h"
asvitkineb2e44d82015-12-01 04:10:2818#include "base/strings/string_util.h"
Alexei Svitkine8724ea502019-06-14 21:51:4619#include "build/build_config.h"
asvitkinebccbb862015-09-04 17:17:4520
21namespace base {
22
23namespace {
24
25// Pointer to the FeatureList instance singleton that was set via
26// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
27// have more control over initialization timing. Leaky.
Daniel Bratell87120182018-02-22 19:07:2628FeatureList* g_feature_list_instance = nullptr;
asvitkinebccbb862015-09-04 17:17:4529
Wez1dc3d68b2019-12-10 06:25:4630// Tracks whether the FeatureList instance was initialized via an accessor, and
31// which Feature that accessor was for, if so.
32const Feature* g_initialized_from_accessor = nullptr;
joedow958f0472016-07-07 22:08:5533
Ken Rockot30f75752019-10-12 08:07:4134#if DCHECK_IS_ON()
35const char* g_reason_overrides_disallowed = nullptr;
36
37void DCheckOverridesAllowed() {
38 const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
39 DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
40}
41#else
42void DCheckOverridesAllowed() {}
43#endif
44
lawrencewu5e03cd32016-12-05 16:23:2845// An allocator entry for a feature in shared memory. The FeatureEntry is
46// followed by a base::Pickle object that contains the feature and trial name.
lawrencewu5e03cd32016-12-05 16:23:2847struct FeatureEntry {
bcwhite3f999d32017-01-11 12:42:1348 // SHA1(FeatureEntry): Increment this if structure changes!
49 static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
50
lawrencewu5e03cd32016-12-05 16:23:2851 // Expected size for 32/64-bit check.
52 static constexpr size_t kExpectedInstanceSize = 8;
53
54 // Specifies whether a feature override enables or disables the feature. Same
55 // values as the OverrideState enum in feature_list.h
56 uint32_t override_state;
57
58 // Size of the pickled structure, NOT the total size of this entry.
59 uint32_t pickle_size;
60
61 // Reads the feature and trial name from the pickle. Calling this is only
62 // valid on an initialized entry that's in shared memory.
63 bool GetFeatureAndTrialName(StringPiece* feature_name,
64 StringPiece* trial_name) const {
65 const char* src =
66 reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
67
68 Pickle pickle(src, pickle_size);
69 PickleIterator pickle_iter(pickle);
70
71 if (!pickle_iter.ReadStringPiece(feature_name))
72 return false;
73
74 // Return true because we are not guaranteed to have a trial name anyways.
75 auto sink = pickle_iter.ReadStringPiece(trial_name);
76 ALLOW_UNUSED_LOCAL(sink);
77 return true;
78 }
79};
80
asvitkineb2e44d82015-12-01 04:10:2881// Some characters are not allowed to appear in feature names or the associated
82// field trial names, as they are used as special characters for command-line
83// serialization. This function checks that the strings are ASCII (since they
84// are used in command-line API functions that require ASCII) and whether there
85// are any reserved characters present, returning true if the string is valid.
86// Only called in DCHECKs.
87bool IsValidFeatureOrFieldTrialName(const std::string& name) {
asvitkine6d31c52e2016-03-22 15:37:5288 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
asvitkineb2e44d82015-12-01 04:10:2889}
90
asvitkinebccbb862015-09-04 17:17:4591} // namespace
92
Tomas Popelaafffa972018-11-13 20:42:0593#if defined(DCHECK_IS_CONFIGURABLE)
Weza6ca5b92018-03-23 19:03:0794const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
Alexei Svitkine8724ea502019-06-14 21:51:4695 FEATURE_DISABLED_BY_DEFAULT};
Tomas Popelaafffa972018-11-13 20:42:0596#endif // defined(DCHECK_IS_CONFIGURABLE)
Sigurdur Asgeirssonad25d872017-09-20 19:10:2997
Chris Watkinsbb7211c2017-11-29 07:16:3898FeatureList::FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:4599
Chris Watkinsbb7211c2017-11-29 07:16:38100FeatureList::~FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:45101
Ken Rockot30f75752019-10-12 08:07:41102FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
103 const char* reason)
104#if DCHECK_IS_ON()
105 : previous_reason_(g_reason_overrides_disallowed) {
106 g_reason_overrides_disallowed = reason;
107}
108#else
109{
110}
111#endif
112
113FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
114#if DCHECK_IS_ON()
115 g_reason_overrides_disallowed = previous_reason_;
116#endif
117}
118
asvitkinebccbb862015-09-04 17:17:45119void FeatureList::InitializeFromCommandLine(
120 const std::string& enable_features,
121 const std::string& disable_features) {
122 DCHECK(!initialized_);
123
124 // Process disabled features first, so that disabled ones take precedence over
125 // enabled ones (since RegisterOverride() uses insert()).
asvitkineb2e44d82015-12-01 04:10:28126 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
127 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
changwan5b9da192016-03-31 07:36:19128
129 initialized_from_command_line_ = true;
asvitkinebccbb862015-09-04 17:17:45130}
131
lawrencewu5e03cd32016-12-05 16:23:28132void FeatureList::InitializeFromSharedMemory(
133 PersistentMemoryAllocator* allocator) {
134 DCHECK(!initialized_);
135
136 PersistentMemoryAllocator::Iterator iter(allocator);
bcwhite3f999d32017-01-11 12:42:13137 const FeatureEntry* entry;
138 while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
lawrencewu5e03cd32016-12-05 16:23:28139 OverrideState override_state =
140 static_cast<OverrideState>(entry->override_state);
141
142 StringPiece feature_name;
143 StringPiece trial_name;
144 if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
145 continue;
146
147 FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
148 RegisterOverride(feature_name, override_state, trial);
149 }
150}
151
asvitkine8423d172015-09-28 23:23:44152bool FeatureList::IsFeatureOverriddenFromCommandLine(
153 const std::string& feature_name,
154 OverrideState state) const {
155 auto it = overrides_.find(feature_name);
156 return it != overrides_.end() && it->second.overridden_state == state &&
157 !it->second.overridden_by_field_trial;
158}
159
asvitkine8423d172015-09-28 23:23:44160void FeatureList::AssociateReportingFieldTrial(
161 const std::string& feature_name,
162 OverrideState for_overridden_state,
163 FieldTrial* field_trial) {
164 DCHECK(
165 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
166
167 // Only one associated field trial is supported per feature. This is generally
168 // enforced server-side.
169 OverrideEntry* entry = &overrides_.find(feature_name)->second;
170 if (entry->field_trial) {
171 NOTREACHED() << "Feature " << feature_name
172 << " already has trial: " << entry->field_trial->trial_name()
173 << ", associating trial: " << field_trial->trial_name();
174 return;
175 }
176
177 entry->field_trial = field_trial;
178}
179
asvitkine86340192015-12-01 00:45:29180void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
181 OverrideState override_state,
182 FieldTrial* field_trial) {
183 DCHECK(field_trial);
Jan Wilken Dörrief61e74c2019-06-07 08:20:02184 DCHECK(!Contains(overrides_, feature_name) ||
asvitkine86340192015-12-01 00:45:29185 !overrides_.find(feature_name)->second.field_trial)
186 << "Feature " << feature_name
187 << " has conflicting field trial overrides: "
188 << overrides_.find(feature_name)->second.field_trial->trial_name()
189 << " / " << field_trial->trial_name();
190
191 RegisterOverride(feature_name, override_state, field_trial);
192}
193
Lily Chend49e3752019-08-09 19:05:24194void FeatureList::RegisterExtraFeatureOverrides(
195 const std::vector<FeatureOverrideInfo>& extra_overrides) {
196 for (const FeatureOverrideInfo& override_info : extra_overrides) {
197 RegisterOverride(override_info.first.get().name, override_info.second,
198 /* field_trial = */ nullptr);
199 }
200}
201
lawrencewu5e03cd32016-12-05 16:23:28202void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
203 DCHECK(initialized_);
204
205 for (const auto& override : overrides_) {
206 Pickle pickle;
207 pickle.WriteString(override.first);
208 if (override.second.field_trial)
209 pickle.WriteString(override.second.field_trial->trial_name());
210
211 size_t total_size = sizeof(FeatureEntry) + pickle.size();
bcwhitecf6a9e82017-02-09 20:44:23212 FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
bcwhite3f999d32017-01-11 12:42:13213 if (!entry)
lawrencewu5e03cd32016-12-05 16:23:28214 return;
215
lawrencewu5e03cd32016-12-05 16:23:28216 entry->override_state = override.second.overridden_state;
217 entry->pickle_size = pickle.size();
218
219 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
220 memcpy(dst, pickle.data(), pickle.size());
221
bcwhite3f999d32017-01-11 12:42:13222 allocator->MakeIterable(entry);
lawrencewu5e03cd32016-12-05 16:23:28223 }
224}
225
asvitkine86340192015-12-01 00:45:29226void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
227 std::string* disable_overrides) {
Alexei Svitkine223d2282018-02-08 00:18:35228 GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
229}
asvitkine86340192015-12-01 00:45:29230
Alexei Svitkine223d2282018-02-08 00:18:35231void FeatureList::GetCommandLineFeatureOverrides(
232 std::string* enable_overrides,
233 std::string* disable_overrides) {
234 GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
asvitkine86340192015-12-01 00:45:29235}
236
asvitkinebccbb862015-09-04 17:17:45237// static
238bool FeatureList::IsEnabled(const Feature& feature) {
Daniel Bratell87120182018-02-22 19:07:26239 if (!g_feature_list_instance) {
Wez1dc3d68b2019-12-10 06:25:46240 g_initialized_from_accessor = &feature;
joedow958f0472016-07-07 22:08:55241 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
242 }
Daniel Bratell87120182018-02-22 19:07:26243 return g_feature_list_instance->IsFeatureEnabled(feature);
asvitkinebccbb862015-09-04 17:17:45244}
245
246// static
jwd07b90382016-05-06 20:39:42247FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
Daniel Bratell87120182018-02-22 19:07:26248 if (!g_feature_list_instance) {
Wez1dc3d68b2019-12-10 06:25:46249 g_initialized_from_accessor = &feature;
Alexei Svitkinec49d1f402017-09-18 23:00:41250 return nullptr;
251 }
Daniel Bratell87120182018-02-22 19:07:26252 return g_feature_list_instance->GetAssociatedFieldTrial(feature);
jwd07b90382016-05-06 20:39:42253}
254
255// static
Alexei Svitkine8724ea502019-06-14 21:51:46256std::vector<StringPiece> FeatureList::SplitFeatureListString(
257 StringPiece input) {
mgiuca30f75882017-03-28 02:07:19258 return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
asvitkine03007d02015-10-21 22:50:06259}
260
261// static
asvitkinee6be55d2016-04-04 23:29:50262bool FeatureList::InitializeInstance(const std::string& enable_features,
changwan5b9da192016-03-31 07:36:19263 const std::string& disable_features) {
Lily Chend49e3752019-08-09 19:05:24264 return InitializeInstance(enable_features, disable_features,
265 std::vector<FeatureOverrideInfo>());
266}
267
268// static
269bool FeatureList::InitializeInstance(
270 const std::string& enable_features,
271 const std::string& disable_features,
272 const std::vector<FeatureOverrideInfo>& extra_overrides) {
changwan5b9da192016-03-31 07:36:19273 // We want to initialize a new instance here to support command-line features
274 // in testing better. For example, we initialize a dummy instance in
275 // base/test/test_suite.cc, and override it in content/browser/
276 // browser_main_loop.cc.
277 // On the other hand, we want to avoid re-initialization from command line.
278 // For example, we initialize an instance in chrome/browser/
279 // chrome_browser_main.cc and do not override it in content/browser/
280 // browser_main_loop.cc.
joedow958f0472016-07-07 22:08:55281 // If the singleton was previously initialized from within an accessor, we
282 // want to prevent callers from reinitializing the singleton and masking the
283 // accessor call(s) which likely returned incorrect information.
Wez1dc3d68b2019-12-10 06:25:46284 if (g_initialized_from_accessor) {
285 DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
286 CHECK(!g_initialized_from_accessor);
287 }
asvitkinee6be55d2016-04-04 23:29:50288 bool instance_existed_before = false;
Daniel Bratell87120182018-02-22 19:07:26289 if (g_feature_list_instance) {
290 if (g_feature_list_instance->initialized_from_command_line_)
asvitkinee6be55d2016-04-04 23:29:50291 return false;
changwan5b9da192016-03-31 07:36:19292
Daniel Bratell87120182018-02-22 19:07:26293 delete g_feature_list_instance;
294 g_feature_list_instance = nullptr;
asvitkinee6be55d2016-04-04 23:29:50295 instance_existed_before = true;
changwan5b9da192016-03-31 07:36:19296 }
297
Alexei Svitkine8724ea502019-06-14 21:51:46298 std::unique_ptr<FeatureList> feature_list(new FeatureList);
changwan5b9da192016-03-31 07:36:19299 feature_list->InitializeFromCommandLine(enable_features, disable_features);
Lily Chend49e3752019-08-09 19:05:24300 feature_list->RegisterExtraFeatureOverrides(extra_overrides);
Alexei Svitkine8724ea502019-06-14 21:51:46301 FeatureList::SetInstance(std::move(feature_list));
asvitkinee6be55d2016-04-04 23:29:50302 return !instance_existed_before;
asvitkine9d96abf2015-11-02 21:52:08303}
304
305// static
asvitkinebccbb862015-09-04 17:17:45306FeatureList* FeatureList::GetInstance() {
Daniel Bratell87120182018-02-22 19:07:26307 return g_feature_list_instance;
asvitkinebccbb862015-09-04 17:17:45308}
309
310// static
dcheng093de9b2016-04-04 21:25:51311void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
Daniel Bratell87120182018-02-22 19:07:26312 DCHECK(!g_feature_list_instance);
asvitkinebccbb862015-09-04 17:17:45313 instance->FinalizeInitialization();
314
315 // Note: Intentional leak of global singleton.
Daniel Bratell87120182018-02-22 19:07:26316 g_feature_list_instance = instance.release();
Wez289477f2017-08-24 20:51:30317
Tomas Popelaafffa972018-11-13 20:42:05318#if defined(DCHECK_IS_CONFIGURABLE)
Wez289477f2017-08-24 20:51:30319 // Update the behaviour of LOG_DCHECK to match the Feature configuration.
320 // DCHECK is also forced to be FATAL if we are running a death-test.
321 // TODO(asvitkine): If we find other use-cases that need integrating here
322 // then define a proper API/hook for the purpose.
Alexei Svitkine8724ea502019-06-14 21:51:46323 if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
324 CommandLine::ForCurrentProcess()->HasSwitch(
Wez289477f2017-08-24 20:51:30325 "gtest_internal_run_death_test")) {
326 logging::LOG_DCHECK = logging::LOG_FATAL;
327 } else {
328 logging::LOG_DCHECK = logging::LOG_INFO;
329 }
Tomas Popelaafffa972018-11-13 20:42:05330#endif // defined(DCHECK_IS_CONFIGURABLE)
asvitkinebccbb862015-09-04 17:17:45331}
332
333// static
asvitkine9499b8d2016-08-09 05:37:07334std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
Daniel Bratell87120182018-02-22 19:07:26335 FeatureList* old_instance = g_feature_list_instance;
336 g_feature_list_instance = nullptr;
Wez1dc3d68b2019-12-10 06:25:46337 g_initialized_from_accessor = nullptr;
Alexei Svitkine8724ea502019-06-14 21:51:46338 return WrapUnique(old_instance);
asvitkine9499b8d2016-08-09 05:37:07339}
340
341// static
342void FeatureList::RestoreInstanceForTesting(
343 std::unique_ptr<FeatureList> instance) {
Daniel Bratell87120182018-02-22 19:07:26344 DCHECK(!g_feature_list_instance);
asvitkine9499b8d2016-08-09 05:37:07345 // Note: Intentional leak of global singleton.
Daniel Bratell87120182018-02-22 19:07:26346 g_feature_list_instance = instance.release();
asvitkinebccbb862015-09-04 17:17:45347}
348
349void FeatureList::FinalizeInitialization() {
350 DCHECK(!initialized_);
Alexei Svitkine8724ea502019-06-14 21:51:46351 // Store the field trial list pointer for DCHECKing.
352 field_trial_list_ = FieldTrialList::GetInstance();
asvitkinebccbb862015-09-04 17:17:45353 initialized_ = true;
354}
355
356bool FeatureList::IsFeatureEnabled(const Feature& feature) {
357 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28358 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45359 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
360
361 auto it = overrides_.find(feature.name);
362 if (it != overrides_.end()) {
363 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44364
365 // Activate the corresponding field trial, if necessary.
366 if (entry.field_trial)
367 entry.field_trial->group();
368
asvitkinebccbb862015-09-04 17:17:45369 // TODO(asvitkine) Expand this section as more support is added.
asvitkine64e9e112016-03-17 17:32:00370
371 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
372 if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
373 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
asvitkinebccbb862015-09-04 17:17:45374 }
375 // Otherwise, return the default state.
376 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
377}
378
jwd07b90382016-05-06 20:39:42379FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
380 DCHECK(initialized_);
381 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
382 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
383
384 auto it = overrides_.find(feature.name);
385 if (it != overrides_.end()) {
386 const OverrideEntry& entry = it->second;
387 return entry.field_trial;
388 }
389
390 return nullptr;
391}
392
asvitkineb2e44d82015-12-01 04:10:28393void FeatureList::RegisterOverridesFromCommandLine(
394 const std::string& feature_list,
395 OverrideState overridden_state) {
396 for (const auto& value : SplitFeatureListString(feature_list)) {
mgiuca30f75882017-03-28 02:07:19397 StringPiece feature_name = value;
Alexei Svitkine8724ea502019-06-14 21:51:46398 FieldTrial* trial = nullptr;
asvitkineb2e44d82015-12-01 04:10:28399
400 // The entry may be of the form FeatureName<FieldTrialName - in which case,
401 // this splits off the field trial name and associates it with the override.
402 std::string::size_type pos = feature_name.find('<');
403 if (pos != std::string::npos) {
Jan Wilken Dörrie884fe9b2020-01-29 10:16:31404 feature_name = StringPiece(value.data(), pos);
Alexei Svitkine8724ea502019-06-14 21:51:46405 trial = FieldTrialList::Find(value.substr(pos + 1).as_string());
406#if !defined(OS_NACL)
407 // If the below DCHECK fires, it means a non-existent trial name was
408 // specified via the "Feature<Trial" command-line syntax.
409 DCHECK(trial) << "trial=" << value.substr(pos + 1);
410#endif // !defined(OS_NACL)
asvitkineb2e44d82015-12-01 04:10:28411 }
412
413 RegisterOverride(feature_name, overridden_state, trial);
414 }
415}
416
417void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44418 OverrideState overridden_state,
419 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45420 DCHECK(!initialized_);
Ken Rockot30f75752019-10-12 08:07:41421 DCheckOverridesAllowed();
asvitkineb2e44d82015-12-01 04:10:28422 if (field_trial) {
423 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
424 << field_trial->trial_name();
425 }
asvitkine6d31c52e2016-03-22 15:37:52426 if (feature_name.starts_with("*")) {
427 feature_name = feature_name.substr(1);
428 overridden_state = OVERRIDE_USE_DEFAULT;
429 }
asvitkineb2e44d82015-12-01 04:10:28430
asvitkine8423d172015-09-28 23:23:44431 // Note: The semantics of insert() is that it does not overwrite the entry if
432 // one already exists for the key. Thus, only the first override for a given
433 // feature name takes effect.
434 overrides_.insert(std::make_pair(
asvitkineb2e44d82015-12-01 04:10:28435 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45436}
437
Alexei Svitkine223d2282018-02-08 00:18:35438void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
439 std::string* disable_overrides,
440 bool command_line_only) {
441 DCHECK(initialized_);
442
Alexei Svitkine8724ea502019-06-14 21:51:46443 // Check that the FieldTrialList this is associated with, if any, is the
444 // active one. If not, it likely indicates that this FeatureList has override
445 // entries from a freed FieldTrial, which may be caused by an incorrect test
446 // set up.
Mikel Astiz504e67f2019-11-28 16:25:07447 if (field_trial_list_)
448 DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
Alexei Svitkine8724ea502019-06-14 21:51:46449
Alexei Svitkine223d2282018-02-08 00:18:35450 enable_overrides->clear();
451 disable_overrides->clear();
452
453 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
454 // order. This is not guaranteed to users of this function, but is useful for
455 // tests to assume the order.
456 for (const auto& entry : overrides_) {
457 if (command_line_only &&
458 (entry.second.field_trial != nullptr ||
459 entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
460 continue;
461 }
462
463 std::string* target_list = nullptr;
464 switch (entry.second.overridden_state) {
465 case OVERRIDE_USE_DEFAULT:
466 case OVERRIDE_ENABLE_FEATURE:
467 target_list = enable_overrides;
468 break;
469 case OVERRIDE_DISABLE_FEATURE:
470 target_list = disable_overrides;
471 break;
472 }
473
474 if (!target_list->empty())
475 target_list->push_back(',');
476 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
477 target_list->push_back('*');
478 target_list->append(entry.first);
479 if (entry.second.field_trial) {
480 target_list->push_back('<');
481 target_list->append(entry.second.field_trial->trial_name());
482 }
483 }
484}
485
asvitkinebccbb862015-09-04 17:17:45486bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
487 AutoLock auto_lock(feature_identity_tracker_lock_);
488
489 auto it = feature_identity_tracker_.find(feature.name);
490 if (it == feature_identity_tracker_.end()) {
491 // If it's not tracked yet, register it.
492 feature_identity_tracker_[feature.name] = &feature;
493 return true;
494 }
495 // Compare address of |feature| to the existing tracked entry.
496 return it->second == &feature;
497}
498
asvitkine8423d172015-09-28 23:23:44499FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
500 FieldTrial* field_trial)
501 : overridden_state(overridden_state),
502 field_trial(field_trial),
503 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45504
505} // namespace base