Home | History | Annotate | Download | only in base
      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 #include "base/feature_list.h"
      6 
      7 #include <stddef.h>
      8 
      9 #include <utility>
     10 #include <vector>
     11 
     12 #include "base/logging.h"
     13 #include "base/metrics/field_trial.h"
     14 #include "base/strings/string_split.h"
     15 #include "base/strings/string_util.h"
     16 
     17 namespace base {
     18 
     19 namespace {
     20 
     21 // Pointer to the FeatureList instance singleton that was set via
     22 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
     23 // have more control over initialization timing. Leaky.
     24 FeatureList* g_instance = nullptr;
     25 
     26 // Tracks whether the FeatureList instance was initialized via an accessor.
     27 bool g_initialized_from_accessor = false;
     28 
     29 // Some characters are not allowed to appear in feature names or the associated
     30 // field trial names, as they are used as special characters for command-line
     31 // serialization. This function checks that the strings are ASCII (since they
     32 // are used in command-line API functions that require ASCII) and whether there
     33 // are any reserved characters present, returning true if the string is valid.
     34 // Only called in DCHECKs.
     35 bool IsValidFeatureOrFieldTrialName(const std::string& name) {
     36   return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
     37 }
     38 
     39 }  // namespace
     40 
     41 FeatureList::FeatureList() {}
     42 
     43 FeatureList::~FeatureList() {}
     44 
     45 void FeatureList::InitializeFromCommandLine(
     46     const std::string& enable_features,
     47     const std::string& disable_features) {
     48   DCHECK(!initialized_);
     49 
     50   // Process disabled features first, so that disabled ones take precedence over
     51   // enabled ones (since RegisterOverride() uses insert()).
     52   RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
     53   RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
     54 
     55   initialized_from_command_line_ = true;
     56 }
     57 
     58 bool FeatureList::IsFeatureOverriddenFromCommandLine(
     59     const std::string& feature_name,
     60     OverrideState state) const {
     61   auto it = overrides_.find(feature_name);
     62   return it != overrides_.end() && it->second.overridden_state == state &&
     63          !it->second.overridden_by_field_trial;
     64 }
     65 
     66 void FeatureList::AssociateReportingFieldTrial(
     67     const std::string& feature_name,
     68     OverrideState for_overridden_state,
     69     FieldTrial* field_trial) {
     70   DCHECK(
     71       IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
     72 
     73   // Only one associated field trial is supported per feature. This is generally
     74   // enforced server-side.
     75   OverrideEntry* entry = &overrides_.find(feature_name)->second;
     76   if (entry->field_trial) {
     77     NOTREACHED() << "Feature " << feature_name
     78                  << " already has trial: " << entry->field_trial->trial_name()
     79                  << ", associating trial: " << field_trial->trial_name();
     80     return;
     81   }
     82 
     83   entry->field_trial = field_trial;
     84 }
     85 
     86 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
     87                                              OverrideState override_state,
     88                                              FieldTrial* field_trial) {
     89   DCHECK(field_trial);
     90   DCHECK(!ContainsKey(overrides_, feature_name) ||
     91          !overrides_.find(feature_name)->second.field_trial)
     92       << "Feature " << feature_name
     93       << " has conflicting field trial overrides: "
     94       << overrides_.find(feature_name)->second.field_trial->trial_name()
     95       << " / " << field_trial->trial_name();
     96 
     97   RegisterOverride(feature_name, override_state, field_trial);
     98 }
     99 
    100 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
    101                                       std::string* disable_overrides) {
    102   DCHECK(initialized_);
    103 
    104   enable_overrides->clear();
    105   disable_overrides->clear();
    106 
    107   // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
    108   // order. This not guaranteed to users of this function, but is useful for
    109   // tests to assume the order.
    110   for (const auto& entry : overrides_) {
    111     std::string* target_list = nullptr;
    112     switch (entry.second.overridden_state) {
    113       case OVERRIDE_USE_DEFAULT:
    114       case OVERRIDE_ENABLE_FEATURE:
    115         target_list = enable_overrides;
    116         break;
    117       case OVERRIDE_DISABLE_FEATURE:
    118         target_list = disable_overrides;
    119         break;
    120     }
    121 
    122     if (!target_list->empty())
    123       target_list->push_back(',');
    124     if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
    125       target_list->push_back('*');
    126     target_list->append(entry.first);
    127     if (entry.second.field_trial) {
    128       target_list->push_back('<');
    129       target_list->append(entry.second.field_trial->trial_name());
    130     }
    131   }
    132 }
    133 
    134 // static
    135 bool FeatureList::IsEnabled(const Feature& feature) {
    136   if (!g_instance) {
    137     g_initialized_from_accessor = true;
    138     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
    139   }
    140   return g_instance->IsFeatureEnabled(feature);
    141 }
    142 
    143 // static
    144 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
    145   return GetInstance()->GetAssociatedFieldTrial(feature);
    146 }
    147 
    148 // static
    149 std::vector<std::string> FeatureList::SplitFeatureListString(
    150     const std::string& input) {
    151   return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
    152 }
    153 
    154 // static
    155 bool FeatureList::InitializeInstance(const std::string& enable_features,
    156                                      const std::string& disable_features) {
    157   // We want to initialize a new instance here to support command-line features
    158   // in testing better. For example, we initialize a dummy instance in
    159   // base/test/test_suite.cc, and override it in content/browser/
    160   // browser_main_loop.cc.
    161   // On the other hand, we want to avoid re-initialization from command line.
    162   // For example, we initialize an instance in chrome/browser/
    163   // chrome_browser_main.cc and do not override it in content/browser/
    164   // browser_main_loop.cc.
    165   // If the singleton was previously initialized from within an accessor, we
    166   // want to prevent callers from reinitializing the singleton and masking the
    167   // accessor call(s) which likely returned incorrect information.
    168   CHECK(!g_initialized_from_accessor);
    169   bool instance_existed_before = false;
    170   if (g_instance) {
    171     if (g_instance->initialized_from_command_line_)
    172       return false;
    173 
    174     delete g_instance;
    175     g_instance = nullptr;
    176     instance_existed_before = true;
    177   }
    178 
    179   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
    180   feature_list->InitializeFromCommandLine(enable_features, disable_features);
    181   base::FeatureList::SetInstance(std::move(feature_list));
    182   return !instance_existed_before;
    183 }
    184 
    185 // static
    186 FeatureList* FeatureList::GetInstance() {
    187   return g_instance;
    188 }
    189 
    190 // static
    191 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
    192   DCHECK(!g_instance);
    193   instance->FinalizeInitialization();
    194 
    195   // Note: Intentional leak of global singleton.
    196   g_instance = instance.release();
    197 }
    198 
    199 // static
    200 void FeatureList::ClearInstanceForTesting() {
    201   delete g_instance;
    202   g_instance = nullptr;
    203   g_initialized_from_accessor = false;
    204 }
    205 
    206 void FeatureList::FinalizeInitialization() {
    207   DCHECK(!initialized_);
    208   initialized_ = true;
    209 }
    210 
    211 bool FeatureList::IsFeatureEnabled(const Feature& feature) {
    212   DCHECK(initialized_);
    213   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
    214   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
    215 
    216   auto it = overrides_.find(feature.name);
    217   if (it != overrides_.end()) {
    218     const OverrideEntry& entry = it->second;
    219 
    220     // Activate the corresponding field trial, if necessary.
    221     if (entry.field_trial)
    222       entry.field_trial->group();
    223 
    224     // TODO(asvitkine) Expand this section as more support is added.
    225 
    226     // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
    227     if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
    228       return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
    229   }
    230   // Otherwise, return the default state.
    231   return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
    232 }
    233 
    234 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
    235   DCHECK(initialized_);
    236   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
    237   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
    238 
    239   auto it = overrides_.find(feature.name);
    240   if (it != overrides_.end()) {
    241     const OverrideEntry& entry = it->second;
    242     return entry.field_trial;
    243   }
    244 
    245   return nullptr;
    246 }
    247 
    248 void FeatureList::RegisterOverridesFromCommandLine(
    249     const std::string& feature_list,
    250     OverrideState overridden_state) {
    251   for (const auto& value : SplitFeatureListString(feature_list)) {
    252     StringPiece feature_name(value);
    253     base::FieldTrial* trial = nullptr;
    254 
    255     // The entry may be of the form FeatureName<FieldTrialName - in which case,
    256     // this splits off the field trial name and associates it with the override.
    257     std::string::size_type pos = feature_name.find('<');
    258     if (pos != std::string::npos) {
    259       feature_name.set(value.data(), pos);
    260       trial = base::FieldTrialList::Find(value.substr(pos + 1));
    261     }
    262 
    263     RegisterOverride(feature_name, overridden_state, trial);
    264   }
    265 }
    266 
    267 void FeatureList::RegisterOverride(StringPiece feature_name,
    268                                    OverrideState overridden_state,
    269                                    FieldTrial* field_trial) {
    270   DCHECK(!initialized_);
    271   if (field_trial) {
    272     DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
    273         << field_trial->trial_name();
    274   }
    275   if (feature_name.starts_with("*")) {
    276     feature_name = feature_name.substr(1);
    277     overridden_state = OVERRIDE_USE_DEFAULT;
    278   }
    279 
    280   // Note: The semantics of insert() is that it does not overwrite the entry if
    281   // one already exists for the key. Thus, only the first override for a given
    282   // feature name takes effect.
    283   overrides_.insert(std::make_pair(
    284       feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
    285 }
    286 
    287 bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
    288   AutoLock auto_lock(feature_identity_tracker_lock_);
    289 
    290   auto it = feature_identity_tracker_.find(feature.name);
    291   if (it == feature_identity_tracker_.end()) {
    292     // If it's not tracked yet, register it.
    293     feature_identity_tracker_[feature.name] = &feature;
    294     return true;
    295   }
    296   // Compare address of |feature| to the existing tracked entry.
    297   return it->second == &feature;
    298 }
    299 
    300 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
    301                                           FieldTrial* field_trial)
    302     : overridden_state(overridden_state),
    303       field_trial(field_trial),
    304       overridden_by_field_trial(field_trial != nullptr) {}
    305 
    306 }  // namespace base
    307