no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / widget / GfxInfoBase.cpp
blob3e32ffbe15136884dc3f37627ede17ae33044897
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 default:
283 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
284 break;
287 return name;
290 // Returns the value of the pref for the relevant feature in aValue.
291 // If the pref doesn't exist, aValue is not touched, and returns false.
292 static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
293 nsACString& aFailureId) {
294 const char* prefname = GetPrefNameForFeature(aFeature);
295 if (!prefname) return false;
297 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
298 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
299 return false;
302 if (aValue == nsIGfxInfo::FEATURE_DENIED) {
303 // We should never see the DENIED status with the downloadable blocklist.
304 return false;
307 nsCString failureprefname(prefname);
308 failureprefname += ".failureid";
309 nsAutoCString failureValue;
310 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
311 if (NS_SUCCEEDED(rv)) {
312 aFailureId = failureValue.get();
313 } else {
314 aFailureId = "FEATURE_FAILURE_BLOCKLIST_PREF";
317 return true;
320 static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
321 const nsACString& aFailureId) {
322 const char* prefname = GetPrefNameForFeature(aFeature);
323 if (!prefname) return;
324 if (XRE_IsParentProcess()) {
325 GfxInfoBase::sFeatureStatus = nullptr;
328 Preferences::SetInt(prefname, aValue);
329 if (!aFailureId.IsEmpty()) {
330 nsAutoCString failureprefname(prefname);
331 failureprefname += ".failureid";
332 Preferences::SetCString(failureprefname.get(), aFailureId);
336 static void RemovePrefForFeature(int32_t aFeature) {
337 const char* prefname = GetPrefNameForFeature(aFeature);
338 if (!prefname) return;
340 if (XRE_IsParentProcess()) {
341 GfxInfoBase::sFeatureStatus = nullptr;
343 Preferences::ClearUser(prefname);
346 static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
347 return NS_SUCCEEDED(
348 Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
351 static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
352 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
355 static void RemovePrefForDriverVersion() {
356 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
359 static OperatingSystem BlocklistOSToOperatingSystem(const nsAString& os) {
360 if (os.EqualsLiteral("WINNT 6.1")) {
361 return OperatingSystem::Windows7;
363 if (os.EqualsLiteral("WINNT 6.2")) {
364 return OperatingSystem::Windows8;
366 if (os.EqualsLiteral("WINNT 6.3")) {
367 return OperatingSystem::Windows8_1;
369 if (os.EqualsLiteral("WINNT 10.0")) {
370 return OperatingSystem::Windows10;
372 if (os.EqualsLiteral("Linux")) {
373 return OperatingSystem::Linux;
375 if (os.EqualsLiteral("Darwin 9")) {
376 return OperatingSystem::OSX10_5;
378 if (os.EqualsLiteral("Darwin 10")) {
379 return OperatingSystem::OSX10_6;
381 if (os.EqualsLiteral("Darwin 11")) {
382 return OperatingSystem::OSX10_7;
384 if (os.EqualsLiteral("Darwin 12")) {
385 return OperatingSystem::OSX10_8;
387 if (os.EqualsLiteral("Darwin 13")) {
388 return OperatingSystem::OSX10_9;
390 if (os.EqualsLiteral("Darwin 14")) {
391 return OperatingSystem::OSX10_10;
393 if (os.EqualsLiteral("Darwin 15")) {
394 return OperatingSystem::OSX10_11;
396 if (os.EqualsLiteral("Darwin 16")) {
397 return OperatingSystem::OSX10_12;
399 if (os.EqualsLiteral("Darwin 17")) {
400 return OperatingSystem::OSX10_13;
402 if (os.EqualsLiteral("Darwin 18")) {
403 return OperatingSystem::OSX10_14;
405 if (os.EqualsLiteral("Darwin 19")) {
406 return OperatingSystem::OSX10_15;
408 if (os.EqualsLiteral("Darwin 20")) {
409 return OperatingSystem::OSX11_0;
411 if (os.EqualsLiteral("Android")) {
412 return OperatingSystem::Android;
413 // For historical reasons, "All" in blocklist means "All Windows"
415 if (os.EqualsLiteral("All")) {
416 return OperatingSystem::Windows;
418 if (os.EqualsLiteral("Darwin")) {
419 return OperatingSystem::OSX;
422 return OperatingSystem::Unknown;
425 static GfxDeviceFamily* BlocklistDevicesToDeviceFamily(
426 nsTArray<nsCString>& devices) {
427 if (devices.Length() == 0) return nullptr;
429 // For each device, get its device ID, and return a freshly-allocated
430 // GfxDeviceFamily with the contents of that array.
431 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
433 for (uint32_t i = 0; i < devices.Length(); ++i) {
434 // We make sure we don't add any "empty" device entries to the array, so
435 // we don't need to check if devices[i] is empty.
436 deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
439 return deviceIds;
442 static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) {
443 MOZ_ASSERT(!aFeature.IsEmpty());
444 if (aFeature.EqualsLiteral("DIRECT2D")) {
445 return nsIGfxInfo::FEATURE_DIRECT2D;
447 if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS")) {
448 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
450 if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS")) {
451 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
453 if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS")) {
454 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
456 if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS")) {
457 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
459 if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE")) {
460 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
462 if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING")) {
463 return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
465 if (aFeature.EqualsLiteral("OPENGL_LAYERS")) {
466 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
468 if (aFeature.EqualsLiteral("WEBGL_OPENGL")) {
469 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
471 if (aFeature.EqualsLiteral("WEBGL_ANGLE")) {
472 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
474 if (aFeature.EqualsLiteral("WEBGL_MSAA")) {
475 return nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA;
477 if (aFeature.EqualsLiteral("STAGEFRIGHT")) {
478 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
480 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE")) {
481 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
483 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE")) {
484 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
486 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_H264")) {
487 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264;
489 if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION")) {
490 return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
492 if (aFeature.EqualsLiteral("DX_INTEROP2")) {
493 return nsIGfxInfo::FEATURE_DX_INTEROP2;
495 if (aFeature.EqualsLiteral("GPU_PROCESS")) {
496 return nsIGfxInfo::FEATURE_GPU_PROCESS;
498 if (aFeature.EqualsLiteral("WEBGL2")) {
499 return nsIGfxInfo::FEATURE_WEBGL2;
501 if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX")) {
502 return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
504 if (aFeature.EqualsLiteral("WEBRENDER")) {
505 return nsIGfxInfo::FEATURE_WEBRENDER;
507 if (aFeature.EqualsLiteral("WEBRENDER_COMPOSITOR")) {
508 return nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR;
510 if (aFeature.EqualsLiteral("DX_NV12")) {
511 return nsIGfxInfo::FEATURE_DX_NV12;
513 if (aFeature.EqualsLiteral("VP8_HW_DECODE")) {
514 return nsIGfxInfo::FEATURE_VP8_HW_DECODE;
516 if (aFeature.EqualsLiteral("VP9_HW_DECODE")) {
517 return nsIGfxInfo::FEATURE_VP9_HW_DECODE;
519 if (aFeature.EqualsLiteral("GL_SWIZZLE")) {
520 return nsIGfxInfo::FEATURE_GL_SWIZZLE;
522 if (aFeature.EqualsLiteral("WEBRENDER_SCISSORED_CACHE_CLEARS")) {
523 return nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS;
525 if (aFeature.EqualsLiteral("ALLOW_WEBGL_OUT_OF_PROCESS")) {
526 return nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS;
528 if (aFeature.EqualsLiteral("THREADSAFE_GL")) {
529 return nsIGfxInfo::FEATURE_THREADSAFE_GL;
531 if (aFeature.EqualsLiteral("X11_EGL")) {
532 return nsIGfxInfo::FEATURE_X11_EGL;
534 if (aFeature.EqualsLiteral("DMABUF")) {
535 return nsIGfxInfo::FEATURE_DMABUF;
537 if (aFeature.EqualsLiteral("WEBGPU")) {
538 return nsIGfxInfo::FEATURE_WEBGPU;
540 if (aFeature.EqualsLiteral("VIDEO_OVERLAY")) {
541 return nsIGfxInfo::FEATURE_VIDEO_OVERLAY;
543 if (aFeature.EqualsLiteral("HW_DECODED_VIDEO_ZERO_COPY")) {
544 return nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY;
546 if (aFeature.EqualsLiteral("REUSE_DECODER_DEVICE")) {
547 return nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE;
549 if (aFeature.EqualsLiteral("WEBRENDER_PARTIAL_PRESENT")) {
550 return nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT;
552 if (aFeature.EqualsLiteral("BACKDROP_FILTER")) {
553 return nsIGfxInfo::FEATURE_BACKDROP_FILTER;
555 if (aFeature.EqualsLiteral("ACCELERATED_CANVAS2D")) {
556 return nsIGfxInfo::FEATURE_ACCELERATED_CANVAS2D;
558 if (aFeature.EqualsLiteral("ALL")) {
559 return GfxDriverInfo::allFeatures;
561 if (aFeature.EqualsLiteral("OPTIONAL")) {
562 return GfxDriverInfo::optionalFeatures;
565 // If we don't recognize the feature, it may be new, and something
566 // this version doesn't understand. So, nothing to do. This is
567 // different from feature not being specified at all, in which case
568 // this method should not get called and we should continue with the
569 // "optional features" blocklisting.
570 return 0;
573 static int32_t BlocklistFeatureStatusToGfxFeatureStatus(
574 const nsAString& aStatus) {
575 if (aStatus.EqualsLiteral("STATUS_OK")) {
576 return nsIGfxInfo::FEATURE_STATUS_OK;
578 if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION")) {
579 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
581 if (aStatus.EqualsLiteral("BLOCKED_DEVICE")) {
582 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
584 if (aStatus.EqualsLiteral("DISCOURAGED")) {
585 return nsIGfxInfo::FEATURE_DISCOURAGED;
587 if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION")) {
588 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
590 if (aStatus.EqualsLiteral("DENIED")) {
591 return nsIGfxInfo::FEATURE_DENIED;
593 if (aStatus.EqualsLiteral("ALLOW_QUALIFIED")) {
594 return nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
596 if (aStatus.EqualsLiteral("ALLOW_ALWAYS")) {
597 return nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
600 // Do not allow it to set STATUS_UNKNOWN. Also, we are not
601 // expecting the "mismatch" status showing up here.
603 return nsIGfxInfo::FEATURE_STATUS_OK;
606 static VersionComparisonOp BlocklistComparatorToComparisonOp(
607 const nsAString& op) {
608 if (op.EqualsLiteral("LESS_THAN")) {
609 return DRIVER_LESS_THAN;
611 if (op.EqualsLiteral("BUILD_ID_LESS_THAN")) {
612 return DRIVER_BUILD_ID_LESS_THAN;
614 if (op.EqualsLiteral("LESS_THAN_OR_EQUAL")) {
615 return DRIVER_LESS_THAN_OR_EQUAL;
617 if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL")) {
618 return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
620 if (op.EqualsLiteral("GREATER_THAN")) {
621 return DRIVER_GREATER_THAN;
623 if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL")) {
624 return DRIVER_GREATER_THAN_OR_EQUAL;
626 if (op.EqualsLiteral("EQUAL")) {
627 return DRIVER_EQUAL;
629 if (op.EqualsLiteral("NOT_EQUAL")) {
630 return DRIVER_NOT_EQUAL;
632 if (op.EqualsLiteral("BETWEEN_EXCLUSIVE")) {
633 return DRIVER_BETWEEN_EXCLUSIVE;
635 if (op.EqualsLiteral("BETWEEN_INCLUSIVE")) {
636 return DRIVER_BETWEEN_INCLUSIVE;
638 if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START")) {
639 return DRIVER_BETWEEN_INCLUSIVE_START;
642 return DRIVER_COMPARISON_IGNORED;
646 Deserialize Blocklist entries from string.
647 e.g:
648 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
650 static bool BlocklistEntryToDriverInfo(const nsACString& aBlocklistEntry,
651 GfxDriverInfo& aDriverInfo) {
652 // If we get an application version to be zero, something is not working
653 // and we are not going to bother checking the blocklist versions.
654 // See TestGfxWidgets.cpp for how version comparison works.
655 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
656 static mozilla::Version zeroV("0");
657 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
658 if (appV <= zeroV) {
659 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
660 << "Invalid application version "
661 << GfxInfoBase::GetApplicationVersion().get();
664 aDriverInfo.mRuleId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID"_ns;
666 for (const auto& keyValue : aBlocklistEntry.Split('\t')) {
667 nsTArray<nsCString> splitted;
668 ParseString(keyValue, ':', splitted);
669 if (splitted.Length() != 2) {
670 // If we don't recognize the input data, we do not want to proceed.
671 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
672 << "Unrecognized data " << nsCString(keyValue).get();
673 return false;
675 const nsCString& key = splitted[0];
676 const nsCString& value = splitted[1];
677 NS_ConvertUTF8toUTF16 dataValue(value);
679 if (value.Length() == 0) {
680 // Safety check for empty values.
681 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
682 << "Empty value for " << key.get();
683 return false;
686 if (key.EqualsLiteral("blockID")) {
687 nsCString blockIdStr = "FEATURE_FAILURE_DL_BLOCKLIST_"_ns + value;
688 aDriverInfo.mRuleId = blockIdStr.get();
689 } else if (key.EqualsLiteral("os")) {
690 aDriverInfo.mOperatingSystem = BlocklistOSToOperatingSystem(dataValue);
691 } else if (key.EqualsLiteral("osversion")) {
692 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
693 } else if (key.EqualsLiteral("windowProtocol")) {
694 aDriverInfo.mWindowProtocol = dataValue;
695 } else if (key.EqualsLiteral("vendor")) {
696 aDriverInfo.mAdapterVendor = dataValue;
697 } else if (key.EqualsLiteral("driverVendor")) {
698 aDriverInfo.mDriverVendor = dataValue;
699 } else if (key.EqualsLiteral("feature")) {
700 aDriverInfo.mFeature = BlocklistFeatureToGfxFeature(dataValue);
701 if (aDriverInfo.mFeature == 0) {
702 // If we don't recognize the feature, we do not want to proceed.
703 gfxWarning() << "Unrecognized feature " << value.get();
704 return false;
706 } else if (key.EqualsLiteral("featureStatus")) {
707 aDriverInfo.mFeatureStatus =
708 BlocklistFeatureStatusToGfxFeatureStatus(dataValue);
709 } else if (key.EqualsLiteral("driverVersion")) {
710 uint64_t version;
711 if (ParseDriverVersion(dataValue, &version))
712 aDriverInfo.mDriverVersion = version;
713 } else if (key.EqualsLiteral("driverVersionMax")) {
714 uint64_t version;
715 if (ParseDriverVersion(dataValue, &version))
716 aDriverInfo.mDriverVersionMax = version;
717 } else if (key.EqualsLiteral("driverVersionComparator")) {
718 aDriverInfo.mComparisonOp = BlocklistComparatorToComparisonOp(dataValue);
719 } else if (key.EqualsLiteral("model")) {
720 aDriverInfo.mModel = dataValue;
721 } else if (key.EqualsLiteral("product")) {
722 aDriverInfo.mProduct = dataValue;
723 } else if (key.EqualsLiteral("manufacturer")) {
724 aDriverInfo.mManufacturer = dataValue;
725 } else if (key.EqualsLiteral("hardware")) {
726 aDriverInfo.mHardware = dataValue;
727 } else if (key.EqualsLiteral("versionRange")) {
728 nsTArray<nsCString> versionRange;
729 ParseString(value, ',', versionRange);
730 if (versionRange.Length() != 2) {
731 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
732 << "Unrecognized versionRange " << value.get();
733 return false;
735 const nsCString& minValue = versionRange[0];
736 const nsCString& maxValue = versionRange[1];
738 mozilla::Version minV(minValue.get());
739 mozilla::Version maxV(maxValue.get());
741 if (minV > zeroV && !(appV >= minV)) {
742 // The version of the application is less than the minimal version
743 // this blocklist entry applies to, so we can just ignore it by
744 // returning false and letting the caller deal with it.
745 return false;
747 if (maxV > zeroV && !(appV <= maxV)) {
748 // The version of the application is more than the maximal version
749 // this blocklist entry applies to, so we can just ignore it by
750 // returning false and letting the caller deal with it.
751 return false;
753 } else if (key.EqualsLiteral("devices")) {
754 nsTArray<nsCString> devices;
755 ParseString(value, ',', devices);
756 GfxDeviceFamily* deviceIds = BlocklistDevicesToDeviceFamily(devices);
757 if (deviceIds) {
758 // Get GfxDriverInfo to adopt the devices array we created.
759 aDriverInfo.mDeleteDevices = true;
760 aDriverInfo.mDevices = deviceIds;
763 // We explicitly ignore unknown elements.
766 return true;
769 NS_IMETHODIMP
770 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
771 const char16_t* aData) {
772 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
773 nsTArray<GfxDriverInfo> driverInfo;
774 NS_ConvertUTF16toUTF8 utf8Data(aData);
776 for (const auto& blocklistEntry : utf8Data.Split('\n')) {
777 GfxDriverInfo di;
778 if (BlocklistEntryToDriverInfo(blocklistEntry, di)) {
779 // XXX Changing this to driverInfo.AppendElement(di) causes leaks.
780 // Probably some non-standard semantics of the copy/move operations?
781 *driverInfo.AppendElement() = di;
782 // Prevent di falling out of scope from destroying the devices.
783 di.mDeleteDevices = false;
784 } else {
785 driverInfo.AppendElement();
789 EvaluateDownloadedBlocklist(driverInfo);
792 return NS_OK;
795 GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
797 GfxInfoBase::~GfxInfoBase() = default;
799 nsresult GfxInfoBase::Init() {
800 InitGfxDriverInfoShutdownObserver();
802 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
803 if (os) {
804 os->AddObserver(this, "blocklist-data-gfxItems", true);
807 return NS_OK;
810 void GfxInfoBase::GetData() {
811 if (mScreenPixels != INT64_MAX) {
812 // Already initialized.
813 return;
816 ScreenManager::GetSingleton().GetTotalScreenPixels(&mScreenPixels);
819 NS_IMETHODIMP
820 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
821 int32_t* aStatus) {
822 // Ignore the gfx.blocklist.all pref on release and beta.
823 #if defined(RELEASE_OR_BETA)
824 int32_t blocklistAll = 0;
825 #else
826 int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
827 #endif
828 if (blocklistAll > 0) {
829 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
830 << "Forcing blocklisting all features";
831 *aStatus = FEATURE_BLOCKED_DEVICE;
832 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
833 return NS_OK;
836 if (blocklistAll < 0) {
837 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
838 << "Ignoring any feature blocklisting.";
839 *aStatus = FEATURE_STATUS_OK;
840 return NS_OK;
843 // This is how we evaluate the downloadable blocklist. If there is no pref,
844 // then we will fallback to checking the static blocklist.
845 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
846 return NS_OK;
849 if (XRE_IsContentProcess() || XRE_IsGPUProcess()) {
850 // Use the cached data received from the parent process.
851 MOZ_ASSERT(sFeatureStatus);
852 bool success = false;
853 for (const auto& fs : *sFeatureStatus) {
854 if (fs.feature() == aFeature) {
855 aFailureId = fs.failureId();
856 *aStatus = fs.status();
857 success = true;
858 break;
861 return success ? NS_OK : NS_ERROR_FAILURE;
864 nsString version;
865 nsTArray<GfxDriverInfo> driverInfo;
866 nsresult rv =
867 GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
868 return rv;
871 nsTArray<gfx::GfxInfoFeatureStatus> GfxInfoBase::GetAllFeatures() {
872 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
873 if (!sFeatureStatus) {
874 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>());
875 for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
876 int32_t status = 0;
877 nsAutoCString failureId;
878 GetFeatureStatus(i, failureId, &status);
879 gfx::GfxInfoFeatureStatus gfxFeatureStatus;
880 gfxFeatureStatus.feature() = i;
881 gfxFeatureStatus.status() = status;
882 gfxFeatureStatus.failureId() = failureId;
883 sFeatureStatus->AppendElement(gfxFeatureStatus);
887 nsTArray<gfx::GfxInfoFeatureStatus> features;
888 for (const auto& status : *sFeatureStatus) {
889 gfx::GfxInfoFeatureStatus copy = status;
890 features.AppendElement(copy);
892 return features;
895 inline bool MatchingAllowStatus(int32_t aStatus) {
896 switch (aStatus) {
897 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
898 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
899 return true;
900 default:
901 return false;
905 // Matching OS go somewhat beyond the simple equality check because of the
906 // "All Windows" and "All OS X" variations.
908 // aBlockedOS is describing the system(s) we are trying to block.
909 // aSystemOS is describing the system we are running on.
911 // aSystemOS should not be "Windows" or "OSX" - it should be set to
912 // a particular version instead.
913 // However, it is valid for aBlockedOS to be one of those generic values,
914 // as we could be blocking all of the versions.
915 inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
916 OperatingSystem aSystemOS,
917 uint32_t aSystemOSBuild) {
918 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
919 aSystemOS != OperatingSystem::OSX);
921 // If the block entry OS is unknown, it doesn't match
922 if (aBlockedOS == OperatingSystem::Unknown) {
923 return false;
926 #if defined(XP_WIN)
927 if (aBlockedOS == OperatingSystem::Windows) {
928 // We do want even "unknown" aSystemOS to fall under "all windows"
929 return true;
932 constexpr uint32_t kMinWin10BuildNumber = 18362;
933 if (aBlockedOS == OperatingSystem::RecentWindows10 &&
934 aSystemOS == OperatingSystem::Windows10) {
935 // For allowlist purposes, we sometimes want to restrict to only recent
936 // versions of Windows 10. This is a bit of a kludge but easier than adding
937 // complicated blocklist infrastructure for build ID comparisons like driver
938 // versions.
939 return aSystemOSBuild >= kMinWin10BuildNumber;
942 if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
943 if (aSystemOS == OperatingSystem::Windows10) {
944 return aSystemOSBuild < kMinWin10BuildNumber;
945 } else {
946 return true;
949 #endif
951 #if defined(XP_MACOSX)
952 if (aBlockedOS == OperatingSystem::OSX) {
953 // We do want even "unknown" aSystemOS to fall under "all OS X"
954 return true;
956 #endif
958 return aSystemOS == aBlockedOS;
961 inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
962 switch (aBatteryStatus) {
963 case BatteryStatus::All:
964 return true;
965 case BatteryStatus::None:
966 return !aHasBattery;
967 case BatteryStatus::Present:
968 return aHasBattery;
971 MOZ_ASSERT_UNREACHABLE("bad battery status");
972 return false;
975 inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
976 int64_t aScreenPixels) {
977 constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
978 constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
980 switch (aScreenStatus) {
981 case ScreenSizeStatus::All:
982 return true;
983 case ScreenSizeStatus::Small:
984 return aScreenPixels <= kMaxSmallPixels;
985 case ScreenSizeStatus::SmallAndMedium:
986 return aScreenPixels <= kMaxMediumPixels;
987 case ScreenSizeStatus::Medium:
988 return aScreenPixels > kMaxSmallPixels &&
989 aScreenPixels <= kMaxMediumPixels;
990 case ScreenSizeStatus::MediumAndLarge:
991 return aScreenPixels > kMaxSmallPixels;
992 case ScreenSizeStatus::Large:
993 return aScreenPixels > kMaxMediumPixels;
996 MOZ_ASSERT_UNREACHABLE("bad screen status");
997 return false;
1000 int32_t GfxInfoBase::FindBlocklistedDeviceInList(
1001 const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
1002 int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
1003 bool aForAllowing) {
1004 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
1006 // Some properties are not available on all platforms.
1007 nsAutoString windowProtocol;
1008 nsresult rv = GetWindowProtocol(windowProtocol);
1009 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
1010 return 0;
1013 bool hasBattery = false;
1014 rv = GetHasBattery(&hasBattery);
1015 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
1016 return 0;
1019 uint32_t osBuild = OperatingSystemBuild();
1021 // Get the adapters once then reuse below
1022 nsAutoString adapterVendorID[2];
1023 nsAutoString adapterDeviceID[2];
1024 nsAutoString adapterDriverVendor[2];
1025 nsAutoString adapterDriverVersionString[2];
1026 bool adapterInfoFailed[2];
1028 adapterInfoFailed[0] =
1029 (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
1030 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
1031 NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
1032 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
1033 adapterInfoFailed[1] =
1034 (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
1035 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
1036 NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
1037 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
1038 // No point in going on if we don't have adapter info
1039 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
1040 return 0;
1043 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1044 uint64_t driverVersion[2] = {0, 0};
1045 if (!adapterInfoFailed[0]) {
1046 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
1048 if (!adapterInfoFailed[1]) {
1049 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
1051 #endif
1053 uint32_t i = 0;
1054 for (; i < info.Length(); i++) {
1055 // If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
1056 // blocklisting. Only consider entries for our search mode.
1057 if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
1058 continue;
1061 // If we don't have the info for this GPU, no need to check further.
1062 // It is unclear that we would ever have a mixture of 1st and 2nd
1063 // GPU, but leaving the code in for that possibility for now.
1064 // (Actually, currently mGpu2 will never be true, so this can
1065 // be optimized out.)
1066 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
1067 if (adapterInfoFailed[infoIndex]) {
1068 continue;
1071 // Do the operating system check first, no point in getting the driver
1072 // info if we won't need to use it.
1073 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
1074 continue;
1077 if (info[i].mOperatingSystemVersion &&
1078 info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
1079 continue;
1082 if (!MatchingBattery(info[i].mBattery, hasBattery)) {
1083 continue;
1086 if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
1087 continue;
1090 if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
1091 continue;
1094 if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
1095 continue;
1098 if (!DoesDriverVendorMatch(info[i].mDriverVendor,
1099 adapterDriverVendor[infoIndex])) {
1100 continue;
1103 if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
1104 nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
1105 if (rv == NS_ERROR_NOT_AVAILABLE) {
1106 // Not found
1107 continue;
1109 if (rv != NS_OK) {
1110 // Failed to search, allowlist should not match, blocklist should match
1111 // for safety reasons
1112 if (aForAllowing) {
1113 continue;
1115 break;
1119 bool match = false;
1121 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
1122 continue;
1124 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
1125 continue;
1127 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
1128 continue;
1130 if (!info[i].mManufacturer.IsEmpty() &&
1131 !info[i].mManufacturer.Equals(Manufacturer())) {
1132 continue;
1135 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1136 switch (info[i].mComparisonOp) {
1137 case DRIVER_LESS_THAN:
1138 match = driverVersion[infoIndex] < info[i].mDriverVersion;
1139 break;
1140 case DRIVER_BUILD_ID_LESS_THAN:
1141 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
1142 break;
1143 case DRIVER_LESS_THAN_OR_EQUAL:
1144 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
1145 break;
1146 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
1147 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
1148 break;
1149 case DRIVER_GREATER_THAN:
1150 match = driverVersion[infoIndex] > info[i].mDriverVersion;
1151 break;
1152 case DRIVER_GREATER_THAN_OR_EQUAL:
1153 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
1154 break;
1155 case DRIVER_EQUAL:
1156 match = driverVersion[infoIndex] == info[i].mDriverVersion;
1157 break;
1158 case DRIVER_NOT_EQUAL:
1159 match = driverVersion[infoIndex] != info[i].mDriverVersion;
1160 break;
1161 case DRIVER_BETWEEN_EXCLUSIVE:
1162 match = driverVersion[infoIndex] > info[i].mDriverVersion &&
1163 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1164 break;
1165 case DRIVER_BETWEEN_INCLUSIVE:
1166 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1167 driverVersion[infoIndex] <= info[i].mDriverVersionMax;
1168 break;
1169 case DRIVER_BETWEEN_INCLUSIVE_START:
1170 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1171 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1172 break;
1173 case DRIVER_COMPARISON_IGNORED:
1174 // We don't have a comparison op, so we match everything.
1175 match = true;
1176 break;
1177 default:
1178 NS_WARNING("Bogus op in GfxDriverInfo");
1179 break;
1181 #else
1182 // We don't care what driver version it was. We only check OS version and if
1183 // the device matches.
1184 match = true;
1185 #endif
1187 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
1188 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
1189 info[i].mFeature == aFeature ||
1190 (info[i].mFeature == GfxDriverInfo::optionalFeatures &&
1191 OnlyAllowFeatureOnKnownConfig(aFeature))) {
1192 status = info[i].mFeatureStatus;
1193 if (!info[i].mRuleId.IsEmpty()) {
1194 aFailureId = info[i].mRuleId.get();
1195 } else {
1196 aFailureId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID";
1198 break;
1203 #if defined(XP_WIN)
1204 // As a very special case, we block D2D on machines with an NVidia 310M GPU
1205 // as either the primary or secondary adapter. D2D is also blocked when the
1206 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
1207 // If the primary GPU already matched something in the blocklist then we
1208 // ignore this special rule. See bug 1008759.
1209 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
1210 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
1211 if (!adapterInfoFailed[1]) {
1212 nsAString& nvVendorID =
1213 (nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
1214 const nsString nv310mDeviceId = u"0x0A70"_ns;
1215 if (nvVendorID.Equals(adapterVendorID[1],
1216 nsCaseInsensitiveStringComparator) &&
1217 nv310mDeviceId.Equals(adapterDeviceID[1],
1218 nsCaseInsensitiveStringComparator)) {
1219 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1220 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
1225 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
1226 // back to the Windows handler, so we must handle this here.
1227 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
1228 if (info[i].mSuggestedVersion) {
1229 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
1230 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
1231 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
1232 aSuggestedVersion.AppendPrintf(
1233 "%lld.%lld.%lld.%lld",
1234 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
1235 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
1236 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
1237 (info[i].mDriverVersion & 0x000000000000ffff));
1240 #endif
1242 return status;
1245 void GfxInfoBase::SetFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>&& aFS) {
1246 MOZ_ASSERT(!sFeatureStatus);
1247 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>(std::move(aFS)));
1250 bool GfxInfoBase::DoesWindowProtocolMatch(
1251 const nsAString& aBlocklistWindowProtocol,
1252 const nsAString& aWindowProtocol) {
1253 return aBlocklistWindowProtocol.Equals(aWindowProtocol,
1254 nsCaseInsensitiveStringComparator) ||
1255 aBlocklistWindowProtocol.Equals(
1256 GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
1257 nsCaseInsensitiveStringComparator);
1260 bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
1261 const nsAString& aAdapterVendor) {
1262 return aBlocklistVendor.Equals(aAdapterVendor,
1263 nsCaseInsensitiveStringComparator) ||
1264 aBlocklistVendor.Equals(
1265 GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
1266 nsCaseInsensitiveStringComparator);
1269 bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
1270 const nsAString& aDriverVendor) {
1271 return aBlocklistVendor.Equals(aDriverVendor,
1272 nsCaseInsensitiveStringComparator) ||
1273 aBlocklistVendor.Equals(
1274 GfxDriverInfo::GetDriverVendor(DriverVendor::All),
1275 nsCaseInsensitiveStringComparator);
1278 bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
1279 return aFeature == nsIGfxInfo::FEATURE_VIDEO_OVERLAY ||
1280 aFeature == nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY;
1283 nsresult GfxInfoBase::GetFeatureStatusImpl(
1284 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
1285 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
1286 OperatingSystem* aOS /* = nullptr */) {
1287 if (aFeature <= 0) {
1288 gfxWarning() << "Invalid feature <= 0";
1289 return NS_OK;
1292 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1293 // Terminate now with the status determined by the derived type (OS-specific
1294 // code).
1295 return NS_OK;
1298 if (sShutdownOccurred) {
1299 // This is futile; we've already commenced shutdown and our blocklists have
1300 // been deleted. We may want to look into resurrecting the blocklist instead
1301 // but for now, just don't even go there.
1302 return NS_OK;
1305 // Ensure any additional initialization required is complete.
1306 GetData();
1308 // If an operating system was provided by the derived GetFeatureStatusImpl,
1309 // grab it here. Otherwise, the OS is unknown.
1310 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
1312 nsAutoString adapterVendorID;
1313 nsAutoString adapterDeviceID;
1314 nsAutoString adapterDriverVersionString;
1315 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
1316 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
1317 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
1318 if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
1319 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
1320 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1321 } else {
1322 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
1324 return NS_OK;
1327 // We only check either the given blocklist, or the static list, as given.
1328 int32_t status;
1329 if (aDriverInfo.Length()) {
1330 status =
1331 FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
1332 aFailureId, os, /* aForAllowing */ false);
1333 } else {
1334 if (!sDriverInfo) {
1335 sDriverInfo = new nsTArray<GfxDriverInfo>();
1337 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
1338 aFeature, aFailureId, os,
1339 /* aForAllowing */ false);
1342 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1343 if (IsFeatureAllowlisted(aFeature)) {
1344 // This feature is actually using the allowlist; that means after we pass
1345 // the blocklist to prevent us explicitly from getting the feature, we now
1346 // need to check the allowlist to ensure we are allowed to get it in the
1347 // first place.
1348 if (aDriverInfo.Length()) {
1349 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
1350 aFeature, aFailureId, os,
1351 /* aForAllowing */ true);
1352 } else {
1353 status = FindBlocklistedDeviceInList(
1354 GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
1355 /* aForAllowing */ true);
1358 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1359 status = nsIGfxInfo::FEATURE_DENIED;
1361 } else {
1362 // It's now done being processed. It's safe to set the status to
1363 // STATUS_OK.
1364 status = nsIGfxInfo::FEATURE_STATUS_OK;
1368 *aStatus = status;
1369 return NS_OK;
1372 NS_IMETHODIMP
1373 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
1374 nsAString& aVersion) {
1375 nsCString version;
1376 if (GetPrefValueForDriverVersion(version)) {
1377 aVersion = NS_ConvertASCIItoUTF16(version);
1378 return NS_OK;
1381 int32_t status;
1382 nsCString discardFailureId;
1383 nsTArray<GfxDriverInfo> driverInfo;
1384 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
1385 discardFailureId);
1388 void GfxInfoBase::EvaluateDownloadedBlocklist(
1389 nsTArray<GfxDriverInfo>& aDriverInfo) {
1390 // If the list is empty, then we don't actually want to call
1391 // GetFeatureStatusImpl since we will use the static list instead. In that
1392 // case, all we want to do is make sure the pref is removed.
1393 if (aDriverInfo.IsEmpty()) {
1394 gfxCriticalNoteOnce << "Evaluate empty downloaded blocklist";
1395 return;
1398 OperatingSystem os = GetOperatingSystem();
1400 // For every feature we know about, we evaluate whether this blocklist has a
1401 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1402 // GetFeatureStatus above, so we don't need to hold on to this blocklist
1403 // anywhere permanent.
1404 for (int feature = 1; feature <= nsIGfxInfo::FEATURE_MAX_VALUE; ++feature) {
1405 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
1406 nsCString failureId;
1407 nsAutoString suggestedVersion;
1409 // Note that we are careful to call the base class method since we only want
1410 // to evaluate the downloadable blocklist for these prefs.
1411 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GfxInfoBase::GetFeatureStatusImpl(
1412 feature, &status, suggestedVersion, aDriverInfo, failureId, &os)));
1414 switch (status) {
1415 default:
1416 MOZ_FALLTHROUGH_ASSERT("Unhandled feature status!");
1417 case nsIGfxInfo::FEATURE_STATUS_UNKNOWN:
1418 // This may be returned during shutdown or for invalid features.
1419 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
1420 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
1421 case nsIGfxInfo::FEATURE_DENIED:
1422 // We cannot use the downloadable blocklist to control the allowlist.
1423 // If a feature is allowlisted, then we should also ignore DENIED
1424 // statuses from GetFeatureStatusImpl because we don't check the
1425 // static list when and this is an expected value. If we wish to
1426 // override the allowlist, it is as simple as creating a normal
1427 // blocklist rule with a BLOCKED* status code.
1428 case nsIGfxInfo::FEATURE_STATUS_OK:
1429 RemovePrefForFeature(feature);
1430 break;
1432 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1433 if (!suggestedVersion.IsEmpty()) {
1434 SetPrefValueForDriverVersion(suggestedVersion);
1435 } else {
1436 RemovePrefForDriverVersion();
1438 [[fallthrough]];
1440 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1441 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1442 case nsIGfxInfo::FEATURE_DISCOURAGED:
1443 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1444 case nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST:
1445 SetPrefValueForFeature(feature, status, failureId);
1446 break;
1451 NS_IMETHODIMP_(void)
1452 GfxInfoBase::LogFailure(const nsACString& failure) {
1453 // gfxCriticalError has a mutex lock of its own, so we may not actually
1454 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1455 // will not return the copy of the logs unless it can get the same lock
1456 // that gfxCriticalError uses. Still, that is so much of an implementation
1457 // detail that it's nicer to just add an extra lock here and in
1458 // ::GetFailures()
1459 MutexAutoLock lock(mMutex);
1461 // By default, gfxCriticalError asserts; make it not assert in this case.
1462 gfxCriticalError(CriticalLog::DefaultOptions(false))
1463 << "(LF) " << failure.BeginReading();
1466 NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
1467 nsTArray<nsCString>& failures) {
1468 MutexAutoLock lock(mMutex);
1470 LogForwarder* logForwarder = Factory::GetLogForwarder();
1471 if (!logForwarder) {
1472 return NS_ERROR_UNEXPECTED;
1475 // There are two string copies in this method, starting with this one. We are
1476 // assuming this is not a big deal, as the size of the array should be small
1477 // and the strings in it should be small as well (the error messages in the
1478 // code.) The second copy happens with the AppendElement() calls.
1479 // Technically, we don't need the mutex lock after the StringVectorCopy()
1480 // call.
1481 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1482 LoggingRecord::const_iterator it;
1483 for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
1484 failures.AppendElement(nsDependentCSubstring(std::get<1>(*it).c_str(),
1485 std::get<1>(*it).size()));
1486 indices.AppendElement(std::get<0>(*it));
1489 return NS_OK;
1492 nsTArray<GfxInfoCollectorBase*>* sCollectors;
1494 static void InitCollectors() {
1495 if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1498 nsresult GfxInfoBase::GetInfo(JSContext* aCx,
1499 JS::MutableHandle<JS::Value> aResult) {
1500 InitCollectors();
1501 InfoObject obj(aCx);
1503 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1504 (*sCollectors)[i]->GetInfo(obj);
1507 // Some example property definitions
1508 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1509 // obj.DefineProperty("renderer", mRendererIDsString);
1510 // obj.DefineProperty("five", 5);
1512 if (!obj.mOk) {
1513 return NS_ERROR_FAILURE;
1516 aResult.setObject(*obj.mObj);
1517 return NS_OK;
1520 nsAutoCString gBaseAppVersion;
1522 const nsCString& GfxInfoBase::GetApplicationVersion() {
1523 static bool versionInitialized = false;
1524 if (!versionInitialized) {
1525 // If we fail to get the version, we will not try again.
1526 versionInitialized = true;
1528 // Get the version from xpcom/system/nsIXULAppInfo.idl
1529 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1530 if (app) {
1531 app->GetVersion(gBaseAppVersion);
1534 return gBaseAppVersion;
1537 /* static */ bool GfxInfoBase::OnlyAllowFeatureOnKnownConfig(int32_t aFeature) {
1538 switch (aFeature) {
1539 // The GPU process doesn't need hardware acceleration and can run on
1540 // devices that we normally block from not being on our whitelist.
1541 case nsIGfxInfo::FEATURE_GPU_PROCESS:
1542 return kIsAndroid;
1543 // We can mostly assume that ANGLE will work
1544 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
1545 // Remote WebGL is needed for Win32k Lockdown, so it should be enabled
1546 // regardless of HW support or not
1547 case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
1548 // Backdrop filter should generally work, especially if we fall back to
1549 // Software WebRender because of an unknown vendor.
1550 case nsIGfxInfo::FEATURE_BACKDROP_FILTER:
1551 return false;
1552 default:
1553 return true;
1557 void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
1558 InitCollectors();
1559 sCollectors->AppendElement(collector);
1562 void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
1563 InitCollectors();
1564 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1565 if ((*sCollectors)[i] == collector) {
1566 sCollectors->RemoveElementAt(i);
1567 break;
1570 if (sCollectors->IsEmpty()) {
1571 delete sCollectors;
1572 sCollectors = nullptr;
1576 static void AppendMonitor(JSContext* aCx, widget::Screen& aScreen,
1577 JS::Handle<JSObject*> aOutArray, int32_t aIndex) {
1578 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1580 auto screenSize = aScreen.GetRect().Size();
1582 JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
1583 JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
1585 JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
1586 JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
1588 // XXX Just preserving behavior since this is exposed to telemetry, but we
1589 // could consider including this everywhere.
1590 #ifdef XP_MACOSX
1591 JS::Rooted<JS::Value> scale(
1592 aCx, JS::NumberValue(aScreen.GetContentsScaleFactor()));
1593 JS_SetProperty(aCx, obj, "scale", scale);
1594 #endif
1596 #ifdef XP_WIN
1597 JS::Rooted<JS::Value> refreshRate(aCx,
1598 JS::Int32Value(aScreen.GetRefreshRate()));
1599 JS_SetProperty(aCx, obj, "refreshRate", refreshRate);
1601 JS::Rooted<JS::Value> pseudoDisplay(
1602 aCx, JS::BooleanValue(aScreen.GetIsPseudoDisplay()));
1603 JS_SetProperty(aCx, obj, "pseudoDisplay", pseudoDisplay);
1604 #endif
1606 JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
1607 JS_SetElement(aCx, aOutArray, aIndex, element);
1610 nsresult GfxInfoBase::FindMonitors(JSContext* aCx,
1611 JS::Handle<JSObject*> aOutArray) {
1612 int32_t index = 0;
1613 auto& sm = ScreenManager::GetSingleton();
1614 for (auto& screen : sm.CurrentScreenList()) {
1615 AppendMonitor(aCx, *screen, aOutArray, index++);
1618 if (index == 0) {
1619 // Ensure we return at least one monitor, this is needed for xpcshell.
1620 RefPtr<Screen> screen = sm.GetPrimaryScreen();
1621 AppendMonitor(aCx, *screen, aOutArray, index++);
1624 return NS_OK;
1627 NS_IMETHODIMP
1628 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
1629 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1631 nsresult rv = FindMonitors(aCx, array);
1632 if (NS_FAILED(rv)) {
1633 return rv;
1636 aResult.setObject(*array);
1637 return NS_OK;
1640 static inline bool SetJSPropertyString(JSContext* aCx,
1641 JS::Handle<JSObject*> aObj,
1642 const char* aProp, const char* aString) {
1643 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1644 if (!str) {
1645 return false;
1648 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1649 return JS_SetProperty(aCx, aObj, aProp, val);
1652 template <typename T>
1653 static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
1654 const T& aValue) {
1655 uint32_t index;
1656 if (!JS::GetArrayLength(aCx, aObj, &index)) {
1657 return false;
1659 return JS_SetElement(aCx, aObj, index, aValue);
1662 nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
1663 JS::MutableHandle<JS::Value> aOut) {
1664 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1665 if (!obj) {
1666 return NS_ERROR_OUT_OF_MEMORY;
1668 aOut.setObject(*obj);
1670 layers::LayersBackend backend =
1671 gfxPlatform::Initialized()
1672 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1673 : layers::LayersBackend::LAYERS_NONE;
1674 const char* backendName = layers::GetLayersBackendName(backend);
1675 SetJSPropertyString(aCx, obj, "compositor", backendName);
1677 // If graphics isn't initialized yet, just stop now.
1678 if (!gfxPlatform::Initialized()) {
1679 return NS_OK;
1682 DescribeFeatures(aCx, obj);
1683 return NS_OK;
1686 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
1687 JS::MutableHandle<JS::Value> aOut) {
1688 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1689 if (!containerObj) {
1690 return NS_ERROR_OUT_OF_MEMORY;
1692 aOut.setObject(*containerObj);
1694 JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
1695 if (!featureArray) {
1696 return NS_ERROR_OUT_OF_MEMORY;
1699 // Collect features.
1700 gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
1701 FeatureState& aFeature) -> void {
1702 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1703 if (!obj) {
1704 return;
1706 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1707 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1708 !SetJSPropertyString(aCx, obj, "status",
1709 FeatureStatusToString(aFeature.GetValue()))) {
1710 return;
1713 JS::Rooted<JS::Value> log(aCx);
1714 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1715 return;
1717 if (!JS_SetProperty(aCx, obj, "log", log)) {
1718 return;
1721 if (!AppendJSElement(aCx, featureArray, obj)) {
1722 return;
1726 JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
1727 if (!fallbackArray) {
1728 return NS_ERROR_OUT_OF_MEMORY;
1731 // Collect fallbacks.
1732 gfxConfig::ForEachFallback(
1733 [&](const char* aName, const char* aMessage) -> void {
1734 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1735 if (!obj) {
1736 return;
1739 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1740 !SetJSPropertyString(aCx, obj, "message", aMessage)) {
1741 return;
1744 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1745 return;
1749 JS::Rooted<JS::Value> val(aCx);
1751 val = JS::ObjectValue(*featureArray);
1752 JS_SetProperty(aCx, containerObj, "features", val);
1754 val = JS::ObjectValue(*fallbackArray);
1755 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1757 return NS_OK;
1760 bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
1761 const FeatureState& aFeature,
1762 JS::MutableHandle<JS::Value> aOut) {
1763 JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
1764 if (!log) {
1765 return false;
1767 aOut.setObject(*log);
1769 aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
1770 const char* aMessage,
1771 const nsCString& aFailureId) -> void {
1772 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1773 if (!obj) {
1774 return;
1777 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1778 !SetJSPropertyString(aCx, obj, "status",
1779 FeatureStatusToString(aStatus)) ||
1780 (!aFailureId.IsEmpty() &&
1781 !SetJSPropertyString(aCx, obj, "failureId", aFailureId.get())) ||
1782 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
1783 return;
1786 if (!AppendJSElement(aCx, log, obj)) {
1787 return;
1791 return true;
1794 void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
1795 JS::Rooted<JSObject*> obj(aCx);
1797 gfx::FeatureState& hwCompositing =
1798 gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
1799 InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
1801 gfx::FeatureState& gpuProcess =
1802 gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
1803 InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
1805 gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
1806 InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
1808 gfx::FeatureState& wrCompositor =
1809 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
1810 InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
1812 gfx::FeatureState& openglCompositing =
1813 gfxConfig::GetFeature(gfx::Feature::OPENGL_COMPOSITING);
1814 InitFeatureObject(aCx, aObj, "openglCompositing", openglCompositing, &obj);
1816 gfx::FeatureState& omtp = gfxConfig::GetFeature(gfx::Feature::OMTP);
1817 InitFeatureObject(aCx, aObj, "omtp", omtp, &obj);
1820 bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
1821 JS::Handle<JSObject*> aContainer,
1822 const char* aName,
1823 mozilla::gfx::FeatureState& aFeatureState,
1824 JS::MutableHandle<JSObject*> aOutObj) {
1825 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1826 if (!obj) {
1827 return false;
1830 nsCString status = aFeatureState.GetStatusAndFailureIdString();
1832 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
1833 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1834 JS_SetProperty(aCx, obj, "status", val);
1836 // Add the feature object to the container.
1838 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1839 JS_SetProperty(aCx, aContainer, aName, val);
1842 aOutObj.set(obj);
1843 return true;
1846 nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
1847 JS::MutableHandle<JS::Value> aOut) {
1848 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1849 if (!array) {
1850 return NS_ERROR_OUT_OF_MEMORY;
1852 aOut.setObject(*array);
1854 DriverCrashGuard::ForEachActiveCrashGuard(
1855 [&](const char* aName, const char* aPrefName) -> void {
1856 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1857 if (!obj) {
1858 return;
1860 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1861 return;
1863 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1864 return;
1866 if (!AppendJSElement(aCx, array, obj)) {
1867 return;
1871 return NS_OK;
1874 NS_IMETHODIMP
1875 GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
1876 *aTargetFrameRate = gfxPlatform::TargetFrameRate();
1877 return NS_OK;
1880 NS_IMETHODIMP
1881 GfxInfoBase::GetCodecSupportInfo(nsACString& aCodecSupportInfo) {
1882 aCodecSupportInfo.Assign(gfx::gfxVars::CodecSupportInfo());
1883 return NS_OK;
1886 NS_IMETHODIMP
1887 GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
1888 *aIsHeadless = gfxPlatform::IsHeadless();
1889 return NS_OK;
1892 #if defined(MOZ_WIDGET_ANDROID)
1894 const char* chromebookProductList[] = {
1895 "asuka", "asurada", "atlas", "auron", "banjo", "banon",
1896 "bob", "brask", "brya", "buddy", "butterfly", "candy",
1897 "caroline", "cave", "celes", "chell", "cherry", "clapper",
1898 "coral", "corsola", "cyan", "daisy", "dedede", "drallion",
1899 "edgar", "elm", "enguarde", "eve", "expresso", "falco",
1900 "fizz", "gandof", "glimmer", "gnawty", "grunt", "guado",
1901 "guybrush", "hana", "hatch", "heli", "jacuzzi", "kalista",
1902 "kefka", "kevin", "kip", "kukui", "lars", "leon",
1903 "link", "lulu", "lumpy", "mccloud", "monroe", "nami",
1904 "nautilus", "ninja", "nissa", "nocturne", "nyan", "octopus",
1905 "orco", "panther", "parrot", "peach", "peppy", "puff",
1906 "pyro", "quawks", "rammus", "reef", "reks", "relm",
1907 "rikku", "samus", "sand", "sarien", "scarlet", "sentry",
1908 "setzer", "skyrim", "snappy", "soraka", "squawks", "staryu",
1909 "stout", "strongbad", "stumpy", "sumo", "swanky", "terra",
1910 "tidus", "tricky", "trogdor", "ultima", "veyron", "volteer",
1911 "winky", "wizpig", "wolf", "x86", "zako", "zork"};
1913 bool ProductIsChromebook(nsCString product) {
1914 size_t result;
1915 return BinarySearchIf(
1916 chromebookProductList, 0, ArrayLength(chromebookProductList),
1917 [&](const char* const aValue) -> int {
1918 return strcmp(product.get(), aValue);
1920 &result);
1922 #endif
1924 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
1925 static StaticAutoPtr<std::pair<Device, nsString>> ret;
1927 std::pair<Device, nsString>* GfxInfoBase::GetFontVisibilityDeterminationPair() {
1928 if(!ret) {
1929 ret = new std::pair<Device, nsString>();
1930 ret->first = Device::Unassigned;
1931 ret->second = u""_ns;
1932 ClearOnShutdown(&ret);
1935 if (ret->first != Device::Unassigned) {
1936 return ret;
1939 #if defined(MOZ_WIDGET_ANDROID)
1940 auto androidReleaseVersion = strtol(
1941 java::sdk::Build::VERSION::RELEASE()->ToCString().get(), nullptr, 10);
1943 auto androidManufacturer = java::sdk::Build::MANUFACTURER()->ToCString();
1944 nsContentUtils::ASCIIToLower(androidManufacturer);
1946 auto androidBrand = java::sdk::Build::BRAND()->ToCString();
1947 nsContentUtils::ASCIIToLower(androidBrand);
1949 auto androidModel = java::sdk::Build::MODEL()->ToCString();
1950 nsContentUtils::ASCIIToLower(androidModel);
1952 auto androidProduct = java::sdk::Build::PRODUCT()->ToCString();
1953 nsContentUtils::ASCIIToLower(androidProduct);
1955 auto androidProductIsChromebook = ProductIsChromebook(androidProduct);
1957 if (androidReleaseVersion < 4 || androidReleaseVersion > 20) {
1958 // Something is screwy, oh well.
1959 ret->second.AppendASCII("Unknown Release Version - ");
1960 ret->first = Device::Android_Unknown_Release_Version;
1961 } else if (androidReleaseVersion <= 8) {
1962 ret->second.AppendASCII("Android <9 - ");
1963 ret->first = Device::Android_sub_9;
1964 } else if (androidReleaseVersion <= 11) {
1965 ret->second.AppendASCII("Android 9-11 - ");
1966 ret->first = Device::Android_9_11;
1967 } else if (androidReleaseVersion > 11) {
1968 ret->second.AppendASCII("Android 12+ - ");
1969 ret->first = Device::Android_12_plus;
1970 } else {
1971 MOZ_CRASH_UNSAFE_PRINTF(
1972 "Somehow wound up in GetFontVisibilityDeterminationPair with a release "
1973 "version of %li",
1974 androidReleaseVersion);
1977 if (androidManufacturer == "google" && androidModel == androidProduct &&
1978 androidProductIsChromebook) {
1979 // Chromebook font set coming later
1980 ret->second.AppendASCII("Chromebook - ");
1981 ret->first = Device::Android_Chromebook;
1983 if (androidBrand == "amazon") {
1984 // Amazon Fire font set coming later
1985 ret->second.AppendASCII("Amazon - ");
1986 ret->first = Device::Android_Amazon;
1988 if (androidBrand == "peloton") {
1989 // We don't know how to categorize fonts on this system
1990 ret->second.AppendASCII("Peloton - ");
1991 ret->first = Device::Android_Unknown_Peloton;
1993 if (androidProduct == "vbox86p") {
1994 ret->second.AppendASCII("vbox - ");
1995 // We can't categorize fonts when running in an emulator on a Desktop
1996 ret->first = Device::Android_Unknown_vbox;
1998 if (androidModel.Find("mitv"_ns) != kNotFound && androidBrand == "xiaomi") {
1999 // We don't know how to categorize fonts on this system
2000 ret->second.AppendASCII("mitv - ");
2001 ret->first = Device::Android_Unknown_mitv;
2004 ret->second.AppendPrintf(
2005 "release_version_str=%s, release_version=%li",
2006 java::sdk::Build::VERSION::RELEASE()->ToCString().get(),
2007 androidReleaseVersion);
2008 ret->second.AppendPrintf(
2009 ", manufacturer=%s, brand=%s, model=%s, product=%s, chromebook=%s",
2010 androidManufacturer.get(), androidBrand.get(), androidModel.get(),
2011 androidProduct.get(), androidProductIsChromebook ? "yes" : "no");
2013 #elif defined(XP_LINUX)
2014 ret->first = Device::Linux_Unknown;
2016 long versionMajor = 0;
2017 FILE* fp = fopen("/etc/os-release", "r");
2018 if (fp) {
2019 char buf[512];
2020 while (fgets(buf, sizeof(buf), fp)) {
2021 if (strncmp(buf, "VERSION_ID=\"", 12) == 0) {
2022 ret->second.AppendPrintf("VERSION_ID=%.11s", buf + 11);
2023 versionMajor = strtol(buf + 12, nullptr, 10);
2024 if (ret->first != Device::Linux_Unknown) {
2025 break;
2029 if (strncmp(buf, "ID=", 3) == 0) {
2030 ret->second.AppendPrintf("ID=%.6s", buf + 3);
2031 if (strncmp(buf + 3, "ubuntu", 6) == 0) {
2032 ret->first = Device::Linux_Ubuntu_any;
2033 } else if (strncmp(buf + 3, "fedora", 6) == 0) {
2034 ret->first = Device::Linux_Fedora_any;
2037 if (versionMajor) {
2038 break;
2042 fclose(fp);
2044 if (ret->first == Device::Linux_Ubuntu_any) {
2045 if (versionMajor == 20) {
2046 ret->first = Device::Linux_Ubuntu_20;
2047 ret->second.Insert(u"Ubuntu 20 - ", 0);
2048 } else if (versionMajor == 22) {
2049 ret->first = Device::Linux_Ubuntu_22;
2050 ret->second.Insert(u"Ubuntu 22 - ", 0);
2051 } else {
2052 ret->second.Insert(u"Ubuntu Unknown - ", 0);
2054 } else if (ret->first == Device::Linux_Fedora_any) {
2055 if (versionMajor == 38) {
2056 ret->first = Device::Linux_Fedora_38;
2057 ret->second.Insert(u"Fedora 38 - ", 0);
2058 } else if (versionMajor == 39) {
2059 ret->first = Device::Linux_Fedora_39;
2060 ret->second.Insert(u"Fedora 39 - ", 0);
2061 } else {
2062 ret->second.Insert(u"Fedora Unknown - ", 0);
2064 } else {
2065 ret->second.Insert(u"Linux Unknown - ", 0);
2068 #elif defined(XP_MACOSX)
2069 ret->first = Device::MacOS_Platform;
2070 ret->second.AppendASCII("macOS Platform");
2071 #elif defined(XP_WIN)
2072 ret->first = Device::Windows_Platform;
2073 ret->second.AppendASCII("Windows Platform");
2074 #else
2075 ret->first = Device::Unknown_Platform;
2076 ret->second.AppendASCII("Unknown Platform");
2077 #endif
2079 return ret;
2082 NS_IMETHODIMP
2083 GfxInfoBase::GetFontVisibilityDetermination(
2084 Device* aFontVisibilityDetermination) {
2085 auto ret = GetFontVisibilityDeterminationPair();
2087 *aFontVisibilityDetermination = ret->first;
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 GfxInfoBase::GetFontVisibilityDeterminationStr(
2093 nsAString& aFontVisibilityDeterminationStr) {
2094 auto ret = GetFontVisibilityDeterminationPair();
2095 aFontVisibilityDeterminationStr.Assign(ret->second);
2096 return NS_OK;
2099 NS_IMETHODIMP
2100 GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
2101 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
2102 nsString outStr;
2104 switch (backend) {
2105 case BackendType::DIRECT2D1_1: {
2106 outStr.AppendPrintf("Direct2D 1.1");
2107 break;
2109 case BackendType::SKIA: {
2110 outStr.AppendPrintf("Skia");
2111 break;
2113 case BackendType::CAIRO: {
2114 outStr.AppendPrintf("Cairo");
2115 break;
2117 default:
2118 return NS_ERROR_FAILURE;
2121 aContentBackend.Assign(outStr);
2122 return NS_OK;
2125 NS_IMETHODIMP
2126 GfxInfoBase::GetAzureCanvasBackend(nsAString& aBackend) {
2127 CopyASCIItoUTF16(mozilla::MakeStringSpan(
2128 gfxPlatform::GetPlatform()->GetAzureCanvasBackend()),
2129 aBackend);
2130 return NS_OK;
2133 NS_IMETHODIMP
2134 GfxInfoBase::GetAzureContentBackend(nsAString& aBackend) {
2135 CopyASCIItoUTF16(mozilla::MakeStringSpan(
2136 gfxPlatform::GetPlatform()->GetAzureContentBackend()),
2137 aBackend);
2138 return NS_OK;
2141 NS_IMETHODIMP
2142 GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
2143 GPUProcessManager* gpu = GPUProcessManager::Get();
2144 if (!gpu) {
2145 // Not supported in content processes.
2146 return NS_ERROR_FAILURE;
2149 *aOutValue = !!gpu->GetGPUChild();
2150 return NS_OK;
2153 NS_IMETHODIMP_(int32_t)
2154 GfxInfoBase::GetMaxRefreshRate(bool* aMixed) {
2155 if (aMixed) {
2156 *aMixed = false;
2159 int32_t maxRefreshRate = 0;
2160 for (auto& screen : ScreenManager::GetSingleton().CurrentScreenList()) {
2161 int32_t refreshRate = screen->GetRefreshRate();
2162 if (aMixed && maxRefreshRate > 0 && maxRefreshRate != refreshRate) {
2163 *aMixed = true;
2165 maxRefreshRate = std::max(maxRefreshRate, refreshRate);
2168 return maxRefreshRate > 0 ? maxRefreshRate : -1;
2171 NS_IMETHODIMP
2172 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
2173 gfxPlatform::GetPlatform();
2175 GPUProcessManager* gpm = GPUProcessManager::Get();
2176 if (aEnable) {
2177 if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
2178 gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
2180 DebugOnly<nsresult> rv = gpm->EnsureGPUReady();
2181 MOZ_ASSERT(rv != NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
2182 } else {
2183 gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
2184 gpm->KillProcess();
2187 *_retval = true;
2188 return NS_OK;
2191 NS_IMETHODIMP GfxInfoBase::KillGPUProcessForTests() {
2192 GPUProcessManager* gpm = GPUProcessManager::Get();
2193 if (!gpm) {
2194 // gfxPlatform has not been initialized.
2195 return NS_ERROR_NOT_INITIALIZED;
2198 gpm->KillProcess();
2199 return NS_OK;
2202 NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
2203 GPUProcessManager* gpm = GPUProcessManager::Get();
2204 if (!gpm) {
2205 // gfxPlatform has not been initialized.
2206 return NS_ERROR_NOT_INITIALIZED;
2209 gpm->CrashProcess();
2210 return NS_OK;
2213 GfxInfoCollectorBase::GfxInfoCollectorBase() {
2214 GfxInfoBase::AddCollector(this);
2217 GfxInfoCollectorBase::~GfxInfoCollectorBase() {
2218 GfxInfoBase::RemoveCollector(this);