Chromium Code Reviews| 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 |