Bug 1376625 - Updating meta data for newly added tests r=ato
[gecko.git] / widget / GfxInfoBase.cpp
blob477fedb57e73bdf95292647857d894e2e43ff37c
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 "GfxInfoWebGL.h"
13 #include "GfxDriverInfo.h"
14 #include "nsCOMPtr.h"
15 #include "nsCOMArray.h"
16 #include "nsString.h"
17 #include "nsUnicharUtils.h"
18 #include "nsVersionComparator.h"
19 #include "mozilla/Services.h"
20 #include "mozilla/Observer.h"
21 #include "nsIObserver.h"
22 #include "nsIObserverService.h"
23 #include "nsIDOMElement.h"
24 #include "nsIDOMHTMLCollection.h"
25 #include "nsIDOMNode.h"
26 #include "nsIDOMNodeList.h"
27 #include "nsTArray.h"
28 #include "nsXULAppAPI.h"
29 #include "nsIXULAppInfo.h"
30 #include "mozilla/Preferences.h"
31 #include "mozilla/dom/ContentChild.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/gfx/GPUProcessManager.h"
34 #include "mozilla/gfx/Logging.h"
35 #include "mozilla/gfx/gfxVars.h"
36 #include "MediaPrefs.h"
37 #include "gfxPrefs.h"
38 #include "gfxPlatform.h"
39 #include "gfxConfig.h"
40 #include "DriverCrashGuard.h"
42 #if defined(MOZ_CRASHREPORTER)
43 #include "nsExceptionHandler.h"
44 #endif
46 using namespace mozilla::widget;
47 using namespace mozilla;
48 using mozilla::MutexAutoLock;
50 nsTArray<GfxDriverInfo>* GfxInfoBase::mDriverInfo;
51 nsTArray<dom::GfxInfoFeatureStatus>* GfxInfoBase::mFeatureStatus;
52 bool GfxInfoBase::mDriverInfoObserverInitialized;
53 bool GfxInfoBase::mShutdownOccurred;
55 // Observes for shutdown so that the child GfxDriverInfo list is freed.
56 class ShutdownObserver : public nsIObserver
58 virtual ~ShutdownObserver() {}
60 public:
61 ShutdownObserver() {}
63 NS_DECL_ISUPPORTS
65 NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
66 const char16_t *aData) override
68 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
70 delete GfxInfoBase::mDriverInfo;
71 GfxInfoBase::mDriverInfo = nullptr;
73 delete GfxInfoBase::mFeatureStatus;
74 GfxInfoBase::mFeatureStatus = nullptr;
76 for (uint32_t i = 0; i < DeviceFamilyMax; i++)
77 delete GfxDriverInfo::mDeviceFamilies[i];
79 for (uint32_t i = 0; i < DeviceVendorMax; i++)
80 delete GfxDriverInfo::mDeviceVendors[i];
82 GfxInfoBase::mShutdownOccurred = true;
84 return NS_OK;
88 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
90 void InitGfxDriverInfoShutdownObserver()
92 if (GfxInfoBase::mDriverInfoObserverInitialized)
93 return;
95 GfxInfoBase::mDriverInfoObserverInitialized = true;
97 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
98 if (!observerService) {
99 NS_WARNING("Could not get observer service!");
100 return;
103 ShutdownObserver *obs = new ShutdownObserver();
104 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
107 using namespace mozilla::widget;
108 using namespace mozilla::gfx;
109 using namespace mozilla;
111 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference)
113 #define BLACKLIST_PREF_BRANCH "gfx.blacklist."
114 #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version"
115 #define BLACKLIST_ENTRY_TAG_NAME "gfxBlacklistEntry"
117 static const char*
118 GetPrefNameForFeature(int32_t aFeature)
120 const char* name = nullptr;
121 switch(aFeature) {
122 case nsIGfxInfo::FEATURE_DIRECT2D:
123 name = BLACKLIST_PREF_BRANCH "direct2d";
124 break;
125 case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
126 name = BLACKLIST_PREF_BRANCH "layers.direct3d9";
127 break;
128 case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
129 name = BLACKLIST_PREF_BRANCH "layers.direct3d10";
130 break;
131 case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
132 name = BLACKLIST_PREF_BRANCH "layers.direct3d10-1";
133 break;
134 case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
135 name = BLACKLIST_PREF_BRANCH "layers.direct3d11";
136 break;
137 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
138 name = BLACKLIST_PREF_BRANCH "direct3d11angle";
139 break;
140 case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
141 name = BLACKLIST_PREF_BRANCH "hardwarevideodecoding";
142 break;
143 case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
144 name = BLACKLIST_PREF_BRANCH "layers.opengl";
145 break;
146 case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
147 name = BLACKLIST_PREF_BRANCH "webgl.opengl";
148 break;
149 case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
150 name = BLACKLIST_PREF_BRANCH "webgl.angle";
151 break;
152 case nsIGfxInfo::FEATURE_WEBGL_MSAA:
153 name = BLACKLIST_PREF_BRANCH "webgl.msaa";
154 break;
155 case nsIGfxInfo::FEATURE_STAGEFRIGHT:
156 name = BLACKLIST_PREF_BRANCH "stagefright";
157 break;
158 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION:
159 name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration";
160 break;
161 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
162 name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
163 break;
164 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
165 name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
166 break;
167 case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
168 name = BLACKLIST_PREF_BRANCH "canvas2d.acceleration";
169 break;
170 case nsIGfxInfo::FEATURE_WEBGL2:
171 name = BLACKLIST_PREF_BRANCH "webgl2";
172 break;
173 case nsIGfxInfo::FEATURE_ADVANCED_LAYERS:
174 name = BLACKLIST_PREF_BRANCH "layers.advanced";
175 break;
176 case nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX:
177 name = BLACKLIST_PREF_BRANCH "d3d11.keyed.mutex";
178 break;
179 case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
180 case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
181 case nsIGfxInfo::FEATURE_DX_INTEROP2:
182 case nsIGfxInfo::FEATURE_GPU_PROCESS:
183 // We don't provide prefs for these features.
184 break;
185 default:
186 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
187 break;
190 return name;
193 // Returns the value of the pref for the relevant feature in aValue.
194 // If the pref doesn't exist, aValue is not touched, and returns false.
195 static bool
196 GetPrefValueForFeature(int32_t aFeature, int32_t& aValue, nsACString& aFailureId)
198 const char *prefname = GetPrefNameForFeature(aFeature);
199 if (!prefname)
200 return false;
202 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
203 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
204 return false;
207 nsCString failureprefname(prefname);
208 failureprefname += ".failureid";
209 nsAutoCString failureValue;
210 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
211 if (NS_SUCCEEDED(rv)) {
212 aFailureId = failureValue.get();
213 } else {
214 aFailureId = "FEATURE_FAILURE_BLACKLIST_PREF";
217 return true;
220 static void
221 SetPrefValueForFeature(int32_t aFeature, int32_t aValue, const nsACString& aFailureId)
223 const char *prefname = GetPrefNameForFeature(aFeature);
224 if (!prefname)
225 return;
227 Preferences::SetInt(prefname, aValue);
228 if (!aFailureId.IsEmpty()) {
229 nsCString failureprefname(prefname);
230 failureprefname += ".failureid";
231 Preferences::SetCString(failureprefname.get(), aFailureId);
235 static void
236 RemovePrefForFeature(int32_t aFeature)
238 const char *prefname = GetPrefNameForFeature(aFeature);
239 if (!prefname)
240 return;
242 Preferences::ClearUser(prefname);
245 static bool
246 GetPrefValueForDriverVersion(nsCString& aVersion)
248 return NS_SUCCEEDED(Preferences::GetCString(SUGGESTED_VERSION_PREF,
249 aVersion));
252 static void
253 SetPrefValueForDriverVersion(const nsAString& aVersion)
255 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
258 static void
259 RemovePrefForDriverVersion()
261 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
265 static OperatingSystem
266 BlacklistOSToOperatingSystem(const nsAString& os)
268 if (os.EqualsLiteral("WINNT 6.1"))
269 return OperatingSystem::Windows7;
270 else if (os.EqualsLiteral("WINNT 6.2"))
271 return OperatingSystem::Windows8;
272 else if (os.EqualsLiteral("WINNT 6.3"))
273 return OperatingSystem::Windows8_1;
274 else if (os.EqualsLiteral("WINNT 10.0"))
275 return OperatingSystem::Windows10;
276 else if (os.EqualsLiteral("Linux"))
277 return OperatingSystem::Linux;
278 else if (os.EqualsLiteral("Darwin 9"))
279 return OperatingSystem::OSX10_5;
280 else if (os.EqualsLiteral("Darwin 10"))
281 return OperatingSystem::OSX10_6;
282 else if (os.EqualsLiteral("Darwin 11"))
283 return OperatingSystem::OSX10_7;
284 else if (os.EqualsLiteral("Darwin 12"))
285 return OperatingSystem::OSX10_8;
286 else if (os.EqualsLiteral("Darwin 13"))
287 return OperatingSystem::OSX10_9;
288 else if (os.EqualsLiteral("Darwin 14"))
289 return OperatingSystem::OSX10_10;
290 else if (os.EqualsLiteral("Darwin 15"))
291 return OperatingSystem::OSX10_11;
292 else if (os.EqualsLiteral("Darwin 16"))
293 return OperatingSystem::OSX10_12;
294 else if (os.EqualsLiteral("Android"))
295 return OperatingSystem::Android;
296 // For historical reasons, "All" in blocklist means "All Windows"
297 else if (os.EqualsLiteral("All"))
298 return OperatingSystem::Windows;
300 return OperatingSystem::Unknown;
303 static GfxDeviceFamily*
304 BlacklistDevicesToDeviceFamily(nsTArray<nsCString>& devices)
306 if (devices.Length() == 0)
307 return nullptr;
309 // For each device, get its device ID, and return a freshly-allocated
310 // GfxDeviceFamily with the contents of that array.
311 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
313 for (uint32_t i = 0; i < devices.Length(); ++i) {
314 // We make sure we don't add any "empty" device entries to the array, so
315 // we don't need to check if devices[i] is empty.
316 deviceIds->AppendElement(NS_ConvertUTF8toUTF16(devices[i]));
319 return deviceIds;
322 static int32_t
323 BlacklistFeatureToGfxFeature(const nsAString& aFeature)
325 MOZ_ASSERT(!aFeature.IsEmpty());
326 if (aFeature.EqualsLiteral("DIRECT2D"))
327 return nsIGfxInfo::FEATURE_DIRECT2D;
328 else if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS"))
329 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
330 else if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS"))
331 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
332 else if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS"))
333 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
334 else if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS"))
335 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
336 else if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE"))
337 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
338 else if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING"))
339 return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
340 else if (aFeature.EqualsLiteral("OPENGL_LAYERS"))
341 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
342 else if (aFeature.EqualsLiteral("WEBGL_OPENGL"))
343 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
344 else if (aFeature.EqualsLiteral("WEBGL_ANGLE"))
345 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
346 else if (aFeature.EqualsLiteral("WEBGL_MSAA"))
347 return nsIGfxInfo::FEATURE_WEBGL_MSAA;
348 else if (aFeature.EqualsLiteral("STAGEFRIGHT"))
349 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
350 else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE"))
351 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
352 else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE"))
353 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
354 else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION"))
355 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION;
356 else if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION"))
357 return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
358 else if (aFeature.EqualsLiteral("WEBGL2"))
359 return nsIGfxInfo::FEATURE_WEBGL2;
360 else if (aFeature.EqualsLiteral("ADVANCED_LAYERS"))
361 return nsIGfxInfo::FEATURE_ADVANCED_LAYERS;
362 else if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX"))
363 return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
365 // If we don't recognize the feature, it may be new, and something
366 // this version doesn't understand. So, nothing to do. This is
367 // different from feature not being specified at all, in which case
368 // this method should not get called and we should continue with the
369 // "all features" blocklisting.
370 return -1;
373 static int32_t
374 BlacklistFeatureStatusToGfxFeatureStatus(const nsAString& aStatus)
376 if (aStatus.EqualsLiteral("STATUS_OK"))
377 return nsIGfxInfo::FEATURE_STATUS_OK;
378 else if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION"))
379 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
380 else if (aStatus.EqualsLiteral("BLOCKED_DEVICE"))
381 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
382 else if (aStatus.EqualsLiteral("DISCOURAGED"))
383 return nsIGfxInfo::FEATURE_DISCOURAGED;
384 else if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION"))
385 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
387 // Do not allow it to set STATUS_UNKNOWN. Also, we are not
388 // expecting the "mismatch" status showing up here.
390 return nsIGfxInfo::FEATURE_STATUS_OK;
393 static VersionComparisonOp
394 BlacklistComparatorToComparisonOp(const nsAString& op)
396 if (op.EqualsLiteral("LESS_THAN"))
397 return DRIVER_LESS_THAN;
398 else if (op.EqualsLiteral("BUILD_ID_LESS_THAN"))
399 return DRIVER_BUILD_ID_LESS_THAN;
400 else if (op.EqualsLiteral("LESS_THAN_OR_EQUAL"))
401 return DRIVER_LESS_THAN_OR_EQUAL;
402 else if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL"))
403 return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
404 else if (op.EqualsLiteral("GREATER_THAN"))
405 return DRIVER_GREATER_THAN;
406 else if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL"))
407 return DRIVER_GREATER_THAN_OR_EQUAL;
408 else if (op.EqualsLiteral("EQUAL"))
409 return DRIVER_EQUAL;
410 else if (op.EqualsLiteral("NOT_EQUAL"))
411 return DRIVER_NOT_EQUAL;
412 else if (op.EqualsLiteral("BETWEEN_EXCLUSIVE"))
413 return DRIVER_BETWEEN_EXCLUSIVE;
414 else if (op.EqualsLiteral("BETWEEN_INCLUSIVE"))
415 return DRIVER_BETWEEN_INCLUSIVE;
416 else if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START"))
417 return DRIVER_BETWEEN_INCLUSIVE_START;
419 return DRIVER_COMPARISON_IGNORED;
424 Deserialize Blacklist entries from string.
425 e.g:
426 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
428 static bool
429 BlacklistEntryToDriverInfo(nsCString& aBlacklistEntry,
430 GfxDriverInfo& aDriverInfo)
432 // If we get an application version to be zero, something is not working
433 // and we are not going to bother checking the blocklist versions.
434 // See TestGfxWidgets.cpp for how version comparison works.
435 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
436 static mozilla::Version zeroV("0");
437 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
438 if (appV <= zeroV) {
439 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Invalid application version " << GfxInfoBase::GetApplicationVersion().get();
442 nsTArray<nsCString> keyValues;
443 ParseString(aBlacklistEntry, '\t', keyValues);
445 aDriverInfo.mRuleId = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_NO_ID");
447 for (uint32_t i = 0; i < keyValues.Length(); ++i) {
448 nsCString keyValue = keyValues[i];
449 nsTArray<nsCString> splitted;
450 ParseString(keyValue, ':', splitted);
451 if (splitted.Length() != 2) {
452 // If we don't recognize the input data, we do not want to proceed.
453 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized data " << keyValue.get();
454 return false;
456 nsCString key = splitted[0];
457 nsCString value = splitted[1];
458 NS_ConvertUTF8toUTF16 dataValue(value);
460 if (value.Length() == 0) {
461 // Safety check for empty values.
462 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Empty value for " << key.get();
463 return false;
466 if (key.EqualsLiteral("blockID")) {
467 nsCString blockIdStr = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_") + value;
468 aDriverInfo.mRuleId = blockIdStr.get();
469 } else if (key.EqualsLiteral("os")) {
470 aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
471 } else if (key.EqualsLiteral("osversion")) {
472 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
473 } else if (key.EqualsLiteral("vendor")) {
474 aDriverInfo.mAdapterVendor = dataValue;
475 } else if (key.EqualsLiteral("feature")) {
476 aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
477 if (aDriverInfo.mFeature < 0) {
478 // If we don't recognize the feature, we do not want to proceed.
479 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized feature " << value.get();
480 return false;
482 } else if (key.EqualsLiteral("featureStatus")) {
483 aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
484 } else if (key.EqualsLiteral("driverVersion")) {
485 uint64_t version;
486 if (ParseDriverVersion(dataValue, &version))
487 aDriverInfo.mDriverVersion = version;
488 } else if (key.EqualsLiteral("driverVersionMax")) {
489 uint64_t version;
490 if (ParseDriverVersion(dataValue, &version))
491 aDriverInfo.mDriverVersionMax = version;
492 } else if (key.EqualsLiteral("driverVersionComparator")) {
493 aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
494 } else if (key.EqualsLiteral("model")) {
495 aDriverInfo.mModel = dataValue;
496 } else if (key.EqualsLiteral("product")) {
497 aDriverInfo.mProduct = dataValue;
498 } else if (key.EqualsLiteral("manufacturer")) {
499 aDriverInfo.mManufacturer = dataValue;
500 } else if (key.EqualsLiteral("hardware")) {
501 aDriverInfo.mHardware = dataValue;
502 } else if (key.EqualsLiteral("versionRange")) {
503 nsTArray<nsCString> versionRange;
504 ParseString(value, ',', versionRange);
505 if (versionRange.Length() != 2) {
506 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized versionRange " << value.get();
507 return false;
509 nsCString minValue = versionRange[0];
510 nsCString maxValue = versionRange[1];
512 mozilla::Version minV(minValue.get());
513 mozilla::Version maxV(maxValue.get());
515 if (minV > zeroV && !(appV >= minV)) {
516 // The version of the application is less than the minimal version
517 // this blocklist entry applies to, so we can just ignore it by
518 // returning false and letting the caller deal with it.
519 return false;
521 if (maxV > zeroV && !(appV <= maxV)) {
522 // The version of the application is more than the maximal version
523 // this blocklist entry applies to, so we can just ignore it by
524 // returning false and letting the caller deal with it.
525 return false;
527 } else if (key.EqualsLiteral("devices")) {
528 nsTArray<nsCString> devices;
529 ParseString(value, ',', devices);
530 GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
531 if (deviceIds) {
532 // Get GfxDriverInfo to adopt the devices array we created.
533 aDriverInfo.mDeleteDevices = true;
534 aDriverInfo.mDevices = deviceIds;
537 // We explicitly ignore unknown elements.
540 return true;
543 static void
544 BlacklistEntriesToDriverInfo(nsTArray<nsCString>& aBlacklistEntries,
545 nsTArray<GfxDriverInfo>& aDriverInfo)
547 aDriverInfo.Clear();
548 aDriverInfo.SetLength(aBlacklistEntries.Length());
550 for (uint32_t i = 0; i < aBlacklistEntries.Length(); ++i) {
551 nsCString blacklistEntry = aBlacklistEntries[i];
552 GfxDriverInfo di;
553 if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
554 aDriverInfo[i] = di;
555 // Prevent di falling out of scope from destroying the devices.
556 di.mDeleteDevices = false;
561 NS_IMETHODIMP
562 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
563 const char16_t* aData)
565 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
566 nsTArray<GfxDriverInfo> driverInfo;
567 nsTArray<nsCString> blacklistEntries;
568 nsCString utf8Data = NS_ConvertUTF16toUTF8(aData);
569 if (utf8Data.Length() > 0) {
570 ParseString(utf8Data, '\n', blacklistEntries);
572 BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
573 EvaluateDownloadedBlacklist(driverInfo);
576 return NS_OK;
579 GfxInfoBase::GfxInfoBase()
580 : mMutex("GfxInfoBase")
584 GfxInfoBase::~GfxInfoBase()
588 nsresult
589 GfxInfoBase::Init()
591 InitGfxDriverInfoShutdownObserver();
592 gfxPrefs::GetSingleton();
593 MediaPrefs::GetSingleton();
595 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
596 if (os) {
597 os->AddObserver(this, "blocklist-data-gfxItems", true);
600 return NS_OK;
603 NS_IMETHODIMP
604 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId, int32_t* aStatus)
606 int32_t blocklistAll = gfxPrefs::BlocklistAll();
607 if (blocklistAll > 0) {
608 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Forcing blocklisting all features";
609 *aStatus = FEATURE_BLOCKED_DEVICE;
610 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
611 return NS_OK;
612 } else if (blocklistAll < 0) {
613 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Ignoring any feature blocklisting.";
614 *aStatus = FEATURE_STATUS_OK;
615 return NS_OK;
618 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
619 return NS_OK;
622 if (XRE_IsContentProcess()) {
623 // Use the cached data received from the parent process.
624 MOZ_ASSERT(mFeatureStatus);
625 bool success = false;
626 for (const auto& fs : *mFeatureStatus) {
627 if (fs.feature() == aFeature) {
628 aFailureId = fs.failureId();
629 *aStatus = fs.status();
630 success = true;
631 break;
634 return success ? NS_OK : NS_ERROR_FAILURE;
637 nsString version;
638 nsTArray<GfxDriverInfo> driverInfo;
639 nsresult rv = GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
640 return rv;
643 // Matching OS go somewhat beyond the simple equality check because of the
644 // "All Windows" and "All OS X" variations.
646 // aBlockedOS is describing the system(s) we are trying to block.
647 // aSystemOS is describing the system we are running on.
649 // aSystemOS should not be "Windows" or "OSX" - it should be set to
650 // a particular version instead.
651 // However, it is valid for aBlockedOS to be one of those generic values,
652 // as we could be blocking all of the versions.
653 inline bool
654 MatchingOperatingSystems(OperatingSystem aBlockedOS, OperatingSystem aSystemOS)
656 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
657 aSystemOS != OperatingSystem::OSX);
659 // If the block entry OS is unknown, it doesn't match
660 if (aBlockedOS == OperatingSystem::Unknown) {
661 return false;
664 #if defined (XP_WIN)
665 if (aBlockedOS == OperatingSystem::Windows) {
666 // We do want even "unknown" aSystemOS to fall under "all windows"
667 return true;
669 #endif
671 #if defined (XP_MACOSX)
672 if (aBlockedOS == OperatingSystem::OSX) {
673 // We do want even "unknown" aSystemOS to fall under "all OS X"
674 return true;
676 #endif
678 return aSystemOS == aBlockedOS;
681 int32_t
682 GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
683 nsAString& aSuggestedVersion,
684 int32_t aFeature,
685 nsACString& aFailureId,
686 OperatingSystem os)
688 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
690 // Get the adapters once then reuse below
691 nsAutoString adapterVendorID[2];
692 nsAutoString adapterDeviceID[2];
693 nsAutoString adapterDriverVersionString[2];
694 bool adapterInfoFailed[2];
696 adapterInfoFailed[0] = (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
697 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
698 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
699 adapterInfoFailed[1] = (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
700 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
701 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
702 // No point in going on if we don't have adapter info
703 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
704 return 0;
707 #if defined(XP_WIN) || defined(ANDROID)
708 uint64_t driverVersion[2] = {0, 0};
709 if (!adapterInfoFailed[0]) {
710 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
712 if (!adapterInfoFailed[1]) {
713 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
715 #endif
717 uint32_t i = 0;
718 for (; i < info.Length(); i++) {
719 // If we don't have the info for this GPU, no need to check further.
720 // It is unclear that we would ever have a mixture of 1st and 2nd
721 // GPU, but leaving the code in for that possibility for now.
722 // (Actually, currently mGpu2 will never be true, so this can
723 // be optimized out.)
724 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
725 if (adapterInfoFailed[infoIndex]) {
726 continue;
729 // Do the operating system check first, no point in getting the driver
730 // info if we won't need to use it.
731 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os)) {
732 continue;
735 if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
736 continue;
739 if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
740 !info[i].mAdapterVendor.Equals(adapterVendorID[infoIndex], nsCaseInsensitiveStringComparator())) {
741 continue;
744 if (info[i].mDevices != GfxDriverInfo::allDevices && info[i].mDevices->Length()) {
745 bool deviceMatches = false;
746 for (uint32_t j = 0; j < info[i].mDevices->Length(); j++) {
747 if ((*info[i].mDevices)[j].Equals(adapterDeviceID[infoIndex], nsCaseInsensitiveStringComparator())) {
748 deviceMatches = true;
749 break;
753 if (!deviceMatches) {
754 continue;
758 bool match = false;
760 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
761 continue;
763 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
764 continue;
766 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
767 continue;
769 if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) {
770 continue;
773 #if defined(XP_WIN) || defined(ANDROID)
774 switch (info[i].mComparisonOp) {
775 case DRIVER_LESS_THAN:
776 match = driverVersion[infoIndex] < info[i].mDriverVersion;
777 break;
778 case DRIVER_BUILD_ID_LESS_THAN:
779 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
780 break;
781 case DRIVER_LESS_THAN_OR_EQUAL:
782 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
783 break;
784 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
785 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
786 break;
787 case DRIVER_GREATER_THAN:
788 match = driverVersion[infoIndex] > info[i].mDriverVersion;
789 break;
790 case DRIVER_GREATER_THAN_OR_EQUAL:
791 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
792 break;
793 case DRIVER_EQUAL:
794 match = driverVersion[infoIndex] == info[i].mDriverVersion;
795 break;
796 case DRIVER_NOT_EQUAL:
797 match = driverVersion[infoIndex] != info[i].mDriverVersion;
798 break;
799 case DRIVER_BETWEEN_EXCLUSIVE:
800 match = driverVersion[infoIndex] > info[i].mDriverVersion && driverVersion[infoIndex] < info[i].mDriverVersionMax;
801 break;
802 case DRIVER_BETWEEN_INCLUSIVE:
803 match = driverVersion[infoIndex] >= info[i].mDriverVersion && driverVersion[infoIndex] <= info[i].mDriverVersionMax;
804 break;
805 case DRIVER_BETWEEN_INCLUSIVE_START:
806 match = driverVersion[infoIndex] >= info[i].mDriverVersion && driverVersion[infoIndex] < info[i].mDriverVersionMax;
807 break;
808 case DRIVER_COMPARISON_IGNORED:
809 // We don't have a comparison op, so we match everything.
810 match = true;
811 break;
812 default:
813 NS_WARNING("Bogus op in GfxDriverInfo");
814 break;
816 #else
817 // We don't care what driver version it was. We only check OS version and if
818 // the device matches.
819 match = true;
820 #endif
822 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
823 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
824 info[i].mFeature == aFeature)
826 status = info[i].mFeatureStatus;
827 if (!info[i].mRuleId.IsEmpty()) {
828 aFailureId = info[i].mRuleId.get();
829 } else {
830 aFailureId = "FEATURE_FAILURE_DL_BLACKLIST_NO_ID";
832 break;
837 #if defined(XP_WIN)
838 // As a very special case, we block D2D on machines with an NVidia 310M GPU
839 // as either the primary or secondary adapter. D2D is also blocked when the
840 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
841 // If the primary GPU already matched something in the blocklist then we
842 // ignore this special rule. See bug 1008759.
843 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
844 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
845 if (!adapterInfoFailed[1]) {
846 nsAString &nvVendorID = (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorNVIDIA);
847 const nsString nv310mDeviceId = NS_LITERAL_STRING("0x0A70");
848 if (nvVendorID.Equals(adapterVendorID[1], nsCaseInsensitiveStringComparator()) &&
849 nv310mDeviceId.Equals(adapterDeviceID[1], nsCaseInsensitiveStringComparator())) {
850 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
851 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
856 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
857 // back to the Windows handler, so we must handle this here.
858 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
859 if (info[i].mSuggestedVersion) {
860 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
861 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
862 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions)
864 aSuggestedVersion.AppendPrintf("%lld.%lld.%lld.%lld",
865 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
866 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
867 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
868 (info[i].mDriverVersion & 0x000000000000ffff));
871 #endif
873 return status;
876 void
877 GfxInfoBase::SetFeatureStatus(const nsTArray<dom::GfxInfoFeatureStatus>& aFS)
879 MOZ_ASSERT(!mFeatureStatus);
880 mFeatureStatus = new nsTArray<dom::GfxInfoFeatureStatus>(aFS);
883 nsresult
884 GfxInfoBase::GetFeatureStatusImpl(int32_t aFeature,
885 int32_t* aStatus,
886 nsAString& aSuggestedVersion,
887 const nsTArray<GfxDriverInfo>& aDriverInfo,
888 nsACString& aFailureId,
889 OperatingSystem* aOS /* = nullptr */)
891 if (aFeature <= 0) {
892 gfxWarning() << "Invalid feature <= 0";
893 return NS_OK;
896 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
897 // Terminate now with the status determined by the derived type (OS-specific
898 // code).
899 return NS_OK;
902 if (mShutdownOccurred) {
903 // This is futile; we've already commenced shutdown and our blocklists have
904 // been deleted. We may want to look into resurrecting the blocklist instead
905 // but for now, just don't even go there.
906 return NS_OK;
909 // If an operating system was provided by the derived GetFeatureStatusImpl,
910 // grab it here. Otherwise, the OS is unknown.
911 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
913 nsAutoString adapterVendorID;
914 nsAutoString adapterDeviceID;
915 nsAutoString adapterDriverVersionString;
916 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
917 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
918 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
920 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
921 *aStatus = FEATURE_BLOCKED_DEVICE;
922 return NS_OK;
925 // Check if the device is blocked from the downloaded blocklist. If not, check
926 // the static list after that. This order is used so that we can later escape
927 // out of static blocks (i.e. if we were wrong or something was patched, we
928 // can back out our static block without doing a release).
929 int32_t status;
930 if (aDriverInfo.Length()) {
931 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature, aFailureId, os);
932 } else {
933 if (!mDriverInfo) {
934 mDriverInfo = new nsTArray<GfxDriverInfo>();
936 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os);
939 // It's now done being processed. It's safe to set the status to STATUS_OK.
940 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
941 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
942 } else {
943 *aStatus = status;
946 return NS_OK;
949 NS_IMETHODIMP
950 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
951 nsAString& aVersion)
953 nsCString version;
954 if (GetPrefValueForDriverVersion(version)) {
955 aVersion = NS_ConvertASCIItoUTF16(version);
956 return NS_OK;
959 int32_t status;
960 nsCString discardFailureId;
961 nsTArray<GfxDriverInfo> driverInfo;
962 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo, discardFailureId);
966 NS_IMETHODIMP
967 GfxInfoBase::GetWebGLParameter(const nsAString& aParam,
968 nsAString& aResult)
970 return GfxInfoWebGL::GetWebGLParameter(aParam, aResult);
973 void
974 GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo)
976 int32_t features[] = {
977 nsIGfxInfo::FEATURE_DIRECT2D,
978 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
979 nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
980 nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
981 nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
982 nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
983 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
984 nsIGfxInfo::FEATURE_OPENGL_LAYERS,
985 nsIGfxInfo::FEATURE_WEBGL_OPENGL,
986 nsIGfxInfo::FEATURE_WEBGL_ANGLE,
987 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
988 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
989 nsIGfxInfo::FEATURE_WEBGL_MSAA,
990 nsIGfxInfo::FEATURE_STAGEFRIGHT,
991 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION,
992 nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
993 nsIGfxInfo::FEATURE_WEBGL2,
994 nsIGfxInfo::FEATURE_ADVANCED_LAYERS,
995 nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX,
999 // For every feature we know about, we evaluate whether this blacklist has a
1000 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1001 // GetFeatureStatus above, so we don't need to hold on to this blacklist
1002 // anywhere permanent.
1003 int i = 0;
1004 while (features[i]) {
1005 int32_t status;
1006 nsCString failureId;
1007 nsAutoString suggestedVersion;
1008 if (NS_SUCCEEDED(GetFeatureStatusImpl(features[i], &status,
1009 suggestedVersion,
1010 aDriverInfo,
1011 failureId))) {
1012 switch (status) {
1013 default:
1014 case nsIGfxInfo::FEATURE_STATUS_OK:
1015 RemovePrefForFeature(features[i]);
1016 break;
1018 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1019 if (!suggestedVersion.IsEmpty()) {
1020 SetPrefValueForDriverVersion(suggestedVersion);
1021 } else {
1022 RemovePrefForDriverVersion();
1024 MOZ_FALLTHROUGH;
1026 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1027 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1028 case nsIGfxInfo::FEATURE_DISCOURAGED:
1029 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1030 SetPrefValueForFeature(features[i], status, failureId);
1031 break;
1035 ++i;
1039 NS_IMETHODIMP_(void)
1040 GfxInfoBase::LogFailure(const nsACString &failure)
1042 // gfxCriticalError has a mutex lock of its own, so we may not actually
1043 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1044 // will not return the copy of the logs unless it can get the same lock
1045 // that gfxCriticalError uses. Still, that is so much of an implementation
1046 // detail that it's nicer to just add an extra lock here and in ::GetFailures()
1047 MutexAutoLock lock(mMutex);
1049 // By default, gfxCriticalError asserts; make it not assert in this case.
1050 gfxCriticalError(CriticalLog::DefaultOptions(false)) << "(LF) " << failure.BeginReading();
1053 /* XPConnect method of returning arrays is very ugly. Would not recommend. */
1054 NS_IMETHODIMP GfxInfoBase::GetFailures(uint32_t* failureCount,
1055 int32_t** indices,
1056 char ***failures)
1058 MutexAutoLock lock(mMutex);
1060 NS_ENSURE_ARG_POINTER(failureCount);
1061 NS_ENSURE_ARG_POINTER(failures);
1063 *failures = nullptr;
1064 *failureCount = 0;
1066 // indices is "allowed" to be null, the caller may not care about them,
1067 // although calling from JS doesn't seem to get us there.
1068 if (indices) *indices = nullptr;
1070 LogForwarder* logForwarder = Factory::GetLogForwarder();
1071 if (!logForwarder) {
1072 return NS_ERROR_UNEXPECTED;
1075 // There are two stirng copies in this method, starting with this one. We are
1076 // assuming this is not a big deal, as the size of the array should be small
1077 // and the strings in it should be small as well (the error messages in the
1078 // code.) The second copy happens with the Clone() calls. Technically,
1079 // we don't need the mutex lock after the StringVectorCopy() call.
1080 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1081 *failureCount = loggedStrings.size();
1083 if (*failureCount != 0) {
1084 *failures = (char**)moz_xmalloc(*failureCount * sizeof(char*));
1085 if (!(*failures)) {
1086 return NS_ERROR_OUT_OF_MEMORY;
1088 if (indices) {
1089 *indices = (int32_t*)moz_xmalloc(*failureCount * sizeof(int32_t));
1090 if (!(*indices)) {
1091 free(*failures);
1092 *failures = nullptr;
1093 return NS_ERROR_OUT_OF_MEMORY;
1097 /* copy over the failure messages into the array we just allocated */
1098 LoggingRecord::const_iterator it;
1099 uint32_t i=0;
1100 for(it = loggedStrings.begin() ; it != loggedStrings.end(); ++it, i++) {
1101 (*failures)[i] = (char*)nsMemory::Clone(Get<1>(*it).c_str(), Get<1>(*it).size() + 1);
1102 if (indices) (*indices)[i] = Get<0>(*it);
1104 if (!(*failures)[i]) {
1105 /* <sarcasm> I'm too afraid to use an inline function... </sarcasm> */
1106 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, (*failures));
1107 *failureCount = i;
1108 return NS_ERROR_OUT_OF_MEMORY;
1113 return NS_OK;
1116 nsTArray<GfxInfoCollectorBase*> *sCollectors;
1118 static void
1119 InitCollectors()
1121 if (!sCollectors)
1122 sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1125 nsresult GfxInfoBase::GetInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
1127 InitCollectors();
1128 InfoObject obj(aCx);
1130 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1131 (*sCollectors)[i]->GetInfo(obj);
1134 // Some example property definitions
1135 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1136 // obj.DefineProperty("renderer", mRendererIDsString);
1137 // obj.DefineProperty("five", 5);
1139 if (!obj.mOk) {
1140 return NS_ERROR_FAILURE;
1143 aResult.setObject(*obj.mObj);
1144 return NS_OK;
1147 nsAutoCString gBaseAppVersion;
1149 const nsCString&
1150 GfxInfoBase::GetApplicationVersion()
1152 static bool versionInitialized = false;
1153 if (!versionInitialized) {
1154 // If we fail to get the version, we will not try again.
1155 versionInitialized = true;
1157 // Get the version from xpcom/system/nsIXULAppInfo.idl
1158 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1159 if (app) {
1160 app->GetVersion(gBaseAppVersion);
1163 return gBaseAppVersion;
1166 void
1167 GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector)
1169 InitCollectors();
1170 sCollectors->AppendElement(collector);
1173 void
1174 GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector)
1176 InitCollectors();
1177 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1178 if ((*sCollectors)[i] == collector) {
1179 sCollectors->RemoveElementAt(i);
1180 break;
1183 if (sCollectors->IsEmpty()) {
1184 delete sCollectors;
1185 sCollectors = nullptr;
1189 NS_IMETHODIMP
1190 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult)
1192 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
1194 nsresult rv = FindMonitors(aCx, array);
1195 if (NS_FAILED(rv)) {
1196 return rv;
1199 aResult.setObject(*array);
1200 return NS_OK;
1203 static const char*
1204 GetLayersBackendName(layers::LayersBackend aBackend)
1206 switch (aBackend) {
1207 case layers::LayersBackend::LAYERS_NONE:
1208 return "none";
1209 case layers::LayersBackend::LAYERS_OPENGL:
1210 return "opengl";
1211 case layers::LayersBackend::LAYERS_D3D11:
1212 return "d3d11";
1213 case layers::LayersBackend::LAYERS_CLIENT:
1214 return "client";
1215 case layers::LayersBackend::LAYERS_WR:
1216 return "webrender";
1217 case layers::LayersBackend::LAYERS_BASIC:
1218 return "basic";
1219 default:
1220 MOZ_ASSERT_UNREACHABLE("unknown layers backend");
1221 return "unknown";
1225 static inline bool
1226 SetJSPropertyString(JSContext* aCx, JS::Handle<JSObject*> aObj,
1227 const char* aProp, const char* aString)
1229 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1230 if (!str) {
1231 return false;
1234 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1235 return JS_SetProperty(aCx, aObj, aProp, val);
1238 template <typename T>
1239 static inline bool
1240 AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj, const T& aValue)
1242 uint32_t index;
1243 if (!JS_GetArrayLength(aCx, aObj, &index)) {
1244 return false;
1246 return JS_SetElement(aCx, aObj, index, aValue);
1249 nsresult
1250 GfxInfoBase::GetFeatures(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
1252 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1253 if (!obj) {
1254 return NS_ERROR_OUT_OF_MEMORY;
1256 aOut.setObject(*obj);
1258 layers::LayersBackend backend = gfxPlatform::Initialized()
1259 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1260 : layers::LayersBackend::LAYERS_NONE;
1261 const char* backendName = GetLayersBackendName(backend);
1262 SetJSPropertyString(aCx, obj, "compositor", backendName);
1264 // If graphics isn't initialized yet, just stop now.
1265 if (!gfxPlatform::Initialized()) {
1266 return NS_OK;
1269 DescribeFeatures(aCx, obj);
1270 return NS_OK;
1273 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
1275 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1276 if (!containerObj) {
1277 return NS_ERROR_OUT_OF_MEMORY;
1279 aOut.setObject(*containerObj);
1281 JS::Rooted<JSObject*> featureArray(aCx, JS_NewArrayObject(aCx, 0));
1282 if (!featureArray) {
1283 return NS_ERROR_OUT_OF_MEMORY;
1286 // Collect features.
1287 gfxConfig::ForEachFeature([&](const char* aName,
1288 const char* aDescription,
1289 FeatureState& aFeature) -> void {
1290 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1291 if (!obj) {
1292 return;
1294 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1295 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1296 !SetJSPropertyString(aCx, obj, "status", FeatureStatusToString(aFeature.GetValue())))
1298 return;
1301 JS::Rooted<JS::Value> log(aCx);
1302 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1303 return;
1305 if (!JS_SetProperty(aCx, obj, "log", log)) {
1306 return;
1309 if (!AppendJSElement(aCx, featureArray, obj)) {
1310 return;
1314 JS::Rooted<JSObject*> fallbackArray(aCx, JS_NewArrayObject(aCx, 0));
1315 if (!fallbackArray) {
1316 return NS_ERROR_OUT_OF_MEMORY;
1319 // Collect fallbacks.
1320 gfxConfig::ForEachFallback([&](const char* aName, const char* aMessage) -> void {
1321 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1322 if (!obj) {
1323 return;
1326 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1327 !SetJSPropertyString(aCx, obj, "message", aMessage))
1329 return;
1332 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1333 return;
1337 JS::Rooted<JS::Value> val(aCx);
1339 val = JS::ObjectValue(*featureArray);
1340 JS_SetProperty(aCx, containerObj, "features", val);
1342 val = JS::ObjectValue(*fallbackArray);
1343 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1345 return NS_OK;
1348 bool
1349 GfxInfoBase::BuildFeatureStateLog(JSContext* aCx, const FeatureState& aFeature,
1350 JS::MutableHandle<JS::Value> aOut)
1352 JS::Rooted<JSObject*> log(aCx, JS_NewArrayObject(aCx, 0));
1353 if (!log) {
1354 return false;
1356 aOut.setObject(*log);
1358 aFeature.ForEachStatusChange([&](const char* aType,
1359 FeatureStatus aStatus,
1360 const char* aMessage) -> void {
1361 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1362 if (!obj) {
1363 return;
1366 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1367 !SetJSPropertyString(aCx, obj, "status", FeatureStatusToString(aStatus)) ||
1368 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage)))
1370 return;
1373 if (!AppendJSElement(aCx, log, obj)) {
1374 return;
1378 return true;
1381 void
1382 GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj)
1384 JS::Rooted<JSObject*> obj(aCx);
1386 gfx::FeatureStatus gpuProcess = gfxConfig::GetValue(Feature::GPU_PROCESS);
1387 InitFeatureObject(aCx, aObj, "gpuProcess", FEATURE_GPU_PROCESS, Some(gpuProcess), &obj);
1389 // Only include AL if the platform attempted to use it.
1390 gfx::FeatureStatus advancedLayers = gfxConfig::GetValue(Feature::ADVANCED_LAYERS);
1391 if (advancedLayers != FeatureStatus::Unused) {
1392 InitFeatureObject(aCx, aObj, "advancedLayers", FEATURE_ADVANCED_LAYERS,
1393 Some(advancedLayers), &obj);
1395 if (gfxConfig::UseFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING)) {
1396 JS::Rooted<JS::Value> trueVal(aCx, JS::BooleanValue(true));
1397 JS_SetProperty(aCx, obj, "noConstantBufferOffsetting", trueVal);
1402 bool
1403 GfxInfoBase::InitFeatureObject(JSContext* aCx,
1404 JS::Handle<JSObject*> aContainer,
1405 const char* aName,
1406 int32_t aFeature,
1407 const Maybe<mozilla::gfx::FeatureStatus>& aFeatureStatus,
1408 JS::MutableHandle<JSObject*> aOutObj)
1410 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1411 if (!obj) {
1412 return false;
1415 nsCString failureId = NS_LITERAL_CSTRING("OK");
1416 int32_t unused;
1417 if (!NS_SUCCEEDED(GetFeatureStatus(aFeature, failureId, &unused))) {
1418 return false;
1421 // Set "status".
1422 if (aFeatureStatus) {
1423 const char* status = FeatureStatusToString(aFeatureStatus.value());
1425 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status));
1426 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1427 JS_SetProperty(aCx, obj, "status", val);
1430 // Add the feature object to the container.
1432 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1433 JS_SetProperty(aCx, aContainer, aName, val);
1436 aOutObj.set(obj);
1437 return true;
1440 nsresult
1441 GfxInfoBase::GetActiveCrashGuards(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
1443 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
1444 if (!array) {
1445 return NS_ERROR_OUT_OF_MEMORY;
1447 aOut.setObject(*array);
1449 DriverCrashGuard::ForEachActiveCrashGuard([&](const char* aName,
1450 const char* aPrefName) -> void {
1451 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1452 if (!obj) {
1453 return;
1455 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1456 return;
1458 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1459 return;
1461 if (!AppendJSElement(aCx, array, obj)) {
1462 return;
1466 return NS_OK;
1469 NS_IMETHODIMP
1470 GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled)
1472 *aWebRenderEnabled = gfxVars::UseWebRender();
1473 return NS_OK;
1476 NS_IMETHODIMP
1477 GfxInfoBase::GetOffMainThreadPaintEnabled(bool* aOffMainThreadPaintEnabled)
1479 *aOffMainThreadPaintEnabled = gfxConfig::IsEnabled(Feature::OMTP);
1480 return NS_OK;
1483 NS_IMETHODIMP
1484 GfxInfoBase::GetIsHeadless(bool* aIsHeadless)
1486 *aIsHeadless = gfxPlatform::IsHeadless();
1487 return NS_OK;
1490 NS_IMETHODIMP
1491 GfxInfoBase::GetContentBackend(nsAString & aContentBackend)
1493 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1494 nsString outStr;
1496 switch (backend) {
1497 case BackendType::DIRECT2D1_1: {
1498 outStr.AppendPrintf("Direct2D 1.1");
1499 break;
1501 case BackendType::SKIA: {
1502 outStr.AppendPrintf("Skia");
1503 break;
1505 case BackendType::CAIRO: {
1506 outStr.AppendPrintf("Cairo");
1507 break;
1509 default:
1510 return NS_ERROR_FAILURE;
1513 aContentBackend.Assign(outStr);
1514 return NS_OK;
1517 NS_IMETHODIMP
1518 GfxInfoBase::GetUsingGPUProcess(bool *aOutValue)
1520 GPUProcessManager* gpu = GPUProcessManager::Get();
1521 if (!gpu) {
1522 // Not supported in content processes.
1523 return NS_ERROR_FAILURE;
1526 *aOutValue = !!gpu->GetGPUChild();
1527 return NS_OK;
1530 NS_IMETHODIMP
1531 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool *_retval)
1533 gfxPlatform::GetPlatform();
1535 GPUProcessManager* gpm = GPUProcessManager::Get();
1536 if (aEnable) {
1537 if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
1538 gfxConfig::UserForceEnable(Feature::GPU_PROCESS, "xpcshell-test");
1540 gpm->LaunchGPUProcess();
1541 gpm->EnsureGPUReady();
1542 } else {
1543 gpm->KillProcess();
1546 *_retval = true;
1547 return NS_OK;
1550 GfxInfoCollectorBase::GfxInfoCollectorBase()
1552 GfxInfoBase::AddCollector(this);
1555 GfxInfoCollectorBase::~GfxInfoCollectorBase()
1557 GfxInfoBase::RemoveCollector(this);