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