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. 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. 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 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 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|. 92 GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) { 93 return &group_to_id_maps_[key]; 94 } 95 96 GroupMapAccessor() { 97 group_to_id_maps_.resize(ID_COLLECTION_COUNT); 98 } 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. 115 static VariationsParamAssociator* GetInstance() { 116 return Singleton<VariationsParamAssociator>::get(); 117 } 118 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 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 149 void ClearAllParamsForTesting() { 150 base::AutoLock scoped_lock(lock_); 151 variation_params_.clear(); 152 } 153 154 private: 155 friend struct DefaultSingletonTraits<VariationsParamAssociator>; 156 157 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. 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 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 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 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 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 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 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 232 void ClearAllVariationIDs() { 233 GroupMapAccessor::GetInstance()->ClearAllMapsForTesting(); 234 } 235 236 void ClearAllVariationParams() { 237 VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting(); 238 } 239 240 } // namespace testing 241 242 } // namespace chrome_variations 243