Bug 1760380 [wpt PR 33241] - Update wpt metadata, a=testonly
[gecko.git] / widget / GfxInfoBase.cpp
blobdb59d8b3d2e8ffc3ef3f4d7fc43a44d4f192b9e0
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 "nsIScreenManager.h"
27 #include "nsTArray.h"
28 #include "nsXULAppAPI.h"
29 #include "nsIXULAppInfo.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/GPUProcessManager.h"
35 #include "mozilla/gfx/Logging.h"
36 #include "mozilla/gfx/gfxVars.h"
38 #include "gfxPlatform.h"
39 #include "gfxConfig.h"
40 #include "DriverCrashGuard.h"
42 using namespace mozilla::widget;
43 using namespace mozilla;
44 using mozilla::MutexAutoLock;
46 nsTArray<GfxDriverInfo>* GfxInfoBase::sDriverInfo;
47 StaticAutoPtr<nsTArray<gfx::GfxInfoFeatureStatus>> GfxInfoBase::sFeatureStatus;
48 bool GfxInfoBase::sDriverInfoObserverInitialized;
49 bool GfxInfoBase::sShutdownOccurred;
51 // Call this when setting sFeatureStatus to a non-null pointer to
52 // ensure destruction even if the GfxInfo component is never instantiated.
53 static void InitFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>* aPtr) {
54 static std::once_flag sOnce;
55 std::call_once(sOnce, [] { ClearOnShutdown(&GfxInfoBase::sFeatureStatus); });
56 GfxInfoBase::sFeatureStatus = aPtr;
59 // Observes for shutdown so that the child GfxDriverInfo list is freed.
60 class ShutdownObserver : public nsIObserver {
61 virtual ~ShutdownObserver() = default;
63 public:
64 ShutdownObserver() = default;
66 NS_DECL_ISUPPORTS
68 NS_IMETHOD Observe(nsISupports* subject, const char* aTopic,
69 const char16_t* aData) override {
70 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
72 delete GfxInfoBase::sDriverInfo;
73 GfxInfoBase::sDriverInfo = nullptr;
75 for (auto& deviceFamily : GfxDriverInfo::sDeviceFamilies) {
76 delete deviceFamily;
77 deviceFamily = nullptr;
80 for (auto& desktop : GfxDriverInfo::sDesktopEnvironment) {
81 delete desktop;
82 desktop = nullptr;
85 for (auto& windowProtocol : GfxDriverInfo::sWindowProtocol) {
86 delete windowProtocol;
87 windowProtocol = nullptr;
90 for (auto& deviceVendor : GfxDriverInfo::sDeviceVendors) {
91 delete deviceVendor;
92 deviceVendor = nullptr;
95 for (auto& driverVendor : GfxDriverInfo::sDriverVendors) {
96 delete driverVendor;
97 driverVendor = nullptr;
100 GfxInfoBase::sShutdownOccurred = true;
102 return NS_OK;
106 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
108 static void InitGfxDriverInfoShutdownObserver() {
109 if (GfxInfoBase::sDriverInfoObserverInitialized) return;
111 GfxInfoBase::sDriverInfoObserverInitialized = true;
113 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
114 if (!observerService) {
115 NS_WARNING("Could not get observer service!");
116 return;
119 ShutdownObserver* obs = new ShutdownObserver();
120 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
123 using namespace mozilla::widget;
124 using namespace mozilla::gfx;
125 using namespace mozilla;
127 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver,
128 nsISupportsWeakReference)
130 #define BLOCKLIST_PREF_BRANCH "gfx.blacklist."
131 #define SUGGESTED_VERSION_PREF BLOCKLIST_PREF_BRANCH "suggested-driver-version"
133 static const char* GetPrefNameForFeature(int32_t aFeature) {
134 const char* name = nullptr;
135 switch (aFeature) {
136 case nsIGfxInfo::FEATURE_DIRECT2D:
137 name = BLOCKLIST_PREF_BRANCH "direct2d";
138 break;
139 case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
140 name = BLOCKLIST_PREF_BRANCH "layers.direct3d9";
141 break;
142 case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
143 name = BLOCKLIST_PREF_BRANCH "layers.direct3d10";
144 break;
145 case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
146 name = BLOCKLIST_PREF_BRANCH "layers.direct3d10-1";
147 break;
148 case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
149 name = BLOCKLIST_PREF_BRANCH "layers.direct3d11";
150 break;
151 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
152 name = BLOCKLIST_PREF_BRANCH "direct3d11angle";
153 break;
154 case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
155 name = BLOCKLIST_PREF_BRANCH "hardwarevideodecoding";
156 break;
157 case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
158 name = BLOCKLIST_PREF_BRANCH "layers.opengl";
159 break;
160 case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
161 name = BLOCKLIST_PREF_BRANCH "webgl.opengl";
162 break;
163 case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
164 name = BLOCKLIST_PREF_BRANCH "webgl.angle";
165 break;
166 case nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA:
167 name = BLOCKLIST_PREF_BRANCH "webgl.msaa";
168 break;
169 case nsIGfxInfo::FEATURE_STAGEFRIGHT:
170 name = BLOCKLIST_PREF_BRANCH "stagefright";
171 break;
172 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264:
173 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.h264";
174 break;
175 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
176 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
177 break;
178 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
179 name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
180 break;
181 case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
182 name = BLOCKLIST_PREF_BRANCH "canvas2d.acceleration";
183 break;
184 case nsIGfxInfo::FEATURE_DX_INTEROP2:
185 name = BLOCKLIST_PREF_BRANCH "dx.interop2";
186 break;
187 case nsIGfxInfo::FEATURE_GPU_PROCESS:
188 name = BLOCKLIST_PREF_BRANCH "gpu.process";
189 break;
190 case nsIGfxInfo::FEATURE_WEBGL2:
191 name = BLOCKLIST_PREF_BRANCH "webgl2";
192 break;
193 case nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX:
194 name = BLOCKLIST_PREF_BRANCH "d3d11.keyed.mutex";
195 break;
196 case nsIGfxInfo::FEATURE_WEBRENDER:
197 name = BLOCKLIST_PREF_BRANCH "webrender";
198 break;
199 case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
200 name = BLOCKLIST_PREF_BRANCH "webrender.compositor";
201 break;
202 case nsIGfxInfo::FEATURE_DX_NV12:
203 name = BLOCKLIST_PREF_BRANCH "dx.nv12";
204 break;
205 case nsIGfxInfo::FEATURE_DX_P010:
206 name = BLOCKLIST_PREF_BRANCH "dx.p010";
207 break;
208 case nsIGfxInfo::FEATURE_DX_P016:
209 name = BLOCKLIST_PREF_BRANCH "dx.p016";
210 break;
211 case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
212 name = BLOCKLIST_PREF_BRANCH "vp8.hw-decode";
213 break;
214 case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
215 name = BLOCKLIST_PREF_BRANCH "vp9.hw-decode";
216 break;
217 case nsIGfxInfo::FEATURE_GL_SWIZZLE:
218 name = BLOCKLIST_PREF_BRANCH "gl.swizzle";
219 break;
220 case nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS:
221 name = BLOCKLIST_PREF_BRANCH "webrender.scissored_cache_clears";
222 break;
223 case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
224 name = BLOCKLIST_PREF_BRANCH "webgl.allow-oop";
225 break;
226 case nsIGfxInfo::FEATURE_THREADSAFE_GL:
227 name = BLOCKLIST_PREF_BRANCH "gl.threadsafe";
228 break;
229 case nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS:
230 name = BLOCKLIST_PREF_BRANCH "webrender.optimized-shaders";
231 break;
232 case nsIGfxInfo::FEATURE_X11_EGL:
233 name = BLOCKLIST_PREF_BRANCH "x11.egl";
234 break;
235 case nsIGfxInfo::FEATURE_DMABUF:
236 name = BLOCKLIST_PREF_BRANCH "dmabuf";
237 break;
238 case nsIGfxInfo::FEATURE_VAAPI:
239 name = BLOCKLIST_PREF_BRANCH "vaapi";
240 break;
241 case nsIGfxInfo::FEATURE_WEBGPU:
242 name = BLOCKLIST_PREF_BRANCH "webgpu";
243 break;
244 case nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE:
245 name = BLOCKLIST_PREF_BRANCH "webrender.program-binary-disk";
246 break;
247 case nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT:
248 name = BLOCKLIST_PREF_BRANCH "webrender.partial-present";
249 break;
250 default:
251 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
252 break;
255 return name;
258 // Returns the value of the pref for the relevant feature in aValue.
259 // If the pref doesn't exist, aValue is not touched, and returns false.
260 static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
261 nsACString& aFailureId) {
262 const char* prefname = GetPrefNameForFeature(aFeature);
263 if (!prefname) return false;
265 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
266 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
267 return false;
270 nsCString failureprefname(prefname);
271 failureprefname += ".failureid";
272 nsAutoCString failureValue;
273 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
274 if (NS_SUCCEEDED(rv)) {
275 aFailureId = failureValue.get();
276 } else {
277 aFailureId = "FEATURE_FAILURE_BLOCKLIST_PREF";
280 return true;
283 static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
284 const nsACString& aFailureId) {
285 const char* prefname = GetPrefNameForFeature(aFeature);
286 if (!prefname) return;
287 if (XRE_IsParentProcess()) {
288 GfxInfoBase::sFeatureStatus = nullptr;
291 Preferences::SetInt(prefname, aValue);
292 if (!aFailureId.IsEmpty()) {
293 nsCString failureprefname(prefname);
294 failureprefname += ".failureid";
295 Preferences::SetCString(failureprefname.get(), aFailureId);
299 static void RemovePrefForFeature(int32_t aFeature) {
300 const char* prefname = GetPrefNameForFeature(aFeature);
301 if (!prefname) return;
303 if (XRE_IsParentProcess()) {
304 GfxInfoBase::sFeatureStatus = nullptr;
306 Preferences::ClearUser(prefname);
309 static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
310 return NS_SUCCEEDED(
311 Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
314 static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
315 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
318 static void RemovePrefForDriverVersion() {
319 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
322 static OperatingSystem BlocklistOSToOperatingSystem(const nsAString& os) {
323 if (os.EqualsLiteral("WINNT 6.1")) {
324 return OperatingSystem::Windows7;
326 if (os.EqualsLiteral("WINNT 6.2")) {
327 return OperatingSystem::Windows8;
329 if (os.EqualsLiteral("WINNT 6.3")) {
330 return OperatingSystem::Windows8_1;
332 if (os.EqualsLiteral("WINNT 10.0")) {
333 return OperatingSystem::Windows10;
335 if (os.EqualsLiteral("Linux")) {
336 return OperatingSystem::Linux;
338 if (os.EqualsLiteral("Darwin 9")) {
339 return OperatingSystem::OSX10_5;
341 if (os.EqualsLiteral("Darwin 10")) {
342 return OperatingSystem::OSX10_6;
344 if (os.EqualsLiteral("Darwin 11")) {
345 return OperatingSystem::OSX10_7;
347 if (os.EqualsLiteral("Darwin 12")) {
348 return OperatingSystem::OSX10_8;
350 if (os.EqualsLiteral("Darwin 13")) {
351 return OperatingSystem::OSX10_9;
353 if (os.EqualsLiteral("Darwin 14")) {
354 return OperatingSystem::OSX10_10;
356 if (os.EqualsLiteral("Darwin 15")) {
357 return OperatingSystem::OSX10_11;
359 if (os.EqualsLiteral("Darwin 16")) {
360 return OperatingSystem::OSX10_12;
362 if (os.EqualsLiteral("Darwin 17")) {
363 return OperatingSystem::OSX10_13;
365 if (os.EqualsLiteral("Darwin 18")) {
366 return OperatingSystem::OSX10_14;
368 if (os.EqualsLiteral("Darwin 19")) {
369 return OperatingSystem::OSX10_15;
371 if (os.EqualsLiteral("Darwin 20")) {
372 return OperatingSystem::OSX11_0;
374 if (os.EqualsLiteral("Android")) {
375 return OperatingSystem::Android;
376 // For historical reasons, "All" in blocklist means "All Windows"
378 if (os.EqualsLiteral("All")) {
379 return OperatingSystem::Windows;
381 if (os.EqualsLiteral("Darwin")) {
382 return OperatingSystem::OSX;
385 return OperatingSystem::Unknown;
388 static GfxDeviceFamily* BlocklistDevicesToDeviceFamily(
389 nsTArray<nsCString>& devices) {
390 if (devices.Length() == 0) return nullptr;
392 // For each device, get its device ID, and return a freshly-allocated
393 // GfxDeviceFamily with the contents of that array.
394 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
396 for (uint32_t i = 0; i < devices.Length(); ++i) {
397 // We make sure we don't add any "empty" device entries to the array, so
398 // we don't need to check if devices[i] is empty.
399 deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
402 return deviceIds;
405 static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) {
406 MOZ_ASSERT(!aFeature.IsEmpty());
407 if (aFeature.EqualsLiteral("DIRECT2D")) {
408 return nsIGfxInfo::FEATURE_DIRECT2D;
410 if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS")) {
411 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
413 if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS")) {
414 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
416 if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS")) {
417 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
419 if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS")) {
420 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
422 if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE")) {
423 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
425 if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING")) {
426 return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
428 if (aFeature.EqualsLiteral("OPENGL_LAYERS")) {
429 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
431 if (aFeature.EqualsLiteral("WEBGL_OPENGL")) {
432 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
434 if (aFeature.EqualsLiteral("WEBGL_ANGLE")) {
435 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
437 if (aFeature.EqualsLiteral("WEBGL_MSAA")) {
438 return nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA;
440 if (aFeature.EqualsLiteral("STAGEFRIGHT")) {
441 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
443 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE")) {
444 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
446 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE")) {
447 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
449 if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_H264")) {
450 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264;
452 if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION")) {
453 return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
455 if (aFeature.EqualsLiteral("DX_INTEROP2")) {
456 return nsIGfxInfo::FEATURE_DX_INTEROP2;
458 if (aFeature.EqualsLiteral("GPU_PROCESS")) {
459 return nsIGfxInfo::FEATURE_GPU_PROCESS;
461 if (aFeature.EqualsLiteral("WEBGL2")) {
462 return nsIGfxInfo::FEATURE_WEBGL2;
464 if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX")) {
465 return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
467 if (aFeature.EqualsLiteral("WEBRENDER")) {
468 return nsIGfxInfo::FEATURE_WEBRENDER;
470 if (aFeature.EqualsLiteral("WEBRENDER_COMPOSITOR")) {
471 return nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR;
473 if (aFeature.EqualsLiteral("DX_NV12")) {
474 return nsIGfxInfo::FEATURE_DX_NV12;
476 if (aFeature.EqualsLiteral("VP8_HW_DECODE")) {
477 return nsIGfxInfo::FEATURE_VP8_HW_DECODE;
479 if (aFeature.EqualsLiteral("VP9_HW_DECODE")) {
480 return nsIGfxInfo::FEATURE_VP9_HW_DECODE;
482 if (aFeature.EqualsLiteral("GL_SWIZZLE")) {
483 return nsIGfxInfo::FEATURE_GL_SWIZZLE;
485 if (aFeature.EqualsLiteral("WEBRENDER_SCISSORED_CACHE_CLEARS")) {
486 return nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS;
488 if (aFeature.EqualsLiteral("ALLOW_WEBGL_OUT_OF_PROCESS")) {
489 return nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS;
491 if (aFeature.EqualsLiteral("THREADSAFE_GL")) {
492 return nsIGfxInfo::FEATURE_THREADSAFE_GL;
494 if (aFeature.EqualsLiteral("X11_EGL")) {
495 return nsIGfxInfo::FEATURE_X11_EGL;
497 if (aFeature.EqualsLiteral("DMABUF")) {
498 return nsIGfxInfo::FEATURE_DMABUF;
500 if (aFeature.EqualsLiteral("VAAPI")) {
501 return nsIGfxInfo::FEATURE_VAAPI;
503 if (aFeature.EqualsLiteral("WEBGPU")) {
504 return nsIGfxInfo::FEATURE_WEBGPU;
506 if (aFeature.EqualsLiteral("WEBRENDER_PARTIAL_PRESENT")) {
507 return nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT;
510 // If we don't recognize the feature, it may be new, and something
511 // this version doesn't understand. So, nothing to do. This is
512 // different from feature not being specified at all, in which case
513 // this method should not get called and we should continue with the
514 // "all features" blocklisting.
515 return -1;
518 static int32_t BlocklistFeatureStatusToGfxFeatureStatus(
519 const nsAString& aStatus) {
520 if (aStatus.EqualsLiteral("STATUS_OK")) {
521 return nsIGfxInfo::FEATURE_STATUS_OK;
523 if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION")) {
524 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
526 if (aStatus.EqualsLiteral("BLOCKED_DEVICE")) {
527 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
529 if (aStatus.EqualsLiteral("DISCOURAGED")) {
530 return nsIGfxInfo::FEATURE_DISCOURAGED;
532 if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION")) {
533 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
535 if (aStatus.EqualsLiteral("DENIED")) {
536 return nsIGfxInfo::FEATURE_DENIED;
538 if (aStatus.EqualsLiteral("ALLOW_QUALIFIED")) {
539 return nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
541 if (aStatus.EqualsLiteral("ALLOW_ALWAYS")) {
542 return nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
545 // Do not allow it to set STATUS_UNKNOWN. Also, we are not
546 // expecting the "mismatch" status showing up here.
548 return nsIGfxInfo::FEATURE_STATUS_OK;
551 static VersionComparisonOp BlocklistComparatorToComparisonOp(
552 const nsAString& op) {
553 if (op.EqualsLiteral("LESS_THAN")) {
554 return DRIVER_LESS_THAN;
556 if (op.EqualsLiteral("BUILD_ID_LESS_THAN")) {
557 return DRIVER_BUILD_ID_LESS_THAN;
559 if (op.EqualsLiteral("LESS_THAN_OR_EQUAL")) {
560 return DRIVER_LESS_THAN_OR_EQUAL;
562 if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL")) {
563 return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
565 if (op.EqualsLiteral("GREATER_THAN")) {
566 return DRIVER_GREATER_THAN;
568 if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL")) {
569 return DRIVER_GREATER_THAN_OR_EQUAL;
571 if (op.EqualsLiteral("EQUAL")) {
572 return DRIVER_EQUAL;
574 if (op.EqualsLiteral("NOT_EQUAL")) {
575 return DRIVER_NOT_EQUAL;
577 if (op.EqualsLiteral("BETWEEN_EXCLUSIVE")) {
578 return DRIVER_BETWEEN_EXCLUSIVE;
580 if (op.EqualsLiteral("BETWEEN_INCLUSIVE")) {
581 return DRIVER_BETWEEN_INCLUSIVE;
583 if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START")) {
584 return DRIVER_BETWEEN_INCLUSIVE_START;
587 return DRIVER_COMPARISON_IGNORED;
591 Deserialize Blocklist entries from string.
592 e.g:
593 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
595 static bool BlocklistEntryToDriverInfo(const nsACString& aBlocklistEntry,
596 GfxDriverInfo& aDriverInfo) {
597 // If we get an application version to be zero, something is not working
598 // and we are not going to bother checking the blocklist versions.
599 // See TestGfxWidgets.cpp for how version comparison works.
600 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
601 static mozilla::Version zeroV("0");
602 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
603 if (appV <= zeroV) {
604 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
605 << "Invalid application version "
606 << GfxInfoBase::GetApplicationVersion().get();
609 aDriverInfo.mRuleId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID"_ns;
611 for (const auto& keyValue : aBlocklistEntry.Split('\t')) {
612 nsTArray<nsCString> splitted;
613 ParseString(keyValue, ':', splitted);
614 if (splitted.Length() != 2) {
615 // If we don't recognize the input data, we do not want to proceed.
616 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
617 << "Unrecognized data " << nsCString(keyValue).get();
618 return false;
620 const nsCString& key = splitted[0];
621 const nsCString& value = splitted[1];
622 NS_ConvertUTF8toUTF16 dataValue(value);
624 if (value.Length() == 0) {
625 // Safety check for empty values.
626 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
627 << "Empty value for " << key.get();
628 return false;
631 if (key.EqualsLiteral("blockID")) {
632 nsCString blockIdStr = "FEATURE_FAILURE_DL_BLOCKLIST_"_ns + value;
633 aDriverInfo.mRuleId = blockIdStr.get();
634 } else if (key.EqualsLiteral("os")) {
635 aDriverInfo.mOperatingSystem = BlocklistOSToOperatingSystem(dataValue);
636 } else if (key.EqualsLiteral("osversion")) {
637 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
638 } else if (key.EqualsLiteral("desktopEnvironment")) {
639 aDriverInfo.mDesktopEnvironment = dataValue;
640 } else if (key.EqualsLiteral("windowProtocol")) {
641 aDriverInfo.mWindowProtocol = dataValue;
642 } else if (key.EqualsLiteral("vendor")) {
643 aDriverInfo.mAdapterVendor = dataValue;
644 } else if (key.EqualsLiteral("driverVendor")) {
645 aDriverInfo.mDriverVendor = dataValue;
646 } else if (key.EqualsLiteral("feature")) {
647 aDriverInfo.mFeature = BlocklistFeatureToGfxFeature(dataValue);
648 if (aDriverInfo.mFeature < 0) {
649 // If we don't recognize the feature, we do not want to proceed.
650 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
651 << "Unrecognized feature " << value.get();
652 return false;
654 } else if (key.EqualsLiteral("featureStatus")) {
655 aDriverInfo.mFeatureStatus =
656 BlocklistFeatureStatusToGfxFeatureStatus(dataValue);
657 } else if (key.EqualsLiteral("driverVersion")) {
658 uint64_t version;
659 if (ParseDriverVersion(dataValue, &version))
660 aDriverInfo.mDriverVersion = version;
661 } else if (key.EqualsLiteral("driverVersionMax")) {
662 uint64_t version;
663 if (ParseDriverVersion(dataValue, &version))
664 aDriverInfo.mDriverVersionMax = version;
665 } else if (key.EqualsLiteral("driverVersionComparator")) {
666 aDriverInfo.mComparisonOp = BlocklistComparatorToComparisonOp(dataValue);
667 } else if (key.EqualsLiteral("model")) {
668 aDriverInfo.mModel = dataValue;
669 } else if (key.EqualsLiteral("product")) {
670 aDriverInfo.mProduct = dataValue;
671 } else if (key.EqualsLiteral("manufacturer")) {
672 aDriverInfo.mManufacturer = dataValue;
673 } else if (key.EqualsLiteral("hardware")) {
674 aDriverInfo.mHardware = dataValue;
675 } else if (key.EqualsLiteral("versionRange")) {
676 nsTArray<nsCString> versionRange;
677 ParseString(value, ',', versionRange);
678 if (versionRange.Length() != 2) {
679 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
680 << "Unrecognized versionRange " << value.get();
681 return false;
683 const nsCString& minValue = versionRange[0];
684 const nsCString& maxValue = versionRange[1];
686 mozilla::Version minV(minValue.get());
687 mozilla::Version maxV(maxValue.get());
689 if (minV > zeroV && !(appV >= minV)) {
690 // The version of the application is less than the minimal version
691 // this blocklist entry applies to, so we can just ignore it by
692 // returning false and letting the caller deal with it.
693 return false;
695 if (maxV > zeroV && !(appV <= maxV)) {
696 // The version of the application is more than the maximal version
697 // this blocklist entry applies to, so we can just ignore it by
698 // returning false and letting the caller deal with it.
699 return false;
701 } else if (key.EqualsLiteral("devices")) {
702 nsTArray<nsCString> devices;
703 ParseString(value, ',', devices);
704 GfxDeviceFamily* deviceIds = BlocklistDevicesToDeviceFamily(devices);
705 if (deviceIds) {
706 // Get GfxDriverInfo to adopt the devices array we created.
707 aDriverInfo.mDeleteDevices = true;
708 aDriverInfo.mDevices = deviceIds;
711 // We explicitly ignore unknown elements.
714 return true;
717 NS_IMETHODIMP
718 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
719 const char16_t* aData) {
720 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
721 nsTArray<GfxDriverInfo> driverInfo;
722 NS_ConvertUTF16toUTF8 utf8Data(aData);
724 for (const auto& blocklistEntry : utf8Data.Split('\n')) {
725 GfxDriverInfo di;
726 if (BlocklistEntryToDriverInfo(blocklistEntry, di)) {
727 // XXX Changing this to driverInfo.AppendElement(di) causes leaks.
728 // Probably some non-standard semantics of the copy/move operations?
729 *driverInfo.AppendElement() = di;
730 // Prevent di falling out of scope from destroying the devices.
731 di.mDeleteDevices = false;
732 } else {
733 driverInfo.AppendElement();
737 EvaluateDownloadedBlocklist(driverInfo);
740 return NS_OK;
743 GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
745 GfxInfoBase::~GfxInfoBase() = default;
747 nsresult GfxInfoBase::Init() {
748 InitGfxDriverInfoShutdownObserver();
750 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
751 if (os) {
752 os->AddObserver(this, "blocklist-data-gfxItems", true);
755 return NS_OK;
758 void GfxInfoBase::GetData() {
759 if (mScreenPixels != INT64_MAX) {
760 // Already initialized.
761 return;
764 nsCOMPtr<nsIScreenManager> manager =
765 do_GetService("@mozilla.org/gfx/screenmanager;1");
766 if (!manager) {
767 MOZ_ASSERT_UNREACHABLE("failed to get nsIScreenManager");
768 return;
771 manager->GetTotalScreenPixels(&mScreenPixels);
774 NS_IMETHODIMP
775 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
776 int32_t* aStatus) {
777 // Ignore the gfx.blocklist.all pref on release and beta.
778 #if defined(RELEASE_OR_BETA)
779 int32_t blocklistAll = 0;
780 #else
781 int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
782 #endif
783 if (blocklistAll > 0) {
784 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
785 << "Forcing blocklisting all features";
786 *aStatus = FEATURE_BLOCKED_DEVICE;
787 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
788 return NS_OK;
791 if (blocklistAll < 0) {
792 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
793 << "Ignoring any feature blocklisting.";
794 *aStatus = FEATURE_STATUS_OK;
795 return NS_OK;
798 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
799 return NS_OK;
802 if (XRE_IsContentProcess() || XRE_IsGPUProcess()) {
803 // Use the cached data received from the parent process.
804 MOZ_ASSERT(sFeatureStatus);
805 bool success = false;
806 for (const auto& fs : *sFeatureStatus) {
807 if (fs.feature() == aFeature) {
808 aFailureId = fs.failureId();
809 *aStatus = fs.status();
810 success = true;
811 break;
814 return success ? NS_OK : NS_ERROR_FAILURE;
817 nsString version;
818 nsTArray<GfxDriverInfo> driverInfo;
819 nsresult rv =
820 GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
821 return rv;
824 nsTArray<gfx::GfxInfoFeatureStatus> GfxInfoBase::GetAllFeatures() {
825 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
826 if (!sFeatureStatus) {
827 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>());
828 for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
829 int32_t status = 0;
830 nsAutoCString failureId;
831 GetFeatureStatus(i, failureId, &status);
832 gfx::GfxInfoFeatureStatus gfxFeatureStatus;
833 gfxFeatureStatus.feature() = i;
834 gfxFeatureStatus.status() = status;
835 gfxFeatureStatus.failureId() = failureId;
836 sFeatureStatus->AppendElement(gfxFeatureStatus);
840 nsTArray<gfx::GfxInfoFeatureStatus> features;
841 for (const auto& status : *sFeatureStatus) {
842 gfx::GfxInfoFeatureStatus copy = status;
843 features.AppendElement(copy);
845 return features;
848 inline bool MatchingAllowStatus(int32_t aStatus) {
849 switch (aStatus) {
850 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
851 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
852 return true;
853 default:
854 return false;
858 // Matching OS go somewhat beyond the simple equality check because of the
859 // "All Windows" and "All OS X" variations.
861 // aBlockedOS is describing the system(s) we are trying to block.
862 // aSystemOS is describing the system we are running on.
864 // aSystemOS should not be "Windows" or "OSX" - it should be set to
865 // a particular version instead.
866 // However, it is valid for aBlockedOS to be one of those generic values,
867 // as we could be blocking all of the versions.
868 inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
869 OperatingSystem aSystemOS,
870 uint32_t aSystemOSBuild) {
871 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
872 aSystemOS != OperatingSystem::OSX);
874 // If the block entry OS is unknown, it doesn't match
875 if (aBlockedOS == OperatingSystem::Unknown) {
876 return false;
879 #if defined(XP_WIN)
880 if (aBlockedOS == OperatingSystem::Windows) {
881 // We do want even "unknown" aSystemOS to fall under "all windows"
882 return true;
885 constexpr uint32_t kMinWin10BuildNumber = 18362;
886 if (aBlockedOS == OperatingSystem::RecentWindows10 &&
887 aSystemOS == OperatingSystem::Windows10) {
888 // For allowlist purposes, we sometimes want to restrict to only recent
889 // versions of Windows 10. This is a bit of a kludge but easier than adding
890 // complicated blocklist infrastructure for build ID comparisons like driver
891 // versions.
892 return aSystemOSBuild >= kMinWin10BuildNumber;
895 if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
896 if (aSystemOS == OperatingSystem::Windows10) {
897 return aSystemOSBuild < kMinWin10BuildNumber;
898 } else {
899 return true;
902 #endif
904 #if defined(XP_MACOSX)
905 if (aBlockedOS == OperatingSystem::OSX) {
906 // We do want even "unknown" aSystemOS to fall under "all OS X"
907 return true;
909 #endif
911 return aSystemOS == aBlockedOS;
914 inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
915 switch (aBatteryStatus) {
916 case BatteryStatus::All:
917 return true;
918 case BatteryStatus::None:
919 return !aHasBattery;
920 case BatteryStatus::Present:
921 return aHasBattery;
924 MOZ_ASSERT_UNREACHABLE("bad battery status");
925 return false;
928 inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
929 int64_t aScreenPixels) {
930 constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
931 constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
933 switch (aScreenStatus) {
934 case ScreenSizeStatus::All:
935 return true;
936 case ScreenSizeStatus::Small:
937 return aScreenPixels <= kMaxSmallPixels;
938 case ScreenSizeStatus::SmallAndMedium:
939 return aScreenPixels <= kMaxMediumPixels;
940 case ScreenSizeStatus::Medium:
941 return aScreenPixels > kMaxSmallPixels &&
942 aScreenPixels <= kMaxMediumPixels;
943 case ScreenSizeStatus::MediumAndLarge:
944 return aScreenPixels > kMaxSmallPixels;
945 case ScreenSizeStatus::Large:
946 return aScreenPixels > kMaxMediumPixels;
949 MOZ_ASSERT_UNREACHABLE("bad screen status");
950 return false;
953 int32_t GfxInfoBase::FindBlocklistedDeviceInList(
954 const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
955 int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
956 bool aForAllowing) {
957 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
959 // Some properties are not available on all platforms.
960 nsAutoString desktopEnvironment;
961 nsresult rv = GetDesktopEnvironment(desktopEnvironment);
962 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
963 return 0;
966 nsAutoString windowProtocol;
967 rv = GetWindowProtocol(windowProtocol);
968 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
969 return 0;
972 bool hasBattery = false;
973 rv = GetHasBattery(&hasBattery);
974 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
975 return 0;
978 uint32_t osBuild = OperatingSystemBuild();
980 // Get the adapters once then reuse below
981 nsAutoString adapterVendorID[2];
982 nsAutoString adapterDeviceID[2];
983 nsAutoString adapterDriverVendor[2];
984 nsAutoString adapterDriverVersionString[2];
985 bool adapterInfoFailed[2];
987 adapterInfoFailed[0] =
988 (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
989 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
990 NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
991 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
992 adapterInfoFailed[1] =
993 (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
994 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
995 NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
996 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
997 // No point in going on if we don't have adapter info
998 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
999 return 0;
1002 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1003 uint64_t driverVersion[2] = {0, 0};
1004 if (!adapterInfoFailed[0]) {
1005 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
1007 if (!adapterInfoFailed[1]) {
1008 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
1010 #endif
1012 uint32_t i = 0;
1013 for (; i < info.Length(); i++) {
1014 // If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
1015 // blocklisting. Only consider entries for our search mode.
1016 if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
1017 continue;
1020 // If we don't have the info for this GPU, no need to check further.
1021 // It is unclear that we would ever have a mixture of 1st and 2nd
1022 // GPU, but leaving the code in for that possibility for now.
1023 // (Actually, currently mGpu2 will never be true, so this can
1024 // be optimized out.)
1025 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
1026 if (adapterInfoFailed[infoIndex]) {
1027 continue;
1030 // Do the operating system check first, no point in getting the driver
1031 // info if we won't need to use it.
1032 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
1033 continue;
1036 if (info[i].mOperatingSystemVersion &&
1037 info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
1038 continue;
1041 if (!MatchingBattery(info[i].mBattery, hasBattery)) {
1042 continue;
1045 if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
1046 continue;
1049 if (!DoesDesktopEnvironmentMatch(info[i].mDesktopEnvironment,
1050 desktopEnvironment)) {
1051 continue;
1054 if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
1055 continue;
1058 if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
1059 continue;
1062 if (!DoesDriverVendorMatch(info[i].mDriverVendor,
1063 adapterDriverVendor[infoIndex])) {
1064 continue;
1067 if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
1068 nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
1069 if (rv == NS_ERROR_NOT_AVAILABLE) {
1070 // Not found
1071 continue;
1073 if (rv != NS_OK) {
1074 // Failed to search, allowlist should not match, blocklist should match
1075 // for safety reasons
1076 if (aForAllowing) {
1077 continue;
1079 break;
1083 bool match = false;
1085 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
1086 continue;
1088 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
1089 continue;
1091 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
1092 continue;
1094 if (!info[i].mManufacturer.IsEmpty() &&
1095 !info[i].mManufacturer.Equals(Manufacturer())) {
1096 continue;
1099 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
1100 switch (info[i].mComparisonOp) {
1101 case DRIVER_LESS_THAN:
1102 match = driverVersion[infoIndex] < info[i].mDriverVersion;
1103 break;
1104 case DRIVER_BUILD_ID_LESS_THAN:
1105 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
1106 break;
1107 case DRIVER_LESS_THAN_OR_EQUAL:
1108 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
1109 break;
1110 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
1111 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
1112 break;
1113 case DRIVER_GREATER_THAN:
1114 match = driverVersion[infoIndex] > info[i].mDriverVersion;
1115 break;
1116 case DRIVER_GREATER_THAN_OR_EQUAL:
1117 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
1118 break;
1119 case DRIVER_EQUAL:
1120 match = driverVersion[infoIndex] == info[i].mDriverVersion;
1121 break;
1122 case DRIVER_NOT_EQUAL:
1123 match = driverVersion[infoIndex] != info[i].mDriverVersion;
1124 break;
1125 case DRIVER_BETWEEN_EXCLUSIVE:
1126 match = driverVersion[infoIndex] > info[i].mDriverVersion &&
1127 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1128 break;
1129 case DRIVER_BETWEEN_INCLUSIVE:
1130 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1131 driverVersion[infoIndex] <= info[i].mDriverVersionMax;
1132 break;
1133 case DRIVER_BETWEEN_INCLUSIVE_START:
1134 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1135 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1136 break;
1137 case DRIVER_COMPARISON_IGNORED:
1138 // We don't have a comparison op, so we match everything.
1139 match = true;
1140 break;
1141 default:
1142 NS_WARNING("Bogus op in GfxDriverInfo");
1143 break;
1145 #else
1146 // We don't care what driver version it was. We only check OS version and if
1147 // the device matches.
1148 match = true;
1149 #endif
1151 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
1152 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
1153 info[i].mFeature == aFeature) {
1154 status = info[i].mFeatureStatus;
1155 if (!info[i].mRuleId.IsEmpty()) {
1156 aFailureId = info[i].mRuleId.get();
1157 } else {
1158 aFailureId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID";
1160 break;
1165 #if defined(XP_WIN)
1166 // As a very special case, we block D2D on machines with an NVidia 310M GPU
1167 // as either the primary or secondary adapter. D2D is also blocked when the
1168 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
1169 // If the primary GPU already matched something in the blocklist then we
1170 // ignore this special rule. See bug 1008759.
1171 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
1172 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
1173 if (!adapterInfoFailed[1]) {
1174 nsAString& nvVendorID =
1175 (nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
1176 const nsString nv310mDeviceId = u"0x0A70"_ns;
1177 if (nvVendorID.Equals(adapterVendorID[1],
1178 nsCaseInsensitiveStringComparator) &&
1179 nv310mDeviceId.Equals(adapterDeviceID[1],
1180 nsCaseInsensitiveStringComparator)) {
1181 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1182 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
1187 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
1188 // back to the Windows handler, so we must handle this here.
1189 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
1190 if (info[i].mSuggestedVersion) {
1191 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
1192 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
1193 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
1194 aSuggestedVersion.AppendPrintf(
1195 "%lld.%lld.%lld.%lld",
1196 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
1197 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
1198 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
1199 (info[i].mDriverVersion & 0x000000000000ffff));
1202 #endif
1204 return status;
1207 void GfxInfoBase::SetFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>&& aFS) {
1208 MOZ_ASSERT(!sFeatureStatus);
1209 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>(std::move(aFS)));
1212 bool GfxInfoBase::DoesDesktopEnvironmentMatch(
1213 const nsAString& aBlocklistDesktop, const nsAString& aDesktopEnv) {
1214 return aBlocklistDesktop.Equals(aDesktopEnv,
1215 nsCaseInsensitiveStringComparator) ||
1216 aBlocklistDesktop.Equals(
1217 GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::All),
1218 nsCaseInsensitiveStringComparator);
1221 bool GfxInfoBase::DoesWindowProtocolMatch(
1222 const nsAString& aBlocklistWindowProtocol,
1223 const nsAString& aWindowProtocol) {
1224 return aBlocklistWindowProtocol.Equals(aWindowProtocol,
1225 nsCaseInsensitiveStringComparator) ||
1226 aBlocklistWindowProtocol.Equals(
1227 GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
1228 nsCaseInsensitiveStringComparator);
1231 bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
1232 const nsAString& aAdapterVendor) {
1233 return aBlocklistVendor.Equals(aAdapterVendor,
1234 nsCaseInsensitiveStringComparator) ||
1235 aBlocklistVendor.Equals(
1236 GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
1237 nsCaseInsensitiveStringComparator);
1240 bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
1241 const nsAString& aDriverVendor) {
1242 return aBlocklistVendor.Equals(aDriverVendor,
1243 nsCaseInsensitiveStringComparator) ||
1244 aBlocklistVendor.Equals(
1245 GfxDriverInfo::GetDriverVendor(DriverVendor::All),
1246 nsCaseInsensitiveStringComparator);
1249 bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
1250 return aFeature == nsIGfxInfo::FEATURE_WEBRENDER;
1253 nsresult GfxInfoBase::GetFeatureStatusImpl(
1254 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
1255 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
1256 OperatingSystem* aOS /* = nullptr */) {
1257 if (aFeature <= 0) {
1258 gfxWarning() << "Invalid feature <= 0";
1259 return NS_OK;
1262 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1263 // Terminate now with the status determined by the derived type (OS-specific
1264 // code).
1265 return NS_OK;
1268 if (sShutdownOccurred) {
1269 // This is futile; we've already commenced shutdown and our blocklists have
1270 // been deleted. We may want to look into resurrecting the blocklist instead
1271 // but for now, just don't even go there.
1272 return NS_OK;
1275 // Ensure any additional initialization required is complete.
1276 GetData();
1278 // If an operating system was provided by the derived GetFeatureStatusImpl,
1279 // grab it here. Otherwise, the OS is unknown.
1280 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
1282 nsAutoString adapterVendorID;
1283 nsAutoString adapterDeviceID;
1284 nsAutoString adapterDriverVersionString;
1285 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
1286 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
1287 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
1288 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
1289 *aStatus = FEATURE_BLOCKED_DEVICE;
1290 return NS_OK;
1293 // Check if the device is blocked from the downloaded blocklist. If not, check
1294 // the static list after that. This order is used so that we can later escape
1295 // out of static blocks (i.e. if we were wrong or something was patched, we
1296 // can back out our static block without doing a release).
1297 int32_t status;
1298 if (aDriverInfo.Length()) {
1299 status =
1300 FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
1301 aFailureId, os, /* aForAllowing */ false);
1302 } else {
1303 if (!sDriverInfo) {
1304 sDriverInfo = new nsTArray<GfxDriverInfo>();
1306 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
1307 aFeature, aFailureId, os,
1308 /* aForAllowing */ false);
1311 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1312 if (IsFeatureAllowlisted(aFeature)) {
1313 // This feature is actually using the allowlist; that means after we pass
1314 // the blocklist to prevent us explicitly from getting the feature, we now
1315 // need to check the allowlist to ensure we are allowed to get it in the
1316 // first place.
1317 if (aDriverInfo.Length()) {
1318 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
1319 aFeature, aFailureId, os,
1320 /* aForAllowing */ true);
1321 } else {
1322 status = FindBlocklistedDeviceInList(
1323 GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
1324 /* aForAllowing */ true);
1327 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1328 status = nsIGfxInfo::FEATURE_DENIED;
1330 } else {
1331 // It's now done being processed. It's safe to set the status to
1332 // STATUS_OK.
1333 status = nsIGfxInfo::FEATURE_STATUS_OK;
1337 *aStatus = status;
1338 return NS_OK;
1341 NS_IMETHODIMP
1342 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
1343 nsAString& aVersion) {
1344 nsCString version;
1345 if (GetPrefValueForDriverVersion(version)) {
1346 aVersion = NS_ConvertASCIItoUTF16(version);
1347 return NS_OK;
1350 int32_t status;
1351 nsCString discardFailureId;
1352 nsTArray<GfxDriverInfo> driverInfo;
1353 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
1354 discardFailureId);
1357 void GfxInfoBase::EvaluateDownloadedBlocklist(
1358 nsTArray<GfxDriverInfo>& aDriverInfo) {
1359 int32_t features[] = {nsIGfxInfo::FEATURE_DIRECT2D,
1360 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
1361 nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
1362 nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
1363 nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
1364 nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
1365 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1366 nsIGfxInfo::FEATURE_OPENGL_LAYERS,
1367 nsIGfxInfo::FEATURE_WEBGL_OPENGL,
1368 nsIGfxInfo::FEATURE_WEBGL_ANGLE,
1369 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
1370 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
1371 nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA,
1372 nsIGfxInfo::FEATURE_STAGEFRIGHT,
1373 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264,
1374 nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
1375 nsIGfxInfo::FEATURE_VP8_HW_DECODE,
1376 nsIGfxInfo::FEATURE_VP9_HW_DECODE,
1377 nsIGfxInfo::FEATURE_DX_INTEROP2,
1378 nsIGfxInfo::FEATURE_GPU_PROCESS,
1379 nsIGfxInfo::FEATURE_WEBGL2,
1380 nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX,
1381 nsIGfxInfo::FEATURE_WEBRENDER,
1382 nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
1383 nsIGfxInfo::FEATURE_DX_NV12,
1384 nsIGfxInfo::FEATURE_DX_P010,
1385 nsIGfxInfo::FEATURE_DX_P016,
1386 nsIGfxInfo::FEATURE_GL_SWIZZLE,
1387 nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS,
1388 nsIGfxInfo::FEATURE_X11_EGL,
1389 nsIGfxInfo::FEATURE_DMABUF,
1390 nsIGfxInfo::FEATURE_VAAPI,
1391 nsIGfxInfo::FEATURE_WEBGPU,
1392 nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
1395 // For every feature we know about, we evaluate whether this blocklist has a
1396 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1397 // GetFeatureStatus above, so we don't need to hold on to this blocklist
1398 // anywhere permanent.
1399 int i = 0;
1400 while (features[i]) {
1401 int32_t status;
1402 nsCString failureId;
1403 nsAutoString suggestedVersion;
1404 if (NS_SUCCEEDED(GetFeatureStatusImpl(
1405 features[i], &status, suggestedVersion, aDriverInfo, failureId))) {
1406 switch (status) {
1407 default:
1408 case nsIGfxInfo::FEATURE_STATUS_OK:
1409 RemovePrefForFeature(features[i]);
1410 break;
1412 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1413 if (!suggestedVersion.IsEmpty()) {
1414 SetPrefValueForDriverVersion(suggestedVersion);
1415 } else {
1416 RemovePrefForDriverVersion();
1418 [[fallthrough]];
1420 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1421 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1422 case nsIGfxInfo::FEATURE_DISCOURAGED:
1423 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1424 SetPrefValueForFeature(features[i], status, failureId);
1425 break;
1429 ++i;
1433 NS_IMETHODIMP_(void)
1434 GfxInfoBase::LogFailure(const nsACString& failure) {
1435 // gfxCriticalError has a mutex lock of its own, so we may not actually
1436 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1437 // will not return the copy of the logs unless it can get the same lock
1438 // that gfxCriticalError uses. Still, that is so much of an implementation
1439 // detail that it's nicer to just add an extra lock here and in
1440 // ::GetFailures()
1441 MutexAutoLock lock(mMutex);
1443 // By default, gfxCriticalError asserts; make it not assert in this case.
1444 gfxCriticalError(CriticalLog::DefaultOptions(false))
1445 << "(LF) " << failure.BeginReading();
1448 NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
1449 nsTArray<nsCString>& failures) {
1450 MutexAutoLock lock(mMutex);
1452 LogForwarder* logForwarder = Factory::GetLogForwarder();
1453 if (!logForwarder) {
1454 return NS_ERROR_UNEXPECTED;
1457 // There are two string copies in this method, starting with this one. We are
1458 // assuming this is not a big deal, as the size of the array should be small
1459 // and the strings in it should be small as well (the error messages in the
1460 // code.) The second copy happens with the AppendElement() calls.
1461 // Technically, we don't need the mutex lock after the StringVectorCopy()
1462 // call.
1463 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1464 LoggingRecord::const_iterator it;
1465 for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
1466 failures.AppendElement(
1467 nsDependentCSubstring(Get<1>(*it).c_str(), Get<1>(*it).size()));
1468 indices.AppendElement(Get<0>(*it));
1471 return NS_OK;
1474 nsTArray<GfxInfoCollectorBase*>* sCollectors;
1476 static void InitCollectors() {
1477 if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1480 nsresult GfxInfoBase::GetInfo(JSContext* aCx,
1481 JS::MutableHandle<JS::Value> aResult) {
1482 InitCollectors();
1483 InfoObject obj(aCx);
1485 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1486 (*sCollectors)[i]->GetInfo(obj);
1489 // Some example property definitions
1490 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1491 // obj.DefineProperty("renderer", mRendererIDsString);
1492 // obj.DefineProperty("five", 5);
1494 if (!obj.mOk) {
1495 return NS_ERROR_FAILURE;
1498 aResult.setObject(*obj.mObj);
1499 return NS_OK;
1502 nsAutoCString gBaseAppVersion;
1504 const nsCString& GfxInfoBase::GetApplicationVersion() {
1505 static bool versionInitialized = false;
1506 if (!versionInitialized) {
1507 // If we fail to get the version, we will not try again.
1508 versionInitialized = true;
1510 // Get the version from xpcom/system/nsIXULAppInfo.idl
1511 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1512 if (app) {
1513 app->GetVersion(gBaseAppVersion);
1516 return gBaseAppVersion;
1519 void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
1520 InitCollectors();
1521 sCollectors->AppendElement(collector);
1524 void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
1525 InitCollectors();
1526 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1527 if ((*sCollectors)[i] == collector) {
1528 sCollectors->RemoveElementAt(i);
1529 break;
1532 if (sCollectors->IsEmpty()) {
1533 delete sCollectors;
1534 sCollectors = nullptr;
1538 nsresult GfxInfoBase::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) {
1539 // If we have no platform specific implementation for detecting monitors, we
1540 // can just get the screen size from gfxPlatform as the best guess.
1541 if (!gfxPlatform::Initialized()) {
1542 return NS_OK;
1545 // If the screen size is empty, we are probably in xpcshell.
1546 gfx::IntSize screenSize = gfxPlatform::GetPlatform()->GetScreenSize();
1548 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1550 JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
1551 JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
1553 JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
1554 JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
1556 JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
1557 JS_SetElement(aCx, aOutArray, 0, element);
1559 return NS_OK;
1562 NS_IMETHODIMP
1563 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult) {
1564 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1566 nsresult rv = FindMonitors(aCx, array);
1567 if (NS_FAILED(rv)) {
1568 return rv;
1571 aResult.setObject(*array);
1572 return NS_OK;
1575 NS_IMETHODIMP
1576 GfxInfoBase::RefreshMonitors() { return NS_ERROR_NOT_IMPLEMENTED; }
1578 static inline bool SetJSPropertyString(JSContext* aCx,
1579 JS::Handle<JSObject*> aObj,
1580 const char* aProp, const char* aString) {
1581 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1582 if (!str) {
1583 return false;
1586 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1587 return JS_SetProperty(aCx, aObj, aProp, val);
1590 template <typename T>
1591 static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
1592 const T& aValue) {
1593 uint32_t index;
1594 if (!JS::GetArrayLength(aCx, aObj, &index)) {
1595 return false;
1597 return JS_SetElement(aCx, aObj, index, aValue);
1600 nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
1601 JS::MutableHandle<JS::Value> aOut) {
1602 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1603 if (!obj) {
1604 return NS_ERROR_OUT_OF_MEMORY;
1606 aOut.setObject(*obj);
1608 layers::LayersBackend backend =
1609 gfxPlatform::Initialized()
1610 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1611 : layers::LayersBackend::LAYERS_NONE;
1612 const char* backendName = layers::GetLayersBackendName(backend);
1613 SetJSPropertyString(aCx, obj, "compositor", backendName);
1615 // If graphics isn't initialized yet, just stop now.
1616 if (!gfxPlatform::Initialized()) {
1617 return NS_OK;
1620 DescribeFeatures(aCx, obj);
1621 return NS_OK;
1624 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
1625 JS::MutableHandle<JS::Value> aOut) {
1626 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1627 if (!containerObj) {
1628 return NS_ERROR_OUT_OF_MEMORY;
1630 aOut.setObject(*containerObj);
1632 JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
1633 if (!featureArray) {
1634 return NS_ERROR_OUT_OF_MEMORY;
1637 // Collect features.
1638 gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
1639 FeatureState& aFeature) -> void {
1640 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1641 if (!obj) {
1642 return;
1644 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1645 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1646 !SetJSPropertyString(aCx, obj, "status",
1647 FeatureStatusToString(aFeature.GetValue()))) {
1648 return;
1651 JS::Rooted<JS::Value> log(aCx);
1652 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1653 return;
1655 if (!JS_SetProperty(aCx, obj, "log", log)) {
1656 return;
1659 if (!AppendJSElement(aCx, featureArray, obj)) {
1660 return;
1664 JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
1665 if (!fallbackArray) {
1666 return NS_ERROR_OUT_OF_MEMORY;
1669 // Collect fallbacks.
1670 gfxConfig::ForEachFallback(
1671 [&](const char* aName, const char* aMessage) -> void {
1672 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1673 if (!obj) {
1674 return;
1677 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1678 !SetJSPropertyString(aCx, obj, "message", aMessage)) {
1679 return;
1682 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1683 return;
1687 JS::Rooted<JS::Value> val(aCx);
1689 val = JS::ObjectValue(*featureArray);
1690 JS_SetProperty(aCx, containerObj, "features", val);
1692 val = JS::ObjectValue(*fallbackArray);
1693 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1695 return NS_OK;
1698 bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
1699 const FeatureState& aFeature,
1700 JS::MutableHandle<JS::Value> aOut) {
1701 JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
1702 if (!log) {
1703 return false;
1705 aOut.setObject(*log);
1707 aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
1708 const char* aMessage,
1709 const nsCString& aFailureId) -> void {
1710 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1711 if (!obj) {
1712 return;
1715 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1716 !SetJSPropertyString(aCx, obj, "status",
1717 FeatureStatusToString(aStatus)) ||
1718 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
1719 return;
1722 if (!AppendJSElement(aCx, log, obj)) {
1723 return;
1727 return true;
1730 void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
1731 JS::Rooted<JSObject*> obj(aCx);
1733 gfx::FeatureState& hwCompositing =
1734 gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
1735 InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
1737 gfx::FeatureState& gpuProcess =
1738 gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
1739 InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
1741 gfx::FeatureState& wrQualified =
1742 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_QUALIFIED);
1743 InitFeatureObject(aCx, aObj, "wrQualified", wrQualified, &obj);
1745 gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
1746 InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
1748 gfx::FeatureState& wrCompositor =
1749 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
1750 InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
1752 gfx::FeatureState& wrSoftware =
1753 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_SOFTWARE);
1754 InitFeatureObject(aCx, aObj, "wrSoftware", wrSoftware, &obj);
1756 gfx::FeatureState& openglCompositing =
1757 gfxConfig::GetFeature(gfx::Feature::OPENGL_COMPOSITING);
1758 InitFeatureObject(aCx, aObj, "openglCompositing", openglCompositing, &obj);
1760 gfx::FeatureState& omtp = gfxConfig::GetFeature(gfx::Feature::OMTP);
1761 InitFeatureObject(aCx, aObj, "omtp", omtp, &obj);
1764 bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
1765 JS::Handle<JSObject*> aContainer,
1766 const char* aName,
1767 mozilla::gfx::FeatureState& aFeatureState,
1768 JS::MutableHandle<JSObject*> aOutObj) {
1769 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1770 if (!obj) {
1771 return false;
1774 nsCString status = aFeatureState.GetStatusAndFailureIdString();
1776 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
1777 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1778 JS_SetProperty(aCx, obj, "status", val);
1780 // Add the feature object to the container.
1782 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1783 JS_SetProperty(aCx, aContainer, aName, val);
1786 aOutObj.set(obj);
1787 return true;
1790 nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
1791 JS::MutableHandle<JS::Value> aOut) {
1792 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1793 if (!array) {
1794 return NS_ERROR_OUT_OF_MEMORY;
1796 aOut.setObject(*array);
1798 DriverCrashGuard::ForEachActiveCrashGuard(
1799 [&](const char* aName, const char* aPrefName) -> void {
1800 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1801 if (!obj) {
1802 return;
1804 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1805 return;
1807 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1808 return;
1810 if (!AppendJSElement(aCx, array, obj)) {
1811 return;
1815 return NS_OK;
1818 NS_IMETHODIMP
1819 GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled) {
1820 *aWebRenderEnabled = gfxVars::UseWebRender();
1821 return NS_OK;
1824 NS_IMETHODIMP
1825 GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
1826 *aTargetFrameRate = gfxPlatform::TargetFrameRate();
1827 return NS_OK;
1830 NS_IMETHODIMP
1831 GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
1832 *aIsHeadless = gfxPlatform::IsHeadless();
1833 return NS_OK;
1836 NS_IMETHODIMP
1837 GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
1838 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1839 nsString outStr;
1841 switch (backend) {
1842 case BackendType::DIRECT2D1_1: {
1843 outStr.AppendPrintf("Direct2D 1.1");
1844 break;
1846 case BackendType::SKIA: {
1847 outStr.AppendPrintf("Skia");
1848 break;
1850 case BackendType::CAIRO: {
1851 outStr.AppendPrintf("Cairo");
1852 break;
1854 default:
1855 return NS_ERROR_FAILURE;
1858 aContentBackend.Assign(outStr);
1859 return NS_OK;
1862 NS_IMETHODIMP
1863 GfxInfoBase::GetAzureCanvasBackend(nsAString& aBackend) {
1864 CopyASCIItoUTF16(mozilla::MakeStringSpan(
1865 gfxPlatform::GetPlatform()->GetAzureCanvasBackend()),
1866 aBackend);
1867 return NS_OK;
1870 NS_IMETHODIMP
1871 GfxInfoBase::GetAzureContentBackend(nsAString& aBackend) {
1872 CopyASCIItoUTF16(mozilla::MakeStringSpan(
1873 gfxPlatform::GetPlatform()->GetAzureContentBackend()),
1874 aBackend);
1875 return NS_OK;
1878 NS_IMETHODIMP
1879 GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
1880 GPUProcessManager* gpu = GPUProcessManager::Get();
1881 if (!gpu) {
1882 // Not supported in content processes.
1883 return NS_ERROR_FAILURE;
1886 *aOutValue = !!gpu->GetGPUChild();
1887 return NS_OK;
1890 NS_IMETHODIMP
1891 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
1892 gfxPlatform::GetPlatform();
1894 GPUProcessManager* gpm = GPUProcessManager::Get();
1895 if (aEnable) {
1896 if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
1897 gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1899 gpm->LaunchGPUProcess();
1900 gpm->EnsureGPUReady();
1901 } else {
1902 gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1903 gpm->KillProcess();
1906 *_retval = true;
1907 return NS_OK;
1910 NS_IMETHODIMP GfxInfoBase::KillGPUProcessForTests() {
1911 GPUProcessManager* gpm = GPUProcessManager::Get();
1912 if (!gpm) {
1913 // gfxPlatform has not been initialized.
1914 return NS_ERROR_NOT_INITIALIZED;
1917 gpm->KillProcess();
1918 return NS_OK;
1921 NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
1922 GPUProcessManager* gpm = GPUProcessManager::Get();
1923 if (!gpm) {
1924 // gfxPlatform has not been initialized.
1925 return NS_ERROR_NOT_INITIALIZED;
1928 gpm->CrashProcess();
1929 return NS_OK;
1932 GfxInfoCollectorBase::GfxInfoCollectorBase() {
1933 GfxInfoBase::AddCollector(this);
1936 GfxInfoCollectorBase::~GfxInfoCollectorBase() {
1937 GfxInfoBase::RemoveCollector(this);