Bug 1892041 - Part 3: Update test exclusions. r=spidermonkey-reviewers,dminor
[gecko.git] / widget / GfxInfoBase.cpp
blob251b46ce661bea523a5e693bc19f36fd640a60eb
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/BinarySearch.h"
30 #include "mozilla/ClearOnShutdown.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/StaticPrefs_gfx.h"
33 #include "mozilla/gfx/2D.h"
34 #include "mozilla/gfx/BuildConstants.h"
35 #include "mozilla/gfx/GPUProcessManager.h"
36 #include "mozilla/gfx/Logging.h"
37 #include "mozilla/gfx/gfxVars.h"
38 #include "mozilla/widget/ScreenManager.h"
39 #include "mozilla/widget/Screen.h"
41 #include "jsapi.h"
43 #include "gfxPlatform.h"
44 #include "gfxConfig.h"
45 #include "DriverCrashGuard.h"
47 #ifdef MOZ_WIDGET_ANDROID
48 # include <set>
49 # include "AndroidBuild.h"
50 #endif
52 using namespace mozilla::widget;
53 using namespace mozilla;
54 using mozilla::MutexAutoLock;
56 nsTArray<GfxDriverInfo>* GfxInfoBase::sDriverInfo;
57 StaticAutoPtr<nsTArray<gfx::GfxInfoFeatureStatus>> GfxInfoBase::sFeatureStatus;
58 bool GfxInfoBase::sDriverInfoObserverInitialized;
59 bool GfxInfoBase::sShutdownOccurred;
61 // Call this when setting sFeatureStatus to a non-null pointer to
62 // ensure destruction even if the GfxInfo component is never instantiated.
63 static void InitFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>* aPtr) {
64 static std::once_flag sOnce;
65 std::call_once(sOnce, [] { ClearOnShutdown(&GfxInfoBase::sFeatureStatus); });
66 GfxInfoBase::sFeatureStatus = aPtr;
69 // Observes for shutdown so that the child GfxDriverInfo list is freed.
70 class ShutdownObserver : public nsIObserver {
71 virtual ~ShutdownObserver() = default;
73 public:
74 ShutdownObserver() = default;
76 NS_DECL_ISUPPORTS
78 NS_IMETHOD Observe(nsISupports* subject, const char* aTopic,
79 const char16_t* aData) override {
80 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
82 delete GfxInfoBase::sDriverInfo;
83 GfxInfoBase::sDriverInfo = nullptr;
85 for (auto& deviceFamily : GfxDriverInfo::sDeviceFamilies) {
86 delete deviceFamily;
87 deviceFamily = nullptr;
90 for (auto& windowProtocol : GfxDriverInfo::sWindowProtocol) {
91 delete windowProtocol;
92 windowProtocol = nullptr;
95 for (auto& deviceVendor : GfxDriverInfo::sDeviceVendors) {
96 delete deviceVendor;
97 deviceVendor = nullptr;
100 for (auto& driverVendor : GfxDriverInfo::sDriverVendors) {
101 delete driverVendor;
102 driverVendor = nullptr;
105 GfxInfoBase::sShutdownOccurred = true;
107 return NS_OK;
111 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
113 static void InitGfxDriverInfoShutdownObserver() {
114 if (GfxInfoBase::sDriverInfoObserverInitialized) return;
116 GfxInfoBase::sDriverInfoObserverInitialized = true;
118 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
119 if (!observerService) {
120 NS_WARNING("Could not get observer service!");
121 return;
124 ShutdownObserver* obs = new ShutdownObserver();
125 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
128 using namespace mozilla::widget;
129 using namespace mozilla::gfx;
130 using namespace mozilla;
132 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver,
133 nsISupportsWeakReference)
135 #define BLOCKLIST_PREF_BRANCH "gfx.blacklist."
136 #define SUGGESTED_VERSION_PREF BLOCKLIST_PREF_BRANCH "suggested-driver-version"
138 static const char* GetPrefNameForFeature(int32_t aFeature) {
139 const char* name = nullptr;
140 switch (aFeature) {
141 case nsIGfxInfo::FEATURE_DIRECT2D:
142 name = BLOCKLIST_PREF_BRANCH "direct2d";
143 break;
144 case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
145 name = BLOCKLIST_PREF_BRANCH "layers.direct3d9";
146 break;
147 case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
148 name = BLOCKLIST_PREF_BRANCH "layers.direct3d10";
149 break;
150 case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
151 name = BLOCKLIST_PREF_BRANCH "layers.direct3d10-1";
152 break;
153 case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
154 name = BLOCKLIST_PREF_BRANCH "layers.direct3d11";
155 break;
156 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
157 name = BLOCKLIST_PREF_BRANCH "direct3d11angle";
158 break;
159 case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
160 name = BLOCKLIST_PREF_BRANCH "hardwarevideodecoding";
161 break;
162 case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
163 name = BLOCKLIST_PREF_BRANCH "layers.opengl";
164 break;
165 case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
166 name = BLOCKLIST_PREF_BRANCH "webgl.opengl";
167 break;
168 case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
169 name = BLOCKLIST_PREF_BRANCH "webgl.angle";
170 break;
171 case nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA:
172 name = BLOCKLIST_PREF_BRANCH "webgl.msaa";
173 break;
174 case nsIGfxInfo::FEATURE_STAGEFRIGHT:
175 name = BLOCKLIST_PREF_BRANCH "stagefright";
176 break;
177 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264:
178 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.h264";
179 break;
180 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
181 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
182 break;
183 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
184 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
185 break;
186 case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
187 name = BLOCKLIST_PREF_BRANCH "canvas2d.acceleration";
188 break;
189 case nsIGfxInfo::FEATURE_DX_INTEROP2:
190 name = BLOCKLIST_PREF_BRANCH "dx.interop2";
191 break;
192 case nsIGfxInfo::FEATURE_GPU_PROCESS:
193 name = BLOCKLIST_PREF_BRANCH "gpu.process";
194 break;
195 case nsIGfxInfo::FEATURE_WEBGL2:
196 name = BLOCKLIST_PREF_BRANCH "webgl2";
197 break;
198 case nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX:
199 name = BLOCKLIST_PREF_BRANCH "d3d11.keyed.mutex";
200 break;
201 case nsIGfxInfo::FEATURE_WEBRENDER:
202 name = BLOCKLIST_PREF_BRANCH "webrender";
203 break;
204 case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
205 name = BLOCKLIST_PREF_BRANCH "webrender.compositor";
206 break;
207 case nsIGfxInfo::FEATURE_DX_NV12:
208 name = BLOCKLIST_PREF_BRANCH "dx.nv12";
209 break;
210 case nsIGfxInfo::FEATURE_DX_P010:
211 name = BLOCKLIST_PREF_BRANCH "dx.p010";
212 break;
213 case nsIGfxInfo::FEATURE_DX_P016:
214 name = BLOCKLIST_PREF_BRANCH "dx.p016";
215 break;
216 case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
217 name = BLOCKLIST_PREF_BRANCH "vp8.hw-decode";
218 break;
219 case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
220 name = BLOCKLIST_PREF_BRANCH "vp9.hw-decode";
221 break;
222 case nsIGfxInfo::FEATURE_GL_SWIZZLE:
223 name = BLOCKLIST_PREF_BRANCH "gl.swizzle";
224 break;
225 case nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS:
226 name = BLOCKLIST_PREF_BRANCH "webrender.scissored_cache_clears";
227 break;
228 case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
229 name = BLOCKLIST_PREF_BRANCH "webgl.allow-oop";
230 break;
231 case nsIGfxInfo::FEATURE_THREADSAFE_GL:
232 name = BLOCKLIST_PREF_BRANCH "gl.threadsafe";
233 break;
234 case nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS:
235 name = BLOCKLIST_PREF_BRANCH "webrender.optimized-shaders";
236 break;
237 case nsIGfxInfo::FEATURE_X11_EGL:
238 name = BLOCKLIST_PREF_BRANCH "x11.egl";
239 break;
240 case nsIGfxInfo::FEATURE_DMABUF:
241 name = BLOCKLIST_PREF_BRANCH "dmabuf";
242 break;
243 case nsIGfxInfo::FEATURE_WEBGPU:
244 name = BLOCKLIST_PREF_BRANCH "webgpu";
245 break;
246 case nsIGfxInfo::FEATURE_VIDEO_OVERLAY:
247 name = BLOCKLIST_PREF_BRANCH "video-overlay";
248 break;
249 case nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY:
250 name = BLOCKLIST_PREF_BRANCH "hw-video-zero-copy";
251 break;
252 case nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE:
253 name = BLOCKLIST_PREF_BRANCH "webrender.program-binary-disk";
254 break;
255 case nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT:
256 name = BLOCKLIST_PREF_BRANCH "webrender.partial-present";
257 break;
258 case nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT:
259 name = BLOCKLIST_PREF_BRANCH "dmabuf.surface-export";
260 break;
261 case nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE:
262 name = BLOCKLIST_PREF_BRANCH "reuse-decoder-device";
263 break;
264 case nsIGfxInfo::FEATURE_BACKDROP_FILTER:
265 name = BLOCKLIST_PREF_BRANCH "backdrop.filter";
266 break;
267 case nsIGfxInfo::FEATURE_ACCELERATED_CANVAS2D:
268 name = BLOCKLIST_PREF_BRANCH "accelerated-canvas2d";
269 break;
270 case nsIGfxInfo::FEATURE_H264_HW_DECODE:
271 name = BLOCKLIST_PREF_BRANCH "h264.hw-decode";
272 break;
273 case nsIGfxInfo::FEATURE_AV1_HW_DECODE:
274 name = BLOCKLIST_PREF_BRANCH "av1.hw-decode";
275 break;
276 case nsIGfxInfo::FEATURE_VIDEO_SOFTWARE_OVERLAY:
277 name = BLOCKLIST_PREF_BRANCH "video-software-overlay";
278 break;
279 case nsIGfxInfo::FEATURE_WEBGL_USE_HARDWARE:
280 name = BLOCKLIST_PREF_BRANCH "webgl-use-hardware";
281 break;
282 case nsIGfxInfo::FEATURE_OVERLAY_VP_AUTO_HDR:
283 name = BLOCKLIST_PREF_BRANCH "overlay-vp-auto-hdr";
284 break;
285 case nsIGfxInfo::FEATURE_OVERLAY_VP_SUPER_RESOLUTION:
286 name = BLOCKLIST_PREF_BRANCH "overlay-vp-super-resolution";
287 break;
288 default:
289 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
290 break;
293 return name;
296 // Returns the value of the pref for the relevant feature in aValue.
297 // If the pref doesn't exist, aValue is not touched, and returns false.
298 static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
299 nsACString& aFailureId) {
300 const char* prefname = GetPrefNameForFeature(aFeature);
301 if (!prefname) return false;
303 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
304 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
305 return false;
308 if (aValue == nsIGfxInfo::FEATURE_DENIED) {
309 // We should never see the DENIED status with the downloadable blocklist.
310 return false;
313 nsCString failureprefname(prefname);
314 failureprefname += ".failureid";
315 nsAutoCString failureValue;
316 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
317 if (NS_SUCCEEDED(rv)) {
318 aFailureId = failureValue.get();
319 } else {
320 aFailureId = "FEATURE_FAILURE_BLOCKLIST_PREF";
323 return true;
326 static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
327 const nsACString& aFailureId) {
328 const char* prefname = GetPrefNameForFeature(aFeature);
329 if (!prefname) return;
330 if (XRE_IsParentProcess()) {
331 GfxInfoBase::sFeatureStatus = nullptr;
334 Preferences::SetInt(prefname, aValue);
335 if (!aFailureId.IsEmpty()) {
336 nsAutoCString failureprefname(prefname);
337 failureprefname += ".failureid";
338 Preferences::SetCString(failureprefname.get(), aFailureId);
342 static void RemovePrefForFeature(int32_t aFeature) {
343 const char* prefname = GetPrefNameForFeature(aFeature);
344 if (!prefname) return;
346 if (XRE_IsParentProcess()) {
347 GfxInfoBase::sFeatureStatus = nullptr;
349 Preferences::ClearUser(prefname);
352 static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
353 return NS_SUCCEEDED(
354 Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
357 static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
358 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
361 static void RemovePrefForDriverVersion() {
362 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
365 static OperatingSystem BlocklistOSToOperatingSystem(const nsAString& os) {
366 if (os.EqualsLiteral("WINNT 6.1")) {
367 return OperatingSystem::Windows7;
369 if (os.EqualsLiteral("WINNT 6.2")) {
370 return OperatingSystem::Windows8;
372 if (os.EqualsLiteral("WINNT 6.3")) {
373 return OperatingSystem::Windows8_1;
375 if (os.EqualsLiteral("WINNT 10.0")) {
376 return OperatingSystem::Windows10;
378 if (os.EqualsLiteral("Linux")) {
379 return OperatingSystem::Linux;
381 if (os.EqualsLiteral("Darwin 9")) {
382 return OperatingSystem::OSX10_5;
384 if (os.EqualsLiteral("Darwin 10")) {
385 return OperatingSystem::OSX10_6;
387 if (os.EqualsLiteral("Darwin 11")) {
388 return OperatingSystem::OSX10_7;
390 if (os.EqualsLiteral("Darwin 12")) {
391 return OperatingSystem::OSX10_8;
393 if (os.EqualsLiteral("Darwin 13")) {
394 return OperatingSystem::OSX10_9;
396 if (os.EqualsLiteral("Darwin 14")) {
397 return OperatingSystem::OSX10_10;
399 if (os.EqualsLiteral("Darwin 15")) {
400 return OperatingSystem::OSX10_11;
402 if (os.EqualsLiteral("Darwin 16")) {
403 return OperatingSystem::OSX10_12;
405 if (os.EqualsLiteral("Darwin 17")) {
406 return OperatingSystem::OSX10_13;
408 if (os.EqualsLiteral("Darwin 18")) {
409 return OperatingSystem::OSX10_14;
411 if (os.EqualsLiteral("Darwin 19")) {
412 return OperatingSystem::OSX10_15;
414 if (os.EqualsLiteral("Darwin 20")) {
415 return OperatingSystem::OSX11_0;
417 if (os.EqualsLiteral("Android")) {
418 return OperatingSystem::Android;
419 // For historical reasons, "All" in blocklist means "All Windows"
421 if (os.EqualsLiteral("All")) {
422 return OperatingSystem::Windows;
424 if (os.EqualsLiteral("Darwin")) {
425 return OperatingSystem::OSX;
428 return OperatingSystem::Unknown;
431 static GfxDeviceFamily* BlocklistDevicesToDeviceFamily(
432 nsTArray<nsCString>& devices) {
433 if (devices.Length() == 0) return nullptr;
435 // For each device, get its device ID, and return a freshly-allocated
436 // GfxDeviceFamily with the contents of that array.
437 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
439 for (uint32_t i = 0; i < devices.Length(); ++i) {
440 // We make sure we don't add any "empty" device entries to the array, so
441 // we don't need to check if devices[i] is empty.
442 deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
445 return deviceIds;
448 static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) {
449 MOZ_ASSERT(!aFeature.IsEmpty());
450 if (aFeature.EqualsLiteral("DIRECT2D")) {
451 return nsIGfxInfo::FEATURE_DIRECT2D;
453 if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS")) {
454 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
456 if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS")) {
457 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
459 if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS")) {
460 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
462 if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS")) {
463 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
465 if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE")) {
466 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
468 if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING")) {
469 return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
471 if (aFeature.EqualsLiteral("OPENGL_LAYERS")) {
472 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
474 if (aFeature.EqualsLiteral("WEBGL_OPENGL")) {
475 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
477 if (aFeature.EqualsLiteral("WEBGL_ANGLE")) {
478 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
480 if (aFeature.EqualsLiteral("WEBGL_MSAA")) {
481 return nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA;
483 if (aFeature.EqualsLiteral("STAGEFRIGHT")) {
484 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
486 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE")) {
487 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
489 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE")) {
490 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
492 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_H264")) {
493 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264;
495 if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION")) {
496 return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
498 if (aFeature.EqualsLiteral("DX_INTEROP2")) {
499 return nsIGfxInfo::FEATURE_DX_INTEROP2;
501 if (aFeature.EqualsLiteral("GPU_PROCESS")) {
502 return nsIGfxInfo::FEATURE_GPU_PROCESS;
504 if (aFeature.EqualsLiteral("WEBGL2")) {
505 return nsIGfxInfo::FEATURE_WEBGL2;
507 if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX")) {
508 return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
510 if (aFeature.EqualsLiteral("WEBRENDER")) {
511 return nsIGfxInfo::FEATURE_WEBRENDER;
513 if (aFeature.EqualsLiteral("WEBRENDER_COMPOSITOR")) {
514 return nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR;
516 if (aFeature.EqualsLiteral("DX_NV12")) {
517 return nsIGfxInfo::FEATURE_DX_NV12;
519 if (aFeature.EqualsLiteral("VP8_HW_DECODE")) {
520 return nsIGfxInfo::FEATURE_VP8_HW_DECODE;
522 if (aFeature.EqualsLiteral("VP9_HW_DECODE")) {
523 return nsIGfxInfo::FEATURE_VP9_HW_DECODE;
525 if (aFeature.EqualsLiteral("GL_SWIZZLE")) {
526 return nsIGfxInfo::FEATURE_GL_SWIZZLE;
528 if (aFeature.EqualsLiteral("WEBRENDER_SCISSORED_CACHE_CLEARS")) {
529 return nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS;
531 if (aFeature.EqualsLiteral("ALLOW_WEBGL_OUT_OF_PROCESS")) {
532 return nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS;
534 if (aFeature.EqualsLiteral("THREADSAFE_GL")) {
535 return nsIGfxInfo::FEATURE_THREADSAFE_GL;
537 if (aFeature.EqualsLiteral("X11_EGL")) {
538 return nsIGfxInfo::FEATURE_X11_EGL;
540 if (aFeature.EqualsLiteral("DMABUF")) {
541 return nsIGfxInfo::FEATURE_DMABUF;
543 if (aFeature.EqualsLiteral("WEBGPU")) {
544 return nsIGfxInfo::FEATURE_WEBGPU;
546 if (aFeature.EqualsLiteral("VIDEO_OVERLAY")) {
547 return nsIGfxInfo::FEATURE_VIDEO_OVERLAY;
549 if (aFeature.EqualsLiteral("HW_DECODED_VIDEO_ZERO_COPY")) {
550 return nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY;
552 if (aFeature.EqualsLiteral("REUSE_DECODER_DEVICE")) {
553 return nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE;
555 if (aFeature.EqualsLiteral("WEBRENDER_PARTIAL_PRESENT")) {
556 return nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT;
558 if (aFeature.EqualsLiteral("BACKDROP_FILTER")) {
559 return nsIGfxInfo::FEATURE_BACKDROP_FILTER;
561 if (aFeature.EqualsLiteral("ACCELERATED_CANVAS2D")) {
562 return nsIGfxInfo::FEATURE_ACCELERATED_CANVAS2D;
564 if (aFeature.EqualsLiteral("FEATURE_OVERLAY_VP_AUTO_HDR")) {
565 return nsIGfxInfo::FEATURE_OVERLAY_VP_AUTO_HDR;
567 if (aFeature.EqualsLiteral("FEATURE_OVERLAY_VP_SUPER_RESOLUTION")) {
568 return nsIGfxInfo::FEATURE_OVERLAY_VP_SUPER_RESOLUTION;
570 if (aFeature.EqualsLiteral("ALL")) {
571 return GfxDriverInfo::allFeatures;
573 if (aFeature.EqualsLiteral("OPTIONAL")) {
574 return GfxDriverInfo::optionalFeatures;
577 // If we don't recognize the feature, it may be new, and something
578 // this version doesn't understand. So, nothing to do. This is
579 // different from feature not being specified at all, in which case
580 // this method should not get called and we should continue with the
581 // "optional features" blocklisting.
582 return 0;
585 static int32_t BlocklistFeatureStatusToGfxFeatureStatus(
586 const nsAString& aStatus) {
587 if (aStatus.EqualsLiteral("STATUS_OK")) {
588 return nsIGfxInfo::FEATURE_STATUS_OK;
590 if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION")) {
591 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
593 if (aStatus.EqualsLiteral("BLOCKED_DEVICE")) {
594 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
596 if (aStatus.EqualsLiteral("DISCOURAGED")) {
597 return nsIGfxInfo::FEATURE_DISCOURAGED;
599 if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION")) {
600 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
602 if (aStatus.EqualsLiteral("DENIED")) {
603 return nsIGfxInfo::FEATURE_DENIED;
605 if (aStatus.EqualsLiteral("ALLOW_QUALIFIED")) {
606 return nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
608 if (aStatus.EqualsLiteral("ALLOW_ALWAYS")) {
609 return nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
612 // Do not allow it to set STATUS_UNKNOWN. Also, we are not
613 // expecting the "mismatch" status showing up here.
615 return nsIGfxInfo::FEATURE_STATUS_OK;
618 static VersionComparisonOp BlocklistComparatorToComparisonOp(
619 const nsAString& op) {
620 if (op.EqualsLiteral("LESS_THAN")) {
621 return DRIVER_LESS_THAN;
623 if (op.EqualsLiteral("BUILD_ID_LESS_THAN")) {
624 return DRIVER_BUILD_ID_LESS_THAN;
626 if (op.EqualsLiteral("LESS_THAN_OR_EQUAL")) {
627 return DRIVER_LESS_THAN_OR_EQUAL;
629 if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL")) {
630 return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
632 if (op.EqualsLiteral("GREATER_THAN")) {
633 return DRIVER_GREATER_THAN;
635 if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL")) {
636 return DRIVER_GREATER_THAN_OR_EQUAL;
638 if (op.EqualsLiteral("EQUAL")) {
639 return DRIVER_EQUAL;
641 if (op.EqualsLiteral("NOT_EQUAL")) {
642 return DRIVER_NOT_EQUAL;
644 if (op.EqualsLiteral("BETWEEN_EXCLUSIVE")) {
645 return DRIVER_BETWEEN_EXCLUSIVE;
647 if (op.EqualsLiteral("BETWEEN_INCLUSIVE")) {
648 return DRIVER_BETWEEN_INCLUSIVE;
650 if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START")) {
651 return DRIVER_BETWEEN_INCLUSIVE_START;
654 return DRIVER_COMPARISON_IGNORED;
658 Deserialize Blocklist entries from string.
659 e.g:
660 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
662 static bool BlocklistEntryToDriverInfo(const nsACString& aBlocklistEntry,
663 GfxDriverInfo& aDriverInfo) {
664 // If we get an application version to be zero, something is not working
665 // and we are not going to bother checking the blocklist versions.
666 // See TestGfxWidgets.cpp for how version comparison works.
667 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
668 static mozilla::Version zeroV("0");
669 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
670 if (appV <= zeroV) {
671 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
672 << "Invalid application version "
673 << GfxInfoBase::GetApplicationVersion().get();
676 aDriverInfo.mRuleId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID"_ns;
678 for (const auto& keyValue : aBlocklistEntry.Split('\t')) {
679 nsTArray<nsCString> splitted;
680 ParseString(keyValue, ':', splitted);
681 if (splitted.Length() != 2) {
682 // If we don't recognize the input data, we do not want to proceed.
683 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
684 << "Unrecognized data " << nsCString(keyValue).get();
685 return false;
687 const nsCString& key = splitted[0];
688 const nsCString& value = splitted[1];
689 NS_ConvertUTF8toUTF16 dataValue(value);
691 if (value.Length() == 0) {
692 // Safety check for empty values.
693 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
694 << "Empty value for " << key.get();
695 return false;
698 if (key.EqualsLiteral("blockID")) {
699 nsCString blockIdStr = "FEATURE_FAILURE_DL_BLOCKLIST_"_ns + value;
700 aDriverInfo.mRuleId = blockIdStr.get();
701 } else if (key.EqualsLiteral("os")) {
702 aDriverInfo.mOperatingSystem = BlocklistOSToOperatingSystem(dataValue);
703 } else if (key.EqualsLiteral("osversion")) {
704 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
705 } else if (key.EqualsLiteral("windowProtocol")) {
706 aDriverInfo.mWindowProtocol = dataValue;
707 } else if (key.EqualsLiteral("vendor")) {
708 aDriverInfo.mAdapterVendor = dataValue;
709 } else if (key.EqualsLiteral("driverVendor")) {
710 aDriverInfo.mDriverVendor = dataValue;
711 } else if (key.EqualsLiteral("feature")) {
712 aDriverInfo.mFeature = BlocklistFeatureToGfxFeature(dataValue);
713 if (aDriverInfo.mFeature == 0) {
714 // If we don't recognize the feature, we do not want to proceed.
715 gfxWarning() << "Unrecognized feature " << value.get();
716 return false;
718 } else if (key.EqualsLiteral("featureStatus")) {
719 aDriverInfo.mFeatureStatus =
720 BlocklistFeatureStatusToGfxFeatureStatus(dataValue);
721 } else if (key.EqualsLiteral("driverVersion")) {
722 uint64_t version;
723 if (ParseDriverVersion(dataValue, &version))
724 aDriverInfo.mDriverVersion = version;
725 } else if (key.EqualsLiteral("driverVersionMax")) {
726 uint64_t version;
727 if (ParseDriverVersion(dataValue, &version))
728 aDriverInfo.mDriverVersionMax = version;
729 } else if (key.EqualsLiteral("driverVersionComparator")) {
730 aDriverInfo.mComparisonOp = BlocklistComparatorToComparisonOp(dataValue);
731 } else if (key.EqualsLiteral("model")) {
732 aDriverInfo.mModel = dataValue;
733 } else if (key.EqualsLiteral("product")) {
734 aDriverInfo.mProduct = dataValue;
735 } else if (key.EqualsLiteral("manufacturer")) {
736 aDriverInfo.mManufacturer = dataValue;
737 } else if (key.EqualsLiteral("hardware")) {
738 aDriverInfo.mHardware = dataValue;
739 } else if (key.EqualsLiteral("versionRange")) {
740 nsTArray<nsCString> versionRange;
741 ParseString(value, ',', versionRange);
742 if (versionRange.Length() != 2) {
743 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
744 << "Unrecognized versionRange " << value.get();
745 return false;
747 const nsCString& minValue = versionRange[0];
748 const nsCString& maxValue = versionRange[1];
750 mozilla::Version minV(minValue.get());
751 mozilla::Version maxV(maxValue.get());
753 if (minV > zeroV && !(appV >= minV)) {
754 // The version of the application is less than the minimal version
755 // this blocklist entry applies to, so we can just ignore it by
756 // returning false and letting the caller deal with it.
757 return false;
759 if (maxV > zeroV && !(appV <= maxV)) {
760 // The version of the application is more than the maximal version
761 // this blocklist entry applies to, so we can just ignore it by
762 // returning false and letting the caller deal with it.
763 return false;
765 } else if (key.EqualsLiteral("devices")) {
766 nsTArray<nsCString> devices;
767 ParseString(value, ',', devices);
768 GfxDeviceFamily* deviceIds = BlocklistDevicesToDeviceFamily(devices);
769 if (deviceIds) {
770 // Get GfxDriverInfo to adopt the devices array we created.
771 aDriverInfo.mDeleteDevices = true;
772 aDriverInfo.mDevices = deviceIds;
775 // We explicitly ignore unknown elements.
778 return true;
781 NS_IMETHODIMP
782 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
783 const char16_t* aData) {
784 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
785 nsTArray<GfxDriverInfo> driverInfo;
786 NS_ConvertUTF16toUTF8 utf8Data(aData);
788 for (const auto& blocklistEntry : utf8Data.Split('\n')) {
789 GfxDriverInfo di;
790 if (BlocklistEntryToDriverInfo(blocklistEntry, di)) {
791 // XXX Changing this to driverInfo.AppendElement(di) causes leaks.
792 // Probably some non-standard semantics of the copy/move operations?
793 *driverInfo.AppendElement() = di;
794 // Prevent di falling out of scope from destroying the devices.
795 di.mDeleteDevices = false;
796 } else {
797 driverInfo.AppendElement();
801 EvaluateDownloadedBlocklist(driverInfo);
804 return NS_OK;
807 GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
809 GfxInfoBase::~GfxInfoBase() = default;
811 nsresult GfxInfoBase::Init() {
812 InitGfxDriverInfoShutdownObserver();
814 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
815 if (os) {
816 os->AddObserver(this, "blocklist-data-gfxItems", true);
819 return NS_OK;
822 void GfxInfoBase::GetData() {
823 if (mScreenPixels != INT64_MAX) {
824 // Already initialized.
825 return;
828 ScreenManager::GetSingleton().GetTotalScreenPixels(&mScreenPixels);
831 NS_IMETHODIMP
832 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
833 int32_t* aStatus) {
834 // Ignore the gfx.blocklist.all pref on release and beta.
835 #if defined(RELEASE_OR_BETA)
836 int32_t blocklistAll = 0;
837 #else
838 int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
839 #endif
840 if (blocklistAll > 0) {
841 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
842 << "Forcing blocklisting all features";
843 *aStatus = FEATURE_BLOCKED_DEVICE;
844 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
845 return NS_OK;
848 if (blocklistAll < 0) {
849 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
850 << "Ignoring any feature blocklisting.";
851 *aStatus = FEATURE_STATUS_OK;
852 return NS_OK;
855 // This is how we evaluate the downloadable blocklist. If there is no pref,
856 // then we will fallback to checking the static blocklist.
857 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
858 return NS_OK;
861 if (XRE_IsContentProcess() || XRE_IsGPUProcess()) {
862 // Use the cached data received from the parent process.
863 MOZ_ASSERT(sFeatureStatus);
864 bool success = false;
865 for (const auto& fs : *sFeatureStatus) {
866 if (fs.feature() == aFeature) {
867 aFailureId = fs.failureId();
868 *aStatus = fs.status();
869 success = true;
870 break;
873 return success ? NS_OK : NS_ERROR_FAILURE;
876 nsString version;
877 nsTArray<GfxDriverInfo> driverInfo;
878 nsresult rv =
879 GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
880 return rv;
883 nsTArray<gfx::GfxInfoFeatureStatus> GfxInfoBase::GetAllFeatures() {
884 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
885 if (!sFeatureStatus) {
886 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>());
887 for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
888 int32_t status = 0;
889 nsAutoCString failureId;
890 GetFeatureStatus(i, failureId, &status);
891 gfx::GfxInfoFeatureStatus gfxFeatureStatus;
892 gfxFeatureStatus.feature() = i;
893 gfxFeatureStatus.status() = status;
894 gfxFeatureStatus.failureId() = failureId;
895 sFeatureStatus->AppendElement(gfxFeatureStatus);
899 nsTArray<gfx::GfxInfoFeatureStatus> features;
900 for (const auto& status : *sFeatureStatus) {
901 gfx::GfxInfoFeatureStatus copy = status;
902 features.AppendElement(copy);
904 return features;
907 inline bool MatchingAllowStatus(int32_t aStatus) {
908 switch (aStatus) {
909 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
910 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
911 return true;
912 default:
913 return false;
917 // Matching OS go somewhat beyond the simple equality check because of the
918 // "All Windows" and "All OS X" variations.
920 // aBlockedOS is describing the system(s) we are trying to block.
921 // aSystemOS is describing the system we are running on.
923 // aSystemOS should not be "Windows" or "OSX" - it should be set to
924 // a particular version instead.
925 // However, it is valid for aBlockedOS to be one of those generic values,
926 // as we could be blocking all of the versions.
927 inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
928 OperatingSystem aSystemOS,
929 uint32_t aSystemOSBuild) {
930 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
931 aSystemOS != OperatingSystem::OSX);
933 // If the block entry OS is unknown, it doesn't match
934 if (aBlockedOS == OperatingSystem::Unknown) {
935 return false;
938 #if defined(XP_WIN)
939 if (aBlockedOS == OperatingSystem::Windows) {
940 // We do want even "unknown" aSystemOS to fall under "all windows"
941 return true;
944 constexpr uint32_t kMinWin10BuildNumber = 18362;
945 if (aBlockedOS == OperatingSystem::RecentWindows10 &&
946 aSystemOS == OperatingSystem::Windows10) {
947 // For allowlist purposes, we sometimes want to restrict to only recent
948 // versions of Windows 10. This is a bit of a kludge but easier than adding
949 // complicated blocklist infrastructure for build ID comparisons like driver
950 // versions.
951 return aSystemOSBuild >= kMinWin10BuildNumber;
954 if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
955 if (aSystemOS == OperatingSystem::Windows10) {
956 return aSystemOSBuild < kMinWin10BuildNumber;
957 } else {
958 return true;
961 #endif
963 #if defined(XP_MACOSX)
964 if (aBlockedOS == OperatingSystem::OSX) {
965 // We do want even "unknown" aSystemOS to fall under "all OS X"
966 return true;
968 #endif
970 return aSystemOS == aBlockedOS;
973 inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
974 switch (aBatteryStatus) {
975 case BatteryStatus::All:
976 return true;
977 case BatteryStatus::None:
978 return !aHasBattery;
979 case BatteryStatus::Present:
980 return aHasBattery;
983 MOZ_ASSERT_UNREACHABLE("bad battery status");
984 return false;
987 inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
988 int64_t aScreenPixels) {
989 constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
990 constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
992 switch (aScreenStatus) {
993 case ScreenSizeStatus::All:
994 return true;
995 case ScreenSizeStatus::Small:
996 return aScreenPixels <= kMaxSmallPixels;
997 case ScreenSizeStatus::SmallAndMedium:
998 return aScreenPixels <= kMaxMediumPixels;
999 case ScreenSizeStatus::Medium:
1000 return aScreenPixels > kMaxSmallPixels &&
1001 aScreenPixels <= kMaxMediumPixels;
1002 case ScreenSizeStatus::MediumAndLarge:
1003 return aScreenPixels > kMaxSmallPixels;
1004 case ScreenSizeStatus::Large:
1005 return aScreenPixels > kMaxMediumPixels;
1008 MOZ_ASSERT_UNREACHABLE("bad screen status");
1009 return false;
1012 int32_t GfxInfoBase::FindBlocklistedDeviceInList(
1013 const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
1014 int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
1015 bool aForAllowing) {
1016 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
1018 // Some properties are not available on all platforms.
1019 nsAutoString windowProtocol;
1020 nsresult rv = GetWindowProtocol(windowProtocol);
1021 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
1022 return 0;
1025 bool hasBattery = false;
1026 rv = GetHasBattery(&hasBattery);
1027 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
1028 return 0;
1031 uint32_t osBuild = OperatingSystemBuild();
1033 // Get the adapters once then reuse below
1034 nsAutoString adapterVendorID[2];
1035 nsAutoString adapterDeviceID[2];
1036 nsAutoString adapterDriverVendor[2];
1037 nsAutoString adapterDriverVersionString[2];
1038 bool adapterInfoFailed[2];
1040 adapterInfoFailed[0] =
1041 (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
1042 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
1043 NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
1044 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
1045 adapterInfoFailed[1] =
1046 (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
1047 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
1048 NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
1049 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
1050 // No point in going on if we don't have adapter info
1051 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
1052 return 0;
1055 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1056 uint64_t driverVersion[2] = {0, 0};
1057 if (!adapterInfoFailed[0]) {
1058 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
1060 if (!adapterInfoFailed[1]) {
1061 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
1063 #endif
1065 uint32_t i = 0;
1066 for (; i < info.Length(); i++) {
1067 // If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
1068 // blocklisting. Only consider entries for our search mode.
1069 if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
1070 continue;
1073 // If we don't have the info for this GPU, no need to check further.
1074 // It is unclear that we would ever have a mixture of 1st and 2nd
1075 // GPU, but leaving the code in for that possibility for now.
1076 // (Actually, currently mGpu2 will never be true, so this can
1077 // be optimized out.)
1078 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
1079 if (adapterInfoFailed[infoIndex]) {
1080 continue;
1083 // Do the operating system check first, no point in getting the driver
1084 // info if we won't need to use it.
1085 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
1086 continue;
1089 if (info[i].mOperatingSystemVersion &&
1090 info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
1091 continue;
1094 if (!MatchingBattery(info[i].mBattery, hasBattery)) {
1095 continue;
1098 if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
1099 continue;
1102 if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
1103 continue;
1106 if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
1107 continue;
1110 if (!DoesDriverVendorMatch(info[i].mDriverVendor,
1111 adapterDriverVendor[infoIndex])) {
1112 continue;
1115 if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
1116 nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
1117 if (rv == NS_ERROR_NOT_AVAILABLE) {
1118 // Not found
1119 continue;
1121 if (rv != NS_OK) {
1122 // Failed to search, allowlist should not match, blocklist should match
1123 // for safety reasons
1124 if (aForAllowing) {
1125 continue;
1127 break;
1131 bool match = false;
1133 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
1134 continue;
1136 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
1137 continue;
1139 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
1140 continue;
1142 if (!info[i].mManufacturer.IsEmpty() &&
1143 !info[i].mManufacturer.Equals(Manufacturer())) {
1144 continue;
1147 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1148 switch (info[i].mComparisonOp) {
1149 case DRIVER_LESS_THAN:
1150 match = driverVersion[infoIndex] < info[i].mDriverVersion;
1151 break;
1152 case DRIVER_BUILD_ID_LESS_THAN:
1153 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
1154 break;
1155 case DRIVER_LESS_THAN_OR_EQUAL:
1156 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
1157 break;
1158 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
1159 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
1160 break;
1161 case DRIVER_GREATER_THAN:
1162 match = driverVersion[infoIndex] > info[i].mDriverVersion;
1163 break;
1164 case DRIVER_GREATER_THAN_OR_EQUAL:
1165 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
1166 break;
1167 case DRIVER_EQUAL:
1168 match = driverVersion[infoIndex] == info[i].mDriverVersion;
1169 break;
1170 case DRIVER_NOT_EQUAL:
1171 match = driverVersion[infoIndex] != info[i].mDriverVersion;
1172 break;
1173 case DRIVER_BETWEEN_EXCLUSIVE:
1174 match = driverVersion[infoIndex] > info[i].mDriverVersion &&
1175 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1176 break;
1177 case DRIVER_BETWEEN_INCLUSIVE:
1178 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1179 driverVersion[infoIndex] <= info[i].mDriverVersionMax;
1180 break;
1181 case DRIVER_BETWEEN_INCLUSIVE_START:
1182 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1183 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1184 break;
1185 case DRIVER_COMPARISON_IGNORED:
1186 // We don't have a comparison op, so we match everything.
1187 match = true;
1188 break;
1189 default:
1190 NS_WARNING("Bogus op in GfxDriverInfo");
1191 break;
1193 #else
1194 // We don't care what driver version it was. We only check OS version and if
1195 // the device matches.
1196 match = true;
1197 #endif
1199 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
1200 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
1201 info[i].mFeature == aFeature ||
1202 (info[i].mFeature == GfxDriverInfo::optionalFeatures &&
1203 OnlyAllowFeatureOnKnownConfig(aFeature))) {
1204 status = info[i].mFeatureStatus;
1205 if (!info[i].mRuleId.IsEmpty()) {
1206 aFailureId = info[i].mRuleId.get();
1207 } else {
1208 aFailureId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID";
1210 break;
1215 #if defined(XP_WIN)
1216 // As a very special case, we block D2D on machines with an NVidia 310M GPU
1217 // as either the primary or secondary adapter. D2D is also blocked when the
1218 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
1219 // If the primary GPU already matched something in the blocklist then we
1220 // ignore this special rule. See bug 1008759.
1221 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
1222 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
1223 if (!adapterInfoFailed[1]) {
1224 nsAString& nvVendorID =
1225 (nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
1226 const nsString nv310mDeviceId = u"0x0A70"_ns;
1227 if (nvVendorID.Equals(adapterVendorID[1],
1228 nsCaseInsensitiveStringComparator) &&
1229 nv310mDeviceId.Equals(adapterDeviceID[1],
1230 nsCaseInsensitiveStringComparator)) {
1231 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1232 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
1237 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
1238 // back to the Windows handler, so we must handle this here.
1239 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
1240 if (info[i].mSuggestedVersion) {
1241 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
1242 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
1243 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
1244 aSuggestedVersion.AppendPrintf(
1245 "%lld.%lld.%lld.%lld",
1246 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
1247 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
1248 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
1249 (info[i].mDriverVersion & 0x000000000000ffff));
1252 #endif
1254 return status;
1257 void GfxInfoBase::SetFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>&& aFS) {
1258 MOZ_ASSERT(!sFeatureStatus);
1259 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>(std::move(aFS)));
1262 bool GfxInfoBase::DoesWindowProtocolMatch(
1263 const nsAString& aBlocklistWindowProtocol,
1264 const nsAString& aWindowProtocol) {
1265 return aBlocklistWindowProtocol.Equals(aWindowProtocol,
1266 nsCaseInsensitiveStringComparator) ||
1267 aBlocklistWindowProtocol.Equals(
1268 GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
1269 nsCaseInsensitiveStringComparator);
1272 bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
1273 const nsAString& aAdapterVendor) {
1274 return aBlocklistVendor.Equals(aAdapterVendor,
1275 nsCaseInsensitiveStringComparator) ||
1276 aBlocklistVendor.Equals(
1277 GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
1278 nsCaseInsensitiveStringComparator);
1281 bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
1282 const nsAString& aDriverVendor) {
1283 return aBlocklistVendor.Equals(aDriverVendor,
1284 nsCaseInsensitiveStringComparator) ||
1285 aBlocklistVendor.Equals(
1286 GfxDriverInfo::GetDriverVendor(DriverVendor::All),
1287 nsCaseInsensitiveStringComparator);
1290 bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
1291 return aFeature == nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY;
1294 nsresult GfxInfoBase::GetFeatureStatusImpl(
1295 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
1296 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
1297 OperatingSystem* aOS /* = nullptr */) {
1298 if (aFeature <= 0) {
1299 gfxWarning() << "Invalid feature <= 0";
1300 return NS_OK;
1303 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1304 // Terminate now with the status determined by the derived type (OS-specific
1305 // code).
1306 return NS_OK;
1309 if (sShutdownOccurred) {
1310 // This is futile; we've already commenced shutdown and our blocklists have
1311 // been deleted. We may want to look into resurrecting the blocklist instead
1312 // but for now, just don't even go there.
1313 return NS_OK;
1316 // Ensure any additional initialization required is complete.
1317 GetData();
1319 // If an operating system was provided by the derived GetFeatureStatusImpl,
1320 // grab it here. Otherwise, the OS is unknown.
1321 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
1323 nsAutoString adapterVendorID;
1324 nsAutoString adapterDeviceID;
1325 nsAutoString adapterDriverVersionString;
1326 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
1327 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
1328 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
1329 if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
1330 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
1331 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1332 } else {
1333 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
1335 return NS_OK;
1338 // We only check either the given blocklist, or the static list, as given.
1339 int32_t status;
1340 if (aDriverInfo.Length()) {
1341 status =
1342 FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
1343 aFailureId, os, /* aForAllowing */ false);
1344 } else {
1345 if (!sDriverInfo) {
1346 sDriverInfo = new nsTArray<GfxDriverInfo>();
1348 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
1349 aFeature, aFailureId, os,
1350 /* aForAllowing */ false);
1353 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1354 if (IsFeatureAllowlisted(aFeature)) {
1355 // This feature is actually using the allowlist; that means after we pass
1356 // the blocklist to prevent us explicitly from getting the feature, we now
1357 // need to check the allowlist to ensure we are allowed to get it in the
1358 // first place.
1359 if (aDriverInfo.Length()) {
1360 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
1361 aFeature, aFailureId, os,
1362 /* aForAllowing */ true);
1363 } else {
1364 status = FindBlocklistedDeviceInList(
1365 GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
1366 /* aForAllowing */ true);
1369 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1370 status = nsIGfxInfo::FEATURE_DENIED;
1372 } else {
1373 // It's now done being processed. It's safe to set the status to
1374 // STATUS_OK.
1375 status = nsIGfxInfo::FEATURE_STATUS_OK;
1379 *aStatus = status;
1380 return NS_OK;
1383 NS_IMETHODIMP
1384 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
1385 nsAString& aVersion) {
1386 nsCString version;
1387 if (GetPrefValueForDriverVersion(version)) {
1388 aVersion = NS_ConvertASCIItoUTF16(version);
1389 return NS_OK;
1392 int32_t status;
1393 nsCString discardFailureId;
1394 nsTArray<GfxDriverInfo> driverInfo;
1395 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
1396 discardFailureId);
1399 void GfxInfoBase::EvaluateDownloadedBlocklist(
1400 nsTArray<GfxDriverInfo>& aDriverInfo) {
1401 // If the list is empty, then we don't actually want to call
1402 // GetFeatureStatusImpl since we will use the static list instead. In that
1403 // case, all we want to do is make sure the pref is removed.
1404 if (aDriverInfo.IsEmpty()) {
1405 gfxCriticalNoteOnce << "Evaluate empty downloaded blocklist";
1406 return;
1409 OperatingSystem os = GetOperatingSystem();
1411 // For every feature we know about, we evaluate whether this blocklist has a
1412 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1413 // GetFeatureStatus above, so we don't need to hold on to this blocklist
1414 // anywhere permanent.
1415 for (int feature = 1; feature <= nsIGfxInfo::FEATURE_MAX_VALUE; ++feature) {
1416 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
1417 nsCString failureId;
1418 nsAutoString suggestedVersion;
1420 // Note that we are careful to call the base class method since we only want
1421 // to evaluate the downloadable blocklist for these prefs.
1422 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GfxInfoBase::GetFeatureStatusImpl(
1423 feature, &status, suggestedVersion, aDriverInfo, failureId, &os)));
1425 switch (status) {
1426 default:
1427 MOZ_FALLTHROUGH_ASSERT("Unhandled feature status!");
1428 case nsIGfxInfo::FEATURE_STATUS_UNKNOWN:
1429 // This may be returned during shutdown or for invalid features.
1430 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
1431 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
1432 case nsIGfxInfo::FEATURE_DENIED:
1433 // We cannot use the downloadable blocklist to control the allowlist.
1434 // If a feature is allowlisted, then we should also ignore DENIED
1435 // statuses from GetFeatureStatusImpl because we don't check the
1436 // static list when and this is an expected value. If we wish to
1437 // override the allowlist, it is as simple as creating a normal
1438 // blocklist rule with a BLOCKED* status code.
1439 case nsIGfxInfo::FEATURE_STATUS_OK:
1440 RemovePrefForFeature(feature);
1441 break;
1443 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1444 if (!suggestedVersion.IsEmpty()) {
1445 SetPrefValueForDriverVersion(suggestedVersion);
1446 } else {
1447 RemovePrefForDriverVersion();
1449 [[fallthrough]];
1451 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1452 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1453 case nsIGfxInfo::FEATURE_DISCOURAGED:
1454 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1455 case nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST:
1456 SetPrefValueForFeature(feature, status, failureId);
1457 break;
1462 NS_IMETHODIMP_(void)
1463 GfxInfoBase::LogFailure(const nsACString& failure) {
1464 // gfxCriticalError has a mutex lock of its own, so we may not actually
1465 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1466 // will not return the copy of the logs unless it can get the same lock
1467 // that gfxCriticalError uses. Still, that is so much of an implementation
1468 // detail that it's nicer to just add an extra lock here and in
1469 // ::GetFailures()
1470 MutexAutoLock lock(mMutex);
1472 // By default, gfxCriticalError asserts; make it not assert in this case.
1473 gfxCriticalError(CriticalLog::DefaultOptions(false))
1474 << "(LF) " << failure.BeginReading();
1477 NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
1478 nsTArray<nsCString>& failures) {
1479 MutexAutoLock lock(mMutex);
1481 LogForwarder* logForwarder = Factory::GetLogForwarder();
1482 if (!logForwarder) {
1483 return NS_ERROR_UNEXPECTED;
1486 // There are two string copies in this method, starting with this one. We are
1487 // assuming this is not a big deal, as the size of the array should be small
1488 // and the strings in it should be small as well (the error messages in the
1489 // code.) The second copy happens with the AppendElement() calls.
1490 // Technically, we don't need the mutex lock after the StringVectorCopy()
1491 // call.
1492 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1493 LoggingRecord::const_iterator it;
1494 for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
1495 failures.AppendElement(nsDependentCSubstring(std::get<1>(*it).c_str(),
1496 std::get<1>(*it).size()));
1497 indices.AppendElement(std::get<0>(*it));
1500 return NS_OK;
1503 nsTArray<GfxInfoCollectorBase*>* sCollectors;
1505 static void InitCollectors() {
1506 if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1509 nsresult GfxInfoBase::GetInfo(JSContext* aCx,
1510 JS::MutableHandle<JS::Value> aResult) {
1511 InitCollectors();
1512 InfoObject obj(aCx);
1514 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1515 (*sCollectors)[i]->GetInfo(obj);
1518 // Some example property definitions
1519 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1520 // obj.DefineProperty("renderer", mRendererIDsString);
1521 // obj.DefineProperty("five", 5);
1523 if (!obj.mOk) {
1524 return NS_ERROR_FAILURE;
1527 aResult.setObject(*obj.mObj);
1528 return NS_OK;
1531 nsAutoCString gBaseAppVersion;
1533 const nsCString& GfxInfoBase::GetApplicationVersion() {
1534 static bool versionInitialized = false;
1535 if (!versionInitialized) {
1536 // If we fail to get the version, we will not try again.
1537 versionInitialized = true;
1539 // Get the version from xpcom/system/nsIXULAppInfo.idl
1540 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1541 if (app) {
1542 app->GetVersion(gBaseAppVersion);
1545 return gBaseAppVersion;
1548 /* static */ bool GfxInfoBase::OnlyAllowFeatureOnKnownConfig(int32_t aFeature) {
1549 switch (aFeature) {
1550 // The GPU process doesn't need hardware acceleration and can run on
1551 // devices that we normally block from not being on our whitelist.
1552 case nsIGfxInfo::FEATURE_GPU_PROCESS:
1553 return kIsAndroid;
1554 // We can mostly assume that ANGLE will work
1555 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
1556 // Remote WebGL is needed for Win32k Lockdown, so it should be enabled
1557 // regardless of HW support or not
1558 case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
1559 // Backdrop filter should generally work, especially if we fall back to
1560 // Software WebRender because of an unknown vendor.
1561 case nsIGfxInfo::FEATURE_BACKDROP_FILTER:
1562 return false;
1563 default:
1564 return true;
1568 void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
1569 InitCollectors();
1570 sCollectors->AppendElement(collector);
1573 void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
1574 InitCollectors();
1575 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1576 if ((*sCollectors)[i] == collector) {
1577 sCollectors->RemoveElementAt(i);
1578 break;
1581 if (sCollectors->IsEmpty()) {
1582 delete sCollectors;
1583 sCollectors = nullptr;
1587 static void AppendMonitor(JSContext* aCx, widget::Screen& aScreen,
1588 JS::Handle<JSObject*> aOutArray, int32_t aIndex) {
1589 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1591 auto screenSize = aScreen.GetRect().Size();
1593 JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
1594 JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
1596 JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
1597 JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
1599 // XXX Just preserving behavior since this is exposed to telemetry, but we
1600 // could consider including this everywhere.
1601 #ifdef XP_MACOSX
1602 JS::Rooted<JS::Value> scale(
1603 aCx, JS::NumberValue(aScreen.GetContentsScaleFactor()));
1604 JS_SetProperty(aCx, obj, "scale", scale);
1605 #endif
1607 #ifdef XP_WIN
1608 JS::Rooted<JS::Value> refreshRate(aCx,
1609 JS::Int32Value(aScreen.GetRefreshRate()));
1610 JS_SetProperty(aCx, obj, "refreshRate", refreshRate);
1612 JS::Rooted<JS::Value> pseudoDisplay(
1613 aCx, JS::BooleanValue(aScreen.GetIsPseudoDisplay()));
1614 JS_SetProperty(aCx, obj, "pseudoDisplay", pseudoDisplay);
1615 #endif
1617 JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
1618 JS_SetElement(aCx, aOutArray, aIndex, element);
1621 nsresult GfxInfoBase::FindMonitors(JSContext* aCx,
1622 JS::Handle<JSObject*> aOutArray) {
1623 int32_t index = 0;
1624 auto& sm = ScreenManager::GetSingleton();
1625 for (auto& screen : sm.CurrentScreenList()) {
1626 AppendMonitor(aCx, *screen, aOutArray, index++);
1629 if (index == 0) {
1630 // Ensure we return at least one monitor, this is needed for xpcshell.
1631 RefPtr<Screen> screen = sm.GetPrimaryScreen();
1632 AppendMonitor(aCx, *screen, aOutArray, index++);
1635 return NS_OK;
1638 NS_IMETHODIMP
1639 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
1640 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1642 nsresult rv = FindMonitors(aCx, array);
1643 if (NS_FAILED(rv)) {
1644 return rv;
1647 aResult.setObject(*array);
1648 return NS_OK;
1651 static inline bool SetJSPropertyString(JSContext* aCx,
1652 JS::Handle<JSObject*> aObj,
1653 const char* aProp, const char* aString) {
1654 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1655 if (!str) {
1656 return false;
1659 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1660 return JS_SetProperty(aCx, aObj, aProp, val);
1663 template <typename T>
1664 static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
1665 const T& aValue) {
1666 uint32_t index;
1667 if (!JS::GetArrayLength(aCx, aObj, &index)) {
1668 return false;
1670 return JS_SetElement(aCx, aObj, index, aValue);
1673 nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
1674 JS::MutableHandle<JS::Value> aOut) {
1675 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1676 if (!obj) {
1677 return NS_ERROR_OUT_OF_MEMORY;
1679 aOut.setObject(*obj);
1681 layers::LayersBackend backend =
1682 gfxPlatform::Initialized()
1683 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1684 : layers::LayersBackend::LAYERS_NONE;
1685 const char* backendName = layers::GetLayersBackendName(backend);
1686 SetJSPropertyString(aCx, obj, "compositor", backendName);
1688 // If graphics isn't initialized yet, just stop now.
1689 if (!gfxPlatform::Initialized()) {
1690 return NS_OK;
1693 DescribeFeatures(aCx, obj);
1694 return NS_OK;
1697 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
1698 JS::MutableHandle<JS::Value> aOut) {
1699 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1700 if (!containerObj) {
1701 return NS_ERROR_OUT_OF_MEMORY;
1703 aOut.setObject(*containerObj);
1705 JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
1706 if (!featureArray) {
1707 return NS_ERROR_OUT_OF_MEMORY;
1710 // Collect features.
1711 gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
1712 FeatureState& aFeature) -> void {
1713 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1714 if (!obj) {
1715 return;
1717 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1718 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1719 !SetJSPropertyString(aCx, obj, "status",
1720 FeatureStatusToString(aFeature.GetValue()))) {
1721 return;
1724 JS::Rooted<JS::Value> log(aCx);
1725 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1726 return;
1728 if (!JS_SetProperty(aCx, obj, "log", log)) {
1729 return;
1732 if (!AppendJSElement(aCx, featureArray, obj)) {
1733 return;
1737 JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
1738 if (!fallbackArray) {
1739 return NS_ERROR_OUT_OF_MEMORY;
1742 // Collect fallbacks.
1743 gfxConfig::ForEachFallback(
1744 [&](const char* aName, const char* aMessage) -> void {
1745 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1746 if (!obj) {
1747 return;
1750 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1751 !SetJSPropertyString(aCx, obj, "message", aMessage)) {
1752 return;
1755 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1756 return;
1760 JS::Rooted<JS::Value> val(aCx);
1762 val = JS::ObjectValue(*featureArray);
1763 JS_SetProperty(aCx, containerObj, "features", val);
1765 val = JS::ObjectValue(*fallbackArray);
1766 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1768 return NS_OK;
1771 bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
1772 const FeatureState& aFeature,
1773 JS::MutableHandle<JS::Value> aOut) {
1774 JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
1775 if (!log) {
1776 return false;
1778 aOut.setObject(*log);
1780 aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
1781 const char* aMessage,
1782 const nsCString& aFailureId) -> void {
1783 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1784 if (!obj) {
1785 return;
1788 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1789 !SetJSPropertyString(aCx, obj, "status",
1790 FeatureStatusToString(aStatus)) ||
1791 (!aFailureId.IsEmpty() &&
1792 !SetJSPropertyString(aCx, obj, "failureId", aFailureId.get())) ||
1793 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
1794 return;
1797 if (!AppendJSElement(aCx, log, obj)) {
1798 return;
1802 return true;
1805 void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
1806 JS::Rooted<JSObject*> obj(aCx);
1808 gfx::FeatureState& hwCompositing =
1809 gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
1810 InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
1812 gfx::FeatureState& gpuProcess =
1813 gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
1814 InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
1816 gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
1817 InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
1819 gfx::FeatureState& wrCompositor =
1820 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
1821 InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
1823 gfx::FeatureState& openglCompositing =
1824 gfxConfig::GetFeature(gfx::Feature::OPENGL_COMPOSITING);
1825 InitFeatureObject(aCx, aObj, "openglCompositing", openglCompositing, &obj);
1827 gfx::FeatureState& omtp = gfxConfig::GetFeature(gfx::Feature::OMTP);
1828 InitFeatureObject(aCx, aObj, "omtp", omtp, &obj);
1831 bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
1832 JS::Handle<JSObject*> aContainer,
1833 const char* aName,
1834 mozilla::gfx::FeatureState& aFeatureState,
1835 JS::MutableHandle<JSObject*> aOutObj) {
1836 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1837 if (!obj) {
1838 return false;
1841 nsCString status = aFeatureState.GetStatusAndFailureIdString();
1843 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
1844 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1845 JS_SetProperty(aCx, obj, "status", val);
1847 // Add the feature object to the container.
1849 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1850 JS_SetProperty(aCx, aContainer, aName, val);
1853 aOutObj.set(obj);
1854 return true;
1857 nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
1858 JS::MutableHandle<JS::Value> aOut) {
1859 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1860 if (!array) {
1861 return NS_ERROR_OUT_OF_MEMORY;
1863 aOut.setObject(*array);
1865 DriverCrashGuard::ForEachActiveCrashGuard(
1866 [&](const char* aName, const char* aPrefName) -> void {
1867 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1868 if (!obj) {
1869 return;
1871 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1872 return;
1874 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1875 return;
1877 if (!AppendJSElement(aCx, array, obj)) {
1878 return;
1882 return NS_OK;
1885 NS_IMETHODIMP
1886 GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
1887 *aTargetFrameRate = gfxPlatform::TargetFrameRate();
1888 return NS_OK;
1891 NS_IMETHODIMP
1892 GfxInfoBase::GetCodecSupportInfo(nsACString& aCodecSupportInfo) {
1893 aCodecSupportInfo.Assign(gfx::gfxVars::CodecSupportInfo());
1894 return NS_OK;
1897 NS_IMETHODIMP
1898 GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
1899 *aIsHeadless = gfxPlatform::IsHeadless();
1900 return NS_OK;
1903 #if defined(MOZ_WIDGET_ANDROID)
1905 const char* chromebookProductList[] = {
1906 "asuka", "asurada", "atlas", "auron", "banjo", "banon",
1907 "bob", "brask", "brya", "buddy", "butterfly", "candy",
1908 "caroline", "cave", "celes", "chell", "cherry", "clapper",
1909 "coral", "corsola", "cyan", "daisy", "dedede", "drallion",
1910 "edgar", "elm", "enguarde", "eve", "expresso", "falco",
1911 "fizz", "gandof", "glimmer", "gnawty", "grunt", "guado",
1912 "guybrush", "hana", "hatch", "heli", "jacuzzi", "kalista",
1913 "kefka", "kevin", "kip", "kukui", "lars", "leon",
1914 "link", "lulu", "lumpy", "mccloud", "monroe", "nami",
1915 "nautilus", "ninja", "nissa", "nocturne", "nyan", "octopus",
1916 "orco", "panther", "parrot", "peach", "peppy", "puff",
1917 "pyro", "quawks", "rammus", "reef", "reks", "relm",
1918 "rikku", "samus", "sand", "sarien", "scarlet", "sentry",
1919 "setzer", "skyrim", "snappy", "soraka", "squawks", "staryu",
1920 "stout", "strongbad", "stumpy", "sumo", "swanky", "terra",
1921 "tidus", "tricky", "trogdor", "ultima", "veyron", "volteer",
1922 "winky", "wizpig", "wolf", "x86", "zako", "zork"};
1924 bool ProductIsChromebook(nsCString product) {
1925 size_t result;
1926 return BinarySearchIf(
1927 chromebookProductList, 0, ArrayLength(chromebookProductList),
1928 [&](const char* const aValue) -> int {
1929 return strcmp(product.get(), aValue);
1931 &result);
1933 #endif
1935 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
1936 static StaticAutoPtr<std::pair<Device, nsString>> ret;
1938 std::pair<Device, nsString>* GfxInfoBase::GetFontVisibilityDeterminationPair() {
1939 if (!ret) {
1940 ret = new std::pair<Device, nsString>();
1941 ret->first = Device::Unassigned;
1942 ret->second = u""_ns;
1943 ClearOnShutdown(&ret);
1946 if (ret->first != Device::Unassigned) {
1947 return ret;
1950 #if defined(MOZ_WIDGET_ANDROID)
1951 auto androidReleaseVersion = strtol(
1952 java::sdk::Build::VERSION::RELEASE()->ToCString().get(), nullptr, 10);
1954 auto androidManufacturer = java::sdk::Build::MANUFACTURER()->ToCString();
1955 nsContentUtils::ASCIIToLower(androidManufacturer);
1957 auto androidBrand = java::sdk::Build::BRAND()->ToCString();
1958 nsContentUtils::ASCIIToLower(androidBrand);
1960 auto androidModel = java::sdk::Build::MODEL()->ToCString();
1961 nsContentUtils::ASCIIToLower(androidModel);
1963 auto androidProduct = java::sdk::Build::PRODUCT()->ToCString();
1964 nsContentUtils::ASCIIToLower(androidProduct);
1966 auto androidProductIsChromebook = ProductIsChromebook(androidProduct);
1968 if (androidReleaseVersion < 4 || androidReleaseVersion > 20) {
1969 // Something is screwy, oh well.
1970 ret->second.AppendASCII("Unknown Release Version - ");
1971 ret->first = Device::Android_Unknown_Release_Version;
1972 } else if (androidReleaseVersion <= 8) {
1973 ret->second.AppendASCII("Android <9 - ");
1974 ret->first = Device::Android_sub_9;
1975 } else if (androidReleaseVersion <= 11) {
1976 ret->second.AppendASCII("Android 9-11 - ");
1977 ret->first = Device::Android_9_11;
1978 } else if (androidReleaseVersion > 11) {
1979 ret->second.AppendASCII("Android 12+ - ");
1980 ret->first = Device::Android_12_plus;
1981 } else {
1982 MOZ_CRASH_UNSAFE_PRINTF(
1983 "Somehow wound up in GetFontVisibilityDeterminationPair with a release "
1984 "version of %li",
1985 androidReleaseVersion);
1988 if (androidManufacturer == "google" && androidModel == androidProduct &&
1989 androidProductIsChromebook) {
1990 // Chromebook font set coming later
1991 ret->second.AppendASCII("Chromebook - ");
1992 ret->first = Device::Android_Chromebook;
1994 if (androidBrand == "amazon") {
1995 // Amazon Fire font set coming later
1996 ret->second.AppendASCII("Amazon - ");
1997 ret->first = Device::Android_Amazon;
1999 if (androidBrand == "peloton") {
2000 // We don't know how to categorize fonts on this system
2001 ret->second.AppendASCII("Peloton - ");
2002 ret->first = Device::Android_Unknown_Peloton;
2004 if (androidProduct == "vbox86p") {
2005 ret->second.AppendASCII("vbox - ");
2006 // We can't categorize fonts when running in an emulator on a Desktop
2007 ret->first = Device::Android_Unknown_vbox;
2009 if (androidModel.Find("mitv"_ns) != kNotFound && androidBrand == "xiaomi") {
2010 // We don't know how to categorize fonts on this system
2011 ret->second.AppendASCII("mitv - ");
2012 ret->first = Device::Android_Unknown_mitv;
2015 ret->second.AppendPrintf(
2016 "release_version_str=%s, release_version=%li",
2017 java::sdk::Build::VERSION::RELEASE()->ToCString().get(),
2018 androidReleaseVersion);
2019 ret->second.AppendPrintf(
2020 ", manufacturer=%s, brand=%s, model=%s, product=%s, chromebook=%s",
2021 androidManufacturer.get(), androidBrand.get(), androidModel.get(),
2022 androidProduct.get(), androidProductIsChromebook ? "yes" : "no");
2024 #elif defined(XP_LINUX)
2025 ret->first = Device::Linux_Unknown;
2027 long versionMajor = 0;
2028 FILE* fp = fopen("/etc/os-release", "r");
2029 if (fp) {
2030 char buf[512];
2031 while (fgets(buf, sizeof(buf), fp)) {
2032 if (strncmp(buf, "VERSION_ID=\"", 12) == 0) {
2033 ret->second.AppendPrintf("VERSION_ID=%.11s", buf + 11);
2034 versionMajor = strtol(buf + 12, nullptr, 10);
2035 if (ret->first != Device::Linux_Unknown) {
2036 break;
2040 if (strncmp(buf, "ID=", 3) == 0) {
2041 ret->second.AppendPrintf("ID=%.6s", buf + 3);
2042 if (strncmp(buf + 3, "ubuntu", 6) == 0) {
2043 ret->first = Device::Linux_Ubuntu_any;
2044 } else if (strncmp(buf + 3, "fedora", 6) == 0) {
2045 ret->first = Device::Linux_Fedora_any;
2048 if (versionMajor) {
2049 break;
2053 fclose(fp);
2055 if (ret->first == Device::Linux_Ubuntu_any) {
2056 if (versionMajor == 20) {
2057 ret->first = Device::Linux_Ubuntu_20;
2058 ret->second.Insert(u"Ubuntu 20 - ", 0);
2059 } else if (versionMajor == 22) {
2060 ret->first = Device::Linux_Ubuntu_22;
2061 ret->second.Insert(u"Ubuntu 22 - ", 0);
2062 } else {
2063 ret->second.Insert(u"Ubuntu Unknown - ", 0);
2065 } else if (ret->first == Device::Linux_Fedora_any) {
2066 if (versionMajor == 38) {
2067 ret->first = Device::Linux_Fedora_38;
2068 ret->second.Insert(u"Fedora 38 - ", 0);
2069 } else if (versionMajor == 39) {
2070 ret->first = Device::Linux_Fedora_39;
2071 ret->second.Insert(u"Fedora 39 - ", 0);
2072 } else {
2073 ret->second.Insert(u"Fedora Unknown - ", 0);
2075 } else {
2076 ret->second.Insert(u"Linux Unknown - ", 0);
2079 #elif defined(XP_MACOSX)
2080 ret->first = Device::MacOS_Platform;
2081 ret->second.AppendASCII("macOS Platform");
2082 #elif defined(XP_WIN)
2083 ret->first = Device::Windows_Platform;
2084 ret->second.AppendASCII("Windows Platform");
2085 #else
2086 ret->first = Device::Unknown_Platform;
2087 ret->second.AppendASCII("Unknown Platform");
2088 #endif
2090 return ret;
2093 NS_IMETHODIMP
2094 GfxInfoBase::GetFontVisibilityDetermination(
2095 Device* aFontVisibilityDetermination) {
2096 auto ret = GetFontVisibilityDeterminationPair();
2098 *aFontVisibilityDetermination = ret->first;
2099 return NS_OK;
2102 NS_IMETHODIMP
2103 GfxInfoBase::GetFontVisibilityDeterminationStr(
2104 nsAString& aFontVisibilityDeterminationStr) {
2105 auto ret = GetFontVisibilityDeterminationPair();
2106 aFontVisibilityDeterminationStr.Assign(ret->second);
2107 return NS_OK;
2110 NS_IMETHODIMP
2111 GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
2112 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
2113 nsString outStr;
2115 switch (backend) {
2116 case BackendType::DIRECT2D1_1: {
2117 outStr.AppendPrintf("Direct2D 1.1");
2118 break;
2120 case BackendType::SKIA: {
2121 outStr.AppendPrintf("Skia");
2122 break;
2124 case BackendType::CAIRO: {
2125 outStr.AppendPrintf("Cairo");
2126 break;
2128 default:
2129 return NS_ERROR_FAILURE;
2132 aContentBackend.Assign(outStr);
2133 return NS_OK;
2136 NS_IMETHODIMP
2137 GfxInfoBase::GetAzureCanvasBackend(nsAString& aBackend) {
2138 CopyASCIItoUTF16(mozilla::MakeStringSpan(
2139 gfxPlatform::GetPlatform()->GetAzureCanvasBackend()),
2140 aBackend);
2141 return NS_OK;
2144 NS_IMETHODIMP
2145 GfxInfoBase::GetAzureContentBackend(nsAString& aBackend) {
2146 CopyASCIItoUTF16(mozilla::MakeStringSpan(
2147 gfxPlatform::GetPlatform()->GetAzureContentBackend()),
2148 aBackend);
2149 return NS_OK;
2152 NS_IMETHODIMP
2153 GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
2154 GPUProcessManager* gpu = GPUProcessManager::Get();
2155 if (!gpu) {
2156 // Not supported in content processes.
2157 return NS_ERROR_FAILURE;
2160 *aOutValue = !!gpu->GetGPUChild();
2161 return NS_OK;
2164 NS_IMETHODIMP
2165 GfxInfoBase::GetUsingRemoteCanvas(bool* aOutValue) {
2166 *aOutValue = gfx::gfxVars::RemoteCanvasEnabled();
2167 return NS_OK;
2170 NS_IMETHODIMP
2171 GfxInfoBase::GetUsingAcceleratedCanvas(bool* aOutValue) {
2172 *aOutValue = gfx::gfxVars::UseAcceleratedCanvas2D();
2173 return NS_OK;
2176 NS_IMETHODIMP_(int32_t)
2177 GfxInfoBase::GetMaxRefreshRate(bool* aMixed) {
2178 if (aMixed) {
2179 *aMixed = false;
2182 int32_t maxRefreshRate = 0;
2183 for (auto& screen : ScreenManager::GetSingleton().CurrentScreenList()) {
2184 int32_t refreshRate = screen->GetRefreshRate();
2185 if (aMixed && maxRefreshRate > 0 && maxRefreshRate != refreshRate) {
2186 *aMixed = true;
2188 maxRefreshRate = std::max(maxRefreshRate, refreshRate);
2191 return maxRefreshRate > 0 ? maxRefreshRate : -1;
2194 NS_IMETHODIMP
2195 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
2196 gfxPlatform::GetPlatform();
2198 GPUProcessManager* gpm = GPUProcessManager::Get();
2199 if (aEnable) {
2200 if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
2201 gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
2203 DebugOnly<nsresult> rv = gpm->EnsureGPUReady();
2204 MOZ_ASSERT(rv != NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
2205 } else {
2206 gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
2207 gpm->KillProcess();
2210 *_retval = true;
2211 return NS_OK;
2214 NS_IMETHODIMP GfxInfoBase::KillGPUProcessForTests() {
2215 GPUProcessManager* gpm = GPUProcessManager::Get();
2216 if (!gpm) {
2217 // gfxPlatform has not been initialized.
2218 return NS_ERROR_NOT_INITIALIZED;
2221 gpm->KillProcess();
2222 return NS_OK;
2225 NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
2226 GPUProcessManager* gpm = GPUProcessManager::Get();
2227 if (!gpm) {
2228 // gfxPlatform has not been initialized.
2229 return NS_ERROR_NOT_INITIALIZED;
2232 gpm->CrashProcess();
2233 return NS_OK;
2236 GfxInfoCollectorBase::GfxInfoCollectorBase() {
2237 GfxInfoBase::AddCollector(this);
2240 GfxInfoCollectorBase::~GfxInfoCollectorBase() {
2241 GfxInfoBase::RemoveCollector(this);