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