blob: 38cdf57c139ff5812ae3063e6e5e74f140750322 [file] [log] [blame]
asvitkinebccbb862015-09-04 17:17:451// 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
asvitkine8423d172015-09-28 23:23:447#include <utility>
asvitkinebccbb862015-09-04 17:17:458#include <vector>
9
10#include "base/logging.h"
asvitkine8423d172015-09-28 23:23:4411#include "base/metrics/field_trial.h"
asvitkinebccbb862015-09-04 17:17:4512#include "base/strings/string_split.h"
13
14namespace base {
15
16namespace {
17
18// Pointer to the FeatureList instance singleton that was set via
19// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
20// have more control over initialization timing. Leaky.
21FeatureList* g_instance = nullptr;
22
asvitkinebccbb862015-09-04 17:17:4523} // namespace
24
25FeatureList::FeatureList() : initialized_(false) {}
26
27FeatureList::~FeatureList() {}
28
29void FeatureList::InitializeFromCommandLine(
30 const std::string& enable_features,
31 const std::string& disable_features) {
32 DCHECK(!initialized_);
33
34 // Process disabled features first, so that disabled ones take precedence over
35 // enabled ones (since RegisterOverride() uses insert()).
36 for (const auto& feature_name : SplitFeatureListString(disable_features)) {
asvitkine8423d172015-09-28 23:23:4437 RegisterOverride(feature_name, OVERRIDE_DISABLE_FEATURE, nullptr);
asvitkinebccbb862015-09-04 17:17:4538 }
39 for (const auto& feature_name : SplitFeatureListString(enable_features)) {
asvitkine8423d172015-09-28 23:23:4440 RegisterOverride(feature_name, OVERRIDE_ENABLE_FEATURE, nullptr);
asvitkinebccbb862015-09-04 17:17:4541 }
42}
43
asvitkine8423d172015-09-28 23:23:4444bool FeatureList::IsFeatureOverriddenFromCommandLine(
45 const std::string& feature_name,
46 OverrideState state) const {
47 auto it = overrides_.find(feature_name);
48 return it != overrides_.end() && it->second.overridden_state == state &&
49 !it->second.overridden_by_field_trial;
50}
51
52void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
53 OverrideState override_state,
54 FieldTrial* field_trial) {
55 DCHECK(field_trial);
56 DCHECK(!ContainsKey(overrides_, feature_name) ||
57 !overrides_.find(feature_name)->second.field_trial)
58 << "Feature " << feature_name
59 << " has conflicting field trial overrides: "
60 << overrides_.find(feature_name)->second.field_trial->trial_name()
61 << " / " << field_trial->trial_name();
62
63 RegisterOverride(feature_name, override_state, field_trial);
64}
65
66void 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
asvitkinebccbb862015-09-04 17:17:4586// static
87bool FeatureList::IsEnabled(const Feature& feature) {
88 return GetInstance()->IsFeatureEnabled(feature);
89}
90
91// static
asvitkine03007d02015-10-21 22:50:0692std::vector<std::string> FeatureList::SplitFeatureListString(
93 const std::string& input) {
94 return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
95}
96
97// static
asvitkinebccbb862015-09-04 17:17:4598FeatureList* FeatureList::GetInstance() {
99 return g_instance;
100}
101
102// static
103void FeatureList::SetInstance(scoped_ptr<FeatureList> instance) {
104 DCHECK(!g_instance);
105 instance->FinalizeInitialization();
106
107 // Note: Intentional leak of global singleton.
108 g_instance = instance.release();
109}
110
111// static
112void FeatureList::ClearInstanceForTesting() {
113 delete g_instance;
114 g_instance = nullptr;
115}
116
117void FeatureList::FinalizeInitialization() {
118 DCHECK(!initialized_);
119 initialized_ = true;
120}
121
122bool FeatureList::IsFeatureEnabled(const Feature& feature) {
123 DCHECK(initialized_);
124 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
125
126 auto it = overrides_.find(feature.name);
127 if (it != overrides_.end()) {
128 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44129
130 // Activate the corresponding field trial, if necessary.
131 if (entry.field_trial)
132 entry.field_trial->group();
133
asvitkinebccbb862015-09-04 17:17:45134 // TODO(asvitkine) Expand this section as more support is added.
135 return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
136 }
137 // Otherwise, return the default state.
138 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
139}
140
141void FeatureList::RegisterOverride(const std::string& feature_name,
asvitkine8423d172015-09-28 23:23:44142 OverrideState overridden_state,
143 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45144 DCHECK(!initialized_);
asvitkine8423d172015-09-28 23:23:44145 // Note: The semantics of insert() is that it does not overwrite the entry if
146 // one already exists for the key. Thus, only the first override for a given
147 // feature name takes effect.
148 overrides_.insert(std::make_pair(
149 feature_name, OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45150}
151
152bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
153 AutoLock auto_lock(feature_identity_tracker_lock_);
154
155 auto it = feature_identity_tracker_.find(feature.name);
156 if (it == feature_identity_tracker_.end()) {
157 // If it's not tracked yet, register it.
158 feature_identity_tracker_[feature.name] = &feature;
159 return true;
160 }
161 // Compare address of |feature| to the existing tracked entry.
162 return it->second == &feature;
163}
164
asvitkine8423d172015-09-28 23:23:44165FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
166 FieldTrial* field_trial)
167 : overridden_state(overridden_state),
168 field_trial(field_trial),
169 overridden_by_field_trial(field_trial != nullptr) {}
asvitkinebccbb862015-09-04 17:17:45170
171} // namespace base