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. 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. 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 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 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|. 87 GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) { 88 return &group_to_id_maps_[key]; 89 } 90 91 GroupMapAccessor() { 92 group_to_id_maps_.resize(ID_COLLECTION_COUNT); 93 } 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. 110 static VariationsParamAssociator* GetInstance() { 111 return Singleton<VariationsParamAssociator>::get(); 112 } 113 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 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 144 void ClearAllParamsForTesting() { 145 base::AutoLock scoped_lock(lock_); 146 variation_params_.clear(); 147 } 148 149 private: 150 friend struct DefaultSingletonTraits<VariationsParamAssociator>; 151 152 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. 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 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 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 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 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 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 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, ¶ms)) { 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 227 void ClearAllVariationIDs() { 228 GroupMapAccessor::GetInstance()->ClearAllMapsForTesting(); 229 } 230 231 void ClearAllVariationParams() { 232 VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting(); 233 } 234 235 } // namespace testing 236 237 } // namespace variations 238