3b8636c28498e184be8cc5769ee04622b07af57c
[platform/framework/web/chromium-efl.git] / base / feature_list.cc
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/feature_list.h"
6
7 #include <stddef.h>
8
9 #include <utility>
10 #include <vector>
11
12 #include "base/debug/alias.h"
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/pickle.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "build/build_config.h"
20
21 namespace base {
22
23 namespace {
24
25 // Pointer to the FeatureList instance singleton that was set via
26 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
27 // have more control over initialization timing. Leaky.
28 FeatureList* g_feature_list_instance = nullptr;
29
30 // Tracks whether the FeatureList instance was initialized via an accessor, and
31 // which Feature that accessor was for, if so.
32 const Feature* g_initialized_from_accessor = nullptr;
33
34 #if DCHECK_IS_ON()
35 const char* g_reason_overrides_disallowed = nullptr;
36
37 void DCheckOverridesAllowed() {
38   const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
39   DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
40 }
41 #else
42 void DCheckOverridesAllowed() {}
43 #endif
44
45 // An allocator entry for a feature in shared memory. The FeatureEntry is
46 // followed by a base::Pickle object that contains the feature and trial name.
47 struct FeatureEntry {
48   // SHA1(FeatureEntry): Increment this if structure changes!
49   static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
50
51   // Expected size for 32/64-bit check.
52   static constexpr size_t kExpectedInstanceSize = 8;
53
54   // Specifies whether a feature override enables or disables the feature. Same
55   // values as the OverrideState enum in feature_list.h
56   uint32_t override_state;
57
58   // Size of the pickled structure, NOT the total size of this entry.
59   uint32_t pickle_size;
60
61   // Reads the feature and trial name from the pickle. Calling this is only
62   // valid on an initialized entry that's in shared memory.
63   bool GetFeatureAndTrialName(StringPiece* feature_name,
64                               StringPiece* trial_name) const {
65     const char* src =
66         reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
67
68     Pickle pickle(src, pickle_size);
69     PickleIterator pickle_iter(pickle);
70
71     if (!pickle_iter.ReadStringPiece(feature_name))
72       return false;
73
74     // Return true because we are not guaranteed to have a trial name anyways.
75     auto sink = pickle_iter.ReadStringPiece(trial_name);
76     ALLOW_UNUSED_LOCAL(sink);
77     return true;
78   }
79 };
80
81 // Some characters are not allowed to appear in feature names or the associated
82 // field trial names, as they are used as special characters for command-line
83 // serialization. This function checks that the strings are ASCII (since they
84 // are used in command-line API functions that require ASCII) and whether there
85 // are any reserved characters present, returning true if the string is valid.
86 // Only called in DCHECKs.
87 bool IsValidFeatureOrFieldTrialName(const std::string& name) {
88   return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
89 }
90
91 }  // namespace
92
93 #if defined(DCHECK_IS_CONFIGURABLE)
94 const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
95                                     FEATURE_DISABLED_BY_DEFAULT};
96 #endif  // defined(DCHECK_IS_CONFIGURABLE)
97
98 FeatureList::FeatureList() = default;
99
100 FeatureList::~FeatureList() = default;
101
102 FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
103     const char* reason)
104 #if DCHECK_IS_ON()
105     : previous_reason_(g_reason_overrides_disallowed) {
106   g_reason_overrides_disallowed = reason;
107 }
108 #else
109 {
110 }
111 #endif
112
113 FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
114 #if DCHECK_IS_ON()
115   g_reason_overrides_disallowed = previous_reason_;
116 #endif
117 }
118
119 void FeatureList::InitializeFromCommandLine(
120     const std::string& enable_features,
121     const std::string& disable_features) {
122   DCHECK(!initialized_);
123
124   // Process disabled features first, so that disabled ones take precedence over
125   // enabled ones (since RegisterOverride() uses insert()).
126   RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
127   RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
128
129   initialized_from_command_line_ = true;
130 }
131
132 void FeatureList::InitializeFromSharedMemory(
133     PersistentMemoryAllocator* allocator) {
134   DCHECK(!initialized_);
135
136   PersistentMemoryAllocator::Iterator iter(allocator);
137   const FeatureEntry* entry;
138   while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
139     OverrideState override_state =
140         static_cast<OverrideState>(entry->override_state);
141
142     StringPiece feature_name;
143     StringPiece trial_name;
144     if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
145       continue;
146
147     FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
148     RegisterOverride(feature_name, override_state, trial);
149   }
150 }
151
152 bool FeatureList::IsFeatureOverriddenFromCommandLine(
153     const std::string& feature_name,
154     OverrideState state) const {
155   auto it = overrides_.find(feature_name);
156   return it != overrides_.end() && it->second.overridden_state == state &&
157          !it->second.overridden_by_field_trial;
158 }
159
160 void FeatureList::AssociateReportingFieldTrial(
161     const std::string& feature_name,
162     OverrideState for_overridden_state,
163     FieldTrial* field_trial) {
164   DCHECK(
165       IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
166
167   // Only one associated field trial is supported per feature. This is generally
168   // enforced server-side.
169   OverrideEntry* entry = &overrides_.find(feature_name)->second;
170   if (entry->field_trial) {
171     NOTREACHED() << "Feature " << feature_name
172                  << " already has trial: " << entry->field_trial->trial_name()
173                  << ", associating trial: " << field_trial->trial_name();
174     return;
175   }
176
177   entry->field_trial = field_trial;
178 }
179
180 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
181                                              OverrideState override_state,
182                                              FieldTrial* field_trial) {
183   DCHECK(field_trial);
184   DCHECK(!Contains(overrides_, feature_name) ||
185          !overrides_.find(feature_name)->second.field_trial)
186       << "Feature " << feature_name
187       << " has conflicting field trial overrides: "
188       << overrides_.find(feature_name)->second.field_trial->trial_name()
189       << " / " << field_trial->trial_name()
190       << ". Please make sure that the trial (study) name is consistent across:"
191       << " (1)The server config, (2)The fieldtrial_testing_config, and"
192       << " (3) The about_flags.cc";
193
194   RegisterOverride(feature_name, override_state, field_trial);
195 }
196
197 void FeatureList::RegisterExtraFeatureOverrides(
198     const std::vector<FeatureOverrideInfo>& extra_overrides) {
199   for (const FeatureOverrideInfo& override_info : extra_overrides) {
200     RegisterOverride(override_info.first.get().name, override_info.second,
201                      /* field_trial = */ nullptr);
202   }
203 }
204
205 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
206   DCHECK(initialized_);
207
208   for (const auto& override : overrides_) {
209     Pickle pickle;
210     pickle.WriteString(override.first);
211     if (override.second.field_trial)
212       pickle.WriteString(override.second.field_trial->trial_name());
213
214     size_t total_size = sizeof(FeatureEntry) + pickle.size();
215     FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
216     if (!entry)
217       return;
218
219     entry->override_state = override.second.overridden_state;
220     entry->pickle_size = pickle.size();
221
222     char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
223     memcpy(dst, pickle.data(), pickle.size());
224
225     allocator->MakeIterable(entry);
226   }
227 }
228
229 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
230                                       std::string* disable_overrides) {
231   GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
232 }
233
234 void FeatureList::GetCommandLineFeatureOverrides(
235     std::string* enable_overrides,
236     std::string* disable_overrides) {
237   GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
238 }
239
240 // static
241 bool FeatureList::IsEnabled(const Feature& feature) {
242   if (!g_feature_list_instance) {
243     g_initialized_from_accessor = &feature;
244     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
245   }
246   return g_feature_list_instance->IsFeatureEnabled(feature);
247 }
248
249 // static
250 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
251   if (!g_feature_list_instance) {
252     g_initialized_from_accessor = &feature;
253     return nullptr;
254   }
255   return g_feature_list_instance->GetAssociatedFieldTrial(feature);
256 }
257
258 // static
259 std::vector<StringPiece> FeatureList::SplitFeatureListString(
260     StringPiece input) {
261   return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
262 }
263
264 // static
265 bool FeatureList::InitializeInstance(const std::string& enable_features,
266                                      const std::string& disable_features) {
267   return InitializeInstance(enable_features, disable_features,
268                             std::vector<FeatureOverrideInfo>());
269 }
270
271 // static
272 bool FeatureList::InitializeInstance(
273     const std::string& enable_features,
274     const std::string& disable_features,
275     const std::vector<FeatureOverrideInfo>& extra_overrides) {
276   // We want to initialize a new instance here to support command-line features
277   // in testing better. For example, we initialize a dummy instance in
278   // base/test/test_suite.cc, and override it in content/browser/
279   // browser_main_loop.cc.
280   // On the other hand, we want to avoid re-initialization from command line.
281   // For example, we initialize an instance in chrome/browser/
282   // chrome_browser_main.cc and do not override it in content/browser/
283   // browser_main_loop.cc.
284   // If the singleton was previously initialized from within an accessor, we
285   // want to prevent callers from reinitializing the singleton and masking the
286   // accessor call(s) which likely returned incorrect information.
287   if (g_initialized_from_accessor) {
288     DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
289     CHECK(!g_initialized_from_accessor);
290   }
291   bool instance_existed_before = false;
292   if (g_feature_list_instance) {
293     if (g_feature_list_instance->initialized_from_command_line_)
294       return false;
295
296     delete g_feature_list_instance;
297     g_feature_list_instance = nullptr;
298     instance_existed_before = true;
299   }
300
301   std::unique_ptr<FeatureList> feature_list(new FeatureList);
302   feature_list->InitializeFromCommandLine(enable_features, disable_features);
303   feature_list->RegisterExtraFeatureOverrides(extra_overrides);
304   FeatureList::SetInstance(std::move(feature_list));
305   return !instance_existed_before;
306 }
307
308 // static
309 FeatureList* FeatureList::GetInstance() {
310   return g_feature_list_instance;
311 }
312
313 // static
314 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
315   DCHECK(!g_feature_list_instance);
316   instance->FinalizeInitialization();
317
318   // Note: Intentional leak of global singleton.
319   g_feature_list_instance = instance.release();
320
321 #if defined(DCHECK_IS_CONFIGURABLE)
322   // Update the behaviour of LOG_DCHECK to match the Feature configuration.
323   // DCHECK is also forced to be FATAL if we are running a death-test.
324   // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
325   // currently run through this codepath, mitigated in
326   // base::TestSuite::Initialize() for now.
327   // TODO(asvitkine): If we find other use-cases that need integrating here
328   // then define a proper API/hook for the purpose.
329   if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
330       CommandLine::ForCurrentProcess()->HasSwitch(
331           "gtest_internal_run_death_test")) {
332     logging::LOG_DCHECK = logging::LOG_FATAL;
333   } else {
334     logging::LOG_DCHECK = logging::LOG_INFO;
335   }
336 #endif  // defined(DCHECK_IS_CONFIGURABLE)
337 }
338
339 // static
340 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
341   FeatureList* old_instance = g_feature_list_instance;
342   g_feature_list_instance = nullptr;
343   g_initialized_from_accessor = nullptr;
344   return WrapUnique(old_instance);
345 }
346
347 // static
348 void FeatureList::RestoreInstanceForTesting(
349     std::unique_ptr<FeatureList> instance) {
350   DCHECK(!g_feature_list_instance);
351   // Note: Intentional leak of global singleton.
352   g_feature_list_instance = instance.release();
353 }
354
355 void FeatureList::FinalizeInitialization() {
356   DCHECK(!initialized_);
357   // Store the field trial list pointer for DCHECKing.
358   field_trial_list_ = FieldTrialList::GetInstance();
359   initialized_ = true;
360 }
361
362 bool FeatureList::IsFeatureEnabled(const Feature& feature) {
363   DCHECK(initialized_);
364   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
365   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
366
367   auto it = overrides_.find(feature.name);
368   if (it != overrides_.end()) {
369     const OverrideEntry& entry = it->second;
370
371     // Activate the corresponding field trial, if necessary.
372     if (entry.field_trial)
373       entry.field_trial->group();
374
375     // TODO(asvitkine) Expand this section as more support is added.
376
377     // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
378     if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
379       return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
380   }
381   // Otherwise, return the default state.
382   return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
383 }
384
385 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
386   DCHECK(initialized_);
387   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
388   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
389
390   auto it = overrides_.find(feature.name);
391   if (it != overrides_.end()) {
392     const OverrideEntry& entry = it->second;
393     return entry.field_trial;
394   }
395
396   return nullptr;
397 }
398
399 void FeatureList::RegisterOverridesFromCommandLine(
400     const std::string& feature_list,
401     OverrideState overridden_state) {
402   for (const auto& value : SplitFeatureListString(feature_list)) {
403     StringPiece feature_name = value;
404     FieldTrial* trial = nullptr;
405
406     // The entry may be of the form FeatureName<FieldTrialName - in which case,
407     // this splits off the field trial name and associates it with the override.
408     std::string::size_type pos = feature_name.find('<');
409     if (pos != std::string::npos) {
410       feature_name = StringPiece(value.data(), pos);
411       trial = FieldTrialList::Find(value.substr(pos + 1).as_string());
412 #if !defined(OS_NACL)
413       // If the below DCHECK fires, it means a non-existent trial name was
414       // specified via the "Feature<Trial" command-line syntax.
415       DCHECK(trial) << "trial=" << value.substr(pos + 1);
416 #endif  // !defined(OS_NACL)
417     }
418
419     RegisterOverride(feature_name, overridden_state, trial);
420   }
421 }
422
423 void FeatureList::RegisterOverride(StringPiece feature_name,
424                                    OverrideState overridden_state,
425                                    FieldTrial* field_trial) {
426   DCHECK(!initialized_);
427   DCheckOverridesAllowed();
428   if (field_trial) {
429     DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
430         << field_trial->trial_name();
431   }
432   if (feature_name.starts_with("*")) {
433     feature_name = feature_name.substr(1);
434     overridden_state = OVERRIDE_USE_DEFAULT;
435   }
436
437   // Note: The semantics of insert() is that it does not overwrite the entry if
438   // one already exists for the key. Thus, only the first override for a given
439   // feature name takes effect.
440   overrides_.insert(std::make_pair(
441       feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
442 }
443
444 void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
445                                           std::string* disable_overrides,
446                                           bool command_line_only) {
447   DCHECK(initialized_);
448
449   // Check that the FieldTrialList this is associated with, if any, is the
450   // active one. If not, it likely indicates that this FeatureList has override
451   // entries from a freed FieldTrial, which may be caused by an incorrect test
452   // set up.
453   if (field_trial_list_)
454     DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
455
456   enable_overrides->clear();
457   disable_overrides->clear();
458
459   // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
460   // order. This is not guaranteed to users of this function, but is useful for
461   // tests to assume the order.
462   for (const auto& entry : overrides_) {
463     if (command_line_only &&
464         (entry.second.field_trial != nullptr ||
465          entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
466       continue;
467     }
468
469     std::string* target_list = nullptr;
470     switch (entry.second.overridden_state) {
471       case OVERRIDE_USE_DEFAULT:
472       case OVERRIDE_ENABLE_FEATURE:
473         target_list = enable_overrides;
474         break;
475       case OVERRIDE_DISABLE_FEATURE:
476         target_list = disable_overrides;
477         break;
478     }
479
480     if (!target_list->empty())
481       target_list->push_back(',');
482     if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
483       target_list->push_back('*');
484     target_list->append(entry.first);
485     if (entry.second.field_trial) {
486       target_list->push_back('<');
487       target_list->append(entry.second.field_trial->trial_name());
488     }
489   }
490 }
491
492 bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
493   AutoLock auto_lock(feature_identity_tracker_lock_);
494
495   auto it = feature_identity_tracker_.find(feature.name);
496   if (it == feature_identity_tracker_.end()) {
497     // If it's not tracked yet, register it.
498     feature_identity_tracker_[feature.name] = &feature;
499     return true;
500   }
501   // Compare address of |feature| to the existing tracked entry.
502   return it->second == &feature;
503 }
504
505 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
506                                           FieldTrial* field_trial)
507     : overridden_state(overridden_state),
508       field_trial(field_trial),
509       overridden_by_field_trial(field_trial != nullptr) {}
510
511 }  // namespace base