Bug 1942006 - Upstream a variety of Servo-specific code from Servo's downstream fork...
[gecko.git] / widget / GfxInfoBase.cpp
blob0b7d81e45e47beb13de2daf6e7ec6a46daa4e0c5
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 "nsIPropertyBag2.h"
20 #include "nsString.h"
21 #include "nsUnicharUtils.h"
22 #include "nsVersionComparator.h"
23 #include "mozilla/Services.h"
24 #include "mozilla/Observer.h"
25 #include "nsIObserver.h"
26 #include "nsIObserverService.h"
27 #include "nsTArray.h"
28 #include "nsXULAppAPI.h"
29 #include "nsIXULAppInfo.h"
30 #include "mozilla/BinarySearch.h"
31 #include "mozilla/ClearOnShutdown.h"
32 #include "mozilla/LookAndFeel.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/StaticPrefs_gfx.h"
35 #include "mozilla/gfx/2D.h"
36 #include "mozilla/gfx/BuildConstants.h"
37 #include "mozilla/gfx/GPUProcessManager.h"
38 #include "mozilla/gfx/Logging.h"
39 #include "mozilla/gfx/gfxVars.h"
40 #include "mozilla/widget/ScreenManager.h"
41 #include "mozilla/widget/Screen.h"
43 #include "jsapi.h"
45 #include "gfxPlatform.h"
46 #include "gfxConfig.h"
47 #include "DriverCrashGuard.h"
49 #ifdef MOZ_WIDGET_ANDROID
50 # include <set>
51 # include "AndroidBuild.h"
52 #endif
54 #if defined(XP_MACOSX)
55 # include "nsCocoaFeatures.h"
56 #endif
58 using namespace mozilla::widget;
59 using namespace mozilla;
60 using mozilla::MutexAutoLock;
62 nsTArray<GfxDriverInfo>* GfxInfoBase::sDriverInfo;
63 StaticAutoPtr<nsTArray<gfx::GfxInfoFeatureStatus>> GfxInfoBase::sFeatureStatus;
64 bool GfxInfoBase::sDriverInfoObserverInitialized;
65 bool GfxInfoBase::sShutdownOccurred;
67 // Call this when setting sFeatureStatus to a non-null pointer to
68 // ensure destruction even if the GfxInfo component is never instantiated.
69 static void InitFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>* aPtr) {
70 static std::once_flag sOnce;
71 std::call_once(sOnce, [] { ClearOnShutdown(&GfxInfoBase::sFeatureStatus); });
72 GfxInfoBase::sFeatureStatus = aPtr;
75 // Observes for shutdown so that the child GfxDriverInfo list is freed.
76 class ShutdownObserver : public nsIObserver {
77 virtual ~ShutdownObserver() = default;
79 public:
80 ShutdownObserver() = default;
82 NS_DECL_ISUPPORTS
84 NS_IMETHOD Observe(nsISupports* subject, const char* aTopic,
85 const char16_t* aData) override {
86 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
88 delete GfxInfoBase::sDriverInfo;
89 GfxInfoBase::sDriverInfo = nullptr;
91 for (auto& deviceFamily : GfxDriverInfo::sDeviceFamilies) {
92 delete deviceFamily;
93 deviceFamily = nullptr;
96 for (auto& windowProtocol : GfxDriverInfo::sWindowProtocol) {
97 delete windowProtocol;
98 windowProtocol = nullptr;
101 for (auto& deviceVendor : GfxDriverInfo::sDeviceVendors) {
102 delete deviceVendor;
103 deviceVendor = nullptr;
106 for (auto& driverVendor : GfxDriverInfo::sDriverVendors) {
107 delete driverVendor;
108 driverVendor = nullptr;
111 GfxInfoBase::sShutdownOccurred = true;
113 return NS_OK;
117 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
119 static void InitGfxDriverInfoShutdownObserver() {
120 if (GfxInfoBase::sDriverInfoObserverInitialized) return;
122 GfxInfoBase::sDriverInfoObserverInitialized = true;
124 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
125 if (!observerService) {
126 NS_WARNING("Could not get observer service!");
127 return;
130 ShutdownObserver* obs = new ShutdownObserver();
131 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
134 using namespace mozilla::widget;
135 using namespace mozilla::gfx;
136 using namespace mozilla;
138 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver,
139 nsISupportsWeakReference)
141 #define BLOCKLIST_PREF_BRANCH "gfx.blacklist."
142 #define SUGGESTED_VERSION_PREF BLOCKLIST_PREF_BRANCH "suggested-driver-version"
144 static const char* GetPrefNameForFeature(int32_t aFeature) {
145 const char* fullpref = nullptr;
146 switch (aFeature) {
147 #define GFXINFO_FEATURE(id, name, pref) \
148 case nsIGfxInfo::FEATURE_##id: \
149 fullpref = BLOCKLIST_PREF_BRANCH pref; \
150 break;
151 #include "mozilla/widget/GfxInfoFeatureDefs.h"
152 #undef GFXINFO_FEATURE
153 default:
154 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
155 break;
158 return fullpref;
161 // Returns the value of the pref for the relevant feature in aValue.
162 // If the pref doesn't exist, aValue is not touched, and returns false.
163 static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
164 nsACString& aFailureId) {
165 const char* prefname = GetPrefNameForFeature(aFeature);
166 if (!prefname) return false;
168 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
169 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
170 return false;
173 if (aValue == nsIGfxInfo::FEATURE_DENIED) {
174 // We should never see the DENIED status with the downloadable blocklist.
175 return false;
178 nsCString failureprefname(prefname);
179 failureprefname += ".failureid";
180 nsAutoCString failureValue;
181 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
182 if (NS_SUCCEEDED(rv)) {
183 aFailureId = failureValue.get();
184 } else {
185 aFailureId = "FEATURE_FAILURE_BLOCKLIST_PREF";
188 return true;
191 static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
192 const nsACString& aFailureId) {
193 const char* prefname = GetPrefNameForFeature(aFeature);
194 if (!prefname) return;
195 if (XRE_IsParentProcess()) {
196 GfxInfoBase::sFeatureStatus = nullptr;
199 Preferences::SetInt(prefname, aValue);
200 if (!aFailureId.IsEmpty()) {
201 nsAutoCString failureprefname(prefname);
202 failureprefname += ".failureid";
203 Preferences::SetCString(failureprefname.get(), aFailureId);
207 static void RemovePrefForFeature(int32_t aFeature) {
208 const char* prefname = GetPrefNameForFeature(aFeature);
209 if (!prefname) return;
211 if (XRE_IsParentProcess()) {
212 GfxInfoBase::sFeatureStatus = nullptr;
214 Preferences::ClearUser(prefname);
217 static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
218 return NS_SUCCEEDED(
219 Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
222 static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
223 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
226 static void RemovePrefForDriverVersion() {
227 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
230 static OperatingSystem BlocklistOSToOperatingSystem(const nsAString& os) {
231 #define GFXINFO_OS(id, name) \
232 if (os.Equals(u##name##_ns)) { \
233 return OperatingSystem::id; \
235 #include "mozilla/widget/GfxInfoOperatingSystemDefs.h"
236 #undef GFXINFO_OS
237 return OperatingSystem::Unknown;
240 static GfxDeviceFamily* BlocklistDevicesToDeviceFamily(
241 nsTArray<nsCString>& devices) {
242 if (devices.Length() == 0) return nullptr;
244 // For each device, get its device ID, and return a freshly-allocated
245 // GfxDeviceFamily with the contents of that array.
246 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
248 for (uint32_t i = 0; i < devices.Length(); ++i) {
249 // We make sure we don't add any "empty" device entries to the array, so
250 // we don't need to check if devices[i] is empty.
251 deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
254 return deviceIds;
257 static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) {
258 MOZ_ASSERT(!aFeature.IsEmpty());
259 #define GFXINFO_FEATURE(id, name, pref) \
260 if (aFeature.Equals(u##name##_ns)) { \
261 return nsIGfxInfo::FEATURE_##id; \
263 #include "mozilla/widget/GfxInfoFeatureDefs.h"
264 #undef GFXINFO_FEATURE
266 // If we don't recognize the feature, it may be new, and something
267 // this version doesn't understand. So, nothing to do. This is
268 // different from feature not being specified at all, in which case
269 // this method should not get called and we should continue with the
270 // "optional features" blocklisting.
271 return nsIGfxInfo::FEATURE_INVALID;
274 static int32_t BlocklistFeatureStatusToGfxFeatureStatus(
275 const nsAString& aStatus) {
276 #define GFXINFO_FEATURE_STATUS(id) \
277 if (aStatus.Equals(u## #id##_ns)) { \
278 return nsIGfxInfo::FEATURE_##id; \
280 #include "mozilla/widget/GfxInfoFeatureStatusDefs.h"
281 #undef GFXINFO_FEATURE_STATUS
282 return nsIGfxInfo::FEATURE_STATUS_OK;
285 static void GfxFeatureStatusToBlocklistFeatureStatus(int32_t aStatus,
286 nsAString& aStatusOut) {
287 switch (aStatus) {
288 #define GFXINFO_FEATURE_STATUS(id) \
289 case nsIGfxInfo::FEATURE_##id: \
290 aStatusOut.Assign(u## #id##_ns); \
291 break;
292 #include "mozilla/widget/GfxInfoFeatureStatusDefs.h"
293 #undef GFXINFO_FEATURE
294 default:
295 MOZ_ASSERT_UNREACHABLE("Unexpected feature status!");
296 break;
300 static VersionComparisonOp BlocklistComparatorToComparisonOp(
301 const nsAString& op) {
302 #define GFXINFO_DRIVER_VERSION_CMP(id) \
303 if (op.Equals(u## #id##_ns)) { \
304 return DRIVER_##id; \
306 #include "mozilla/widget/GfxInfoDriverVersionCmpDefs.h"
307 #undef GFXINFO_DRIVER_VERSION_CMP
309 // The default is to ignore it.
310 return DRIVER_COMPARISON_IGNORED;
314 Deserialize Blocklist entries from string.
315 e.g:
316 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
318 static bool BlocklistEntryToDriverInfo(const nsACString& aBlocklistEntry,
319 GfxDriverInfo& aDriverInfo) {
320 // If we get an application version to be zero, something is not working
321 // and we are not going to bother checking the blocklist versions.
322 // See TestGfxWidgets.cpp for how version comparison works.
323 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
324 static mozilla::Version zeroV("0");
325 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
326 if (appV <= zeroV) {
327 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
328 << "Invalid application version "
329 << GfxInfoBase::GetApplicationVersion().get();
332 aDriverInfo.mRuleId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID"_ns;
334 for (const auto& keyValue : aBlocklistEntry.Split('\t')) {
335 nsTArray<nsCString> splitted;
336 ParseString(keyValue, ':', splitted);
337 if (splitted.Length() != 2) {
338 // If we don't recognize the input data, we do not want to proceed.
339 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
340 << "Unrecognized data " << nsCString(keyValue).get();
341 return false;
343 const nsCString& key = splitted[0];
344 const nsCString& value = splitted[1];
345 NS_ConvertUTF8toUTF16 dataValue(value);
347 if (value.Length() == 0) {
348 // Safety check for empty values.
349 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
350 << "Empty value for " << key.get();
351 return false;
354 if (key.EqualsLiteral("blockID")) {
355 nsCString blockIdStr = "FEATURE_FAILURE_DL_BLOCKLIST_"_ns + value;
356 aDriverInfo.mRuleId = blockIdStr.get();
357 } else if (key.EqualsLiteral("os")) {
358 aDriverInfo.mOperatingSystem = BlocklistOSToOperatingSystem(dataValue);
359 } else if (key.EqualsLiteral("osversion")) {
360 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
361 } else if (key.EqualsLiteral("windowProtocol")) {
362 aDriverInfo.mWindowProtocol = dataValue;
363 } else if (key.EqualsLiteral("vendor")) {
364 aDriverInfo.mAdapterVendor = dataValue;
365 } else if (key.EqualsLiteral("driverVendor")) {
366 aDriverInfo.mDriverVendor = dataValue;
367 } else if (key.EqualsLiteral("feature")) {
368 aDriverInfo.mFeature = BlocklistFeatureToGfxFeature(dataValue);
369 if (aDriverInfo.mFeature == nsIGfxInfo::FEATURE_INVALID) {
370 // If we don't recognize the feature, we do not want to proceed.
371 gfxWarning() << "Unrecognized feature " << value.get();
372 return false;
374 } else if (key.EqualsLiteral("featureStatus")) {
375 aDriverInfo.mFeatureStatus =
376 BlocklistFeatureStatusToGfxFeatureStatus(dataValue);
377 } else if (key.EqualsLiteral("driverVersion")) {
378 uint64_t version;
379 if (ParseDriverVersion(dataValue, &version))
380 aDriverInfo.mDriverVersion = version;
381 } else if (key.EqualsLiteral("driverVersionMax")) {
382 uint64_t version;
383 if (ParseDriverVersion(dataValue, &version))
384 aDriverInfo.mDriverVersionMax = version;
385 } else if (key.EqualsLiteral("driverVersionComparator")) {
386 aDriverInfo.mComparisonOp = BlocklistComparatorToComparisonOp(dataValue);
387 } else if (key.EqualsLiteral("model")) {
388 aDriverInfo.mModel = dataValue;
389 } else if (key.EqualsLiteral("product")) {
390 aDriverInfo.mProduct = dataValue;
391 } else if (key.EqualsLiteral("manufacturer")) {
392 aDriverInfo.mManufacturer = dataValue;
393 } else if (key.EqualsLiteral("hardware")) {
394 aDriverInfo.mHardware = dataValue;
395 } else if (key.EqualsLiteral("versionRange")) {
396 nsTArray<nsCString> versionRange;
397 ParseString(value, ',', versionRange);
398 if (versionRange.Length() != 2) {
399 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
400 << "Unrecognized versionRange " << value.get();
401 return false;
403 const nsCString& minValue = versionRange[0];
404 const nsCString& maxValue = versionRange[1];
406 mozilla::Version minV(minValue.get());
407 mozilla::Version maxV(maxValue.get());
409 if (minV > zeroV && !(appV >= minV)) {
410 // The version of the application is less than the minimal version
411 // this blocklist entry applies to, so we can just ignore it by
412 // returning false and letting the caller deal with it.
413 return false;
415 if (maxV > zeroV && !(appV <= maxV)) {
416 // The version of the application is more than the maximal version
417 // this blocklist entry applies to, so we can just ignore it by
418 // returning false and letting the caller deal with it.
419 return false;
421 } else if (key.EqualsLiteral("devices")) {
422 nsTArray<nsCString> devices;
423 ParseString(value, ',', devices);
424 GfxDeviceFamily* deviceIds = BlocklistDevicesToDeviceFamily(devices);
425 if (deviceIds) {
426 // Get GfxDriverInfo to adopt the devices array we created.
427 aDriverInfo.mDeleteDevices = true;
428 aDriverInfo.mDevices = deviceIds;
431 // We explicitly ignore unknown elements.
434 return true;
437 NS_IMETHODIMP
438 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
439 const char16_t* aData) {
440 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
441 nsTArray<GfxDriverInfo> driverInfo;
442 NS_ConvertUTF16toUTF8 utf8Data(aData);
444 for (const auto& blocklistEntry : utf8Data.Split('\n')) {
445 GfxDriverInfo di;
446 if (BlocklistEntryToDriverInfo(blocklistEntry, di)) {
447 // XXX Changing this to driverInfo.AppendElement(di) causes leaks.
448 // Probably some non-standard semantics of the copy/move operations?
449 *driverInfo.AppendElement() = di;
450 // Prevent di falling out of scope from destroying the devices.
451 di.mDeleteDevices = false;
452 } else {
453 driverInfo.AppendElement();
457 EvaluateDownloadedBlocklist(driverInfo);
460 return NS_OK;
463 GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
465 GfxInfoBase::~GfxInfoBase() = default;
467 nsresult GfxInfoBase::Init() {
468 InitGfxDriverInfoShutdownObserver();
470 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
471 if (os) {
472 os->AddObserver(this, "blocklist-data-gfxItems", true);
475 return NS_OK;
478 void GfxInfoBase::GetData() {
479 if (mScreenPixels != INT64_MAX) {
480 // Already initialized.
481 return;
484 ScreenManager::GetSingleton().GetTotalScreenPixels(&mScreenPixels);
487 NS_IMETHODIMP
488 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
489 int32_t* aStatus) {
490 // Ignore the gfx.blocklist.all pref on release and beta.
491 #if defined(RELEASE_OR_BETA)
492 int32_t blocklistAll = 0;
493 #else
494 int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
495 #endif
496 if (blocklistAll > 0) {
497 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
498 << "Forcing blocklisting all features";
499 *aStatus = FEATURE_BLOCKED_DEVICE;
500 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
501 return NS_OK;
504 if (blocklistAll < 0) {
505 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
506 << "Ignoring any feature blocklisting.";
507 *aStatus = FEATURE_STATUS_OK;
508 return NS_OK;
511 // This is how we evaluate the downloadable blocklist. If there is no pref,
512 // then we will fallback to checking the static blocklist.
513 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
514 return NS_OK;
517 if (XRE_IsContentProcess() || XRE_IsGPUProcess()) {
518 // Use the cached data received from the parent process.
519 MOZ_ASSERT(sFeatureStatus);
520 bool success = false;
521 for (const auto& fs : *sFeatureStatus) {
522 if (fs.feature() == aFeature) {
523 aFailureId = fs.failureId();
524 *aStatus = fs.status();
525 success = true;
526 break;
529 return success ? NS_OK : NS_ERROR_FAILURE;
532 nsString version;
533 nsTArray<GfxDriverInfo> driverInfo;
534 nsresult rv =
535 GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
536 return rv;
539 NS_IMETHODIMP
540 GfxInfoBase::GetFeatureStatusStr(const nsAString& aFeature,
541 nsACString& aFailureId, nsAString& aStatus) {
542 int32_t feature = BlocklistFeatureToGfxFeature(aFeature);
543 if (feature == nsIGfxInfo::FEATURE_INVALID) {
544 NS_ConvertUTF16toUTF8 feature(aFeature);
545 gfxWarning() << "Unrecognized feature " << feature.get();
546 return NS_ERROR_INVALID_ARG;
548 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
549 nsresult rv = GetFeatureStatus(feature, aFailureId, &status);
550 GfxFeatureStatusToBlocklistFeatureStatus(status, aStatus);
551 return rv;
554 nsTArray<gfx::GfxInfoFeatureStatus> GfxInfoBase::GetAllFeatures() {
555 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
556 if (!sFeatureStatus) {
557 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>());
558 for (int32_t i = nsIGfxInfo::FEATURE_START; i < nsIGfxInfo::FEATURE_COUNT;
559 ++i) {
560 int32_t status = nsIGfxInfo::FEATURE_STATUS_INVALID;
561 nsAutoCString failureId;
562 GetFeatureStatus(i, failureId, &status);
563 gfx::GfxInfoFeatureStatus gfxFeatureStatus;
564 gfxFeatureStatus.feature() = i;
565 gfxFeatureStatus.status() = status;
566 gfxFeatureStatus.failureId() = failureId;
567 sFeatureStatus->AppendElement(gfxFeatureStatus);
571 nsTArray<gfx::GfxInfoFeatureStatus> features;
572 for (const auto& status : *sFeatureStatus) {
573 gfx::GfxInfoFeatureStatus copy = status;
574 features.AppendElement(copy);
576 return features;
579 inline bool MatchingAllowStatus(int32_t aStatus) {
580 switch (aStatus) {
581 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
582 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
583 return true;
584 default:
585 return false;
589 // Matching OS go somewhat beyond the simple equality check because of the
590 // "All Windows" and "All OS X" variations.
592 // aBlockedOS is describing the system(s) we are trying to block.
593 // aSystemOS is describing the system we are running on.
595 // aSystemOS should not be "Windows" or "OSX" - it should be set to
596 // a particular version instead.
597 // However, it is valid for aBlockedOS to be one of those generic values,
598 // as we could be blocking all of the versions.
599 inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
600 OperatingSystem aSystemOS,
601 uint32_t aSystemOSBuild) {
602 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
603 aSystemOS != OperatingSystem::OSX);
605 // If the block entry OS is unknown, it doesn't match
606 if (aBlockedOS == OperatingSystem::Unknown) {
607 return false;
610 #if defined(XP_WIN)
611 if (aBlockedOS == OperatingSystem::Windows) {
612 // We do want even "unknown" aSystemOS to fall under "all windows"
613 return true;
616 constexpr uint32_t kMinWin10BuildNumber = 18362;
617 if (aBlockedOS == OperatingSystem::RecentWindows10 &&
618 aSystemOS == OperatingSystem::Windows10) {
619 // For allowlist purposes, we sometimes want to restrict to only recent
620 // versions of Windows 10. This is a bit of a kludge but easier than adding
621 // complicated blocklist infrastructure for build ID comparisons like driver
622 // versions.
623 return aSystemOSBuild >= kMinWin10BuildNumber;
626 if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
627 if (aSystemOS == OperatingSystem::Windows10) {
628 return aSystemOSBuild < kMinWin10BuildNumber;
629 } else {
630 return true;
633 #endif
635 #if defined(XP_MACOSX)
636 if (aBlockedOS == OperatingSystem::OSX) {
637 // We do want even "unknown" aSystemOS to fall under "all OS X"
638 return true;
640 #endif
642 return aSystemOS == aBlockedOS;
645 inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
646 switch (aBatteryStatus) {
647 case BatteryStatus::All:
648 return true;
649 case BatteryStatus::None:
650 return !aHasBattery;
651 case BatteryStatus::Present:
652 return aHasBattery;
655 MOZ_ASSERT_UNREACHABLE("bad battery status");
656 return false;
659 inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
660 int64_t aScreenPixels) {
661 constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
662 constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
664 switch (aScreenStatus) {
665 case ScreenSizeStatus::All:
666 return true;
667 case ScreenSizeStatus::Small:
668 return aScreenPixels <= kMaxSmallPixels;
669 case ScreenSizeStatus::SmallAndMedium:
670 return aScreenPixels <= kMaxMediumPixels;
671 case ScreenSizeStatus::Medium:
672 return aScreenPixels > kMaxSmallPixels &&
673 aScreenPixels <= kMaxMediumPixels;
674 case ScreenSizeStatus::MediumAndLarge:
675 return aScreenPixels > kMaxSmallPixels;
676 case ScreenSizeStatus::Large:
677 return aScreenPixels > kMaxMediumPixels;
680 MOZ_ASSERT_UNREACHABLE("bad screen status");
681 return false;
684 int32_t GfxInfoBase::FindBlocklistedDeviceInList(
685 const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
686 int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
687 bool aForAllowing) {
688 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
690 // Some properties are not available on all platforms.
691 nsAutoString windowProtocol;
692 nsresult rv = GetWindowProtocol(windowProtocol);
693 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
694 return 0;
697 bool hasBattery = false;
698 rv = GetHasBattery(&hasBattery);
699 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
700 return 0;
703 uint32_t osBuild = OperatingSystemBuild();
705 // Get the adapters once then reuse below
706 nsAutoString adapterVendorID[2];
707 nsAutoString adapterDeviceID[2];
708 nsAutoString adapterDriverVendor[2];
709 nsAutoString adapterDriverVersionString[2];
710 bool adapterInfoFailed[2];
712 adapterInfoFailed[0] =
713 (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
714 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
715 NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
716 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
717 adapterInfoFailed[1] =
718 (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
719 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
720 NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
721 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
722 // No point in going on if we don't have adapter info
723 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
724 return 0;
727 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
728 uint64_t driverVersion[2] = {0, 0};
729 if (!adapterInfoFailed[0]) {
730 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
732 if (!adapterInfoFailed[1]) {
733 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
735 #endif
737 uint32_t i = 0;
738 for (; i < info.Length(); i++) {
739 // If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
740 // blocklisting. Only consider entries for our search mode.
741 if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
742 continue;
745 // If we don't have the info for this GPU, no need to check further.
746 // It is unclear that we would ever have a mixture of 1st and 2nd
747 // GPU, but leaving the code in for that possibility for now.
748 // (Actually, currently mGpu2 will never be true, so this can
749 // be optimized out.)
750 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
751 if (adapterInfoFailed[infoIndex]) {
752 continue;
755 // Do the operating system check first, no point in getting the driver
756 // info if we won't need to use it.
757 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
758 continue;
761 if (info[i].mOperatingSystemVersion &&
762 info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
763 continue;
766 if (!MatchingBattery(info[i].mBattery, hasBattery)) {
767 continue;
770 if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
771 continue;
774 if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
775 continue;
778 if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
779 continue;
782 if (!DoesDriverVendorMatch(info[i].mDriverVendor,
783 adapterDriverVendor[infoIndex])) {
784 continue;
787 if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
788 nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
789 if (rv == NS_ERROR_NOT_AVAILABLE) {
790 // Not found
791 continue;
793 if (rv != NS_OK) {
794 // Failed to search, allowlist should not match, blocklist should match
795 // for safety reasons
796 if (aForAllowing) {
797 continue;
799 break;
803 bool match = false;
805 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
806 continue;
808 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
809 continue;
811 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
812 continue;
814 if (!info[i].mManufacturer.IsEmpty() &&
815 !info[i].mManufacturer.Equals(Manufacturer())) {
816 continue;
819 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
820 switch (info[i].mComparisonOp) {
821 case DRIVER_LESS_THAN:
822 match = driverVersion[infoIndex] < info[i].mDriverVersion;
823 break;
824 case DRIVER_BUILD_ID_LESS_THAN:
825 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
826 break;
827 case DRIVER_LESS_THAN_OR_EQUAL:
828 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
829 break;
830 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
831 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
832 break;
833 case DRIVER_GREATER_THAN:
834 match = driverVersion[infoIndex] > info[i].mDriverVersion;
835 break;
836 case DRIVER_GREATER_THAN_OR_EQUAL:
837 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
838 break;
839 case DRIVER_EQUAL:
840 match = driverVersion[infoIndex] == info[i].mDriverVersion;
841 break;
842 case DRIVER_NOT_EQUAL:
843 match = driverVersion[infoIndex] != info[i].mDriverVersion;
844 break;
845 case DRIVER_BETWEEN_EXCLUSIVE:
846 match = driverVersion[infoIndex] > info[i].mDriverVersion &&
847 driverVersion[infoIndex] < info[i].mDriverVersionMax;
848 break;
849 case DRIVER_BETWEEN_INCLUSIVE:
850 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
851 driverVersion[infoIndex] <= info[i].mDriverVersionMax;
852 break;
853 case DRIVER_BETWEEN_INCLUSIVE_START:
854 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
855 driverVersion[infoIndex] < info[i].mDriverVersionMax;
856 break;
857 case DRIVER_COMPARISON_IGNORED:
858 // We don't have a comparison op, so we match everything.
859 match = true;
860 break;
861 default:
862 NS_WARNING("Bogus op in GfxDriverInfo");
863 break;
865 #else
866 // We don't care what driver version it was. We only check OS version and if
867 // the device matches.
868 match = true;
869 #endif
871 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
872 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
873 info[i].mFeature == aFeature ||
874 (info[i].mFeature == GfxDriverInfo::optionalFeatures &&
875 OnlyAllowFeatureOnKnownConfig(aFeature))) {
876 status = info[i].mFeatureStatus;
877 if (!info[i].mRuleId.IsEmpty()) {
878 aFailureId = info[i].mRuleId.get();
879 } else {
880 aFailureId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID";
882 break;
887 #if defined(XP_WIN)
888 // As a very special case, we block D2D on machines with an NVidia 310M GPU
889 // as either the primary or secondary adapter. D2D is also blocked when the
890 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
891 // If the primary GPU already matched something in the blocklist then we
892 // ignore this special rule. See bug 1008759.
893 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
894 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
895 if (!adapterInfoFailed[1]) {
896 nsAString& nvVendorID =
897 (nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
898 const nsString nv310mDeviceId = u"0x0A70"_ns;
899 if (nvVendorID.Equals(adapterVendorID[1],
900 nsCaseInsensitiveStringComparator) &&
901 nv310mDeviceId.Equals(adapterDeviceID[1],
902 nsCaseInsensitiveStringComparator)) {
903 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
904 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
909 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
910 // back to the Windows handler, so we must handle this here.
911 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
912 if (info[i].mSuggestedVersion) {
913 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
914 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
915 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
916 aSuggestedVersion.AppendPrintf(
917 "%lld.%lld.%lld.%lld",
918 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
919 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
920 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
921 (info[i].mDriverVersion & 0x000000000000ffff));
924 #endif
926 return status;
929 void GfxInfoBase::SetFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>&& aFS) {
930 MOZ_ASSERT(!sFeatureStatus);
931 InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>(std::move(aFS)));
934 bool GfxInfoBase::DoesWindowProtocolMatch(
935 const nsAString& aBlocklistWindowProtocol,
936 const nsAString& aWindowProtocol) {
937 return aBlocklistWindowProtocol.Equals(aWindowProtocol,
938 nsCaseInsensitiveStringComparator) ||
939 aBlocklistWindowProtocol.Equals(
940 GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
941 nsCaseInsensitiveStringComparator);
944 bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
945 const nsAString& aAdapterVendor) {
946 return aBlocklistVendor.Equals(aAdapterVendor,
947 nsCaseInsensitiveStringComparator) ||
948 aBlocklistVendor.Equals(
949 GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
950 nsCaseInsensitiveStringComparator);
953 bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
954 const nsAString& aDriverVendor) {
955 return aBlocklistVendor.Equals(aDriverVendor,
956 nsCaseInsensitiveStringComparator) ||
957 aBlocklistVendor.Equals(
958 GfxDriverInfo::GetDriverVendor(DriverVendor::All),
959 nsCaseInsensitiveStringComparator);
962 bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
963 return aFeature == nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY;
966 nsresult GfxInfoBase::GetFeatureStatusImpl(
967 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
968 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
969 OperatingSystem* aOS /* = nullptr */) {
970 if (aFeature <= 0) {
971 gfxWarning() << "Invalid feature <= 0";
972 return NS_OK;
975 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
976 // Terminate now with the status determined by the derived type (OS-specific
977 // code).
978 return NS_OK;
981 if (sShutdownOccurred) {
982 // This is futile; we've already commenced shutdown and our blocklists have
983 // been deleted. We may want to look into resurrecting the blocklist instead
984 // but for now, just don't even go there.
985 return NS_OK;
988 // Ensure any additional initialization required is complete.
989 GetData();
991 // If an operating system was provided by the derived GetFeatureStatusImpl,
992 // grab it here. Otherwise, the OS is unknown.
993 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
995 nsAutoString adapterVendorID;
996 nsAutoString adapterDeviceID;
997 nsAutoString adapterDriverVersionString;
998 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
999 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
1000 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
1001 if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
1002 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
1003 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1004 } else {
1005 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
1007 return NS_OK;
1010 // We only check either the given blocklist, or the static list, as given.
1011 int32_t status;
1012 if (aDriverInfo.Length()) {
1013 status =
1014 FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
1015 aFailureId, os, /* aForAllowing */ false);
1016 } else {
1017 if (!sDriverInfo) {
1018 sDriverInfo = new nsTArray<GfxDriverInfo>();
1020 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
1021 aFeature, aFailureId, os,
1022 /* aForAllowing */ false);
1025 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1026 if (IsFeatureAllowlisted(aFeature)) {
1027 // This feature is actually using the allowlist; that means after we pass
1028 // the blocklist to prevent us explicitly from getting the feature, we now
1029 // need to check the allowlist to ensure we are allowed to get it in the
1030 // first place.
1031 if (aDriverInfo.Length()) {
1032 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
1033 aFeature, aFailureId, os,
1034 /* aForAllowing */ true);
1035 } else {
1036 status = FindBlocklistedDeviceInList(
1037 GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
1038 /* aForAllowing */ true);
1041 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1042 status = nsIGfxInfo::FEATURE_DENIED;
1044 } else {
1045 // It's now done being processed. It's safe to set the status to
1046 // STATUS_OK.
1047 status = nsIGfxInfo::FEATURE_STATUS_OK;
1051 *aStatus = status;
1052 return NS_OK;
1055 NS_IMETHODIMP
1056 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
1057 nsAString& aVersion) {
1058 nsCString version;
1059 if (GetPrefValueForDriverVersion(version)) {
1060 aVersion = NS_ConvertASCIItoUTF16(version);
1061 return NS_OK;
1064 int32_t status;
1065 nsCString discardFailureId;
1066 nsTArray<GfxDriverInfo> driverInfo;
1067 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
1068 discardFailureId);
1071 NS_IMETHODIMP
1072 GfxInfoBase::GetFeatureSuggestedDriverVersionStr(const nsAString& aFeature,
1073 nsAString& aVersion) {
1074 int32_t feature = BlocklistFeatureToGfxFeature(aFeature);
1075 if (feature == nsIGfxInfo::FEATURE_INVALID) {
1076 NS_ConvertUTF16toUTF8 feature(aFeature);
1077 gfxWarning() << "Unrecognized feature " << feature.get();
1078 return NS_ERROR_INVALID_ARG;
1080 return GetFeatureSuggestedDriverVersion(feature, aVersion);
1083 void GfxInfoBase::EvaluateDownloadedBlocklist(
1084 nsTArray<GfxDriverInfo>& aDriverInfo) {
1085 // If the list is empty, then we don't actually want to call
1086 // GetFeatureStatusImpl since we will use the static list instead. In that
1087 // case, all we want to do is make sure the pref is removed.
1088 if (aDriverInfo.IsEmpty()) {
1089 gfxCriticalNoteOnce << "Evaluate empty downloaded blocklist";
1090 return;
1093 OperatingSystem os = GetOperatingSystem();
1095 // For every feature we know about, we evaluate whether this blocklist has a
1096 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1097 // GetFeatureStatus above, so we don't need to hold on to this blocklist
1098 // anywhere permanent.
1099 for (int feature = nsIGfxInfo::FEATURE_START;
1100 feature < nsIGfxInfo::FEATURE_COUNT; ++feature) {
1101 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
1102 nsCString failureId;
1103 nsAutoString suggestedVersion;
1105 // Note that we are careful to call the base class method since we only want
1106 // to evaluate the downloadable blocklist for these prefs.
1107 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GfxInfoBase::GetFeatureStatusImpl(
1108 feature, &status, suggestedVersion, aDriverInfo, failureId, &os)));
1110 switch (status) {
1111 default:
1112 MOZ_FALLTHROUGH_ASSERT("Unhandled feature status!");
1113 case nsIGfxInfo::FEATURE_STATUS_UNKNOWN:
1114 // This may be returned during shutdown or for invalid features.
1115 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
1116 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
1117 case nsIGfxInfo::FEATURE_DENIED:
1118 // We cannot use the downloadable blocklist to control the allowlist.
1119 // If a feature is allowlisted, then we should also ignore DENIED
1120 // statuses from GetFeatureStatusImpl because we don't check the
1121 // static list when and this is an expected value. If we wish to
1122 // override the allowlist, it is as simple as creating a normal
1123 // blocklist rule with a BLOCKED* status code.
1124 case nsIGfxInfo::FEATURE_STATUS_OK:
1125 RemovePrefForFeature(feature);
1126 break;
1128 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1129 if (!suggestedVersion.IsEmpty()) {
1130 SetPrefValueForDriverVersion(suggestedVersion);
1131 } else {
1132 RemovePrefForDriverVersion();
1134 [[fallthrough]];
1136 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1137 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1138 case nsIGfxInfo::FEATURE_DISCOURAGED:
1139 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1140 case nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST:
1141 SetPrefValueForFeature(feature, status, failureId);
1142 break;
1147 NS_IMETHODIMP_(void)
1148 GfxInfoBase::LogFailure(const nsACString& failure) {
1149 // gfxCriticalError has a mutex lock of its own, so we may not actually
1150 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1151 // will not return the copy of the logs unless it can get the same lock
1152 // that gfxCriticalError uses. Still, that is so much of an implementation
1153 // detail that it's nicer to just add an extra lock here and in
1154 // ::GetFailures()
1155 MutexAutoLock lock(mMutex);
1157 // By default, gfxCriticalError asserts; make it not assert in this case.
1158 gfxCriticalError(CriticalLog::DefaultOptions(false))
1159 << "(LF) " << failure.BeginReading();
1162 NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
1163 nsTArray<nsCString>& failures) {
1164 MutexAutoLock lock(mMutex);
1166 LogForwarder* logForwarder = Factory::GetLogForwarder();
1167 if (!logForwarder) {
1168 return NS_ERROR_UNEXPECTED;
1171 // There are two string copies in this method, starting with this one. We are
1172 // assuming this is not a big deal, as the size of the array should be small
1173 // and the strings in it should be small as well (the error messages in the
1174 // code.) The second copy happens with the AppendElement() calls.
1175 // Technically, we don't need the mutex lock after the StringVectorCopy()
1176 // call.
1177 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1178 LoggingRecord::const_iterator it;
1179 for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
1180 failures.AppendElement(nsDependentCSubstring(std::get<1>(*it).c_str(),
1181 std::get<1>(*it).size()));
1182 indices.AppendElement(std::get<0>(*it));
1185 return NS_OK;
1188 nsTArray<GfxInfoCollectorBase*>* sCollectors;
1190 static void InitCollectors() {
1191 if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1194 nsresult GfxInfoBase::GetInfo(JSContext* aCx,
1195 JS::MutableHandle<JS::Value> aResult) {
1196 InitCollectors();
1197 InfoObject obj(aCx);
1199 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1200 (*sCollectors)[i]->GetInfo(obj);
1203 // Some example property definitions
1204 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1205 // obj.DefineProperty("renderer", mRendererIDsString);
1206 // obj.DefineProperty("five", 5);
1208 if (!obj.mOk) {
1209 return NS_ERROR_FAILURE;
1212 aResult.setObject(*obj.mObj);
1213 return NS_OK;
1216 MOZ_RUNINIT nsAutoCString gBaseAppVersion;
1218 const nsCString& GfxInfoBase::GetApplicationVersion() {
1219 static bool versionInitialized = false;
1220 if (!versionInitialized) {
1221 // If we fail to get the version, we will not try again.
1222 versionInitialized = true;
1224 // Get the version from xpcom/system/nsIXULAppInfo.idl
1225 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1226 if (app) {
1227 app->GetVersion(gBaseAppVersion);
1230 return gBaseAppVersion;
1233 /* static */ bool GfxInfoBase::OnlyAllowFeatureOnKnownConfig(int32_t aFeature) {
1234 switch (aFeature) {
1235 // The GPU process doesn't need hardware acceleration and can run on
1236 // devices that we normally block from not being on our whitelist.
1237 case nsIGfxInfo::FEATURE_GPU_PROCESS:
1238 return kIsAndroid;
1239 // We can mostly assume that ANGLE will work
1240 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
1241 // Remote WebGL is needed for Win32k Lockdown, so it should be enabled
1242 // regardless of HW support or not
1243 case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
1244 // Backdrop filter should generally work, especially if we fall back to
1245 // Software WebRender because of an unknown vendor.
1246 case nsIGfxInfo::FEATURE_BACKDROP_FILTER:
1247 return false;
1248 default:
1249 return true;
1253 void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
1254 InitCollectors();
1255 sCollectors->AppendElement(collector);
1258 void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
1259 InitCollectors();
1260 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1261 if ((*sCollectors)[i] == collector) {
1262 sCollectors->RemoveElementAt(i);
1263 break;
1266 if (sCollectors->IsEmpty()) {
1267 delete sCollectors;
1268 sCollectors = nullptr;
1272 static void AppendMonitor(JSContext* aCx, widget::Screen& aScreen,
1273 JS::Handle<JSObject*> aOutArray, int32_t aIndex) {
1274 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1276 auto screenSize = aScreen.GetRect().Size();
1278 JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
1279 JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
1281 JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
1282 JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
1284 JS::Rooted<JS::Value> defaultCssScaleFactor(
1285 aCx,
1286 JS::Float32Value(static_cast<float>(aScreen.GetDefaultCSSScaleFactor())));
1287 JS_SetProperty(aCx, obj, "defaultCSSScaleFactor", defaultCssScaleFactor);
1289 JS::Rooted<JS::Value> contentsScaleFactor(
1290 aCx, JS::NumberValue(aScreen.GetContentsScaleFactor()));
1291 JS_SetProperty(aCx, obj, "contentsScaleFactor", contentsScaleFactor);
1293 #ifdef XP_WIN
1294 JS::Rooted<JS::Value> refreshRate(aCx,
1295 JS::Int32Value(aScreen.GetRefreshRate()));
1296 JS_SetProperty(aCx, obj, "refreshRate", refreshRate);
1298 JS::Rooted<JS::Value> pseudoDisplay(
1299 aCx, JS::BooleanValue(aScreen.GetIsPseudoDisplay()));
1300 JS_SetProperty(aCx, obj, "pseudoDisplay", pseudoDisplay);
1301 #endif
1303 JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
1304 JS_SetElement(aCx, aOutArray, aIndex, element);
1307 nsresult GfxInfoBase::FindMonitors(JSContext* aCx,
1308 JS::Handle<JSObject*> aOutArray) {
1309 int32_t index = 0;
1310 auto& sm = ScreenManager::GetSingleton();
1311 for (auto& screen : sm.CurrentScreenList()) {
1312 AppendMonitor(aCx, *screen, aOutArray, index++);
1315 if (index == 0) {
1316 // Ensure we return at least one monitor, this is needed for xpcshell.
1317 RefPtr<Screen> screen = sm.GetPrimaryScreen();
1318 AppendMonitor(aCx, *screen, aOutArray, index++);
1321 return NS_OK;
1324 NS_IMETHODIMP
1325 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
1326 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1328 nsresult rv = FindMonitors(aCx, array);
1329 if (NS_FAILED(rv)) {
1330 return rv;
1333 aResult.setObject(*array);
1334 return NS_OK;
1337 static inline bool SetJSPropertyString(JSContext* aCx,
1338 JS::Handle<JSObject*> aObj,
1339 const char* aProp, const char* aString) {
1340 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1341 if (!str) {
1342 return false;
1345 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1346 return JS_SetProperty(aCx, aObj, aProp, val);
1349 template <typename T>
1350 static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
1351 const T& aValue) {
1352 uint32_t index;
1353 if (!JS::GetArrayLength(aCx, aObj, &index)) {
1354 return false;
1356 return JS_SetElement(aCx, aObj, index, aValue);
1359 nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
1360 JS::MutableHandle<JS::Value> aOut) {
1361 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1362 if (!obj) {
1363 return NS_ERROR_OUT_OF_MEMORY;
1365 aOut.setObject(*obj);
1367 layers::LayersBackend backend =
1368 gfxPlatform::Initialized()
1369 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1370 : layers::LayersBackend::LAYERS_NONE;
1371 const char* backendName = layers::GetLayersBackendName(backend);
1372 SetJSPropertyString(aCx, obj, "compositor", backendName);
1374 // If graphics isn't initialized yet, just stop now.
1375 if (!gfxPlatform::Initialized()) {
1376 return NS_OK;
1379 DescribeFeatures(aCx, obj);
1380 return NS_OK;
1383 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
1384 JS::MutableHandle<JS::Value> aOut) {
1385 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1386 if (!containerObj) {
1387 return NS_ERROR_OUT_OF_MEMORY;
1389 aOut.setObject(*containerObj);
1391 JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
1392 if (!featureArray) {
1393 return NS_ERROR_OUT_OF_MEMORY;
1396 // Collect features.
1397 gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
1398 FeatureState& aFeature) -> void {
1399 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1400 if (!obj) {
1401 return;
1403 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1404 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1405 !SetJSPropertyString(aCx, obj, "status",
1406 FeatureStatusToString(aFeature.GetValue()))) {
1407 return;
1410 JS::Rooted<JS::Value> log(aCx);
1411 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1412 return;
1414 if (!JS_SetProperty(aCx, obj, "log", log)) {
1415 return;
1418 if (!AppendJSElement(aCx, featureArray, obj)) {
1419 return;
1423 JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
1424 if (!fallbackArray) {
1425 return NS_ERROR_OUT_OF_MEMORY;
1428 // Collect fallbacks.
1429 gfxConfig::ForEachFallback(
1430 [&](const char* aName, const char* aMessage) -> void {
1431 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1432 if (!obj) {
1433 return;
1436 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1437 !SetJSPropertyString(aCx, obj, "message", aMessage)) {
1438 return;
1441 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1442 return;
1446 JS::Rooted<JS::Value> val(aCx);
1448 val = JS::ObjectValue(*featureArray);
1449 JS_SetProperty(aCx, containerObj, "features", val);
1451 val = JS::ObjectValue(*fallbackArray);
1452 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1454 return NS_OK;
1457 bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
1458 const FeatureState& aFeature,
1459 JS::MutableHandle<JS::Value> aOut) {
1460 JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
1461 if (!log) {
1462 return false;
1464 aOut.setObject(*log);
1466 aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
1467 const char* aMessage,
1468 const nsCString& aFailureId) -> void {
1469 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1470 if (!obj) {
1471 return;
1474 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1475 !SetJSPropertyString(aCx, obj, "status",
1476 FeatureStatusToString(aStatus)) ||
1477 (!aFailureId.IsEmpty() &&
1478 !SetJSPropertyString(aCx, obj, "failureId", aFailureId.get())) ||
1479 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
1480 return;
1483 if (!AppendJSElement(aCx, log, obj)) {
1484 return;
1488 return true;
1491 void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
1492 JS::Rooted<JSObject*> obj(aCx);
1494 gfx::FeatureState& hwCompositing =
1495 gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
1496 InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
1498 gfx::FeatureState& gpuProcess =
1499 gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
1500 InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
1502 gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
1503 InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
1505 gfx::FeatureState& wrCompositor =
1506 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
1507 InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
1509 gfx::FeatureState& openglCompositing =
1510 gfxConfig::GetFeature(gfx::Feature::OPENGL_COMPOSITING);
1511 InitFeatureObject(aCx, aObj, "openglCompositing", openglCompositing, &obj);
1513 gfx::FeatureState& omtp = gfxConfig::GetFeature(gfx::Feature::OMTP);
1514 InitFeatureObject(aCx, aObj, "omtp", omtp, &obj);
1517 bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
1518 JS::Handle<JSObject*> aContainer,
1519 const char* aName,
1520 mozilla::gfx::FeatureState& aFeatureState,
1521 JS::MutableHandle<JSObject*> aOutObj) {
1522 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1523 if (!obj) {
1524 return false;
1527 nsCString status = aFeatureState.GetStatusAndFailureIdString();
1529 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
1530 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1531 JS_SetProperty(aCx, obj, "status", val);
1533 // Add the feature object to the container.
1535 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1536 JS_SetProperty(aCx, aContainer, aName, val);
1539 aOutObj.set(obj);
1540 return true;
1543 nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
1544 JS::MutableHandle<JS::Value> aOut) {
1545 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1546 if (!array) {
1547 return NS_ERROR_OUT_OF_MEMORY;
1549 aOut.setObject(*array);
1551 DriverCrashGuard::ForEachActiveCrashGuard(
1552 [&](const char* aName, const char* aPrefName) -> void {
1553 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1554 if (!obj) {
1555 return;
1557 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1558 return;
1560 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1561 return;
1563 if (!AppendJSElement(aCx, array, obj)) {
1564 return;
1568 return NS_OK;
1571 NS_IMETHODIMP
1572 GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
1573 *aTargetFrameRate = gfxPlatform::TargetFrameRate();
1574 return NS_OK;
1577 NS_IMETHODIMP
1578 GfxInfoBase::GetCodecSupportInfo(nsACString& aCodecSupportInfo) {
1579 aCodecSupportInfo.Assign(gfx::gfxVars::CodecSupportInfo());
1580 return NS_OK;
1583 NS_IMETHODIMP
1584 GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
1585 *aIsHeadless = gfxPlatform::IsHeadless();
1586 return NS_OK;
1589 #if defined(MOZ_WIDGET_ANDROID)
1591 const char* chromebookProductList[] = {
1592 "asuka", "asurada", "atlas", "auron", "banjo", "banon",
1593 "bob", "brask", "brya", "buddy", "butterfly", "candy",
1594 "caroline", "cave", "celes", "chell", "cherry", "clapper",
1595 "coral", "corsola", "cyan", "daisy", "dedede", "drallion",
1596 "edgar", "elm", "enguarde", "eve", "expresso", "falco",
1597 "fizz", "gandof", "glimmer", "gnawty", "grunt", "guado",
1598 "guybrush", "hana", "hatch", "heli", "jacuzzi", "kalista",
1599 "kefka", "kevin", "kip", "kukui", "lars", "leon",
1600 "link", "lulu", "lumpy", "mccloud", "monroe", "nami",
1601 "nautilus", "ninja", "nissa", "nocturne", "nyan", "octopus",
1602 "orco", "panther", "parrot", "peach", "peppy", "puff",
1603 "pyro", "quawks", "rammus", "reef", "reks", "relm",
1604 "rikku", "samus", "sand", "sarien", "scarlet", "sentry",
1605 "setzer", "skyrim", "snappy", "soraka", "squawks", "staryu",
1606 "stout", "strongbad", "stumpy", "sumo", "swanky", "terra",
1607 "tidus", "tricky", "trogdor", "ultima", "veyron", "volteer",
1608 "winky", "wizpig", "wolf", "x86", "zako", "zork"};
1610 bool ProductIsChromebook(nsCString product) {
1611 size_t result;
1612 return BinarySearchIf(
1613 chromebookProductList, 0, std::size(chromebookProductList),
1614 [&](const char* const aValue) -> int {
1615 return strcmp(product.get(), aValue);
1617 &result);
1619 #endif
1621 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
1622 static StaticAutoPtr<std::pair<Device, nsString>> ret;
1624 std::pair<Device, nsString>* GfxInfoBase::GetFontVisibilityDeterminationPair() {
1625 if (!ret) {
1626 ret = new std::pair<Device, nsString>();
1627 ret->first = Device::Unassigned;
1628 ret->second = u""_ns;
1629 ClearOnShutdown(&ret);
1632 if (ret->first != Device::Unassigned) {
1633 return ret;
1636 #if defined(MOZ_WIDGET_ANDROID)
1637 auto androidReleaseVersion = strtol(
1638 java::sdk::Build::VERSION::RELEASE()->ToCString().get(), nullptr, 10);
1640 auto androidManufacturer = java::sdk::Build::MANUFACTURER()->ToCString();
1641 nsContentUtils::ASCIIToLower(androidManufacturer);
1643 auto androidBrand = java::sdk::Build::BRAND()->ToCString();
1644 nsContentUtils::ASCIIToLower(androidBrand);
1646 auto androidModel = java::sdk::Build::MODEL()->ToCString();
1647 nsContentUtils::ASCIIToLower(androidModel);
1649 auto androidProduct = java::sdk::Build::PRODUCT()->ToCString();
1650 nsContentUtils::ASCIIToLower(androidProduct);
1652 auto androidProductIsChromebook = ProductIsChromebook(androidProduct);
1654 if (androidReleaseVersion < 4 || androidReleaseVersion > 20) {
1655 // Something is screwy, oh well.
1656 ret->second.AppendASCII("Unknown Release Version - ");
1657 ret->first = Device::Android_Unknown_Release_Version;
1658 } else if (androidReleaseVersion <= 8) {
1659 ret->second.AppendASCII("Android <9 - ");
1660 ret->first = Device::Android_sub_9;
1661 } else if (androidReleaseVersion <= 11) {
1662 ret->second.AppendASCII("Android 9-11 - ");
1663 ret->first = Device::Android_9_11;
1664 } else if (androidReleaseVersion > 11) {
1665 ret->second.AppendASCII("Android 12+ - ");
1666 ret->first = Device::Android_12_plus;
1667 } else {
1668 MOZ_CRASH_UNSAFE_PRINTF(
1669 "Somehow wound up in GetFontVisibilityDeterminationPair with a release "
1670 "version of %li",
1671 androidReleaseVersion);
1674 if (androidManufacturer == "google" && androidModel == androidProduct &&
1675 androidProductIsChromebook) {
1676 // Chromebook font set coming later
1677 ret->second.AppendASCII("Chromebook - ");
1678 ret->first = Device::Android_Chromebook;
1680 if (androidBrand == "amazon") {
1681 // Amazon Fire font set coming later
1682 ret->second.AppendASCII("Amazon - ");
1683 ret->first = Device::Android_Amazon;
1685 if (androidBrand == "peloton") {
1686 // We don't know how to categorize fonts on this system
1687 ret->second.AppendASCII("Peloton - ");
1688 ret->first = Device::Android_Unknown_Peloton;
1690 if (androidProduct == "vbox86p") {
1691 ret->second.AppendASCII("vbox - ");
1692 // We can't categorize fonts when running in an emulator on a Desktop
1693 ret->first = Device::Android_Unknown_vbox;
1695 if (androidModel.Find("mitv"_ns) != kNotFound && androidBrand == "xiaomi") {
1696 // We don't know how to categorize fonts on this system
1697 ret->second.AppendASCII("mitv - ");
1698 ret->first = Device::Android_Unknown_mitv;
1701 ret->second.AppendPrintf(
1702 "release_version_str=%s, release_version=%li",
1703 java::sdk::Build::VERSION::RELEASE()->ToCString().get(),
1704 androidReleaseVersion);
1705 ret->second.AppendPrintf(
1706 ", manufacturer=%s, brand=%s, model=%s, product=%s, chromebook=%s",
1707 androidManufacturer.get(), androidBrand.get(), androidModel.get(),
1708 androidProduct.get(), androidProductIsChromebook ? "yes" : "no");
1710 #elif defined(XP_LINUX)
1711 ret->first = Device::Linux_Unknown;
1713 long versionMajor = 0;
1714 FILE* fp = fopen("/etc/os-release", "r");
1715 if (fp) {
1716 char buf[512];
1717 while (fgets(buf, sizeof(buf), fp)) {
1718 if (strncmp(buf, "VERSION_ID=\"", 12) == 0) {
1719 ret->second.AppendPrintf("VERSION_ID=%.11s", buf + 11);
1720 versionMajor = strtol(buf + 12, nullptr, 10);
1721 if (ret->first != Device::Linux_Unknown) {
1722 break;
1726 if (strncmp(buf, "ID=", 3) == 0) {
1727 ret->second.AppendPrintf("ID=%.6s", buf + 3);
1728 if (strncmp(buf + 3, "ubuntu", 6) == 0) {
1729 ret->first = Device::Linux_Ubuntu_any;
1730 } else if (strncmp(buf + 3, "fedora", 6) == 0) {
1731 ret->first = Device::Linux_Fedora_any;
1734 if (versionMajor) {
1735 break;
1739 fclose(fp);
1741 if (ret->first == Device::Linux_Ubuntu_any) {
1742 if (versionMajor == 20) {
1743 ret->first = Device::Linux_Ubuntu_20;
1744 ret->second.Insert(u"Ubuntu 20 - ", 0);
1745 } else if (versionMajor == 22) {
1746 ret->first = Device::Linux_Ubuntu_22;
1747 ret->second.Insert(u"Ubuntu 22 - ", 0);
1748 } else {
1749 ret->second.Insert(u"Ubuntu Unknown - ", 0);
1751 } else if (ret->first == Device::Linux_Fedora_any) {
1752 if (versionMajor == 38) {
1753 ret->first = Device::Linux_Fedora_38;
1754 ret->second.Insert(u"Fedora 38 - ", 0);
1755 } else if (versionMajor == 39) {
1756 ret->first = Device::Linux_Fedora_39;
1757 ret->second.Insert(u"Fedora 39 - ", 0);
1758 } else {
1759 ret->second.Insert(u"Fedora Unknown - ", 0);
1761 } else {
1762 ret->second.Insert(u"Linux Unknown - ", 0);
1765 #elif defined(XP_MACOSX)
1766 ret->first = Device::MacOS_Unknown;
1767 ret->second.AppendASCII("macOS Platform");
1769 int major = 0;
1770 int minor = 0;
1771 int bugfix = 0;
1772 nsCocoaFeatures::GetSystemVersion(major, minor, bugfix);
1773 if (major == 0) {
1774 return ret;
1777 ret->first = major >= 13 ? Device::MacOS_13_plus : Device::MacOS_sub_13;
1778 ret->second.AppendPrintf("macOS %d.%d.%d", major, minor, bugfix);
1779 #elif defined(XP_WIN)
1780 ret->first = Device::Windows_Platform;
1781 ret->second.AppendASCII("Windows Platform");
1782 #else
1783 ret->first = Device::Unknown_Platform;
1784 ret->second.AppendASCII("Unknown Platform");
1785 #endif
1787 return ret;
1790 NS_IMETHODIMP
1791 GfxInfoBase::GetFontVisibilityDetermination(
1792 Device* aFontVisibilityDetermination) {
1793 auto ret = GetFontVisibilityDeterminationPair();
1795 *aFontVisibilityDetermination = ret->first;
1796 return NS_OK;
1799 NS_IMETHODIMP
1800 GfxInfoBase::GetFontVisibilityDeterminationStr(
1801 nsAString& aFontVisibilityDeterminationStr) {
1802 auto ret = GetFontVisibilityDeterminationPair();
1803 aFontVisibilityDeterminationStr.Assign(ret->second);
1804 return NS_OK;
1807 NS_IMETHODIMP
1808 GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
1809 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1810 nsString outStr;
1812 switch (backend) {
1813 case BackendType::DIRECT2D1_1: {
1814 outStr.AppendPrintf("Direct2D 1.1");
1815 break;
1817 case BackendType::SKIA: {
1818 outStr.AppendPrintf("Skia");
1819 break;
1821 case BackendType::CAIRO: {
1822 outStr.AppendPrintf("Cairo");
1823 break;
1825 default:
1826 return NS_ERROR_FAILURE;
1829 aContentBackend.Assign(outStr);
1830 return NS_OK;
1833 NS_IMETHODIMP
1834 GfxInfoBase::GetAzureCanvasBackend(nsAString& aBackend) {
1835 CopyASCIItoUTF16(mozilla::MakeStringSpan(
1836 gfxPlatform::GetPlatform()->GetAzureCanvasBackend()),
1837 aBackend);
1838 return NS_OK;
1841 NS_IMETHODIMP
1842 GfxInfoBase::GetAzureContentBackend(nsAString& aBackend) {
1843 CopyASCIItoUTF16(mozilla::MakeStringSpan(
1844 gfxPlatform::GetPlatform()->GetAzureContentBackend()),
1845 aBackend);
1846 return NS_OK;
1849 NS_IMETHODIMP
1850 GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
1851 GPUProcessManager* gpu = GPUProcessManager::Get();
1852 if (!gpu) {
1853 // Not supported in content processes.
1854 return NS_ERROR_FAILURE;
1857 *aOutValue = !!gpu->GetGPUChild();
1858 return NS_OK;
1861 NS_IMETHODIMP
1862 GfxInfoBase::GetUsingRemoteCanvas(bool* aOutValue) {
1863 *aOutValue = gfx::gfxVars::RemoteCanvasEnabled();
1864 return NS_OK;
1867 NS_IMETHODIMP
1868 GfxInfoBase::GetUsingAcceleratedCanvas(bool* aOutValue) {
1869 *aOutValue = gfx::gfxVars::UseAcceleratedCanvas2D();
1870 return NS_OK;
1873 NS_IMETHODIMP_(int32_t)
1874 GfxInfoBase::GetMaxRefreshRate(bool* aMixed) {
1875 if (aMixed) {
1876 *aMixed = false;
1879 int32_t maxRefreshRate = 0;
1880 for (auto& screen : ScreenManager::GetSingleton().CurrentScreenList()) {
1881 int32_t refreshRate = screen->GetRefreshRate();
1882 if (aMixed && maxRefreshRate > 0 && maxRefreshRate != refreshRate) {
1883 *aMixed = true;
1885 maxRefreshRate = std::max(maxRefreshRate, refreshRate);
1888 return maxRefreshRate > 0 ? maxRefreshRate : -1;
1891 NS_IMETHODIMP
1892 GfxInfoBase::GetTextScaleFactor(float* aOutValue) {
1893 *aOutValue = LookAndFeel::GetTextScaleFactor();
1894 return NS_OK;
1897 NS_IMETHODIMP
1898 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
1899 gfxPlatform::GetPlatform();
1901 GPUProcessManager* gpm = GPUProcessManager::Get();
1902 if (aEnable) {
1903 if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
1904 gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1906 DebugOnly<nsresult> rv = gpm->EnsureGPUReady();
1907 MOZ_ASSERT(rv != NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
1908 } else {
1909 gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1910 gpm->KillProcess();
1913 *_retval = true;
1914 return NS_OK;
1917 NS_IMETHODIMP GfxInfoBase::KillGPUProcessForTests() {
1918 GPUProcessManager* gpm = GPUProcessManager::Get();
1919 if (!gpm) {
1920 // gfxPlatform has not been initialized.
1921 return NS_ERROR_NOT_INITIALIZED;
1924 gpm->KillProcess();
1925 return NS_OK;
1928 NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
1929 GPUProcessManager* gpm = GPUProcessManager::Get();
1930 if (!gpm) {
1931 // gfxPlatform has not been initialized.
1932 return NS_ERROR_NOT_INITIALIZED;
1935 gpm->CrashProcess();
1936 return NS_OK;
1939 GfxInfoCollectorBase::GfxInfoCollectorBase() {
1940 GfxInfoBase::AddCollector(this);
1943 GfxInfoCollectorBase::~GfxInfoCollectorBase() {
1944 GfxInfoBase::RemoveCollector(this);