| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/common/variations/variations_util.h" | 5 #include "chrome/common/variations/variations_util.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <string> | 9 #include <string> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/feature_list.h" | 12 #include "base/feature_list.h" |
| 13 #include "base/metrics/field_trial.h" | 13 #include "base/metrics/field_trial.h" |
| 14 #include "base/strings/string_split.h" | 14 #include "base/strings/string_split.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "chrome/common/variations/fieldtrial_testing_config.h" | 16 #include "chrome/common/variations/fieldtrial_testing_config.h" |
| 17 #include "components/variations/variations_associated_data.h" | 17 #include "components/variations/variations_associated_data.h" |
| 18 #include "net/base/escape.h" | 18 #include "net/base/escape.h" |
| 19 | 19 |
| 20 namespace chrome_variations { | 20 namespace chrome_variations { |
| 21 | 21 |
| 22 namespace { | 22 namespace { |
| 23 | 23 |
| 24 std::string EscapeValue(const std::string& value) { | 24 std::string EscapeValue(const std::string& value) { |
| 25 return net::UnescapeURLComponent( | 25 return net::UnescapeURLComponent( |
| 26 value, net::UnescapeRule::PATH_SEPARATORS | | 26 value, net::UnescapeRule::PATH_SEPARATORS | |
| 27 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); | 27 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); |
| 28 } | 28 } |
| 29 | 29 |
| 30 void AssociateParamsFromGroup(const std::string& trial_name, |
| 31 const FieldTrialTestingGroup& group, |
| 32 base::FeatureList* feature_list) { |
| 33 if (group.params_size != 0) { |
| 34 std::map<std::string, std::string> params; |
| 35 for (size_t i = 0; i < group.params_size; ++i) { |
| 36 const FieldTrialTestingGroupParams& param = group.params[i]; |
| 37 params[param.key] = param.value; |
| 38 } |
| 39 variations::AssociateVariationParams(trial_name, group.name, params); |
| 40 } |
| 41 base::FieldTrial* trial = |
| 42 base::FieldTrialList::CreateFieldTrial(trial_name, group.name); |
| 43 |
| 44 if (!trial) { |
| 45 DLOG(WARNING) << "Field trial config trial skipped: " << trial_name |
| 46 << "." << group.name |
| 47 << " (it is overridden from chrome://flags)"; |
| 48 return; |
| 49 } |
| 50 |
| 51 for (size_t i = 0; i < group.enable_features_size; ++i) { |
| 52 feature_list->RegisterFieldTrialOverride( |
| 53 group.enable_features[i], base::FeatureList::OVERRIDE_ENABLE_FEATURE, |
| 54 trial); |
| 55 } |
| 56 for (size_t i = 0; i < group.disable_features_size; ++i) { |
| 57 feature_list->RegisterFieldTrialOverride( |
| 58 group.disable_features[i], |
| 59 base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial); |
| 60 } |
| 61 } |
| 62 |
| 30 } // namespace | 63 } // namespace |
| 31 | 64 |
| 32 bool AssociateParamsFromString(const std::string& varations_string) { | 65 bool AssociateParamsFromString(const std::string& varations_string) { |
| 33 // Format: Trial1.Group1:k1/v1/k2/v2,Trial2.Group2:k1/v1/k2/v2 | 66 // Format: Trial1.Group1:k1/v1/k2/v2,Trial2.Group2:k1/v1/k2/v2 |
| 34 std::set<std::pair<std::string, std::string>> trial_groups; | 67 std::set<std::pair<std::string, std::string>> trial_groups; |
| 35 for (const base::StringPiece& experiment_group : base::SplitStringPiece( | 68 for (const base::StringPiece& experiment_group : base::SplitStringPiece( |
| 36 varations_string, ",", | 69 varations_string, ",", |
| 37 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | 70 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { |
| 38 std::vector<base::StringPiece> experiment = base::SplitStringPiece( | 71 std::vector<base::StringPiece> experiment = base::SplitStringPiece( |
| 39 experiment_group, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 72 experiment_group, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 40 if (experiment.size() != 2) { | 73 if (experiment.size() != 2) { |
| 41 DLOG(ERROR) << "Experiment and params should be separated by ':'"; | 74 DLOG(ERROR) << "Experiment and params should be separated by ':'"; |
| 42 return false; | 75 return false; |
| 43 } | 76 } |
| 44 | 77 |
| 45 std::vector<std::string> group_parts = base::SplitString( | 78 std::vector<std::string> group_parts = base::SplitString( |
| 46 experiment[0], ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 79 experiment[0], ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 47 if (group_parts.size() != 2) { | 80 if (group_parts.size() != 2) { |
| 48 DLOG(ERROR) << "Study and group name should be separated by '.'"; | 81 DLOG(ERROR) << "Trial and group name should be separated by '.'"; |
| 49 return false; | 82 return false; |
| 50 } | 83 } |
| 51 | 84 |
| 52 std::vector<std::string> key_values = base::SplitString( | 85 std::vector<std::string> key_values = base::SplitString( |
| 53 experiment[1], "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 86 experiment[1], "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 54 if (key_values.size() % 2 != 0) { | 87 if (key_values.size() % 2 != 0) { |
| 55 DLOG(ERROR) << "Param name and param value should be separated by '/'"; | 88 DLOG(ERROR) << "Param name and param value should be separated by '/'"; |
| 56 return false; | 89 return false; |
| 57 } | 90 } |
| 58 std::string trial = EscapeValue(group_parts[0]); | 91 std::string trial = EscapeValue(group_parts[0]); |
| 59 std::string group = EscapeValue(group_parts[1]); | 92 std::string group = EscapeValue(group_parts[1]); |
| 60 auto trial_group = std::make_pair(trial, group); | 93 auto trial_group = std::make_pair(trial, group); |
| 61 if (trial_groups.find(trial_group) != trial_groups.end()) { | 94 if (trial_groups.find(trial_group) != trial_groups.end()) { |
| 62 DLOG(ERROR) << base::StringPrintf( | 95 DLOG(ERROR) << base::StringPrintf( |
| 63 "A (study, group) pair listed more than once. (%s, %s)", | 96 "A (trial, group) pair listed more than once. (%s, %s)", |
| 64 trial.c_str(), group.c_str()); | 97 trial.c_str(), group.c_str()); |
| 65 return false; | 98 return false; |
| 66 } | 99 } |
| 67 trial_groups.insert(trial_group); | 100 trial_groups.insert(trial_group); |
| 68 std::map<std::string, std::string> params; | 101 std::map<std::string, std::string> params; |
| 69 for (size_t i = 0; i < key_values.size(); i += 2) { | 102 for (size_t i = 0; i < key_values.size(); i += 2) { |
| 70 std::string key = EscapeValue(key_values[i]); | 103 std::string key = EscapeValue(key_values[i]); |
| 71 std::string value = EscapeValue(key_values[i + 1]); | 104 std::string value = EscapeValue(key_values[i + 1]); |
| 72 params[key] = value; | 105 params[key] = value; |
| 73 } | 106 } |
| 74 variations::AssociateVariationParams(trial, group, params); | 107 variations::AssociateVariationParams(trial, group, params); |
| 75 } | 108 } |
| 76 return true; | 109 return true; |
| 77 } | 110 } |
| 78 | 111 |
| 79 void AssociateParamsFromFieldTrialConfig(const FieldTrialTestingConfig& config, | 112 void AssociateParamsFromFieldTrialConfig(const FieldTrialTestingConfig& config, |
| 80 base::FeatureList* feature_list) { | 113 base::FeatureList* feature_list) { |
| 81 for (size_t i = 0; i < config.groups_size; ++i) { | 114 for (size_t i = 0; i < config.trials_size; ++i) { |
| 82 const FieldTrialTestingGroup& group = config.groups[i]; | 115 const FieldTrialTestingTrial& trial = config.trials[i]; |
| 83 if (group.params_size != 0) { | 116 if (trial.groups_size > 0) { |
| 84 std::map<std::string, std::string> params; | 117 AssociateParamsFromGroup(trial.name, trial.groups[0], feature_list); |
| 85 for (size_t j = 0; j < group.params_size; ++j) { | 118 } else { |
| 86 const FieldTrialGroupParams& param = group.params[j]; | 119 DLOG(ERROR) << "Unexpected empty trial: " << trial.name; |
| 87 params[param.key] = param.value; | |
| 88 } | |
| 89 variations::AssociateVariationParams(group.study, group.group_name, | |
| 90 params); | |
| 91 } | |
| 92 base::FieldTrial* trial = | |
| 93 base::FieldTrialList::CreateFieldTrial(group.study, group.group_name); | |
| 94 | |
| 95 if (!trial) { | |
| 96 DLOG(WARNING) << "Field trial config study skipped: " << group.study | |
| 97 << "." << group.group_name | |
| 98 << " (it is overridden from chrome://flags)"; | |
| 99 continue; | |
| 100 } | |
| 101 | |
| 102 for (size_t j = 0; j < group.enable_features_size; ++j) { | |
| 103 feature_list->RegisterFieldTrialOverride( | |
| 104 group.enable_features[j], base::FeatureList::OVERRIDE_ENABLE_FEATURE, | |
| 105 trial); | |
| 106 } | |
| 107 for (size_t j = 0; j < group.disable_features_size; ++j) { | |
| 108 feature_list->RegisterFieldTrialOverride( | |
| 109 group.disable_features[j], | |
| 110 base::FeatureList::OVERRIDE_DISABLE_FEATURE, trial); | |
| 111 } | 120 } |
| 112 } | 121 } |
| 113 } | 122 } |
| 114 | 123 |
| 115 void AssociateDefaultFieldTrialConfig(base::FeatureList* feature_list) { | 124 void AssociateDefaultFieldTrialConfig(base::FeatureList* feature_list) { |
| 116 AssociateParamsFromFieldTrialConfig(kFieldTrialConfig, feature_list); | 125 AssociateParamsFromFieldTrialConfig(kFieldTrialConfig, feature_list); |
| 117 } | 126 } |
| 118 | 127 |
| 119 } // namespace chrome_variations | 128 } // namespace chrome_variations |
| OLD | NEW |