• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params)) {
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