123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- // Copyright 2015 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/feature_list.h"
- #include <string>
- #include <tuple>
- #include <stddef.h>
- #include "base/base_paths.h"
- #include "base/base_switches.h"
- #include "base/containers/contains.h"
- #include "base/debug/alias.h"
- #include "base/debug/stack_trace.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/metrics/field_trial.h"
- #include "base/metrics/field_trial_param_associator.h"
- #include "base/metrics/field_trial_params.h"
- #include "base/metrics/persistent_memory_allocator.h"
- #include "base/notreached.h"
- #include "base/path_service.h"
- #include "base/pickle.h"
- #include "base/rand_util.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/sequence_manager/work_queue.h"
- #include "build/build_config.h"
- namespace base {
- namespace {
- // Pointer to the FeatureList instance singleton that was set via
- // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
- // have more control over initialization timing. Leaky.
- FeatureList* g_feature_list_instance = nullptr;
- // Tracks whether the FeatureList instance was initialized via an accessor, and
- // which Feature that accessor was for, if so.
- const Feature* g_initialized_from_accessor = nullptr;
- #if DCHECK_IS_ON()
- // Tracks whether the use of base::Feature is allowed for this module.
- // See ForbidUseForCurrentModule().
- bool g_use_allowed = true;
- const char* g_reason_overrides_disallowed = nullptr;
- void DCheckOverridesAllowed() {
- const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
- DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
- }
- #else
- void DCheckOverridesAllowed() {}
- #endif
- // An allocator entry for a feature in shared memory. The FeatureEntry is
- // followed by a base::Pickle object that contains the feature and trial name.
- struct FeatureEntry {
- // SHA1(FeatureEntry): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 2;
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 16;
- // Specifies whether a feature override enables or disables the feature. Same
- // values as the OverrideState enum in feature_list.h
- uint32_t override_state;
- // On e.g. x86, alignof(uint64_t) is 4. Ensure consistent size and alignment
- // of `pickle_size` across platforms.
- uint32_t padding;
- // Size of the pickled structure, NOT the total size of this entry.
- uint64_t pickle_size;
- // Reads the feature and trial name from the pickle. Calling this is only
- // valid on an initialized entry that's in shared memory.
- bool GetFeatureAndTrialName(StringPiece* feature_name,
- StringPiece* trial_name) const {
- const char* src =
- reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
- Pickle pickle(src, checked_cast<size_t>(pickle_size));
- PickleIterator pickle_iter(pickle);
- if (!pickle_iter.ReadStringPiece(feature_name))
- return false;
- // Return true because we are not guaranteed to have a trial name anyways.
- std::ignore = pickle_iter.ReadStringPiece(trial_name);
- return true;
- }
- };
- // Some characters are not allowed to appear in feature names or the associated
- // field trial names, as they are used as special characters for command-line
- // serialization. This function checks that the strings are ASCII (since they
- // are used in command-line API functions that require ASCII) and whether there
- // are any reserved characters present, returning true if the string is valid.
- // Only called in DCHECKs.
- bool IsValidFeatureOrFieldTrialName(StringPiece name) {
- return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
- }
- // Splits |text| into two parts by the |separator| where the first part will be
- // returned updated in |first| and the second part will be returned as |second|.
- // This function returns false if there is more than one |separator| in |first|.
- // If there is no |separator| presented in |first|, this function will not
- // modify |first| and |second|. It's used for splitting the |enable_features|
- // flag into feature name, field trial name and feature parameters.
- bool SplitIntoTwo(StringPiece text,
- StringPiece separator,
- StringPiece* first,
- std::string* second) {
- std::vector<StringPiece> parts =
- SplitStringPiece(text, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
- if (parts.size() == 2) {
- *second = std::string(parts[1]);
- } else if (parts.size() > 2) {
- DLOG(ERROR) << "Only one '" << separator
- << "' is allowed but got: " << *first;
- return false;
- }
- *first = parts[0];
- return true;
- }
- // Checks and parses the |enable_features| flag and sets
- // |parsed_enable_features| to be a comma-separated list of features,
- // |force_fieldtrials| to be a comma-separated list of field trials that each
- // feature want to associate with and |force_fieldtrial_params| to be the field
- // trial parameters for each field trial.
- // Returns true if |enable_features| is parsable, otherwise false.
- bool ParseEnableFeatures(const std::string& enable_features,
- std::string* parsed_enable_features,
- std::string* force_fieldtrials,
- std::string* force_fieldtrial_params) {
- std::vector<std::string> enable_features_list;
- std::vector<std::string> force_fieldtrials_list;
- std::vector<std::string> force_fieldtrial_params_list;
- for (const auto& enable_feature :
- FeatureList::SplitFeatureListString(enable_features)) {
- std::string feature_name;
- std::string study;
- std::string group;
- std::string feature_params;
- if (!FeatureList::ParseEnableFeatureString(
- enable_feature, &feature_name, &study, &group, &feature_params)) {
- return false;
- }
- // If feature params were set but group and study weren't, associate the
- // feature and its feature params to a synthetic field trial as the
- // feature params only make sense when it's combined with a field trial.
- if (!feature_params.empty()) {
- force_fieldtrials_list.push_back(study + "/" + group);
- force_fieldtrial_params_list.push_back(study + "." + group + ":" +
- feature_params);
- }
- enable_features_list.push_back(
- study.empty() ? feature_name : (feature_name + "<" + study));
- }
- *parsed_enable_features = JoinString(enable_features_list, ",");
- // Field trial separator is currently a slash. See
- // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
- *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
- *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
- return true;
- }
- } // namespace
- #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
- const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
- FEATURE_DISABLED_BY_DEFAULT};
- #endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
- FeatureList::FeatureList() = default;
- FeatureList::~FeatureList() = default;
- FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
- const char* reason)
- #if DCHECK_IS_ON()
- : previous_reason_(g_reason_overrides_disallowed) {
- g_reason_overrides_disallowed = reason;
- }
- #else
- {
- }
- #endif
- FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
- #if DCHECK_IS_ON()
- g_reason_overrides_disallowed = previous_reason_;
- #endif
- }
- void FeatureList::InitializeFromCommandLine(
- const std::string& enable_features,
- const std::string& disable_features) {
- DCHECK(!initialized_);
- std::string parsed_enable_features;
- std::string force_fieldtrials;
- std::string force_fieldtrial_params;
- bool parse_enable_features_result =
- ParseEnableFeatures(enable_features, &parsed_enable_features,
- &force_fieldtrials, &force_fieldtrial_params);
- DCHECK(parse_enable_features_result) << StringPrintf(
- "The --%s list is unparsable or invalid, please check the format.",
- ::switches::kEnableFeatures);
- // Only create field trials when field_trial_list is available. Some tests
- // don't have field trial list available.
- if (FieldTrialList::GetInstance()) {
- bool associate_params_result = AssociateFieldTrialParamsFromString(
- force_fieldtrial_params, &UnescapeValue);
- DCHECK(associate_params_result) << StringPrintf(
- "The field trial parameters part of the --%s list is invalid. Make "
- "sure "
- "you %%-encode the following characters in param values: %%:/.,",
- ::switches::kEnableFeatures);
- bool create_trials_result =
- FieldTrialList::CreateTrialsFromString(force_fieldtrials);
- DCHECK(create_trials_result)
- << StringPrintf("Invalid field trials are specified in --%s.",
- ::switches::kEnableFeatures);
- }
- // Process disabled features first, so that disabled ones take precedence over
- // enabled ones (since RegisterOverride() uses insert()).
- RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
- RegisterOverridesFromCommandLine(parsed_enable_features,
- OVERRIDE_ENABLE_FEATURE);
- initialized_from_command_line_ = true;
- }
- void FeatureList::InitializeFromSharedMemory(
- PersistentMemoryAllocator* allocator) {
- DCHECK(!initialized_);
- PersistentMemoryAllocator::Iterator iter(allocator);
- const FeatureEntry* entry;
- while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
- OverrideState override_state =
- static_cast<OverrideState>(entry->override_state);
- StringPiece feature_name;
- StringPiece trial_name;
- if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
- continue;
- FieldTrial* trial = FieldTrialList::Find(trial_name);
- RegisterOverride(feature_name, override_state, trial);
- }
- }
- bool FeatureList::IsFeatureOverridden(const std::string& feature_name) const {
- return overrides_.count(feature_name);
- }
- bool FeatureList::IsFeatureOverriddenFromCommandLine(
- const std::string& feature_name) const {
- auto it = overrides_.find(feature_name);
- return it != overrides_.end() && !it->second.overridden_by_field_trial;
- }
- bool FeatureList::IsFeatureOverriddenFromCommandLine(
- const std::string& feature_name,
- OverrideState state) const {
- auto it = overrides_.find(feature_name);
- return it != overrides_.end() && !it->second.overridden_by_field_trial &&
- it->second.overridden_state == state;
- }
- void FeatureList::AssociateReportingFieldTrial(
- const std::string& feature_name,
- OverrideState for_overridden_state,
- FieldTrial* field_trial) {
- DCHECK(
- IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
- // Only one associated field trial is supported per feature. This is generally
- // enforced server-side.
- OverrideEntry* entry = &overrides_.find(feature_name)->second;
- if (entry->field_trial) {
- NOTREACHED() << "Feature " << feature_name
- << " already has trial: " << entry->field_trial->trial_name()
- << ", associating trial: " << field_trial->trial_name();
- return;
- }
- entry->field_trial = field_trial;
- }
- void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
- OverrideState override_state,
- FieldTrial* field_trial) {
- DCHECK(field_trial);
- DCHECK(!Contains(overrides_, feature_name) ||
- !overrides_.find(feature_name)->second.field_trial)
- << "Feature " << feature_name << " is overriden multiple times in these "
- << "trials: "
- << overrides_.find(feature_name)->second.field_trial->trial_name()
- << " and " << field_trial->trial_name() << ". "
- << "Check the trial (study) in (1) the server config, "
- << "(2) fieldtrial_testing_config.json, (3) about_flags.cc, and "
- << "(4) client-side field trials.";
- RegisterOverride(feature_name, override_state, field_trial);
- }
- void FeatureList::RegisterExtraFeatureOverrides(
- const std::vector<FeatureOverrideInfo>& extra_overrides) {
- for (const FeatureOverrideInfo& override_info : extra_overrides) {
- RegisterOverride(override_info.first.get().name, override_info.second,
- /* field_trial = */ nullptr);
- }
- }
- void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
- DCHECK(initialized_);
- for (const auto& override : overrides_) {
- Pickle pickle;
- pickle.WriteString(override.first);
- if (override.second.field_trial)
- pickle.WriteString(override.second.field_trial->trial_name());
- size_t total_size = sizeof(FeatureEntry) + pickle.size();
- FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
- if (!entry)
- return;
- entry->override_state = override.second.overridden_state;
- entry->pickle_size = pickle.size();
- char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
- memcpy(dst, pickle.data(), pickle.size());
- allocator->MakeIterable(entry);
- }
- }
- void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
- std::string* disable_overrides,
- bool include_group_name) const {
- GetFeatureOverridesImpl(enable_overrides, disable_overrides, false,
- include_group_name);
- }
- void FeatureList::GetCommandLineFeatureOverrides(
- std::string* enable_overrides,
- std::string* disable_overrides) const {
- GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
- }
- // static
- bool FeatureList::IsEnabled(const Feature& feature) {
- #if DCHECK_IS_ON()
- CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
- #endif
- if (!g_feature_list_instance) {
- g_initialized_from_accessor = &feature;
- return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
- }
- return g_feature_list_instance->IsFeatureEnabled(feature);
- }
- // static
- absl::optional<bool> FeatureList::GetStateIfOverridden(const Feature& feature) {
- #if DCHECK_IS_ON()
- CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
- #endif
- if (!g_feature_list_instance) {
- g_initialized_from_accessor = &feature;
- // If there is no feature list, there can be no overrides.
- return absl::nullopt;
- }
- return g_feature_list_instance->IsFeatureEnabledIfOverridden(feature);
- }
- // static
- FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
- #if DCHECK_IS_ON()
- // See documentation for ForbidUseForCurrentModule.
- CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
- #endif
- if (!g_feature_list_instance) {
- g_initialized_from_accessor = &feature;
- return nullptr;
- }
- return g_feature_list_instance->GetAssociatedFieldTrial(feature);
- }
- // static
- std::vector<StringPiece> FeatureList::SplitFeatureListString(
- StringPiece input) {
- return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
- }
- // static
- bool FeatureList::ParseEnableFeatureString(StringPiece enable_feature,
- std::string* feature_name,
- std::string* study_name,
- std::string* group_name,
- std::string* params) {
- StringPiece first;
- // First, check whether ":" is present. If true, feature parameters were
- // set for this feature.
- std::string feature_params;
- if (!SplitIntoTwo(enable_feature, ":", &first, &feature_params))
- return false;
- // Then, check whether "." is present. If true, a group was specified for
- // this feature.
- std::string group;
- if (!SplitIntoTwo(first, ".", &first, &group))
- return false;
- // Finally, check whether "<" is present. If true, a study was specified for
- // this feature.
- std::string study;
- if (!SplitIntoTwo(first, "<", &first, &study))
- return false;
- std::string enable_feature_name(first);
- // If feature params were set but group and study weren't, associate the
- // feature and its feature params to a synthetic field trial as the
- // feature params only make sense when it's combined with a field trial.
- if (!feature_params.empty()) {
- study = study.empty() ? "Study" + enable_feature_name : study;
- group = group.empty() ? "Group" + enable_feature_name : group;
- }
- feature_name->swap(enable_feature_name);
- study_name->swap(study);
- group_name->swap(group);
- params->swap(feature_params);
- return true;
- }
- // static
- bool FeatureList::InitializeInstance(const std::string& enable_features,
- const std::string& disable_features) {
- return InitializeInstance(enable_features, disable_features,
- std::vector<FeatureOverrideInfo>());
- }
- // static
- bool FeatureList::InitializeInstance(
- const std::string& enable_features,
- const std::string& disable_features,
- const std::vector<FeatureOverrideInfo>& extra_overrides) {
- // We want to initialize a new instance here to support command-line features
- // in testing better. For example, we initialize a dummy instance in
- // base/test/test_suite.cc, and override it in content/browser/
- // browser_main_loop.cc.
- // On the other hand, we want to avoid re-initialization from command line.
- // For example, we initialize an instance in chrome/browser/
- // chrome_browser_main.cc and do not override it in content/browser/
- // browser_main_loop.cc.
- // If the singleton was previously initialized from within an accessor, we
- // want to prevent callers from reinitializing the singleton and masking the
- // accessor call(s) which likely returned incorrect information.
- if (g_initialized_from_accessor) {
- DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
- CHECK(!g_initialized_from_accessor);
- }
- bool instance_existed_before = false;
- if (g_feature_list_instance) {
- if (g_feature_list_instance->initialized_from_command_line_)
- return false;
- delete g_feature_list_instance;
- g_feature_list_instance = nullptr;
- instance_existed_before = true;
- }
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
- feature_list->InitializeFromCommandLine(enable_features, disable_features);
- feature_list->RegisterExtraFeatureOverrides(extra_overrides);
- FeatureList::SetInstance(std::move(feature_list));
- return !instance_existed_before;
- }
- // static
- FeatureList* FeatureList::GetInstance() {
- return g_feature_list_instance;
- }
- // static
- void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
- DCHECK(!g_feature_list_instance);
- instance->FinalizeInitialization();
- // Note: Intentional leak of global singleton.
- g_feature_list_instance = instance.release();
- #if BUILDFLAG(IS_ANDROID)
- ConfigureRandBytesFieldTrial();
- #endif
- base::sequence_manager::internal::WorkQueue::ConfigureCapacityFieldTrial();
- #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
- // Update the behaviour of LOGGING_DCHECK to match the Feature configuration.
- // DCHECK is also forced to be FATAL if we are running a death-test.
- // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
- // currently run through this codepath, mitigated in
- // base::TestSuite::Initialize() for now.
- // TODO(asvitkine): If we find other use-cases that need integrating here
- // then define a proper API/hook for the purpose.
- if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
- CommandLine::ForCurrentProcess()->HasSwitch(
- "gtest_internal_run_death_test")) {
- logging::LOGGING_DCHECK = logging::LOG_FATAL;
- } else {
- logging::LOGGING_DCHECK = logging::LOG_INFO;
- }
- #endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
- }
- // static
- std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
- FeatureList* old_instance = g_feature_list_instance;
- g_feature_list_instance = nullptr;
- g_initialized_from_accessor = nullptr;
- return WrapUnique(old_instance);
- }
- // static
- void FeatureList::RestoreInstanceForTesting(
- std::unique_ptr<FeatureList> instance) {
- DCHECK(!g_feature_list_instance);
- // Note: Intentional leak of global singleton.
- g_feature_list_instance = instance.release();
- }
- // static
- void FeatureList::ForbidUseForCurrentModule() {
- #if DCHECK_IS_ON()
- // Verify there hasn't been any use prior to being called.
- DCHECK(!g_initialized_from_accessor);
- g_use_allowed = false;
- #endif // DCHECK_IS_ON()
- }
- void FeatureList::FinalizeInitialization() {
- DCHECK(!initialized_);
- // Store the field trial list pointer for DCHECKing.
- field_trial_list_ = FieldTrialList::GetInstance();
- initialized_ = true;
- }
- bool FeatureList::IsFeatureEnabled(const Feature& feature) const {
- OverrideState overridden_state = GetOverrideState(feature);
- // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
- if (overridden_state != OVERRIDE_USE_DEFAULT)
- return overridden_state == OVERRIDE_ENABLE_FEATURE;
- return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
- }
- absl::optional<bool> FeatureList::IsFeatureEnabledIfOverridden(
- const Feature& feature) const {
- OverrideState overridden_state = GetOverrideState(feature);
- // If marked as OVERRIDE_USE_DEFAULT, fall through to returning empty.
- if (overridden_state != OVERRIDE_USE_DEFAULT)
- return overridden_state == OVERRIDE_ENABLE_FEATURE;
- return absl::nullopt;
- }
- FeatureList::OverrideState FeatureList::GetOverrideState(
- const Feature& feature) const {
- DCHECK(initialized_);
- DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
- DCHECK(CheckFeatureIdentity(feature)) << feature.name;
- return GetOverrideStateByFeatureName(feature.name);
- }
- FeatureList::OverrideState FeatureList::GetOverrideStateByFeatureName(
- StringPiece feature_name) const {
- DCHECK(initialized_);
- DCHECK(IsValidFeatureOrFieldTrialName(feature_name)) << feature_name;
- auto it = overrides_.find(feature_name);
- if (it != overrides_.end()) {
- const OverrideEntry& entry = it->second;
- // Activate the corresponding field trial, if necessary.
- if (entry.field_trial)
- entry.field_trial->group();
- // TODO(asvitkine) Expand this section as more support is added.
- return entry.overridden_state;
- }
- // Otherwise, report that we want to use the default state.
- return OVERRIDE_USE_DEFAULT;
- }
- FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) const {
- DCHECK(initialized_);
- DCHECK(CheckFeatureIdentity(feature)) << feature.name;
- return GetAssociatedFieldTrialByFeatureName(feature.name);
- }
- const base::FeatureList::OverrideEntry*
- FeatureList::GetOverrideEntryByFeatureName(StringPiece name) const {
- DCHECK(initialized_);
- DCHECK(IsValidFeatureOrFieldTrialName(name)) << name;
- auto it = overrides_.find(name);
- if (it != overrides_.end()) {
- const OverrideEntry& entry = it->second;
- return &entry;
- }
- return nullptr;
- }
- FieldTrial* FeatureList::GetAssociatedFieldTrialByFeatureName(
- StringPiece name) const {
- DCHECK(initialized_);
- const base::FeatureList::OverrideEntry* entry =
- GetOverrideEntryByFeatureName(name);
- if (entry) {
- return entry->field_trial;
- }
- return nullptr;
- }
- FieldTrial* FeatureList::GetEnabledFieldTrialByFeatureName(
- StringPiece name) const {
- DCHECK(initialized_);
- const base::FeatureList::OverrideEntry* entry =
- GetOverrideEntryByFeatureName(name);
- if (entry &&
- entry->overridden_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
- return entry->field_trial;
- }
- return nullptr;
- }
- std::unique_ptr<FeatureList::Accessor> FeatureList::ConstructAccessor() {
- if (initialized_) {
- // This function shouldn't be called after initialization.
- NOTREACHED();
- return nullptr;
- }
- // Use new and WrapUnique because we want to restrict access to the Accessor's
- // constructor.
- return base::WrapUnique(new Accessor(this));
- }
- void FeatureList::RegisterOverridesFromCommandLine(
- const std::string& feature_list,
- OverrideState overridden_state) {
- for (const auto& value : SplitFeatureListString(feature_list)) {
- StringPiece feature_name = value;
- FieldTrial* trial = nullptr;
- // The entry may be of the form FeatureName<FieldTrialName - in which case,
- // this splits off the field trial name and associates it with the override.
- std::string::size_type pos = feature_name.find('<');
- if (pos != std::string::npos) {
- feature_name = StringPiece(value.data(), pos);
- trial = FieldTrialList::Find(value.substr(pos + 1));
- #if !BUILDFLAG(IS_NACL)
- // If the below DCHECK fires, it means a non-existent trial name was
- // specified via the "Feature<Trial" command-line syntax.
- DCHECK(trial) << "trial='" << value.substr(pos + 1) << "' does not exist";
- #endif // !BUILDFLAG(IS_NACL)
- }
- RegisterOverride(feature_name, overridden_state, trial);
- }
- }
- void FeatureList::RegisterOverride(StringPiece feature_name,
- OverrideState overridden_state,
- FieldTrial* field_trial) {
- DCHECK(!initialized_);
- DCheckOverridesAllowed();
- if (field_trial) {
- DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
- << field_trial->trial_name();
- }
- if (StartsWith(feature_name, "*")) {
- feature_name = feature_name.substr(1);
- overridden_state = OVERRIDE_USE_DEFAULT;
- }
- // Note: The semantics of emplace() is that it does not overwrite the entry if
- // one already exists for the key. Thus, only the first override for a given
- // feature name takes effect.
- overrides_.emplace(std::string(feature_name),
- OverrideEntry(overridden_state, field_trial));
- }
- void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
- std::string* disable_overrides,
- bool command_line_only,
- bool include_group_name) const {
- DCHECK(initialized_);
- // Check that the FieldTrialList this is associated with, if any, is the
- // active one. If not, it likely indicates that this FeatureList has override
- // entries from a freed FieldTrial, which may be caused by an incorrect test
- // set up.
- if (field_trial_list_)
- DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
- enable_overrides->clear();
- disable_overrides->clear();
- // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
- // order. This is not guaranteed to users of this function, but is useful for
- // tests to assume the order.
- for (const auto& entry : overrides_) {
- if (command_line_only &&
- (entry.second.field_trial != nullptr ||
- entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
- continue;
- }
- std::string* target_list = nullptr;
- switch (entry.second.overridden_state) {
- case OVERRIDE_USE_DEFAULT:
- case OVERRIDE_ENABLE_FEATURE:
- target_list = enable_overrides;
- break;
- case OVERRIDE_DISABLE_FEATURE:
- target_list = disable_overrides;
- break;
- }
- if (!target_list->empty())
- target_list->push_back(',');
- if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
- target_list->push_back('*');
- target_list->append(entry.first);
- if (entry.second.field_trial) {
- auto* const field_trial = entry.second.field_trial;
- target_list->push_back('<');
- target_list->append(field_trial->trial_name());
- if (include_group_name) {
- target_list->push_back('.');
- target_list->append(field_trial->GetGroupNameWithoutActivation());
- }
- }
- }
- }
- bool FeatureList::CheckFeatureIdentity(const Feature& feature) const {
- AutoLock auto_lock(feature_identity_tracker_lock_);
- auto it = feature_identity_tracker_.find(feature.name);
- if (it == feature_identity_tracker_.end()) {
- // If it's not tracked yet, register it.
- feature_identity_tracker_[feature.name] = &feature;
- return true;
- }
- // Compare address of |feature| to the existing tracked entry.
- return it->second == &feature;
- }
- FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
- FieldTrial* field_trial)
- : overridden_state(overridden_state),
- field_trial(field_trial),
- overridden_by_field_trial(field_trial != nullptr) {}
- FeatureList::Accessor::Accessor(FeatureList* feature_list)
- : feature_list_(feature_list) {}
- FeatureList::OverrideState FeatureList::Accessor::GetOverrideStateByFeatureName(
- StringPiece feature_name) {
- return feature_list_->GetOverrideStateByFeatureName(feature_name);
- }
- bool FeatureList::Accessor::GetParamsByFeatureName(
- StringPiece feature_name,
- std::map<std::string, std::string>* params) {
- base::FieldTrial* trial =
- feature_list_->GetAssociatedFieldTrialByFeatureName(feature_name);
- return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial,
- params);
- }
- } // namespace base
|