feature_entry.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Copyright 2015 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. #include "components/flags_ui/feature_entry.h"
  5. #include "base/check_op.h"
  6. #include "base/logging.h"
  7. #include "base/notreached.h"
  8. #include "base/strings/strcat.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "ui/base/l10n/l10n_util.h"
  13. namespace flags_ui {
  14. // WARNING: '@' is also used in the html file. If you update this constant you
  15. // also need to update the html file.
  16. const char kMultiSeparatorChar = '@';
  17. // These descriptions are translated for display in Chrome Labs. If these
  18. // strings are changed the translated strings in Chrome Labs must also be
  19. // changed (IDS_CHROMELABS_XXX).
  20. const char kGenericExperimentChoiceDefault[] = "Default";
  21. const char kGenericExperimentChoiceEnabled[] = "Enabled";
  22. const char kGenericExperimentChoiceDisabled[] = "Disabled";
  23. const char kGenericExperimentChoiceAutomatic[] = "Automatic";
  24. bool FeatureEntry::InternalNameMatches(const std::string& name) const {
  25. if (!base::StartsWith(name, internal_name, base::CompareCase::SENSITIVE))
  26. return false;
  27. const size_t internal_name_length = strlen(internal_name);
  28. switch (type) {
  29. case FeatureEntry::SINGLE_VALUE:
  30. case FeatureEntry::SINGLE_DISABLE_VALUE:
  31. case FeatureEntry::ORIGIN_LIST_VALUE:
  32. return name.size() == internal_name_length;
  33. case FeatureEntry::MULTI_VALUE:
  34. case FeatureEntry::ENABLE_DISABLE_VALUE:
  35. case FeatureEntry::FEATURE_VALUE:
  36. case FeatureEntry::FEATURE_WITH_PARAMS_VALUE:
  37. #if BUILDFLAG(IS_CHROMEOS_ASH)
  38. case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
  39. case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
  40. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  41. // Check that the pattern matches what's produced by NameForOption().
  42. int index = -1;
  43. return name.size() > internal_name_length + 1 &&
  44. name[internal_name_length] == kMultiSeparatorChar &&
  45. base::StringToInt(name.substr(internal_name_length + 1), &index) &&
  46. index >= 0 && index < NumOptions();
  47. }
  48. }
  49. int FeatureEntry::NumOptions() const {
  50. switch (type) {
  51. case ENABLE_DISABLE_VALUE:
  52. case FEATURE_VALUE:
  53. #if BUILDFLAG(IS_CHROMEOS_ASH)
  54. case PLATFORM_FEATURE_NAME_VALUE:
  55. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  56. return 3;
  57. case MULTI_VALUE:
  58. return choices.size();
  59. case FEATURE_WITH_PARAMS_VALUE:
  60. #if BUILDFLAG(IS_CHROMEOS_ASH)
  61. case PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
  62. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  63. return 3 + GetVariations().size();
  64. default:
  65. return 0;
  66. }
  67. }
  68. std::string FeatureEntry::NameForOption(int index) const {
  69. DCHECK(type == FeatureEntry::MULTI_VALUE ||
  70. type == FeatureEntry::ENABLE_DISABLE_VALUE ||
  71. type == FeatureEntry::FEATURE_VALUE ||
  72. type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
  73. #if BUILDFLAG(IS_CHROMEOS_ASH)
  74. || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
  75. type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
  76. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  77. );
  78. DCHECK_LT(index, NumOptions());
  79. return std::string(internal_name) + testing::kMultiSeparator +
  80. base::NumberToString(index);
  81. }
  82. // The order in which these descriptions are returned is the same in the
  83. // LabsComboboxModel::GetItemAt(..) (in the chrome_labs_item_view.cc file) for
  84. // the translated version of these strings. If there are changes to this, the
  85. // same changes must be made in LabsComboboxModel
  86. std::u16string FeatureEntry::DescriptionForOption(int index) const {
  87. DCHECK(type == FeatureEntry::MULTI_VALUE ||
  88. type == FeatureEntry::ENABLE_DISABLE_VALUE ||
  89. type == FeatureEntry::FEATURE_VALUE ||
  90. type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
  91. #if BUILDFLAG(IS_CHROMEOS_ASH)
  92. || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
  93. type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
  94. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  95. );
  96. DCHECK_LT(index, NumOptions());
  97. const char* description = nullptr;
  98. if (type == FeatureEntry::ENABLE_DISABLE_VALUE ||
  99. type == FeatureEntry::FEATURE_VALUE
  100. #if BUILDFLAG(IS_CHROMEOS_ASH)
  101. || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE
  102. #endif
  103. ) {
  104. const char* const kEnableDisableDescriptions[] = {
  105. kGenericExperimentChoiceDefault,
  106. kGenericExperimentChoiceEnabled,
  107. kGenericExperimentChoiceDisabled,
  108. };
  109. description = kEnableDisableDescriptions[index];
  110. } else if (type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
  111. #if BUILDFLAG(IS_CHROMEOS_ASH)
  112. || type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
  113. #endif
  114. ) {
  115. if (index == 0) {
  116. description = kGenericExperimentChoiceDefault;
  117. } else if (index == 1) {
  118. description = kGenericExperimentChoiceEnabled;
  119. } else if (index < NumOptions() - 1) {
  120. // First two options do not have variations params.
  121. int variation_index = index - 2;
  122. return base::ASCIIToUTF16(
  123. base::StrCat({kGenericExperimentChoiceEnabled, " ",
  124. GetVariations()[variation_index].description_text}));
  125. } else {
  126. DCHECK_EQ(NumOptions() - 1, index);
  127. description = kGenericExperimentChoiceDisabled;
  128. }
  129. } else {
  130. description = choices[index].description;
  131. }
  132. return base::ASCIIToUTF16(description);
  133. }
  134. const FeatureEntry::Choice& FeatureEntry::ChoiceForOption(int index) const {
  135. DCHECK_EQ(FeatureEntry::MULTI_VALUE, type);
  136. DCHECK_LT(index, NumOptions());
  137. return choices[index];
  138. }
  139. FeatureEntry::FeatureState FeatureEntry::StateForOption(int index) const {
  140. DCHECK(type == FeatureEntry::FEATURE_VALUE ||
  141. type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
  142. #if BUILDFLAG(IS_CHROMEOS_ASH)
  143. || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
  144. type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
  145. #endif
  146. );
  147. DCHECK_LT(index, NumOptions());
  148. if (index == 0)
  149. return FeatureEntry::FeatureState::DEFAULT;
  150. if (index == NumOptions() - 1)
  151. return FeatureEntry::FeatureState::DISABLED;
  152. return FeatureEntry::FeatureState::ENABLED;
  153. }
  154. const FeatureEntry::FeatureVariation* FeatureEntry::VariationForOption(
  155. int index) const {
  156. DCHECK(type == FeatureEntry::FEATURE_VALUE ||
  157. type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
  158. #if BUILDFLAG(IS_CHROMEOS_ASH)
  159. || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
  160. type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
  161. #endif
  162. );
  163. DCHECK_LT(index, NumOptions());
  164. if ((type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
  165. #if BUILDFLAG(IS_CHROMEOS_ASH)
  166. || type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
  167. #endif
  168. ) &&
  169. index > 1 && index < NumOptions() - 1) {
  170. // We have no variations for FEATURE_VALUE type or
  171. // PLATFORM_FEATURE_NAME_VALUE type. Option at |index| corresponds to
  172. // variation at |index| - 2 as the list starts with "Default" and "Enabled"
  173. // (with default parameters).
  174. return &GetVariations()[index - 2];
  175. }
  176. return nullptr;
  177. }
  178. bool FeatureEntry::IsValid() const {
  179. switch (type) {
  180. case FeatureEntry::SINGLE_VALUE:
  181. case FeatureEntry::SINGLE_DISABLE_VALUE:
  182. case FeatureEntry::ORIGIN_LIST_VALUE:
  183. return true;
  184. case FeatureEntry::MULTI_VALUE:
  185. if (choices.size() == 0) {
  186. LOG(ERROR) << "no choice is found";
  187. return false;
  188. }
  189. if (!ChoiceForOption(0).command_line_switch) {
  190. LOG(ERROR) << "command_line_swtich is null";
  191. return false;
  192. }
  193. if (ChoiceForOption(0).command_line_switch[0] != '\0') {
  194. LOG(ERROR) << "The command line value of the first item must be empty";
  195. return false;
  196. }
  197. return true;
  198. case FeatureEntry::ENABLE_DISABLE_VALUE:
  199. if (!switches.command_line_switch) {
  200. LOG(ERROR) << "command_line_switch is null";
  201. return false;
  202. }
  203. if (!switches.command_line_value) {
  204. LOG(ERROR) << "command_line_value is null";
  205. return false;
  206. }
  207. if (!switches.disable_command_line_switch) {
  208. LOG(ERROR) << "disable_command_line_switch is null";
  209. return false;
  210. }
  211. if (!switches.disable_command_line_value) {
  212. LOG(ERROR) << "disable_command_line_value is null";
  213. return false;
  214. }
  215. return true;
  216. case FeatureEntry::FEATURE_VALUE:
  217. if (!feature.feature) {
  218. LOG(ERROR) << "no feature is set";
  219. return false;
  220. }
  221. return true;
  222. case FeatureEntry::FEATURE_WITH_PARAMS_VALUE:
  223. if (!feature.feature) {
  224. LOG(ERROR) << "no feature is set";
  225. return false;
  226. }
  227. if (feature.feature_variations.size() == 0) {
  228. LOG(ERROR) << "feature_variations is empty";
  229. return false;
  230. }
  231. if (!feature.feature_trial_name) {
  232. LOG(ERROR) << "feature_trial_name is null";
  233. return false;
  234. }
  235. return true;
  236. #if BUILDFLAG(IS_CHROMEOS_ASH)
  237. case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
  238. if (!platform_feature_name.name) {
  239. LOG(ERROR) << "no feature name is set";
  240. return false;
  241. }
  242. return true;
  243. case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
  244. if (!platform_feature_name.name) {
  245. LOG(ERROR) << "no feature name is set";
  246. return false;
  247. }
  248. if (platform_feature_name.feature_variations.size() == 0) {
  249. LOG(ERROR) << "feature_variations is empty";
  250. return false;
  251. }
  252. if (!platform_feature_name.feature_trial_name) {
  253. LOG(ERROR) << "feature_trial_name is null";
  254. return false;
  255. }
  256. return true;
  257. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  258. }
  259. NOTREACHED();
  260. return false;
  261. }
  262. const base::span<const FeatureEntry::FeatureVariation>
  263. FeatureEntry::GetVariations() const {
  264. if (type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
  265. return feature.feature_variations;
  266. }
  267. #if BUILDFLAG(IS_CHROMEOS_ASH)
  268. if (type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE) {
  269. return platform_feature_name.feature_variations;
  270. }
  271. #endif
  272. NOTREACHED();
  273. return base::span<const FeatureEntry::FeatureVariation>();
  274. }
  275. namespace testing {
  276. const char kMultiSeparator[] = {kMultiSeparatorChar, '\0'};
  277. } // namespace testing
  278. } // namespace flags_ui