[M120 Migration]Fix for crash during chrome exit
[platform/framework/web/chromium-efl.git] / chrome / browser / unexpire_flags.cc
1 // Copyright 2019 The Chromium Authors
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 "chrome/browser/unexpire_flags.h"
6
7 #include "base/containers/contains.h"
8 #include "base/containers/flat_map.h"
9 #include "base/no_destructor.h"
10 #include "chrome/browser/expired_flags_list.h"
11 #include "chrome/browser/unexpire_flags_gen.h"
12 #include "chrome/common/chrome_version.h"
13 #include "components/flags_ui/flags_storage.h"
14
15 namespace flags {
16
17 namespace {
18
19 using FlagNameToExpirationMap = base::flat_map<std::string, int>;
20
21 static FlagNameToExpirationMap* GetFlagExpirationOverrideMap() {
22   static base::NoDestructor<FlagNameToExpirationMap> map;
23   return map.get();
24 }
25
26 int ExpirationMilestoneForFlag(const char* flag) {
27   if (base::Contains(*GetFlagExpirationOverrideMap(), flag)) {
28     return GetFlagExpirationOverrideMap()->at(flag);
29   }
30
31   for (int i = 0; kExpiredFlags[i].name; ++i) {
32     const ExpiredFlag* f = &kExpiredFlags[i];
33     if (strcmp(f->name, flag)) {
34       continue;
35     }
36
37     // To keep the size of the expired flags list down,
38     // //tools/flags/generate_expired_flags.py doesn't emit flags with expiry
39     // mstone -1; it makes no sense for these flags to be in the expiry list
40     // anyway. However, if a bug did cause that to happen, and this function
41     // didn't handle that case, all flags with expiration -1 would immediately
42     // expire, which would be very bad. As such there's an extra error-check
43     // here: a DCHECK to catch bugs in the script, and a regular if to ensure we
44     // never expire flags that should never expire.
45     DCHECK_NE(f->mstone, -1);
46     return f->mstone;
47   }
48   return -1;
49 }
50
51 // This function is a nasty hack - normally, the logic to turn flags into
52 // feature names happens inside flags_ui::FlagsState, but this function is used
53 // from the setup code of FlagsState, so it can't rely on FlagsState having been
54 // set up. As such, we look into the backing FlagsStorage and hardcode how
55 // enabled flags look inside that storage.
56 std::set<int> UnexpiredMilestonesFromStorage(
57     const flags_ui::FlagsStorage* storage) {
58   std::set<int> unexpired;
59   for (const auto& f : storage->GetFlags()) {
60     int mstone;
61     if (sscanf(f.c_str(), "temporary-unexpire-flags-m%d@1", &mstone) == 1) {
62       unexpired.insert(mstone);
63     }
64   }
65   return unexpired;
66 }
67
68 }  // namespace
69
70 bool IsFlagExpired(const flags_ui::FlagsStorage* storage,
71                    const char* internal_name) {
72   DCHECK(storage);
73
74   int mstone = ExpirationMilestoneForFlag(internal_name);
75   if (mstone == -1) {
76     return false;
77   }
78
79   // This is extremely horrible:
80   //
81   // In order to know if a flag is expired or not, normally this function
82   // queries the state of base::FeatureList to check whether the unexpire
83   // feature for that milestone is enabled. However, when *creating* the initial
84   // base::FeatureList instance, these features won't be initialized yet, which
85   // leads to this issue:
86   //
87   // * Assume a flag "foo-bar" for feature FooBar that expires in M83.
88   // * Also, assume that temporary-unexpire-flags-m83 is enabled.
89   //
90   // If both of those are true, then if IsFlagExpired("foo-bar") is called
91   // *during* initial feature list setup, it will return true rather than false,
92   // which will cause FooBar to be set to its default rather than the
93   // non-default value that the flag may be to. This happens because the
94   // TemporaryUnexpireFlagsM83 feature hasn't been initialized yet, so it gets
95   // treated as its default state (disabled).
96   //
97   // To deal with that and make this function behave more correctly during
98   // FeatureList initialization, also consult the backing FlagsStorage from the
99   // FlagsState and look at the temporary-unexpire-flags-m$M flags directly, as
100   // well as looking at their features.
101   //
102   // This still has a problem: during browser startup, if the unexpire feature
103   // will be configured by some other mechanism (group policy, etc), that
104   // feature's value won't apply in time here and the bug described will happen.
105   // In fact, that is a design behavior of the feature system, since flag
106   // unexpiry happens during FeatureList initialization.
107   // TODO(ellyjones): what might we do about that?
108   std::set<int> unexpired_milestones = UnexpiredMilestonesFromStorage(storage);
109   if (base::Contains(unexpired_milestones, mstone)) {
110     return false;
111   }
112
113   // Otherwise, the flag is expired if its expiration mstone is less than the
114   // mstone of this copy of Chromium.
115   return mstone < CHROME_VERSION_MAJOR;
116 }
117
118 namespace testing {
119
120 void SetFlagExpiration(const std::string& name, int mstone) {
121   GetFlagExpirationOverrideMap()->insert_or_assign(name, mstone);
122 }
123
124 }  // namespace testing
125
126 }  // namespace flags