File: unexpire_flags.cc

package info (click to toggle)
chromium 140.0.7339.127-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,192,880 kB
  • sloc: cpp: 35,093,808; ansic: 7,161,670; javascript: 4,199,694; python: 1,441,797; asm: 949,904; xml: 747,503; pascal: 187,748; perl: 88,691; sh: 88,248; objc: 79,953; sql: 52,714; cs: 44,599; fortran: 24,137; makefile: 22,114; tcl: 15,277; php: 13,980; yacc: 9,000; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (128 lines) | stat: -rw-r--r-- 4,895 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/unexpire_flags.h"

#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
#include "chrome/browser/expired_flags_list.h"
#include "chrome/browser/unexpire_flags_gen.h"
#include "chrome/common/chrome_version.h"
#include "components/webui/flags/flags_storage.h"

namespace flags {

namespace {

using FlagNameToExpirationMap = base::flat_map<std::string, int>;

static FlagNameToExpirationMap* GetFlagExpirationOverrideMap() {
  static base::NoDestructor<FlagNameToExpirationMap> map;
  return map.get();
}

int ExpirationMilestoneForFlag(const char* flag) {
  if (base::Contains(*GetFlagExpirationOverrideMap(), flag)) {
    return GetFlagExpirationOverrideMap()->at(flag);
  }

  for (int i = 0; UNSAFE_TODO(kExpiredFlags[i]).name; ++i) {
    const ExpiredFlag* f = &UNSAFE_TODO(kExpiredFlags[i]);
    if (UNSAFE_TODO(strcmp(f->name, flag))) {
      continue;
    }

    // To keep the size of the expired flags list down,
    // //tools/flags/generate_expired_flags.py doesn't emit flags with expiry
    // mstone -1; it makes no sense for these flags to be in the expiry list
    // anyway. However, if a bug did cause that to happen, and this function
    // didn't handle that case, all flags with expiration -1 would immediately
    // expire, which would be very bad. As such there's an extra error-check
    // here: a DCHECK to catch bugs in the script, and a regular if to ensure we
    // never expire flags that should never expire.
    DCHECK_NE(f->mstone, -1);
    return f->mstone;
  }
  return -1;
}

// This function is a nasty hack - normally, the logic to turn flags into
// feature names happens inside flags_ui::FlagsState, but this function is used
// from the setup code of FlagsState, so it can't rely on FlagsState having been
// set up. As such, we look into the backing FlagsStorage and hardcode how
// enabled flags look inside that storage.
std::set<int> UnexpiredMilestonesFromStorage(
    const flags_ui::FlagsStorage* storage) {
  std::set<int> unexpired;
  for (const auto& f : storage->GetFlags()) {
    int mstone;
    if (UNSAFE_TODO(sscanf(f.c_str(), "temporary-unexpire-flags-m%d@1",
                           &mstone)) == 1) {
      unexpired.insert(mstone);
    }
  }
  return unexpired;
}

}  // namespace

bool IsFlagExpired(const flags_ui::FlagsStorage* storage,
                   const char* internal_name) {
  DCHECK(storage);

  int mstone = ExpirationMilestoneForFlag(internal_name);
  if (mstone == -1) {
    return false;
  }

  // This is extremely horrible:
  //
  // In order to know if a flag is expired or not, normally this function
  // queries the state of base::FeatureList to check whether the unexpire
  // feature for that milestone is enabled. However, when *creating* the initial
  // base::FeatureList instance, these features won't be initialized yet, which
  // leads to this issue:
  //
  // * Assume a flag "foo-bar" for feature FooBar that expires in M83.
  // * Also, assume that temporary-unexpire-flags-m83 is enabled.
  //
  // If both of those are true, then if IsFlagExpired("foo-bar") is called
  // *during* initial feature list setup, it will return true rather than false,
  // which will cause FooBar to be set to its default rather than the
  // non-default value that the flag may be to. This happens because the
  // TemporaryUnexpireFlagsM83 feature hasn't been initialized yet, so it gets
  // treated as its default state (disabled).
  //
  // To deal with that and make this function behave more correctly during
  // FeatureList initialization, also consult the backing FlagsStorage from the
  // FlagsState and look at the temporary-unexpire-flags-m$M flags directly, as
  // well as looking at their features.
  //
  // This still has a problem: during browser startup, if the unexpire feature
  // will be configured by some other mechanism (group policy, etc), that
  // feature's value won't apply in time here and the bug described will happen.
  // In fact, that is a design behavior of the feature system, since flag
  // unexpiry happens during FeatureList initialization.
  // TODO(ellyjones): what might we do about that?
  std::set<int> unexpired_milestones = UnexpiredMilestonesFromStorage(storage);
  if (base::Contains(unexpired_milestones, mstone)) {
    return false;
  }

  // Otherwise, the flag is expired if its expiration mstone is less than the
  // mstone of this copy of Chromium.
  return mstone < CHROME_VERSION_MAJOR;
}

namespace testing {

void SetFlagExpiration(const std::string& name, int mstone) {
  GetFlagExpirationOverrideMap()->insert_or_assign(name, mstone);
}

}  // namespace testing

}  // namespace flags