blob: d62c785652838ce27d90ebaaf5c66aa1f1d40c70 [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"
Joel Fernandes541c6d02021-05-10 13:42:166#include <string>
asvitkinebccbb862015-09-04 17:17:457
Hans Wennborga8c0b332021-05-06 14:48:088// feature_list.h is a widely included header and its size impacts build
9// time. Try not to raise this limit unless necessary. See
10// https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
11#ifndef NACL_TC_REV
Peter Boströma709c13c2021-07-22 20:43:2712#pragma clang max_tokens_here 530000
Hans Wennborga8c0b332021-05-06 14:48:0813#endif
14
avi9b6f42932015-12-26 22:15:1415#include <stddef.h>
16
Will Harris196c69c2020-12-15 22:57:4817#include "base/base_paths.h"
Weilun Shie81c6b92020-07-06 20:33:5918#include "base/base_switches.h"
Lei Zhangc0f9fc52021-05-22 08:00:5319#include "base/containers/contains.h"
Wez1dc3d68b2019-12-10 06:25:4620#include "base/debug/alias.h"
Joel Fernandes034037c2020-09-11 05:16:2021#include "base/debug/stack_trace.h"
asvitkinebccbb862015-09-04 17:17:4522#include "base/logging.h"
asvitkine9499b8d2016-08-09 05:37:0723#include "base/memory/ptr_util.h"
asvitkine8423d172015-09-28 23:23:4424#include "base/metrics/field_trial.h"
Hans Wennborga8c0b332021-05-06 14:48:0825#include "base/metrics/persistent_memory_allocator.h"
Will Harris196c69c2020-12-15 22:57:4826#include "base/path_service.h"
lawrencewu5e03cd32016-12-05 16:23:2827#include "base/pickle.h"
Jan Wilken Dörrie5db50ac2021-02-15 11:43:1628#include "base/strings/string_piece.h"
asvitkinebccbb862015-09-04 17:17:4529#include "base/strings/string_split.h"
asvitkineb2e44d82015-12-01 04:10:2830#include "base/strings/string_util.h"
Weilun Shie81c6b92020-07-06 20:33:5931#include "base/strings/stringprintf.h"
Alexei Svitkine8724ea502019-06-14 21:51:4632#include "build/build_config.h"
asvitkinebccbb862015-09-04 17:17:4533
34namespace base {
35
36namespace {
37
38// Pointer to the FeatureList instance singleton that was set via
39// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
40// have more control over initialization timing. Leaky.
Daniel Bratell87120182018-02-22 19:07:2641FeatureList* g_feature_list_instance = nullptr;
asvitkinebccbb862015-09-04 17:17:4542
Wez1dc3d68b2019-12-10 06:25:4643// Tracks whether the FeatureList instance was initialized via an accessor, and
44// which Feature that accessor was for, if so.
45const Feature* g_initialized_from_accessor = nullptr;
joedow958f0472016-07-07 22:08:5546
Ken Rockot30f75752019-10-12 08:07:4147#if DCHECK_IS_ON()
Will Harris196c69c2020-12-15 22:57:4848// Tracks whether the use of base::Feature is allowed for this module.
49// See ForbidUseForCurrentModule().
50bool g_use_allowed = true;
51
Ken Rockot30f75752019-10-12 08:07:4152const char* g_reason_overrides_disallowed = nullptr;
53
54void DCheckOverridesAllowed() {
55 const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
56 DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
57}
58#else
59void DCheckOverridesAllowed() {}
60#endif
61
lawrencewu5e03cd32016-12-05 16:23:2862// An allocator entry for a feature in shared memory. The FeatureEntry is
63// followed by a base::Pickle object that contains the feature and trial name.
lawrencewu5e03cd32016-12-05 16:23:2864struct FeatureEntry {
bcwhite3f999d32017-01-11 12:42:1365 // SHA1(FeatureEntry): Increment this if structure changes!
66 static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
67
lawrencewu5e03cd32016-12-05 16:23:2868 // Expected size for 32/64-bit check.
69 static constexpr size_t kExpectedInstanceSize = 8;
70
71 // Specifies whether a feature override enables or disables the feature. Same
72 // values as the OverrideState enum in feature_list.h
73 uint32_t override_state;
74
75 // Size of the pickled structure, NOT the total size of this entry.
76 uint32_t pickle_size;
77
78 // Reads the feature and trial name from the pickle. Calling this is only
79 // valid on an initialized entry that's in shared memory.
80 bool GetFeatureAndTrialName(StringPiece* feature_name,
81 StringPiece* trial_name) const {
82 const char* src =
83 reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
84
85 Pickle pickle(src, pickle_size);
86 PickleIterator pickle_iter(pickle);
87
88 if (!pickle_iter.ReadStringPiece(feature_name))
89 return false;
90
91 // Return true because we are not guaranteed to have a trial name anyways.
92 auto sink = pickle_iter.ReadStringPiece(trial_name);
93 ALLOW_UNUSED_LOCAL(sink);
94 return true;
95 }
96};
97
asvitkineb2e44d82015-12-01 04:10:2898// Some characters are not allowed to appear in feature names or the associated
99// field trial names, as they are used as special characters for command-line
100// serialization. This function checks that the strings are ASCII (since they
101// are used in command-line API functions that require ASCII) and whether there
102// are any reserved characters present, returning true if the string is valid.
103// Only called in DCHECKs.
104bool IsValidFeatureOrFieldTrialName(const std::string& name) {
asvitkine6d31c52e2016-03-22 15:37:52105 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
asvitkineb2e44d82015-12-01 04:10:28106}
107
Weilun Shie81c6b92020-07-06 20:33:59108// Splits |first| into two parts by the |separator| where the first part will be
109// returned updated in |first| and the second part will be returned as |second|.
110// This function returns false if there is more than one |separator| in |first|.
111// If there is no |separator| presented in |first|, this function will not
112// modify |first| and |second|. It's used for splitting the |enable_features|
113// flag into feature name, field trial name and feature parameters.
114bool SplitIntoTwo(const std::string& separator,
115 StringPiece* first,
116 std::string* second) {
117 std::vector<StringPiece> parts =
118 SplitStringPiece(*first, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
119 if (parts.size() == 2) {
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16120 *second = std::string(parts[1]);
Weilun Shie81c6b92020-07-06 20:33:59121 } else if (parts.size() > 2) {
122 DLOG(ERROR) << "Only one '" << separator
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16123 << "' is allowed but got: " << *first;
Weilun Shie81c6b92020-07-06 20:33:59124 return false;
125 }
126 *first = parts[0];
127 return true;
128}
129
130// Checks and parses the |enable_features| flag and sets
131// |parsed_enable_features| to be a comma-separated list of features,
132// |force_fieldtrials| to be a comma-separated list of field trials that each
133// feature want to associate with and |force_fieldtrial_params| to be the field
134// trial parameters for each field trial.
135// Returns true if |enable_features| is parsable, otherwise false.
136bool ParseEnableFeatures(const std::string& enable_features,
137 std::string* parsed_enable_features,
138 std::string* force_fieldtrials,
139 std::string* force_fieldtrial_params) {
140 std::vector<std::string> enable_features_list;
141 std::vector<std::string> force_fieldtrials_list;
142 std::vector<std::string> force_fieldtrial_params_list;
143 for (auto& enable_feature :
144 FeatureList::SplitFeatureListString(enable_features)) {
145 // First, check whether ":" is present. If true, feature parameters were
146 // set for this feature.
147 std::string feature_params;
148 if (!SplitIntoTwo(":", &enable_feature, &feature_params))
149 return false;
150 // Then, check whether "." is present. If true, a group was specified for
151 // this feature.
152 std::string group;
153 if (!SplitIntoTwo(".", &enable_feature, &group))
154 return false;
155 // Finally, check whether "<" is present. If true, a study was specified for
156 // this feature.
157 std::string study;
158 if (!SplitIntoTwo("<", &enable_feature, &study))
159 return false;
160
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16161 const std::string feature_name(enable_feature);
Weilun Shie81c6b92020-07-06 20:33:59162 // If feature params were set but group and study weren't, associate the
163 // feature and its feature params to a synthetic field trial as the
164 // feature params only make sense when it's combined with a field trial.
165 if (!feature_params.empty()) {
166 study = study.empty() ? "Study" + feature_name : study;
167 group = group.empty() ? "Group" + feature_name : group;
168 force_fieldtrials_list.push_back(study + "/" + group);
169 force_fieldtrial_params_list.push_back(study + "." + group + ":" +
170 feature_params);
171 }
172 enable_features_list.push_back(
173 study.empty() ? feature_name : (feature_name + "<" + study));
174 }
175
176 *parsed_enable_features = JoinString(enable_features_list, ",");
177 // Field trial separator is currently a slash. See
178 // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
179 *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
180 *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
181 return true;
182}
183
asvitkinebccbb862015-09-04 17:17:45184} // namespace
185
Tomas Popelaafffa972018-11-13 20:42:05186#if defined(DCHECK_IS_CONFIGURABLE)
Weza6ca5b92018-03-23 19:03:07187const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
Alexei Svitkine8724ea502019-06-14 21:51:46188 FEATURE_DISABLED_BY_DEFAULT};
Tomas Popelaafffa972018-11-13 20:42:05189#endif // defined(DCHECK_IS_CONFIGURABLE)
Sigurdur Asgeirssonad25d872017-09-20 19:10:29190
Chris Watkinsbb7211c2017-11-29 07:16:38191FeatureList::FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:45192
Chris Watkinsbb7211c2017-11-29 07:16:38193FeatureList::~FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:45194
Ken Rockot30f75752019-10-12 08:07:41195FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
196 const char* reason)
197#if DCHECK_IS_ON()
198 : previous_reason_(g_reason_overrides_disallowed) {
199 g_reason_overrides_disallowed = reason;
200}
201#else
202{
203}
204#endif
205
206FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
207#if DCHECK_IS_ON()
208 g_reason_overrides_disallowed = previous_reason_;
209#endif
210}
211
asvitkinebccbb862015-09-04 17:17:45212void FeatureList::InitializeFromCommandLine(
213 const std::string& enable_features,
214 const std::string& disable_features) {
215 DCHECK(!initialized_);
216
Weilun Shie81c6b92020-07-06 20:33:59217 std::string parsed_enable_features;
218 std::string force_fieldtrials;
219 std::string force_fieldtrial_params;
220 bool parse_enable_features_result =
221 ParseEnableFeatures(enable_features, &parsed_enable_features,
222 &force_fieldtrials, &force_fieldtrial_params);
223 DCHECK(parse_enable_features_result) << StringPrintf(
224 "The --%s list is unparsable or invalid, please check the format.",
225 ::switches::kEnableFeatures);
Weilun Shie81c6b92020-07-06 20:33:59226
Weilun Shi1cd8fb92020-07-17 23:31:00227 // Only create field trials when field_trial_list is available. Some tests
228 // don't have field trial list available.
229 if (FieldTrialList::GetInstance()) {
230 bool associate_params_result = AssociateFieldTrialParamsFromString(
231 force_fieldtrial_params, &UnescapeValue);
232 DCHECK(associate_params_result) << StringPrintf(
233 "The field trial parameters part of the --%s list is invalid. Make "
234 "sure "
235 "you %%-encode the following characters in param values: %%:/.,",
236 ::switches::kEnableFeatures);
Weilun Shie81c6b92020-07-06 20:33:59237
Weilun Shi1cd8fb92020-07-17 23:31:00238 bool create_trials_result =
239 FieldTrialList::CreateTrialsFromString(force_fieldtrials);
240 DCHECK(create_trials_result)
241 << StringPrintf("Invalid field trials are specified in --%s.",
242 ::switches::kEnableFeatures);
243 }
244
245 // Process disabled features first, so that disabled ones take precedence over
246 // enabled ones (since RegisterOverride() uses insert()).
247 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
248 RegisterOverridesFromCommandLine(parsed_enable_features,
249 OVERRIDE_ENABLE_FEATURE);
250
251 initialized_from_command_line_ = true;
Weilun Shie81c6b92020-07-06 20:33:59252}
253
lawrencewu5e03cd32016-12-05 16:23:28254void FeatureList::InitializeFromSharedMemory(
255 PersistentMemoryAllocator* allocator) {
256 DCHECK(!initialized_);
257
258 PersistentMemoryAllocator::Iterator iter(allocator);
bcwhite3f999d32017-01-11 12:42:13259 const FeatureEntry* entry;
260 while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
lawrencewu5e03cd32016-12-05 16:23:28261 OverrideState override_state =
262 static_cast<OverrideState>(entry->override_state);
263
264 StringPiece feature_name;
265 StringPiece trial_name;
266 if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
267 continue;
268
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16269 FieldTrial* trial = FieldTrialList::Find(trial_name);
lawrencewu5e03cd32016-12-05 16:23:28270 RegisterOverride(feature_name, override_state, trial);
271 }
272}
273
Xianzhu Wang05355f4a2020-09-02 01:22:16274bool FeatureList::IsFeatureOverridden(const std::string& feature_name) const {
275 return overrides_.count(feature_name);
276}
277
asvitkine8423d172015-09-28 23:23:44278bool FeatureList::IsFeatureOverriddenFromCommandLine(
Collin Baker3435ba662020-10-07 18:07:09279 const std::string& feature_name) const {
280 auto it = overrides_.find(feature_name);
281 return it != overrides_.end() && !it->second.overridden_by_field_trial;
282}
283
284bool FeatureList::IsFeatureOverriddenFromCommandLine(
asvitkine8423d172015-09-28 23:23:44285 const std::string& feature_name,
286 OverrideState state) const {
287 auto it = overrides_.find(feature_name);
Collin Baker3435ba662020-10-07 18:07:09288 return it != overrides_.end() && !it->second.overridden_by_field_trial &&
289 it->second.overridden_state == state;
asvitkine8423d172015-09-28 23:23:44290}
291
asvitkine8423d172015-09-28 23:23:44292void FeatureList::AssociateReportingFieldTrial(
293 const std::string& feature_name,
294 OverrideState for_overridden_state,
295 FieldTrial* field_trial) {
296 DCHECK(
297 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
298
299 // Only one associated field trial is supported per feature. This is generally
300 // enforced server-side.
301 OverrideEntry* entry = &overrides_.find(feature_name)->second;
302 if (entry->field_trial) {
303 NOTREACHED() << "Feature " << feature_name
304 << " already has trial: " << entry->field_trial->trial_name()
305 << ", associating trial: " << field_trial->trial_name();
306 return;
307 }
308
309 entry->field_trial = field_trial;
310}
311
asvitkine86340192015-12-01 00:45:29312void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
313 OverrideState override_state,
314 FieldTrial* field_trial) {
315 DCHECK(field_trial);
Jan Wilken Dörrief61e74c2019-06-07 08:20:02316 DCHECK(!Contains(overrides_, feature_name) ||
asvitkine86340192015-12-01 00:45:29317 !overrides_.find(feature_name)->second.field_trial)
Caitlin Fischer847398022021-07-28 02:58:31318 << "Feature " << feature_name << " is overriden multiple times in these "
319 << "trials: "
asvitkine86340192015-12-01 00:45:29320 << overrides_.find(feature_name)->second.field_trial->trial_name()
Caitlin Fischer847398022021-07-28 02:58:31321 << " and " << field_trial->trial_name() << ". "
322 << "Check the trial (study) in (1) the server config, "
323 << "(2) fieldtrial_testing_config.json, (3) about_flags.cc, and "
324 << "(4) client-side field trials.";
asvitkine86340192015-12-01 00:45:29325
326 RegisterOverride(feature_name, override_state, field_trial);
327}
328
Lily Chend49e3752019-08-09 19:05:24329void FeatureList::RegisterExtraFeatureOverrides(
330 const std::vector<FeatureOverrideInfo>& extra_overrides) {
331 for (const FeatureOverrideInfo& override_info : extra_overrides) {
332 RegisterOverride(override_info.first.get().name, override_info.second,
333 /* field_trial = */ nullptr);
334 }
335}
336
lawrencewu5e03cd32016-12-05 16:23:28337void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
338 DCHECK(initialized_);
339
340 for (const auto& override : overrides_) {
341 Pickle pickle;
342 pickle.WriteString(override.first);
343 if (override.second.field_trial)
344 pickle.WriteString(override.second.field_trial->trial_name());
345
346 size_t total_size = sizeof(FeatureEntry) + pickle.size();
bcwhitecf6a9e82017-02-09 20:44:23347 FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
bcwhite3f999d32017-01-11 12:42:13348 if (!entry)
lawrencewu5e03cd32016-12-05 16:23:28349 return;
350
lawrencewu5e03cd32016-12-05 16:23:28351 entry->override_state = override.second.overridden_state;
352 entry->pickle_size = pickle.size();
353
354 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
355 memcpy(dst, pickle.data(), pickle.size());
356
bcwhite3f999d32017-01-11 12:42:13357 allocator->MakeIterable(entry);
lawrencewu5e03cd32016-12-05 16:23:28358 }
359}
360
asvitkine86340192015-12-01 00:45:29361void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
362 std::string* disable_overrides) {
Alexei Svitkine223d2282018-02-08 00:18:35363 GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
364}
asvitkine86340192015-12-01 00:45:29365
Alexei Svitkine223d2282018-02-08 00:18:35366void FeatureList::GetCommandLineFeatureOverrides(
367 std::string* enable_overrides,
368 std::string* disable_overrides) {
369 GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
asvitkine86340192015-12-01 00:45:29370}
371
asvitkinebccbb862015-09-04 17:17:45372// static
373bool FeatureList::IsEnabled(const Feature& feature) {
Will Harris196c69c2020-12-15 22:57:48374#if DCHECK_IS_ON()
375 CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
376#endif
Daniel Bratell87120182018-02-22 19:07:26377 if (!g_feature_list_instance) {
Wez1dc3d68b2019-12-10 06:25:46378 g_initialized_from_accessor = &feature;
joedow958f0472016-07-07 22:08:55379 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
380 }
Daniel Bratell87120182018-02-22 19:07:26381 return g_feature_list_instance->IsFeatureEnabled(feature);
asvitkinebccbb862015-09-04 17:17:45382}
383
384// static
Leszek Swirskif68e123f2021-07-22 18:23:39385absl::optional<bool> FeatureList::GetStateIfOverridden(const Feature& feature) {
386#if DCHECK_IS_ON()
387 CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
388#endif
389 if (!g_feature_list_instance) {
390 g_initialized_from_accessor = &feature;
391 // If there is no feature list, there can be no overrides.
392 return absl::nullopt;
393 }
394 return g_feature_list_instance->IsFeatureEnabledIfOverridden(feature);
395}
396
397// static
jwd07b90382016-05-06 20:39:42398FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
Will Harris196c69c2020-12-15 22:57:48399#if DCHECK_IS_ON()
400 // See documentation for ForbidUseForCurrentModule.
401 CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
402#endif
Daniel Bratell87120182018-02-22 19:07:26403 if (!g_feature_list_instance) {
Wez1dc3d68b2019-12-10 06:25:46404 g_initialized_from_accessor = &feature;
Alexei Svitkinec49d1f402017-09-18 23:00:41405 return nullptr;
406 }
Daniel Bratell87120182018-02-22 19:07:26407 return g_feature_list_instance->GetAssociatedFieldTrial(feature);
jwd07b90382016-05-06 20:39:42408}
409
410// static
Alexei Svitkine8724ea502019-06-14 21:51:46411std::vector<StringPiece> FeatureList::SplitFeatureListString(
412 StringPiece input) {
mgiuca30f75882017-03-28 02:07:19413 return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
asvitkine03007d02015-10-21 22:50:06414}
415
416// static
asvitkinee6be55d2016-04-04 23:29:50417bool FeatureList::InitializeInstance(const std::string& enable_features,
changwan5b9da192016-03-31 07:36:19418 const std::string& disable_features) {
Lily Chend49e3752019-08-09 19:05:24419 return InitializeInstance(enable_features, disable_features,
420 std::vector<FeatureOverrideInfo>());
421}
422
423// static
424bool FeatureList::InitializeInstance(
425 const std::string& enable_features,
426 const std::string& disable_features,
427 const std::vector<FeatureOverrideInfo>& extra_overrides) {
changwan5b9da192016-03-31 07:36:19428 // We want to initialize a new instance here to support command-line features
429 // in testing better. For example, we initialize a dummy instance in
430 // base/test/test_suite.cc, and override it in content/browser/
431 // browser_main_loop.cc.
432 // On the other hand, we want to avoid re-initialization from command line.
433 // For example, we initialize an instance in chrome/browser/
434 // chrome_browser_main.cc and do not override it in content/browser/
435 // browser_main_loop.cc.
joedow958f0472016-07-07 22:08:55436 // If the singleton was previously initialized from within an accessor, we
437 // want to prevent callers from reinitializing the singleton and masking the
438 // accessor call(s) which likely returned incorrect information.
Wez1dc3d68b2019-12-10 06:25:46439 if (g_initialized_from_accessor) {
440 DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
441 CHECK(!g_initialized_from_accessor);
442 }
asvitkinee6be55d2016-04-04 23:29:50443 bool instance_existed_before = false;
Daniel Bratell87120182018-02-22 19:07:26444 if (g_feature_list_instance) {
445 if (g_feature_list_instance->initialized_from_command_line_)
asvitkinee6be55d2016-04-04 23:29:50446 return false;
changwan5b9da192016-03-31 07:36:19447
Daniel Bratell87120182018-02-22 19:07:26448 delete g_feature_list_instance;
449 g_feature_list_instance = nullptr;
asvitkinee6be55d2016-04-04 23:29:50450 instance_existed_before = true;
changwan5b9da192016-03-31 07:36:19451 }
452
Alexei Svitkine8724ea502019-06-14 21:51:46453 std::unique_ptr<FeatureList> feature_list(new FeatureList);
changwan5b9da192016-03-31 07:36:19454 feature_list->InitializeFromCommandLine(enable_features, disable_features);
Lily Chend49e3752019-08-09 19:05:24455 feature_list->RegisterExtraFeatureOverrides(extra_overrides);
Alexei Svitkine8724ea502019-06-14 21:51:46456 FeatureList::SetInstance(std::move(feature_list));
asvitkinee6be55d2016-04-04 23:29:50457 return !instance_existed_before;
asvitkine9d96abf2015-11-02 21:52:08458}
459
460// static
asvitkinebccbb862015-09-04 17:17:45461FeatureList* FeatureList::GetInstance() {
Daniel Bratell87120182018-02-22 19:07:26462 return g_feature_list_instance;
asvitkinebccbb862015-09-04 17:17:45463}
464
465// static
dcheng093de9b2016-04-04 21:25:51466void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
Daniel Bratell87120182018-02-22 19:07:26467 DCHECK(!g_feature_list_instance);
asvitkinebccbb862015-09-04 17:17:45468 instance->FinalizeInitialization();
469
470 // Note: Intentional leak of global singleton.
Daniel Bratell87120182018-02-22 19:07:26471 g_feature_list_instance = instance.release();
Wez289477f2017-08-24 20:51:30472
Tomas Popelaafffa972018-11-13 20:42:05473#if defined(DCHECK_IS_CONFIGURABLE)
Lei Zhang93dd42572020-10-23 18:45:53474 // Update the behaviour of LOGGING_DCHECK to match the Feature configuration.
Wez289477f2017-08-24 20:51:30475 // DCHECK is also forced to be FATAL if we are running a death-test.
Gabriel Charettee9421ec2020-05-21 18:20:03476 // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
477 // currently run through this codepath, mitigated in
478 // base::TestSuite::Initialize() for now.
Wez289477f2017-08-24 20:51:30479 // TODO(asvitkine): If we find other use-cases that need integrating here
480 // then define a proper API/hook for the purpose.
Alexei Svitkine8724ea502019-06-14 21:51:46481 if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
482 CommandLine::ForCurrentProcess()->HasSwitch(
Wez289477f2017-08-24 20:51:30483 "gtest_internal_run_death_test")) {
Lei Zhang93dd42572020-10-23 18:45:53484 logging::LOGGING_DCHECK = logging::LOG_FATAL;
Wez289477f2017-08-24 20:51:30485 } else {
Lei Zhang93dd42572020-10-23 18:45:53486 logging::LOGGING_DCHECK = logging::LOG_INFO;
Wez289477f2017-08-24 20:51:30487 }
Tomas Popelaafffa972018-11-13 20:42:05488#endif // defined(DCHECK_IS_CONFIGURABLE)
asvitkinebccbb862015-09-04 17:17:45489}
490
491// static
asvitkine9499b8d2016-08-09 05:37:07492std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
Daniel Bratell87120182018-02-22 19:07:26493 FeatureList* old_instance = g_feature_list_instance;
494 g_feature_list_instance = nullptr;
Wez1dc3d68b2019-12-10 06:25:46495 g_initialized_from_accessor = nullptr;
Alexei Svitkine8724ea502019-06-14 21:51:46496 return WrapUnique(old_instance);
asvitkine9499b8d2016-08-09 05:37:07497}
498
499// static
500void FeatureList::RestoreInstanceForTesting(
501 std::unique_ptr<FeatureList> instance) {
Daniel Bratell87120182018-02-22 19:07:26502 DCHECK(!g_feature_list_instance);
asvitkine9499b8d2016-08-09 05:37:07503 // Note: Intentional leak of global singleton.
Daniel Bratell87120182018-02-22 19:07:26504 g_feature_list_instance = instance.release();
asvitkinebccbb862015-09-04 17:17:45505}
506
Will Harris196c69c2020-12-15 22:57:48507// static
508void FeatureList::ForbidUseForCurrentModule() {
509#if DCHECK_IS_ON()
510 // Verify there hasn't been any use prior to being called.
511 DCHECK(!g_initialized_from_accessor);
512 g_use_allowed = false;
513#endif // DCHECK_IS_ON()
514}
515
asvitkinebccbb862015-09-04 17:17:45516void FeatureList::FinalizeInitialization() {
517 DCHECK(!initialized_);
Alexei Svitkine8724ea502019-06-14 21:51:46518 // Store the field trial list pointer for DCHECKing.
519 field_trial_list_ = FieldTrialList::GetInstance();
asvitkinebccbb862015-09-04 17:17:45520 initialized_ = true;
521}
522
523bool FeatureList::IsFeatureEnabled(const Feature& feature) {
Leszek Swirskif68e123f2021-07-22 18:23:39524 OverrideState overridden_state = GetOverrideState(feature);
525
526 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
527 if (overridden_state != OVERRIDE_USE_DEFAULT)
528 return overridden_state == OVERRIDE_ENABLE_FEATURE;
529
530 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
531}
532
533absl::optional<bool> FeatureList::IsFeatureEnabledIfOverridden(
534 const Feature& feature) {
535 OverrideState overridden_state = GetOverrideState(feature);
536
537 // If marked as OVERRIDE_USE_DEFAULT, fall through to returning empty.
538 if (overridden_state != OVERRIDE_USE_DEFAULT)
539 return overridden_state == OVERRIDE_ENABLE_FEATURE;
540
541 return absl::nullopt;
542}
543
544FeatureList::OverrideState FeatureList::GetOverrideState(
545 const Feature& feature) {
asvitkinebccbb862015-09-04 17:17:45546 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28547 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45548 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
549
550 auto it = overrides_.find(feature.name);
551 if (it != overrides_.end()) {
552 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44553
554 // Activate the corresponding field trial, if necessary.
555 if (entry.field_trial)
556 entry.field_trial->group();
557
asvitkinebccbb862015-09-04 17:17:45558 // TODO(asvitkine) Expand this section as more support is added.
asvitkine64e9e112016-03-17 17:32:00559
Leszek Swirskif68e123f2021-07-22 18:23:39560 return entry.overridden_state;
asvitkinebccbb862015-09-04 17:17:45561 }
Leszek Swirskif68e123f2021-07-22 18:23:39562 // Otherwise, report that we want to use the default state.
563 return OVERRIDE_USE_DEFAULT;
asvitkinebccbb862015-09-04 17:17:45564}
565
jwd07b90382016-05-06 20:39:42566FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
567 DCHECK(initialized_);
jwd07b90382016-05-06 20:39:42568 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
569
Joel Fernandes541c6d02021-05-10 13:42:16570 return GetAssociatedFieldTrialByFeatureName(feature.name);
571}
572
573const base::FeatureList::OverrideEntry*
574FeatureList::GetOverrideEntryByFeatureName(StringPiece name) {
575 DCHECK(initialized_);
576 DCHECK(IsValidFeatureOrFieldTrialName(std::string(name))) << name;
577
578 auto it = overrides_.find(name);
jwd07b90382016-05-06 20:39:42579 if (it != overrides_.end()) {
580 const OverrideEntry& entry = it->second;
Joel Fernandes541c6d02021-05-10 13:42:16581 return &entry;
jwd07b90382016-05-06 20:39:42582 }
Joel Fernandes541c6d02021-05-10 13:42:16583 return nullptr;
584}
jwd07b90382016-05-06 20:39:42585
Joel Fernandes541c6d02021-05-10 13:42:16586FieldTrial* FeatureList::GetAssociatedFieldTrialByFeatureName(
587 StringPiece name) {
588 DCHECK(initialized_);
589
590 const base::FeatureList::OverrideEntry* entry =
591 GetOverrideEntryByFeatureName(name);
592 if (entry) {
593 return entry->field_trial;
594 }
595 return nullptr;
596}
597
598FieldTrial* FeatureList::GetEnabledFieldTrialByFeatureName(StringPiece name) {
599 DCHECK(initialized_);
600
601 const base::FeatureList::OverrideEntry* entry =
602 GetOverrideEntryByFeatureName(name);
603 if (entry &&
604 entry->overridden_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
605 return entry->field_trial;
606 }
jwd07b90382016-05-06 20:39:42607 return nullptr;
608}
609
asvitkineb2e44d82015-12-01 04:10:28610void FeatureList::RegisterOverridesFromCommandLine(
611 const std::string& feature_list,
612 OverrideState overridden_state) {
613 for (const auto& value : SplitFeatureListString(feature_list)) {
mgiuca30f75882017-03-28 02:07:19614 StringPiece feature_name = value;
Alexei Svitkine8724ea502019-06-14 21:51:46615 FieldTrial* trial = nullptr;
asvitkineb2e44d82015-12-01 04:10:28616
617 // The entry may be of the form FeatureName<FieldTrialName - in which case,
618 // this splits off the field trial name and associates it with the override.
619 std::string::size_type pos = feature_name.find('<');
620 if (pos != std::string::npos) {
Jan Wilken Dörrie884fe9b2020-01-29 10:16:31621 feature_name = StringPiece(value.data(), pos);
Jan Wilken Dörriee7843132021-02-16 20:32:59622 trial = FieldTrialList::Find(value.substr(pos + 1));
Alexei Svitkine8724ea502019-06-14 21:51:46623#if !defined(OS_NACL)
624 // If the below DCHECK fires, it means a non-existent trial name was
625 // specified via the "Feature<Trial" command-line syntax.
Camillo Bruni6efb3582021-03-29 17:05:41626 DCHECK(trial) << "trial='" << value.substr(pos + 1) << "' does not exist";
Alexei Svitkine8724ea502019-06-14 21:51:46627#endif // !defined(OS_NACL)
asvitkineb2e44d82015-12-01 04:10:28628 }
629
630 RegisterOverride(feature_name, overridden_state, trial);
631 }
632}
633
634void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44635 OverrideState overridden_state,
636 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45637 DCHECK(!initialized_);
Ken Rockot30f75752019-10-12 08:07:41638 DCheckOverridesAllowed();
asvitkineb2e44d82015-12-01 04:10:28639 if (field_trial) {
640 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
641 << field_trial->trial_name();
642 }
Jan Wilken Dörrief05bb102020-08-18 19:35:56643 if (StartsWith(feature_name, "*")) {
asvitkine6d31c52e2016-03-22 15:37:52644 feature_name = feature_name.substr(1);
645 overridden_state = OVERRIDE_USE_DEFAULT;
646 }
asvitkineb2e44d82015-12-01 04:10:28647
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16648 // Note: The semantics of emplace() is that it does not overwrite the entry if
asvitkine8423d172015-09-28 23:23:44649 // one already exists for the key. Thus, only the first override for a given
650 // feature name takes effect.
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16651 overrides_.emplace(std::string(feature_name),
652 OverrideEntry(overridden_state, field_trial));
asvitkinebccbb862015-09-04 17:17:45653}
654
Alexei Svitkine223d2282018-02-08 00:18:35655void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
656 std::string* disable_overrides,
657 bool command_line_only) {
658 DCHECK(initialized_);
659
Alexei Svitkine8724ea502019-06-14 21:51:46660 // Check that the FieldTrialList this is associated with, if any, is the
661 // active one. If not, it likely indicates that this FeatureList has override
662 // entries from a freed FieldTrial, which may be caused by an incorrect test
663 // set up.
Mikel Astiz504e67f2019-11-28 16:25:07664 if (field_trial_list_)
665 DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
Alexei Svitkine8724ea502019-06-14 21:51:46666
Alexei Svitkine223d2282018-02-08 00:18:35667 enable_overrides->clear();
668 disable_overrides->clear();
669
670 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
671 // order. This is not guaranteed to users of this function, but is useful for
672 // tests to assume the order.
673 for (const auto& entry : overrides_) {
674 if (command_line_only &&
675 (entry.second.field_trial != nullptr ||
676 entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
677 continue;
678 }
679
680 std::string* target_list = nullptr;
681 switch (entry.second.overridden_state) {
682 case OVERRIDE_USE_DEFAULT:
683 case OVERRIDE_ENABLE_FEATURE:
684 target_list = enable_overrides;
685 break;
686 case OVERRIDE_DISABLE_FEATURE:
687 target_list = disable_overrides;
688 break;
689 }
690
691 if (!target_list->empty())
692 target_list->push_back(',');
693 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
694 target_list->push_back('*');
695 target_list->append(entry.first);
696 if (entry.second.field_trial) {
697 target_list->push_back('<');
698 target_list->append(entry.second.field_trial->trial_name());
699 }
700 }
701}
702
asvitkinebccbb862015-09-04 17:17:45703bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
704 AutoLock auto_lock(feature_identity_tracker_lock_);
705
706 auto it = feature_identity_tracker_.find(feature.name);
707 if (it == feature_identity_tracker_.end()) {
708 // If it's not tracked yet, register it.
709 feature_identity_tracker_[feature.name] = &feature;
710 return true;
711 }
712 // Compare address of |feature| to the existing tracked entry.
713 return it->second == &feature;
714}
715
asvitkine8423d172015-09-28 23:23:44716FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
717 FieldTrial* field_trial)
718 : overridden_state(overridden_state),
719 field_trial(field_trial),
720 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45721
722} // namespace base