1 // 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 #ifndef BASE_FEATURE_LIST_H_ 6 #define BASE_FEATURE_LIST_H_ 7 8 #include <map> 9 #include <memory> 10 #include <string> 11 #include <vector> 12 13 #include "base/base_export.h" 14 #include "base/gtest_prod_util.h" 15 #include "base/macros.h" 16 #include "base/strings/string_piece.h" 17 #include "base/synchronization/lock.h" 18 19 namespace base { 20 21 class FieldTrial; 22 23 // Specifies whether a given feature is enabled or disabled by default. 24 enum FeatureState { 25 FEATURE_DISABLED_BY_DEFAULT, 26 FEATURE_ENABLED_BY_DEFAULT, 27 }; 28 29 // The Feature struct is used to define the default state for a feature. See 30 // comment below for more details. There must only ever be one struct instance 31 // for a given feature name - generally defined as a constant global variable or 32 // file static. 33 struct BASE_EXPORT Feature { 34 // The name of the feature. This should be unique to each feature and is used 35 // for enabling/disabling features via command line flags and experiments. 36 const char* const name; 37 38 // The default state (i.e. enabled or disabled) for this feature. 39 const FeatureState default_state; 40 }; 41 42 // The FeatureList class is used to determine whether a given feature is on or 43 // off. It provides an authoritative answer, taking into account command-line 44 // overrides and experimental control. 45 // 46 // The basic use case is for any feature that can be toggled (e.g. through 47 // command-line or an experiment) to have a defined Feature struct, e.g.: 48 // 49 // const base::Feature kMyGreatFeature { 50 // "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT 51 // }; 52 // 53 // Then, client code that wishes to query the state of the feature would check: 54 // 55 // if (base::FeatureList::IsEnabled(kMyGreatFeature)) { 56 // // Feature code goes here. 57 // } 58 // 59 // Behind the scenes, the above call would take into account any command-line 60 // flags to enable or disable the feature, any experiments that may control it 61 // and finally its default state (in that order of priority), to determine 62 // whether the feature is on. 63 // 64 // Features can be explicitly forced on or off by specifying a list of comma- 65 // separated feature names via the following command-line flags: 66 // 67 // --enable-features=Feature5,Feature7 68 // --disable-features=Feature1,Feature2,Feature3 69 // 70 // After initialization (which should be done single-threaded), the FeatureList 71 // API is thread safe. 72 // 73 // Note: This class is a singleton, but does not use base/memory/singleton.h in 74 // order to have control over its initialization sequence. Specifically, the 75 // intended use is to create an instance of this class and fully initialize it, 76 // before setting it as the singleton for a process, via SetInstance(). 77 class BASE_EXPORT FeatureList { 78 public: 79 FeatureList(); 80 ~FeatureList(); 81 82 // Initializes feature overrides via command-line flags |enable_features| and 83 // |disable_features|, each of which is a comma-separated list of features to 84 // enable or disable, respectively. If a feature appears on both lists, then 85 // it will be disabled. If a list entry has the format "FeatureName<TrialName" 86 // then this initialization will also associate the feature state override 87 // with the named field trial, if it exists. If a feature name is prefixed 88 // with the '*' character, it will be created with OVERRIDE_USE_DEFAULT - 89 // which is useful for associating with a trial while using the default state. 90 // Must only be invoked during the initialization phase (before 91 // FinalizeInitialization() has been called). 92 void InitializeFromCommandLine(const std::string& enable_features, 93 const std::string& disable_features); 94 95 // Specifies whether a feature override enables or disables the feature. 96 enum OverrideState { 97 OVERRIDE_USE_DEFAULT, 98 OVERRIDE_DISABLE_FEATURE, 99 OVERRIDE_ENABLE_FEATURE, 100 }; 101 102 // Returns true if the state of |feature_name| has been overridden via 103 // |InitializeFromCommandLine()|. 104 bool IsFeatureOverriddenFromCommandLine(const std::string& feature_name, 105 OverrideState state) const; 106 107 // Associates a field trial for reporting purposes corresponding to the 108 // command-line setting the feature state to |for_overridden_state|. The trial 109 // will be activated when the state of the feature is first queried. This 110 // should be called during registration, after InitializeFromCommandLine() has 111 // been called but before the instance is registered via SetInstance(). 112 void AssociateReportingFieldTrial(const std::string& feature_name, 113 OverrideState for_overridden_state, 114 FieldTrial* field_trial); 115 116 // Registers a field trial to override the enabled state of the specified 117 // feature to |override_state|. Command-line overrides still take precedence 118 // over field trials, so this will have no effect if the feature is being 119 // overridden from the command-line. The associated field trial will be 120 // activated when the feature state for this feature is queried. This should 121 // be called during registration, after InitializeFromCommandLine() has been 122 // called but before the instance is registered via SetInstance(). 123 void RegisterFieldTrialOverride(const std::string& feature_name, 124 OverrideState override_state, 125 FieldTrial* field_trial); 126 127 // Returns comma-separated lists of feature names (in the same format that is 128 // accepted by InitializeFromCommandLine()) corresponding to features that 129 // have been overridden - either through command-line or via FieldTrials. For 130 // those features that have an associated FieldTrial, the output entry will be 131 // of the format "FeatureName<TrialName", where "TrialName" is the name of the 132 // FieldTrial. Features that have overrides with OVERRIDE_USE_DEFAULT will be 133 // added to |enable_overrides| with a '*' character prefix. Must be called 134 // only after the instance has been initialized and registered. 135 void GetFeatureOverrides(std::string* enable_overrides, 136 std::string* disable_overrides); 137 138 // Returns whether the given |feature| is enabled. Must only be called after 139 // the singleton instance has been registered via SetInstance(). Additionally, 140 // a feature with a given name must only have a single corresponding Feature 141 // struct, which is checked in builds with DCHECKs enabled. 142 static bool IsEnabled(const Feature& feature); 143 144 // Returns the field trial associated with the given |feature|. Must only be 145 // called after the singleton instance has been registered via SetInstance(). 146 static FieldTrial* GetFieldTrial(const Feature& feature); 147 148 // Splits a comma-separated string containing feature names into a vector. 149 static std::vector<std::string> SplitFeatureListString( 150 const std::string& input); 151 152 // Initializes and sets an instance of FeatureList with feature overrides via 153 // command-line flags |enable_features| and |disable_features| if one has not 154 // already been set from command-line flags. Returns true if an instance did 155 // not previously exist. See InitializeFromCommandLine() for more details 156 // about |enable_features| and |disable_features| parameters. 157 static bool InitializeInstance(const std::string& enable_features, 158 const std::string& disable_features); 159 160 // Returns the singleton instance of FeatureList. Will return null until an 161 // instance is registered via SetInstance(). 162 static FeatureList* GetInstance(); 163 164 // Registers the given |instance| to be the singleton feature list for this 165 // process. This should only be called once and |instance| must not be null. 166 static void SetInstance(std::unique_ptr<FeatureList> instance); 167 168 // Clears the previously-registered singleton instance for tests. 169 static void ClearInstanceForTesting(); 170 171 private: 172 FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity); 173 174 struct OverrideEntry { 175 // The overridden enable (on/off) state of the feature. 176 const OverrideState overridden_state; 177 178 // An optional associated field trial, which will be activated when the 179 // state of the feature is queried for the first time. Weak pointer to the 180 // FieldTrial object that is owned by the FieldTrialList singleton. 181 base::FieldTrial* field_trial; 182 183 // Specifies whether the feature's state is overridden by |field_trial|. 184 // If it's not, and |field_trial| is not null, it means it is simply an 185 // associated field trial for reporting purposes (and |overridden_state| 186 // came from the command-line). 187 const bool overridden_by_field_trial; 188 189 // TODO(asvitkine): Expand this as more support is added. 190 191 // Constructs an OverrideEntry for the given |overridden_state|. If 192 // |field_trial| is not null, it implies that |overridden_state| comes from 193 // the trial, so |overridden_by_field_trial| will be set to true. 194 OverrideEntry(OverrideState overridden_state, FieldTrial* field_trial); 195 }; 196 197 // Finalizes the initialization state of the FeatureList, so that no further 198 // overrides can be registered. This is called by SetInstance() on the 199 // singleton feature list that is being registered. 200 void FinalizeInitialization(); 201 202 // Returns whether the given |feature| is enabled. This is invoked by the 203 // public FeatureList::IsEnabled() static function on the global singleton. 204 // Requires the FeatureList to have already been fully initialized. 205 bool IsFeatureEnabled(const Feature& feature); 206 207 // Returns the field trial associated with the given |feature|. This is 208 // invoked by the public FeatureList::GetFieldTrial() static function on the 209 // global singleton. Requires the FeatureList to have already been fully 210 // initialized. 211 base::FieldTrial* GetAssociatedFieldTrial(const Feature& feature); 212 213 // For each feature name in comma-separated list of strings |feature_list|, 214 // registers an override with the specified |overridden_state|. Also, will 215 // associate an optional named field trial if the entry is of the format 216 // "FeatureName<TrialName". 217 void RegisterOverridesFromCommandLine(const std::string& feature_list, 218 OverrideState overridden_state); 219 220 // Registers an override for feature |feature_name|. The override specifies 221 // whether the feature should be on or off (via |overridden_state|), which 222 // will take precedence over the feature's default state. If |field_trial| is 223 // not null, registers the specified field trial object to be associated with 224 // the feature, which will activate the field trial when the feature state is 225 // queried. If an override is already registered for the given feature, it 226 // will not be changed. 227 void RegisterOverride(StringPiece feature_name, 228 OverrideState overridden_state, 229 FieldTrial* field_trial); 230 231 // Verifies that there's only a single definition of a Feature struct for a 232 // given feature name. Keeps track of the first seen Feature struct for each 233 // feature. Returns false when called on a Feature struct with a different 234 // address than the first one it saw for that feature name. Used only from 235 // DCHECKs and tests. 236 bool CheckFeatureIdentity(const Feature& feature); 237 238 // Map from feature name to an OverrideEntry struct for the feature, if it 239 // exists. 240 std::map<std::string, OverrideEntry> overrides_; 241 242 // Locked map that keeps track of seen features, to ensure a single feature is 243 // only defined once. This verification is only done in builds with DCHECKs 244 // enabled. 245 Lock feature_identity_tracker_lock_; 246 std::map<std::string, const Feature*> feature_identity_tracker_; 247 248 // Whether this object has been fully initialized. This gets set to true as a 249 // result of FinalizeInitialization(). 250 bool initialized_ = false; 251 252 // Whether this object has been initialized from command line. 253 bool initialized_from_command_line_ = false; 254 255 DISALLOW_COPY_AND_ASSIGN(FeatureList); 256 }; 257 258 } // namespace base 259 260 #endif // BASE_FEATURE_LIST_H_ 261