• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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