Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / gtk / GfxInfo.cpp
blob01a528c921b816157e813e1d725c039f429b3cab
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
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 "GfxInfo.h"
10 #include <cctype>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <string>
14 #include <poll.h>
15 #include <sys/types.h>
16 #include <sys/utsname.h>
17 #include <sys/wait.h>
18 #include <glib.h>
19 #include <fcntl.h>
21 #include "mozilla/gfx/Logging.h"
22 #include "mozilla/SSE.h"
23 #include "mozilla/glean/GfxMetrics.h"
24 #include "mozilla/XREAppData.h"
25 #include "mozilla/ScopeExit.h"
26 #include "mozilla/GUniquePtr.h"
27 #include "mozilla/StaticPrefs_media.h"
28 #include "nsCRTGlue.h"
29 #include "nsExceptionHandler.h"
30 #include "nsPrintfCString.h"
31 #include "nsString.h"
32 #include "nsStringFwd.h"
33 #include "nsUnicharUtils.h"
34 #include "nsWhitespaceTokenizer.h"
35 #include "prenv.h"
36 #include "WidgetUtilsGtk.h"
37 #include "MediaCodecsSupport.h"
38 #include "nsAppRunner.h"
40 // How long we wait for data from glxtest/vaapi test process in milliseconds.
41 #define GFX_TEST_TIMEOUT 4000
42 #define VAAPI_TEST_TIMEOUT 2000
43 #define V4L2_TEST_TIMEOUT 2000
45 #define GLX_PROBE_BINARY u"glxtest"_ns
46 #define VAAPI_PROBE_BINARY u"vaapitest"_ns
47 #define V4L2_PROBE_BINARY u"v4l2test"_ns
49 namespace mozilla::widget {
51 #ifdef DEBUG
52 NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug)
53 #endif
55 int GfxInfo::sGLXTestPipe = -1;
56 pid_t GfxInfo::sGLXTestPID = 0;
58 // bits to use decoding codec information returned from glxtest
59 constexpr int CODEC_HW_H264 = 1 << 4;
60 constexpr int CODEC_HW_VP8 = 1 << 5;
61 constexpr int CODEC_HW_VP9 = 1 << 6;
62 constexpr int CODEC_HW_AV1 = 1 << 7;
64 nsresult GfxInfo::Init() {
65 mGLMajorVersion = 0;
66 mGLMinorVersion = 0;
67 mHasTextureFromPixmap = false;
68 mIsMesa = false;
69 mIsAccelerated = true;
70 mIsWayland = false;
71 mIsXWayland = false;
72 mHasMultipleGPUs = false;
73 mGlxTestError = false;
74 return GfxInfoBase::Init();
77 void GfxInfo::AddCrashReportAnnotations() {
78 CrashReporter::RecordAnnotationNSCString(
79 CrashReporter::Annotation::AdapterVendorID, mVendorId);
80 CrashReporter::RecordAnnotationNSCString(
81 CrashReporter::Annotation::AdapterDeviceID, mDeviceId);
82 CrashReporter::RecordAnnotationNSCString(
83 CrashReporter::Annotation::AdapterDriverVendor, mDriverVendor);
84 CrashReporter::RecordAnnotationNSCString(
85 CrashReporter::Annotation::AdapterDriverVersion, mDriverVersion);
86 CrashReporter::RecordAnnotationBool(CrashReporter::Annotation::IsWayland,
87 mIsWayland);
88 CrashReporter::RecordAnnotationNSCString(
89 CrashReporter::Annotation::DesktopEnvironment,
90 GetDesktopEnvironmentIdentifier());
92 if (mHasMultipleGPUs) {
93 nsAutoCString note;
94 note.AppendLiteral("Has dual GPUs.");
95 if (!mSecondaryVendorId.IsEmpty()) {
96 note.AppendLiteral(" GPU #2: AdapterVendorID2: ");
97 note.Append(mSecondaryVendorId);
98 note.AppendLiteral(", AdapterDeviceID2: ");
99 note.Append(mSecondaryDeviceId);
101 CrashReporter::AppendAppNotesToCrashReport(note);
105 static bool MakeFdNonBlocking(int fd) {
106 return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) != -1;
109 static bool ManageChildProcess(const char* aProcessName, int* aPID, int* aPipe,
110 int aTimeout, char** aData) {
111 // Don't try anything if we failed before
112 if (*aPID < 0) {
113 return false;
116 GIOChannel* channel = nullptr;
117 *aData = nullptr;
119 auto free = mozilla::MakeScopeExit([&] {
120 if (channel) {
121 g_io_channel_unref(channel);
123 if (*aPipe >= 0) {
124 close(*aPipe);
125 *aPipe = -1;
129 const TimeStamp deadline =
130 TimeStamp::Now() + TimeDuration::FromMilliseconds(aTimeout);
132 struct pollfd pfd{};
133 pfd.fd = *aPipe;
134 pfd.events = POLLIN;
136 while (poll(&pfd, 1, aTimeout) != 1) {
137 if (errno != EAGAIN && errno != EINTR) {
138 gfxCriticalNote << "ManageChildProcess(" << aProcessName
139 << "): poll failed: " << strerror(errno) << "\n";
140 return false;
142 if (TimeStamp::Now() > deadline) {
143 gfxCriticalNote << "ManageChildProcess(" << aProcessName
144 << "): process hangs\n";
145 return false;
149 channel = g_io_channel_unix_new(*aPipe);
150 MakeFdNonBlocking(*aPipe);
152 GUniquePtr<GError> error;
153 gsize length = 0;
154 int ret;
155 do {
156 error = nullptr;
157 ret = g_io_channel_read_to_end(channel, aData, &length,
158 getter_Transfers(error));
159 } while (ret == G_IO_STATUS_AGAIN && TimeStamp::Now() < deadline);
160 if (error || ret != G_IO_STATUS_NORMAL) {
161 gfxCriticalNote << "ManageChildProcess(" << aProcessName
162 << "): failed to read data from child process: ";
163 if (error) {
164 gfxCriticalNote << error->message;
165 } else {
166 gfxCriticalNote << "timeout";
168 return false;
171 int status = 0;
172 int pid = *aPID;
173 *aPID = -1;
175 while (true) {
176 int ret = waitpid(pid, &status, WNOHANG);
177 if (ret > 0) {
178 break;
180 if (ret < 0) {
181 if (errno == ECHILD) {
182 // Bug 718629
183 // ECHILD happens when the glxtest process got reaped got reaped after a
184 // PR_CreateProcess as per bug 227246. This shouldn't matter, as we
185 // still seem to get the data from the pipe, and if we didn't, the
186 // outcome would be to blocklist anyway.
187 return true;
189 if (errno != EAGAIN && errno != EINTR) {
190 gfxCriticalNote << "ManageChildProcess(" << aProcessName
191 << "): waitpid failed: " << strerror(errno) << "\n";
192 return false;
195 if (TimeStamp::Now() > deadline) {
196 gfxCriticalNote << "ManageChildProcess(" << aProcessName
197 << "): process hangs\n";
198 return false;
200 // Wait 50ms to another waitpid() check.
201 usleep(50000);
204 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
207 // to understand this function, see bug 639842. We retrieve the OpenGL driver
208 // information in a separate process to protect against bad drivers.
209 void GfxInfo::GetData() {
210 if (mInitialized) {
211 return;
213 mInitialized = true;
215 // In some cases (xpcshell test, Profile manager etc.)
216 // FireGLXTestProcess() is not fired in advance
217 // so we call it here.
218 GfxInfo::FireGLXTestProcess();
220 GfxInfoBase::GetData();
222 char* glxData = nullptr;
223 auto free = mozilla::MakeScopeExit([&] { g_free((void*)glxData); });
225 bool error = !ManageChildProcess("glxtest", &sGLXTestPID, &sGLXTestPipe,
226 GFX_TEST_TIMEOUT, &glxData);
227 if (error) {
228 gfxCriticalNote << "glxtest: ManageChildProcess failed\n";
231 nsCString glVendor;
232 nsCString glRenderer;
233 nsCString glVersion;
234 nsCString textureFromPixmap;
235 nsCString testType;
237 // Available if GLX_MESA_query_renderer is supported.
238 nsCString mesaVendor;
239 nsCString mesaDevice;
240 nsCString mesaAccelerated;
241 // Available if using a DRI-based libGL stack.
242 nsCString driDriver;
243 nsCString adapterRam;
245 nsCString drmRenderDevice;
247 nsCString ddxDriver;
249 AutoTArray<nsCString, 2> pciVendors;
250 AutoTArray<nsCString, 2> pciDevices;
252 nsCString* stringToFill = nullptr;
253 bool logString = false;
254 bool errorLog = false;
256 char* bufptr = glxData;
258 while (true) {
259 char* line = NS_strtok("\n", &bufptr);
260 if (!line) break;
261 if (stringToFill) {
262 stringToFill->Assign(line);
263 stringToFill = nullptr;
264 } else if (logString) {
265 gfxCriticalNote << "glxtest: " << line;
266 logString = false;
267 } else if (!strcmp(line, "VENDOR")) {
268 stringToFill = &glVendor;
269 } else if (!strcmp(line, "RENDERER")) {
270 stringToFill = &glRenderer;
271 } else if (!strcmp(line, "VERSION")) {
272 stringToFill = &glVersion;
273 } else if (!strcmp(line, "TFP")) {
274 stringToFill = &textureFromPixmap;
275 } else if (!strcmp(line, "MESA_VENDOR_ID")) {
276 stringToFill = &mesaVendor;
277 } else if (!strcmp(line, "MESA_DEVICE_ID")) {
278 stringToFill = &mesaDevice;
279 } else if (!strcmp(line, "MESA_ACCELERATED")) {
280 stringToFill = &mesaAccelerated;
281 } else if (!strcmp(line, "MESA_VRAM")) {
282 stringToFill = &adapterRam;
283 } else if (!strcmp(line, "DDX_DRIVER")) {
284 stringToFill = &ddxDriver;
285 } else if (!strcmp(line, "DRI_DRIVER")) {
286 stringToFill = &driDriver;
287 } else if (!strcmp(line, "PCI_VENDOR_ID")) {
288 stringToFill = pciVendors.AppendElement();
289 } else if (!strcmp(line, "PCI_DEVICE_ID")) {
290 stringToFill = pciDevices.AppendElement();
291 } else if (!strcmp(line, "DRM_RENDERDEVICE")) {
292 stringToFill = &drmRenderDevice;
293 } else if (!strcmp(line, "TEST_TYPE")) {
294 stringToFill = &testType;
295 } else if (!strcmp(line, "WARNING")) {
296 logString = true;
297 } else if (!strcmp(line, "ERROR")) {
298 logString = true;
299 errorLog = true;
303 MOZ_ASSERT(pciDevices.Length() == pciVendors.Length(),
304 "Missing PCI vendors/devices");
306 size_t pciLen = std::min(pciVendors.Length(), pciDevices.Length());
307 mHasMultipleGPUs = pciLen > 1;
309 if (!strcmp(textureFromPixmap.get(), "TRUE")) mHasTextureFromPixmap = true;
311 // only useful for Linux kernel version check for FGLRX driver.
312 // assumes X client == X server, which is sad.
313 struct utsname unameobj{};
314 if (uname(&unameobj) >= 0) {
315 mOS.Assign(unameobj.sysname);
316 mOSRelease.Assign(unameobj.release);
319 const char* spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
320 if (spoofedVendor) glVendor.Assign(spoofedVendor);
321 const char* spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
322 if (spoofedRenderer) glRenderer.Assign(spoofedRenderer);
323 const char* spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
324 if (spoofedVersion) glVersion.Assign(spoofedVersion);
325 const char* spoofedOS = PR_GetEnv("MOZ_GFX_SPOOF_OS");
326 if (spoofedOS) mOS.Assign(spoofedOS);
327 const char* spoofedOSRelease = PR_GetEnv("MOZ_GFX_SPOOF_OS_RELEASE");
328 if (spoofedOSRelease) mOSRelease.Assign(spoofedOSRelease);
330 // Scan the GL_VERSION string for the GL and driver versions.
331 nsCWhitespaceTokenizer tokenizer(glVersion);
332 while (tokenizer.hasMoreTokens()) {
333 nsCString token(tokenizer.nextToken());
334 unsigned int major = 0, minor = 0, revision = 0, patch = 0;
335 if (sscanf(token.get(), "%u.%u.%u.%u", &major, &minor, &revision, &patch) >=
336 2) {
337 // A survey of GL_VENDOR strings indicates that the first version is
338 // always the GL version, the second is usually the driver version.
339 if (mGLMajorVersion == 0) {
340 mGLMajorVersion = major;
341 mGLMinorVersion = minor;
342 } else if (mDriverVersion.IsEmpty()) { // Not already spoofed.
343 mDriverVersion =
344 nsPrintfCString("%u.%u.%u.%u", major, minor, revision, patch);
349 if (mGLMajorVersion == 0) {
350 NS_WARNING("Failed to parse GL version!");
353 mDrmRenderDevice = std::move(drmRenderDevice);
354 mTestType = std::move(testType);
356 // Mesa always exposes itself in the GL_VERSION string, but not always the
357 // GL_VENDOR string.
358 mIsMesa = glVersion.Find("Mesa") != -1;
360 // We need to use custom driver vendor IDs for mesa so we can treat them
361 // differently than the proprietary drivers.
362 if (mIsMesa) {
363 mIsAccelerated = !mesaAccelerated.Equals("FALSE");
364 // Process software rasterizers before the DRI driver string; we may be
365 // forcing software rasterization on a DRI-accelerated X server by using
366 // LIBGL_ALWAYS_SOFTWARE or a similar restriction.
367 if (strcasestr(glRenderer.get(), "llvmpipe")) {
368 CopyUTF16toUTF8(
369 GfxDriverInfo::GetDriverVendor(DriverVendor::MesaLLVMPipe),
370 mDriverVendor);
371 mIsAccelerated = false;
372 } else if (strcasestr(glRenderer.get(), "softpipe")) {
373 CopyUTF16toUTF8(
374 GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSoftPipe),
375 mDriverVendor);
376 mIsAccelerated = false;
377 } else if (strcasestr(glRenderer.get(), "software rasterizer")) {
378 CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSWRast),
379 mDriverVendor);
380 mIsAccelerated = false;
381 } else if (strcasestr(driDriver.get(), "vmwgfx")) {
382 CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaVM),
383 mDriverVendor);
384 mIsAccelerated = false;
385 } else if (!mIsAccelerated) {
386 CopyUTF16toUTF8(
387 GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSWUnknown),
388 mDriverVendor);
389 } else if (!driDriver.IsEmpty()) {
390 mDriverVendor = nsPrintfCString("mesa/%s", driDriver.get());
391 } else {
392 // Some other mesa configuration where we couldn't get enough info.
393 NS_WARNING("Failed to detect Mesa driver being used!");
394 CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaUnknown),
395 mDriverVendor);
398 if (!mesaVendor.IsEmpty()) {
399 mVendorId = mesaVendor;
402 if (!mesaDevice.IsEmpty()) {
403 mDeviceId = mesaDevice;
406 if (!mIsAccelerated && mVendorId.IsEmpty()) {
407 mVendorId.Assign(glVendor.get());
410 if (!mIsAccelerated && mDeviceId.IsEmpty()) {
411 mDeviceId.Assign(glRenderer.get());
413 } else if (glVendor.EqualsLiteral("NVIDIA Corporation")) {
414 CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA),
415 mVendorId);
416 mDriverVendor.AssignLiteral("nvidia/unknown");
417 // TODO: Use NV-CONTROL X11 extension to query Device ID and VRAM.
418 } else if (glVendor.EqualsLiteral("ATI Technologies Inc.")) {
419 CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::ATI),
420 mVendorId);
421 mDriverVendor.AssignLiteral("ati/unknown");
422 // TODO: Look into ways to find the device ID on FGLRX.
423 } else {
424 NS_WARNING("Failed to detect GL vendor!");
427 if (!adapterRam.IsEmpty()) {
428 mAdapterRAM = (uint32_t)atoi(adapterRam.get());
431 // If we have the DRI driver, we can derive the vendor ID from that if needed.
432 if (mVendorId.IsEmpty() && !driDriver.IsEmpty()) {
433 const char* nvidiaDrivers[] = {"nouveau", "tegra", nullptr};
434 for (size_t i = 0; nvidiaDrivers[i]; ++i) {
435 if (driDriver.Equals(nvidiaDrivers[i])) {
436 CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA),
437 mVendorId);
438 break;
442 if (mVendorId.IsEmpty()) {
443 const char* intelDrivers[] = {"iris", "crocus", "i915", "i965",
444 "i810", "intel", nullptr};
445 for (size_t i = 0; intelDrivers[i]; ++i) {
446 if (driDriver.Equals(intelDrivers[i])) {
447 CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::Intel),
448 mVendorId);
449 break;
454 if (mVendorId.IsEmpty()) {
455 const char* amdDrivers[] = {"r600", "r200", "r100",
456 "radeon", "radeonsi", nullptr};
457 for (size_t i = 0; amdDrivers[i]; ++i) {
458 if (driDriver.Equals(amdDrivers[i])) {
459 CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::ATI),
460 mVendorId);
461 break;
466 if (mVendorId.IsEmpty()) {
467 if (driDriver.EqualsLiteral("freedreno")) {
468 CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::Qualcomm),
469 mVendorId);
474 // If we still don't have a vendor ID, we can try the PCI vendor list.
475 if (mVendorId.IsEmpty()) {
476 if (pciVendors.IsEmpty()) {
477 gfxCriticalNote << "No GPUs detected via PCI\n";
478 } else {
479 for (size_t i = 0; i < pciVendors.Length(); ++i) {
480 if (mVendorId.IsEmpty()) {
481 mVendorId = pciVendors[i];
482 } else if (mVendorId != pciVendors[i]) {
483 gfxCriticalNote << "More than 1 GPU vendor detected via PCI, cannot "
484 "deduce vendor\n";
485 mVendorId.Truncate();
486 break;
492 // If we know the vendor ID, but didn't get a device ID, we can guess from the
493 // PCI device list.
494 if (mDeviceId.IsEmpty() && !mVendorId.IsEmpty()) {
495 for (size_t i = 0; i < pciLen; ++i) {
496 if (mVendorId.Equals(pciVendors[i])) {
497 if (mDeviceId.IsEmpty()) {
498 mDeviceId = pciDevices[i];
499 } else if (mDeviceId != pciDevices[i]) {
500 gfxCriticalNote << "More than 1 GPU from same vendor detected via "
501 "PCI, cannot deduce device\n";
502 mDeviceId.Truncate();
503 break;
509 // Assuming we know the vendor, we should check for a secondary card.
510 if (!mVendorId.IsEmpty()) {
511 if (pciLen > 2) {
512 gfxCriticalNote
513 << "More than 2 GPUs detected via PCI, secondary GPU is arbitrary\n";
515 for (size_t i = 0; i < pciLen; ++i) {
516 if (!mVendorId.Equals(pciVendors[i]) ||
517 (!mDeviceId.IsEmpty() && !mDeviceId.Equals(pciDevices[i]))) {
518 mSecondaryVendorId = pciVendors[i];
519 mSecondaryDeviceId = pciDevices[i];
520 break;
525 // If we couldn't choose, log them.
526 if (mVendorId.IsEmpty()) {
527 for (size_t i = 0; i < pciLen; ++i) {
528 gfxCriticalNote << "PCI candidate " << pciVendors[i].get() << "/"
529 << pciDevices[i].get() << "\n";
533 // Fallback to GL_VENDOR and GL_RENDERER.
534 if (mVendorId.IsEmpty()) {
535 mVendorId.Assign(glVendor.get());
537 if (mDeviceId.IsEmpty()) {
538 mDeviceId.Assign(glRenderer.get());
541 mAdapterDescription.Assign(glRenderer);
543 // Make a best effort guess at whether or not we are using the XWayland compat
544 // layer. For all intents and purposes, we should otherwise believe we are
545 // using X11.
546 mIsWayland = GdkIsWaylandDisplay();
547 mIsXWayland = IsXWaylandProtocol();
549 if (!ddxDriver.IsEmpty()) {
550 PRInt32 start = 0;
551 PRInt32 loc = ddxDriver.Find(";", start);
552 while (loc != kNotFound) {
553 nsCString line(ddxDriver.get() + start, loc - start);
554 mDdxDrivers.AppendElement(std::move(line));
556 start = loc + 1;
557 loc = ddxDriver.Find(";", start);
561 if (error || errorLog || mTestType.IsEmpty()) {
562 if (!mAdapterDescription.IsEmpty()) {
563 mAdapterDescription.AppendLiteral(" (See failure log)");
564 } else {
565 mAdapterDescription.AppendLiteral("See failure log");
568 mGlxTestError = true;
571 AddCrashReportAnnotations();
574 int GfxInfo::FireTestProcess(const nsAString& aBinaryFile, int* aOutPipe,
575 const char** aStringArgs) {
576 nsCOMPtr<nsIFile> appFile;
577 nsresult rv = XRE_GetBinaryPath(getter_AddRefs(appFile));
578 if (NS_FAILED(rv)) {
579 gfxCriticalNote << "Couldn't find application file.\n";
580 return false;
582 nsCOMPtr<nsIFile> exePath;
583 rv = appFile->GetParent(getter_AddRefs(exePath));
584 if (NS_FAILED(rv)) {
585 gfxCriticalNote << "Couldn't get application directory.\n";
586 return false;
588 exePath->Append(aBinaryFile);
590 #define MAX_ARGS 8
591 char* argv[MAX_ARGS + 2];
593 argv[0] = strdup(exePath->NativePath().get());
594 for (int i = 0; i < MAX_ARGS; i++) {
595 if (aStringArgs[i]) {
596 argv[i + 1] = strdup(aStringArgs[i]);
597 } else {
598 argv[i + 1] = nullptr;
599 break;
603 // Use G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD flags
604 // to g_spawn_async_with_pipes() run posix_spawn() directly.
605 int pid;
606 GUniquePtr<GError> err;
607 g_spawn_async_with_pipes(
608 nullptr, argv, nullptr,
609 GSpawnFlags(G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD),
610 nullptr, nullptr, &pid, nullptr, aOutPipe, nullptr,
611 getter_Transfers(err));
612 if (err) {
613 gfxCriticalNote << "FireTestProcess failed: " << err->message << "\n";
614 pid = 0;
616 for (auto& arg : argv) {
617 if (!arg) {
618 break;
620 free(arg);
622 return pid;
625 bool GfxInfo::FireGLXTestProcess() {
626 if (sGLXTestPID != 0) {
627 return true;
630 int pfd[2];
631 if (pipe(pfd) == -1) {
632 gfxCriticalNote << "FireGLXTestProcess failed to create pipe\n";
633 return false;
635 sGLXTestPipe = pfd[0];
637 auto pipeID = std::to_string(pfd[1]);
638 const char* args[] = {"-f", pipeID.c_str(),
639 IsWaylandEnabled() ? "-w" : nullptr, nullptr};
640 sGLXTestPID = FireTestProcess(GLX_PROBE_BINARY, nullptr, args);
641 // Set pid to -1 to avoid further test launch.
642 if (!sGLXTestPID) {
643 sGLXTestPID = -1;
645 close(pfd[1]);
646 return true;
649 void GfxInfo::GetDataVAAPI() {
650 if (mIsVAAPISupported.isSome()) {
651 return;
653 mIsVAAPISupported = Some(false);
655 #ifdef MOZ_ENABLE_VAAPI
656 char* vaapiData = nullptr;
657 auto free = mozilla::MakeScopeExit([&] { g_free((void*)vaapiData); });
659 int vaapiPipe = -1;
660 int vaapiPID = 0;
661 const char* args[] = {"-d", mDrmRenderDevice.get(), nullptr};
662 vaapiPID = FireTestProcess(VAAPI_PROBE_BINARY, &vaapiPipe, args);
663 if (!vaapiPID) {
664 return;
667 if (!ManageChildProcess("vaapitest", &vaapiPID, &vaapiPipe,
668 VAAPI_TEST_TIMEOUT, &vaapiData)) {
669 gfxCriticalNote << "vaapitest: ManageChildProcess failed\n";
670 return;
673 char* bufptr = vaapiData;
674 char* line;
675 while ((line = NS_strtok("\n", &bufptr))) {
676 if (!strcmp(line, "VAAPI_SUPPORTED")) {
677 line = NS_strtok("\n", &bufptr);
678 if (!line) {
679 gfxCriticalNote << "vaapitest: Failed to get VAAPI support\n";
680 return;
682 mIsVAAPISupported = Some(!strcmp(line, "TRUE"));
683 } else if (!strcmp(line, "VAAPI_HWCODECS")) {
684 line = NS_strtok("\n", &bufptr);
685 if (!line) {
686 gfxCriticalNote << "vaapitest: Failed to get VAAPI codecs\n";
687 return;
690 std::istringstream(line) >> mVAAPISupportedCodecs;
691 if (mVAAPISupportedCodecs & CODEC_HW_H264) {
692 media::MCSInfo::AddSupport(
693 media::MediaCodecsSupport::H264HardwareDecode);
695 if (mVAAPISupportedCodecs & CODEC_HW_VP8) {
696 media::MCSInfo::AddSupport(
697 media::MediaCodecsSupport::VP8HardwareDecode);
699 if (mVAAPISupportedCodecs & CODEC_HW_VP9) {
700 media::MCSInfo::AddSupport(
701 media::MediaCodecsSupport::VP9HardwareDecode);
703 if (mVAAPISupportedCodecs & CODEC_HW_AV1) {
704 media::MCSInfo::AddSupport(
705 media::MediaCodecsSupport::AV1HardwareDecode);
707 } else if (!strcmp(line, "WARNING") || !strcmp(line, "ERROR")) {
708 gfxCriticalNote << "vaapitest: " << line;
709 line = NS_strtok("\n", &bufptr);
710 if (line) {
711 gfxCriticalNote << "vaapitest: " << line << "\n";
713 return;
716 #endif
719 // Probe all V4L2 devices and check their capabilities
720 void GfxInfo::GetDataV4L2() {
721 if (mIsV4L2Supported.isSome()) {
722 // We have already probed v4l2 support, no need to do it again.
723 return;
725 mIsV4L2Supported = Some(false);
727 #ifdef MOZ_ENABLE_V4L2
728 DIR* dir = opendir("/dev");
729 if (!dir) {
730 gfxCriticalNote << "Could not list /dev\n";
731 return;
733 struct dirent* dir_entry;
734 while ((dir_entry = readdir(dir))) {
735 if (!strncmp(dir_entry->d_name, "video", 5)) {
736 nsCString path = "/dev/"_ns;
737 path += nsDependentCString(dir_entry->d_name);
738 V4L2ProbeDevice(path);
741 closedir(dir);
742 #endif // MOZ_ENABLE_V4L2
745 // Check the capabilities of a single V4L2 device. If the device doesn't work
746 // or doesn't support any codecs we recognise, then we just ignore it. If it
747 // does support recognised codecs then add these codecs to the supported list
748 // and mark V4L2 as supported: We only need a single working device to enable
749 // V4L2, when we come to decode FFmpeg will probe all the devices and choose
750 // the appropriate one.
751 void GfxInfo::V4L2ProbeDevice(nsCString& dev) {
752 char* v4l2Data = nullptr;
753 auto free = mozilla::MakeScopeExit([&] { g_free((void*)v4l2Data); });
755 int v4l2Pipe = -1;
756 int v4l2PID = 0;
757 const char* args[] = {"-d", dev.get(), nullptr};
758 v4l2PID = FireTestProcess(V4L2_PROBE_BINARY, &v4l2Pipe, args);
759 if (!v4l2PID) {
760 gfxCriticalNote << "Failed to start v4l2test process\n";
761 return;
764 if (!ManageChildProcess("v4l2test", &v4l2PID, &v4l2Pipe, V4L2_TEST_TIMEOUT,
765 &v4l2Data)) {
766 gfxCriticalNote << "v4l2test: ManageChildProcess failed\n";
767 return;
770 char* bufptr = v4l2Data;
771 char* line;
772 nsTArray<nsCString> capFormats;
773 nsTArray<nsCString> outFormats;
774 bool supported = false;
775 // Use gfxWarning rather than gfxCriticalNote from here on because the
776 // errors/warnings output by v4l2test are generally just caused by devices
777 // which aren't M2M decoders. Set gfx.logging.level=5 to see these messages.
779 while ((line = NS_strtok("\n", &bufptr))) {
780 if (!strcmp(line, "V4L2_SUPPORTED")) {
781 line = NS_strtok("\n", &bufptr);
782 if (!line) {
783 gfxWarning() << "v4l2test: Failed to get V4L2 support\n";
784 return;
786 supported = !strcmp(line, "TRUE");
787 } else if (!strcmp(line, "V4L2_CAPTURE_FMTS")) {
788 line = NS_strtok("\n", &bufptr);
789 if (!line) {
790 gfxWarning() << "v4l2test: Failed to get V4L2 CAPTURE formats\n";
791 return;
793 char* capture_fmt;
794 while ((capture_fmt = NS_strtok(" ", &line))) {
795 capFormats.AppendElement(capture_fmt);
797 } else if (!strcmp(line, "V4L2_OUTPUT_FMTS")) {
798 line = NS_strtok("\n", &bufptr);
799 if (!line) {
800 gfxWarning() << "v4l2test: Failed to get V4L2 OUTPUT formats\n";
801 return;
803 char* output_fmt;
804 while ((output_fmt = NS_strtok(" ", &line))) {
805 outFormats.AppendElement(output_fmt);
807 } else if (!strcmp(line, "WARNING") || !strcmp(line, "ERROR")) {
808 line = NS_strtok("\n", &bufptr);
809 if (line) {
810 gfxWarning() << "v4l2test: " << line << "\n";
812 return;
816 // If overall SUPPORTED flag is not TRUE then stop now
817 if (!supported) {
818 return;
821 // Currently the V4L2 decode platform only supports YUV420 and NV12
822 if (!capFormats.Contains("YV12") && !capFormats.Contains("NV12")) {
823 return;
826 // Supported codecs
827 if (outFormats.Contains("H264")) {
828 mIsV4L2Supported = Some(true);
829 media::MCSInfo::AddSupport(media::MediaCodecsSupport::H264HardwareDecode);
830 mV4L2SupportedCodecs |= CODEC_HW_H264;
834 const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() {
835 if (!sDriverInfo->Length()) {
836 // Mesa 10.0 provides the GLX_MESA_query_renderer extension, which allows us
837 // to query device IDs backing a GL context for blocklisting.
838 APPEND_TO_DRIVER_BLOCKLIST_EXT(
839 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
840 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
841 GfxDriverInfo::optionalFeatures,
842 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
843 V(10, 0, 0, 0), "FEATURE_FAILURE_OLD_MESA", "Mesa 10.0");
845 // NVIDIA Mesa baseline (see bug 1714391).
846 APPEND_TO_DRIVER_BLOCKLIST_EXT(
847 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
848 WindowProtocol::All, DriverVendor::MesaNouveau, DeviceFamily::All,
849 GfxDriverInfo::optionalFeatures,
850 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
851 V(11, 0, 0, 0), "FEATURE_FAILURE_OLD_NV_MESA", "Mesa 11.0");
853 // NVIDIA baseline (ported from old blocklist)
854 APPEND_TO_DRIVER_BLOCKLIST_EXT(
855 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
856 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
857 GfxDriverInfo::optionalFeatures,
858 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
859 V(257, 21, 0, 0), "FEATURE_FAILURE_OLD_NVIDIA", "NVIDIA 257.21");
861 // fglrx baseline (chosen arbitrarily as 2013-07-22 release).
862 APPEND_TO_DRIVER_BLOCKLIST(
863 OperatingSystem::Linux, DeviceFamily::AtiAll,
864 GfxDriverInfo::optionalFeatures,
865 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
866 V(13, 15, 100, 1), "FEATURE_FAILURE_OLD_FGLRX", "fglrx 13.15.100.1");
868 ////////////////////////////////////
869 // FEATURE_WEBRENDER
871 // All Mesa software drivers, they should get Software WebRender instead.
872 APPEND_TO_DRIVER_BLOCKLIST_EXT(
873 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
874 WindowProtocol::All, DriverVendor::SoftwareMesaAll, DeviceFamily::All,
875 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
876 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_SOFTWARE_GL",
877 "");
879 // Older generation Intel devices do not perform well with WebRender.
880 APPEND_TO_DRIVER_BLOCKLIST(
881 OperatingSystem::Linux, DeviceFamily::IntelWebRenderBlocked,
882 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
883 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "INTEL_DEVICE_GEN5_OR_OLDER",
884 "");
886 // Nvidia Mesa baseline, see bug 1563859.
887 APPEND_TO_DRIVER_BLOCKLIST_EXT(
888 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
889 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::NvidiaAll,
890 nsIGfxInfo::FEATURE_WEBRENDER,
891 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
892 V(18, 2, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 18.2.0.0");
894 // Disable on all older Nvidia drivers due to stability issues.
895 APPEND_TO_DRIVER_BLOCKLIST_EXT(
896 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
897 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
898 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
899 DRIVER_LESS_THAN, V(470, 82, 0, 0),
900 "FEATURE_FAILURE_WEBRENDER_OLD_NVIDIA", "470.82.0");
902 // Older generation NVIDIA devices do not perform well with WebRender.
903 APPEND_TO_DRIVER_BLOCKLIST(
904 OperatingSystem::Linux, DeviceFamily::NvidiaWebRenderBlocked,
905 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
906 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
907 "NVIDIA_EARLY_TESLA_AND_C67_C68", "");
909 // Mesa baseline, chosen arbitrarily. Linux users are generally good about
910 // updating their Mesa libraries so we don't want to arbitarily support
911 // WebRender on old drivers with outstanding bugs to work around.
912 APPEND_TO_DRIVER_BLOCKLIST_EXT(
913 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
914 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
915 nsIGfxInfo::FEATURE_WEBRENDER,
916 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
917 V(17, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 17.0.0.0");
919 // Mesa baseline for non-Intel/NVIDIA/ATI devices. These other devices will
920 // often have less mature drivers so let's block older Mesa versions.
921 APPEND_TO_DRIVER_BLOCKLIST_EXT(
922 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
923 WindowProtocol::All, DriverVendor::MesaNonIntelNvidiaAtiAll,
924 DeviceFamily::All, nsIGfxInfo::FEATURE_WEBRENDER,
925 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
926 V(22, 2, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA_OTHER",
927 "Mesa 22.2.0.0");
929 // Bug 1690568 / Bug 1393793 - Require Mesa 17.3.0+ for devices using the
930 // AMD r600 driver to avoid shader compilation issues.
931 APPEND_TO_DRIVER_BLOCKLIST_EXT(
932 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
933 WindowProtocol::All, DriverVendor::MesaR600, DeviceFamily::All,
934 nsIGfxInfo::FEATURE_WEBRENDER,
935 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
936 V(17, 3, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA_R600",
937 "Mesa 17.3.0.0");
939 // Disable on all ATI devices not using Mesa for now.
940 APPEND_TO_DRIVER_BLOCKLIST_EXT(
941 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
942 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::AtiAll,
943 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
944 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
945 "FEATURE_FAILURE_WEBRENDER_NO_LINUX_ATI", "");
947 // Disable R600 GPUs with Mesa drivers.
948 // Bug 1673939 - Garbled text on RS880 GPUs with Mesa drivers.
949 APPEND_TO_DRIVER_BLOCKLIST_EXT(
950 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
951 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AmdR600,
952 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
953 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
954 "FEATURE_FAILURE_WEBRENDER_BUG_1673939",
955 "https://gitlab.freedesktop.org/mesa/mesa/-/issues/3720");
957 // Bug 1635186 - Poor performance with video playing in a background window
958 // on XWayland. Keep in sync with FEATURE_X11_EGL below to only enable them
959 // together by default. Only Mesa and Nvidia binary drivers are expected
960 // on Wayland right now.
961 APPEND_TO_DRIVER_BLOCKLIST_EXT(
962 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
963 WindowProtocol::XWayland, DriverVendor::MesaAll, DeviceFamily::All,
964 nsIGfxInfo::FEATURE_WEBRENDER,
965 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
966 V(17, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_BUG_1635186",
967 "Mesa 17.0.0.0");
969 // Bug 1815481 - Disable mesa drivers in virtual machines.
970 APPEND_TO_DRIVER_BLOCKLIST_EXT(
971 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
972 WindowProtocol::All, DriverVendor::MesaVM, DeviceFamily::All,
973 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
974 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
975 "FEATURE_FAILURE_WEBRENDER_MESA_VM", "");
976 // Disable hardware mesa drivers in virtual machines due to instability.
977 APPEND_TO_DRIVER_BLOCKLIST_EXT(
978 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
979 WindowProtocol::All, DriverVendor::MesaVM, DeviceFamily::All,
980 nsIGfxInfo::FEATURE_WEBGL_USE_HARDWARE,
981 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
982 V(0, 0, 0, 0), "FEATURE_FAILURE_WEBGL_MESA_VM", "");
984 ////////////////////////////////////
985 // FEATURE_WEBRENDER_COMPOSITOR
986 APPEND_TO_DRIVER_BLOCKLIST(
987 OperatingSystem::Linux, DeviceFamily::All,
988 nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
989 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
990 V(0, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_COMPOSITOR_DISABLED", "");
992 ////////////////////////////////////
993 // FEATURE_X11_EGL
994 APPEND_TO_DRIVER_BLOCKLIST_EXT(
995 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
996 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
997 nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
998 DRIVER_LESS_THAN, V(17, 0, 0, 0), "FEATURE_X11_EGL_OLD_MESA",
999 "Mesa 17.0.0.0");
1001 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1002 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1003 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::NvidiaAll,
1004 nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
1005 DRIVER_LESS_THAN, V(18, 2, 0, 0), "FEATURE_X11_EGL_OLD_MESA_NOUVEAU",
1006 "Mesa 18.2.0.0");
1008 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1009 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1010 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
1011 nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
1012 DRIVER_LESS_THAN, V(470, 82, 0, 0),
1013 "FEATURE_ROLLOUT_X11_EGL_NVIDIA_BINARY", "470.82.0");
1015 // Disable on all AMD devices not using Mesa.
1016 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1017 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1018 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::AtiAll,
1019 nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1020 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
1021 "FEATURE_FAILURE_X11_EGL_NO_LINUX_ATI", "");
1023 ////////////////////////////////////
1024 // FEATURE_DMABUF
1025 // Disabled due to high volume crash tracked in bug 1788573, fixed in the
1026 // 545 driver.
1027 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1028 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1029 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
1030 nsIGfxInfo::FEATURE_DMABUF, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1031 DRIVER_LESS_THAN, V(545, 23, 6, 0), "FEATURE_FAILURE_BUG_1788573", "");
1033 // Disabled due to high volume crash tracked in bug 1913778. It appears that
1034 // only this version of the driver is affected.
1035 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1036 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1037 WindowProtocol::All, DriverVendor::MesaRadeonsi, DeviceFamily::AtiAll,
1038 nsIGfxInfo::FEATURE_DMABUF, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
1039 DRIVER_EQUAL, V(24, 1, 3, 0), "FEATURE_FAILURE_BUG_1913778", "");
1041 ////////////////////////////////////
1042 // FEATURE_DMABUF_SURFACE_EXPORT
1043 // Disabled on all Mesa drivers due to various issue, among them:
1044 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6666
1045 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6796
1046 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6688
1047 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6988
1048 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1049 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1050 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
1051 nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT,
1052 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
1053 V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", "");
1055 ////////////////////////////////////
1056 // FEATURE_DMABUF_WEBGL
1057 // Disabled due to DMABuf rendering/correctness with WebGL on Nvidia driver,
1058 // tracked in bug 1924578.
1059 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1060 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1061 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
1062 nsIGfxInfo::FEATURE_DMABUF_WEBGL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1063 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_BUG_1924578",
1064 "");
1066 ////////////////////////////////////
1067 // FEATURE_HARDWARE_VIDEO_DECODING
1068 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1069 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1070 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
1071 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1072 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
1073 V(21, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_MESA",
1074 "Mesa 21.0.0.0");
1076 // Disable on all NVIDIA hardware
1077 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1078 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1079 WindowProtocol::All, DriverVendor::All, DeviceFamily::NvidiaAll,
1080 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1081 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
1082 V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_NO_LINUX_NVIDIA", "");
1084 // Disable on all AMD devices not using Mesa.
1085 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1086 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1087 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::AtiAll,
1088 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1089 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
1090 V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_NO_LINUX_AMD", "");
1092 // Disable on r600 driver due to decoding artifacts (Bug 1824307)
1093 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1094 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1095 WindowProtocol::All, DriverVendor::MesaR600, DeviceFamily::All,
1096 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1097 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
1098 V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_NO_R600", "");
1100 // Disable on AMD devices using broken Mesa (Bug 1832080).
1101 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1102 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1103 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiAll,
1104 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1105 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_LESS_THAN, V(23, 1, 1, 0),
1106 "FEATURE_HARDWARE_VIDEO_DECODING_AMD_DISABLE", "Mesa 23.1.1.0");
1108 // Disable on Release/late Beta on AMD
1109 #if !defined(EARLY_BETA_OR_EARLIER)
1110 APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Linux, DeviceFamily::AtiAll,
1111 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1112 nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1113 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
1114 "FEATURE_HARDWARE_VIDEO_DECODING_DISABLE", "");
1115 #endif
1116 ////////////////////////////////////
1117 // FEATURE_HW_DECODED_VIDEO_ZERO_COPY - ALLOWLIST
1118 APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Linux, DeviceFamily::All,
1119 nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY,
1120 nsIGfxInfo::FEATURE_ALLOW_ALWAYS,
1121 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
1122 "FEATURE_ROLLOUT_ALL");
1124 // Disable on AMD devices using broken Mesa (Bug 1837138).
1125 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1126 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1127 WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiAll,
1128 nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY,
1129 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_LESS_THAN, V(23, 1, 1, 0),
1130 "FEATURE_HARDWARE_VIDEO_ZERO_COPY_LINUX_AMD_DISABLE", "Mesa 23.1.1.0");
1132 ////////////////////////////////////
1133 // FEATURE_WEBRENDER_PARTIAL_PRESENT
1134 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1135 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1136 WindowProtocol::X11, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
1137 nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
1138 nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
1139 V(0, 0, 0, 0), "FEATURE_ROLLOUT_WR_PARTIAL_PRESENT_NVIDIA_BINARY", "");
1141 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1142 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1143 WindowProtocol::XWayland, DriverVendor::MesaAll, DeviceFamily::All,
1144 nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
1145 nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
1146 V(21, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_PARTIAL_PRESENT_BUG_1677892",
1147 "Mesa 21.0.0.0");
1149 ////////////////////////////////////
1151 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1152 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1153 WindowProtocol::All, DriverVendor::MesaNouveau, DeviceFamily::All,
1154 nsIGfxInfo::FEATURE_THREADSAFE_GL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1155 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
1156 "FEATURE_FAILURE_THREADSAFE_GL_NOUVEAU", "");
1158 // Disabled due to high volume crash tracked in bug 1788573, fixed in the
1159 // 545 driver.
1160 APPEND_TO_DRIVER_BLOCKLIST_EXT(
1161 OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
1162 WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
1163 nsIGfxInfo::FEATURE_THREADSAFE_GL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1164 DRIVER_LESS_THAN, V(545, 23, 6, 0), "FEATURE_FAILURE_BUG_1788573", "");
1166 // AMD R600 family does not perform well with WebRender.
1167 APPEND_TO_DRIVER_BLOCKLIST(
1168 OperatingSystem::Linux, DeviceFamily::AmdR600,
1169 nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
1170 DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "AMD_R600_FAMILY", "");
1172 return *sDriverInfo;
1175 bool GfxInfo::DoesWindowProtocolMatch(const nsAString& aBlocklistWindowProtocol,
1176 const nsAString& aWindowProtocol) {
1177 if (mIsWayland &&
1178 aBlocklistWindowProtocol.Equals(
1179 GfxDriverInfo::GetWindowProtocol(WindowProtocol::WaylandAll),
1180 nsCaseInsensitiveStringComparator)) {
1181 return true;
1183 if (!mIsWayland &&
1184 aBlocklistWindowProtocol.Equals(
1185 GfxDriverInfo::GetWindowProtocol(WindowProtocol::X11All),
1186 nsCaseInsensitiveStringComparator)) {
1187 return true;
1189 return GfxInfoBase::DoesWindowProtocolMatch(aBlocklistWindowProtocol,
1190 aWindowProtocol);
1193 bool GfxInfo::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
1194 const nsAString& aDriverVendor) {
1195 if (mIsMesa) {
1196 if (aBlocklistVendor.Equals(
1197 GfxDriverInfo::GetDriverVendor(DriverVendor::MesaAll),
1198 nsCaseInsensitiveStringComparator)) {
1199 return true;
1201 if (mIsAccelerated &&
1202 aBlocklistVendor.Equals(
1203 GfxDriverInfo::GetDriverVendor(DriverVendor::HardwareMesaAll),
1204 nsCaseInsensitiveStringComparator)) {
1205 return true;
1207 if (!mIsAccelerated &&
1208 aBlocklistVendor.Equals(
1209 GfxDriverInfo::GetDriverVendor(DriverVendor::SoftwareMesaAll),
1210 nsCaseInsensitiveStringComparator)) {
1211 return true;
1213 if (aBlocklistVendor.Equals(GfxDriverInfo::GetDriverVendor(
1214 DriverVendor::MesaNonIntelNvidiaAtiAll),
1215 nsCaseInsensitiveStringComparator)) {
1216 return !mVendorId.Equals("0x8086") && !mVendorId.Equals("0x10de") &&
1217 !mVendorId.Equals("0x1002");
1220 if (!mIsMesa && aBlocklistVendor.Equals(
1221 GfxDriverInfo::GetDriverVendor(DriverVendor::NonMesaAll),
1222 nsCaseInsensitiveStringComparator)) {
1223 return true;
1225 return GfxInfoBase::DoesDriverVendorMatch(aBlocklistVendor, aDriverVendor);
1228 nsresult GfxInfo::GetFeatureStatusImpl(
1229 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedDriverVersion,
1230 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
1231 OperatingSystem* aOS /* = nullptr */)
1234 NS_ENSURE_ARG_POINTER(aStatus);
1235 *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
1236 aSuggestedDriverVersion.SetIsVoid(true);
1237 OperatingSystem os = OperatingSystem::Linux;
1238 if (aOS) *aOS = os;
1240 if (sShutdownOccurred) {
1241 return NS_OK;
1244 GetData();
1246 if (mGlxTestError) {
1247 // If glxtest failed, block most features by default.
1248 if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
1249 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1250 aFailureId = "FEATURE_FAILURE_GLXTEST_FAILED";
1251 } else {
1252 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
1254 return NS_OK;
1257 if (mGLMajorVersion == 1) {
1258 // We're on OpenGL 1. In most cases that indicates really old hardware.
1259 // We better block them, rather than rely on them to fail gracefully,
1260 // because they don't! see bug 696636
1261 if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
1262 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1263 aFailureId = "FEATURE_FAILURE_OPENGL_1";
1264 } else {
1265 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
1267 return NS_OK;
1270 // Blocklist software GL implementations from using layers acceleration.
1271 // On the test infrastructure, we'll force-enable layers acceleration.
1272 if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS && !mIsAccelerated &&
1273 !PR_GetEnv("MOZ_LAYERS_ALLOW_SOFTWARE_GL")) {
1274 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1275 aFailureId = "FEATURE_FAILURE_SOFTWARE_GL";
1276 return NS_OK;
1279 if (aFeature == nsIGfxInfo::FEATURE_WEBRENDER) {
1280 // Don't try Webrender on devices where we are guaranteed to fail.
1281 if (mGLMajorVersion < 3) {
1282 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1283 aFailureId = "FEATURE_FAILURE_OPENGL_LESS_THAN_3";
1284 return NS_OK;
1287 // Bug 1710400: Disable Webrender on the deprecated Intel DDX driver
1288 for (const nsCString& driver : mDdxDrivers) {
1289 if (strcasestr(driver.get(), "Intel")) {
1290 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1291 aFailureId = "FEATURE_FAILURE_DDX_INTEL";
1292 return NS_OK;
1297 const struct {
1298 int32_t mFeature;
1299 int32_t mCodec;
1300 } kFeatureToCodecs[] = {{nsIGfxInfo::FEATURE_H264_HW_DECODE, CODEC_HW_H264},
1301 {nsIGfxInfo::FEATURE_VP8_HW_DECODE, CODEC_HW_VP8},
1302 {nsIGfxInfo::FEATURE_VP9_HW_DECODE, CODEC_HW_VP9},
1303 {nsIGfxInfo::FEATURE_AV1_HW_DECODE, CODEC_HW_AV1}};
1305 for (const auto& pair : kFeatureToCodecs) {
1306 if (aFeature != pair.mFeature) {
1307 continue;
1309 if ((mVAAPISupportedCodecs & pair.mCodec) ||
1310 (mV4L2SupportedCodecs & pair.mCodec)) {
1311 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
1312 } else {
1313 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST;
1314 aFailureId = "FEATURE_FAILURE_VIDEO_DECODING_MISSING";
1316 return NS_OK;
1319 auto ret = GfxInfoBase::GetFeatureStatusImpl(
1320 aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
1322 // Probe VA-API/V4L2 on supported devices only
1323 if (aFeature == nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING) {
1324 if (!StaticPrefs::media_hardware_video_decoding_enabled_AtStartup()) {
1325 return ret;
1327 bool probeHWDecode =
1328 mIsAccelerated &&
1329 (*aStatus == nsIGfxInfo::FEATURE_STATUS_OK ||
1330 StaticPrefs::media_hardware_video_decoding_force_enabled_AtStartup() ||
1331 StaticPrefs::media_ffmpeg_vaapi_enabled_AtStartup());
1332 if (probeHWDecode) {
1333 GetDataVAAPI();
1334 GetDataV4L2();
1335 } else {
1336 mIsVAAPISupported = Some(false);
1337 mIsV4L2Supported = Some(false);
1339 if (!mIsVAAPISupported.value() && !mIsV4L2Supported.value()) {
1340 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST;
1341 aFailureId = "FEATURE_FAILURE_VIDEO_DECODING_TEST_FAILED";
1345 return ret;
1348 NS_IMETHODIMP
1349 GfxInfo::GetD2DEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; }
1351 NS_IMETHODIMP
1352 GfxInfo::GetDWriteEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; }
1354 NS_IMETHODIMP
1355 GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) {
1356 return NS_ERROR_FAILURE;
1359 NS_IMETHODIMP GfxInfo::GetHasBattery(bool* aHasBattery) {
1360 return NS_ERROR_NOT_IMPLEMENTED;
1363 NS_IMETHODIMP
1364 GfxInfo::GetEmbeddedInFirefoxReality(bool* aEmbeddedInFirefoxReality) {
1365 return NS_ERROR_FAILURE;
1368 NS_IMETHODIMP
1369 GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) {
1370 return NS_ERROR_FAILURE;
1373 NS_IMETHODIMP
1374 GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) {
1375 GetData();
1376 if (mIsWayland) {
1377 aWindowProtocol = GfxDriverInfo::GetWindowProtocol(WindowProtocol::Wayland);
1378 } else if (mIsXWayland) {
1379 aWindowProtocol =
1380 GfxDriverInfo::GetWindowProtocol(WindowProtocol::XWayland);
1381 } else {
1382 aWindowProtocol = GfxDriverInfo::GetWindowProtocol(WindowProtocol::X11);
1384 glean::gfx::linux_window_protocol.Set(NS_ConvertUTF16toUTF8(aWindowProtocol));
1385 return NS_OK;
1388 NS_IMETHODIMP
1389 GfxInfo::GetTestType(nsAString& aTestType) {
1390 GetData();
1391 AppendASCIItoUTF16(mTestType, aTestType);
1392 return NS_OK;
1395 NS_IMETHODIMP
1396 GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {
1397 GetData();
1398 AppendASCIItoUTF16(mAdapterDescription, aAdapterDescription);
1399 return NS_OK;
1402 NS_IMETHODIMP
1403 GfxInfo::GetAdapterDescription2(nsAString& aAdapterDescription) {
1404 return NS_ERROR_FAILURE;
1407 NS_IMETHODIMP
1408 GfxInfo::GetAdapterRAM(uint32_t* aAdapterRAM) {
1409 GetData();
1410 *aAdapterRAM = mAdapterRAM;
1411 return NS_OK;
1414 NS_IMETHODIMP
1415 GfxInfo::GetAdapterRAM2(uint32_t* aAdapterRAM) { return NS_ERROR_FAILURE; }
1417 NS_IMETHODIMP
1418 GfxInfo::GetAdapterDriver(nsAString& aAdapterDriver) {
1419 aAdapterDriver.Truncate();
1420 return NS_OK;
1423 NS_IMETHODIMP
1424 GfxInfo::GetAdapterDriver2(nsAString& aAdapterDriver) {
1425 return NS_ERROR_FAILURE;
1428 NS_IMETHODIMP
1429 GfxInfo::GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) {
1430 GetData();
1431 CopyASCIItoUTF16(mDriverVendor, aAdapterDriverVendor);
1432 return NS_OK;
1435 NS_IMETHODIMP
1436 GfxInfo::GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) {
1437 return NS_ERROR_FAILURE;
1440 NS_IMETHODIMP
1441 GfxInfo::GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) {
1442 GetData();
1443 CopyASCIItoUTF16(mDriverVersion, aAdapterDriverVersion);
1444 return NS_OK;
1447 NS_IMETHODIMP
1448 GfxInfo::GetAdapterDriverVersion2(nsAString& aAdapterDriverVersion) {
1449 return NS_ERROR_FAILURE;
1452 NS_IMETHODIMP
1453 GfxInfo::GetAdapterDriverDate(nsAString& aAdapterDriverDate) {
1454 aAdapterDriverDate.Truncate();
1455 return NS_OK;
1458 NS_IMETHODIMP
1459 GfxInfo::GetAdapterDriverDate2(nsAString& aAdapterDriverDate) {
1460 return NS_ERROR_FAILURE;
1463 NS_IMETHODIMP
1464 GfxInfo::GetAdapterVendorID(nsAString& aAdapterVendorID) {
1465 GetData();
1466 CopyUTF8toUTF16(mVendorId, aAdapterVendorID);
1467 return NS_OK;
1470 NS_IMETHODIMP
1471 GfxInfo::GetAdapterVendorID2(nsAString& aAdapterVendorID) {
1472 GetData();
1473 CopyUTF8toUTF16(mSecondaryVendorId, aAdapterVendorID);
1474 return NS_OK;
1477 NS_IMETHODIMP
1478 GfxInfo::GetAdapterDeviceID(nsAString& aAdapterDeviceID) {
1479 GetData();
1480 CopyUTF8toUTF16(mDeviceId, aAdapterDeviceID);
1481 return NS_OK;
1484 NS_IMETHODIMP
1485 GfxInfo::GetAdapterDeviceID2(nsAString& aAdapterDeviceID) {
1486 GetData();
1487 CopyUTF8toUTF16(mSecondaryDeviceId, aAdapterDeviceID);
1488 return NS_OK;
1491 NS_IMETHODIMP
1492 GfxInfo::GetAdapterSubsysID(nsAString& aAdapterSubsysID) {
1493 return NS_ERROR_FAILURE;
1496 NS_IMETHODIMP
1497 GfxInfo::GetAdapterSubsysID2(nsAString& aAdapterSubsysID) {
1498 return NS_ERROR_FAILURE;
1501 NS_IMETHODIMP
1502 GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) {
1503 // This is never the case, as the active GPU should be the primary GPU.
1504 *aIsGPU2Active = false;
1505 return NS_OK;
1508 NS_IMETHODIMP
1509 GfxInfo::GetDrmRenderDevice(nsACString& aDrmRenderDevice) {
1510 GetData();
1511 aDrmRenderDevice.Assign(mDrmRenderDevice);
1512 return NS_OK;
1515 #ifdef DEBUG
1517 // Implement nsIGfxInfoDebug
1518 // We don't support spoofing anything on Linux
1520 NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString& aVendorID) {
1521 GetData();
1522 CopyUTF16toUTF8(aVendorID, mVendorId);
1523 mIsAccelerated = true;
1524 return NS_OK;
1527 NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString& aDeviceID) {
1528 GetData();
1529 CopyUTF16toUTF8(aDeviceID, mDeviceId);
1530 return NS_OK;
1533 NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString& aDriverVersion) {
1534 GetData();
1535 CopyUTF16toUTF8(aDriverVersion, mDriverVersion);
1536 return NS_OK;
1539 NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) {
1540 // We don't support OS versioning on Linux. There's just "Linux".
1541 return NS_OK;
1544 #endif
1546 } // namespace mozilla::widget