#include "base/feature_list.h"
#include <string>
#include <tuple>
#include <stddef.h>
#include "base/base_switches.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.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/no_destructor.h"
#include "base/notreached.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 "build/build_config.h"
namespace base {
namespace {
FeatureList* g_feature_list_instance = nullptr;
class EarlyFeatureAccessTracker {
public:
static EarlyFeatureAccessTracker* GetInstance() {
static NoDestructor<EarlyFeatureAccessTracker> instance;
return instance.get();
}
void AccessedFeature(const Feature& feature) {
AutoLock lock(lock_);
if (fail_instantly_)
Fail(&feature);
else if (!feature_)
feature_ = &feature;
}
void AssertNoAccess() {
AutoLock lock(lock_);
if (feature_)
Fail(feature_);
}
void FailOnFeatureAccessWithoutFeatureList() {
AutoLock lock(lock_);
if (feature_)
Fail(feature_);
fail_instantly_ = true;
}
void Reset() {
AutoLock lock(lock_);
feature_ = nullptr;
fail_instantly_ = false;
}
private:
void Fail(const Feature* feature) {
#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS) && !defined(OHOS_COOKIE)
#if !BUILDFLAG(IS_NACL)
SCOPED_CRASH_KEY_STRING256("FeatureList", "feature-accessed-too-early",
feature->name);
#endif
DCHECK(!feature) << "Accessed feature " << feature->name
<< " before FeatureList registration.";
base::debug::DumpWithoutCrashing();
#endif
}
friend class NoDestructor<EarlyFeatureAccessTracker>;
EarlyFeatureAccessTracker() = default;
~EarlyFeatureAccessTracker() = default;
Lock lock_;
raw_ptr<const Feature> feature_ GUARDED_BY(lock_) = nullptr;
bool fail_instantly_ GUARDED_BY(lock_) = false;
};
#if DCHECK_IS_ON()
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
struct FeatureEntry {
static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 2;
static constexpr size_t kExpectedInstanceSize = 16;
uint32_t override_state;
uint32_t padding;
uint64_t pickle_size;
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;
std::ignore = pickle_iter.ReadStringPiece(trial_name);
return true;
}
};
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;
}
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.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, ",");
*force_fieldtrials = JoinString(force_fieldtrials_list, "/");
*force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
return true;
}
std::pair<FeatureList::OverrideState, uint16_t> UnpackFeatureCache(
uint32_t packed_cache_value) {
return std::make_pair(
static_cast<FeatureList::OverrideState>(packed_cache_value >> 24),
packed_cache_value & 0xFFFF);
}
uint32_t PackFeatureCache(FeatureList::OverrideState override_state,
uint32_t caching_context) {
return (static_cast<uint32_t>(override_state) << 24) |
(caching_context & 0xFFFF);
}
}
#if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
BASE_FEATURE(kDCheckIsFatalFeature,
"DcheckIsFatal",
FEATURE_DISABLED_BY_DEFAULT);
#endif
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);
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);
}
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 {
#ifdef OHOS_SCROLLBAR
AutoLock lock(overrides_lock_);
#endif
return overrides_.count(feature_name);
}
bool FeatureList::IsFeatureOverriddenFromCommandLine(
const std::string& feature_name) const {
#ifdef OHOS_SCROLLBAR
AutoLock lock(overrides_lock_);
#endif
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 {
#ifdef OHOS_SCROLLBAR
AutoLock lock(overrides_lock_);
#endif
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));
#ifdef OHOS_SCROLLBAR
{
AutoLock lock(overrides_lock_);
#endif
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;
#ifdef OHOS_SCROLLBAR
}
#endif
}
void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
OverrideState override_state,
FieldTrial* field_trial) {
DCHECK(field_trial);
DCHECK(!HasAssociatedFieldTrialByFeatureName(feature_name))
<< "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,
nullptr);
}
}
void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
DCHECK(initialized_);
#if defined(OHOS_SCROLLBAR)
AutoLock lock(overrides_lock_);
#endif
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);
}
}
#if defined(OHOS_SCROLLBAR)
void FeatureList::ModifyFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
DCHECK(initialized_);
LOG(INFO) << "modify features";
PersistentMemoryAllocator::Iterator iter(allocator);
const FeatureEntry* entry;
while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
StringPiece feature_name;
StringPiece trial_name;
if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
continue;
if (feature_name == "OverlayScrollbar" || feature_name == "ForceScrollbar") {
allocator->Delete(entry);
}
}
AddFeatureToField(allocator, "");
}
void FeatureList::AddFeatureToField(PersistentMemoryAllocator* allocator, std::string feature_name) {
AutoLock lock(overrides_lock_);
for (const auto& override : overrides_) {
if (override.first != "OverlayScrollbar" && override.first != "ForceScrollbar") {
continue;
}
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);
}
}
#endif
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);
}
bool FeatureList::IsEnabled(const Feature& feature) {
if (!g_feature_list_instance) {
EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
}
return g_feature_list_instance->IsFeatureEnabled(feature);
}
bool FeatureList::IsValidFeatureOrFieldTrialName(StringPiece name) {
return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
}
#if defined(OHOS_SCROLLBAR)
void FeatureList::SetScrollbarEnable(bool enable) {
if (g_feature_list_instance) {
OverrideState state = OVERRIDE_ENABLE_FEATURE;
if (enable) {
state = OVERRIDE_DISABLE_FEATURE;
}
LOG(INFO) << "set Scrollbar:" << enable << " state:" << state;
g_feature_list_instance->SetOverrideStateByFeatureName("OverlayScrollbar", state);
} else {
LOG(ERROR) << "set Scrollbar error";
}
}
#endif
absl::optional<bool> FeatureList::GetStateIfOverridden(const Feature& feature) {
if (!g_feature_list_instance) {
EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
return absl::nullopt;
}
return g_feature_list_instance->IsFeatureEnabledIfOverridden(feature);
}
FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
if (!g_feature_list_instance) {
EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
return nullptr;
}
return g_feature_list_instance->GetAssociatedFieldTrial(feature);
}
std::vector<StringPiece> FeatureList::SplitFeatureListString(
StringPiece input) {
return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
}
bool FeatureList::ParseEnableFeatureString(StringPiece enable_feature,
std::string* feature_name,
std::string* study_name,
std::string* group_name,
std::string* params) {
StringPiece first;
std::string feature_params;
if (!SplitIntoTwo(enable_feature, ":", &first, &feature_params))
return false;
std::string group;
if (!SplitIntoTwo(first, ".", &first, &group))
return false;
std::string study;
if (!SplitIntoTwo(first, "<", &first, &study))
return false;
std::string enable_feature_name(first);
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;
}
bool FeatureList::InitializeInstance(const std::string& enable_features,
const std::string& disable_features) {
return InitializeInstance(enable_features, disable_features,
std::vector<FeatureOverrideInfo>());
}
bool FeatureList::InitializeInstance(
const std::string& enable_features,
const std::string& disable_features,
const std::vector<FeatureOverrideInfo>& extra_overrides) {
EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
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;
}
FeatureList* FeatureList::GetInstance() {
return g_feature_list_instance;
}
void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
DCHECK(!g_feature_list_instance);
instance->FinalizeInitialization();
g_feature_list_instance = instance.release();
EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
#if !BUILDFLAG(IS_NACL)
internal::ConfigureBoringSSLBackedRandBytesFieldTrial();
#endif
#if BUILDFLAG(IS_ANDROID)
internal::ConfigureRandBytesFieldTrial();
#endif
#if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
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
}
std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
FeatureList* old_instance = g_feature_list_instance;
g_feature_list_instance = nullptr;
EarlyFeatureAccessTracker::GetInstance()->Reset();
return WrapUnique(old_instance);
}
void FeatureList::RestoreInstanceForTesting(
std::unique_ptr<FeatureList> instance) {
DCHECK(!g_feature_list_instance);
g_feature_list_instance = instance.release();
}
void FeatureList::FailOnFeatureAccessWithoutFeatureList() {
EarlyFeatureAccessTracker::GetInstance()
->FailOnFeatureAccessWithoutFeatureList();
}
void FeatureList::SetCachingContextForTesting(uint16_t caching_context) {
caching_context_ = caching_context;
}
void FeatureList::FinalizeInitialization() {
DCHECK(!initialized_);
field_trial_list_ = FieldTrialList::GetInstance();
initialized_ = true;
}
bool FeatureList::IsFeatureEnabled(const Feature& feature) const {
OverrideState overridden_state = GetOverrideState(feature);
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 (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
<< " has multiple definitions. Either it is defined more than once in "
"code or (for component builds) the code is built into multiple "
"components (shared libraries) without a corresponding export "
"statement";
uint32_t current_cache_value =
feature.cached_value.load(std::memory_order_relaxed);
auto unpacked = UnpackFeatureCache(current_cache_value);
if (unpacked.second == caching_context_)
return unpacked.first;
OverrideState state = GetOverrideStateByFeatureName(feature.name);
uint32_t new_cache_value = PackFeatureCache(state, caching_context_);
feature.cached_value.store(new_cache_value, std::memory_order_relaxed);
return state;
}
FeatureList::OverrideState FeatureList::GetOverrideStateByFeatureName(
StringPiece feature_name) const {
DCHECK(initialized_);
DCHECK(IsValidFeatureOrFieldTrialName(feature_name)) << feature_name;
#ifdef OHOS_SCROLLBAR
AutoLock lock(overrides_lock_);
#endif
auto it = overrides_.find(feature_name);
if (it != overrides_.end()) {
const OverrideEntry& entry = it->second;
if (entry.field_trial)
entry.field_trial->Activate();
return entry.overridden_state;
}
return OVERRIDE_USE_DEFAULT;
}
#if defined(OHOS_SCROLLBAR)
void FeatureList::SetOverrideStateByFeatureName(
StringPiece feature_name, OverrideState state) {
DCHECK(initialized_);
DCHECK(IsValidFeatureOrFieldTrialName(feature_name)) << feature_name;
AutoLock lock(overrides_lock_);
auto it = overrides_.find(feature_name);
if (it == overrides_.end()) {
overrides_.emplace(std::string(feature_name),
OverrideEntry(state, nullptr));
}
}
#endif
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;
#ifdef OHOS_SCROLLBAR
AutoLock lock(overrides_lock_);
#endif
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;
}
bool FeatureList::HasAssociatedFieldTrialByFeatureName(StringPiece name) const {
DCHECK(!initialized_);
#ifdef OHOS_SCROLLBAR
AutoLock lock(overrides_lock_);
#endif
auto entry = overrides_.find(name);
return entry != overrides_.end() && entry->second.field_trial != 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_) {
NOTREACHED();
return nullptr;
}
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;
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)
DCHECK(trial) << "trial='" << value.substr(pos + 1) << "' does not exist";
#endif
}
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;
}
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_);
if (field_trial_list_)
DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
enable_overrides->clear();
disable_overrides->clear();
#if defined(OHOS_SCROLLBAR)
AutoLock lock(overrides_lock_);
#endif
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.get();
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()) {
feature_identity_tracker_[feature.name] = &feature;
return true;
}
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);
}
}