| Index: components/flags_ui/flags_state.cc
|
| diff --git a/components/flags_ui/flags_state.cc b/components/flags_ui/flags_state.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b7345b2b1fa3f86045e962f5bfd45e05f6c3dc49
|
| --- /dev/null
|
| +++ b/components/flags_ui/flags_state.cc
|
| @@ -0,0 +1,632 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "components/flags_ui/flags_state.h"
|
| +
|
| +#include "base/callback.h"
|
| +#include "base/feature_list.h"
|
| +#include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "base/values.h"
|
| +#include "build/build_config.h"
|
| +#include "components/flags_ui/feature_entry.h"
|
| +#include "components/flags_ui/flags_storage.h"
|
| +#include "components/flags_ui/flags_ui_switches.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +
|
| +namespace flags_ui {
|
| +
|
| +namespace {
|
| +
|
| +// Convert switch constants to proper CommandLine::StringType strings.
|
| +base::CommandLine::StringType GetSwitchString(const std::string& flag) {
|
| + base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
|
| + cmd_line.AppendSwitch(flag);
|
| + DCHECK_EQ(2U, cmd_line.argv().size());
|
| + return cmd_line.argv()[1];
|
| +}
|
| +
|
| +// Scoops flags from a command line.
|
| +// Only switches between --flag-switches-begin and --flag-switches-end are
|
| +// compared. The embedder may use |extra_flag_sentinel_begin_flag_name| and
|
| +// |extra_sentinel_end_flag_name| to specify other delimiters, if supported.
|
| +std::set<base::CommandLine::StringType> ExtractFlagsFromCommandLine(
|
| + const base::CommandLine& cmdline,
|
| + const char* extra_flag_sentinel_begin_flag_name,
|
| + const char* extra_flag_sentinel_end_flag_name) {
|
| + DCHECK_EQ(!!extra_flag_sentinel_begin_flag_name,
|
| + !!extra_flag_sentinel_end_flag_name);
|
| + std::set<base::CommandLine::StringType> flags;
|
| + // First do the ones between --flag-switches-begin and --flag-switches-end.
|
| + base::CommandLine::StringVector::const_iterator first =
|
| + std::find(cmdline.argv().begin(), cmdline.argv().end(),
|
| + GetSwitchString(switches::kFlagSwitchesBegin));
|
| + base::CommandLine::StringVector::const_iterator last =
|
| + std::find(cmdline.argv().begin(), cmdline.argv().end(),
|
| + GetSwitchString(switches::kFlagSwitchesEnd));
|
| + if (first != cmdline.argv().end() && last != cmdline.argv().end())
|
| + flags.insert(first + 1, last);
|
| +
|
| + // Then add those between the extra sentinels.
|
| + if (extra_flag_sentinel_begin_flag_name &&
|
| + extra_flag_sentinel_end_flag_name) {
|
| + first = std::find(cmdline.argv().begin(), cmdline.argv().end(),
|
| + GetSwitchString(extra_flag_sentinel_begin_flag_name));
|
| + last = std::find(cmdline.argv().begin(), cmdline.argv().end(),
|
| + GetSwitchString(extra_flag_sentinel_end_flag_name));
|
| + if (first != cmdline.argv().end() && last != cmdline.argv().end())
|
| + flags.insert(first + 1, last);
|
| + }
|
| + return flags;
|
| +}
|
| +
|
| +const struct {
|
| + unsigned bit;
|
| + const char* const name;
|
| +} kBitsToOs[] = {
|
| + {kOsMac, "Mac"},
|
| + {kOsWin, "Windows"},
|
| + {kOsLinux, "Linux"},
|
| + {kOsCrOS, "Chrome OS"},
|
| + {kOsAndroid, "Android"},
|
| + {kOsCrOSOwnerOnly, "Chrome OS (owner only)"},
|
| +};
|
| +
|
| +// Adds a |StringValue| to |list| for each platform where |bitmask| indicates
|
| +// whether the entry is available on that platform.
|
| +void AddOsStrings(unsigned bitmask, base::ListValue* list) {
|
| + for (size_t i = 0; i < arraysize(kBitsToOs); ++i) {
|
| + if (bitmask & kBitsToOs[i].bit)
|
| + list->Append(new base::StringValue(kBitsToOs[i].name));
|
| + }
|
| +}
|
| +
|
| +// Adds the internal names for the specified entry to |names|.
|
| +void AddInternalName(const FeatureEntry& e, std::set<std::string>* names) {
|
| + switch (e.type) {
|
| + case FeatureEntry::SINGLE_VALUE:
|
| + case FeatureEntry::SINGLE_DISABLE_VALUE:
|
| + names->insert(e.internal_name);
|
| + break;
|
| + case FeatureEntry::MULTI_VALUE:
|
| + case FeatureEntry::ENABLE_DISABLE_VALUE:
|
| + case FeatureEntry::FEATURE_VALUE:
|
| + for (int i = 0; i < e.num_choices; ++i)
|
| + names->insert(e.NameForChoice(i));
|
| + break;
|
| + }
|
| +}
|
| +
|
| +// Confirms that an entry is valid, used in a DCHECK in
|
| +// SanitizeList below.
|
| +bool ValidateFeatureEntry(const FeatureEntry& e) {
|
| + switch (e.type) {
|
| + case FeatureEntry::SINGLE_VALUE:
|
| + case FeatureEntry::SINGLE_DISABLE_VALUE:
|
| + DCHECK_EQ(0, e.num_choices);
|
| + DCHECK(!e.choices);
|
| + return true;
|
| + case FeatureEntry::MULTI_VALUE:
|
| + DCHECK_GT(e.num_choices, 0);
|
| + DCHECK(e.choices);
|
| + DCHECK(e.choices[0].command_line_switch);
|
| + DCHECK_EQ('\0', e.choices[0].command_line_switch[0]);
|
| + return true;
|
| + case FeatureEntry::ENABLE_DISABLE_VALUE:
|
| + DCHECK_EQ(3, e.num_choices);
|
| + DCHECK(!e.choices);
|
| + DCHECK(e.command_line_switch);
|
| + DCHECK(e.command_line_value);
|
| + DCHECK(e.disable_command_line_switch);
|
| + DCHECK(e.disable_command_line_value);
|
| + return true;
|
| + case FeatureEntry::FEATURE_VALUE:
|
| + DCHECK_EQ(3, e.num_choices);
|
| + DCHECK(!e.choices);
|
| + DCHECK(e.feature);
|
| + return true;
|
| + }
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| +// Returns true if none of this entry's options have been enabled.
|
| +bool IsDefaultValue(const FeatureEntry& entry,
|
| + const std::set<std::string>& enabled_entries) {
|
| + switch (entry.type) {
|
| + case FeatureEntry::SINGLE_VALUE:
|
| + case FeatureEntry::SINGLE_DISABLE_VALUE:
|
| + return enabled_entries.count(entry.internal_name) == 0;
|
| + case FeatureEntry::MULTI_VALUE:
|
| + case FeatureEntry::ENABLE_DISABLE_VALUE:
|
| + case FeatureEntry::FEATURE_VALUE:
|
| + for (int i = 0; i < entry.num_choices; ++i) {
|
| + if (enabled_entries.count(entry.NameForChoice(i)) > 0)
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| + NOTREACHED();
|
| + return true;
|
| +}
|
| +
|
| +// Returns the Value representing the choice data in the specified entry.
|
| +base::Value* CreateChoiceData(const FeatureEntry& entry,
|
| + const std::set<std::string>& enabled_entries) {
|
| + DCHECK(entry.type == FeatureEntry::MULTI_VALUE ||
|
| + entry.type == FeatureEntry::ENABLE_DISABLE_VALUE ||
|
| + entry.type == FeatureEntry::FEATURE_VALUE);
|
| + base::ListValue* result = new base::ListValue;
|
| + for (int i = 0; i < entry.num_choices; ++i) {
|
| + base::DictionaryValue* value = new base::DictionaryValue;
|
| + const std::string name = entry.NameForChoice(i);
|
| + value->SetString("internal_name", name);
|
| + value->SetString("description", entry.DescriptionForChoice(i));
|
| + value->SetBoolean("selected", enabled_entries.count(name) > 0);
|
| + result->Append(value);
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// Keeps track of affected switches for each FeatureEntry, based on which
|
| +// choice is selected for it.
|
| +struct SwitchEntry {
|
| + // Corresponding base::Feature to toggle.
|
| + std::string feature_name;
|
| +
|
| + // If |feature_name| is not empty, the state (enable/disabled) to set.
|
| + bool feature_state;
|
| +
|
| + // The name of the switch to add.
|
| + std::string switch_name;
|
| +
|
| + // If |switch_name| is not empty, the value of the switch to set.
|
| + std::string switch_value;
|
| +
|
| + SwitchEntry() : feature_state(false) {}
|
| +};
|
| +
|
| +FlagsState::FlagsState(const FeatureEntry* feature_entries,
|
| + size_t num_feature_entries)
|
| + : feature_entries_(feature_entries),
|
| + num_feature_entries_(num_feature_entries),
|
| + needs_restart_(false) {}
|
| +
|
| +FlagsState::~FlagsState() {}
|
| +
|
| +void FlagsState::ConvertFlagsToSwitches(
|
| + FlagsStorage* flags_storage,
|
| + base::CommandLine* command_line,
|
| + SentinelsMode sentinels,
|
| + const char* enable_features_flag_name,
|
| + const char* disable_features_flag_name) {
|
| + std::set<std::string> enabled_entries;
|
| +
|
| + GetSanitizedEnabledFlagsForCurrentPlatform(flags_storage, &enabled_entries);
|
| +
|
| + std::map<std::string, SwitchEntry> name_to_switch_map;
|
| + for (size_t i = 0; i < num_feature_entries_; ++i) {
|
| + const FeatureEntry& e = feature_entries_[i];
|
| + switch (e.type) {
|
| + case FeatureEntry::SINGLE_VALUE:
|
| + case FeatureEntry::SINGLE_DISABLE_VALUE:
|
| + AddSwitchMapping(e.internal_name, e.command_line_switch,
|
| + e.command_line_value, &name_to_switch_map);
|
| + break;
|
| + case FeatureEntry::MULTI_VALUE:
|
| + for (int j = 0; j < e.num_choices; ++j) {
|
| + AddSwitchMapping(e.NameForChoice(j), e.choices[j].command_line_switch,
|
| + e.choices[j].command_line_value,
|
| + &name_to_switch_map);
|
| + }
|
| + break;
|
| + case FeatureEntry::ENABLE_DISABLE_VALUE:
|
| + AddSwitchMapping(e.NameForChoice(0), std::string(), std::string(),
|
| + &name_to_switch_map);
|
| + AddSwitchMapping(e.NameForChoice(1), e.command_line_switch,
|
| + e.command_line_value, &name_to_switch_map);
|
| + AddSwitchMapping(e.NameForChoice(2), e.disable_command_line_switch,
|
| + e.disable_command_line_value, &name_to_switch_map);
|
| + break;
|
| + case FeatureEntry::FEATURE_VALUE:
|
| + AddFeatureMapping(e.NameForChoice(0), std::string(), false,
|
| + &name_to_switch_map);
|
| + AddFeatureMapping(e.NameForChoice(1), e.feature->name, true,
|
| + &name_to_switch_map);
|
| + AddFeatureMapping(e.NameForChoice(2), e.feature->name, false,
|
| + &name_to_switch_map);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + AddSwitchesToCommandLine(enabled_entries, name_to_switch_map, sentinels,
|
| + command_line, enable_features_flag_name,
|
| + disable_features_flag_name);
|
| +}
|
| +
|
| +bool FlagsState::IsRestartNeededToCommitChanges() {
|
| + return needs_restart_;
|
| +}
|
| +
|
| +void FlagsState::SetFeatureEntryEnabled(FlagsStorage* flags_storage,
|
| + const std::string& internal_name,
|
| + bool enable) {
|
| + size_t at_index = internal_name.find(testing::kMultiSeparator);
|
| + if (at_index != std::string::npos) {
|
| + DCHECK(enable);
|
| + // We're being asked to enable a multi-choice entry. Disable the
|
| + // currently selected choice.
|
| + DCHECK_NE(at_index, 0u);
|
| + const std::string entry_name = internal_name.substr(0, at_index);
|
| + SetFeatureEntryEnabled(flags_storage, entry_name, false);
|
| +
|
| + // And enable the new choice, if it is not the default first choice.
|
| + if (internal_name != entry_name + "@0") {
|
| + std::set<std::string> enabled_entries;
|
| + GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
|
| + needs_restart_ |= enabled_entries.insert(internal_name).second;
|
| + flags_storage->SetFlags(enabled_entries);
|
| + }
|
| + return;
|
| + }
|
| +
|
| + std::set<std::string> enabled_entries;
|
| + GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
|
| +
|
| + const FeatureEntry* e = nullptr;
|
| + for (size_t i = 0; i < num_feature_entries_; ++i) {
|
| + if (feature_entries_[i].internal_name == internal_name) {
|
| + e = feature_entries_ + i;
|
| + break;
|
| + }
|
| + }
|
| + DCHECK(e);
|
| +
|
| + if (e->type == FeatureEntry::SINGLE_VALUE) {
|
| + if (enable)
|
| + needs_restart_ |= enabled_entries.insert(internal_name).second;
|
| + else
|
| + needs_restart_ |= (enabled_entries.erase(internal_name) > 0);
|
| + } else if (e->type == FeatureEntry::SINGLE_DISABLE_VALUE) {
|
| + if (!enable)
|
| + needs_restart_ |= enabled_entries.insert(internal_name).second;
|
| + else
|
| + needs_restart_ |= (enabled_entries.erase(internal_name) > 0);
|
| + } else {
|
| + if (enable) {
|
| + // Enable the first choice.
|
| + needs_restart_ |= enabled_entries.insert(e->NameForChoice(0)).second;
|
| + } else {
|
| + // Find the currently enabled choice and disable it.
|
| + for (int i = 0; i < e->num_choices; ++i) {
|
| + std::string choice_name = e->NameForChoice(i);
|
| + if (enabled_entries.find(choice_name) != enabled_entries.end()) {
|
| + needs_restart_ = true;
|
| + enabled_entries.erase(choice_name);
|
| + // Continue on just in case there's a bug and more than one
|
| + // entry for this choice was enabled.
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + flags_storage->SetFlags(enabled_entries);
|
| +}
|
| +
|
| +void FlagsState::RemoveFlagsSwitches(
|
| + std::map<std::string, base::CommandLine::StringType>* switch_list) {
|
| + for (const auto& entry : flags_switches_)
|
| + switch_list->erase(entry.first);
|
| +
|
| + // If feature entries were added to --enable-features= or --disable-features=
|
| + // lists, remove them here while preserving existing values.
|
| + for (const auto& entry : appended_switches_) {
|
| + const auto& switch_name = entry.first;
|
| + const auto& switch_added_values = entry.second;
|
| +
|
| + // The below is either a std::string or a base::string16 based on platform.
|
| + const auto& existing_value = (*switch_list)[switch_name];
|
| +#if defined(OS_WIN)
|
| + const std::string existing_value_utf8 = base::UTF16ToUTF8(existing_value);
|
| +#else
|
| + const std::string& existing_value_utf8 = existing_value;
|
| +#endif
|
| +
|
| + std::vector<std::string> features =
|
| + base::FeatureList::SplitFeatureListString(existing_value_utf8);
|
| + std::vector<std::string> remaining_features;
|
| + // For any featrue name in |features| that is not in |switch_added_values| -
|
| + // i.e. it wasn't added by about_flags code, add it to |remaining_features|.
|
| + for (const std::string& feature : features) {
|
| + if (!ContainsKey(switch_added_values, feature))
|
| + remaining_features.push_back(feature);
|
| + }
|
| +
|
| + // Either remove the flag entirely if |remaining_features| is empty, or set
|
| + // the new list.
|
| + if (remaining_features.empty()) {
|
| + switch_list->erase(switch_name);
|
| + } else {
|
| + std::string switch_value = base::JoinString(remaining_features, ",");
|
| +#if defined(OS_WIN)
|
| + (*switch_list)[switch_name] = base::UTF8ToUTF16(switch_value);
|
| +#else
|
| + (*switch_list)[switch_name] = switch_value;
|
| +#endif
|
| + }
|
| + }
|
| +}
|
| +
|
| +void FlagsState::ResetAllFlags(FlagsStorage* flags_storage) {
|
| + needs_restart_ = true;
|
| +
|
| + std::set<std::string> no_entries;
|
| + flags_storage->SetFlags(no_entries);
|
| +}
|
| +
|
| +void FlagsState::Reset() {
|
| + needs_restart_ = false;
|
| + flags_switches_.clear();
|
| + appended_switches_.clear();
|
| +}
|
| +
|
| +void FlagsState::GetFlagFeatureEntries(
|
| + FlagsStorage* flags_storage,
|
| + FlagAccess access,
|
| + base::ListValue* supported_entries,
|
| + base::ListValue* unsupported_entries,
|
| + base::Callback<bool(const FeatureEntry&)> skip_feature_entry) {
|
| + std::set<std::string> enabled_entries;
|
| + GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
|
| +
|
| + int current_platform = GetCurrentPlatform();
|
| +
|
| + for (size_t i = 0; i < num_feature_entries_; ++i) {
|
| + const FeatureEntry& entry = feature_entries_[i];
|
| + if (skip_feature_entry.Run(entry))
|
| + continue;
|
| +
|
| + base::DictionaryValue* data = new base::DictionaryValue();
|
| + data->SetString("internal_name", entry.internal_name);
|
| + data->SetString("name", l10n_util::GetStringUTF16(entry.visible_name_id));
|
| + data->SetString("description",
|
| + l10n_util::GetStringUTF16(entry.visible_description_id));
|
| +
|
| + base::ListValue* supported_platforms = new base::ListValue();
|
| + AddOsStrings(entry.supported_platforms, supported_platforms);
|
| + data->Set("supported_platforms", supported_platforms);
|
| + // True if the switch is not currently passed.
|
| + bool is_default_value = IsDefaultValue(entry, enabled_entries);
|
| + data->SetBoolean("is_default", is_default_value);
|
| +
|
| + switch (entry.type) {
|
| + case FeatureEntry::SINGLE_VALUE:
|
| + case FeatureEntry::SINGLE_DISABLE_VALUE:
|
| + data->SetBoolean(
|
| + "enabled",
|
| + (!is_default_value && entry.type == FeatureEntry::SINGLE_VALUE) ||
|
| + (is_default_value &&
|
| + entry.type == FeatureEntry::SINGLE_DISABLE_VALUE));
|
| + break;
|
| + case FeatureEntry::MULTI_VALUE:
|
| + case FeatureEntry::ENABLE_DISABLE_VALUE:
|
| + case FeatureEntry::FEATURE_VALUE:
|
| + data->Set("choices", CreateChoiceData(entry, enabled_entries));
|
| + break;
|
| + }
|
| +
|
| + bool supported = (entry.supported_platforms & current_platform) != 0;
|
| +#if defined(OS_CHROMEOS)
|
| + if (access == kOwnerAccessToFlags &&
|
| + (entry.supported_platforms & kOsCrOSOwnerOnly) != 0) {
|
| + supported = true;
|
| + }
|
| +#endif
|
| + if (supported)
|
| + supported_entries->Append(data);
|
| + else
|
| + unsupported_entries->Append(data);
|
| + }
|
| +}
|
| +
|
| +// static
|
| +int FlagsState::GetCurrentPlatform() {
|
| +#if defined(OS_MACOSX)
|
| + return kOsMac;
|
| +#elif defined(OS_WIN)
|
| + return kOsWin;
|
| +#elif defined(OS_CHROMEOS) // Needs to be before the OS_LINUX check.
|
| + return kOsCrOS;
|
| +#elif defined(OS_LINUX) || defined(OS_OPENBSD)
|
| + return kOsLinux;
|
| +#elif defined(OS_ANDROID)
|
| + return kOsAndroid;
|
| +#else
|
| +#error Unknown platform
|
| +#endif
|
| +}
|
| +
|
| +// static
|
| +bool FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
|
| + const base::CommandLine& new_cmdline,
|
| + const base::CommandLine& active_cmdline,
|
| + std::set<base::CommandLine::StringType>* out_difference,
|
| + const char* extra_flag_sentinel_begin_flag_name,
|
| + const char* extra_flag_sentinel_end_flag_name) {
|
| + std::set<base::CommandLine::StringType> new_flags =
|
| + ExtractFlagsFromCommandLine(new_cmdline,
|
| + extra_flag_sentinel_begin_flag_name,
|
| + extra_flag_sentinel_end_flag_name);
|
| + std::set<base::CommandLine::StringType> active_flags =
|
| + ExtractFlagsFromCommandLine(active_cmdline,
|
| + extra_flag_sentinel_begin_flag_name,
|
| + extra_flag_sentinel_end_flag_name);
|
| +
|
| + bool result = false;
|
| + // Needed because std::equal doesn't check if the 2nd set is empty.
|
| + if (new_flags.size() == active_flags.size()) {
|
| + result =
|
| + std::equal(new_flags.begin(), new_flags.end(), active_flags.begin());
|
| + }
|
| +
|
| + if (out_difference && !result) {
|
| + std::set_symmetric_difference(
|
| + new_flags.begin(), new_flags.end(), active_flags.begin(),
|
| + active_flags.end(),
|
| + std::inserter(*out_difference, out_difference->begin()));
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +void FlagsState::AddSwitchMapping(
|
| + const std::string& key,
|
| + const std::string& switch_name,
|
| + const std::string& switch_value,
|
| + std::map<std::string, SwitchEntry>* name_to_switch_map) {
|
| + DCHECK(!ContainsKey(*name_to_switch_map, key));
|
| +
|
| + SwitchEntry* entry = &(*name_to_switch_map)[key];
|
| + entry->switch_name = switch_name;
|
| + entry->switch_value = switch_value;
|
| +}
|
| +
|
| +void FlagsState::AddFeatureMapping(
|
| + const std::string& key,
|
| + const std::string& feature_name,
|
| + bool feature_state,
|
| + std::map<std::string, SwitchEntry>* name_to_switch_map) {
|
| + DCHECK(!ContainsKey(*name_to_switch_map, key));
|
| +
|
| + SwitchEntry* entry = &(*name_to_switch_map)[key];
|
| + entry->feature_name = feature_name;
|
| + entry->feature_state = feature_state;
|
| +}
|
| +
|
| +void FlagsState::AddSwitchesToCommandLine(
|
| + const std::set<std::string>& enabled_entries,
|
| + const std::map<std::string, SwitchEntry>& name_to_switch_map,
|
| + SentinelsMode sentinels,
|
| + base::CommandLine* command_line,
|
| + const char* enable_features_flag_name,
|
| + const char* disable_features_flag_name) {
|
| + std::map<std::string, bool> feature_switches;
|
| + if (sentinels == kAddSentinels) {
|
| + command_line->AppendSwitch(switches::kFlagSwitchesBegin);
|
| + flags_switches_[switches::kFlagSwitchesBegin] = std::string();
|
| + }
|
| +
|
| + for (const std::string& entry_name : enabled_entries) {
|
| + const auto& entry_it = name_to_switch_map.find(entry_name);
|
| + if (entry_it == name_to_switch_map.end()) {
|
| + NOTREACHED();
|
| + continue;
|
| + }
|
| +
|
| + const SwitchEntry& entry = entry_it->second;
|
| + if (!entry.feature_name.empty()) {
|
| + feature_switches[entry.feature_name] = entry.feature_state;
|
| + } else if (!entry.switch_name.empty()) {
|
| + command_line->AppendSwitchASCII(entry.switch_name, entry.switch_value);
|
| + flags_switches_[entry.switch_name] = entry.switch_value;
|
| + }
|
| + // If an entry doesn't match either of the above, then it is likely the
|
| + // default entry for a FEATURE_VALUE entry. Safe to ignore.
|
| + }
|
| +
|
| + if (!feature_switches.empty()) {
|
| + MergeFeatureCommandLineSwitch(feature_switches, enable_features_flag_name,
|
| + true, command_line);
|
| + MergeFeatureCommandLineSwitch(feature_switches, disable_features_flag_name,
|
| + false, command_line);
|
| + }
|
| +
|
| + if (sentinels == kAddSentinels) {
|
| + command_line->AppendSwitch(switches::kFlagSwitchesEnd);
|
| + flags_switches_[switches::kFlagSwitchesEnd] = std::string();
|
| + }
|
| +}
|
| +
|
| +void FlagsState::MergeFeatureCommandLineSwitch(
|
| + const std::map<std::string, bool>& feature_switches,
|
| + const char* switch_name,
|
| + bool feature_state,
|
| + base::CommandLine* command_line) {
|
| + std::string original_switch_value =
|
| + command_line->GetSwitchValueASCII(switch_name);
|
| + std::vector<std::string> features =
|
| + base::FeatureList::SplitFeatureListString(original_switch_value);
|
| + // Only add features that don't already exist in the lists.
|
| + // Note: The ContainsValue() call results in O(n^2) performance, but in
|
| + // practice n should be very small.
|
| + for (const auto& entry : feature_switches) {
|
| + if (entry.second == feature_state &&
|
| + !ContainsValue(features, entry.first)) {
|
| + features.push_back(entry.first);
|
| + appended_switches_[switch_name].insert(entry.first);
|
| + }
|
| + }
|
| + // Update the switch value only if it didn't change. This avoids setting an
|
| + // empty list or duplicating the same list (since AppendSwitch() adds the
|
| + // switch to the end but doesn't remove previous ones).
|
| + std::string switch_value = base::JoinString(features, ",");
|
| + if (switch_value != original_switch_value)
|
| + command_line->AppendSwitchASCII(switch_name, switch_value);
|
| +}
|
| +
|
| +void FlagsState::SanitizeList(FlagsStorage* flags_storage) {
|
| + std::set<std::string> known_entries;
|
| + for (size_t i = 0; i < num_feature_entries_; ++i) {
|
| + DCHECK(ValidateFeatureEntry(feature_entries_[i]));
|
| + AddInternalName(feature_entries_[i], &known_entries);
|
| + }
|
| +
|
| + std::set<std::string> enabled_entries = flags_storage->GetFlags();
|
| +
|
| + std::set<std::string> new_enabled_entries =
|
| + base::STLSetIntersection<std::set<std::string>>(known_entries,
|
| + enabled_entries);
|
| +
|
| + if (new_enabled_entries != enabled_entries)
|
| + flags_storage->SetFlags(new_enabled_entries);
|
| +}
|
| +
|
| +void FlagsState::GetSanitizedEnabledFlags(FlagsStorage* flags_storage,
|
| + std::set<std::string>* result) {
|
| + SanitizeList(flags_storage);
|
| + *result = flags_storage->GetFlags();
|
| +}
|
| +
|
| +void FlagsState::GetSanitizedEnabledFlagsForCurrentPlatform(
|
| + FlagsStorage* flags_storage,
|
| + std::set<std::string>* result) {
|
| + GetSanitizedEnabledFlags(flags_storage, result);
|
| +
|
| + // Filter out any entries that aren't enabled on the current platform. We
|
| + // don't remove these from prefs else syncing to a platform with a different
|
| + // set of entries would be lossy.
|
| + std::set<std::string> platform_entries;
|
| + int current_platform = GetCurrentPlatform();
|
| + for (size_t i = 0; i < num_feature_entries_; ++i) {
|
| + const FeatureEntry& entry = feature_entries_[i];
|
| + if (entry.supported_platforms & current_platform)
|
| + AddInternalName(entry, &platform_entries);
|
| +#if defined(OS_CHROMEOS)
|
| + if (feature_entries_[i].supported_platforms & kOsCrOSOwnerOnly)
|
| + AddInternalName(entry, &platform_entries);
|
| +#endif
|
| + }
|
| +
|
| + std::set<std::string> new_enabled_entries =
|
| + base::STLSetIntersection<std::set<std::string>>(platform_entries,
|
| + *result);
|
| +
|
| + result->swap(new_enabled_entries);
|
| +}
|
| +
|
| +} // namespace flags_ui
|
|
|