| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/feature_list.h" | 5 #include "base/feature_list.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/metrics/field_trial.h" | 13 #include "base/metrics/field_trial.h" |
| 14 #include "base/strings/string_split.h" | 14 #include "base/strings/string_split.h" |
| 15 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
| 16 | 16 |
| 17 namespace base { | 17 namespace base { |
| 18 | 18 |
| 19 namespace { | 19 namespace { |
| 20 | 20 |
| 21 // Pointer to the FeatureList instance singleton that was set via | 21 // Pointer to the FeatureList instance singleton that was set via |
| 22 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to | 22 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to |
| 23 // have more control over initialization timing. Leaky. | 23 // have more control over initialization timing. Leaky. |
| 24 FeatureList* g_instance = nullptr; | 24 FeatureList* g_instance = nullptr; |
| 25 | 25 |
| 26 // Tracks whether the FeatureList instance was initialized via an accessor. |
| 27 bool g_initialized_from_accessor = false; |
| 28 |
| 26 // Some characters are not allowed to appear in feature names or the associated | 29 // Some characters are not allowed to appear in feature names or the associated |
| 27 // field trial names, as they are used as special characters for command-line | 30 // field trial names, as they are used as special characters for command-line |
| 28 // serialization. This function checks that the strings are ASCII (since they | 31 // serialization. This function checks that the strings are ASCII (since they |
| 29 // are used in command-line API functions that require ASCII) and whether there | 32 // are used in command-line API functions that require ASCII) and whether there |
| 30 // are any reserved characters present, returning true if the string is valid. | 33 // are any reserved characters present, returning true if the string is valid. |
| 31 // Only called in DCHECKs. | 34 // Only called in DCHECKs. |
| 32 bool IsValidFeatureOrFieldTrialName(const std::string& name) { | 35 bool IsValidFeatureOrFieldTrialName(const std::string& name) { |
| 33 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; | 36 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; |
| 34 } | 37 } |
| 35 | 38 |
| 36 } // namespace | 39 } // namespace |
| 37 | 40 |
| 38 FeatureList::FeatureList() | 41 FeatureList::FeatureList() {} |
| 39 : initialized_(false), | |
| 40 initialized_from_command_line_(false) { | |
| 41 } | |
| 42 | 42 |
| 43 FeatureList::~FeatureList() {} | 43 FeatureList::~FeatureList() {} |
| 44 | 44 |
| 45 void FeatureList::InitializeFromCommandLine( | 45 void FeatureList::InitializeFromCommandLine( |
| 46 const std::string& enable_features, | 46 const std::string& enable_features, |
| 47 const std::string& disable_features) { | 47 const std::string& disable_features) { |
| 48 DCHECK(!initialized_); | 48 DCHECK(!initialized_); |
| 49 | 49 |
| 50 // Process disabled features first, so that disabled ones take precedence over | 50 // Process disabled features first, so that disabled ones take precedence over |
| 51 // enabled ones (since RegisterOverride() uses insert()). | 51 // enabled ones (since RegisterOverride() uses insert()). |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 target_list->append(entry.first); | 126 target_list->append(entry.first); |
| 127 if (entry.second.field_trial) { | 127 if (entry.second.field_trial) { |
| 128 target_list->push_back('<'); | 128 target_list->push_back('<'); |
| 129 target_list->append(entry.second.field_trial->trial_name()); | 129 target_list->append(entry.second.field_trial->trial_name()); |
| 130 } | 130 } |
| 131 } | 131 } |
| 132 } | 132 } |
| 133 | 133 |
| 134 // static | 134 // static |
| 135 bool FeatureList::IsEnabled(const Feature& feature) { | 135 bool FeatureList::IsEnabled(const Feature& feature) { |
| 136 return GetInstance()->IsFeatureEnabled(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); |
| 137 } | 141 } |
| 138 | 142 |
| 139 // static | 143 // static |
| 140 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) { | 144 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) { |
| 141 return GetInstance()->GetAssociatedFieldTrial(feature); | 145 return GetInstance()->GetAssociatedFieldTrial(feature); |
| 142 } | 146 } |
| 143 | 147 |
| 144 // static | 148 // static |
| 145 std::vector<std::string> FeatureList::SplitFeatureListString( | 149 std::vector<std::string> FeatureList::SplitFeatureListString( |
| 146 const std::string& input) { | 150 const std::string& input) { |
| 147 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); | 151 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); |
| 148 } | 152 } |
| 149 | 153 |
| 150 // static | 154 // static |
| 151 bool FeatureList::InitializeInstance(const std::string& enable_features, | 155 bool FeatureList::InitializeInstance(const std::string& enable_features, |
| 152 const std::string& disable_features) { | 156 const std::string& disable_features) { |
| 153 // We want to initialize a new instance here to support command-line features | 157 // We want to initialize a new instance here to support command-line features |
| 154 // in testing better. For example, we initialize a dummy instance in | 158 // in testing better. For example, we initialize a dummy instance in |
| 155 // base/test/test_suite.cc, and override it in content/browser/ | 159 // base/test/test_suite.cc, and override it in content/browser/ |
| 156 // browser_main_loop.cc. | 160 // browser_main_loop.cc. |
| 157 // On the other hand, we want to avoid re-initialization from command line. | 161 // On the other hand, we want to avoid re-initialization from command line. |
| 158 // For example, we initialize an instance in chrome/browser/ | 162 // For example, we initialize an instance in chrome/browser/ |
| 159 // chrome_browser_main.cc and do not override it in content/browser/ | 163 // chrome_browser_main.cc and do not override it in content/browser/ |
| 160 // browser_main_loop.cc. | 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); |
| 161 bool instance_existed_before = false; | 169 bool instance_existed_before = false; |
| 162 if (g_instance) { | 170 if (g_instance) { |
| 163 if (g_instance->initialized_from_command_line_) | 171 if (g_instance->initialized_from_command_line_) |
| 164 return false; | 172 return false; |
| 165 | 173 |
| 166 delete g_instance; | 174 delete g_instance; |
| 167 g_instance = nullptr; | 175 g_instance = nullptr; |
| 168 instance_existed_before = true; | 176 instance_existed_before = true; |
| 169 } | 177 } |
| 170 | 178 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 185 instance->FinalizeInitialization(); | 193 instance->FinalizeInitialization(); |
| 186 | 194 |
| 187 // Note: Intentional leak of global singleton. | 195 // Note: Intentional leak of global singleton. |
| 188 g_instance = instance.release(); | 196 g_instance = instance.release(); |
| 189 } | 197 } |
| 190 | 198 |
| 191 // static | 199 // static |
| 192 void FeatureList::ClearInstanceForTesting() { | 200 void FeatureList::ClearInstanceForTesting() { |
| 193 delete g_instance; | 201 delete g_instance; |
| 194 g_instance = nullptr; | 202 g_instance = nullptr; |
| 203 g_initialized_from_accessor = false; |
| 195 } | 204 } |
| 196 | 205 |
| 197 void FeatureList::FinalizeInitialization() { | 206 void FeatureList::FinalizeInitialization() { |
| 198 DCHECK(!initialized_); | 207 DCHECK(!initialized_); |
| 199 initialized_ = true; | 208 initialized_ = true; |
| 200 } | 209 } |
| 201 | 210 |
| 202 bool FeatureList::IsFeatureEnabled(const Feature& feature) { | 211 bool FeatureList::IsFeatureEnabled(const Feature& feature) { |
| 203 DCHECK(initialized_); | 212 DCHECK(initialized_); |
| 204 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; | 213 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 288 return it->second == &feature; | 297 return it->second == &feature; |
| 289 } | 298 } |
| 290 | 299 |
| 291 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, | 300 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, |
| 292 FieldTrial* field_trial) | 301 FieldTrial* field_trial) |
| 293 : overridden_state(overridden_state), | 302 : overridden_state(overridden_state), |
| 294 field_trial(field_trial), | 303 field_trial(field_trial), |
| 295 overridden_by_field_trial(field_trial != nullptr) {} | 304 overridden_by_field_trial(field_trial != nullptr) {} |
| 296 | 305 |
| 297 } // namespace base | 306 } // namespace base |
| OLD | NEW |