// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/variations/variations_associated_data.h" #include #include #include #include "base/macros.h" #include "base/memory/singleton.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_param_associator.h" #include "base/metrics/field_trial_params.h" #include "base/strings/string_split.h" #include "components/variations/variations_http_header_provider.h" namespace variations { namespace { // The internal singleton accessor for the map, used to keep it thread-safe. class GroupMapAccessor { public: typedef std::map GroupToIDMap; // Retrieve the singleton. static GroupMapAccessor* GetInstance() { return base::Singleton::get(); } // Note that this normally only sets the ID for a group the first time, unless // |force| is set to true, in which case it will always override it. void AssociateID(IDCollectionKey key, const ActiveGroupId& group_identifier, const VariationID id, const bool force) { #if !defined(NDEBUG) DCHECK_EQ(4, ID_COLLECTION_COUNT); // Ensure that at most one of the trigger/non-trigger/signed-in web property // IDs are set. if (key == GOOGLE_WEB_PROPERTIES || key == GOOGLE_WEB_PROPERTIES_TRIGGER || key == GOOGLE_WEB_PROPERTIES_SIGNED_IN) { if (key != GOOGLE_WEB_PROPERTIES) DCHECK_EQ(EMPTY_ID, GetID(GOOGLE_WEB_PROPERTIES, group_identifier)); if (key != GOOGLE_WEB_PROPERTIES_TRIGGER) { DCHECK_EQ(EMPTY_ID, GetID(GOOGLE_WEB_PROPERTIES_TRIGGER, group_identifier)); } if (key != GOOGLE_WEB_PROPERTIES_SIGNED_IN) { DCHECK_EQ(EMPTY_ID, GetID(GOOGLE_WEB_PROPERTIES_SIGNED_IN, group_identifier)); } } // Validate that all collections with this |group_identifier| have the same // associated ID. for (int i = 0; i < ID_COLLECTION_COUNT; ++i) { IDCollectionKey other_key = static_cast(i); if (other_key == key) continue; VariationID other_id = GetID(other_key, group_identifier); DCHECK(other_id == EMPTY_ID || other_id == id); } #endif base::AutoLock scoped_lock(lock_); GroupToIDMap* group_to_id_map = GetGroupToIDMap(key); if (force || group_to_id_map->find(group_identifier) == group_to_id_map->end()) (*group_to_id_map)[group_identifier] = id; } VariationID GetID(IDCollectionKey key, const ActiveGroupId& group_identifier) { base::AutoLock scoped_lock(lock_); GroupToIDMap* group_to_id_map = GetGroupToIDMap(key); GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier); if (it == group_to_id_map->end()) return EMPTY_ID; return it->second; } void ClearAllMapsForTesting() { base::AutoLock scoped_lock(lock_); for (int i = 0; i < ID_COLLECTION_COUNT; ++i) { GroupToIDMap* map = GetGroupToIDMap(static_cast(i)); DCHECK(map); map->clear(); } } private: friend struct base::DefaultSingletonTraits; // Retrieves the GroupToIDMap for |key|. GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) { return &group_to_id_maps_[key]; } GroupMapAccessor() { group_to_id_maps_.resize(ID_COLLECTION_COUNT); } ~GroupMapAccessor() {} base::Lock lock_; std::vector group_to_id_maps_; DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor); }; } // namespace void AssociateGoogleVariationID(IDCollectionKey key, const std::string& trial_name, const std::string& group_name, VariationID id) { GroupMapAccessor::GetInstance()->AssociateID( key, MakeActiveGroupId(trial_name, group_name), id, false); } void AssociateGoogleVariationIDForce(IDCollectionKey key, const std::string& trial_name, const std::string& group_name, VariationID id) { AssociateGoogleVariationIDForceHashes( key, MakeActiveGroupId(trial_name, group_name), id); } void AssociateGoogleVariationIDForceHashes(IDCollectionKey key, const ActiveGroupId& active_group, VariationID id) { GroupMapAccessor::GetInstance()->AssociateID(key, active_group, id, true); } VariationID GetGoogleVariationID(IDCollectionKey key, const std::string& trial_name, const std::string& group_name) { return GetGoogleVariationIDFromHashes( key, MakeActiveGroupId(trial_name, group_name)); } VariationID GetGoogleVariationIDFromHashes( IDCollectionKey key, const ActiveGroupId& active_group) { return GroupMapAccessor::GetInstance()->GetID(key, active_group); } bool AssociateVariationParams( const std::string& trial_name, const std::string& group_name, const std::map& params) { return base::AssociateFieldTrialParams(trial_name, group_name, params); } bool GetVariationParams(const std::string& trial_name, std::map* params) { return base::GetFieldTrialParams(trial_name, params); } bool GetVariationParamsByFeature(const base::Feature& feature, std::map* params) { return base::GetFieldTrialParamsByFeature(feature, params); } std::string GetVariationParamValue(const std::string& trial_name, const std::string& param_name) { return base::GetFieldTrialParamValue(trial_name, param_name); } std::string GetVariationParamValueByFeature(const base::Feature& feature, const std::string& param_name) { return base::GetFieldTrialParamValueByFeature(feature, param_name); } int GetVariationParamByFeatureAsInt(const base::Feature& feature, const std::string& param_name, int default_value) { return base::GetFieldTrialParamByFeatureAsInt(feature, param_name, default_value); } double GetVariationParamByFeatureAsDouble(const base::Feature& feature, const std::string& param_name, double default_value) { return base::GetFieldTrialParamByFeatureAsDouble(feature, param_name, default_value); } bool GetVariationParamByFeatureAsBool(const base::Feature& feature, const std::string& param_name, bool default_value) { return base::GetFieldTrialParamByFeatureAsBool(feature, param_name, default_value); } // Functions below are exposed for testing explicitly behind this namespace. // They simply wrap existing functions in this file. namespace testing { void ClearAllVariationIDs() { GroupMapAccessor::GetInstance()->ClearAllMapsForTesting(); } void ClearAllVariationParams() { base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting(); } } // namespace testing } // namespace variations