// 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. #ifndef BASE_FEATURE_LIST_H_ #define BASE_FEATURE_LIST_H_ #include <atomic> #include <functional> #include <map> #include <memory> #include <optional> #include <string> #include <string_view> #include <utility> #include <vector> #include "base/base_export.h" #include "base/compiler_specific.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/dcheck_is_on.h" #include "base/feature_list_buildflags.h" #include "base/gtest_prod_util.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/synchronization/lock.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" namespace base { class FieldTrial; class FieldTrialList; class PersistentMemoryAllocator; #if BUILDFLAG(IS_CHROMEOS_ASH) class FeatureVisitor; #endif // BUILDFLAG(IS_CHROMEOS_ASH) // Specifies whether a given feature is enabled or disabled by default. // NOTE: The actual runtime state may be different, due to a field trial or a // command line switch. enum FeatureState { … }; // Recommended macros for declaring and defining features and parameters: // // - `kFeature` is the C++ identifier that will be used for the `base::Feature`. // - `name` is the feature name, which must be globally unique. This name is // used to enable/disable features via experiments and command-line flags. // Names should use CamelCase-style naming, e.g. "MyGreatFeature". // - `default_state` is the default state to use for the feature, i.e. // `base::FEATURE_DISABLED_BY_DEFAULT` or `base::FEATURE_ENABLED_BY_DEFAULT`. // As noted above, the actual runtime state may differ from the default state, // due to field trials or command-line switches. // Provides a forward declaration for `kFeature` in a header file, e.g. // // BASE_DECLARE_FEATURE(kMyFeature); // // If the feature needs to be marked as exported, i.e. it is referenced by // multiple components, then write: // // COMPONENT_EXPORT(MY_COMPONENT) BASE_DECLARE_FEATURE(kMyFeature); #define BASE_DECLARE_FEATURE(kFeature) … // Provides a definition for `kFeature` with `name` and `default_state`, e.g. // // BASE_FEATURE(kMyFeature, "MyFeature", base::FEATURE_DISABLED_BY_DEFAULT); // // Features should *not* be defined in header files; do not use this macro in // header files. #define BASE_FEATURE(feature, name, default_state) … // Provides a forward declaration for `feature_object_name` in a header file, // e.g. // // BASE_DECLARE_FEATURE_PARAM(kMyFeatureParam); // // If the feature needs to be marked as exported, i.e. it is referenced by // multiple components, then write: // // COMPONENT_EXPORT(MY_COMPONENT) BASE_DECLARE_FEATURE_PARAM(kMyFeatureParam); // // This macro enables optimizations to make the second and later calls faster, // but requires additional memory uses. If you obtain the parameter only once, // you can instantiate base::FeatureParam directly, or can call // base::GetFieldTrialParamByFeatureAsInt or equivalent functions for other // types directly. #define BASE_DECLARE_FEATURE_PARAM(T, feature_object_name) … // Provides a definition for `feature_object_name` with `T`, `feature`, `name` // and `default_value`, with an internal parsed value cache, e.g. // // BASE_FEATURE_PARAM(int, kMyFeatureParam, kMyFeature, "MyFeatureParam", 0); // // `T` is a parameter type, one of bool, int, size_t, double, std::string, and // base::TimeDelta. Enum types are not supported for now. // // For now, ScopedFeatureList doesn't work to change the value dynamically when // the cache is used with this macro. // // It should *not* be defined in header files; do not use this macro in header // files. #define BASE_FEATURE_PARAM(T, feature_object_name, feature, name, \ default_value) … // Same as BASE_FEATURE_PARAM() but used for enum type parameters with on extra // argument, `options`. See base::FeatureParam<Enum> template declaration in // //base/metrics/field_trial_params.h for `options`' details. #define BASE_FEATURE_ENUM_PARAM(T, feature_object_name, feature, name, \ default_value, options) … // Secret handshake to (try to) ensure all places that construct a base::Feature // go through the helper `BASE_FEATURE()` macro above. namespace internal { enum class FeatureMacroHandshake { … }; } // The Feature struct is used to define the default state for a feature. There // must only ever be one struct instance for a given feature name—generally // defined as a constant global variable or file static. Declare and define // features using the `BASE_DECLARE_FEATURE()` and `BASE_FEATURE()` macros // above, as there are some subtleties involved. // // Feature constants are internally mutable, as this allows them to contain a // mutable member to cache their override state, while still remaining declared // as const. This cache member allows for significantly faster IsEnabled() // checks. // // However, the "Mutable Constants" check [1] detects this as a regression, // because this usually means that a readonly symbol is put in writable memory // when readonly memory would be more efficient. // // The performance gains of the cache are large enough to offset the downsides // to having the symbols in bssdata rather than rodata. Use LOGICALLY_CONST to // suppress the "Mutable Constants" check. // // [1]: // https://crsrc.org/c/docs/speed/binary_size/android_binary_size_trybot.md#Mutable-Constants struct BASE_EXPORT LOGICALLY_CONST Feature { … }; #if BUILDFLAG(DCHECK_IS_CONFIGURABLE) // DCHECKs have been built-in, and are configurable at run-time to be fatal, or // not, via a DcheckIsFatal feature. We define the Feature here since it is // checked in FeatureList::SetInstance(). See https://crbug.com/596231. BASE_EXPORT BASE_DECLARE_FEATURE(kDCheckIsFatalFeature); #endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE) // The FeatureList class is used to determine whether a given feature is on or // off. It provides an authoritative answer, taking into account command-line // overrides and experimental control. // // The basic use case is for any feature that can be toggled (e.g. through // command-line or an experiment) to have a defined Feature struct, e.g.: // // const base::Feature kMyGreatFeature { // "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT // }; // // Then, client code that wishes to query the state of the feature would check: // // if (base::FeatureList::IsEnabled(kMyGreatFeature)) { // // Feature code goes here. // } // // Behind the scenes, the above call would take into account any command-line // flags to enable or disable the feature, any experiments that may control it // and finally its default state (in that order of priority), to determine // whether the feature is on. // // Features can be explicitly forced on or off by specifying a list of comma- // separated feature names via the following command-line flags: // // --enable-features=Feature5,Feature7 // --disable-features=Feature1,Feature2,Feature3 // // To enable/disable features in a test, do NOT append --enable-features or // --disable-features to the command-line directly. Instead, use // ScopedFeatureList. See base/test/scoped_feature_list.h for details. // // After initialization (which should be done single-threaded), the FeatureList // API is thread safe. // // Note: This class is a singleton, but does not use base/memory/singleton.h in // order to have control over its initialization sequence. Specifically, the // intended use is to create an instance of this class and fully initialize it, // before setting it as the singleton for a process, via SetInstance(). class BASE_EXPORT FeatureList { … }; } // namespace base #endif // BASE_FEATURE_LIST_H_