Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / widget / GfxInfoBase.cpp
blob553337fdea86ed0f761b6b4b2dedaf2ccddeaca8
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 "nsAutoPtr.h"
17 #include "nsString.h"
18 #include "nsUnicharUtils.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 "mozilla/Preferences.h"
30 #include "mozilla/dom/ContentChild.h"
31 #include "mozilla/gfx/2D.h"
32 #include "mozilla/gfx/Logging.h"
34 using namespace mozilla::widget;
35 using namespace mozilla;
36 using mozilla::MutexAutoLock;
38 nsTArray<GfxDriverInfo>* GfxInfoBase::mDriverInfo;
39 bool GfxInfoBase::mDriverInfoObserverInitialized;
41 // Observes for shutdown so that the child GfxDriverInfo list is freed.
42 class ShutdownObserver : public nsIObserver
44 virtual ~ShutdownObserver() {}
46 public:
47 ShutdownObserver() {}
49 NS_DECL_ISUPPORTS
51 NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
52 const char16_t *aData) override
54 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
56 delete GfxInfoBase::mDriverInfo;
57 GfxInfoBase::mDriverInfo = nullptr;
59 for (uint32_t i = 0; i < DeviceFamilyMax; i++)
60 delete GfxDriverInfo::mDeviceFamilies[i];
62 for (uint32_t i = 0; i < DeviceVendorMax; i++)
63 delete GfxDriverInfo::mDeviceVendors[i];
65 return NS_OK;
69 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
71 void InitGfxDriverInfoShutdownObserver()
73 if (GfxInfoBase::mDriverInfoObserverInitialized)
74 return;
76 GfxInfoBase::mDriverInfoObserverInitialized = true;
78 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
79 if (!observerService) {
80 NS_WARNING("Could not get observer service!");
81 return;
84 ShutdownObserver *obs = new ShutdownObserver();
85 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
88 using namespace mozilla::widget;
89 using namespace mozilla::gfx;
90 using namespace mozilla;
92 #ifdef XP_MACOSX
93 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIGfxInfo2, nsIObserver, nsISupportsWeakReference)
94 #else
95 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference)
96 #endif
98 #define BLACKLIST_PREF_BRANCH "gfx.blacklist."
99 #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version"
100 #define BLACKLIST_ENTRY_TAG_NAME "gfxBlacklistEntry"
102 static const char*
103 GetPrefNameForFeature(int32_t aFeature)
105 const char* name = nullptr;
106 switch(aFeature) {
107 case nsIGfxInfo::FEATURE_DIRECT2D:
108 name = BLACKLIST_PREF_BRANCH "direct2d";
109 break;
110 case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
111 name = BLACKLIST_PREF_BRANCH "layers.direct3d9";
112 break;
113 case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
114 name = BLACKLIST_PREF_BRANCH "layers.direct3d10";
115 break;
116 case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
117 name = BLACKLIST_PREF_BRANCH "layers.direct3d10-1";
118 break;
119 case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
120 name = BLACKLIST_PREF_BRANCH "layers.direct3d11";
121 break;
122 case nsIGfxInfo::FEATURE_DXVA:
123 name = BLACKLIST_PREF_BRANCH "dxva";
124 break;
125 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
126 name = BLACKLIST_PREF_BRANCH "direct3d11angle";
127 break;
128 case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
129 name = BLACKLIST_PREF_BRANCH "layers.opengl";
130 break;
131 case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
132 name = BLACKLIST_PREF_BRANCH "webgl.opengl";
133 break;
134 case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
135 name = BLACKLIST_PREF_BRANCH "webgl.angle";
136 break;
137 case nsIGfxInfo::FEATURE_WEBGL_MSAA:
138 name = BLACKLIST_PREF_BRANCH "webgl.msaa";
139 break;
140 case nsIGfxInfo::FEATURE_STAGEFRIGHT:
141 name = BLACKLIST_PREF_BRANCH "stagefright";
142 break;
143 default:
144 break;
147 return name;
150 // Returns the value of the pref for the relevant feature in aValue.
151 // If the pref doesn't exist, aValue is not touched, and returns false.
152 static bool
153 GetPrefValueForFeature(int32_t aFeature, int32_t& aValue)
155 const char *prefname = GetPrefNameForFeature(aFeature);
156 if (!prefname)
157 return false;
159 aValue = false;
160 return NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue));
163 static void
164 SetPrefValueForFeature(int32_t aFeature, int32_t aValue)
166 const char *prefname = GetPrefNameForFeature(aFeature);
167 if (!prefname)
168 return;
170 Preferences::SetInt(prefname, aValue);
173 static void
174 RemovePrefForFeature(int32_t aFeature)
176 const char *prefname = GetPrefNameForFeature(aFeature);
177 if (!prefname)
178 return;
180 Preferences::ClearUser(prefname);
183 static bool
184 GetPrefValueForDriverVersion(nsCString& aVersion)
186 return NS_SUCCEEDED(Preferences::GetCString(SUGGESTED_VERSION_PREF,
187 &aVersion));
190 static void
191 SetPrefValueForDriverVersion(const nsAString& aVersion)
193 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
196 static void
197 RemovePrefForDriverVersion()
199 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
202 // <foo>Hello</foo> - "Hello" is stored as a child text node of the foo node.
203 static bool
204 BlacklistNodeToTextValue(nsIDOMNode *aBlacklistNode, nsAString& aValue)
206 nsAutoString value;
207 if (NS_FAILED(aBlacklistNode->GetTextContent(value)))
208 return false;
210 value.Trim(" \t\r\n");
211 aValue = value;
213 return true;
216 static OperatingSystem
217 BlacklistOSToOperatingSystem(const nsAString& os)
219 if (os.EqualsLiteral("WINNT 5.1"))
220 return DRIVER_OS_WINDOWS_XP;
221 else if (os.EqualsLiteral("WINNT 5.2"))
222 return DRIVER_OS_WINDOWS_SERVER_2003;
223 else if (os.EqualsLiteral("WINNT 6.0"))
224 return DRIVER_OS_WINDOWS_VISTA;
225 else if (os.EqualsLiteral("WINNT 6.1"))
226 return DRIVER_OS_WINDOWS_7;
227 else if (os.EqualsLiteral("WINNT 6.2"))
228 return DRIVER_OS_WINDOWS_8;
229 else if (os.EqualsLiteral("WINNT 6.3"))
230 return DRIVER_OS_WINDOWS_8_1;
231 else if (os.EqualsLiteral("Linux"))
232 return DRIVER_OS_LINUX;
233 else if (os.EqualsLiteral("Darwin 9"))
234 return DRIVER_OS_OS_X_10_5;
235 else if (os.EqualsLiteral("Darwin 10"))
236 return DRIVER_OS_OS_X_10_6;
237 else if (os.EqualsLiteral("Darwin 11"))
238 return DRIVER_OS_OS_X_10_7;
239 else if (os.EqualsLiteral("Darwin 12"))
240 return DRIVER_OS_OS_X_10_8;
241 else if (os.EqualsLiteral("Darwin 13"))
242 return DRIVER_OS_OS_X_10_9;
243 else if (os.EqualsLiteral("Darwin 14"))
244 return DRIVER_OS_OS_X_10_10;
245 else if (os.EqualsLiteral("Android"))
246 return DRIVER_OS_ANDROID;
247 else if (os.EqualsLiteral("All"))
248 return DRIVER_OS_ALL;
250 return DRIVER_OS_UNKNOWN;
253 static GfxDeviceFamily*
254 BlacklistDevicesToDeviceFamily(nsIDOMHTMLCollection* aDevices)
256 uint32_t length;
257 if (NS_FAILED(aDevices->GetLength(&length)))
258 return nullptr;
260 // For each <device>, get its device ID, and return a freshly-allocated
261 // GfxDeviceFamily with the contents of that array.
262 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
264 for (uint32_t i = 0; i < length; ++i) {
265 nsCOMPtr<nsIDOMNode> node;
266 if (NS_FAILED(aDevices->Item(i, getter_AddRefs(node))) || !node)
267 continue;
269 nsAutoString deviceValue;
270 if (!BlacklistNodeToTextValue(node, deviceValue))
271 continue;
273 deviceIds->AppendElement(deviceValue);
276 return deviceIds;
279 static int32_t
280 BlacklistFeatureToGfxFeature(const nsAString& aFeature)
282 if (aFeature.EqualsLiteral("DIRECT2D"))
283 return nsIGfxInfo::FEATURE_DIRECT2D;
284 else if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS"))
285 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
286 else if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS"))
287 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
288 else if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS"))
289 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
290 else if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS"))
291 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
292 else if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE"))
293 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
294 else if (aFeature.EqualsLiteral("DXVA"))
295 return nsIGfxInfo::FEATURE_DXVA;
296 else if (aFeature.EqualsLiteral("OPENGL_LAYERS"))
297 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
298 else if (aFeature.EqualsLiteral("WEBGL_OPENGL"))
299 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
300 else if (aFeature.EqualsLiteral("WEBGL_ANGLE"))
301 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
302 else if (aFeature.EqualsLiteral("WEBGL_MSAA"))
303 return nsIGfxInfo::FEATURE_WEBGL_MSAA;
304 else if (aFeature.EqualsLiteral("STAGEFRIGHT"))
305 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
306 return 0;
309 static int32_t
310 BlacklistFeatureStatusToGfxFeatureStatus(const nsAString& aStatus)
312 if (aStatus.EqualsLiteral("STATUS_OK"))
313 return nsIGfxInfo::FEATURE_STATUS_OK;
314 else if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION"))
315 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
316 else if (aStatus.EqualsLiteral("BLOCKED_DEVICE"))
317 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
318 else if (aStatus.EqualsLiteral("DISCOURAGED"))
319 return nsIGfxInfo::FEATURE_DISCOURAGED;
320 else if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION"))
321 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
323 // Do not allow it to set STATUS_UNKNOWN.
325 return nsIGfxInfo::FEATURE_STATUS_OK;
328 static VersionComparisonOp
329 BlacklistComparatorToComparisonOp(const nsAString& op)
331 if (op.EqualsLiteral("LESS_THAN"))
332 return DRIVER_LESS_THAN;
333 else if (op.EqualsLiteral("LESS_THAN_OR_EQUAL"))
334 return DRIVER_LESS_THAN_OR_EQUAL;
335 else if (op.EqualsLiteral("GREATER_THAN"))
336 return DRIVER_GREATER_THAN;
337 else if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL"))
338 return DRIVER_GREATER_THAN_OR_EQUAL;
339 else if (op.EqualsLiteral("EQUAL"))
340 return DRIVER_EQUAL;
341 else if (op.EqualsLiteral("NOT_EQUAL"))
342 return DRIVER_NOT_EQUAL;
343 else if (op.EqualsLiteral("BETWEEN_EXCLUSIVE"))
344 return DRIVER_BETWEEN_EXCLUSIVE;
345 else if (op.EqualsLiteral("BETWEEN_INCLUSIVE"))
346 return DRIVER_BETWEEN_INCLUSIVE;
347 else if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START"))
348 return DRIVER_BETWEEN_INCLUSIVE_START;
350 return DRIVER_COMPARISON_IGNORED;
353 // Arbitrarily returns the first |tagname| child of |element|.
354 static bool
355 BlacklistNodeGetChildByName(nsIDOMElement *element,
356 const nsAString& tagname,
357 nsIDOMNode** firstchild)
359 nsCOMPtr<nsIDOMHTMLCollection> nodelist;
360 if (NS_FAILED(element->GetElementsByTagName(tagname,
361 getter_AddRefs(nodelist))) ||
362 !nodelist) {
363 return false;
366 nsCOMPtr<nsIDOMNode> node;
367 if (NS_FAILED(nodelist->Item(0, getter_AddRefs(node))) || !node)
368 return false;
370 node.forget(firstchild);
371 return true;
376 <gfxBlacklistEntry>
377 <os>WINNT 6.0</os>
378 <vendor>0x8086</vendor>
379 <devices>
380 <device>0x2582</device>
381 <device>0x2782</device>
382 </devices>
383 <feature> DIRECT3D_10_LAYERS </feature>
384 <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
385 <driverVersion> 8.52.322.2202 </driverVersion>
386 <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
387 </gfxBlacklistEntry>
390 static bool
391 BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
392 GfxDriverInfo& aDriverInfo)
394 nsAutoString nodename;
395 if (NS_FAILED(aBlacklistEntry->GetNodeName(nodename)) ||
396 nodename != NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME)) {
397 return false;
400 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aBlacklistEntry);
401 if (!element)
402 return false;
404 nsCOMPtr<nsIDOMNode> dataNode;
405 nsAutoString dataValue;
407 // <os>WINNT 6.0</os>
408 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("os"),
409 getter_AddRefs(dataNode))) {
410 BlacklistNodeToTextValue(dataNode, dataValue);
411 aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
414 // <osversion>14</osversion> currently only used for Android
415 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("osversion"),
416 getter_AddRefs(dataNode))) {
417 BlacklistNodeToTextValue(dataNode, dataValue);
418 aDriverInfo.mOperatingSystemVersion = strtoul(NS_LossyConvertUTF16toASCII(dataValue).get(),
419 nullptr, 10);
422 // <vendor>0x8086</vendor>
423 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("vendor"),
424 getter_AddRefs(dataNode))) {
425 BlacklistNodeToTextValue(dataNode, dataValue);
426 aDriverInfo.mAdapterVendor = dataValue;
429 // <devices>
430 // <device>0x2582</device>
431 // <device>0x2782</device>
432 // </devices>
433 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("devices"),
434 getter_AddRefs(dataNode))) {
435 nsCOMPtr<nsIDOMElement> devicesElement = do_QueryInterface(dataNode);
436 if (devicesElement) {
438 // Get only the <device> nodes, because BlacklistDevicesToDeviceFamily
439 // assumes it is passed no other nodes.
440 nsCOMPtr<nsIDOMHTMLCollection> devices;
441 if (NS_SUCCEEDED(devicesElement->GetElementsByTagName(NS_LITERAL_STRING("device"),
442 getter_AddRefs(devices)))) {
443 GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
444 if (deviceIds) {
445 // Get GfxDriverInfo to adopt the devices array we created.
446 aDriverInfo.mDeleteDevices = true;
447 aDriverInfo.mDevices = deviceIds;
453 // <feature> DIRECT3D_10_LAYERS </feature>
454 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("feature"),
455 getter_AddRefs(dataNode))) {
456 BlacklistNodeToTextValue(dataNode, dataValue);
457 aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
460 // <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
461 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("featureStatus"),
462 getter_AddRefs(dataNode))) {
463 BlacklistNodeToTextValue(dataNode, dataValue);
464 aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
467 // <driverVersion> 8.52.322.2202 </driverVersion>
468 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersion"),
469 getter_AddRefs(dataNode))) {
470 BlacklistNodeToTextValue(dataNode, dataValue);
471 uint64_t version;
472 if (ParseDriverVersion(dataValue, &version))
473 aDriverInfo.mDriverVersion = version;
476 // <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
477 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersionComparator"),
478 getter_AddRefs(dataNode))) {
479 BlacklistNodeToTextValue(dataNode, dataValue);
480 aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
483 // <model>foo</model>
484 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("model"),
485 getter_AddRefs(dataNode))) {
486 BlacklistNodeToTextValue(dataNode, dataValue);
487 aDriverInfo.mModel = dataValue;
489 // <product>foo</product>
490 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("product"),
491 getter_AddRefs(dataNode))) {
492 BlacklistNodeToTextValue(dataNode, dataValue);
493 aDriverInfo.mProduct = dataValue;
495 // <manufacturer>foo</manufacturer>
496 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("manufacturer"),
497 getter_AddRefs(dataNode))) {
498 BlacklistNodeToTextValue(dataNode, dataValue);
499 aDriverInfo.mManufacturer = dataValue;
501 // <hardware>foo</hardware>
502 if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("hardware"),
503 getter_AddRefs(dataNode))) {
504 BlacklistNodeToTextValue(dataNode, dataValue);
505 aDriverInfo.mHardware = dataValue;
508 // We explicitly ignore unknown elements.
510 return true;
513 static void
514 BlacklistEntriesToDriverInfo(nsIDOMHTMLCollection* aBlacklistEntries,
515 nsTArray<GfxDriverInfo>& aDriverInfo)
517 uint32_t length;
518 if (NS_FAILED(aBlacklistEntries->GetLength(&length)))
519 return;
521 aDriverInfo.Clear();
522 aDriverInfo.SetLength(length);
523 for (uint32_t i = 0; i < length; ++i) {
524 nsCOMPtr<nsIDOMNode> blacklistEntry;
525 if (NS_SUCCEEDED(aBlacklistEntries->Item(i,
526 getter_AddRefs(blacklistEntry))) &&
527 blacklistEntry) {
528 GfxDriverInfo di;
529 if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
530 aDriverInfo[i] = di;
532 // Prevent di falling out of scope from destroying the devices.
533 di.mDeleteDevices = false;
538 NS_IMETHODIMP
539 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
540 const char16_t* aData)
542 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
543 nsCOMPtr<nsIDOMElement> gfxItems = do_QueryInterface(aSubject);
544 if (gfxItems) {
545 nsCOMPtr<nsIDOMHTMLCollection> blacklistEntries;
546 if (NS_SUCCEEDED(gfxItems->
547 GetElementsByTagName(NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME),
548 getter_AddRefs(blacklistEntries))) &&
549 blacklistEntries)
551 nsTArray<GfxDriverInfo> driverInfo;
552 BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
553 EvaluateDownloadedBlacklist(driverInfo);
558 return NS_OK;
561 GfxInfoBase::GfxInfoBase()
562 : mMutex("GfxInfoBase")
566 GfxInfoBase::~GfxInfoBase()
570 nsresult
571 GfxInfoBase::Init()
573 InitGfxDriverInfoShutdownObserver();
575 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
576 if (os) {
577 os->AddObserver(this, "blocklist-data-gfxItems", true);
580 return NS_OK;
583 NS_IMETHODIMP
584 GfxInfoBase::GetFeatureStatus(int32_t aFeature, int32_t* aStatus)
586 if (GetPrefValueForFeature(aFeature, *aStatus))
587 return NS_OK;
589 if (XRE_GetProcessType() == GoannaProcessType_Content) {
590 // Delegate to the parent process.
591 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
592 bool success;
593 cc->SendGetGraphicsFeatureStatus(aFeature, aStatus, &success);
594 return success ? NS_OK : NS_ERROR_FAILURE;
597 nsString version;
598 nsTArray<GfxDriverInfo> driverInfo;
599 return GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo);
602 int32_t
603 GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
604 nsAString& aSuggestedVersion,
605 int32_t aFeature,
606 OperatingSystem os)
608 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
610 uint32_t i = 0;
611 for (; i < info.Length(); i++) {
612 // XXX: it would be better not to do this everytime round the loop
613 nsAutoString adapterVendorID;
614 nsAutoString adapterDeviceID;
615 nsAutoString adapterDriverVersionString;
616 if (info[i].mGpu2) {
617 if (NS_FAILED(GetAdapterVendorID2(adapterVendorID)) ||
618 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID)) ||
619 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString)))
621 return 0;
623 } else {
624 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
625 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
626 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
628 return 0;
632 #if defined(XP_WIN) || defined(ANDROID)
633 uint64_t driverVersion;
634 ParseDriverVersion(adapterDriverVersionString, &driverVersion);
635 #endif
638 if (info[i].mOperatingSystem != DRIVER_OS_ALL &&
639 info[i].mOperatingSystem != os)
641 continue;
644 if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
645 continue;
648 if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
649 !info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) {
650 continue;
653 if (info[i].mDevices != GfxDriverInfo::allDevices && info[i].mDevices->Length()) {
654 bool deviceMatches = false;
655 for (uint32_t j = 0; j < info[i].mDevices->Length(); j++) {
656 if ((*info[i].mDevices)[j].Equals(adapterDeviceID, nsCaseInsensitiveStringComparator())) {
657 deviceMatches = true;
658 break;
662 if (!deviceMatches) {
663 continue;
667 bool match = false;
669 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
670 continue;
672 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
673 continue;
675 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
676 continue;
678 if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) {
679 continue;
682 #if defined(XP_WIN) || defined(ANDROID)
683 switch (info[i].mComparisonOp) {
684 case DRIVER_LESS_THAN:
685 match = driverVersion < info[i].mDriverVersion;
686 break;
687 case DRIVER_LESS_THAN_OR_EQUAL:
688 match = driverVersion <= info[i].mDriverVersion;
689 break;
690 case DRIVER_GREATER_THAN:
691 match = driverVersion > info[i].mDriverVersion;
692 break;
693 case DRIVER_GREATER_THAN_OR_EQUAL:
694 match = driverVersion >= info[i].mDriverVersion;
695 break;
696 case DRIVER_EQUAL:
697 match = driverVersion == info[i].mDriverVersion;
698 break;
699 case DRIVER_NOT_EQUAL:
700 match = driverVersion != info[i].mDriverVersion;
701 break;
702 case DRIVER_BETWEEN_EXCLUSIVE:
703 match = driverVersion > info[i].mDriverVersion && driverVersion < info[i].mDriverVersionMax;
704 break;
705 case DRIVER_BETWEEN_INCLUSIVE:
706 match = driverVersion >= info[i].mDriverVersion && driverVersion <= info[i].mDriverVersionMax;
707 break;
708 case DRIVER_BETWEEN_INCLUSIVE_START:
709 match = driverVersion >= info[i].mDriverVersion && driverVersion < info[i].mDriverVersionMax;
710 break;
711 case DRIVER_COMPARISON_IGNORED:
712 // We don't have a comparison op, so we match everything.
713 match = true;
714 break;
715 default:
716 NS_WARNING("Bogus op in GfxDriverInfo");
717 break;
719 #else
720 // We don't care what driver version it was. We only check OS version and if
721 // the device matches.
722 match = true;
723 #endif
725 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
726 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
727 info[i].mFeature == aFeature)
729 status = info[i].mFeatureStatus;
730 break;
735 #if defined(XP_WIN)
736 // As a very special case, we block D2D on machines with an NVidia 310M GPU
737 // as either the primary or secondary adapter. D2D is also blocked when the
738 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
739 // If the primary GPU already matched something in the blocklist then we
740 // ignore this special rule. See bug 1008759.
741 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
742 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
743 nsAutoString adapterVendorID2;
744 nsAutoString adapterDeviceID2;
745 if ((!NS_FAILED(GetAdapterVendorID2(adapterVendorID2))) &&
746 (!NS_FAILED(GetAdapterDeviceID2(adapterDeviceID2))))
748 nsAString &nvVendorID = (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorNVIDIA);
749 const nsString nv310mDeviceId = NS_LITERAL_STRING("0x0A70");
750 if (nvVendorID.Equals(adapterVendorID2, nsCaseInsensitiveStringComparator()) &&
751 nv310mDeviceId.Equals(adapterDeviceID2, nsCaseInsensitiveStringComparator())) {
752 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
757 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
758 // back to the Windows handler, so we must handle this here.
759 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
760 if (info[i].mSuggestedVersion) {
761 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
762 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
763 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions)
765 aSuggestedVersion.AppendPrintf("%lld.%lld.%lld.%lld",
766 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
767 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
768 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
769 (info[i].mDriverVersion & 0x000000000000ffff));
772 #endif
774 return status;
777 nsresult
778 GfxInfoBase::GetFeatureStatusImpl(int32_t aFeature,
779 int32_t* aStatus,
780 nsAString& aSuggestedVersion,
781 const nsTArray<GfxDriverInfo>& aDriverInfo,
782 OperatingSystem* aOS /* = nullptr */)
784 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
785 // Terminate now with the status determined by the derived type (OS-specific
786 // code).
787 return NS_OK;
790 // If an operating system was provided by the derived GetFeatureStatusImpl,
791 // grab it here. Otherwise, the OS is unknown.
792 OperatingSystem os = DRIVER_OS_UNKNOWN;
793 if (aOS)
794 os = *aOS;
796 nsAutoString adapterVendorID;
797 nsAutoString adapterDeviceID;
798 nsAutoString adapterDriverVersionString;
799 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
800 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
801 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
803 return NS_OK;
806 // Check if the device is blocked from the downloaded blocklist. If not, check
807 // the static list after that. This order is used so that we can later escape
808 // out of static blocks (i.e. if we were wrong or something was patched, we
809 // can back out our static block without doing a release).
810 int32_t status;
811 if (aDriverInfo.Length()) {
812 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature, os);
813 } else {
814 if (!mDriverInfo) {
815 mDriverInfo = new nsTArray<GfxDriverInfo>();
817 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion, aFeature, os);
820 // It's now done being processed. It's safe to set the status to STATUS_OK.
821 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
822 *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
823 } else {
824 *aStatus = status;
827 return NS_OK;
830 NS_IMETHODIMP
831 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
832 nsAString& aVersion)
834 nsCString version;
835 if (GetPrefValueForDriverVersion(version)) {
836 aVersion = NS_ConvertASCIItoUTF16(version);
837 return NS_OK;
840 int32_t status;
841 nsTArray<GfxDriverInfo> driverInfo;
842 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo);
846 NS_IMETHODIMP
847 GfxInfoBase::GetWebGLParameter(const nsAString& aParam,
848 nsAString& aResult)
850 return GfxInfoWebGL::GetWebGLParameter(aParam, aResult);
853 void
854 GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo)
856 int32_t features[] = {
857 nsIGfxInfo::FEATURE_DIRECT2D,
858 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
859 nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
860 nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
861 nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
862 nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
863 nsIGfxInfo::FEATURE_DXVA,
864 nsIGfxInfo::FEATURE_OPENGL_LAYERS,
865 nsIGfxInfo::FEATURE_WEBGL_OPENGL,
866 nsIGfxInfo::FEATURE_WEBGL_ANGLE,
867 nsIGfxInfo::FEATURE_WEBGL_MSAA,
868 nsIGfxInfo::FEATURE_STAGEFRIGHT,
872 // For every feature we know about, we evaluate whether this blacklist has a
873 // non-STATUS_OK status. If it does, we set the pref we evaluate in
874 // GetFeatureStatus above, so we don't need to hold on to this blacklist
875 // anywhere permanent.
876 int i = 0;
877 while (features[i]) {
878 int32_t status;
879 nsAutoString suggestedVersion;
880 if (NS_SUCCEEDED(GetFeatureStatusImpl(features[i], &status,
881 suggestedVersion,
882 aDriverInfo))) {
883 switch (status) {
884 default:
885 case nsIGfxInfo::FEATURE_STATUS_OK:
886 RemovePrefForFeature(features[i]);
887 break;
889 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
890 if (!suggestedVersion.IsEmpty()) {
891 SetPrefValueForDriverVersion(suggestedVersion);
892 } else {
893 RemovePrefForDriverVersion();
895 // FALLTHROUGH
897 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
898 case nsIGfxInfo::FEATURE_DISCOURAGED:
899 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
900 SetPrefValueForFeature(features[i], status);
901 break;
905 ++i;
909 NS_IMETHODIMP_(void)
910 GfxInfoBase::LogFailure(const nsACString &failure)
912 // gfxCriticalError has a mutex lock of its own, so we may not actually
913 // need this lock. ::GetFailures() accesses the data but the LogForwarder
914 // will not return the copy of the logs unless it can get the same lock
915 // that gfxCriticalError uses. Still, that is so much of an implementation
916 // detail that it's nicer to just add an extra lock here and in ::GetFailures()
917 MutexAutoLock lock(mMutex);
919 // By default, gfxCriticalError asserts; make it not assert in this case.
920 gfxCriticalError(CriticalLog::DefaultOptions(false)) << "(LF) " << failure.BeginReading();
923 /* void getFailures (out unsigned long failureCount, [optional, array, size_is (failureCount)] out long indices, [array, size_is (failureCount), retval] out string failures); */
924 /* XPConnect method of returning arrays is very ugly. Would not recommend. Fallable nsMemory::Alloc makes things worse */
925 NS_IMETHODIMP GfxInfoBase::GetFailures(uint32_t* failureCount,
926 int32_t** indices,
927 char ***failures)
929 MutexAutoLock lock(mMutex);
931 NS_ENSURE_ARG_POINTER(failureCount);
932 NS_ENSURE_ARG_POINTER(failures);
934 *failures = nullptr;
935 *failureCount = 0;
937 // indices is "allowed" to be null, the caller may not care about them,
938 // although calling from JS doesn't seem to get us there.
939 if (indices) *indices = nullptr;
941 LogForwarder* logForwarder = Factory::GetLogForwarder();
942 if (!logForwarder) {
943 return NS_ERROR_UNEXPECTED;
946 // There are two stirng copies in this method, starting with this one. We are
947 // assuming this is not a big deal, as the size of the array should be small
948 // and the strings in it should be small as well (the error messages in the
949 // code.) The second copy happens with the Clone() calls. Technically,
950 // we don't need the mutex lock after the StringVectorCopy() call.
951 std::vector<std::pair<int32_t,std::string> > loggedStrings = logForwarder->StringsVectorCopy();
952 *failureCount = loggedStrings.size();
954 if (*failureCount != 0) {
955 *failures = (char**)nsMemory::Alloc(*failureCount * sizeof(char*));
956 if (!(*failures)) {
957 return NS_ERROR_OUT_OF_MEMORY;
959 if (indices) {
960 *indices = (int32_t*)nsMemory::Alloc(*failureCount * sizeof(int32_t));
961 if (!(*indices)) {
962 nsMemory::Free(*failures);
963 *failures = nullptr;
964 return NS_ERROR_OUT_OF_MEMORY;
968 /* copy over the failure messages into the array we just allocated */
969 std::vector<std::pair<int32_t, std::string> >::const_iterator it;
970 uint32_t i=0;
971 for(it = loggedStrings.begin() ; it != loggedStrings.end(); ++it, i++) {
972 (*failures)[i] = (char*)nsMemory::Clone((*it).second.c_str(), (*it).second.size() + 1);
973 if (indices) (*indices)[i] = (*it).first;
975 if (!(*failures)[i]) {
976 /* <sarcasm> I'm too afraid to use an inline function... </sarcasm> */
977 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, (*failures));
978 *failureCount = i;
979 return NS_ERROR_OUT_OF_MEMORY;
984 return NS_OK;
987 nsTArray<GfxInfoCollectorBase*> *sCollectors;
989 static void
990 InitCollectors()
992 if (!sCollectors)
993 sCollectors = new nsTArray<GfxInfoCollectorBase*>;
996 nsresult GfxInfoBase::GetInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
998 InitCollectors();
999 InfoObject obj(aCx);
1001 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1002 (*sCollectors)[i]->GetInfo(obj);
1005 // Some example property definitions
1006 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1007 // obj.DefineProperty("renderer", mRendererIDsString);
1008 // obj.DefineProperty("five", 5);
1010 if (!obj.mOk) {
1011 return NS_ERROR_FAILURE;
1014 aResult.setObject(*obj.mObj);
1015 return NS_OK;
1018 void
1019 GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector)
1021 InitCollectors();
1022 sCollectors->AppendElement(collector);
1025 void
1026 GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector)
1028 InitCollectors();
1029 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1030 if ((*sCollectors)[i] == collector) {
1031 sCollectors->RemoveElementAt(i);
1032 break;
1035 if (sCollectors->IsEmpty()) {
1036 delete sCollectors;
1037 sCollectors = nullptr;
1041 GfxInfoCollectorBase::GfxInfoCollectorBase()
1043 GfxInfoBase::AddCollector(this);
1046 GfxInfoCollectorBase::~GfxInfoCollectorBase()
1048 GfxInfoBase::RemoveCollector(this);