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/. */
7 #include "WebRenderRollout.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/ScopeExit.h"
10 #include "mozilla/Services.h"
12 #include "nsIObserver.h"
13 #include "nsIObserverService.h"
14 #include "nsISupportsImpl.h"
15 #include "nsXULAppAPI.h"
20 static const char* const WR_ROLLOUT_PREF
= "gfx.webrender.all.qualified";
21 static const bool WR_ROLLOUT_PREF_DEFAULTVALUE
= true;
22 static const char* const WR_ROLLOUT_DEFAULT_PREF
=
23 "gfx.webrender.all.qualified.default";
24 static const bool WR_ROLLOUT_DEFAULT_PREF_DEFAULTVALUE
= false;
25 static const char* const WR_ROLLOUT_PREF_OVERRIDE
=
26 "gfx.webrender.all.qualified.gfxPref-default-override";
27 static const char* const WR_ROLLOUT_HW_QUALIFIED_OVERRIDE
=
28 "gfx.webrender.all.qualified.hardware-override";
29 static const char* const PROFILE_BEFORE_CHANGE_TOPIC
= "profile-before-change";
31 // If the "gfx.webrender.all.qualified" pref is true we want to enable
32 // WebRender for qualified hardware. This pref may be set by the Normandy
33 // Preference Rollout feature. The Normandy pref rollout code sets default
34 // values on rolled out prefs on every startup. Default pref values are not
35 // persisted; they only exist in memory for that session. Gfx starts up
36 // before Normandy does. So it's too early to observe the WR qualified pref
37 // changed by Normandy rollout on gfx startup. So we add a shutdown observer to
38 // save the default value on shutdown, and read the saved value on startup
40 class WrRolloutPrefShutdownSaver final
: public nsIObserver
{
44 NS_IMETHOD
Observe(nsISupports
*, const char* aTopic
,
45 const char16_t
*) override
{
46 if (strcmp(PROFILE_BEFORE_CHANGE_TOPIC
, aTopic
) != 0) {
47 // Not the observer we're looking for, move along.
53 // Shouldn't receive another notification, remove the observer.
54 RefPtr
<WrRolloutPrefShutdownSaver
> kungFuDeathGrip(this);
55 nsCOMPtr
<nsIObserverService
> observerService
=
56 mozilla::services::GetObserverService();
57 if (NS_WARN_IF(!observerService
)) {
58 return NS_ERROR_FAILURE
;
60 observerService
->RemoveObserver(this, PROFILE_BEFORE_CHANGE_TOPIC
);
64 static void AddShutdownObserver() {
65 MOZ_ASSERT(XRE_IsParentProcess());
66 nsCOMPtr
<nsIObserverService
> observerService
=
67 mozilla::services::GetObserverService();
68 if (NS_WARN_IF(!observerService
)) {
71 RefPtr
<WrRolloutPrefShutdownSaver
> wrRolloutSaver
=
72 new WrRolloutPrefShutdownSaver();
73 observerService
->AddObserver(wrRolloutSaver
, PROFILE_BEFORE_CHANGE_TOPIC
,
78 virtual ~WrRolloutPrefShutdownSaver() = default;
80 void SaveRolloutPref() {
81 if (Preferences::HasUserValue(WR_ROLLOUT_PREF
) ||
82 Preferences::GetType(WR_ROLLOUT_PREF
) == nsIPrefBranch::PREF_INVALID
) {
83 // Don't need to create a backup of default value, because either:
84 // 1. the user or the WR SHIELD study has set a user pref value, or
85 // 2. we've not had a default pref set by Normandy that needs to be saved
86 // for reading before Normandy has started up.
91 Preferences::GetBool(WR_ROLLOUT_PREF
, false, PrefValueKind::Default
);
92 Preferences::SetBool(WR_ROLLOUT_DEFAULT_PREF
, defaultValue
);
96 NS_IMPL_ISUPPORTS(WrRolloutPrefShutdownSaver
, nsIObserver
)
98 /* static */ void WebRenderRollout::Init() {
99 WrRolloutPrefShutdownSaver::AddShutdownObserver();
102 /* static */ Maybe
<bool> WebRenderRollout::CalculateQualifiedOverride() {
103 // This pref only ever gets set in test_pref_rollout_workaround, and in
104 // that case we want to ignore the MOZ_WEBRENDER=0 that will be set by
105 // the test harness so as to actually make the test work.
106 if (!Preferences::HasUserValue(WR_ROLLOUT_HW_QUALIFIED_OVERRIDE
)) {
109 return Some(Preferences::GetBool(WR_ROLLOUT_HW_QUALIFIED_OVERRIDE
, false));
112 // If the "gfx.webrender.all.qualified" pref is true we want to enable
113 // WebRender for qualifying hardware. The Normandy pref rollout code sets
114 // default values on rolled out prefs on every startup, but Gfx starts up
115 // before Normandy does. So it's too early to observe the WR qualified pref
116 // default value changed by Normandy rollout here yet. So we have a shutdown
117 // observer to save the default value on shutdown, and read the saved default
118 // value here instead, and emulate the behavior of the pref system, with
119 // respect to default/user values of the rollout pref.
120 /* static */ bool WebRenderRollout::CalculateQualified() {
121 auto clearPrefOnExit
= MakeScopeExit([]() {
122 // Clear the mirror of the default value of the rollout pref on scope exit,
123 // if we have one. This ensures the user doesn't mess with the pref.
124 // If we need it again, we'll re-create it on shutdown.
125 Preferences::ClearUser(WR_ROLLOUT_DEFAULT_PREF
);
128 if (!Preferences::HasUserValue(WR_ROLLOUT_PREF
) &&
129 Preferences::HasUserValue(WR_ROLLOUT_DEFAULT_PREF
)) {
130 // The user has not set a user pref, and we have a default value set by the
131 // shutdown observer. Let's use this as it should be the value Normandy set
132 // before startup. WR_ROLLOUT_DEFAULT_PREF should only be set on shutdown by
133 // the shutdown observer.
134 // Normandy runs *during* startup, but *after* this code here runs (hence
135 // the need for the workaround).
136 // To have a value stored in the WR_ROLLOUT_DEFAULT_PREF pref here, during
137 // the previous run Normandy must have set a default value on the in-memory
138 // pref, and on shutdown we stored the default value in this
139 // WR_ROLLOUT_DEFAULT_PREF user pref. Then once the user restarts, we
140 // observe this pref. Normandy is the only way a default (not user) value
141 // can be set for this pref.
142 return Preferences::GetBool(WR_ROLLOUT_DEFAULT_PREF
,
143 WR_ROLLOUT_DEFAULT_PREF_DEFAULTVALUE
);
146 // We don't have a user value for the rollout pref, and we don't have the
147 // value of the rollout pref at last shutdown stored. So we should fallback
148 // to using the default. *But* if we're running
149 // under the Marionette pref rollout work-around test, we may want to override
150 // the default value expressed here, so we can test the "default disabled;
151 // rollout pref enabled" case.
152 // Note that those preferences can't be defined in all.js nor
153 // StaticPrefsList.h as they would create the pref, leading SaveRolloutPref()
154 // above to abort early as the pref would have a valid type.
155 // We also don't want those prefs to appear in about:config.
156 if (Preferences::HasUserValue(WR_ROLLOUT_PREF_OVERRIDE
)) {
157 return Preferences::GetBool(WR_ROLLOUT_PREF_OVERRIDE
);
159 return Preferences::GetBool(WR_ROLLOUT_PREF
, WR_ROLLOUT_PREF_DEFAULTVALUE
);
163 } // namespace mozilla