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

Side by Side Diff: chrome/browser/about_flags.cc

Issue 1411453004: Componentize internal class FlagsState in flags_ui component. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@feature_entry
Patch Set: Fix build with gn and typo in gyp file Created 5 years, 1 month 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 (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 "chrome/browser/about_flags.h" 5 #include "chrome/browser/about_flags.h"
6 6
7 #include <iterator> 7 #include <iterator>
8 #include <map> 8 #include <map>
9 #include <set> 9 #include <set>
10 #include <utility> 10 #include <utility>
11 11
12 #include "base/bind.h"
13 #include "base/callback.h"
12 #include "base/command_line.h" 14 #include "base/command_line.h"
13 #include "base/feature_list.h" 15 #include "base/feature_list.h"
14 #include "base/memory/singleton.h" 16 #include "base/memory/singleton.h"
15 #include "base/metrics/sparse_histogram.h" 17 #include "base/metrics/sparse_histogram.h"
16 #include "base/stl_util.h" 18 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h" 20 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h" 21 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h" 22 #include "base/values.h"
21 #include "cc/base/switches.h" 23 #include "cc/base/switches.h"
22 #include "chrome/common/channel_info.h" 24 #include "chrome/common/channel_info.h"
23 #include "chrome/common/chrome_content_client.h" 25 #include "chrome/common/chrome_content_client.h"
24 #include "chrome/common/chrome_switches.h" 26 #include "chrome/common/chrome_switches.h"
25 #include "chrome/grit/chromium_strings.h" 27 #include "chrome/grit/chromium_strings.h"
26 #include "chrome/grit/generated_resources.h" 28 #include "chrome/grit/generated_resources.h"
27 #include "chrome/grit/google_chrome_strings.h" 29 #include "chrome/grit/google_chrome_strings.h"
28 #include "components/autofill/core/common/autofill_switches.h" 30 #include "components/autofill/core/common/autofill_switches.h"
29 #include "components/browser_sync/common/browser_sync_switches.h" 31 #include "components/browser_sync/common/browser_sync_switches.h"
30 #include "components/cloud_devices/common/cloud_devices_switches.h" 32 #include "components/cloud_devices/common/cloud_devices_switches.h"
31 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switc hes.h" 33 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switc hes.h"
32 #include "components/dom_distiller/core/dom_distiller_switches.h" 34 #include "components/dom_distiller/core/dom_distiller_switches.h"
33 #include "components/enhanced_bookmarks/enhanced_bookmark_switches.h" 35 #include "components/enhanced_bookmarks/enhanced_bookmark_switches.h"
34 #include "components/flags_ui/feature_entry_macros.h" 36 #include "components/flags_ui/feature_entry_macros.h"
35 #include "components/flags_ui/flags_storage.h" 37 #include "components/flags_ui/flags_storage.h"
38 #include "components/flags_ui/flags_ui_switches.h"
36 #include "components/metrics/metrics_hashes.h" 39 #include "components/metrics/metrics_hashes.h"
37 #include "components/nacl/common/nacl_switches.h" 40 #include "components/nacl/common/nacl_switches.h"
38 #include "components/offline_pages/offline_page_switches.h" 41 #include "components/offline_pages/offline_page_switches.h"
39 #include "components/omnibox/browser/omnibox_switches.h" 42 #include "components/omnibox/browser/omnibox_switches.h"
40 #include "components/password_manager/core/common/password_manager_switches.h" 43 #include "components/password_manager/core/common/password_manager_switches.h"
41 #include "components/plugins/common/plugins_switches.h" 44 #include "components/plugins/common/plugins_switches.h"
42 #include "components/proximity_auth/switches.h" 45 #include "components/proximity_auth/switches.h"
43 #include "components/search/search_switches.h" 46 #include "components/search/search_switches.h"
44 #include "components/signin/core/common/signin_switches.h" 47 #include "components/signin/core/common/signin_switches.h"
45 #include "components/sync_driver/sync_driver_switches.h" 48 #include "components/sync_driver/sync_driver_switches.h"
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 82
80 #if defined(ENABLE_EXTENSIONS) 83 #if defined(ENABLE_EXTENSIONS)
81 #include "extensions/common/switches.h" 84 #include "extensions/common/switches.h"
82 #endif 85 #endif
83 86
84 #if defined(USE_OZONE) 87 #if defined(USE_OZONE)
85 #include "ui/ozone/public/ozone_switches.h" 88 #include "ui/ozone/public/ozone_switches.h"
86 #endif 89 #endif
87 90
88 using flags_ui::FeatureEntry; 91 using flags_ui::FeatureEntry;
92 using flags_ui::kOsMac;
93 using flags_ui::kOsWin;
94 using flags_ui::kOsLinux;
95 using flags_ui::kOsCrOS;
96 using flags_ui::kOsAndroid;
97 using flags_ui::kOsCrOSOwnerOnly;
89 98
90 namespace about_flags { 99 namespace about_flags {
91 100
92 namespace { 101 namespace {
93 102
94 // Enumeration of OSs.
95 enum {
96 kOsMac = 1 << 0,
97 kOsWin = 1 << 1,
98 kOsLinux = 1 << 2,
99 kOsCrOS = 1 << 3,
100 kOsAndroid = 1 << 4,
101 kOsCrOSOwnerOnly = 1 << 5
102 };
103
104 const unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS | kOsAndroid; 103 const unsigned kOsAll = kOsMac | kOsWin | kOsLinux | kOsCrOS | kOsAndroid;
105 const unsigned kOsDesktop = kOsMac | kOsWin | kOsLinux | kOsCrOS; 104 const unsigned kOsDesktop = kOsMac | kOsWin | kOsLinux | kOsCrOS;
106 105
107 // Adds a |StringValue| to |list| for each platform where |bitmask| indicates
108 // whether the entry is available on that platform.
109 void AddOsStrings(unsigned bitmask, base::ListValue* list) {
110 struct {
111 unsigned bit;
112 const char* const name;
113 } kBitsToOs[] = {
114 {kOsMac, "Mac"},
115 {kOsWin, "Windows"},
116 {kOsLinux, "Linux"},
117 {kOsCrOS, "Chrome OS"},
118 {kOsAndroid, "Android"},
119 {kOsCrOSOwnerOnly, "Chrome OS (owner only)"},
120 };
121 for (size_t i = 0; i < arraysize(kBitsToOs); ++i) {
122 if (bitmask & kBitsToOs[i].bit)
123 list->Append(new base::StringValue(kBitsToOs[i].name));
124 }
125 }
126
127 // Convert switch constants to proper CommandLine::StringType strings.
128 base::CommandLine::StringType GetSwitchString(const std::string& flag) {
129 base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
130 cmd_line.AppendSwitch(flag);
131 DCHECK_EQ(2U, cmd_line.argv().size());
132 return cmd_line.argv()[1];
133 }
134
135 // Scoops flags from a command line.
136 std::set<base::CommandLine::StringType> ExtractFlagsFromCommandLine(
137 const base::CommandLine& cmdline) {
138 std::set<base::CommandLine::StringType> flags;
139 // First do the ones between --flag-switches-begin and --flag-switches-end.
140 base::CommandLine::StringVector::const_iterator first =
141 std::find(cmdline.argv().begin(), cmdline.argv().end(),
142 GetSwitchString(switches::kFlagSwitchesBegin));
143 base::CommandLine::StringVector::const_iterator last =
144 std::find(cmdline.argv().begin(), cmdline.argv().end(),
145 GetSwitchString(switches::kFlagSwitchesEnd));
146 if (first != cmdline.argv().end() && last != cmdline.argv().end())
147 flags.insert(first + 1, last);
148 #if defined(OS_CHROMEOS)
149 // Then add those between --policy-switches-begin and --policy-switches-end.
150 first = std::find(cmdline.argv().begin(), cmdline.argv().end(),
151 GetSwitchString(chromeos::switches::kPolicySwitchesBegin));
152 last = std::find(cmdline.argv().begin(), cmdline.argv().end(),
153 GetSwitchString(chromeos::switches::kPolicySwitchesEnd));
154 if (first != cmdline.argv().end() && last != cmdline.argv().end())
155 flags.insert(first + 1, last);
156 #endif
157 return flags;
158 }
159
160 const FeatureEntry::Choice kTouchEventsChoices[] = { 106 const FeatureEntry::Choice kTouchEventsChoices[] = {
161 { IDS_GENERIC_EXPERIMENT_CHOICE_AUTOMATIC, "", "" }, 107 { IDS_GENERIC_EXPERIMENT_CHOICE_AUTOMATIC, "", "" },
162 { IDS_GENERIC_EXPERIMENT_CHOICE_ENABLED, 108 { IDS_GENERIC_EXPERIMENT_CHOICE_ENABLED,
163 switches::kTouchEvents, 109 switches::kTouchEvents,
164 switches::kTouchEventsEnabled }, 110 switches::kTouchEventsEnabled },
165 { IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED, 111 { IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED,
166 switches::kTouchEvents, 112 switches::kTouchEvents,
167 switches::kTouchEventsDisabled } 113 switches::kTouchEventsDisabled }
168 }; 114 };
169 115
(...skipping 1942 matching lines...) Expand 10 before | Expand all | Expand 10 after
2112 {"enable-appcontainer", IDS_FLAGS_ENABLE_APPCONTAINER_NAME, 2058 {"enable-appcontainer", IDS_FLAGS_ENABLE_APPCONTAINER_NAME,
2113 IDS_FLAGS_ENABLE_APPCONTAINER_DESCRIPTION, kOsWin, 2059 IDS_FLAGS_ENABLE_APPCONTAINER_DESCRIPTION, kOsWin,
2114 ENABLE_DISABLE_VALUE_TYPE(switches::kEnableAppContainer, 2060 ENABLE_DISABLE_VALUE_TYPE(switches::kEnableAppContainer,
2115 switches::kDisableAppContainer)}, 2061 switches::kDisableAppContainer)},
2116 #endif // defined(OS_WIN) 2062 #endif // defined(OS_WIN)
2117 // NOTE: Adding new command-line switches requires adding corresponding 2063 // NOTE: Adding new command-line switches requires adding corresponding
2118 // entries to enum "LoginCustomFlags" in histograms.xml. See note in 2064 // entries to enum "LoginCustomFlags" in histograms.xml. See note in
2119 // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test. 2065 // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
2120 }; 2066 };
2121 2067
2122 // Stores and encapsulates the little state that about:flags has. 2068 class FlagsStateSingleton {
2123 class FlagsState {
2124 public: 2069 public:
2125 FlagsState() 2070 FlagsStateSingleton()
2126 : feature_entries(kFeatureEntries), 2071 : flags_state_(kFeatureEntries, arraysize(kFeatureEntries)) {}
2127 num_feature_entries(arraysize(kFeatureEntries)), 2072 ~FlagsStateSingleton() {}
2128 needs_restart_(false) {}
2129 void ConvertFlagsToSwitches(flags_ui::FlagsStorage* flags_storage,
2130 base::CommandLine* command_line,
2131 SentinelsMode sentinels);
2132 bool IsRestartNeededToCommitChanges();
2133 void SetFeatureEntryEnabled(flags_ui::FlagsStorage* flags_storage,
2134 const std::string& internal_name,
2135 bool enable);
2136 void RemoveFlagsSwitches(
2137 std::map<std::string, base::CommandLine::StringType>* switch_list);
2138 void ResetAllFlags(flags_ui::FlagsStorage* flags_storage);
2139 void Reset();
2140 2073
2141 // Gets the list of feature entries. Entries that are available for the 2074 static FlagsStateSingleton* GetInstance() {
2142 // current platform are appended to |supported_entries|; all other entries are 2075 return base::Singleton<FlagsStateSingleton>::get();
2143 // appended to |unsupported_entries|. 2076 }
2144 void GetFlagFeatureEntries(flags_ui::FlagsStorage* flags_storage,
2145 FlagAccess access,
2146 base::ListValue* supported_entries,
2147 base::ListValue* unsupported_entries);
2148 2077
2149 void SetFeatureEntries(const FeatureEntry* entries, size_t count); 2078 static flags_ui::FlagsState* GetFlagsState() {
2150 const FeatureEntry* GetFeatureEntries(size_t* count); 2079 return &GetInstance()->flags_state_;
2151
2152 // Returns the singleton instance of this class
2153 static FlagsState* GetInstance() {
2154 return base::Singleton<FlagsState>::get();
2155 } 2080 }
2156 2081
2157 private: 2082 private:
2158 // Keeps track of affected switches for each FeatureEntry, based on which 2083 flags_ui::FlagsState flags_state_;
2159 // choice is selected for it.
2160 struct SwitchEntry {
2161 // Corresponding base::Feature to toggle.
2162 std::string feature_name;
2163 2084
2164 // If |feature_name| is not empty, the state (enable/disabled) to set. 2085 DISALLOW_COPY_AND_ASSIGN(FlagsStateSingleton);
2165 bool feature_state;
2166
2167 // The name of the switch to add.
2168 std::string switch_name;
2169
2170 // If |switch_name| is not empty, the value of the switch to set.
2171 std::string switch_value;
2172
2173 SwitchEntry() : feature_state(false) {}
2174 };
2175
2176 // Adds mapping to |name_to_switch_map| to set the given switch name/value.
2177 void AddSwitchMapping(const std::string& key,
2178 const std::string& switch_name,
2179 const std::string& switch_value,
2180 std::map<std::string, SwitchEntry>* name_to_switch_map);
2181
2182 // Adds mapping to |name_to_switch_map| to toggle base::Feature |feature_name|
2183 // to state |feature_state|.
2184 void AddFeatureMapping(
2185 const std::string& key,
2186 const std::string& feature_name,
2187 bool feature_state,
2188 std::map<std::string, SwitchEntry>* name_to_switch_map);
2189
2190 // Updates the switches in |command_line| by applying the modifications
2191 // specified in |name_to_switch_map| for each entry in |enabled_entries|.
2192 void AddSwitchesToCommandLine(
2193 const std::set<std::string>& enabled_entries,
2194 const std::map<std::string, SwitchEntry>& name_to_switch_map,
2195 SentinelsMode sentinels,
2196 base::CommandLine* command_line);
2197
2198 // Updates |command_line| by merging the value of the --enable-features= or
2199 // --disable-features= list (per the |switch_name| param) with corresponding
2200 // entries in |feature_switches| that have value |feature_state|. Keeps track
2201 // of the changes by updating |appended_switches|.
2202 void MergeFeatureCommandLineSwitch(
2203 const std::map<std::string, bool>& feature_switches,
2204 const char* switch_name,
2205 bool feature_state,
2206 base::CommandLine* command_line);
2207
2208 // Removes all entries from prefs::kEnabledLabsExperiments that are unknown,
2209 // to prevent this list to become very long as entries are added and removed.
2210 void SanitizeList(flags_ui::FlagsStorage* flags_storage);
2211
2212 void GetSanitizedEnabledFlags(flags_ui::FlagsStorage* flags_storage,
2213 std::set<std::string>* result);
2214
2215 // Variant of GetSanitizedEnabledFlags that also removes any flags that aren't
2216 // enabled on the current platform.
2217 void GetSanitizedEnabledFlagsForCurrentPlatform(
2218 flags_ui::FlagsStorage* flags_storage,
2219 std::set<std::string>* result);
2220
2221 const FeatureEntry* feature_entries;
2222 size_t num_feature_entries;
2223
2224 bool needs_restart_;
2225 std::map<std::string, std::string> flags_switches_;
2226
2227 // Map from switch name to a set of string, that keeps track which strings
2228 // were appended to existing (list value) switches.
2229 std::map<std::string, std::set<std::string>> appended_switches_;
2230
2231 DISALLOW_COPY_AND_ASSIGN(FlagsState);
2232 }; 2086 };
2233 2087
2234 // Adds the internal names for the specified entry to |names|. 2088 bool SkipConditionalFeatureEntry(version_info::Channel channel,
2235 void AddInternalName(const FeatureEntry& e, std::set<std::string>* names) { 2089 const FeatureEntry& entry) {
2236 switch (e.type) {
2237 case FeatureEntry::SINGLE_VALUE:
2238 case FeatureEntry::SINGLE_DISABLE_VALUE:
2239 names->insert(e.internal_name);
2240 break;
2241 case FeatureEntry::MULTI_VALUE:
2242 case FeatureEntry::ENABLE_DISABLE_VALUE:
2243 case FeatureEntry::FEATURE_VALUE:
2244 for (int i = 0; i < e.num_choices; ++i)
2245 names->insert(e.NameForChoice(i));
2246 break;
2247 }
2248 }
2249
2250 // Confirms that an entry is valid, used in a DCHECK in
2251 // SanitizeList below.
2252 bool ValidateFeatureEntry(const FeatureEntry& e) {
2253 switch (e.type) {
2254 case FeatureEntry::SINGLE_VALUE:
2255 case FeatureEntry::SINGLE_DISABLE_VALUE:
2256 DCHECK_EQ(0, e.num_choices);
2257 DCHECK(!e.choices);
2258 return true;
2259 case FeatureEntry::MULTI_VALUE:
2260 DCHECK_GT(e.num_choices, 0);
2261 DCHECK(e.choices);
2262 DCHECK(e.choices[0].command_line_switch);
2263 DCHECK_EQ('\0', e.choices[0].command_line_switch[0]);
2264 return true;
2265 case FeatureEntry::ENABLE_DISABLE_VALUE:
2266 DCHECK_EQ(3, e.num_choices);
2267 DCHECK(!e.choices);
2268 DCHECK(e.command_line_switch);
2269 DCHECK(e.command_line_value);
2270 DCHECK(e.disable_command_line_switch);
2271 DCHECK(e.disable_command_line_value);
2272 return true;
2273 case FeatureEntry::FEATURE_VALUE:
2274 DCHECK_EQ(3, e.num_choices);
2275 DCHECK(!e.choices);
2276 DCHECK(e.feature_name);
2277 return true;
2278 }
2279 NOTREACHED();
2280 return false;
2281 }
2282
2283 void FlagsState::SanitizeList(flags_ui::FlagsStorage* flags_storage) {
2284 std::set<std::string> known_entries;
2285 for (size_t i = 0; i < num_feature_entries; ++i) {
2286 DCHECK(ValidateFeatureEntry(feature_entries[i]));
2287 AddInternalName(feature_entries[i], &known_entries);
2288 }
2289
2290 std::set<std::string> enabled_entries = flags_storage->GetFlags();
2291
2292 std::set<std::string> new_enabled_entries =
2293 base::STLSetIntersection<std::set<std::string> >(
2294 known_entries, enabled_entries);
2295
2296 if (new_enabled_entries != enabled_entries)
2297 flags_storage->SetFlags(new_enabled_entries);
2298 }
2299
2300 void FlagsState::GetSanitizedEnabledFlags(flags_ui::FlagsStorage* flags_storage,
2301 std::set<std::string>* result) {
2302 SanitizeList(flags_storage);
2303 *result = flags_storage->GetFlags();
2304 }
2305
2306 bool SkipConditionalFeatureEntry(const FeatureEntry& entry) {
2307 version_info::Channel channel = chrome::GetChannel();
2308
2309 #if defined(OS_ANDROID) 2090 #if defined(OS_ANDROID)
2310 // enable-data-reduction-proxy-dev is only available for the Dev/Beta channel. 2091 // enable-data-reduction-proxy-dev is only available for the Dev/Beta channel.
2311 if (!strcmp("enable-data-reduction-proxy-dev", entry.internal_name) && 2092 if (!strcmp("enable-data-reduction-proxy-dev", entry.internal_name) &&
2312 channel != version_info::Channel::BETA && 2093 channel != version_info::Channel::BETA &&
2313 channel != version_info::Channel::DEV) { 2094 channel != version_info::Channel::DEV) {
2314 return true; 2095 return true;
2315 } 2096 }
2316 // enable-data-reduction-proxy-alt is only available for the Dev channel. 2097 // enable-data-reduction-proxy-alt is only available for the Dev channel.
2317 if (!strcmp("enable-data-reduction-proxy-alt", entry.internal_name) && 2098 if (!strcmp("enable-data-reduction-proxy-alt", entry.internal_name) &&
2318 channel != version_info::Channel::DEV) { 2099 channel != version_info::Channel::DEV) {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
2357 channel != version_info::Channel::UNKNOWN && 2138 channel != version_info::Channel::UNKNOWN &&
2358 channel != version_info::Channel::CANARY && 2139 channel != version_info::Channel::CANARY &&
2359 channel != version_info::Channel::DEV) { 2140 channel != version_info::Channel::DEV) {
2360 return true; 2141 return true;
2361 } 2142 }
2362 #endif 2143 #endif
2363 2144
2364 return false; 2145 return false;
2365 } 2146 }
2366 2147
2367 void FlagsState::GetSanitizedEnabledFlagsForCurrentPlatform(
2368 flags_ui::FlagsStorage* flags_storage,
2369 std::set<std::string>* result) {
2370 GetSanitizedEnabledFlags(flags_storage, result);
2371
2372 // Filter out any entries that aren't enabled on the current platform. We
2373 // don't remove these from prefs else syncing to a platform with a different
2374 // set of entries would be lossy.
2375 std::set<std::string> platform_entries;
2376 int current_platform = GetCurrentPlatform();
2377 for (size_t i = 0; i < num_feature_entries; ++i) {
2378 const FeatureEntry& entry = feature_entries[i];
2379 if (entry.supported_platforms & current_platform)
2380 AddInternalName(entry, &platform_entries);
2381 #if defined(OS_CHROMEOS)
2382 if (feature_entries[i].supported_platforms & kOsCrOSOwnerOnly)
2383 AddInternalName(entry, &platform_entries);
2384 #endif
2385 }
2386
2387 std::set<std::string> new_enabled_entries =
2388 base::STLSetIntersection<std::set<std::string> >(
2389 platform_entries, *result);
2390
2391 result->swap(new_enabled_entries);
2392 }
2393
2394 // Returns true if none of this entry's options have been enabled.
2395 bool IsDefaultValue(
2396 const FeatureEntry& entry,
2397 const std::set<std::string>& enabled_entries) {
2398 switch (entry.type) {
2399 case FeatureEntry::SINGLE_VALUE:
2400 case FeatureEntry::SINGLE_DISABLE_VALUE:
2401 return enabled_entries.count(entry.internal_name) == 0;
2402 case FeatureEntry::MULTI_VALUE:
2403 case FeatureEntry::ENABLE_DISABLE_VALUE:
2404 case FeatureEntry::FEATURE_VALUE:
2405 for (int i = 0; i < entry.num_choices; ++i) {
2406 if (enabled_entries.count(entry.NameForChoice(i)) > 0)
2407 return false;
2408 }
2409 return true;
2410 }
2411 NOTREACHED();
2412 return true;
2413 }
2414
2415 // Returns the Value representing the choice data in the specified entry.
2416 base::Value* CreateChoiceData(
2417 const FeatureEntry& entry,
2418 const std::set<std::string>& enabled_entries) {
2419 DCHECK(entry.type == FeatureEntry::MULTI_VALUE ||
2420 entry.type == FeatureEntry::ENABLE_DISABLE_VALUE ||
2421 entry.type == FeatureEntry::FEATURE_VALUE);
2422 base::ListValue* result = new base::ListValue;
2423 for (int i = 0; i < entry.num_choices; ++i) {
2424 base::DictionaryValue* value = new base::DictionaryValue;
2425 const std::string name = entry.NameForChoice(i);
2426 value->SetString("internal_name", name);
2427 value->SetString("description", entry.DescriptionForChoice(i));
2428 value->SetBoolean("selected", enabled_entries.count(name) > 0);
2429 result->Append(value);
2430 }
2431 return result;
2432 }
2433
2434 } // namespace 2148 } // namespace
2435 2149
2436 void ConvertFlagsToSwitches(flags_ui::FlagsStorage* flags_storage, 2150 void ConvertFlagsToSwitches(flags_ui::FlagsStorage* flags_storage,
2437 base::CommandLine* command_line, 2151 base::CommandLine* command_line,
2438 SentinelsMode sentinels) { 2152 flags_ui::SentinelsMode sentinels) {
2439 FlagsState::GetInstance()->ConvertFlagsToSwitches(flags_storage, 2153 if (command_line->HasSwitch(switches::kNoExperiments))
2440 command_line, 2154 return;
2441 sentinels); 2155
2156 FlagsStateSingleton::GetFlagsState()->ConvertFlagsToSwitches(
2157 flags_storage, command_line, sentinels, switches::kEnableFeatures,
2158 switches::kDisableFeatures);
2442 } 2159 }
2443 2160
2444 bool AreSwitchesIdenticalToCurrentCommandLine( 2161 bool AreSwitchesIdenticalToCurrentCommandLine(
2445 const base::CommandLine& new_cmdline, 2162 const base::CommandLine& new_cmdline,
2446 const base::CommandLine& active_cmdline, 2163 const base::CommandLine& active_cmdline,
2447 std::set<base::CommandLine::StringType>* out_difference) { 2164 std::set<base::CommandLine::StringType>* out_difference) {
2448 std::set<base::CommandLine::StringType> new_flags = 2165 #if !defined(OS_CHROMEOS)
2449 ExtractFlagsFromCommandLine(new_cmdline); 2166 const char* extra_flag_sentinel_begin_flag_name = nullptr;
2450 std::set<base::CommandLine::StringType> active_flags = 2167 const char* extra_flag_sentinel_end_flag_name = nullptr;
2451 ExtractFlagsFromCommandLine(active_cmdline); 2168 #else
2452 2169 const char* extra_flag_sentinel_begin_flag_name =
2453 bool result = false; 2170 chromeos::switches::kPolicySwitchesBegin;
2454 // Needed because std::equal doesn't check if the 2nd set is empty. 2171 const char* extra_flag_sentinel_end_flag_name =
2455 if (new_flags.size() == active_flags.size()) { 2172 chromeos::switches::kPolicySwitchesEnd;
2456 result =
2457 std::equal(new_flags.begin(), new_flags.end(), active_flags.begin());
2458 }
2459
2460 if (out_difference && !result) {
2461 std::set_symmetric_difference(
2462 new_flags.begin(),
2463 new_flags.end(),
2464 active_flags.begin(),
2465 active_flags.end(),
2466 std::inserter(*out_difference, out_difference->begin()));
2467 }
2468
2469 return result;
2470 }
2471
2472 void FlagsState::GetFlagFeatureEntries(flags_ui::FlagsStorage* flags_storage,
2473 FlagAccess access,
2474 base::ListValue* supported_entries,
2475 base::ListValue* unsupported_entries) {
2476 std::set<std::string> enabled_entries;
2477 GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
2478
2479 int current_platform = GetCurrentPlatform();
2480
2481 for (size_t i = 0; i < num_feature_entries; ++i) {
2482 const FeatureEntry& entry = feature_entries[i];
2483 if (SkipConditionalFeatureEntry(entry))
2484 continue;
2485
2486 base::DictionaryValue* data = new base::DictionaryValue();
2487 data->SetString("internal_name", entry.internal_name);
2488 data->SetString("name",
2489 l10n_util::GetStringUTF16(entry.visible_name_id));
2490 data->SetString("description",
2491 l10n_util::GetStringUTF16(
2492 entry.visible_description_id));
2493
2494 base::ListValue* supported_platforms = new base::ListValue();
2495 AddOsStrings(entry.supported_platforms, supported_platforms);
2496 data->Set("supported_platforms", supported_platforms);
2497 // True if the switch is not currently passed.
2498 bool is_default_value = IsDefaultValue(entry, enabled_entries);
2499 data->SetBoolean("is_default", is_default_value);
2500
2501 switch (entry.type) {
2502 case FeatureEntry::SINGLE_VALUE:
2503 case FeatureEntry::SINGLE_DISABLE_VALUE:
2504 data->SetBoolean(
2505 "enabled",
2506 (!is_default_value &&
2507 entry.type == FeatureEntry::SINGLE_VALUE) ||
2508 (is_default_value &&
2509 entry.type == FeatureEntry::SINGLE_DISABLE_VALUE));
2510 break;
2511 case FeatureEntry::MULTI_VALUE:
2512 case FeatureEntry::ENABLE_DISABLE_VALUE:
2513 data->Set("choices", CreateChoiceData(entry, enabled_entries));
2514 break;
2515 default:
Alexei Svitkine (slow) 2015/11/06 21:22:28 I landed https://codereview.chromium.org/140602302
sdefresne 2015/11/12 11:37:25 Done.
2516 NOTREACHED();
2517 }
2518
2519 bool supported = (entry.supported_platforms & current_platform) != 0;
2520 #if defined(OS_CHROMEOS)
2521 if (access == kOwnerAccessToFlags &&
2522 (entry.supported_platforms & kOsCrOSOwnerOnly) != 0) {
2523 supported = true;
2524 }
2525 #endif 2173 #endif
2526 if (supported) 2174 return flags_ui::AreSwitchesIdenticalToCurrentCommandLine(
2527 supported_entries->Append(data); 2175 new_cmdline, active_cmdline, out_difference,
2528 else 2176 extra_flag_sentinel_begin_flag_name, extra_flag_sentinel_end_flag_name);
2529 unsupported_entries->Append(data);
2530 }
2531 } 2177 }
2532 2178
2533 void GetFlagFeatureEntries(flags_ui::FlagsStorage* flags_storage, 2179 void GetFlagFeatureEntries(flags_ui::FlagsStorage* flags_storage,
2534 FlagAccess access, 2180 flags_ui::FlagAccess access,
2535 base::ListValue* supported_entries, 2181 base::ListValue* supported_entries,
2536 base::ListValue* unsupported_entries) { 2182 base::ListValue* unsupported_entries) {
2537 FlagsState::GetInstance()->GetFlagFeatureEntries(flags_storage, access, 2183 version_info::Channel channel = chrome::GetChannel();
2538 supported_entries, 2184 FlagsStateSingleton::GetFlagsState()->GetFlagFeatureEntries(
2539 unsupported_entries); 2185 flags_storage, access, supported_entries, unsupported_entries,
2186 base::Bind(&SkipConditionalFeatureEntry, channel));
2540 } 2187 }
2541 2188
2542 bool IsRestartNeededToCommitChanges() { 2189 bool IsRestartNeededToCommitChanges() {
2543 return FlagsState::GetInstance()->IsRestartNeededToCommitChanges(); 2190 return FlagsStateSingleton::GetFlagsState()->IsRestartNeededToCommitChanges();
2544 } 2191 }
2545 2192
2546 void SetFeatureEntryEnabled(flags_ui::FlagsStorage* flags_storage, 2193 void SetFeatureEntryEnabled(flags_ui::FlagsStorage* flags_storage,
2547 const std::string& internal_name, 2194 const std::string& internal_name,
2548 bool enable) { 2195 bool enable) {
2549 FlagsState::GetInstance()->SetFeatureEntryEnabled(flags_storage, 2196 FlagsStateSingleton::GetFlagsState()->SetFeatureEntryEnabled(
2550 internal_name, enable); 2197 flags_storage, internal_name, enable);
2551 } 2198 }
2552 2199
2553 void RemoveFlagsSwitches( 2200 void RemoveFlagsSwitches(
2554 std::map<std::string, base::CommandLine::StringType>* switch_list) { 2201 std::map<std::string, base::CommandLine::StringType>* switch_list) {
2555 FlagsState::GetInstance()->RemoveFlagsSwitches(switch_list); 2202 FlagsStateSingleton::GetFlagsState()->RemoveFlagsSwitches(switch_list);
2556 } 2203 }
2557 2204
2558 void ResetAllFlags(flags_ui::FlagsStorage* flags_storage) { 2205 void ResetAllFlags(flags_ui::FlagsStorage* flags_storage) {
2559 FlagsState::GetInstance()->ResetAllFlags(flags_storage); 2206 FlagsStateSingleton::GetFlagsState()->ResetAllFlags(flags_storage);
2560 }
2561
2562 int GetCurrentPlatform() {
2563 #if defined(OS_MACOSX)
2564 return kOsMac;
2565 #elif defined(OS_WIN)
2566 return kOsWin;
2567 #elif defined(OS_CHROMEOS) // Needs to be before the OS_LINUX check.
2568 return kOsCrOS;
2569 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
2570 return kOsLinux;
2571 #elif defined(OS_ANDROID)
2572 return kOsAndroid;
2573 #else
2574 #error Unknown platform
2575 #endif
2576 } 2207 }
2577 2208
2578 void RecordUMAStatistics(flags_ui::FlagsStorage* flags_storage) { 2209 void RecordUMAStatistics(flags_ui::FlagsStorage* flags_storage) {
2579 std::set<std::string> flags = flags_storage->GetFlags(); 2210 std::set<std::string> flags = flags_storage->GetFlags();
2580 for (const std::string& flag : flags) { 2211 for (const std::string& flag : flags) {
2581 std::string action("AboutFlags_"); 2212 std::string action("AboutFlags_");
2582 action += flag; 2213 action += flag;
2583 content::RecordComputedAction(action); 2214 content::RecordComputedAction(action);
2584 } 2215 }
2585 // Since flag metrics are recorded every startup, add a tick so that the 2216 // Since flag metrics are recorded every startup, add a tick so that the
(...skipping 28 matching lines...) Expand all
2614 } 2245 }
2615 DVLOG(1) << "ReportCustomFlags(): histogram='" << uma_histogram_hame 2246 DVLOG(1) << "ReportCustomFlags(): histogram='" << uma_histogram_hame
2616 << "' '" << flag << "', uma_id=" << uma_id; 2247 << "' '" << flag << "', uma_id=" << uma_id;
2617 2248
2618 // Sparse histogram macro does not cache the histogram, so it's safe 2249 // Sparse histogram macro does not cache the histogram, so it's safe
2619 // to use macro with non-static histogram name here. 2250 // to use macro with non-static histogram name here.
2620 UMA_HISTOGRAM_SPARSE_SLOWLY(uma_histogram_hame, uma_id); 2251 UMA_HISTOGRAM_SPARSE_SLOWLY(uma_histogram_hame, uma_id);
2621 } 2252 }
2622 } 2253 }
2623 2254
2624 //////////////////////////////////////////////////////////////////////////////
2625 // FlagsState implementation.
2626
2627 namespace {
2628
2629 void FlagsState::ConvertFlagsToSwitches(flags_ui::FlagsStorage* flags_storage,
2630 base::CommandLine* command_line,
2631 SentinelsMode sentinels) {
2632 if (command_line->HasSwitch(switches::kNoExperiments))
2633 return;
2634
2635 std::set<std::string> enabled_entries;
2636
2637 GetSanitizedEnabledFlagsForCurrentPlatform(flags_storage, &enabled_entries);
2638
2639 std::map<std::string, SwitchEntry> name_to_switch_map;
2640 for (size_t i = 0; i < num_feature_entries; ++i) {
2641 const FeatureEntry& e = feature_entries[i];
2642 switch (e.type) {
2643 case FeatureEntry::SINGLE_VALUE:
2644 case FeatureEntry::SINGLE_DISABLE_VALUE:
2645 AddSwitchMapping(e.internal_name, e.command_line_switch,
2646 e.command_line_value, &name_to_switch_map);
2647 break;
2648 case FeatureEntry::MULTI_VALUE:
2649 for (int j = 0; j < e.num_choices; ++j) {
2650 AddSwitchMapping(e.NameForChoice(j), e.choices[j].command_line_switch,
2651 e.choices[j].command_line_value,
2652 &name_to_switch_map);
2653 }
2654 break;
2655 case FeatureEntry::ENABLE_DISABLE_VALUE:
2656 AddSwitchMapping(e.NameForChoice(0), std::string(), std::string(),
2657 &name_to_switch_map);
2658 AddSwitchMapping(e.NameForChoice(1), e.command_line_switch,
2659 e.command_line_value, &name_to_switch_map);
2660 AddSwitchMapping(e.NameForChoice(2), e.disable_command_line_switch,
2661 e.disable_command_line_value, &name_to_switch_map);
2662 break;
2663 case FeatureEntry::FEATURE_VALUE:
2664 AddFeatureMapping(e.NameForChoice(0), std::string(), false,
2665 &name_to_switch_map);
2666 AddFeatureMapping(e.NameForChoice(1), e.feature_name, true,
2667 &name_to_switch_map);
2668 AddFeatureMapping(e.NameForChoice(2), e.feature_name, false,
2669 &name_to_switch_map);
2670 break;
2671 }
2672 }
2673
2674 AddSwitchesToCommandLine(enabled_entries, name_to_switch_map, sentinels,
2675 command_line);
2676 }
2677
2678 bool FlagsState::IsRestartNeededToCommitChanges() {
2679 return needs_restart_;
2680 }
2681
2682 void FlagsState::SetFeatureEntryEnabled(flags_ui::FlagsStorage* flags_storage,
2683 const std::string& internal_name,
2684 bool enable) {
2685 size_t at_index = internal_name.find(flags_ui::testing::kMultiSeparator);
2686 if (at_index != std::string::npos) {
2687 DCHECK(enable);
2688 // We're being asked to enable a multi-choice entry. Disable the
2689 // currently selected choice.
2690 DCHECK_NE(at_index, 0u);
2691 const std::string entry_name = internal_name.substr(0, at_index);
2692 SetFeatureEntryEnabled(flags_storage, entry_name, false);
2693
2694 // And enable the new choice, if it is not the default first choice.
2695 if (internal_name != entry_name + "@0") {
2696 std::set<std::string> enabled_entries;
2697 GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
2698 needs_restart_ |= enabled_entries.insert(internal_name).second;
2699 flags_storage->SetFlags(enabled_entries);
2700 }
2701 return;
2702 }
2703
2704 std::set<std::string> enabled_entries;
2705 GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
2706
2707 const FeatureEntry* e = nullptr;
2708 for (size_t i = 0; i < num_feature_entries; ++i) {
2709 if (feature_entries[i].internal_name == internal_name) {
2710 e = feature_entries + i;
2711 break;
2712 }
2713 }
2714 DCHECK(e);
2715
2716 if (e->type == FeatureEntry::SINGLE_VALUE) {
2717 if (enable)
2718 needs_restart_ |= enabled_entries.insert(internal_name).second;
2719 else
2720 needs_restart_ |= (enabled_entries.erase(internal_name) > 0);
2721 } else if (e->type == FeatureEntry::SINGLE_DISABLE_VALUE) {
2722 if (!enable)
2723 needs_restart_ |= enabled_entries.insert(internal_name).second;
2724 else
2725 needs_restart_ |= (enabled_entries.erase(internal_name) > 0);
2726 } else {
2727 if (enable) {
2728 // Enable the first choice.
2729 needs_restart_ |= enabled_entries.insert(e->NameForChoice(0)).second;
2730 } else {
2731 // Find the currently enabled choice and disable it.
2732 for (int i = 0; i < e->num_choices; ++i) {
2733 std::string choice_name = e->NameForChoice(i);
2734 if (enabled_entries.find(choice_name) !=
2735 enabled_entries.end()) {
2736 needs_restart_ = true;
2737 enabled_entries.erase(choice_name);
2738 // Continue on just in case there's a bug and more than one
2739 // entry for this choice was enabled.
2740 }
2741 }
2742 }
2743 }
2744
2745 flags_storage->SetFlags(enabled_entries);
2746 }
2747
2748 void FlagsState::RemoveFlagsSwitches(
2749 std::map<std::string, base::CommandLine::StringType>* switch_list) {
2750 for (const auto& entry : flags_switches_)
2751 switch_list->erase(entry.first);
2752
2753 // If feature entries were added to --enable-features= or --disable-features=
2754 // lists, remove them here while preserving existing values.
2755 for (const auto& entry : appended_switches_) {
2756 const auto& switch_name = entry.first;
2757 const auto& switch_added_values = entry.second;
2758
2759 // The below is either a std::string or a base::string16 based on platform.
2760 const auto& existing_value = (*switch_list)[switch_name];
2761 #if defined(OS_WIN)
2762 const std::string existing_value_utf8 = base::UTF16ToUTF8(existing_value);
2763 #else
2764 const std::string& existing_value_utf8 = existing_value;
2765 #endif
2766
2767 std::vector<std::string> features =
2768 base::FeatureList::SplitFeatureListString(existing_value_utf8);
2769 std::vector<std::string> remaining_features;
2770 // For any featrue name in |features| that is not in |switch_added_values| -
2771 // i.e. it wasn't added by about_flags code, add it to |remaining_features|.
2772 for (const std::string& feature : features) {
2773 if (!ContainsKey(switch_added_values, feature))
2774 remaining_features.push_back(feature);
2775 }
2776
2777 // Either remove the flag entirely if |remaining_features| is empty, or set
2778 // the new list.
2779 if (remaining_features.empty()) {
2780 switch_list->erase(switch_name);
2781 } else {
2782 std::string switch_value = base::JoinString(remaining_features, ",");
2783 #if defined(OS_WIN)
2784 (*switch_list)[switch_name] = base::UTF8ToUTF16(switch_value);
2785 #else
2786 (*switch_list)[switch_name] = switch_value;
2787 #endif
2788 }
2789 }
2790 }
2791
2792 void FlagsState::ResetAllFlags(flags_ui::FlagsStorage* flags_storage) {
2793 needs_restart_ = true;
2794
2795 std::set<std::string> no_entries;
2796 flags_storage->SetFlags(no_entries);
2797 }
2798
2799 void FlagsState::Reset() {
2800 needs_restart_ = false;
2801 flags_switches_.clear();
2802 appended_switches_.clear();
2803 }
2804
2805 void FlagsState::SetFeatureEntries(const FeatureEntry* entries, size_t count) {
2806 feature_entries = entries;
2807 num_feature_entries = count;
2808 }
2809
2810 const FeatureEntry* FlagsState::GetFeatureEntries(size_t* count) {
2811 *count = num_feature_entries;
2812 return feature_entries;
2813 }
2814
2815 void FlagsState::AddSwitchMapping(
2816 const std::string& key,
2817 const std::string& switch_name,
2818 const std::string& switch_value,
2819 std::map<std::string, SwitchEntry>* name_to_switch_map) {
2820 DCHECK(!ContainsKey(*name_to_switch_map, key));
2821
2822 SwitchEntry* entry = &(*name_to_switch_map)[key];
2823 entry->switch_name = switch_name;
2824 entry->switch_value = switch_value;
2825 }
2826
2827 void FlagsState::AddFeatureMapping(
2828 const std::string& key,
2829 const std::string& feature_name,
2830 bool feature_state,
2831 std::map<std::string, SwitchEntry>* name_to_switch_map) {
2832 DCHECK(!ContainsKey(*name_to_switch_map, key));
2833
2834 SwitchEntry* entry = &(*name_to_switch_map)[key];
2835 entry->feature_name = feature_name;
2836 entry->feature_state = feature_state;
2837 }
2838
2839 void FlagsState::AddSwitchesToCommandLine(
2840 const std::set<std::string>& enabled_entries,
2841 const std::map<std::string, SwitchEntry>& name_to_switch_map,
2842 SentinelsMode sentinels,
2843 base::CommandLine* command_line) {
2844 std::map<std::string, bool> feature_switches;
2845 if (sentinels == kAddSentinels) {
2846 command_line->AppendSwitch(switches::kFlagSwitchesBegin);
2847 flags_switches_[switches::kFlagSwitchesBegin] = std::string();
2848 }
2849
2850 for (const std::string& entry_name : enabled_entries) {
2851 const auto& entry_it = name_to_switch_map.find(entry_name);
2852 if (entry_it == name_to_switch_map.end()) {
2853 NOTREACHED();
2854 continue;
2855 }
2856
2857 const SwitchEntry& entry = entry_it->second;
2858 if (!entry.feature_name.empty()) {
2859 feature_switches[entry.feature_name] = entry.feature_state;
2860 } else if (!entry.switch_name.empty()) {
2861 command_line->AppendSwitchASCII(entry.switch_name, entry.switch_value);
2862 flags_switches_[entry.switch_name] = entry.switch_value;
2863 }
2864 // If an entry doesn't match either of the above, then it is likely the
2865 // default entry for a FEATURE_VALUE entry. Safe to ignore.
2866 }
2867
2868 if (!feature_switches.empty()) {
2869 MergeFeatureCommandLineSwitch(feature_switches, switches::kEnableFeatures,
2870 true, command_line);
2871 MergeFeatureCommandLineSwitch(feature_switches, switches::kDisableFeatures,
2872 false, command_line);
2873 }
2874
2875 if (sentinels == kAddSentinels) {
2876 command_line->AppendSwitch(switches::kFlagSwitchesEnd);
2877 flags_switches_[switches::kFlagSwitchesEnd] = std::string();
2878 }
2879 }
2880
2881 void FlagsState::MergeFeatureCommandLineSwitch(
2882 const std::map<std::string, bool>& feature_switches,
2883 const char* switch_name,
2884 bool feature_state,
2885 base::CommandLine* command_line) {
2886 std::string original_switch_value =
2887 command_line->GetSwitchValueASCII(switch_name);
2888 std::vector<std::string> features =
2889 base::FeatureList::SplitFeatureListString(original_switch_value);
2890 // Only add features that don't already exist in the lists.
2891 // Note: The ContainsValue() call results in O(n^2) performance, but in
2892 // practice n should be very small.
2893 for (const auto& entry : feature_switches) {
2894 if (entry.second == feature_state &&
2895 !ContainsValue(features, entry.first)) {
2896 features.push_back(entry.first);
2897 appended_switches_[switch_name].insert(entry.first);
2898 }
2899 }
2900 // Update the switch value only if it didn't change. This avoids setting an
2901 // empty list or duplicating the same list (since AppendSwitch() adds the
2902 // switch to the end but doesn't remove previous ones).
2903 std::string switch_value = base::JoinString(features, ",");
2904 if (switch_value != original_switch_value)
2905 command_line->AppendSwitchASCII(switch_name, switch_value);
2906 }
2907
2908 } // namespace
2909
2910 namespace testing { 2255 namespace testing {
2911 2256
2912 const base::HistogramBase::Sample kBadSwitchFormatHistogramId = 0; 2257 const base::HistogramBase::Sample kBadSwitchFormatHistogramId = 0;
2913 2258
2914 void ClearState() {
2915 FlagsState::GetInstance()->Reset();
2916 }
2917
2918 void SetFeatureEntries(const FeatureEntry* entries, size_t count) {
2919 if (!entries) {
2920 entries = kFeatureEntries;
2921 count = arraysize(kFeatureEntries);
2922 }
2923 FlagsState::GetInstance()->SetFeatureEntries(entries, count);
2924 }
2925
2926 const FeatureEntry* GetFeatureEntries(size_t* count) { 2259 const FeatureEntry* GetFeatureEntries(size_t* count) {
2927 return FlagsState::GetInstance()->GetFeatureEntries(count); 2260 *count = arraysize(kFeatureEntries);
2261 return kFeatureEntries;
2928 } 2262 }
2929 2263
2930 } // namespace testing 2264 } // namespace testing
2931 2265
2932 } // namespace about_flags 2266 } // namespace about_flags
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698