X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=base%2Ffeature_list.cc;h=6575c527eae64a532fbc006d89e32e976de9099f;hb=63f8096971c62f71555090052b9e1280b32560df;hp=3b8636c28498e184be8cc5769ee04622b07af57c;hpb=c32dc2689dccb948ad9c26d78f47d18b8a433ecb;p=platform%2Fframework%2Fweb%2Fchromium-efl.git diff --git a/base/feature_list.cc b/base/feature_list.cc index 3b8636c..6575c52 100644 --- a/base/feature_list.cc +++ b/base/feature_list.cc @@ -1,21 +1,32 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors // 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 +#include +#include -#include -#include +#include -#include "base/debug/alias.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 { @@ -27,9 +38,74 @@ namespace { // 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; +// Tracks access to Feature state before FeatureList registration. +class EarlyFeatureAccessTracker { + public: + static EarlyFeatureAccessTracker* GetInstance() { + static NoDestructor instance; + return instance.get(); + } + + // Invoked when `feature` is accessed before FeatureList registration. + void AccessedFeature(const Feature& feature) { + AutoLock lock(lock_); + if (fail_instantly_) + Fail(&feature); + else if (!feature_) + feature_ = &feature; + } + + // Asserts that no feature was accessed before FeatureList registration. + void AssertNoAccess() { + AutoLock lock(lock_); + if (feature_) + Fail(feature_); + } + + // Makes calls to AccessedFeature() fail instantly. + void FailOnFeatureAccessWithoutFeatureList() { + AutoLock lock(lock_); + if (feature_) + Fail(feature_); + fail_instantly_ = true; + } + + // Resets the state of this tracker. + void Reset() { + AutoLock lock(lock_); + feature_ = nullptr; + fail_instantly_ = false; + } + + private: + void Fail(const Feature* feature) { + // TODO(crbug.com/1358639): Enable this check on all platforms. +#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS) +#if !BUILDFLAG(IS_NACL) + // Create a crash key with the name of the feature accessed too early, to + // facilitate crash triage. + SCOPED_CRASH_KEY_STRING256("FeatureList", "feature-accessed-too-early", + feature->name); +#endif // !BUILDFLAG(IS_NACL) + CHECK(!feature) << "Accessed feature " << feature->name + << " before FeatureList registration."; +#endif // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && + // !BUILDFLAG(IS_CHROMEOS) + } + + friend class NoDestructor; + + EarlyFeatureAccessTracker() = default; + ~EarlyFeatureAccessTracker() = default; + + Lock lock_; + + // First feature to be accessed before FeatureList registration. + raw_ptr feature_ GUARDED_BY(lock_) = nullptr; + + // Whether AccessedFeature() should fail instantly. + bool fail_instantly_ GUARDED_BY(lock_) = false; +}; #if DCHECK_IS_ON() const char* g_reason_overrides_disallowed = nullptr; @@ -46,17 +122,21 @@ void DCheckOverridesAllowed() {} // 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 + 1; + static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 2; // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = 8; + 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. - uint32_t pickle_size; + 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. @@ -65,35 +145,105 @@ struct FeatureEntry { const char* src = reinterpret_cast(this) + sizeof(FeatureEntry); - Pickle pickle(src, pickle_size); + Pickle pickle(src, checked_cast(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. - auto sink = pickle_iter.ReadStringPiece(trial_name); - ALLOW_UNUSED_LOCAL(sink); + 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(const std::string& 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 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 enable_features_list; + std::vector force_fieldtrials_list; + std::vector 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; +} + +std::pair UnpackFeatureCache( + uint32_t packed_cache_value) { + return std::make_pair( + static_cast(packed_cache_value >> 24), + packed_cache_value & 0xFFFF); +} + +uint32_t PackFeatureCache(FeatureList::OverrideState override_state, + uint32_t caching_context) { + return (static_cast(override_state) << 24) | + (caching_context & 0xFFFF); } } // namespace -#if defined(DCHECK_IS_CONFIGURABLE) -const Feature kDCheckIsFatalFeature{"DcheckIsFatal", - FEATURE_DISABLED_BY_DEFAULT}; -#endif // defined(DCHECK_IS_CONFIGURABLE) +#if BUILDFLAG(DCHECK_IS_CONFIGURABLE) +BASE_FEATURE(kDCheckIsFatalFeature, + "DcheckIsFatal", + FEATURE_DISABLED_BY_DEFAULT); +#endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE) FeatureList::FeatureList() = default; @@ -121,10 +271,39 @@ void FeatureList::InitializeFromCommandLine( 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(enable_features, OVERRIDE_ENABLE_FEATURE); + RegisterOverridesFromCommandLine(parsed_enable_features, + OVERRIDE_ENABLE_FEATURE); initialized_from_command_line_ = true; } @@ -144,17 +323,27 @@ void FeatureList::InitializeFromSharedMemory( if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name)) continue; - FieldTrial* trial = FieldTrialList::Find(trial_name.as_string()); + 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_state == state && - !it->second.overridden_by_field_trial; + return it != overrides_.end() && !it->second.overridden_by_field_trial && + it->second.overridden_state == state; } void FeatureList::AssociateReportingFieldTrial( @@ -181,15 +370,14 @@ 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 - << " has conflicting field trial overrides: " + DCHECK(!HasAssociatedFieldTrialByFeatureName(feature_name)) + << "Feature " << feature_name << " is overriden multiple times in these " + << "trials: " << overrides_.find(feature_name)->second.field_trial->trial_name() - << " / " << field_trial->trial_name() - << ". Please make sure that the trial (study) name is consistent across:" - << " (1)The server config, (2)The fieldtrial_testing_config, and" - << " (3) The about_flags.cc"; + << " 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); } @@ -227,29 +415,46 @@ void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) { } void FeatureList::GetFeatureOverrides(std::string* enable_overrides, - std::string* disable_overrides) { - GetFeatureOverridesImpl(enable_overrides, disable_overrides, false); + 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) { + std::string* disable_overrides) const { GetFeatureOverridesImpl(enable_overrides, disable_overrides, true); } // static bool FeatureList::IsEnabled(const Feature& feature) { if (!g_feature_list_instance) { - g_initialized_from_accessor = &feature; + EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature); return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; } return g_feature_list_instance->IsFeatureEnabled(feature); } // static +bool FeatureList::IsValidFeatureOrFieldTrialName(StringPiece name) { + return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; +} + +// static +absl::optional FeatureList::GetStateIfOverridden(const Feature& feature) { + if (!g_feature_list_instance) { + EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(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 (!g_feature_list_instance) { - g_initialized_from_accessor = &feature; + EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature); return nullptr; } return g_feature_list_instance->GetAssociatedFieldTrial(feature); @@ -262,6 +467,45 @@ std::vector FeatureList::SplitFeatureListString( } // 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, @@ -284,10 +528,7 @@ bool FeatureList::InitializeInstance( // 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); - } + EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess(); bool instance_existed_before = false; if (g_feature_list_instance) { if (g_feature_list_instance->initialized_from_command_line_) @@ -318,8 +559,19 @@ void FeatureList::SetInstance(std::unique_ptr instance) { // Note: Intentional leak of global singleton. g_feature_list_instance = instance.release(); -#if defined(DCHECK_IS_CONFIGURABLE) - // Update the behaviour of LOG_DCHECK to match the Feature configuration. + EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess(); + +#if !BUILDFLAG(IS_NACL) + // Configured first because it takes precedence over the getrandom() trial. + internal::ConfigureBoringSSLBackedRandBytesFieldTrial(); +#endif + +#if BUILDFLAG(IS_ANDROID) + internal::ConfigureRandBytesFieldTrial(); +#endif + +#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 @@ -329,18 +581,18 @@ void FeatureList::SetInstance(std::unique_ptr instance) { if (FeatureList::IsEnabled(kDCheckIsFatalFeature) || CommandLine::ForCurrentProcess()->HasSwitch( "gtest_internal_run_death_test")) { - logging::LOG_DCHECK = logging::LOG_FATAL; + logging::LOGGING_DCHECK = logging::LOG_FATAL; } else { - logging::LOG_DCHECK = logging::LOG_INFO; + logging::LOGGING_DCHECK = logging::LOG_INFO; } -#endif // defined(DCHECK_IS_CONFIGURABLE) +#endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE) } // static std::unique_ptr FeatureList::ClearInstanceForTesting() { FeatureList* old_instance = g_feature_list_instance; g_feature_list_instance = nullptr; - g_initialized_from_accessor = nullptr; + EarlyFeatureAccessTracker::GetInstance()->Reset(); return WrapUnique(old_instance); } @@ -352,6 +604,16 @@ void FeatureList::RestoreInstanceForTesting( g_feature_list_instance = instance.release(); } +// static +void FeatureList::FailOnFeatureAccessWithoutFeatureList() { + EarlyFeatureAccessTracker::GetInstance() + ->FailOnFeatureAccessWithoutFeatureList(); +} + +void FeatureList::SetCachingContextForTesting(uint16_t caching_context) { + caching_context_ = caching_context; +} + void FeatureList::FinalizeInitialization() { DCHECK(!initialized_); // Store the field trial list pointer for DCHECKing. @@ -359,43 +621,145 @@ void FeatureList::FinalizeInitialization() { initialized_ = true; } -bool FeatureList::IsFeatureEnabled(const Feature& feature) { +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 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; + 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_); + + // Update the cache with the new value. + // In non-test code, this value can be in one of 2 states: either it's unset, + // or another thread has updated it to the same value we're about to write. + // Because of this, a plain `store` yields the correct result in all cases. + // In test code, it's possible for a different thread to have installed a new + // `ScopedFeatureList` and written a value that's different than the one we're + // about to write, although that would be a thread safety violation already + // and such tests should be fixed. + 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; - auto it = overrides_.find(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(); + entry.field_trial->Activate(); // TODO(asvitkine) Expand this section as more support is added. - // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below. - if (entry.overridden_state != OVERRIDE_USE_DEFAULT) - return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; + return entry.overridden_state; } - // Otherwise, return the default state. - return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; + // Otherwise, report that we want to use the default state. + return OVERRIDE_USE_DEFAULT; } -FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) { +FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) const { DCHECK(initialized_); - DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; DCHECK(CheckFeatureIdentity(feature)) << feature.name; - auto it = overrides_.find(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.field_trial; + 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_); + 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::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) { @@ -408,12 +772,12 @@ void FeatureList::RegisterOverridesFromCommandLine( 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).as_string()); -#if !defined(OS_NACL) + 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 "Featuretrial_name())) << field_trial->trial_name(); } - if (feature_name.starts_with("*")) { + if (StartsWith(feature_name, "*")) { feature_name = feature_name.substr(1); overridden_state = OVERRIDE_USE_DEFAULT; } - // Note: The semantics of insert() is that it does not overwrite the entry if + // 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_.insert(std::make_pair( - feature_name.as_string(), OverrideEntry(overridden_state, field_trial))); + 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 command_line_only, + bool include_group_name) const { DCHECK(initialized_); // Check that the FieldTrialList this is associated with, if any, is the @@ -483,13 +848,18 @@ void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides, 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(entry.second.field_trial->trial_name()); + 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) { +bool FeatureList::CheckFeatureIdentity(const Feature& feature) const { AutoLock auto_lock(feature_identity_tracker_lock_); auto it = feature_identity_tracker_.find(feature.name); @@ -508,4 +878,21 @@ FeatureList::OverrideEntry::OverrideEntry(OverrideState 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* params) { + base::FieldTrial* trial = + feature_list_->GetAssociatedFieldTrialByFeatureName(feature_name); + return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial, + params); +} + } // namespace base