OLD | NEW |
---|---|
(Empty) | |
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 | |
5 #include "components/flags_ui/flags_state.h" | |
6 | |
7 #include "base/callback.h" | |
8 #include "base/feature_list.h" | |
9 #include "base/logging.h" | |
10 #include "base/stl_util.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "base/values.h" | |
14 #include "build/build_config.h" | |
15 #include "components/flags_ui/feature_entry.h" | |
16 #include "components/flags_ui/flags_storage.h" | |
17 #include "components/flags_ui/flags_ui_switches.h" | |
18 #include "ui/base/l10n/l10n_util.h" | |
19 | |
20 namespace flags_ui { | |
21 | |
22 namespace { | |
23 | |
24 const struct { | |
Alexei Svitkine (slow)
2015/11/16 21:52:54
Nit: I'd define it next to the function that uses
sdefresne
2015/11/17 16:28:56
Done.
| |
25 unsigned bit; | |
26 const char* const name; | |
27 } kBitsToOs[] = { | |
28 {kOsMac, "Mac"}, | |
29 {kOsWin, "Windows"}, | |
30 {kOsLinux, "Linux"}, | |
31 {kOsCrOS, "Chrome OS"}, | |
32 {kOsAndroid, "Android"}, | |
33 {kOsCrOSOwnerOnly, "Chrome OS (owner only)"}, | |
34 }; | |
35 | |
36 // Convert switch constants to proper CommandLine::StringType strings. | |
37 base::CommandLine::StringType GetSwitchString(const std::string& flag) { | |
38 base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM); | |
39 cmd_line.AppendSwitch(flag); | |
40 DCHECK_EQ(2U, cmd_line.argv().size()); | |
41 return cmd_line.argv()[1]; | |
42 } | |
43 | |
44 // Scoops flags from a command line. | |
Alexei Svitkine (slow)
2015/11/16 21:52:54
Nit: Expand the comment to mention the two new par
sdefresne
2015/11/17 16:28:56
Done.
| |
45 std::set<base::CommandLine::StringType> ExtractFlagsFromCommandLine( | |
46 const base::CommandLine& cmdline, | |
47 const char* extra_flag_sentinel_begin_flag_name, | |
48 const char* extra_flag_sentinel_end_flag_name) { | |
49 DCHECK_EQ(!!extra_flag_sentinel_begin_flag_name, | |
50 !!extra_flag_sentinel_end_flag_name); | |
51 std::set<base::CommandLine::StringType> flags; | |
52 // First do the ones between --flag-switches-begin and --flag-switches-end. | |
53 base::CommandLine::StringVector::const_iterator first = | |
54 std::find(cmdline.argv().begin(), cmdline.argv().end(), | |
55 GetSwitchString(switches::kFlagSwitchesBegin)); | |
56 base::CommandLine::StringVector::const_iterator last = | |
57 std::find(cmdline.argv().begin(), cmdline.argv().end(), | |
58 GetSwitchString(switches::kFlagSwitchesEnd)); | |
59 if (first != cmdline.argv().end() && last != cmdline.argv().end()) | |
60 flags.insert(first + 1, last); | |
61 if (extra_flag_sentinel_begin_flag_name && | |
62 extra_flag_sentinel_end_flag_name) { | |
63 // Then add those between the extra sentinels. | |
Alexei Svitkine (slow)
2015/11/16 21:52:54
Nit: Move this comment above the outside if and ad
sdefresne
2015/11/17 16:28:56
Done.
| |
64 first = std::find(cmdline.argv().begin(), cmdline.argv().end(), | |
65 GetSwitchString(extra_flag_sentinel_begin_flag_name)); | |
66 last = std::find(cmdline.argv().begin(), cmdline.argv().end(), | |
67 GetSwitchString(extra_flag_sentinel_end_flag_name)); | |
68 if (first != cmdline.argv().end() && last != cmdline.argv().end()) | |
69 flags.insert(first + 1, last); | |
70 } | |
71 return flags; | |
72 } | |
73 | |
74 // Adds a |StringValue| to |list| for each platform where |bitmask| indicates | |
75 // whether the entry is available on that platform. | |
76 void AddOsStrings(unsigned bitmask, base::ListValue* list) { | |
77 for (size_t i = 0; i < arraysize(kBitsToOs); ++i) { | |
78 if (bitmask & kBitsToOs[i].bit) | |
79 list->Append(new base::StringValue(kBitsToOs[i].name)); | |
80 } | |
81 } | |
82 | |
83 // Adds the internal names for the specified entry to |names|. | |
84 void AddInternalName(const FeatureEntry& e, std::set<std::string>* names) { | |
85 switch (e.type) { | |
86 case FeatureEntry::SINGLE_VALUE: | |
87 case FeatureEntry::SINGLE_DISABLE_VALUE: | |
88 names->insert(e.internal_name); | |
89 break; | |
90 case FeatureEntry::MULTI_VALUE: | |
91 case FeatureEntry::ENABLE_DISABLE_VALUE: | |
92 case FeatureEntry::FEATURE_VALUE: | |
93 for (int i = 0; i < e.num_choices; ++i) | |
94 names->insert(e.NameForChoice(i)); | |
95 break; | |
96 } | |
97 } | |
98 | |
99 // Confirms that an entry is valid, used in a DCHECK in | |
100 // SanitizeList below. | |
101 bool ValidateFeatureEntry(const FeatureEntry& e) { | |
102 switch (e.type) { | |
103 case FeatureEntry::SINGLE_VALUE: | |
104 case FeatureEntry::SINGLE_DISABLE_VALUE: | |
105 DCHECK_EQ(0, e.num_choices); | |
106 DCHECK(!e.choices); | |
107 return true; | |
108 case FeatureEntry::MULTI_VALUE: | |
109 DCHECK_GT(e.num_choices, 0); | |
110 DCHECK(e.choices); | |
111 DCHECK(e.choices[0].command_line_switch); | |
112 DCHECK_EQ('\0', e.choices[0].command_line_switch[0]); | |
113 return true; | |
114 case FeatureEntry::ENABLE_DISABLE_VALUE: | |
115 DCHECK_EQ(3, e.num_choices); | |
116 DCHECK(!e.choices); | |
117 DCHECK(e.command_line_switch); | |
118 DCHECK(e.command_line_value); | |
119 DCHECK(e.disable_command_line_switch); | |
120 DCHECK(e.disable_command_line_value); | |
121 return true; | |
122 case FeatureEntry::FEATURE_VALUE: | |
123 DCHECK_EQ(3, e.num_choices); | |
124 DCHECK(!e.choices); | |
125 DCHECK(e.feature); | |
126 return true; | |
127 } | |
128 NOTREACHED(); | |
129 return false; | |
130 } | |
131 | |
132 // Returns true if none of this entry's options have been enabled. | |
133 bool IsDefaultValue(const FeatureEntry& entry, | |
134 const std::set<std::string>& enabled_entries) { | |
135 switch (entry.type) { | |
136 case FeatureEntry::SINGLE_VALUE: | |
137 case FeatureEntry::SINGLE_DISABLE_VALUE: | |
138 return enabled_entries.count(entry.internal_name) == 0; | |
139 case FeatureEntry::MULTI_VALUE: | |
140 case FeatureEntry::ENABLE_DISABLE_VALUE: | |
141 case FeatureEntry::FEATURE_VALUE: | |
142 for (int i = 0; i < entry.num_choices; ++i) { | |
143 if (enabled_entries.count(entry.NameForChoice(i)) > 0) | |
144 return false; | |
145 } | |
146 return true; | |
147 } | |
148 NOTREACHED(); | |
149 return true; | |
150 } | |
151 | |
152 // Returns the Value representing the choice data in the specified entry. | |
153 base::Value* CreateChoiceData(const FeatureEntry& entry, | |
154 const std::set<std::string>& enabled_entries) { | |
155 DCHECK(entry.type == FeatureEntry::MULTI_VALUE || | |
156 entry.type == FeatureEntry::ENABLE_DISABLE_VALUE || | |
157 entry.type == FeatureEntry::FEATURE_VALUE); | |
158 base::ListValue* result = new base::ListValue; | |
159 for (int i = 0; i < entry.num_choices; ++i) { | |
160 base::DictionaryValue* value = new base::DictionaryValue; | |
161 const std::string name = entry.NameForChoice(i); | |
162 value->SetString("internal_name", name); | |
163 value->SetString("description", entry.DescriptionForChoice(i)); | |
164 value->SetBoolean("selected", enabled_entries.count(name) > 0); | |
165 result->Append(value); | |
166 } | |
167 return result; | |
168 } | |
169 | |
170 } // namespace | |
171 | |
172 // Keeps track of affected switches for each FeatureEntry, based on which | |
173 // choice is selected for it. | |
174 struct SwitchEntry { | |
175 // Corresponding base::Feature to toggle. | |
176 std::string feature_name; | |
177 | |
178 // If |feature_name| is not empty, the state (enable/disabled) to set. | |
179 bool feature_state; | |
180 | |
181 // The name of the switch to add. | |
182 std::string switch_name; | |
183 | |
184 // If |switch_name| is not empty, the value of the switch to set. | |
185 std::string switch_value; | |
186 | |
187 SwitchEntry() : feature_state(false) {} | |
188 }; | |
189 | |
190 FlagsState::FlagsState(const FeatureEntry* feature_entries, | |
191 size_t num_feature_entries) | |
192 : feature_entries_(feature_entries), | |
193 num_feature_entries_(num_feature_entries), | |
194 needs_restart_(false) {} | |
195 | |
196 FlagsState::~FlagsState() {} | |
197 | |
198 void FlagsState::ConvertFlagsToSwitches( | |
199 FlagsStorage* flags_storage, | |
200 base::CommandLine* command_line, | |
201 SentinelsMode sentinels, | |
202 const char* enable_features_flag_name, | |
203 const char* disable_features_flag_name) { | |
204 std::set<std::string> enabled_entries; | |
205 | |
206 GetSanitizedEnabledFlagsForCurrentPlatform(flags_storage, &enabled_entries); | |
207 | |
208 std::map<std::string, SwitchEntry> name_to_switch_map; | |
209 for (size_t i = 0; i < num_feature_entries_; ++i) { | |
210 const FeatureEntry& e = feature_entries_[i]; | |
211 switch (e.type) { | |
212 case FeatureEntry::SINGLE_VALUE: | |
213 case FeatureEntry::SINGLE_DISABLE_VALUE: | |
214 AddSwitchMapping(e.internal_name, e.command_line_switch, | |
215 e.command_line_value, &name_to_switch_map); | |
216 break; | |
217 case FeatureEntry::MULTI_VALUE: | |
218 for (int j = 0; j < e.num_choices; ++j) { | |
219 AddSwitchMapping(e.NameForChoice(j), e.choices[j].command_line_switch, | |
220 e.choices[j].command_line_value, | |
221 &name_to_switch_map); | |
222 } | |
223 break; | |
224 case FeatureEntry::ENABLE_DISABLE_VALUE: | |
225 AddSwitchMapping(e.NameForChoice(0), std::string(), std::string(), | |
226 &name_to_switch_map); | |
227 AddSwitchMapping(e.NameForChoice(1), e.command_line_switch, | |
228 e.command_line_value, &name_to_switch_map); | |
229 AddSwitchMapping(e.NameForChoice(2), e.disable_command_line_switch, | |
230 e.disable_command_line_value, &name_to_switch_map); | |
231 break; | |
232 case FeatureEntry::FEATURE_VALUE: | |
233 AddFeatureMapping(e.NameForChoice(0), std::string(), false, | |
234 &name_to_switch_map); | |
235 AddFeatureMapping(e.NameForChoice(1), e.feature->name, true, | |
236 &name_to_switch_map); | |
237 AddFeatureMapping(e.NameForChoice(2), e.feature->name, false, | |
238 &name_to_switch_map); | |
239 break; | |
240 } | |
241 } | |
242 | |
243 AddSwitchesToCommandLine(enabled_entries, name_to_switch_map, sentinels, | |
244 command_line, enable_features_flag_name, | |
245 disable_features_flag_name); | |
246 } | |
247 | |
248 bool FlagsState::IsRestartNeededToCommitChanges() { | |
249 return needs_restart_; | |
250 } | |
251 | |
252 void FlagsState::SetFeatureEntryEnabled(FlagsStorage* flags_storage, | |
253 const std::string& internal_name, | |
254 bool enable) { | |
255 size_t at_index = internal_name.find(testing::kMultiSeparator); | |
256 if (at_index != std::string::npos) { | |
257 DCHECK(enable); | |
258 // We're being asked to enable a multi-choice entry. Disable the | |
259 // currently selected choice. | |
260 DCHECK_NE(at_index, 0u); | |
261 const std::string entry_name = internal_name.substr(0, at_index); | |
262 SetFeatureEntryEnabled(flags_storage, entry_name, false); | |
263 | |
264 // And enable the new choice, if it is not the default first choice. | |
265 if (internal_name != entry_name + "@0") { | |
266 std::set<std::string> enabled_entries; | |
267 GetSanitizedEnabledFlags(flags_storage, &enabled_entries); | |
268 needs_restart_ |= enabled_entries.insert(internal_name).second; | |
269 flags_storage->SetFlags(enabled_entries); | |
270 } | |
271 return; | |
272 } | |
273 | |
274 std::set<std::string> enabled_entries; | |
275 GetSanitizedEnabledFlags(flags_storage, &enabled_entries); | |
276 | |
277 const FeatureEntry* e = nullptr; | |
278 for (size_t i = 0; i < num_feature_entries_; ++i) { | |
279 if (feature_entries_[i].internal_name == internal_name) { | |
280 e = feature_entries_ + i; | |
281 break; | |
282 } | |
283 } | |
284 DCHECK(e); | |
285 | |
286 if (e->type == FeatureEntry::SINGLE_VALUE) { | |
287 if (enable) | |
288 needs_restart_ |= enabled_entries.insert(internal_name).second; | |
289 else | |
290 needs_restart_ |= (enabled_entries.erase(internal_name) > 0); | |
291 } else if (e->type == FeatureEntry::SINGLE_DISABLE_VALUE) { | |
292 if (!enable) | |
293 needs_restart_ |= enabled_entries.insert(internal_name).second; | |
294 else | |
295 needs_restart_ |= (enabled_entries.erase(internal_name) > 0); | |
296 } else { | |
297 if (enable) { | |
298 // Enable the first choice. | |
299 needs_restart_ |= enabled_entries.insert(e->NameForChoice(0)).second; | |
300 } else { | |
301 // Find the currently enabled choice and disable it. | |
302 for (int i = 0; i < e->num_choices; ++i) { | |
303 std::string choice_name = e->NameForChoice(i); | |
304 if (enabled_entries.find(choice_name) != enabled_entries.end()) { | |
305 needs_restart_ = true; | |
306 enabled_entries.erase(choice_name); | |
307 // Continue on just in case there's a bug and more than one | |
308 // entry for this choice was enabled. | |
309 } | |
310 } | |
311 } | |
312 } | |
313 | |
314 flags_storage->SetFlags(enabled_entries); | |
315 } | |
316 | |
317 void FlagsState::RemoveFlagsSwitches( | |
318 std::map<std::string, base::CommandLine::StringType>* switch_list) { | |
319 for (const auto& entry : flags_switches_) | |
320 switch_list->erase(entry.first); | |
321 | |
322 // If feature entries were added to --enable-features= or --disable-features= | |
323 // lists, remove them here while preserving existing values. | |
324 for (const auto& entry : appended_switches_) { | |
325 const auto& switch_name = entry.first; | |
326 const auto& switch_added_values = entry.second; | |
327 | |
328 // The below is either a std::string or a base::string16 based on platform. | |
329 const auto& existing_value = (*switch_list)[switch_name]; | |
330 #if defined(OS_WIN) | |
331 const std::string existing_value_utf8 = base::UTF16ToUTF8(existing_value); | |
332 #else | |
333 const std::string& existing_value_utf8 = existing_value; | |
334 #endif | |
335 | |
336 std::vector<std::string> features = | |
337 base::FeatureList::SplitFeatureListString(existing_value_utf8); | |
338 std::vector<std::string> remaining_features; | |
339 // For any featrue name in |features| that is not in |switch_added_values| - | |
340 // i.e. it wasn't added by about_flags code, add it to |remaining_features|. | |
341 for (const std::string& feature : features) { | |
342 if (!ContainsKey(switch_added_values, feature)) | |
343 remaining_features.push_back(feature); | |
344 } | |
345 | |
346 // Either remove the flag entirely if |remaining_features| is empty, or set | |
347 // the new list. | |
348 if (remaining_features.empty()) { | |
349 switch_list->erase(switch_name); | |
350 } else { | |
351 std::string switch_value = base::JoinString(remaining_features, ","); | |
352 #if defined(OS_WIN) | |
353 (*switch_list)[switch_name] = base::UTF8ToUTF16(switch_value); | |
354 #else | |
355 (*switch_list)[switch_name] = switch_value; | |
356 #endif | |
357 } | |
358 } | |
359 } | |
360 | |
361 void FlagsState::ResetAllFlags(FlagsStorage* flags_storage) { | |
362 needs_restart_ = true; | |
363 | |
364 std::set<std::string> no_entries; | |
365 flags_storage->SetFlags(no_entries); | |
366 } | |
367 | |
368 void FlagsState::Reset() { | |
369 needs_restart_ = false; | |
370 flags_switches_.clear(); | |
371 appended_switches_.clear(); | |
372 } | |
373 | |
374 void FlagsState::GetFlagFeatureEntries( | |
375 FlagsStorage* flags_storage, | |
376 FlagAccess access, | |
377 base::ListValue* supported_entries, | |
378 base::ListValue* unsupported_entries, | |
379 base::Callback<bool(const FeatureEntry&)> skip_feature_entry) { | |
380 std::set<std::string> enabled_entries; | |
381 GetSanitizedEnabledFlags(flags_storage, &enabled_entries); | |
382 | |
383 int current_platform = GetCurrentPlatform(); | |
384 | |
385 for (size_t i = 0; i < num_feature_entries_; ++i) { | |
386 const FeatureEntry& entry = feature_entries_[i]; | |
387 if (skip_feature_entry.Run(entry)) | |
388 continue; | |
389 | |
390 base::DictionaryValue* data = new base::DictionaryValue(); | |
391 data->SetString("internal_name", entry.internal_name); | |
392 data->SetString("name", l10n_util::GetStringUTF16(entry.visible_name_id)); | |
393 data->SetString("description", | |
394 l10n_util::GetStringUTF16(entry.visible_description_id)); | |
395 | |
396 base::ListValue* supported_platforms = new base::ListValue(); | |
397 AddOsStrings(entry.supported_platforms, supported_platforms); | |
398 data->Set("supported_platforms", supported_platforms); | |
399 // True if the switch is not currently passed. | |
400 bool is_default_value = IsDefaultValue(entry, enabled_entries); | |
401 data->SetBoolean("is_default", is_default_value); | |
402 | |
403 switch (entry.type) { | |
404 case FeatureEntry::SINGLE_VALUE: | |
405 case FeatureEntry::SINGLE_DISABLE_VALUE: | |
406 data->SetBoolean( | |
407 "enabled", | |
408 (!is_default_value && entry.type == FeatureEntry::SINGLE_VALUE) || | |
409 (is_default_value && | |
410 entry.type == FeatureEntry::SINGLE_DISABLE_VALUE)); | |
411 break; | |
412 case FeatureEntry::MULTI_VALUE: | |
413 case FeatureEntry::ENABLE_DISABLE_VALUE: | |
414 case FeatureEntry::FEATURE_VALUE: | |
415 data->Set("choices", CreateChoiceData(entry, enabled_entries)); | |
416 break; | |
417 default: | |
Alexei Svitkine (slow)
2015/11/16 21:52:54
Remove default.
sdefresne
2015/11/17 16:28:56
Done.
| |
418 NOTREACHED(); | |
419 } | |
420 | |
421 bool supported = (entry.supported_platforms & current_platform) != 0; | |
422 #if defined(OS_CHROMEOS) | |
423 if (access == kOwnerAccessToFlags && | |
424 (entry.supported_platforms & kOsCrOSOwnerOnly) != 0) { | |
425 supported = true; | |
426 } | |
427 #endif | |
428 if (supported) | |
429 supported_entries->Append(data); | |
430 else | |
431 unsupported_entries->Append(data); | |
432 } | |
433 } | |
434 | |
435 // static | |
436 int FlagsState::GetCurrentPlatform() { | |
437 #if defined(OS_MACOSX) | |
438 return kOsMac; | |
439 #elif defined(OS_WIN) | |
440 return kOsWin; | |
441 #elif defined(OS_CHROMEOS) // Needs to be before the OS_LINUX check. | |
442 return kOsCrOS; | |
443 #elif defined(OS_LINUX) || defined(OS_OPENBSD) | |
444 return kOsLinux; | |
445 #elif defined(OS_ANDROID) | |
446 return kOsAndroid; | |
447 #else | |
448 #error Unknown platform | |
449 #endif | |
450 } | |
451 | |
452 // static | |
453 bool FlagsState::AreSwitchesIdenticalToCurrentCommandLine( | |
454 const base::CommandLine& new_cmdline, | |
455 const base::CommandLine& active_cmdline, | |
456 std::set<base::CommandLine::StringType>* out_difference, | |
457 const char* extra_flag_sentinel_begin_flag_name, | |
458 const char* extra_flag_sentinel_end_flag_name) { | |
459 std::set<base::CommandLine::StringType> new_flags = | |
460 ExtractFlagsFromCommandLine(new_cmdline, | |
461 extra_flag_sentinel_begin_flag_name, | |
462 extra_flag_sentinel_end_flag_name); | |
463 std::set<base::CommandLine::StringType> active_flags = | |
464 ExtractFlagsFromCommandLine(active_cmdline, | |
465 extra_flag_sentinel_begin_flag_name, | |
466 extra_flag_sentinel_end_flag_name); | |
467 | |
468 bool result = false; | |
469 // Needed because std::equal doesn't check if the 2nd set is empty. | |
470 if (new_flags.size() == active_flags.size()) { | |
471 result = | |
472 std::equal(new_flags.begin(), new_flags.end(), active_flags.begin()); | |
473 } | |
474 | |
475 if (out_difference && !result) { | |
476 std::set_symmetric_difference( | |
477 new_flags.begin(), new_flags.end(), active_flags.begin(), | |
478 active_flags.end(), | |
479 std::inserter(*out_difference, out_difference->begin())); | |
480 } | |
481 | |
482 return result; | |
483 } | |
484 | |
485 void FlagsState::AddSwitchMapping( | |
486 const std::string& key, | |
487 const std::string& switch_name, | |
488 const std::string& switch_value, | |
489 std::map<std::string, SwitchEntry>* name_to_switch_map) { | |
490 DCHECK(!ContainsKey(*name_to_switch_map, key)); | |
491 | |
492 SwitchEntry* entry = &(*name_to_switch_map)[key]; | |
493 entry->switch_name = switch_name; | |
494 entry->switch_value = switch_value; | |
495 } | |
496 | |
497 void FlagsState::AddFeatureMapping( | |
498 const std::string& key, | |
499 const std::string& feature_name, | |
500 bool feature_state, | |
501 std::map<std::string, SwitchEntry>* name_to_switch_map) { | |
502 DCHECK(!ContainsKey(*name_to_switch_map, key)); | |
503 | |
504 SwitchEntry* entry = &(*name_to_switch_map)[key]; | |
505 entry->feature_name = feature_name; | |
506 entry->feature_state = feature_state; | |
507 } | |
508 | |
509 void FlagsState::AddSwitchesToCommandLine( | |
510 const std::set<std::string>& enabled_entries, | |
511 const std::map<std::string, SwitchEntry>& name_to_switch_map, | |
512 SentinelsMode sentinels, | |
513 base::CommandLine* command_line, | |
514 const char* enable_features_flag_name, | |
515 const char* disable_features_flag_name) { | |
516 std::map<std::string, bool> feature_switches; | |
517 if (sentinels == kAddSentinels) { | |
518 command_line->AppendSwitch(switches::kFlagSwitchesBegin); | |
519 flags_switches_[switches::kFlagSwitchesBegin] = std::string(); | |
520 } | |
521 | |
522 for (const std::string& entry_name : enabled_entries) { | |
523 const auto& entry_it = name_to_switch_map.find(entry_name); | |
524 if (entry_it == name_to_switch_map.end()) { | |
525 NOTREACHED(); | |
526 continue; | |
527 } | |
528 | |
529 const SwitchEntry& entry = entry_it->second; | |
530 if (!entry.feature_name.empty()) { | |
531 feature_switches[entry.feature_name] = entry.feature_state; | |
532 } else if (!entry.switch_name.empty()) { | |
533 command_line->AppendSwitchASCII(entry.switch_name, entry.switch_value); | |
534 flags_switches_[entry.switch_name] = entry.switch_value; | |
535 } | |
536 // If an entry doesn't match either of the above, then it is likely the | |
537 // default entry for a FEATURE_VALUE entry. Safe to ignore. | |
538 } | |
539 | |
540 if (!feature_switches.empty()) { | |
541 MergeFeatureCommandLineSwitch(feature_switches, enable_features_flag_name, | |
542 true, command_line); | |
543 MergeFeatureCommandLineSwitch(feature_switches, disable_features_flag_name, | |
544 false, command_line); | |
545 } | |
546 | |
547 if (sentinels == kAddSentinels) { | |
548 command_line->AppendSwitch(switches::kFlagSwitchesEnd); | |
549 flags_switches_[switches::kFlagSwitchesEnd] = std::string(); | |
550 } | |
551 } | |
552 | |
553 void FlagsState::MergeFeatureCommandLineSwitch( | |
554 const std::map<std::string, bool>& feature_switches, | |
555 const char* switch_name, | |
556 bool feature_state, | |
557 base::CommandLine* command_line) { | |
558 std::string original_switch_value = | |
559 command_line->GetSwitchValueASCII(switch_name); | |
560 std::vector<std::string> features = | |
561 base::FeatureList::SplitFeatureListString(original_switch_value); | |
562 // Only add features that don't already exist in the lists. | |
563 // Note: The ContainsValue() call results in O(n^2) performance, but in | |
564 // practice n should be very small. | |
565 for (const auto& entry : feature_switches) { | |
566 if (entry.second == feature_state && | |
567 !ContainsValue(features, entry.first)) { | |
568 features.push_back(entry.first); | |
569 appended_switches_[switch_name].insert(entry.first); | |
570 } | |
571 } | |
572 // Update the switch value only if it didn't change. This avoids setting an | |
573 // empty list or duplicating the same list (since AppendSwitch() adds the | |
574 // switch to the end but doesn't remove previous ones). | |
575 std::string switch_value = base::JoinString(features, ","); | |
576 if (switch_value != original_switch_value) | |
577 command_line->AppendSwitchASCII(switch_name, switch_value); | |
578 } | |
579 | |
580 void FlagsState::SanitizeList(FlagsStorage* flags_storage) { | |
581 std::set<std::string> known_entries; | |
582 for (size_t i = 0; i < num_feature_entries_; ++i) { | |
583 DCHECK(ValidateFeatureEntry(feature_entries_[i])); | |
584 AddInternalName(feature_entries_[i], &known_entries); | |
585 } | |
586 | |
587 std::set<std::string> enabled_entries = flags_storage->GetFlags(); | |
588 | |
589 std::set<std::string> new_enabled_entries = | |
590 base::STLSetIntersection<std::set<std::string>>(known_entries, | |
591 enabled_entries); | |
592 | |
593 if (new_enabled_entries != enabled_entries) | |
594 flags_storage->SetFlags(new_enabled_entries); | |
595 } | |
596 | |
597 void FlagsState::GetSanitizedEnabledFlags(FlagsStorage* flags_storage, | |
598 std::set<std::string>* result) { | |
599 SanitizeList(flags_storage); | |
600 *result = flags_storage->GetFlags(); | |
601 } | |
602 | |
603 void FlagsState::GetSanitizedEnabledFlagsForCurrentPlatform( | |
604 FlagsStorage* flags_storage, | |
605 std::set<std::string>* result) { | |
606 GetSanitizedEnabledFlags(flags_storage, result); | |
607 | |
608 // Filter out any entries that aren't enabled on the current platform. We | |
609 // don't remove these from prefs else syncing to a platform with a different | |
610 // set of entries would be lossy. | |
611 std::set<std::string> platform_entries; | |
612 int current_platform = GetCurrentPlatform(); | |
613 for (size_t i = 0; i < num_feature_entries_; ++i) { | |
614 const FeatureEntry& entry = feature_entries_[i]; | |
615 if (entry.supported_platforms & current_platform) | |
616 AddInternalName(entry, &platform_entries); | |
617 #if defined(OS_CHROMEOS) | |
618 if (feature_entries_[i].supported_platforms & kOsCrOSOwnerOnly) | |
619 AddInternalName(entry, &platform_entries); | |
620 #endif | |
621 } | |
622 | |
623 std::set<std::string> new_enabled_entries = | |
624 base::STLSetIntersection<std::set<std::string>>(platform_entries, | |
625 *result); | |
626 | |
627 result->swap(new_enabled_entries); | |
628 } | |
629 | |
630 } // namespace flags_ui | |
OLD | NEW |