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