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

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

Powered by Google App Engine
This is Rietveld 408576698