1 // Copyright 2013 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 "components/variations/variations_associated_data.h"
6
7 #include <map>
8 #include <utility>
9 #include <vector>
10
11 #include "base/memory/singleton.h"
12
13 namespace chrome_variations {
14
15 // TODO(asvitkine): Delete these when this file moves to variations namespace.
16 using variations::ActiveGroupId;
17 using variations::ActiveGroupIdCompare;
18 using variations::MakeActiveGroupId;
19
20 namespace {
21
22 // The internal singleton accessor for the map, used to keep it thread-safe.
23 class GroupMapAccessor {
24 public:
25 typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare>
26 GroupToIDMap;
27
28 // Retrieve the singleton.
GetInstance()29 static GroupMapAccessor* GetInstance() {
30 return Singleton<GroupMapAccessor>::get();
31 }
32
33 // Note that this normally only sets the ID for a group the first time, unless
34 // |force| is set to true, in which case it will always override it.
AssociateID(IDCollectionKey key,const ActiveGroupId & group_identifier,const VariationID id,const bool force)35 void AssociateID(IDCollectionKey key,
36 const ActiveGroupId& group_identifier,
37 const VariationID id,
38 const bool force) {
39 #if !defined(NDEBUG)
40 DCHECK_EQ(3, ID_COLLECTION_COUNT);
41 // Ensure that at most one of the trigger/non-trigger web property IDs are
42 // set.
43 if (key == GOOGLE_WEB_PROPERTIES || key == GOOGLE_WEB_PROPERTIES_TRIGGER) {
44 IDCollectionKey other_key = key == GOOGLE_WEB_PROPERTIES ?
45 GOOGLE_WEB_PROPERTIES_TRIGGER : GOOGLE_WEB_PROPERTIES;
46 DCHECK_EQ(EMPTY_ID, GetID(other_key, group_identifier));
47 }
48
49 // Validate that all collections with this |group_identifier| have the same
50 // associated ID.
51 for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
52 IDCollectionKey other_key = static_cast<IDCollectionKey>(i);
53 if (other_key == key)
54 continue;
55 VariationID other_id = GetID(other_key, group_identifier);
56 DCHECK(other_id == EMPTY_ID || other_id == id);
57 }
58 #endif
59
60 base::AutoLock scoped_lock(lock_);
61
62 GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
63 if (force ||
64 group_to_id_map->find(group_identifier) == group_to_id_map->end())
65 (*group_to_id_map)[group_identifier] = id;
66 }
67
GetID(IDCollectionKey key,const ActiveGroupId & group_identifier)68 VariationID GetID(IDCollectionKey key,
69 const ActiveGroupId& group_identifier) {
70 base::AutoLock scoped_lock(lock_);
71 GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
72 GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier);
73 if (it == group_to_id_map->end())
74 return EMPTY_ID;
75 return it->second;
76 }
77
ClearAllMapsForTesting()78 void ClearAllMapsForTesting() {
79 base::AutoLock scoped_lock(lock_);
80
81 for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
82 GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i));
83 DCHECK(map);
84 map->clear();
85 }
86 }
87
88 private:
89 friend struct DefaultSingletonTraits<GroupMapAccessor>;
90
91 // Retrieves the GroupToIDMap for |key|.
GetGroupToIDMap(IDCollectionKey key)92 GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) {
93 return &group_to_id_maps_[key];
94 }
95
GroupMapAccessor()96 GroupMapAccessor() {
97 group_to_id_maps_.resize(ID_COLLECTION_COUNT);
98 }
~GroupMapAccessor()99 ~GroupMapAccessor() {}
100
101 base::Lock lock_;
102 std::vector<GroupToIDMap> group_to_id_maps_;
103
104 DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor);
105 };
106
107 // Singleton helper class that keeps track of the parameters of all variations
108 // and ensures access to these is thread-safe.
109 class VariationsParamAssociator {
110 public:
111 typedef std::pair<std::string, std::string> VariationKey;
112 typedef std::map<std::string, std::string> VariationParams;
113
114 // Retrieve the singleton.
GetInstance()115 static VariationsParamAssociator* GetInstance() {
116 return Singleton<VariationsParamAssociator>::get();
117 }
118
AssociateVariationParams(const std::string & trial_name,const std::string & group_name,const VariationParams & params)119 bool AssociateVariationParams(const std::string& trial_name,
120 const std::string& group_name,
121 const VariationParams& params) {
122 base::AutoLock scoped_lock(lock_);
123
124 if (IsFieldTrialActive(trial_name))
125 return false;
126
127 const VariationKey key(trial_name, group_name);
128 if (ContainsKey(variation_params_, key))
129 return false;
130
131 variation_params_[key] = params;
132 return true;
133 }
134
GetVariationParams(const std::string & trial_name,VariationParams * params)135 bool GetVariationParams(const std::string& trial_name,
136 VariationParams* params) {
137 base::AutoLock scoped_lock(lock_);
138
139 const std::string group_name =
140 base::FieldTrialList::FindFullName(trial_name);
141 const VariationKey key(trial_name, group_name);
142 if (!ContainsKey(variation_params_, key))
143 return false;
144
145 *params = variation_params_[key];
146 return true;
147 }
148
ClearAllParamsForTesting()149 void ClearAllParamsForTesting() {
150 base::AutoLock scoped_lock(lock_);
151 variation_params_.clear();
152 }
153
154 private:
155 friend struct DefaultSingletonTraits<VariationsParamAssociator>;
156
VariationsParamAssociator()157 VariationsParamAssociator() {}
~VariationsParamAssociator()158 ~VariationsParamAssociator() {}
159
160 // Tests whether a field trial is active (i.e. group() has been called on it).
161 // TODO(asvitkine): Expose this as an API on base::FieldTrial.
IsFieldTrialActive(const std::string & trial_name)162 bool IsFieldTrialActive(const std::string& trial_name) {
163 base::FieldTrial::ActiveGroups active_groups;
164 base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
165 for (size_t i = 0; i < active_groups.size(); ++i) {
166 if (active_groups[i].trial_name == trial_name)
167 return true;
168 }
169 return false;
170 }
171
172 base::Lock lock_;
173 std::map<VariationKey, VariationParams> variation_params_;
174
175 DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator);
176 };
177
178 } // namespace
179
AssociateGoogleVariationID(IDCollectionKey key,const std::string & trial_name,const std::string & group_name,VariationID id)180 void AssociateGoogleVariationID(IDCollectionKey key,
181 const std::string& trial_name,
182 const std::string& group_name,
183 VariationID id) {
184 GroupMapAccessor::GetInstance()->AssociateID(
185 key, MakeActiveGroupId(trial_name, group_name), id, false);
186 }
187
AssociateGoogleVariationIDForce(IDCollectionKey key,const std::string & trial_name,const std::string & group_name,VariationID id)188 void AssociateGoogleVariationIDForce(IDCollectionKey key,
189 const std::string& trial_name,
190 const std::string& group_name,
191 VariationID id) {
192 GroupMapAccessor::GetInstance()->AssociateID(
193 key, MakeActiveGroupId(trial_name, group_name), id, true);
194 }
195
GetGoogleVariationID(IDCollectionKey key,const std::string & trial_name,const std::string & group_name)196 VariationID GetGoogleVariationID(IDCollectionKey key,
197 const std::string& trial_name,
198 const std::string& group_name) {
199 return GroupMapAccessor::GetInstance()->GetID(
200 key, MakeActiveGroupId(trial_name, group_name));
201 }
202
AssociateVariationParams(const std::string & trial_name,const std::string & group_name,const std::map<std::string,std::string> & params)203 bool AssociateVariationParams(
204 const std::string& trial_name,
205 const std::string& group_name,
206 const std::map<std::string, std::string>& params) {
207 return VariationsParamAssociator::GetInstance()->AssociateVariationParams(
208 trial_name, group_name, params);
209 }
210
GetVariationParams(const std::string & trial_name,std::map<std::string,std::string> * params)211 bool GetVariationParams(const std::string& trial_name,
212 std::map<std::string, std::string>* params) {
213 return VariationsParamAssociator::GetInstance()->GetVariationParams(
214 trial_name, params);
215 }
216
GetVariationParamValue(const std::string & trial_name,const std::string & param_name)217 std::string GetVariationParamValue(const std::string& trial_name,
218 const std::string& param_name) {
219 std::map<std::string, std::string> params;
220 if (GetVariationParams(trial_name, ¶ms)) {
221 std::map<std::string, std::string>::iterator it = params.find(param_name);
222 if (it != params.end())
223 return it->second;
224 }
225 return std::string();
226 }
227
228 // Functions below are exposed for testing explicitly behind this namespace.
229 // They simply wrap existing functions in this file.
230 namespace testing {
231
ClearAllVariationIDs()232 void ClearAllVariationIDs() {
233 GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
234 }
235
ClearAllVariationParams()236 void ClearAllVariationParams() {
237 VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting();
238 }
239
240 } // namespace testing
241
242 } // namespace chrome_variations
243