blob: ca058265a60ff022d21e2c1515e0955ddd0c4ecc [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
Weilun Shie81c6b92020-07-06 20:33:5912#include "base/base_switches.h"
Wez1dc3d68b2019-12-10 06:25:4613#include "base/debug/alias.h"
asvitkinebccbb862015-09-04 17:17:4514#include "base/logging.h"
asvitkine9499b8d2016-08-09 05:37:0715#include "base/memory/ptr_util.h"
asvitkine8423d172015-09-28 23:23:4416#include "base/metrics/field_trial.h"
lawrencewu5e03cd32016-12-05 16:23:2817#include "base/pickle.h"
asvitkinebccbb862015-09-04 17:17:4518#include "base/strings/string_split.h"
asvitkineb2e44d82015-12-01 04:10:2819#include "base/strings/string_util.h"
Weilun Shie81c6b92020-07-06 20:33:5920#include "base/strings/stringprintf.h"
Alexei Svitkine8724ea502019-06-14 21:51:4621#include "build/build_config.h"
asvitkinebccbb862015-09-04 17:17:4522
23namespace base {
24
25namespace {
26
27// Pointer to the FeatureList instance singleton that was set via
28// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
29// have more control over initialization timing. Leaky.
Daniel Bratell87120182018-02-22 19:07:2630FeatureList* g_feature_list_instance = nullptr;
asvitkinebccbb862015-09-04 17:17:4531
Wez1dc3d68b2019-12-10 06:25:4632// Tracks whether the FeatureList instance was initialized via an accessor, and
33// which Feature that accessor was for, if so.
34const Feature* g_initialized_from_accessor = nullptr;
joedow958f0472016-07-07 22:08:5535
Ken Rockot30f75752019-10-12 08:07:4136#if DCHECK_IS_ON()
37const char* g_reason_overrides_disallowed = nullptr;
38
39void DCheckOverridesAllowed() {
40 const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
41 DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
42}
43#else
44void DCheckOverridesAllowed() {}
45#endif
46
lawrencewu5e03cd32016-12-05 16:23:2847// An allocator entry for a feature in shared memory. The FeatureEntry is
48// followed by a base::Pickle object that contains the feature and trial name.
lawrencewu5e03cd32016-12-05 16:23:2849struct FeatureEntry {
bcwhite3f999d32017-01-11 12:42:1350 // SHA1(FeatureEntry): Increment this if structure changes!
51 static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
52
lawrencewu5e03cd32016-12-05 16:23:2853 // Expected size for 32/64-bit check.
54 static constexpr size_t kExpectedInstanceSize = 8;
55
56 // Specifies whether a feature override enables or disables the feature. Same
57 // values as the OverrideState enum in feature_list.h
58 uint32_t override_state;
59
60 // Size of the pickled structure, NOT the total size of this entry.
61 uint32_t pickle_size;
62
63 // Reads the feature and trial name from the pickle. Calling this is only
64 // valid on an initialized entry that's in shared memory.
65 bool GetFeatureAndTrialName(StringPiece* feature_name,
66 StringPiece* trial_name) const {
67 const char* src =
68 reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
69
70 Pickle pickle(src, pickle_size);
71 PickleIterator pickle_iter(pickle);
72
73 if (!pickle_iter.ReadStringPiece(feature_name))
74 return false;
75
76 // Return true because we are not guaranteed to have a trial name anyways.
77 auto sink = pickle_iter.ReadStringPiece(trial_name);
78 ALLOW_UNUSED_LOCAL(sink);
79 return true;
80 }
81};
82
asvitkineb2e44d82015-12-01 04:10:2883// Some characters are not allowed to appear in feature names or the associated
84// field trial names, as they are used as special characters for command-line
85// serialization. This function checks that the strings are ASCII (since they
86// are used in command-line API functions that require ASCII) and whether there
87// are any reserved characters present, returning true if the string is valid.
88// Only called in DCHECKs.
89bool IsValidFeatureOrFieldTrialName(const std::string& name) {
asvitkine6d31c52e2016-03-22 15:37:5290 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
asvitkineb2e44d82015-12-01 04:10:2891}
92
Weilun Shie81c6b92020-07-06 20:33:5993// Splits |first| into two parts by the |separator| where the first part will be
94// returned updated in |first| and the second part will be returned as |second|.
95// This function returns false if there is more than one |separator| in |first|.
96// If there is no |separator| presented in |first|, this function will not
97// modify |first| and |second|. It's used for splitting the |enable_features|
98// flag into feature name, field trial name and feature parameters.
99bool SplitIntoTwo(const std::string& separator,
100 StringPiece* first,
101 std::string* second) {
102 std::vector<StringPiece> parts =
103 SplitStringPiece(*first, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
104 if (parts.size() == 2) {
105 *second = parts[1].as_string();
106 } else if (parts.size() > 2) {
107 DLOG(ERROR) << "Only one '" << separator
108 << "' is allowed but got: " << first->as_string();
109 return false;
110 }
111 *first = parts[0];
112 return true;
113}
114
115// Checks and parses the |enable_features| flag and sets
116// |parsed_enable_features| to be a comma-separated list of features,
117// |force_fieldtrials| to be a comma-separated list of field trials that each
118// feature want to associate with and |force_fieldtrial_params| to be the field
119// trial parameters for each field trial.
120// Returns true if |enable_features| is parsable, otherwise false.
121bool ParseEnableFeatures(const std::string& enable_features,
122 std::string* parsed_enable_features,
123 std::string* force_fieldtrials,
124 std::string* force_fieldtrial_params) {
125 std::vector<std::string> enable_features_list;
126 std::vector<std::string> force_fieldtrials_list;
127 std::vector<std::string> force_fieldtrial_params_list;
128 for (auto& enable_feature :
129 FeatureList::SplitFeatureListString(enable_features)) {
130 // First, check whether ":" is present. If true, feature parameters were
131 // set for this feature.
132 std::string feature_params;
133 if (!SplitIntoTwo(":", &enable_feature, &feature_params))
134 return false;
135 // Then, check whether "." is present. If true, a group was specified for
136 // this feature.
137 std::string group;
138 if (!SplitIntoTwo(".", &enable_feature, &group))
139 return false;
140 // Finally, check whether "<" is present. If true, a study was specified for
141 // this feature.
142 std::string study;
143 if (!SplitIntoTwo("<", &enable_feature, &study))
144 return false;
145
146 const std::string feature_name = enable_feature.as_string();
147 // If feature params were set but group and study weren't, associate the
148 // feature and its feature params to a synthetic field trial as the
149 // feature params only make sense when it's combined with a field trial.
150 if (!feature_params.empty()) {
151 study = study.empty() ? "Study" + feature_name : study;
152 group = group.empty() ? "Group" + feature_name : group;
153 force_fieldtrials_list.push_back(study + "/" + group);
154 force_fieldtrial_params_list.push_back(study + "." + group + ":" +
155 feature_params);
156 }
157 enable_features_list.push_back(
158 study.empty() ? feature_name : (feature_name + "<" + study));
159 }
160
161 *parsed_enable_features = JoinString(enable_features_list, ",");
162 // Field trial separator is currently a slash. See
163 // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
164 *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
165 *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
166 return true;
167}
168
asvitkinebccbb862015-09-04 17:17:45169} // namespace
170
Tomas Popelaafffa972018-11-13 20:42:05171#if defined(DCHECK_IS_CONFIGURABLE)
Weza6ca5b92018-03-23 19:03:07172const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
Alexei Svitkine8724ea502019-06-14 21:51:46173 FEATURE_DISABLED_BY_DEFAULT};
Tomas Popelaafffa972018-11-13 20:42:05174#endif // defined(DCHECK_IS_CONFIGURABLE)
Sigurdur Asgeirssonad25d872017-09-20 19:10:29175
Chris Watkinsbb7211c2017-11-29 07:16:38176FeatureList::FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:45177
Chris Watkinsbb7211c2017-11-29 07:16:38178FeatureList::~FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:45179
Ken Rockot30f75752019-10-12 08:07:41180FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
181 const char* reason)
182#if DCHECK_IS_ON()
183 : previous_reason_(g_reason_overrides_disallowed) {
184 g_reason_overrides_disallowed = reason;
185}
186#else
187{
188}
189#endif
190
191FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
192#if DCHECK_IS_ON()
193 g_reason_overrides_disallowed = previous_reason_;
194#endif
195}
196
asvitkinebccbb862015-09-04 17:17:45197void FeatureList::InitializeFromCommandLine(
198 const std::string& enable_features,
199 const std::string& disable_features) {
200 DCHECK(!initialized_);
201
Weilun Shie81c6b92020-07-06 20:33:59202 std::string parsed_enable_features;
203 std::string force_fieldtrials;
204 std::string force_fieldtrial_params;
205 bool parse_enable_features_result =
206 ParseEnableFeatures(enable_features, &parsed_enable_features,
207 &force_fieldtrials, &force_fieldtrial_params);
208 DCHECK(parse_enable_features_result) << StringPrintf(
209 "The --%s list is unparsable or invalid, please check the format.",
210 ::switches::kEnableFeatures);
Weilun Shie81c6b92020-07-06 20:33:59211
Weilun Shi1cd8fb92020-07-17 23:31:00212 // Only create field trials when field_trial_list is available. Some tests
213 // don't have field trial list available.
214 if (FieldTrialList::GetInstance()) {
215 bool associate_params_result = AssociateFieldTrialParamsFromString(
216 force_fieldtrial_params, &UnescapeValue);
217 DCHECK(associate_params_result) << StringPrintf(
218 "The field trial parameters part of the --%s list is invalid. Make "
219 "sure "
220 "you %%-encode the following characters in param values: %%:/.,",
221 ::switches::kEnableFeatures);
Weilun Shie81c6b92020-07-06 20:33:59222
Weilun Shi1cd8fb92020-07-17 23:31:00223 bool create_trials_result =
224 FieldTrialList::CreateTrialsFromString(force_fieldtrials);
225 DCHECK(create_trials_result)
226 << StringPrintf("Invalid field trials are specified in --%s.",
227 ::switches::kEnableFeatures);
228 }
229
230 // Process disabled features first, so that disabled ones take precedence over
231 // enabled ones (since RegisterOverride() uses insert()).
232 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
233 RegisterOverridesFromCommandLine(parsed_enable_features,
234 OVERRIDE_ENABLE_FEATURE);
235
236 initialized_from_command_line_ = true;
Weilun Shie81c6b92020-07-06 20:33:59237}
238
lawrencewu5e03cd32016-12-05 16:23:28239void FeatureList::InitializeFromSharedMemory(
240 PersistentMemoryAllocator* allocator) {
241 DCHECK(!initialized_);
242
243 PersistentMemoryAllocator::Iterator iter(allocator);
bcwhite3f999d32017-01-11 12:42:13244 const FeatureEntry* entry;
245 while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
lawrencewu5e03cd32016-12-05 16:23:28246 OverrideState override_state =
247 static_cast<OverrideState>(entry->override_state);
248
249 StringPiece feature_name;
250 StringPiece trial_name;
251 if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
252 continue;
253
254 FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
255 RegisterOverride(feature_name, override_state, trial);
256 }
257}
258
asvitkine8423d172015-09-28 23:23:44259bool FeatureList::IsFeatureOverriddenFromCommandLine(
260 const std::string& feature_name,
261 OverrideState state) const {
262 auto it = overrides_.find(feature_name);
263 return it != overrides_.end() && it->second.overridden_state == state &&
264 !it->second.overridden_by_field_trial;
265}
266
asvitkine8423d172015-09-28 23:23:44267void FeatureList::AssociateReportingFieldTrial(
268 const std::string& feature_name,
269 OverrideState for_overridden_state,
270 FieldTrial* field_trial) {
271 DCHECK(
272 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
273
274 // Only one associated field trial is supported per feature. This is generally
275 // enforced server-side.
276 OverrideEntry* entry = &overrides_.find(feature_name)->second;
277 if (entry->field_trial) {
278 NOTREACHED() << "Feature " << feature_name
279 << " already has trial: " << entry->field_trial->trial_name()
280 << ", associating trial: " << field_trial->trial_name();
281 return;
282 }
283
284 entry->field_trial = field_trial;
285}
286
asvitkine86340192015-12-01 00:45:29287void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
288 OverrideState override_state,
289 FieldTrial* field_trial) {
290 DCHECK(field_trial);
Jan Wilken Dörrief61e74c2019-06-07 08:20:02291 DCHECK(!Contains(overrides_, feature_name) ||
asvitkine86340192015-12-01 00:45:29292 !overrides_.find(feature_name)->second.field_trial)
293 << "Feature " << feature_name
294 << " has conflicting field trial overrides: "
295 << overrides_.find(feature_name)->second.field_trial->trial_name()
Mei Liange569c272020-03-06 02:41:59296 << " / " << field_trial->trial_name()
297 << ". Please make sure that the trial (study) name is consistent across:"
298 << " (1)The server config, (2)The fieldtrial_testing_config, and"
299 << " (3) The about_flags.cc";
asvitkine86340192015-12-01 00:45:29300
301 RegisterOverride(feature_name, override_state, field_trial);
302}
303
Lily Chend49e3752019-08-09 19:05:24304void FeatureList::RegisterExtraFeatureOverrides(
305 const std::vector<FeatureOverrideInfo>& extra_overrides) {
306 for (const FeatureOverrideInfo& override_info : extra_overrides) {
307 RegisterOverride(override_info.first.get().name, override_info.second,
308 /* field_trial = */ nullptr);
309 }
310}
311
lawrencewu5e03cd32016-12-05 16:23:28312void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
313 DCHECK(initialized_);
314
315 for (const auto& override : overrides_) {
316 Pickle pickle;
317 pickle.WriteString(override.first);
318 if (override.second.field_trial)
319 pickle.WriteString(override.second.field_trial->trial_name());
320
321 size_t total_size = sizeof(FeatureEntry) + pickle.size();
bcwhitecf6a9e82017-02-09 20:44:23322 FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
bcwhite3f999d32017-01-11 12:42:13323 if (!entry)
lawrencewu5e03cd32016-12-05 16:23:28324 return;
325
lawrencewu5e03cd32016-12-05 16:23:28326 entry->override_state = override.second.overridden_state;
327 entry->pickle_size = pickle.size();
328
329 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
330 memcpy(dst, pickle.data(), pickle.size());
331
bcwhite3f999d32017-01-11 12:42:13332 allocator->MakeIterable(entry);
lawrencewu5e03cd32016-12-05 16:23:28333 }
334}
335
asvitkine86340192015-12-01 00:45:29336void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
337 std::string* disable_overrides) {
Alexei Svitkine223d2282018-02-08 00:18:35338 GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
339}
asvitkine86340192015-12-01 00:45:29340
Alexei Svitkine223d2282018-02-08 00:18:35341void FeatureList::GetCommandLineFeatureOverrides(
342 std::string* enable_overrides,
343 std::string* disable_overrides) {
344 GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
asvitkine86340192015-12-01 00:45:29345}
346
asvitkinebccbb862015-09-04 17:17:45347// static
348bool FeatureList::IsEnabled(const Feature& feature) {
Daniel Bratell87120182018-02-22 19:07:26349 if (!g_feature_list_instance) {
Wez1dc3d68b2019-12-10 06:25:46350 g_initialized_from_accessor = &feature;
joedow958f0472016-07-07 22:08:55351 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
352 }
Daniel Bratell87120182018-02-22 19:07:26353 return g_feature_list_instance->IsFeatureEnabled(feature);
asvitkinebccbb862015-09-04 17:17:45354}
355
356// static
jwd07b90382016-05-06 20:39:42357FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
Daniel Bratell87120182018-02-22 19:07:26358 if (!g_feature_list_instance) {
Wez1dc3d68b2019-12-10 06:25:46359 g_initialized_from_accessor = &feature;
Alexei Svitkinec49d1f402017-09-18 23:00:41360 return nullptr;
361 }
Daniel Bratell87120182018-02-22 19:07:26362 return g_feature_list_instance->GetAssociatedFieldTrial(feature);
jwd07b90382016-05-06 20:39:42363}
364
365// static
Alexei Svitkine8724ea502019-06-14 21:51:46366std::vector<StringPiece> FeatureList::SplitFeatureListString(
367 StringPiece input) {
mgiuca30f75882017-03-28 02:07:19368 return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
asvitkine03007d02015-10-21 22:50:06369}
370
371// static
asvitkinee6be55d2016-04-04 23:29:50372bool FeatureList::InitializeInstance(const std::string& enable_features,
changwan5b9da192016-03-31 07:36:19373 const std::string& disable_features) {
Lily Chend49e3752019-08-09 19:05:24374 return InitializeInstance(enable_features, disable_features,
375 std::vector<FeatureOverrideInfo>());
376}
377
378// static
379bool FeatureList::InitializeInstance(
380 const std::string& enable_features,
381 const std::string& disable_features,
382 const std::vector<FeatureOverrideInfo>& extra_overrides) {
changwan5b9da192016-03-31 07:36:19383 // We want to initialize a new instance here to support command-line features
384 // in testing better. For example, we initialize a dummy instance in
385 // base/test/test_suite.cc, and override it in content/browser/
386 // browser_main_loop.cc.
387 // On the other hand, we want to avoid re-initialization from command line.
388 // For example, we initialize an instance in chrome/browser/
389 // chrome_browser_main.cc and do not override it in content/browser/
390 // browser_main_loop.cc.
joedow958f0472016-07-07 22:08:55391 // If the singleton was previously initialized from within an accessor, we
392 // want to prevent callers from reinitializing the singleton and masking the
393 // accessor call(s) which likely returned incorrect information.
Wez1dc3d68b2019-12-10 06:25:46394 if (g_initialized_from_accessor) {
395 DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
396 CHECK(!g_initialized_from_accessor);
397 }
asvitkinee6be55d2016-04-04 23:29:50398 bool instance_existed_before = false;
Daniel Bratell87120182018-02-22 19:07:26399 if (g_feature_list_instance) {
400 if (g_feature_list_instance->initialized_from_command_line_)
asvitkinee6be55d2016-04-04 23:29:50401 return false;
changwan5b9da192016-03-31 07:36:19402
Daniel Bratell87120182018-02-22 19:07:26403 delete g_feature_list_instance;
404 g_feature_list_instance = nullptr;
asvitkinee6be55d2016-04-04 23:29:50405 instance_existed_before = true;
changwan5b9da192016-03-31 07:36:19406 }
407
Alexei Svitkine8724ea502019-06-14 21:51:46408 std::unique_ptr<FeatureList> feature_list(new FeatureList);
changwan5b9da192016-03-31 07:36:19409 feature_list->InitializeFromCommandLine(enable_features, disable_features);
Lily Chend49e3752019-08-09 19:05:24410 feature_list->RegisterExtraFeatureOverrides(extra_overrides);
Alexei Svitkine8724ea502019-06-14 21:51:46411 FeatureList::SetInstance(std::move(feature_list));
asvitkinee6be55d2016-04-04 23:29:50412 return !instance_existed_before;
asvitkine9d96abf2015-11-02 21:52:08413}
414
415// static
asvitkinebccbb862015-09-04 17:17:45416FeatureList* FeatureList::GetInstance() {
Daniel Bratell87120182018-02-22 19:07:26417 return g_feature_list_instance;
asvitkinebccbb862015-09-04 17:17:45418}
419
420// static
dcheng093de9b2016-04-04 21:25:51421void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
Daniel Bratell87120182018-02-22 19:07:26422 DCHECK(!g_feature_list_instance);
asvitkinebccbb862015-09-04 17:17:45423 instance->FinalizeInitialization();
424
425 // Note: Intentional leak of global singleton.
Daniel Bratell87120182018-02-22 19:07:26426 g_feature_list_instance = instance.release();
Wez289477f2017-08-24 20:51:30427
Tomas Popelaafffa972018-11-13 20:42:05428#if defined(DCHECK_IS_CONFIGURABLE)
Wez289477f2017-08-24 20:51:30429 // Update the behaviour of LOG_DCHECK to match the Feature configuration.
430 // DCHECK is also forced to be FATAL if we are running a death-test.
Gabriel Charettee9421ec2020-05-21 18:20:03431 // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
432 // currently run through this codepath, mitigated in
433 // base::TestSuite::Initialize() for now.
Wez289477f2017-08-24 20:51:30434 // TODO(asvitkine): If we find other use-cases that need integrating here
435 // then define a proper API/hook for the purpose.
Alexei Svitkine8724ea502019-06-14 21:51:46436 if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
437 CommandLine::ForCurrentProcess()->HasSwitch(
Wez289477f2017-08-24 20:51:30438 "gtest_internal_run_death_test")) {
439 logging::LOG_DCHECK = logging::LOG_FATAL;
440 } else {
441 logging::LOG_DCHECK = logging::LOG_INFO;
442 }
Tomas Popelaafffa972018-11-13 20:42:05443#endif // defined(DCHECK_IS_CONFIGURABLE)
asvitkinebccbb862015-09-04 17:17:45444}
445
446// static
asvitkine9499b8d2016-08-09 05:37:07447std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
Daniel Bratell87120182018-02-22 19:07:26448 FeatureList* old_instance = g_feature_list_instance;
449 g_feature_list_instance = nullptr;
Wez1dc3d68b2019-12-10 06:25:46450 g_initialized_from_accessor = nullptr;
Alexei Svitkine8724ea502019-06-14 21:51:46451 return WrapUnique(old_instance);
asvitkine9499b8d2016-08-09 05:37:07452}
453
454// static
455void FeatureList::RestoreInstanceForTesting(
456 std::unique_ptr<FeatureList> instance) {
Daniel Bratell87120182018-02-22 19:07:26457 DCHECK(!g_feature_list_instance);
asvitkine9499b8d2016-08-09 05:37:07458 // Note: Intentional leak of global singleton.
Daniel Bratell87120182018-02-22 19:07:26459 g_feature_list_instance = instance.release();
asvitkinebccbb862015-09-04 17:17:45460}
461
462void FeatureList::FinalizeInitialization() {
463 DCHECK(!initialized_);
Alexei Svitkine8724ea502019-06-14 21:51:46464 // Store the field trial list pointer for DCHECKing.
465 field_trial_list_ = FieldTrialList::GetInstance();
asvitkinebccbb862015-09-04 17:17:45466 initialized_ = true;
467}
468
469bool FeatureList::IsFeatureEnabled(const Feature& feature) {
470 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28471 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45472 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
473
474 auto it = overrides_.find(feature.name);
475 if (it != overrides_.end()) {
476 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44477
478 // Activate the corresponding field trial, if necessary.
479 if (entry.field_trial)
480 entry.field_trial->group();
481
asvitkinebccbb862015-09-04 17:17:45482 // TODO(asvitkine) Expand this section as more support is added.
asvitkine64e9e112016-03-17 17:32:00483
484 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
485 if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
486 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
asvitkinebccbb862015-09-04 17:17:45487 }
488 // Otherwise, return the default state.
489 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
490}
491
jwd07b90382016-05-06 20:39:42492FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
493 DCHECK(initialized_);
494 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
495 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
496
497 auto it = overrides_.find(feature.name);
498 if (it != overrides_.end()) {
499 const OverrideEntry& entry = it->second;
500 return entry.field_trial;
501 }
502
503 return nullptr;
504}
505
asvitkineb2e44d82015-12-01 04:10:28506void FeatureList::RegisterOverridesFromCommandLine(
507 const std::string& feature_list,
508 OverrideState overridden_state) {
509 for (const auto& value : SplitFeatureListString(feature_list)) {
mgiuca30f75882017-03-28 02:07:19510 StringPiece feature_name = value;
Alexei Svitkine8724ea502019-06-14 21:51:46511 FieldTrial* trial = nullptr;
asvitkineb2e44d82015-12-01 04:10:28512
513 // The entry may be of the form FeatureName<FieldTrialName - in which case,
514 // this splits off the field trial name and associates it with the override.
515 std::string::size_type pos = feature_name.find('<');
516 if (pos != std::string::npos) {
Jan Wilken Dörrie884fe9b2020-01-29 10:16:31517 feature_name = StringPiece(value.data(), pos);
Alexei Svitkine8724ea502019-06-14 21:51:46518 trial = FieldTrialList::Find(value.substr(pos + 1).as_string());
519#if !defined(OS_NACL)
520 // If the below DCHECK fires, it means a non-existent trial name was
521 // specified via the "Feature<Trial" command-line syntax.
522 DCHECK(trial) << "trial=" << value.substr(pos + 1);
523#endif // !defined(OS_NACL)
asvitkineb2e44d82015-12-01 04:10:28524 }
525
526 RegisterOverride(feature_name, overridden_state, trial);
527 }
528}
529
530void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44531 OverrideState overridden_state,
532 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45533 DCHECK(!initialized_);
Ken Rockot30f75752019-10-12 08:07:41534 DCheckOverridesAllowed();
asvitkineb2e44d82015-12-01 04:10:28535 if (field_trial) {
536 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
537 << field_trial->trial_name();
538 }
Jan Wilken Dörrief05bb102020-08-18 19:35:56539 if (StartsWith(feature_name, "*")) {
asvitkine6d31c52e2016-03-22 15:37:52540 feature_name = feature_name.substr(1);
541 overridden_state = OVERRIDE_USE_DEFAULT;
542 }
asvitkineb2e44d82015-12-01 04:10:28543
asvitkine8423d172015-09-28 23:23:44544 // Note: The semantics of insert() is that it does not overwrite the entry if
545 // one already exists for the key. Thus, only the first override for a given
546 // feature name takes effect.
547 overrides_.insert(std::make_pair(
asvitkineb2e44d82015-12-01 04:10:28548 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45549}
550
Alexei Svitkine223d2282018-02-08 00:18:35551void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
552 std::string* disable_overrides,
553 bool command_line_only) {
554 DCHECK(initialized_);
555
Alexei Svitkine8724ea502019-06-14 21:51:46556 // Check that the FieldTrialList this is associated with, if any, is the
557 // active one. If not, it likely indicates that this FeatureList has override
558 // entries from a freed FieldTrial, which may be caused by an incorrect test
559 // set up.
Mikel Astiz504e67f2019-11-28 16:25:07560 if (field_trial_list_)
561 DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
Alexei Svitkine8724ea502019-06-14 21:51:46562
Alexei Svitkine223d2282018-02-08 00:18:35563 enable_overrides->clear();
564 disable_overrides->clear();
565
566 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
567 // order. This is not guaranteed to users of this function, but is useful for
568 // tests to assume the order.
569 for (const auto& entry : overrides_) {
570 if (command_line_only &&
571 (entry.second.field_trial != nullptr ||
572 entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
573 continue;
574 }
575
576 std::string* target_list = nullptr;
577 switch (entry.second.overridden_state) {
578 case OVERRIDE_USE_DEFAULT:
579 case OVERRIDE_ENABLE_FEATURE:
580 target_list = enable_overrides;
581 break;
582 case OVERRIDE_DISABLE_FEATURE:
583 target_list = disable_overrides;
584 break;
585 }
586
587 if (!target_list->empty())
588 target_list->push_back(',');
589 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
590 target_list->push_back('*');
591 target_list->append(entry.first);
592 if (entry.second.field_trial) {
593 target_list->push_back('<');
594 target_list->append(entry.second.field_trial->trial_name());
595 }
596 }
597}
598
asvitkinebccbb862015-09-04 17:17:45599bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
600 AutoLock auto_lock(feature_identity_tracker_lock_);
601
602 auto it = feature_identity_tracker_.find(feature.name);
603 if (it == feature_identity_tracker_.end()) {
604 // If it's not tracked yet, register it.
605 feature_identity_tracker_[feature.name] = &feature;
606 return true;
607 }
608 // Compare address of |feature| to the existing tracked entry.
609 return it->second == &feature;
610}
611
asvitkine8423d172015-09-28 23:23:44612FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
613 FieldTrial* field_trial)
614 : overridden_state(overridden_state),
615 field_trial(field_trial),
616 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45617
618} // namespace base