Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / GfxInfoBase.cpp
bloba8a119b5f4c47a5aba44a0a4fc3a9cbb8ea497e7
1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/ArrayUtils.h"
10 #include "GfxInfoBase.h"
12 #include <mutex> // std::call_once
14 #include "GfxDriverInfo.h"
15 #include "js/Array.h" // JS::GetArrayLength, JS::NewArrayObject
16 #include "js/PropertyAndElement.h" // JS_SetElement, JS_SetProperty
17 #include "nsCOMPtr.h"
18 #include "nsCOMArray.h"
19 #include "nsString.h"
20 #include "nsUnicharUtils.h"
21 #include "nsVersionComparator.h"
22 #include "mozilla/Services.h"
23 #include "mozilla/Observer.h"
24 #include "nsIObserver.h"
25 #include "nsIObserverService.h"
26 #include "nsTArray.h"
27 #include "nsXULAppAPI.h"
28 #include "nsIXULAppInfo.h"
29 #include "mozilla/ClearOnShutdown.h"
30 #include "mozilla/Preferences.h"
31 #include "mozilla/StaticPrefs_gfx.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/gfx/GPUProcessManager.h"
34 #include "mozilla/gfx/Logging.h"
35 #include "mozilla/gfx/gfxVars.h"
36 #include "mozilla/widget/ScreenManager.h"
37 #include "mozilla/widget/Screen.h"
39 #include "gfxPlatform.h"
40 #include "gfxConfig.h"
41 #include "DriverCrashGuard.h"
43 using namespace mozilla::widget;
44 using namespace mozilla;
45 using mozilla::MutexAutoLock;
47 nsTArray<GfxDriverInfo>* GfxInfoBase::sDriverInfo;
48 StaticAutoPtr<nsTArray<gfx::GfxInfoFeatureStatus>> GfxInfoBase::sFeatureStatus;
49 bool GfxInfoBase::sDriverInfoObserverInitialized;
50 bool GfxInfoBase::sShutdownOccurred;
52 // Call this when setting sFeatureStatus to a non-null pointer to
53 // ensure destruction even if the GfxInfo component is never instantiated.
54 static void InitFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>* aPtr) {
55 static std::once_flag sOnce;
56 std::call_once(sOnce, [] { ClearOnShutdown(&GfxInfoBase::sFeatureStatus); });
57 GfxInfoBase::sFeatureStatus = aPtr;
60 // Observes for shutdown so that the child GfxDriverInfo list is freed.
61 class ShutdownObserver : public nsIObserver {
62 virtual ~ShutdownObserver() = default;
64 public:
65 ShutdownObserver() = default;
67 NS_DECL_ISUPPORTS
69 NS_IMETHOD Observe(nsISupports* subject, const char* aTopic,
70 const char16_t* aData) override {
71 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
73 delete GfxInfoBase::sDriverInfo;
74 GfxInfoBase::sDriverInfo = nullptr;
76 for (auto& deviceFamily : GfxDriverInfo::sDeviceFamilies) {
77 delete deviceFamily;
78 deviceFamily = nullptr;
81 for (auto& desktop : GfxDriverInfo::sDesktopEnvironment) {
82 delete desktop;
83 desktop = nullptr;
86 for (auto& windowProtocol : GfxDriverInfo::sWindowProtocol) {
87 delete windowProtocol;
88 windowProtocol = nullptr;
91 for (auto& deviceVendor : GfxDriverInfo::sDeviceVendors) {
92 delete deviceVendor;
93 deviceVendor = nullptr;
96 for (auto& driverVendor : GfxDriverInfo::sDriverVendors) {
97 delete driverVendor;
98 driverVendor = nullptr;
101 GfxInfoBase::sShutdownOccurred = true;
103 return NS_OK;
107 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
109 static void InitGfxDriverInfoShutdownObserver() {
110 if (GfxInfoBase::sDriverInfoObserverInitialized) return;
112 GfxInfoBase::sDriverInfoObserverInitialized = true;
114 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
115 if (!observerService) {
116 NS_WARNING("Could not get observer service!");
117 return;
120 ShutdownObserver* obs = new ShutdownObserver();
121 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
124 using namespace mozilla::widget;
125 using namespace mozilla::gfx;
126 using namespace mozilla;
128 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver,
129 nsISupportsWeakReference)
131 #define BLOCKLIST_PREF_BRANCH "gfx.blacklist."
132 #define SUGGESTED_VERSION_PREF BLOCKLIST_PREF_BRANCH "suggested-driver-version"
134 static const char* GetPrefNameForFeature(int32_t aFeature) {
135 const char* name = nullptr;
136 switch (aFeature) {
137 case nsIGfxInfo::FEATURE_DIRECT2D:
138 name = BLOCKLIST_PREF_BRANCH "direct2d";
139 break;
140 case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
141 name = BLOCKLIST_PREF_BRANCH "layers.direct3d9";
142 break;
143 case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
144 name = BLOCKLIST_PREF_BRANCH "layers.direct3d10";
145 break;
146 case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
147 name = BLOCKLIST_PREF_BRANCH "layers.direct3d10-1";
148 break;
149 case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
150 name = BLOCKLIST_PREF_BRANCH "layers.direct3d11";
151 break;
152 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
153 name = BLOCKLIST_PREF_BRANCH "direct3d11angle";
154 break;
155 case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
156 name = BLOCKLIST_PREF_BRANCH "hardwarevideodecoding";
157 break;
158 case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
159 name = BLOCKLIST_PREF_BRANCH "layers.opengl";
160 break;
161 case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
162 name = BLOCKLIST_PREF_BRANCH "webgl.opengl";
163 break;
164 case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
165 name = BLOCKLIST_PREF_BRANCH "webgl.angle";
166 break;
167 case nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA:
168 name = BLOCKLIST_PREF_BRANCH "webgl.msaa";
169 break;
170 case nsIGfxInfo::FEATURE_STAGEFRIGHT:
171 name = BLOCKLIST_PREF_BRANCH "stagefright";
172 break;
173 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264:
174 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.h264";
175 break;
176 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
177 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
178 break;
179 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
180 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
181 break;
182 case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
183 name = BLOCKLIST_PREF_BRANCH "canvas2d.acceleration";
184 break;
185 case nsIGfxInfo::FEATURE_DX_INTEROP2:
186 name = BLOCKLIST_PREF_BRANCH "dx.interop2";
187 break;
188 case nsIGfxInfo::FEATURE_GPU_PROCESS:
189 name = BLOCKLIST_PREF_BRANCH "gpu.process";
190 break;
191 case nsIGfxInfo::FEATURE_WEBGL2:
192 name = BLOCKLIST_PREF_BRANCH "webgl2";
193 break;
194 case nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX:
195 name = BLOCKLIST_PREF_BRANCH "d3d11.keyed.mutex";
196 break;
197 case nsIGfxInfo::FEATURE_WEBRENDER:
198 name = BLOCKLIST_PREF_BRANCH "webrender";
199 break;
200 case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
201 name = BLOCKLIST_PREF_BRANCH "webrender.compositor";
202 break;
203 case nsIGfxInfo::FEATURE_DX_NV12:
204 name = BLOCKLIST_PREF_BRANCH "dx.nv12";
205 break;
206 case nsIGfxInfo::FEATURE_DX_P010:
207 name = BLOCKLIST_PREF_BRANCH "dx.p010";
208 break;
209 case nsIGfxInfo::FEATURE_DX_P016:
210 name = BLOCKLIST_PREF_BRANCH "dx.p016";
211 break;
212 case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
213 name = BLOCKLIST_PREF_BRANCH "vp8.hw-decode";
214 break;
215 case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
216 name = BLOCKLIST_PREF_BRANCH "vp9.hw-decode";
217 break;
218 case nsIGfxInfo::FEATURE_GL_SWIZZLE:
219 name = BLOCKLIST_PREF_BRANCH "gl.swizzle";
220 break;
221 case nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS:
222 name = BLOCKLIST_PREF_BRANCH "webrender.scissored_cache_clears";
223 break;
224 case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
225 name = BLOCKLIST_PREF_BRANCH "webgl.allow-oop";
226 break;
227 case nsIGfxInfo::FEATURE_THREADSAFE_GL:
228 name = BLOCKLIST_PREF_BRANCH "gl.threadsafe";
229 break;
230 case nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS:
231 name = BLOCKLIST_PREF_BRANCH "webrender.optimized-shaders";
232 break;
233 case nsIGfxInfo::FEATURE_X11_EGL:
234 name = BLOCKLIST_PREF_BRANCH "x11.egl";
235 break;
236 case nsIGfxInfo::FEATURE_DMABUF:
237 name = BLOCKLIST_PREF_BRANCH "dmabuf";
238 break;
239 case nsIGfxInfo::FEATURE_WEBGPU:
240 name = BLOCKLIST_PREF_BRANCH "webgpu";
241 break;
242 case nsIGfxInfo::FEATURE_VIDEO_OVERLAY:
243 name = BLOCKLIST_PREF_BRANCH "video-overlay";
244 break;
245 case nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY:
246 name = BLOCKLIST_PREF_BRANCH "hw-video-zero-copy";
247 break;
248 case nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE:
249 name = BLOCKLIST_PREF_BRANCH "webrender.program-binary-disk";
250 break;
251 case nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT:
252 name = BLOCKLIST_PREF_BRANCH "webrender.partial-present";
253 break;
254 case nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT:
255 name = BLOCKLIST_PREF_BRANCH "dmabuf.surface-export";
256 break;
257 case nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE:
258 name = BLOCKLIST_PREF_BRANCH "reuse-decoder-device";
259 break;
260 case nsIGfxInfo::FEATURE_BACKDROP_FILTER:
261 name = BLOCKLIST_PREF_BRANCH "backdrop.filter";
262 break;
263 default:
264 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
265 break;
268 return name;
271 // Returns the value of the pref for the relevant feature in aValue.
272 // If the pref doesn't exist, aValue is not touched, and returns false.
273 static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
274 nsACString& aFailureId) {
275 const char* prefname = GetPrefNameForFeature(aFeature);
276 if (!prefname) return false;
278 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
279 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
280 return false;
283 nsCString failureprefname(prefname);
284 failureprefname += ".failureid";
285 nsAutoCString failureValue;
286 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
287 if (NS_SUCCEEDED(rv)) {
288 aFailureId = failureValue.get();
289 } else {
290 aFailureId = "FEATURE_FAILURE_BLOCKLIST_PREF";
293 return true;
296 static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
297 const nsACString& aFailureId) {
298 const char* prefname = GetPrefNameForFeature(aFeature);
299 if (!prefname) return;
300 if (XRE_IsParentProcess()) {
301 GfxInfoBase::sFeatureStatus = nullptr;
304 Preferences::SetInt(prefname, aValue);
305 if (!aFailureId.IsEmpty()) {
306 nsCString failureprefname(prefname);
307 failureprefname += ".failureid";
308 Preferences::SetCString(failureprefname.get(), aFailureId);
312 static void RemovePrefForFeature(int32_t aFeature) {
313 const char* prefname = GetPrefNameForFeature(aFeature);
314 if (!prefname) return;
316 if (XRE_IsParentProcess()) {
317 GfxInfoBase::sFeatureStatus = nullptr;
319 Preferences::ClearUser(prefname);
322 static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
323 return NS_SUCCEEDED(
324 Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
327 static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
328 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
331 static void RemovePrefForDriverVersion() {
332 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
335 static OperatingSystem BlocklistOSToOperatingSystem(const nsAString& os) {
336 if (os.EqualsLiteral("WINNT 6.1")) {
337 return OperatingSystem::Windows7;
339 if (os.EqualsLiteral("WINNT 6.2")) {
340 return OperatingSystem::Windows8;
342 if (os.EqualsLiteral("WINNT 6.3")) {
343 return OperatingSystem::Windows8_1;
345 if (os.EqualsLiteral("WINNT 10.0")) {
346 return OperatingSystem::Windows10;
348 if (os.EqualsLiteral("Linux")) {
349 return OperatingSystem::Linux;
351 if (os.EqualsLiteral("Darwin 9")) {
352 return OperatingSystem::OSX10_5;
354 if (os.EqualsLiteral("Darwin 10")) {
355 return OperatingSystem::OSX10_6;
357 if (os.EqualsLiteral("Darwin 11")) {
358 return OperatingSystem::OSX10_7;
360 if (os.EqualsLiteral("Darwin 12")) {
361 return OperatingSystem::OSX10_8;
363 if (os.EqualsLiteral("Darwin 13")) {
364 return OperatingSystem::OSX10_9;
366 if (os.EqualsLiteral("Darwin 14")) {
367 return OperatingSystem::OSX10_10;
369 if (os.EqualsLiteral("Darwin 15")) {
370 return OperatingSystem::OSX10_11;
372 if (os.EqualsLiteral("Darwin 16")) {
373 return OperatingSystem::OSX10_12;
375 if (os.EqualsLiteral("Darwin 17")) {
376 return OperatingSystem::OSX10_13;
378 if (os.EqualsLiteral("Darwin 18")) {
379 return OperatingSystem::OSX10_14;
381 if (os.EqualsLiteral("Darwin 19")) {
382 return OperatingSystem::OSX10_15;
384 if (os.EqualsLiteral("Darwin 20")) {
385 return OperatingSystem::OSX11_0;
387 if (os.EqualsLiteral("Android")) {
388 return OperatingSystem::Android;
389 // For historical reasons, "All" in blocklist means "All Windows"
391 if (os.EqualsLiteral("All")) {
392 return OperatingSystem::Windows;
394 if (os.EqualsLiteral("Darwin")) {
395 return OperatingSystem::OSX;
398 return OperatingSystem::Unknown;
401 static GfxDeviceFamily* BlocklistDevicesToDeviceFamily(
402 nsTArray<nsCString>& devices) {
403 if (devices.Length() == 0) return nullptr;
405 // For each device, get its device ID, and return a freshly-allocated
406 // GfxDeviceFamily with the contents of that array.
407 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
409 for (uint32_t i = 0; i < devices.Length(); ++i) {
410 // We make sure we don't add any "empty" device entries to the array, so
411 // we don't need to check if devices[i] is empty.
412 deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
415 return deviceIds;
418 static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) {
419 MOZ_ASSERT(!aFeature.IsEmpty());
420 if (aFeature.EqualsLiteral("DIRECT2D")) {
421 return nsIGfxInfo::FEATURE_DIRECT2D;
423 if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS")) {
424 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
426 if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS")) {
427 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
429 if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS")) {
430 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
432 if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS")) {
433 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
435 if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE")) {
436 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
438 if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING")) {
439 return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
441 if (aFeature.EqualsLiteral("OPENGL_LAYERS")) {
442 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
444 if (aFeature.EqualsLiteral("WEBGL_OPENGL")) {
445 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
447 if (aFeature.EqualsLiteral("WEBGL_ANGLE")) {
448 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
450 if (aFeature.EqualsLiteral("WEBGL_MSAA")) {
451 return nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA;
453 if (aFeature.EqualsLiteral("STAGEFRIGHT")) {
454 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
456 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE")) {
457 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
459 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE")) {
460 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
462 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_H264")) {
463 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264;
465 if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION")) {
466 return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
468 if (aFeature.EqualsLiteral("DX_INTEROP2")) {
469 return nsIGfxInfo::FEATURE_DX_INTEROP2;
471 if (aFeature.EqualsLiteral("GPU_PROCESS")) {
472 return nsIGfxInfo::FEATURE_GPU_PROCESS;
474 if (aFeature.EqualsLiteral("WEBGL2")) {
475 return nsIGfxInfo::FEATURE_WEBGL2;
477 if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX")) {
478 return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
480 if (aFeature.EqualsLiteral("WEBRENDER")) {
481 return nsIGfxInfo::FEATURE_WEBRENDER;
483 if (aFeature.EqualsLiteral("WEBRENDER_COMPOSITOR")) {
484 return nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR;
486 if (aFeature.EqualsLiteral("DX_NV12")) {
487 return nsIGfxInfo::FEATURE_DX_NV12;
489 if (aFeature.EqualsLiteral("VP8_HW_DECODE")) {
490 return nsIGfxInfo::FEATURE_VP8_HW_DECODE;
492 if (aFeature.EqualsLiteral("VP9_HW_DECODE")) {
493 return nsIGfxInfo::FEATURE_VP9_HW_DECODE;
495 if (aFeature.EqualsLiteral("GL_SWIZZLE")) {
496 return nsIGfxInfo::FEATURE_GL_SWIZZLE;
498 if (aFeature.EqualsLiteral("WEBRENDER_SCISSORED_CACHE_CLEARS")) {
499 return nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS;
501 if (aFeature.EqualsLiteral("ALLOW_WEBGL_OUT_OF_PROCESS")) {
502 return nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS;
504 if (aFeature.EqualsLiteral("THREADSAFE_GL")) {
505 return nsIGfxInfo::FEATURE_THREADSAFE_GL;
507 if (aFeature.EqualsLiteral("X11_EGL")) {
508 return nsIGfxInfo::FEATURE_X11_EGL;
510 if (aFeature.EqualsLiteral("DMABUF")) {
511 return nsIGfxInfo::FEATURE_DMABUF;
513 if (aFeature.EqualsLiteral("WEBGPU")) {
514 return nsIGfxInfo::FEATURE_WEBGPU;
516 if (aFeature.EqualsLiteral("VIDEO_OVERLAY")) {
517 return nsIGfxInfo::FEATURE_VIDEO_OVERLAY;
519 if (aFeature.EqualsLiteral("HW_DECODED_VIDEO_ZERO_COPY")) {
520 return nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY;
522 if (aFeature.EqualsLiteral("REUSE_DECODER_DEVICE")) {
523 return nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE;
525 if (aFeature.EqualsLiteral("WEBRENDER_PARTIAL_PRESENT")) {
526 return nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT;
528 if (aFeature.EqualsLiteral("BACKDROP_FILTER")) {
529 return nsIGfxInfo::FEATURE_BACKDROP_FILTER;
532 // If we don't recognize the feature, it may be new, and something
533 // this version doesn't understand. So, nothing to do. This is
534 // different from feature not being specified at all, in which case
535 // this method should not get called and we should continue with the
536 // "all features" blocklisting.
537 return -1;
540 static int32_t BlocklistFeatureStatusToGfxFeatureStatus(
541 const nsAString& aStatus) {
542 if (aStatus.EqualsLiteral("STATUS_OK")) {
543 return nsIGfxInfo::FEATURE_STATUS_OK;
545 if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION")) {
546 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
548 if (aStatus.EqualsLiteral("BLOCKED_DEVICE")) {
549 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
551 if (aStatus.EqualsLiteral("DISCOURAGED")) {
552 return nsIGfxInfo::FEATURE_DISCOURAGED;
554 if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION")) {
555 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
557 if (aStatus.EqualsLiteral("DENIED")) {
558 return nsIGfxInfo::FEATURE_DENIED;
560 if (aStatus.EqualsLiteral("ALLOW_QUALIFIED")) {
561 return nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
563 if (aStatus.EqualsLiteral("ALLOW_ALWAYS")) {
564 return nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
567 // Do not allow it to set STATUS_UNKNOWN. Also, we are not
568 // expecting the "mismatch" status showing up here.
570 return nsIGfxInfo::FEATURE_STATUS_OK;
573 static VersionComparisonOp BlocklistComparatorToComparisonOp(
574 const nsAString& op) {
575 if (op.EqualsLiteral("LESS_THAN")) {
576 return DRIVER_LESS_THAN;
578 if (op.EqualsLiteral("BUILD_ID_LESS_THAN")) {
579 return DRIVER_BUILD_ID_LESS_THAN;
581 if (op.EqualsLiteral("LESS_THAN_OR_EQUAL")) {
582 return DRIVER_LESS_THAN_OR_EQUAL;
584 if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL")) {
585 return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
587 if (op.EqualsLiteral("GREATER_THAN")) {
588 return DRIVER_GREATER_THAN;
590 if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL")) {
591 return DRIVER_GREATER_THAN_OR_EQUAL;
593 if (op.EqualsLiteral("EQUAL")) {
594 return DRIVER_EQUAL;
596 if (op.EqualsLiteral("NOT_EQUAL")) {
597 return DRIVER_NOT_EQUAL;
599 if (op.EqualsLiteral("BETWEEN_EXCLUSIVE")) {
600 return DRIVER_BETWEEN_EXCLUSIVE;
602 if (op.EqualsLiteral("BETWEEN_INCLUSIVE")) {
603 return DRIVER_BETWEEN_INCLUSIVE;
605 if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START")) {
606 return DRIVER_BETWEEN_INCLUSIVE_START;
609 return DRIVER_COMPARISON_IGNORED;
613 Deserialize Blocklist entries from string.
614 e.g:
615 os:WINNT 6.0\tvendor:0x8086\tdevices:0x2582,0x2782\tfeature:DIRECT3D_10_LAYERS\tfeatureStatus:BLOCKED_DRIVER_VERSION\tdriverVersion:8.52.322.2202\tdriverVersionComparator:LESS_THAN_OR_EQUAL
617 static bool BlocklistEntryToDriverInfo(const nsACString& aBlocklistEntry,
618 GfxDriverInfo& aDriverInfo) {
619 // If we get an application version to be zero, something is not working
620 // and we are not going to bother checking the blocklist versions.
621 // See TestGfxWidgets.cpp for how version comparison works.
622 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
623 static mozilla::Version zeroV("0");
624 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
625 if (appV <= zeroV) {
626 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
627 << "Invalid application version "
628 << GfxInfoBase::GetApplicationVersion().get();
631 aDriverInfo.mRuleId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID"_ns;
633 for (const auto& keyValue : aBlocklistEntry.Split('\t')) {
634 nsTArray<nsCString> splitted;
635 ParseString(keyValue, ':', splitted);
636 if (splitted.Length() != 2) {
637 // If we don't recognize the input data, we do not want to proceed.
638 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
639 << "Unrecognized data " << nsCString(keyValue).get();
640 return false;
642 const nsCString& key = splitted[0];
643 const nsCString& value = splitted[1];
644 NS_ConvertUTF8toUTF16 dataValue(value);
646 if (value.Length() == 0) {
647 // Safety check for empty values.
648 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
649 << "Empty value for " << key.get();
650 return false;
653 if (key.EqualsLiteral("blockID")) {
654 nsCString blockIdStr = "FEATURE_FAILURE_DL_BLOCKLIST_"_ns + value;
655 aDriverInfo.mRuleId = blockIdStr.get();
656 } else if (key.EqualsLiteral("os")) {
657 aDriverInfo.mOperatingSystem = BlocklistOSToOperatingSystem(dataValue);
658 } else if (key.EqualsLiteral("osversion")) {
659 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
660 } else if (key.EqualsLiteral("desktopEnvironment")) {
661 aDriverInfo.mDesktopEnvironment = dataValue;
662 } else if (key.EqualsLiteral("windowProtocol")) {
663 aDriverInfo.mWindowProtocol = dataValue;
664 } else if (key.EqualsLiteral("vendor")) {
665 aDriverInfo.mAdapterVendor = dataValue;
666 } else if (key.EqualsLiteral("driverVendor")) {
667 aDriverInfo.mDriverVendor = dataValue;
668 } else if (key.EqualsLiteral("feature")) {
669 aDriverInfo.mFeature = BlocklistFeatureToGfxFeature(dataValue);
670 if (aDriverInfo.mFeature < 0) {
671 // If we don't recognize the feature, we do not want to proceed.
672 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
673 << "Unrecognized feature " << value.get();
674 return false;
676 } else if (key.EqualsLiteral("featureStatus")) {
677 aDriverInfo.mFeatureStatus =
678 BlocklistFeatureStatusToGfxFeatureStatus(dataValue);
679 } else if (key.EqualsLiteral("driverVersion")) {
680 uint64_t version;
681 if (ParseDriverVersion(dataValue, &version))
682 aDriverInfo.mDriverVersion = version;
683 } else if (key.EqualsLiteral("driverVersionMax")) {
684 uint64_t version;
685 if (ParseDriverVersion(dataValue, &version))
686 aDriverInfo.mDriverVersionMax = version;
687 } else if (key.EqualsLiteral("driverVersionComparator")) {
688 aDriverInfo.mComparisonOp = BlocklistComparatorToComparisonOp(dataValue);
689 } else if (key.EqualsLiteral("model")) {
690 aDriverInfo.mModel = dataValue;
691 } else if (key.EqualsLiteral("product")) {
692 aDriverInfo.mProduct = dataValue;
693 } else if (key.EqualsLiteral("manufacturer")) {
694 aDriverInfo.mManufacturer = dataValue;
695 } else if (key.EqualsLiteral("hardware")) {
696 aDriverInfo.mHardware = dataValue;
697 } else if (key.EqualsLiteral("versionRange")) {
698 nsTArray<nsCString> versionRange;
699 ParseString(value, ',', versionRange);
700 if (versionRange.Length() != 2) {
701 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
702 << "Unrecognized versionRange " << value.get();
703 return false;
705 const nsCString& minValue = versionRange[0];
706 const nsCString& maxValue = versionRange[1];
708 mozilla::Version minV(minValue.get());
709 mozilla::Version maxV(maxValue.get());
711 if (minV > zeroV && !(appV >= minV)) {
712 // The version of the application is less than the minimal version
713 // this blocklist entry applies to, so we can just ignore it by
714 // returning false and letting the caller deal with it.
715 return false;
717 if (maxV > zeroV && !(appV <= maxV)) {
718 // The version of the application is more than the maximal version
719 // this blocklist entry applies to, so we can just ignore it by
720 // returning false and letting the caller deal with it.
721 return false;
723 } else if (key.EqualsLiteral("devices")) {
724 nsTArray<nsCString> devices;
725 ParseString(value, ',', devices);
726 GfxDeviceFamily* deviceIds = BlocklistDevicesToDeviceFamily(devices);
727 if (deviceIds) {
728 // Get GfxDriverInfo to adopt the devices array we created.
729 aDriverInfo.mDeleteDevices = true;
730 aDriverInfo.mDevices = deviceIds;
733 // We explicitly ignore unknown elements.
736 return true;
739 NS_IMETHODIMP
740 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
741 const char16_t* aData) {
742 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
743 nsTArray<GfxDriverInfo> driverInfo;
744 NS_ConvertUTF16toUTF8 utf8Data(aData);
746 for (const auto& blocklistEntry : utf8Data.Split('\n')) {
747 GfxDriverInfo di;
748 if (BlocklistEntryToDriverInfo(blocklistEntry, di)) {
749 // XXX Changing this to driverInfo.AppendElement(di) causes leaks.
750 // Probably some non-standard semantics of the copy/move operations?
751 *driverInfo.AppendElement() = di;
752 // Prevent di falling out of scope from destroying the devices.
753 di.mDeleteDevices = false;
754 } else {
755 driverInfo.AppendElement();
759 EvaluateDownloadedBlocklist(driverInfo);
762 return NS_OK;
765 GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
767 GfxInfoBase::~GfxInfoBase() = default;
769 nsresult GfxInfoBase::Init() {
770 InitGfxDriverInfoShutdownObserver();
772 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
773 if (os) {
774 os->AddObserver(this, "blocklist-data-gfxItems", true);
777 return NS_OK;
780 void GfxInfoBase::GetData() {
781 if (mScreenPixels != INT64_MAX) {
782 // Already initialized.
783 return;
786 ScreenManager::GetSingleton().GetTotalScreenPixels(&mScreenPixels);
789 NS_IMETHODIMP
790 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
791 int32_t* aStatus) {
792 // Ignore the gfx.blocklist.all pref on release and beta.
793 #if defined(RELEASE_OR_BETA)
794 int32_t blocklistAll = 0;
795 #else
796 int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
797 #endif
798 if (blocklistAll > 0) {
799 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
800 << "Forcing blocklisting all features";
801 *aStatus = FEATURE_BLOCKED_DEVICE;
802 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
803 return NS_OK;
806 if (blocklistAll < 0) {
807 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
808 << "Ignoring any feature blocklisting.";
809 *aStatus = FEATURE_STATUS_OK;
810 return NS_OK;
813 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
814 return NS_OK;
817 if (XRE_IsContentProcess() || XRE_IsGPUProcess()) {
818 // Use the cached data received from the parent process.
819 MOZ_ASSERT(sFeatureStatus);
820 bool success = false;
821 for (const auto& fs : *sFeatureStatus) {
822 if (fs.feature() == aFeature) {
823 aFailureId = fs.failureId();
824 *aStatus = fs.status();
825 success = true;
826 break;
829 return success ? NS_OK : NS_ERROR_FAILURE;
832 nsString version;
833 nsTArray<GfxDriverInfo> driverInfo;
834 nsresult rv =
835 GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
836 return rv;
839 nsTArray<gfx::GfxInfoFeatureStatus> GfxInfoBase::GetAllFeatures() {
840 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
841 if (!sFeatureStatus) {
842 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>());
843 for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
844 int32_t status = 0;
845 nsAutoCString failureId;
846 GetFeatureStatus(i, failureId, &status);
847 gfx::GfxInfoFeatureStatus gfxFeatureStatus;
848 gfxFeatureStatus.feature() = i;
849 gfxFeatureStatus.status() = status;
850 gfxFeatureStatus.failureId() = failureId;
851 sFeatureStatus->AppendElement(gfxFeatureStatus);
855 nsTArray<gfx::GfxInfoFeatureStatus> features;
856 for (const auto& status : *sFeatureStatus) {
857 gfx::GfxInfoFeatureStatus copy = status;
858 features.AppendElement(copy);
860 return features;
863 inline bool MatchingAllowStatus(int32_t aStatus) {
864 switch (aStatus) {
865 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
866 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
867 return true;
868 default:
869 return false;
873 // Matching OS go somewhat beyond the simple equality check because of the
874 // "All Windows" and "All OS X" variations.
876 // aBlockedOS is describing the system(s) we are trying to block.
877 // aSystemOS is describing the system we are running on.
879 // aSystemOS should not be "Windows" or "OSX" - it should be set to
880 // a particular version instead.
881 // However, it is valid for aBlockedOS to be one of those generic values,
882 // as we could be blocking all of the versions.
883 inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
884 OperatingSystem aSystemOS,
885 uint32_t aSystemOSBuild) {
886 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
887 aSystemOS != OperatingSystem::OSX);
889 // If the block entry OS is unknown, it doesn't match
890 if (aBlockedOS == OperatingSystem::Unknown) {
891 return false;
894 #if defined(XP_WIN)
895 if (aBlockedOS == OperatingSystem::Windows) {
896 // We do want even "unknown" aSystemOS to fall under "all windows"
897 return true;
900 constexpr uint32_t kMinWin10BuildNumber = 18362;
901 if (aBlockedOS == OperatingSystem::RecentWindows10 &&
902 aSystemOS == OperatingSystem::Windows10) {
903 // For allowlist purposes, we sometimes want to restrict to only recent
904 // versions of Windows 10. This is a bit of a kludge but easier than adding
905 // complicated blocklist infrastructure for build ID comparisons like driver
906 // versions.
907 return aSystemOSBuild >= kMinWin10BuildNumber;
910 if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
911 if (aSystemOS == OperatingSystem::Windows10) {
912 return aSystemOSBuild < kMinWin10BuildNumber;
913 } else {
914 return true;
917 #endif
919 #if defined(XP_MACOSX)
920 if (aBlockedOS == OperatingSystem::OSX) {
921 // We do want even "unknown" aSystemOS to fall under "all OS X"
922 return true;
924 #endif
926 return aSystemOS == aBlockedOS;
929 inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
930 switch (aBatteryStatus) {
931 case BatteryStatus::All:
932 return true;
933 case BatteryStatus::None:
934 return !aHasBattery;
935 case BatteryStatus::Present:
936 return aHasBattery;
939 MOZ_ASSERT_UNREACHABLE("bad battery status");
940 return false;
943 inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
944 int64_t aScreenPixels) {
945 constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
946 constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
948 switch (aScreenStatus) {
949 case ScreenSizeStatus::All:
950 return true;
951 case ScreenSizeStatus::Small:
952 return aScreenPixels <= kMaxSmallPixels;
953 case ScreenSizeStatus::SmallAndMedium:
954 return aScreenPixels <= kMaxMediumPixels;
955 case ScreenSizeStatus::Medium:
956 return aScreenPixels > kMaxSmallPixels &&
957 aScreenPixels <= kMaxMediumPixels;
958 case ScreenSizeStatus::MediumAndLarge:
959 return aScreenPixels > kMaxSmallPixels;
960 case ScreenSizeStatus::Large:
961 return aScreenPixels > kMaxMediumPixels;
964 MOZ_ASSERT_UNREACHABLE("bad screen status");
965 return false;
968 int32_t GfxInfoBase::FindBlocklistedDeviceInList(
969 const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
970 int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
971 bool aForAllowing) {
972 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
974 // Some properties are not available on all platforms.
975 nsAutoString desktopEnvironment;
976 nsresult rv = GetDesktopEnvironment(desktopEnvironment);
977 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
978 return 0;
981 nsAutoString windowProtocol;
982 rv = GetWindowProtocol(windowProtocol);
983 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
984 return 0;
987 bool hasBattery = false;
988 rv = GetHasBattery(&hasBattery);
989 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
990 return 0;
993 uint32_t osBuild = OperatingSystemBuild();
995 // Get the adapters once then reuse below
996 nsAutoString adapterVendorID[2];
997 nsAutoString adapterDeviceID[2];
998 nsAutoString adapterDriverVendor[2];
999 nsAutoString adapterDriverVersionString[2];
1000 bool adapterInfoFailed[2];
1002 adapterInfoFailed[0] =
1003 (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
1004 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
1005 NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
1006 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
1007 adapterInfoFailed[1] =
1008 (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
1009 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
1010 NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
1011 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
1012 // No point in going on if we don't have adapter info
1013 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
1014 return 0;
1017 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1018 uint64_t driverVersion[2] = {0, 0};
1019 if (!adapterInfoFailed[0]) {
1020 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
1022 if (!adapterInfoFailed[1]) {
1023 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
1025 #endif
1027 uint32_t i = 0;
1028 for (; i < info.Length(); i++) {
1029 // If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
1030 // blocklisting. Only consider entries for our search mode.
1031 if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
1032 continue;
1035 // If we don't have the info for this GPU, no need to check further.
1036 // It is unclear that we would ever have a mixture of 1st and 2nd
1037 // GPU, but leaving the code in for that possibility for now.
1038 // (Actually, currently mGpu2 will never be true, so this can
1039 // be optimized out.)
1040 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
1041 if (adapterInfoFailed[infoIndex]) {
1042 continue;
1045 // Do the operating system check first, no point in getting the driver
1046 // info if we won't need to use it.
1047 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
1048 continue;
1051 if (info[i].mOperatingSystemVersion &&
1052 info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
1053 continue;
1056 if (!MatchingBattery(info[i].mBattery, hasBattery)) {
1057 continue;
1060 if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
1061 continue;
1064 if (!DoesDesktopEnvironmentMatch(info[i].mDesktopEnvironment,
1065 desktopEnvironment)) {
1066 continue;
1069 if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
1070 continue;
1073 if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
1074 continue;
1077 if (!DoesDriverVendorMatch(info[i].mDriverVendor,
1078 adapterDriverVendor[infoIndex])) {
1079 continue;
1082 if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
1083 nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
1084 if (rv == NS_ERROR_NOT_AVAILABLE) {
1085 // Not found
1086 continue;
1088 if (rv != NS_OK) {
1089 // Failed to search, allowlist should not match, blocklist should match
1090 // for safety reasons
1091 if (aForAllowing) {
1092 continue;
1094 break;
1098 bool match = false;
1100 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
1101 continue;
1103 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
1104 continue;
1106 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
1107 continue;
1109 if (!info[i].mManufacturer.IsEmpty() &&
1110 !info[i].mManufacturer.Equals(Manufacturer())) {
1111 continue;
1114 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1115 switch (info[i].mComparisonOp) {
1116 case DRIVER_LESS_THAN:
1117 match = driverVersion[infoIndex] < info[i].mDriverVersion;
1118 break;
1119 case DRIVER_BUILD_ID_LESS_THAN:
1120 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
1121 break;
1122 case DRIVER_LESS_THAN_OR_EQUAL:
1123 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
1124 break;
1125 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
1126 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
1127 break;
1128 case DRIVER_GREATER_THAN:
1129 match = driverVersion[infoIndex] > info[i].mDriverVersion;
1130 break;
1131 case DRIVER_GREATER_THAN_OR_EQUAL:
1132 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
1133 break;
1134 case DRIVER_EQUAL:
1135 match = driverVersion[infoIndex] == info[i].mDriverVersion;
1136 break;
1137 case DRIVER_NOT_EQUAL:
1138 match = driverVersion[infoIndex] != info[i].mDriverVersion;
1139 break;
1140 case DRIVER_BETWEEN_EXCLUSIVE:
1141 match = driverVersion[infoIndex] > info[i].mDriverVersion &&
1142 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1143 break;
1144 case DRIVER_BETWEEN_INCLUSIVE:
1145 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1146 driverVersion[infoIndex] <= info[i].mDriverVersionMax;
1147 break;
1148 case DRIVER_BETWEEN_INCLUSIVE_START:
1149 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1150 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1151 break;
1152 case DRIVER_COMPARISON_IGNORED:
1153 // We don't have a comparison op, so we match everything.
1154 match = true;
1155 break;
1156 default:
1157 NS_WARNING("Bogus op in GfxDriverInfo");
1158 break;
1160 #else
1161 // We don't care what driver version it was. We only check OS version and if
1162 // the device matches.
1163 match = true;
1164 #endif
1166 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
1167 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
1168 info[i].mFeature == aFeature) {
1169 status = info[i].mFeatureStatus;
1170 if (!info[i].mRuleId.IsEmpty()) {
1171 aFailureId = info[i].mRuleId.get();
1172 } else {
1173 aFailureId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID";
1175 break;
1180 #if defined(XP_WIN)
1181 // As a very special case, we block D2D on machines with an NVidia 310M GPU
1182 // as either the primary or secondary adapter. D2D is also blocked when the
1183 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
1184 // If the primary GPU already matched something in the blocklist then we
1185 // ignore this special rule. See bug 1008759.
1186 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
1187 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
1188 if (!adapterInfoFailed[1]) {
1189 nsAString& nvVendorID =
1190 (nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
1191 const nsString nv310mDeviceId = u"0x0A70"_ns;
1192 if (nvVendorID.Equals(adapterVendorID[1],
1193 nsCaseInsensitiveStringComparator) &&
1194 nv310mDeviceId.Equals(adapterDeviceID[1],
1195 nsCaseInsensitiveStringComparator)) {
1196 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1197 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
1202 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
1203 // back to the Windows handler, so we must handle this here.
1204 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
1205 if (info[i].mSuggestedVersion) {
1206 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
1207 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
1208 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
1209 aSuggestedVersion.AppendPrintf(
1210 "%lld.%lld.%lld.%lld",
1211 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
1212 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
1213 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
1214 (info[i].mDriverVersion & 0x000000000000ffff));
1217 #endif
1219 return status;
1222 void GfxInfoBase::SetFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>&& aFS) {
1223 MOZ_ASSERT(!sFeatureStatus);
1224 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>(std::move(aFS)));
1227 bool GfxInfoBase::DoesDesktopEnvironmentMatch(
1228 const nsAString& aBlocklistDesktop, const nsAString& aDesktopEnv) {
1229 return aBlocklistDesktop.Equals(aDesktopEnv,
1230 nsCaseInsensitiveStringComparator) ||
1231 aBlocklistDesktop.Equals(
1232 GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::All),
1233 nsCaseInsensitiveStringComparator);
1236 bool GfxInfoBase::DoesWindowProtocolMatch(
1237 const nsAString& aBlocklistWindowProtocol,
1238 const nsAString& aWindowProtocol) {
1239 return aBlocklistWindowProtocol.Equals(aWindowProtocol,
1240 nsCaseInsensitiveStringComparator) ||
1241 aBlocklistWindowProtocol.Equals(
1242 GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
1243 nsCaseInsensitiveStringComparator);
1246 bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
1247 const nsAString& aAdapterVendor) {
1248 return aBlocklistVendor.Equals(aAdapterVendor,
1249 nsCaseInsensitiveStringComparator) ||
1250 aBlocklistVendor.Equals(
1251 GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
1252 nsCaseInsensitiveStringComparator);
1255 bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
1256 const nsAString& aDriverVendor) {
1257 return aBlocklistVendor.Equals(aDriverVendor,
1258 nsCaseInsensitiveStringComparator) ||
1259 aBlocklistVendor.Equals(
1260 GfxDriverInfo::GetDriverVendor(DriverVendor::All),
1261 nsCaseInsensitiveStringComparator);
1264 bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
1265 return aFeature == nsIGfxInfo::FEATURE_WEBRENDER ||
1266 aFeature == nsIGfxInfo::FEATURE_VIDEO_OVERLAY ||
1267 aFeature == nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY ||
1268 aFeature == nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE;
1271 nsresult GfxInfoBase::GetFeatureStatusImpl(
1272 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
1273 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
1274 OperatingSystem* aOS /* = nullptr */) {
1275 if (aFeature <= 0) {
1276 gfxWarning() << "Invalid feature <= 0";
1277 return NS_OK;
1280 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1281 // Terminate now with the status determined by the derived type (OS-specific
1282 // code).
1283 return NS_OK;
1286 if (sShutdownOccurred) {
1287 // This is futile; we've already commenced shutdown and our blocklists have
1288 // been deleted. We may want to look into resurrecting the blocklist instead
1289 // but for now, just don't even go there.
1290 return NS_OK;
1293 // Ensure any additional initialization required is complete.
1294 GetData();
1296 // If an operating system was provided by the derived GetFeatureStatusImpl,
1297 // grab it here. Otherwise, the OS is unknown.
1298 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
1300 nsAutoString adapterVendorID;
1301 nsAutoString adapterDeviceID;
1302 nsAutoString adapterDriverVersionString;
1303 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
1304 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
1305 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
1306 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
1307 *aStatus = FEATURE_BLOCKED_DEVICE;
1308 return NS_OK;
1311 // Check if the device is blocked from the downloaded blocklist. If not, check
1312 // the static list after that. This order is used so that we can later escape
1313 // out of static blocks (i.e. if we were wrong or something was patched, we
1314 // can back out our static block without doing a release).
1315 int32_t status;
1316 if (aDriverInfo.Length()) {
1317 status =
1318 FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
1319 aFailureId, os, /* aForAllowing */ false);
1320 } else {
1321 if (!sDriverInfo) {
1322 sDriverInfo = new nsTArray<GfxDriverInfo>();
1324 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
1325 aFeature, aFailureId, os,
1326 /* aForAllowing */ false);
1329 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1330 if (IsFeatureAllowlisted(aFeature)) {
1331 // This feature is actually using the allowlist; that means after we pass
1332 // the blocklist to prevent us explicitly from getting the feature, we now
1333 // need to check the allowlist to ensure we are allowed to get it in the
1334 // first place.
1335 if (aDriverInfo.Length()) {
1336 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
1337 aFeature, aFailureId, os,
1338 /* aForAllowing */ true);
1339 } else {
1340 status = FindBlocklistedDeviceInList(
1341 GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
1342 /* aForAllowing */ true);
1345 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1346 status = nsIGfxInfo::FEATURE_DENIED;
1348 } else {
1349 // It's now done being processed. It's safe to set the status to
1350 // STATUS_OK.
1351 status = nsIGfxInfo::FEATURE_STATUS_OK;
1355 *aStatus = status;
1356 return NS_OK;
1359 NS_IMETHODIMP
1360 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
1361 nsAString& aVersion) {
1362 nsCString version;
1363 if (GetPrefValueForDriverVersion(version)) {
1364 aVersion = NS_ConvertASCIItoUTF16(version);
1365 return NS_OK;
1368 int32_t status;
1369 nsCString discardFailureId;
1370 nsTArray<GfxDriverInfo> driverInfo;
1371 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
1372 discardFailureId);
1375 void GfxInfoBase::EvaluateDownloadedBlocklist(
1376 nsTArray<GfxDriverInfo>& aDriverInfo) {
1377 int32_t features[] = {nsIGfxInfo::FEATURE_DIRECT2D,
1378 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
1379 nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
1380 nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
1381 nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
1382 nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
1383 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1384 nsIGfxInfo::FEATURE_OPENGL_LAYERS,
1385 nsIGfxInfo::FEATURE_WEBGL_OPENGL,
1386 nsIGfxInfo::FEATURE_WEBGL_ANGLE,
1387 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
1388 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
1389 nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA,
1390 nsIGfxInfo::FEATURE_STAGEFRIGHT,
1391 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264,
1392 nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
1393 nsIGfxInfo::FEATURE_VP8_HW_DECODE,
1394 nsIGfxInfo::FEATURE_VP9_HW_DECODE,
1395 nsIGfxInfo::FEATURE_DX_INTEROP2,
1396 nsIGfxInfo::FEATURE_GPU_PROCESS,
1397 nsIGfxInfo::FEATURE_WEBGL2,
1398 nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX,
1399 nsIGfxInfo::FEATURE_WEBRENDER,
1400 nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
1401 nsIGfxInfo::FEATURE_DX_NV12,
1402 nsIGfxInfo::FEATURE_DX_P010,
1403 nsIGfxInfo::FEATURE_DX_P016,
1404 nsIGfxInfo::FEATURE_GL_SWIZZLE,
1405 nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS,
1406 nsIGfxInfo::FEATURE_X11_EGL,
1407 nsIGfxInfo::FEATURE_DMABUF,
1408 nsIGfxInfo::FEATURE_WEBGPU,
1409 nsIGfxInfo::FEATURE_VIDEO_OVERLAY,
1410 nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY,
1411 nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE,
1412 nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
1413 nsIGfxInfo::FEATURE_BACKDROP_FILTER,
1416 // For every feature we know about, we evaluate whether this blocklist has a
1417 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1418 // GetFeatureStatus above, so we don't need to hold on to this blocklist
1419 // anywhere permanent.
1420 int i = 0;
1421 while (features[i]) {
1422 int32_t status;
1423 nsCString failureId;
1424 nsAutoString suggestedVersion;
1425 if (NS_SUCCEEDED(GetFeatureStatusImpl(
1426 features[i], &status, suggestedVersion, aDriverInfo, failureId))) {
1427 switch (status) {
1428 default:
1429 case nsIGfxInfo::FEATURE_STATUS_OK:
1430 RemovePrefForFeature(features[i]);
1431 break;
1433 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1434 if (!suggestedVersion.IsEmpty()) {
1435 SetPrefValueForDriverVersion(suggestedVersion);
1436 } else {
1437 RemovePrefForDriverVersion();
1439 [[fallthrough]];
1441 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1442 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1443 case nsIGfxInfo::FEATURE_DISCOURAGED:
1444 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1445 SetPrefValueForFeature(features[i], status, failureId);
1446 break;
1450 ++i;
1454 NS_IMETHODIMP_(void)
1455 GfxInfoBase::LogFailure(const nsACString& failure) {
1456 // gfxCriticalError has a mutex lock of its own, so we may not actually
1457 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1458 // will not return the copy of the logs unless it can get the same lock
1459 // that gfxCriticalError uses. Still, that is so much of an implementation
1460 // detail that it's nicer to just add an extra lock here and in
1461 // ::GetFailures()
1462 MutexAutoLock lock(mMutex);
1464 // By default, gfxCriticalError asserts; make it not assert in this case.
1465 gfxCriticalError(CriticalLog::DefaultOptions(false))
1466 << "(LF) " << failure.BeginReading();
1469 NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
1470 nsTArray<nsCString>& failures) {
1471 MutexAutoLock lock(mMutex);
1473 LogForwarder* logForwarder = Factory::GetLogForwarder();
1474 if (!logForwarder) {
1475 return NS_ERROR_UNEXPECTED;
1478 // There are two string copies in this method, starting with this one. We are
1479 // assuming this is not a big deal, as the size of the array should be small
1480 // and the strings in it should be small as well (the error messages in the
1481 // code.) The second copy happens with the AppendElement() calls.
1482 // Technically, we don't need the mutex lock after the StringVectorCopy()
1483 // call.
1484 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1485 LoggingRecord::const_iterator it;
1486 for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
1487 failures.AppendElement(
1488 nsDependentCSubstring(Get<1>(*it).c_str(), Get<1>(*it).size()));
1489 indices.AppendElement(Get<0>(*it));
1492 return NS_OK;
1495 nsTArray<GfxInfoCollectorBase*>* sCollectors;
1497 static void InitCollectors() {
1498 if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1501 nsresult GfxInfoBase::GetInfo(JSContext* aCx,
1502 JS::MutableHandle<JS::Value> aResult) {
1503 InitCollectors();
1504 InfoObject obj(aCx);
1506 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1507 (*sCollectors)[i]->GetInfo(obj);
1510 // Some example property definitions
1511 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1512 // obj.DefineProperty("renderer", mRendererIDsString);
1513 // obj.DefineProperty("five", 5);
1515 if (!obj.mOk) {
1516 return NS_ERROR_FAILURE;
1519 aResult.setObject(*obj.mObj);
1520 return NS_OK;
1523 nsAutoCString gBaseAppVersion;
1525 const nsCString& GfxInfoBase::GetApplicationVersion() {
1526 static bool versionInitialized = false;
1527 if (!versionInitialized) {
1528 // If we fail to get the version, we will not try again.
1529 versionInitialized = true;
1531 // Get the version from xpcom/system/nsIXULAppInfo.idl
1532 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1533 if (app) {
1534 app->GetVersion(gBaseAppVersion);
1537 return gBaseAppVersion;
1540 void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
1541 InitCollectors();
1542 sCollectors->AppendElement(collector);
1545 void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
1546 InitCollectors();
1547 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1548 if ((*sCollectors)[i] == collector) {
1549 sCollectors->RemoveElementAt(i);
1550 break;
1553 if (sCollectors->IsEmpty()) {
1554 delete sCollectors;
1555 sCollectors = nullptr;
1559 static void AppendMonitor(JSContext* aCx, widget::Screen& aScreen,
1560 JS::Handle<JSObject*> aOutArray, int32_t aIndex) {
1561 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1563 auto screenSize = aScreen.GetRect().Size();
1565 JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
1566 JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
1568 JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
1569 JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
1571 // XXX Just preserving behavior since this is exposed to telemetry, but we
1572 // could consider including this everywhere.
1573 #ifdef XP_MACOSX
1574 JS::Rooted<JS::Value> scale(
1575 aCx, JS::NumberValue(aScreen.GetContentsScaleFactor()));
1576 JS_SetProperty(aCx, obj, "scale", scale);
1577 #endif
1579 #ifdef XP_WIN
1580 JS::Rooted<JS::Value> refreshRate(aCx,
1581 JS::Int32Value(aScreen.GetRefreshRate()));
1582 JS_SetProperty(aCx, obj, "refreshRate", refreshRate);
1584 JS::Rooted<JS::Value> pseudoDisplay(
1585 aCx, JS::BooleanValue(aScreen.GetIsPseudoDisplay()));
1586 JS_SetProperty(aCx, obj, "pseudoDisplay", pseudoDisplay);
1587 #endif
1589 JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
1590 JS_SetElement(aCx, aOutArray, aIndex, element);
1593 nsresult GfxInfoBase::FindMonitors(JSContext* aCx,
1594 JS::Handle<JSObject*> aOutArray) {
1595 int32_t index = 0;
1596 auto& sm = ScreenManager::GetSingleton();
1597 for (auto& screen : sm.CurrentScreenList()) {
1598 AppendMonitor(aCx, *screen, aOutArray, index++);
1601 if (index == 0) {
1602 // Ensure we return at least one monitor, this is needed for xpcshell.
1603 RefPtr<Screen> screen = sm.GetPrimaryScreen();
1604 AppendMonitor(aCx, *screen, aOutArray, index++);
1607 return NS_OK;
1610 NS_IMETHODIMP
1611 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
1612 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1614 nsresult rv = FindMonitors(aCx, array);
1615 if (NS_FAILED(rv)) {
1616 return rv;
1619 aResult.setObject(*array);
1620 return NS_OK;
1623 static inline bool SetJSPropertyString(JSContext* aCx,
1624 JS::Handle<JSObject*> aObj,
1625 const char* aProp, const char* aString) {
1626 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1627 if (!str) {
1628 return false;
1631 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1632 return JS_SetProperty(aCx, aObj, aProp, val);
1635 template <typename T>
1636 static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
1637 const T& aValue) {
1638 uint32_t index;
1639 if (!JS::GetArrayLength(aCx, aObj, &index)) {
1640 return false;
1642 return JS_SetElement(aCx, aObj, index, aValue);
1645 nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
1646 JS::MutableHandle<JS::Value> aOut) {
1647 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1648 if (!obj) {
1649 return NS_ERROR_OUT_OF_MEMORY;
1651 aOut.setObject(*obj);
1653 layers::LayersBackend backend =
1654 gfxPlatform::Initialized()
1655 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1656 : layers::LayersBackend::LAYERS_NONE;
1657 const char* backendName = layers::GetLayersBackendName(backend);
1658 SetJSPropertyString(aCx, obj, "compositor", backendName);
1660 // If graphics isn't initialized yet, just stop now.
1661 if (!gfxPlatform::Initialized()) {
1662 return NS_OK;
1665 DescribeFeatures(aCx, obj);
1666 return NS_OK;
1669 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
1670 JS::MutableHandle<JS::Value> aOut) {
1671 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1672 if (!containerObj) {
1673 return NS_ERROR_OUT_OF_MEMORY;
1675 aOut.setObject(*containerObj);
1677 JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
1678 if (!featureArray) {
1679 return NS_ERROR_OUT_OF_MEMORY;
1682 // Collect features.
1683 gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
1684 FeatureState& aFeature) -> void {
1685 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1686 if (!obj) {
1687 return;
1689 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1690 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1691 !SetJSPropertyString(aCx, obj, "status",
1692 FeatureStatusToString(aFeature.GetValue()))) {
1693 return;
1696 JS::Rooted<JS::Value> log(aCx);
1697 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1698 return;
1700 if (!JS_SetProperty(aCx, obj, "log", log)) {
1701 return;
1704 if (!AppendJSElement(aCx, featureArray, obj)) {
1705 return;
1709 JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
1710 if (!fallbackArray) {
1711 return NS_ERROR_OUT_OF_MEMORY;
1714 // Collect fallbacks.
1715 gfxConfig::ForEachFallback(
1716 [&](const char* aName, const char* aMessage) -> void {
1717 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1718 if (!obj) {
1719 return;
1722 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1723 !SetJSPropertyString(aCx, obj, "message", aMessage)) {
1724 return;
1727 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1728 return;
1732 JS::Rooted<JS::Value> val(aCx);
1734 val = JS::ObjectValue(*featureArray);
1735 JS_SetProperty(aCx, containerObj, "features", val);
1737 val = JS::ObjectValue(*fallbackArray);
1738 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1740 return NS_OK;
1743 bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
1744 const FeatureState& aFeature,
1745 JS::MutableHandle<JS::Value> aOut) {
1746 JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
1747 if (!log) {
1748 return false;
1750 aOut.setObject(*log);
1752 aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
1753 const char* aMessage,
1754 const nsCString& aFailureId) -> void {
1755 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1756 if (!obj) {
1757 return;
1760 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1761 !SetJSPropertyString(aCx, obj, "status",
1762 FeatureStatusToString(aStatus)) ||
1763 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
1764 return;
1767 if (!AppendJSElement(aCx, log, obj)) {
1768 return;
1772 return true;
1775 void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
1776 JS::Rooted<JSObject*> obj(aCx);
1778 gfx::FeatureState& hwCompositing =
1779 gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
1780 InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
1782 gfx::FeatureState& gpuProcess =
1783 gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
1784 InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
1786 gfx::FeatureState& wrQualified =
1787 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_QUALIFIED);
1788 InitFeatureObject(aCx, aObj, "wrQualified", wrQualified, &obj);
1790 gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
1791 InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
1793 gfx::FeatureState& wrCompositor =
1794 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
1795 InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
1797 gfx::FeatureState& wrSoftware =
1798 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_SOFTWARE);
1799 InitFeatureObject(aCx, aObj, "wrSoftware", wrSoftware, &obj);
1801 gfx::FeatureState& openglCompositing =
1802 gfxConfig::GetFeature(gfx::Feature::OPENGL_COMPOSITING);
1803 InitFeatureObject(aCx, aObj, "openglCompositing", openglCompositing, &obj);
1805 gfx::FeatureState& omtp = gfxConfig::GetFeature(gfx::Feature::OMTP);
1806 InitFeatureObject(aCx, aObj, "omtp", omtp, &obj);
1809 bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
1810 JS::Handle<JSObject*> aContainer,
1811 const char* aName,
1812 mozilla::gfx::FeatureState& aFeatureState,
1813 JS::MutableHandle<JSObject*> aOutObj) {
1814 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1815 if (!obj) {
1816 return false;
1819 nsCString status = aFeatureState.GetStatusAndFailureIdString();
1821 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
1822 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1823 JS_SetProperty(aCx, obj, "status", val);
1825 // Add the feature object to the container.
1827 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1828 JS_SetProperty(aCx, aContainer, aName, val);
1831 aOutObj.set(obj);
1832 return true;
1835 nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
1836 JS::MutableHandle<JS::Value> aOut) {
1837 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1838 if (!array) {
1839 return NS_ERROR_OUT_OF_MEMORY;
1841 aOut.setObject(*array);
1843 DriverCrashGuard::ForEachActiveCrashGuard(
1844 [&](const char* aName, const char* aPrefName) -> void {
1845 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1846 if (!obj) {
1847 return;
1849 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1850 return;
1852 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1853 return;
1855 if (!AppendJSElement(aCx, array, obj)) {
1856 return;
1860 return NS_OK;
1863 NS_IMETHODIMP
1864 GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled) {
1865 *aWebRenderEnabled = gfxVars::UseWebRender();
1866 return NS_OK;
1869 NS_IMETHODIMP
1870 GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
1871 *aTargetFrameRate = gfxPlatform::TargetFrameRate();
1872 return NS_OK;
1875 NS_IMETHODIMP
1876 GfxInfoBase::GetCodecSupportInfo(nsACString& aCodecSupportInfo) {
1877 aCodecSupportInfo.Assign(gfx::gfxVars::CodecSupportInfo());
1878 return NS_OK;
1881 NS_IMETHODIMP
1882 GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
1883 *aIsHeadless = gfxPlatform::IsHeadless();
1884 return NS_OK;
1887 NS_IMETHODIMP
1888 GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
1889 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1890 nsString outStr;
1892 switch (backend) {
1893 case BackendType::DIRECT2D1_1: {
1894 outStr.AppendPrintf("Direct2D 1.1");
1895 break;
1897 case BackendType::SKIA: {
1898 outStr.AppendPrintf("Skia");
1899 break;
1901 case BackendType::CAIRO: {
1902 outStr.AppendPrintf("Cairo");
1903 break;
1905 default:
1906 return NS_ERROR_FAILURE;
1909 aContentBackend.Assign(outStr);
1910 return NS_OK;
1913 NS_IMETHODIMP
1914 GfxInfoBase::GetAzureCanvasBackend(nsAString& aBackend) {
1915 CopyASCIItoUTF16(mozilla::MakeStringSpan(
1916 gfxPlatform::GetPlatform()->GetAzureCanvasBackend()),
1917 aBackend);
1918 return NS_OK;
1921 NS_IMETHODIMP
1922 GfxInfoBase::GetAzureContentBackend(nsAString& aBackend) {
1923 CopyASCIItoUTF16(mozilla::MakeStringSpan(
1924 gfxPlatform::GetPlatform()->GetAzureContentBackend()),
1925 aBackend);
1926 return NS_OK;
1929 NS_IMETHODIMP
1930 GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
1931 GPUProcessManager* gpu = GPUProcessManager::Get();
1932 if (!gpu) {
1933 // Not supported in content processes.
1934 return NS_ERROR_FAILURE;
1937 *aOutValue = !!gpu->GetGPUChild();
1938 return NS_OK;
1941 NS_IMETHODIMP_(int32_t)
1942 GfxInfoBase::GetMaxRefreshRate(bool* aMixed) {
1943 if (aMixed) {
1944 *aMixed = false;
1947 int32_t maxRefreshRate = 0;
1948 for (auto& screen : ScreenManager::GetSingleton().CurrentScreenList()) {
1949 int32_t refreshRate = screen->GetRefreshRate();
1950 if (aMixed && maxRefreshRate > 0 && maxRefreshRate != refreshRate) {
1951 *aMixed = true;
1953 maxRefreshRate = std::max(maxRefreshRate, refreshRate);
1956 return maxRefreshRate > 0 ? maxRefreshRate : -1;
1959 NS_IMETHODIMP
1960 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
1961 gfxPlatform::GetPlatform();
1963 GPUProcessManager* gpm = GPUProcessManager::Get();
1964 if (aEnable) {
1965 if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
1966 gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1968 gpm->EnsureGPUReady();
1969 } else {
1970 gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1971 gpm->KillProcess();
1974 *_retval = true;
1975 return NS_OK;
1978 NS_IMETHODIMP GfxInfoBase::KillGPUProcessForTests() {
1979 GPUProcessManager* gpm = GPUProcessManager::Get();
1980 if (!gpm) {
1981 // gfxPlatform has not been initialized.
1982 return NS_ERROR_NOT_INITIALIZED;
1985 gpm->KillProcess();
1986 return NS_OK;
1989 NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
1990 GPUProcessManager* gpm = GPUProcessManager::Get();
1991 if (!gpm) {
1992 // gfxPlatform has not been initialized.
1993 return NS_ERROR_NOT_INITIALIZED;
1996 gpm->CrashProcess();
1997 return NS_OK;
2000 GfxInfoCollectorBase::GfxInfoCollectorBase() {
2001 GfxInfoBase::AddCollector(this);
2004 GfxInfoCollectorBase::~GfxInfoCollectorBase() {
2005 GfxInfoBase::RemoveCollector(this);