Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(745)

Side by Side Diff: components/flags_ui/flags_state.cc

Issue 2036193002: Allow overriding variation parameter via chrome://flags. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: After code review Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 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 "components/flags_ui/flags_state.h" 5 #include "components/flags_ui/flags_state.h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/feature_list.h" 11 #include "base/feature_list.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/macros.h" 13 #include "base/macros.h"
14 #include "base/metrics/field_trial.h"
14 #include "base/stl_util.h" 15 #include "base/stl_util.h"
15 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h" 18 #include "base/values.h"
18 #include "build/build_config.h" 19 #include "build/build_config.h"
19 #include "components/flags_ui/feature_entry.h" 20 #include "components/flags_ui/feature_entry.h"
20 #include "components/flags_ui/flags_storage.h" 21 #include "components/flags_ui/flags_storage.h"
21 #include "components/flags_ui/flags_ui_switches.h" 22 #include "components/flags_ui/flags_ui_switches.h"
23 #include "components/variations/variations_associated_data.h"
22 #include "ui/base/l10n/l10n_util.h" 24 #include "ui/base/l10n/l10n_util.h"
23 25
24 namespace flags_ui { 26 namespace flags_ui {
25 27
26 namespace { 28 namespace {
27 29
28 // Convert switch constants to proper CommandLine::StringType strings. 30 // Convert switch constants to proper CommandLine::StringType strings.
29 base::CommandLine::StringType GetSwitchString(const std::string& flag) { 31 base::CommandLine::StringType GetSwitchString(const std::string& flag) {
30 base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM); 32 base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
31 cmd_line.AppendSwitch(flag); 33 cmd_line.AppendSwitch(flag);
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 // Adds the internal names for the specified entry to |names|. 94 // Adds the internal names for the specified entry to |names|.
93 void AddInternalName(const FeatureEntry& e, std::set<std::string>* names) { 95 void AddInternalName(const FeatureEntry& e, std::set<std::string>* names) {
94 switch (e.type) { 96 switch (e.type) {
95 case FeatureEntry::SINGLE_VALUE: 97 case FeatureEntry::SINGLE_VALUE:
96 case FeatureEntry::SINGLE_DISABLE_VALUE: 98 case FeatureEntry::SINGLE_DISABLE_VALUE:
97 names->insert(e.internal_name); 99 names->insert(e.internal_name);
98 break; 100 break;
99 case FeatureEntry::MULTI_VALUE: 101 case FeatureEntry::MULTI_VALUE:
100 case FeatureEntry::ENABLE_DISABLE_VALUE: 102 case FeatureEntry::ENABLE_DISABLE_VALUE:
101 case FeatureEntry::FEATURE_VALUE: 103 case FeatureEntry::FEATURE_VALUE:
102 for (int i = 0; i < e.num_choices; ++i) 104 case FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE:
103 names->insert(e.NameForChoice(i)); 105 for (int i = 0; i < e.num_options; ++i)
106 names->insert(e.NameForOption(i));
104 break; 107 break;
105 } 108 }
106 } 109 }
107 110
108 // Confirms that an entry is valid, used in a DCHECK in 111 // Confirms that an entry is valid, used in a DCHECK in
109 // SanitizeList below. 112 // SanitizeList below.
110 bool ValidateFeatureEntry(const FeatureEntry& e) { 113 bool ValidateFeatureEntry(const FeatureEntry& e) {
111 switch (e.type) { 114 switch (e.type) {
112 case FeatureEntry::SINGLE_VALUE: 115 case FeatureEntry::SINGLE_VALUE:
113 case FeatureEntry::SINGLE_DISABLE_VALUE: 116 case FeatureEntry::SINGLE_DISABLE_VALUE:
114 DCHECK_EQ(0, e.num_choices); 117 DCHECK_EQ(0, e.num_options);
115 DCHECK(!e.choices); 118 DCHECK(!e.choices);
116 return true; 119 return true;
117 case FeatureEntry::MULTI_VALUE: 120 case FeatureEntry::MULTI_VALUE:
118 DCHECK_GT(e.num_choices, 0); 121 DCHECK_GT(e.num_options, 0);
119 DCHECK(e.choices); 122 DCHECK(e.choices);
120 DCHECK(e.choices[0].command_line_switch); 123 DCHECK(e.ChoiceForOption(0).command_line_switch);
121 DCHECK_EQ('\0', e.choices[0].command_line_switch[0]); 124 DCHECK_EQ('\0', e.ChoiceForOption(0).command_line_switch[0]);
122 return true; 125 return true;
123 case FeatureEntry::ENABLE_DISABLE_VALUE: 126 case FeatureEntry::ENABLE_DISABLE_VALUE:
124 DCHECK_EQ(3, e.num_choices); 127 DCHECK_EQ(3, e.num_options);
125 DCHECK(!e.choices); 128 DCHECK(!e.choices);
126 DCHECK(e.command_line_switch); 129 DCHECK(e.command_line_switch);
127 DCHECK(e.command_line_value); 130 DCHECK(e.command_line_value);
128 DCHECK(e.disable_command_line_switch); 131 DCHECK(e.disable_command_line_switch);
129 DCHECK(e.disable_command_line_value); 132 DCHECK(e.disable_command_line_value);
130 return true; 133 return true;
131 case FeatureEntry::FEATURE_VALUE: 134 case FeatureEntry::FEATURE_VALUE:
132 DCHECK_EQ(3, e.num_choices); 135 DCHECK_EQ(3, e.num_options);
133 DCHECK(!e.choices); 136 DCHECK(!e.choices);
134 DCHECK(e.feature); 137 DCHECK(e.feature);
135 return true; 138 return true;
139 case FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE:
140 DCHECK_GT(e.num_options, 2);
141 DCHECK(!e.choices);
142 DCHECK(e.feature);
143 DCHECK(e.feature_variations);
144 DCHECK(e.feature_trial_name);
145 return true;
136 } 146 }
137 NOTREACHED(); 147 NOTREACHED();
138 return false; 148 return false;
139 } 149 }
140 150
141 // Returns true if none of this entry's options have been enabled. 151 // Returns true if none of this entry's options have been enabled.
142 bool IsDefaultValue(const FeatureEntry& entry, 152 bool IsDefaultValue(const FeatureEntry& entry,
143 const std::set<std::string>& enabled_entries) { 153 const std::set<std::string>& enabled_entries) {
144 switch (entry.type) { 154 switch (entry.type) {
145 case FeatureEntry::SINGLE_VALUE: 155 case FeatureEntry::SINGLE_VALUE:
146 case FeatureEntry::SINGLE_DISABLE_VALUE: 156 case FeatureEntry::SINGLE_DISABLE_VALUE:
147 return enabled_entries.count(entry.internal_name) == 0; 157 return enabled_entries.count(entry.internal_name) == 0;
148 case FeatureEntry::MULTI_VALUE: 158 case FeatureEntry::MULTI_VALUE:
149 case FeatureEntry::ENABLE_DISABLE_VALUE: 159 case FeatureEntry::ENABLE_DISABLE_VALUE:
150 case FeatureEntry::FEATURE_VALUE: 160 case FeatureEntry::FEATURE_VALUE:
151 for (int i = 0; i < entry.num_choices; ++i) { 161 case FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE:
152 if (enabled_entries.count(entry.NameForChoice(i)) > 0) 162 for (int i = 0; i < entry.num_options; ++i) {
163 if (enabled_entries.count(entry.NameForOption(i)) > 0)
153 return false; 164 return false;
154 } 165 }
155 return true; 166 return true;
156 } 167 }
157 NOTREACHED(); 168 NOTREACHED();
158 return true; 169 return true;
159 } 170 }
160 171
161 // Returns the Value representing the choice data in the specified entry. 172 // Returns the Value representing the choice data in the specified entry.
162 base::Value* CreateChoiceData(const FeatureEntry& entry, 173 base::Value* CreateOptionsData(const FeatureEntry& entry,
163 const std::set<std::string>& enabled_entries) { 174 const std::set<std::string>& enabled_entries) {
164 DCHECK(entry.type == FeatureEntry::MULTI_VALUE || 175 DCHECK(entry.type == FeatureEntry::MULTI_VALUE ||
165 entry.type == FeatureEntry::ENABLE_DISABLE_VALUE || 176 entry.type == FeatureEntry::ENABLE_DISABLE_VALUE ||
166 entry.type == FeatureEntry::FEATURE_VALUE); 177 entry.type == FeatureEntry::FEATURE_VALUE ||
178 entry.type == FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE);
167 base::ListValue* result = new base::ListValue; 179 base::ListValue* result = new base::ListValue;
168 for (int i = 0; i < entry.num_choices; ++i) { 180 for (int i = 0; i < entry.num_options; ++i) {
169 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue); 181 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
170 const std::string name = entry.NameForChoice(i); 182 const std::string name = entry.NameForOption(i);
171 value->SetString("internal_name", name); 183 value->SetString("internal_name", name);
172 value->SetString("description", entry.DescriptionForChoice(i)); 184 value->SetString("description", entry.DescriptionForOption(i));
173 value->SetBoolean("selected", enabled_entries.count(name) > 0); 185 value->SetBoolean("selected", enabled_entries.count(name) > 0);
174 result->Append(std::move(value)); 186 result->Append(std::move(value));
175 } 187 }
176 return result; 188 return result;
177 } 189 }
178 190
191 // Registers variation parameters specified by |feature_variation| for the field
192 // trial named |feature_trial_name|, unless a group for this trial has already
193 // been created (e.g. via command-line switches that take precedence over
194 // about:flags). In the trial, the function creates a new constant group called
195 // |kTrialGroupAboutFlags|.
196 void RegisterFeatureVariationParameters(
197 const std::string& feature_trial_name,
198 const FeatureEntry::FeatureVariation& feature_variation) {
199 std::map<std::string, std::string> params;
200 for (int i = 0; i < feature_variation.num_params; ++i) {
201 params[feature_variation.params[i].param_name] =
202 feature_variation.params[i].param_value;
203 }
204
205 bool success = variations::AssociateVariationParams(
206 feature_trial_name, kTrialGroupAboutFlags, params);
207 if (success) {
208 // Successful association also means that no group is created and
209 // selected for the trial, yet. Thus, create the trial to select the group.
Alexei Svitkine (slow) 2016/06/16 09:40:57 Nit: Does "selected" fit on line above?
jkrcal 2016/06/16 14:49:17 Done (you were right, it fits).
210 // This way, the parameters cannot get overwritten in later phases
211 // (such as from the server).
212 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
213 feature_trial_name, kTrialGroupAboutFlags);
214 if (!trial) {
215 DLOG(WARNING) << "Could not create the trial " << feature_trial_name
216 << " with group " << kTrialGroupAboutFlags;
217 }
218 }
219 }
220
179 } // namespace 221 } // namespace
180 222
181 // Keeps track of affected switches for each FeatureEntry, based on which 223 // Keeps track of affected switches for each FeatureEntry, based on which
182 // choice is selected for it. 224 // choice is selected for it.
183 struct SwitchEntry { 225 struct SwitchEntry {
184 // Corresponding base::Feature to toggle. 226 // Corresponding base::Feature to toggle.
185 std::string feature_name; 227 std::string feature_name;
186 228
187 // If |feature_name| is not empty, the state (enable/disabled) to set. 229 // If |feature_name| is not empty, the state (enable/disabled) to set.
188 bool feature_state; 230 bool feature_state;
(...skipping 28 matching lines...) Expand all
217 std::map<std::string, SwitchEntry> name_to_switch_map; 259 std::map<std::string, SwitchEntry> name_to_switch_map;
218 for (size_t i = 0; i < num_feature_entries_; ++i) { 260 for (size_t i = 0; i < num_feature_entries_; ++i) {
219 const FeatureEntry& e = feature_entries_[i]; 261 const FeatureEntry& e = feature_entries_[i];
220 switch (e.type) { 262 switch (e.type) {
221 case FeatureEntry::SINGLE_VALUE: 263 case FeatureEntry::SINGLE_VALUE:
222 case FeatureEntry::SINGLE_DISABLE_VALUE: 264 case FeatureEntry::SINGLE_DISABLE_VALUE:
223 AddSwitchMapping(e.internal_name, e.command_line_switch, 265 AddSwitchMapping(e.internal_name, e.command_line_switch,
224 e.command_line_value, &name_to_switch_map); 266 e.command_line_value, &name_to_switch_map);
225 break; 267 break;
226 case FeatureEntry::MULTI_VALUE: 268 case FeatureEntry::MULTI_VALUE:
227 for (int j = 0; j < e.num_choices; ++j) { 269 for (int j = 0; j < e.num_options; ++j) {
228 AddSwitchMapping(e.NameForChoice(j), e.choices[j].command_line_switch, 270 AddSwitchMapping(
229 e.choices[j].command_line_value, 271 e.NameForOption(j), e.ChoiceForOption(j).command_line_switch,
230 &name_to_switch_map); 272 e.ChoiceForOption(j).command_line_value, &name_to_switch_map);
231 } 273 }
232 break; 274 break;
233 case FeatureEntry::ENABLE_DISABLE_VALUE: 275 case FeatureEntry::ENABLE_DISABLE_VALUE:
234 AddSwitchMapping(e.NameForChoice(0), std::string(), std::string(), 276 AddSwitchMapping(e.NameForOption(0), std::string(), std::string(),
235 &name_to_switch_map); 277 &name_to_switch_map);
236 AddSwitchMapping(e.NameForChoice(1), e.command_line_switch, 278 AddSwitchMapping(e.NameForOption(1), e.command_line_switch,
237 e.command_line_value, &name_to_switch_map); 279 e.command_line_value, &name_to_switch_map);
238 AddSwitchMapping(e.NameForChoice(2), e.disable_command_line_switch, 280 AddSwitchMapping(e.NameForOption(2), e.disable_command_line_switch,
239 e.disable_command_line_value, &name_to_switch_map); 281 e.disable_command_line_value, &name_to_switch_map);
240 break; 282 break;
241 case FeatureEntry::FEATURE_VALUE: 283 case FeatureEntry::FEATURE_VALUE:
242 AddFeatureMapping(e.NameForChoice(0), std::string(), false, 284 case FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE:
243 &name_to_switch_map); 285 for (int j = 0; j < e.num_options; ++j) {
244 AddFeatureMapping(e.NameForChoice(1), e.feature->name, true, 286 FeatureEntry::FeatureState state = e.StateForOption(j);
245 &name_to_switch_map); 287 if (state == FeatureEntry::FeatureState::DEFAULT) {
246 AddFeatureMapping(e.NameForChoice(2), e.feature->name, false, 288 AddFeatureMapping(e.NameForOption(j), std::string(), false,
247 &name_to_switch_map); 289 &name_to_switch_map);
290 } else {
291 AddFeatureMapping(e.NameForOption(j), e.feature->name,
292 state == FeatureEntry::FeatureState::ENABLED,
293 &name_to_switch_map);
294 }
295 }
248 break; 296 break;
249 } 297 }
250 } 298 }
251 299
252 AddSwitchesToCommandLine(enabled_entries, name_to_switch_map, sentinels, 300 AddSwitchesToCommandLine(enabled_entries, name_to_switch_map, sentinels,
253 command_line, enable_features_flag_name, 301 command_line, enable_features_flag_name,
254 disable_features_flag_name); 302 disable_features_flag_name);
255 } 303 }
256 304
257 bool FlagsState::IsRestartNeededToCommitChanges() { 305 bool FlagsState::IsRestartNeededToCommitChanges() {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 else 346 else
299 needs_restart_ |= (enabled_entries.erase(internal_name) > 0); 347 needs_restart_ |= (enabled_entries.erase(internal_name) > 0);
300 } else if (e->type == FeatureEntry::SINGLE_DISABLE_VALUE) { 348 } else if (e->type == FeatureEntry::SINGLE_DISABLE_VALUE) {
301 if (!enable) 349 if (!enable)
302 needs_restart_ |= enabled_entries.insert(internal_name).second; 350 needs_restart_ |= enabled_entries.insert(internal_name).second;
303 else 351 else
304 needs_restart_ |= (enabled_entries.erase(internal_name) > 0); 352 needs_restart_ |= (enabled_entries.erase(internal_name) > 0);
305 } else { 353 } else {
306 if (enable) { 354 if (enable) {
307 // Enable the first choice. 355 // Enable the first choice.
308 needs_restart_ |= enabled_entries.insert(e->NameForChoice(0)).second; 356 needs_restart_ |= enabled_entries.insert(e->NameForOption(0)).second;
309 } else { 357 } else {
310 // Find the currently enabled choice and disable it. 358 // Find the currently enabled choice and disable it.
311 for (int i = 0; i < e->num_choices; ++i) { 359 for (int i = 0; i < e->num_options; ++i) {
312 std::string choice_name = e->NameForChoice(i); 360 std::string choice_name = e->NameForOption(i);
313 if (enabled_entries.find(choice_name) != enabled_entries.end()) { 361 if (enabled_entries.find(choice_name) != enabled_entries.end()) {
314 needs_restart_ = true; 362 needs_restart_ = true;
315 enabled_entries.erase(choice_name); 363 enabled_entries.erase(choice_name);
316 // Continue on just in case there's a bug and more than one 364 // Continue on just in case there's a bug and more than one
317 // entry for this choice was enabled. 365 // entry for this choice was enabled.
318 } 366 }
319 } 367 }
320 } 368 }
321 } 369 }
322 370
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 std::set<std::string> no_entries; 421 std::set<std::string> no_entries;
374 flags_storage->SetFlags(no_entries); 422 flags_storage->SetFlags(no_entries);
375 } 423 }
376 424
377 void FlagsState::Reset() { 425 void FlagsState::Reset() {
378 needs_restart_ = false; 426 needs_restart_ = false;
379 flags_switches_.clear(); 427 flags_switches_.clear();
380 appended_switches_.clear(); 428 appended_switches_.clear();
381 } 429 }
382 430
431 void FlagsState::RegisterAllFeatureVariationParameters(
432 FlagsStorage* flags_storage) {
433 std::set<std::string> enabled_entries;
434 GetSanitizedEnabledFlagsForCurrentPlatform(flags_storage, &enabled_entries);
435
436 for (size_t i = 0; i < num_feature_entries_; ++i) {
437 const FeatureEntry& e = feature_entries_[i];
438 if (e.type == FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE) {
439 for (int j = 0; j < e.num_options; ++j) {
440 const FeatureEntry::FeatureVariation* variation =
441 e.VariationForOption(j);
442 if (variation != nullptr && enabled_entries.count(e.NameForOption(j))) {
443 // If the option is selected by the user & has variation, register it.
444 RegisterFeatureVariationParameters(e.feature_trial_name, *variation);
445 // TODO(jkrcal) The code does not associate the feature with the field
446 // trial |e.feature_trial_name|. The reason is that features
447 // overridden in chrome://flags are translated to command-line flags
448 // and thus treated earlier in the initialization. The fix requires
449 // larger changes.
Alexei Svitkine (slow) 2016/06/16 09:40:57 Please also mention what this means in practice.
jkrcal 2016/06/16 14:49:17 Done.
450 }
451 }
452 }
453 }
454 }
455
383 void FlagsState::GetFlagFeatureEntries( 456 void FlagsState::GetFlagFeatureEntries(
384 FlagsStorage* flags_storage, 457 FlagsStorage* flags_storage,
385 FlagAccess access, 458 FlagAccess access,
386 base::ListValue* supported_entries, 459 base::ListValue* supported_entries,
387 base::ListValue* unsupported_entries, 460 base::ListValue* unsupported_entries,
388 base::Callback<bool(const FeatureEntry&)> skip_feature_entry) { 461 base::Callback<bool(const FeatureEntry&)> skip_feature_entry) {
389 std::set<std::string> enabled_entries; 462 std::set<std::string> enabled_entries;
390 GetSanitizedEnabledFlags(flags_storage, &enabled_entries); 463 GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
391 464
392 int current_platform = GetCurrentPlatform(); 465 int current_platform = GetCurrentPlatform();
(...skipping 21 matching lines...) Expand all
414 case FeatureEntry::SINGLE_DISABLE_VALUE: 487 case FeatureEntry::SINGLE_DISABLE_VALUE:
415 data->SetBoolean( 488 data->SetBoolean(
416 "enabled", 489 "enabled",
417 (!is_default_value && entry.type == FeatureEntry::SINGLE_VALUE) || 490 (!is_default_value && entry.type == FeatureEntry::SINGLE_VALUE) ||
418 (is_default_value && 491 (is_default_value &&
419 entry.type == FeatureEntry::SINGLE_DISABLE_VALUE)); 492 entry.type == FeatureEntry::SINGLE_DISABLE_VALUE));
420 break; 493 break;
421 case FeatureEntry::MULTI_VALUE: 494 case FeatureEntry::MULTI_VALUE:
422 case FeatureEntry::ENABLE_DISABLE_VALUE: 495 case FeatureEntry::ENABLE_DISABLE_VALUE:
423 case FeatureEntry::FEATURE_VALUE: 496 case FeatureEntry::FEATURE_VALUE:
424 data->Set("choices", CreateChoiceData(entry, enabled_entries)); 497 case FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE:
498 data->Set("options", CreateOptionsData(entry, enabled_entries));
425 break; 499 break;
426 } 500 }
427 501
428 bool supported = (entry.supported_platforms & current_platform) != 0; 502 bool supported = (entry.supported_platforms & current_platform) != 0;
429 #if defined(OS_CHROMEOS) 503 #if defined(OS_CHROMEOS)
430 if (access == kOwnerAccessToFlags && 504 if (access == kOwnerAccessToFlags &&
431 (entry.supported_platforms & kOsCrOSOwnerOnly) != 0) { 505 (entry.supported_platforms & kOsCrOSOwnerOnly) != 0) {
432 supported = true; 506 supported = true;
433 } 507 }
434 #endif 508 #endif
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
634 } 708 }
635 709
636 std::set<std::string> new_enabled_entries = 710 std::set<std::string> new_enabled_entries =
637 base::STLSetIntersection<std::set<std::string>>(platform_entries, 711 base::STLSetIntersection<std::set<std::string>>(platform_entries,
638 *result); 712 *result);
639 713
640 result->swap(new_enabled_entries); 714 result->swap(new_enabled_entries);
641 } 715 }
642 716
643 } // namespace flags_ui 717 } // namespace flags_ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698