1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "gfxFeature.h"
8
9 #include "mozilla/Preferences.h"
10 #include "mozilla/Sprintf.h"
11 #include "nsString.h"
12
13 namespace mozilla {
14 namespace gfx {
15
IsEnabled() const16 bool FeatureState::IsEnabled() const {
17 return IsInitialized() && IsFeatureStatusSuccess(GetValue());
18 }
19
GetValue() const20 FeatureStatus FeatureState::GetValue() const {
21 if (!IsInitialized()) {
22 return FeatureStatus::Unused;
23 }
24
25 if (mRuntime.mStatus != FeatureStatus::Unused) {
26 return mRuntime.mStatus;
27 }
28 if (mUser.mStatus == FeatureStatus::ForceEnabled) {
29 return FeatureStatus::ForceEnabled;
30 }
31 if (mEnvironment.mStatus != FeatureStatus::Unused) {
32 return mEnvironment.mStatus;
33 }
34 if (mUser.mStatus != FeatureStatus::Unused) {
35 return mUser.mStatus;
36 }
37 return mDefault.mStatus;
38 }
39
SetDefault(bool aEnable,FeatureStatus aDisableStatus,const char * aDisableMessage)40 bool FeatureState::SetDefault(bool aEnable, FeatureStatus aDisableStatus,
41 const char* aDisableMessage) {
42 if (!aEnable) {
43 DisableByDefault(aDisableStatus, aDisableMessage,
44 "FEATURE_FAILURE_DISABLED"_ns);
45 return false;
46 }
47 EnableByDefault();
48 return true;
49 }
50
SetDefaultFromPref(const char * aPrefName,bool aIsEnablePref,bool aDefaultValue,Maybe<bool> aUserValue)51 void FeatureState::SetDefaultFromPref(const char* aPrefName, bool aIsEnablePref,
52 bool aDefaultValue,
53 Maybe<bool> aUserValue) {
54 bool baseValue =
55 Preferences::GetBool(aPrefName, aDefaultValue, PrefValueKind::Default);
56 SetDefault(baseValue == aIsEnablePref, FeatureStatus::Disabled,
57 "Disabled by default");
58
59 if (aUserValue) {
60 if (*aUserValue == aIsEnablePref) {
61 nsCString message("Enabled via ");
62 message.AppendASCII(aPrefName);
63 UserEnable(message.get());
64 } else {
65 nsCString message("Disabled via ");
66 message.AppendASCII(aPrefName);
67 UserDisable(message.get(), "FEATURE_FAILURE_PREF_OFF"_ns);
68 }
69 }
70 }
71
SetDefaultFromPref(const char * aPrefName,bool aIsEnablePref,bool aDefaultValue)72 void FeatureState::SetDefaultFromPref(const char* aPrefName, bool aIsEnablePref,
73 bool aDefaultValue) {
74 Maybe<bool> userValue;
75 if (Preferences::HasUserValue(aPrefName)) {
76 userValue.emplace(Preferences::GetBool(aPrefName, aDefaultValue));
77 }
78
79 SetDefaultFromPref(aPrefName, aIsEnablePref, aDefaultValue, userValue);
80 }
81
InitOrUpdate(bool aEnable,FeatureStatus aDisableStatus,const char * aDisableMessage)82 bool FeatureState::InitOrUpdate(bool aEnable, FeatureStatus aDisableStatus,
83 const char* aDisableMessage) {
84 if (!IsInitialized()) {
85 return SetDefault(aEnable, aDisableStatus, aDisableMessage);
86 }
87 return MaybeSetFailed(aEnable, aDisableStatus, aDisableMessage, nsCString());
88 }
89
UserEnable(const char * aMessage)90 void FeatureState::UserEnable(const char* aMessage) {
91 AssertInitialized();
92 SetUser(FeatureStatus::Available, aMessage, nsCString());
93 }
94
UserForceEnable(const char * aMessage)95 void FeatureState::UserForceEnable(const char* aMessage) {
96 AssertInitialized();
97 SetUser(FeatureStatus::ForceEnabled, aMessage, nsCString());
98 }
99
UserDisable(const char * aMessage,const nsACString & aFailureId)100 void FeatureState::UserDisable(const char* aMessage,
101 const nsACString& aFailureId) {
102 AssertInitialized();
103 SetUser(FeatureStatus::Disabled, aMessage, aFailureId);
104 }
105
Disable(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)106 void FeatureState::Disable(FeatureStatus aStatus, const char* aMessage,
107 const nsACString& aFailureId) {
108 AssertInitialized();
109
110 // We should never bother setting an environment status to "enabled," since
111 // it could override an explicit user decision to disable it.
112 MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
113
114 SetEnvironment(aStatus, aMessage, aFailureId);
115 }
116
SetFailed(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)117 void FeatureState::SetFailed(FeatureStatus aStatus, const char* aMessage,
118 const nsACString& aFailureId) {
119 AssertInitialized();
120
121 // We should never bother setting a runtime status to "enabled," since it
122 // could override an explicit user decision to disable it.
123 MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
124
125 SetRuntime(aStatus, aMessage, aFailureId);
126 }
127
MaybeSetFailed(bool aEnable,FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)128 bool FeatureState::MaybeSetFailed(bool aEnable, FeatureStatus aStatus,
129 const char* aMessage,
130 const nsACString& aFailureId) {
131 if (!aEnable) {
132 SetFailed(aStatus, aMessage, aFailureId);
133 return false;
134 }
135 return true;
136 }
137
MaybeSetFailed(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)138 bool FeatureState::MaybeSetFailed(FeatureStatus aStatus, const char* aMessage,
139 const nsACString& aFailureId) {
140 return MaybeSetFailed(IsFeatureStatusSuccess(aStatus), aStatus, aMessage,
141 aFailureId);
142 }
143
DisabledByDefault() const144 bool FeatureState::DisabledByDefault() const {
145 return mDefault.mStatus != FeatureStatus::Available;
146 }
147
IsForcedOnByUser() const148 bool FeatureState::IsForcedOnByUser() const {
149 AssertInitialized();
150 return mUser.mStatus == FeatureStatus::ForceEnabled;
151 }
152
EnableByDefault()153 void FeatureState::EnableByDefault() {
154 // User/runtime decisions should not have been made yet.
155 MOZ_ASSERT(!mUser.IsInitialized());
156 MOZ_ASSERT(!mEnvironment.IsInitialized());
157 MOZ_ASSERT(!mRuntime.IsInitialized());
158
159 mDefault.Set(FeatureStatus::Available);
160 }
161
DisableByDefault(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)162 void FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage,
163 const nsACString& aFailureId) {
164 // User/runtime decisions should not have been made yet.
165 MOZ_ASSERT(!mUser.IsInitialized());
166 MOZ_ASSERT(!mEnvironment.IsInitialized());
167 MOZ_ASSERT(!mRuntime.IsInitialized());
168
169 // Make sure that when disabling we actually use a failure status.
170 MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
171
172 mDefault.Set(aStatus, aMessage, aFailureId);
173 }
174
SetUser(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)175 void FeatureState::SetUser(FeatureStatus aStatus, const char* aMessage,
176 const nsACString& aFailureId) {
177 // Default decision must have been made, but not runtime or environment.
178 MOZ_ASSERT(mDefault.IsInitialized());
179 MOZ_ASSERT(!mEnvironment.IsInitialized());
180 MOZ_ASSERT(!mRuntime.IsInitialized());
181
182 mUser.Set(aStatus, aMessage, aFailureId);
183 }
184
SetEnvironment(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)185 void FeatureState::SetEnvironment(FeatureStatus aStatus, const char* aMessage,
186 const nsACString& aFailureId) {
187 // Default decision must have been made, but not runtime.
188 MOZ_ASSERT(mDefault.IsInitialized());
189 MOZ_ASSERT(!mRuntime.IsInitialized());
190
191 mEnvironment.Set(aStatus, aMessage, aFailureId);
192 }
193
SetRuntime(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)194 void FeatureState::SetRuntime(FeatureStatus aStatus, const char* aMessage,
195 const nsACString& aFailureId) {
196 AssertInitialized();
197
198 mRuntime.Set(aStatus, aMessage, aFailureId);
199 }
200
GetRuntimeMessage() const201 const char* FeatureState::GetRuntimeMessage() const {
202 MOZ_ASSERT(IsFeatureStatusFailure(mRuntime.mStatus));
203 return mRuntime.mMessage;
204 }
205
ForEachStatusChange(const StatusIterCallback & aCallback) const206 void FeatureState::ForEachStatusChange(
207 const StatusIterCallback& aCallback) const {
208 AssertInitialized();
209
210 aCallback("default", mDefault.mStatus, mDefault.MessageOrNull(),
211 mDefault.FailureId());
212 if (mUser.IsInitialized()) {
213 aCallback("user", mUser.mStatus, mUser.Message(), mDefault.FailureId());
214 }
215 if (mEnvironment.IsInitialized()) {
216 aCallback("env", mEnvironment.mStatus, mEnvironment.Message(),
217 mDefault.FailureId());
218 }
219 if (mRuntime.IsInitialized()) {
220 aCallback("runtime", mRuntime.mStatus, mRuntime.Message(),
221 mDefault.FailureId());
222 }
223 }
224
GetFailureMessage() const225 const char* FeatureState::GetFailureMessage() const {
226 AssertInitialized();
227 MOZ_ASSERT(!IsEnabled());
228
229 if (mRuntime.mStatus != FeatureStatus::Unused &&
230 IsFeatureStatusFailure(mRuntime.mStatus)) {
231 return mRuntime.mMessage;
232 }
233 if (mEnvironment.mStatus != FeatureStatus::Unused &&
234 IsFeatureStatusFailure(mEnvironment.mStatus)) {
235 return mEnvironment.mMessage;
236 }
237 if (mUser.mStatus != FeatureStatus::Unused &&
238 IsFeatureStatusFailure(mUser.mStatus)) {
239 return mUser.mMessage;
240 }
241
242 MOZ_ASSERT(IsFeatureStatusFailure(mDefault.mStatus));
243 return mDefault.mMessage;
244 }
245
GetFailureId() const246 const nsCString& FeatureState::GetFailureId() const {
247 MOZ_ASSERT(!IsEnabled());
248
249 if (mRuntime.mStatus != FeatureStatus::Unused) {
250 return mRuntime.mFailureId;
251 }
252 if (mEnvironment.mStatus != FeatureStatus::Unused) {
253 return mEnvironment.mFailureId;
254 }
255 if (mUser.mStatus != FeatureStatus::Unused) {
256 return mUser.mFailureId;
257 }
258
259 return mDefault.mFailureId;
260 }
261
GetStatusAndFailureIdString() const262 nsCString FeatureState::GetStatusAndFailureIdString() const {
263 nsCString status;
264 auto value = GetValue();
265 switch (value) {
266 case FeatureStatus::Blocklisted:
267 case FeatureStatus::Disabled:
268 case FeatureStatus::Unavailable:
269 case FeatureStatus::UnavailableNoAngle:
270 case FeatureStatus::Blocked:
271 status.AppendPrintf("%s:%s", FeatureStatusToString(value),
272 GetFailureId().get());
273 break;
274 default:
275 status.Append(FeatureStatusToString(value));
276 break;
277 }
278
279 return status;
280 }
281
Reset()282 void FeatureState::Reset() {
283 mDefault.Set(FeatureStatus::Unused);
284 mUser.Set(FeatureStatus::Unused);
285 mEnvironment.Set(FeatureStatus::Unused);
286 mRuntime.Set(FeatureStatus::Unused);
287 }
288
Set(FeatureStatus aStatus)289 void FeatureState::Instance::Set(FeatureStatus aStatus) {
290 mStatus = aStatus;
291 mMessage[0] = '\0';
292 mFailureId.Truncate();
293 }
294
Set(FeatureStatus aStatus,const char * aMessage,const nsACString & aFailureId)295 void FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage,
296 const nsACString& aFailureId) {
297 mStatus = aStatus;
298 if (aMessage) {
299 SprintfLiteral(mMessage, "%s", aMessage);
300 } else {
301 mMessage[0] = '\0';
302 }
303 mFailureId.Assign(aFailureId);
304 }
305
306 } // namespace gfx
307 } // namespace mozilla
308