michael@0: /* vim: se cin sw=2 ts=2 et : */ michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "GfxInfoBase.h" michael@0: michael@0: #include "GfxInfoWebGL.h" michael@0: #include "GfxDriverInfo.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/Observer.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMHTMLCollection.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMNodeList.h" michael@0: #include "nsTArray.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: michael@0: #if defined(MOZ_CRASHREPORTER) michael@0: #include "nsExceptionHandler.h" michael@0: #endif michael@0: michael@0: using namespace mozilla::widget; michael@0: using namespace mozilla; michael@0: using mozilla::MutexAutoLock; michael@0: michael@0: nsTArray* GfxInfoBase::mDriverInfo; michael@0: bool GfxInfoBase::mDriverInfoObserverInitialized; michael@0: michael@0: // Observes for shutdown so that the child GfxDriverInfo list is freed. michael@0: class ShutdownObserver : public nsIObserver michael@0: { michael@0: public: michael@0: ShutdownObserver() {} michael@0: virtual ~ShutdownObserver() {} michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: NS_IMETHOD Observe(nsISupports *subject, const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0); michael@0: michael@0: delete GfxInfoBase::mDriverInfo; michael@0: GfxInfoBase::mDriverInfo = nullptr; michael@0: michael@0: for (uint32_t i = 0; i < DeviceFamilyMax; i++) michael@0: delete GfxDriverInfo::mDeviceFamilies[i]; michael@0: michael@0: for (uint32_t i = 0; i < DeviceVendorMax; i++) michael@0: delete GfxDriverInfo::mDeviceVendors[i]; michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver) michael@0: michael@0: void InitGfxDriverInfoShutdownObserver() michael@0: { michael@0: if (GfxInfoBase::mDriverInfoObserverInitialized) michael@0: return; michael@0: michael@0: GfxInfoBase::mDriverInfoObserverInitialized = true; michael@0: michael@0: nsCOMPtr observerService = services::GetObserverService(); michael@0: if (!observerService) { michael@0: NS_WARNING("Could not get observer service!"); michael@0: return; michael@0: } michael@0: michael@0: ShutdownObserver *obs = new ShutdownObserver(); michael@0: observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); michael@0: } michael@0: michael@0: using namespace mozilla::widget; michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference) michael@0: michael@0: #define BLACKLIST_PREF_BRANCH "gfx.blacklist." michael@0: #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version" michael@0: #define BLACKLIST_ENTRY_TAG_NAME "gfxBlacklistEntry" michael@0: michael@0: static const char* michael@0: GetPrefNameForFeature(int32_t aFeature) michael@0: { michael@0: const char* name = nullptr; michael@0: switch(aFeature) { michael@0: case nsIGfxInfo::FEATURE_DIRECT2D: michael@0: name = BLACKLIST_PREF_BRANCH "direct2d"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS: michael@0: name = BLACKLIST_PREF_BRANCH "layers.direct3d9"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS: michael@0: name = BLACKLIST_PREF_BRANCH "layers.direct3d10"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS: michael@0: name = BLACKLIST_PREF_BRANCH "layers.direct3d10-1"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_OPENGL_LAYERS: michael@0: name = BLACKLIST_PREF_BRANCH "layers.opengl"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_WEBGL_OPENGL: michael@0: name = BLACKLIST_PREF_BRANCH "webgl.opengl"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_WEBGL_ANGLE: michael@0: name = BLACKLIST_PREF_BRANCH "webgl.angle"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_WEBGL_MSAA: michael@0: name = BLACKLIST_PREF_BRANCH "webgl.msaa"; michael@0: break; michael@0: case nsIGfxInfo::FEATURE_STAGEFRIGHT: michael@0: name = BLACKLIST_PREF_BRANCH "stagefright"; michael@0: break; michael@0: default: michael@0: break; michael@0: }; michael@0: michael@0: return name; michael@0: } michael@0: michael@0: // Returns the value of the pref for the relevant feature in aValue. michael@0: // If the pref doesn't exist, aValue is not touched, and returns false. michael@0: static bool michael@0: GetPrefValueForFeature(int32_t aFeature, int32_t& aValue) michael@0: { michael@0: const char *prefname = GetPrefNameForFeature(aFeature); michael@0: if (!prefname) michael@0: return false; michael@0: michael@0: aValue = false; michael@0: return NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue)); michael@0: } michael@0: michael@0: static void michael@0: SetPrefValueForFeature(int32_t aFeature, int32_t aValue) michael@0: { michael@0: const char *prefname = GetPrefNameForFeature(aFeature); michael@0: if (!prefname) michael@0: return; michael@0: michael@0: Preferences::SetInt(prefname, aValue); michael@0: } michael@0: michael@0: static void michael@0: RemovePrefForFeature(int32_t aFeature) michael@0: { michael@0: const char *prefname = GetPrefNameForFeature(aFeature); michael@0: if (!prefname) michael@0: return; michael@0: michael@0: Preferences::ClearUser(prefname); michael@0: } michael@0: michael@0: static bool michael@0: GetPrefValueForDriverVersion(nsCString& aVersion) michael@0: { michael@0: return NS_SUCCEEDED(Preferences::GetCString(SUGGESTED_VERSION_PREF, michael@0: &aVersion)); michael@0: } michael@0: michael@0: static void michael@0: SetPrefValueForDriverVersion(const nsAString& aVersion) michael@0: { michael@0: Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion); michael@0: } michael@0: michael@0: static void michael@0: RemovePrefForDriverVersion() michael@0: { michael@0: Preferences::ClearUser(SUGGESTED_VERSION_PREF); michael@0: } michael@0: michael@0: // Hello - "Hello" is stored as a child text node of the foo node. michael@0: static bool michael@0: BlacklistNodeToTextValue(nsIDOMNode *aBlacklistNode, nsAString& aValue) michael@0: { michael@0: nsAutoString value; michael@0: if (NS_FAILED(aBlacklistNode->GetTextContent(value))) michael@0: return false; michael@0: michael@0: value.Trim(" \t\r\n"); michael@0: aValue = value; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static OperatingSystem michael@0: BlacklistOSToOperatingSystem(const nsAString& os) michael@0: { michael@0: if (os == NS_LITERAL_STRING("WINNT 5.1")) michael@0: return DRIVER_OS_WINDOWS_XP; michael@0: else if (os == NS_LITERAL_STRING("WINNT 5.2")) michael@0: return DRIVER_OS_WINDOWS_SERVER_2003; michael@0: else if (os == NS_LITERAL_STRING("WINNT 6.0")) michael@0: return DRIVER_OS_WINDOWS_VISTA; michael@0: else if (os == NS_LITERAL_STRING("WINNT 6.1")) michael@0: return DRIVER_OS_WINDOWS_7; michael@0: else if (os == NS_LITERAL_STRING("WINNT 6.2")) michael@0: return DRIVER_OS_WINDOWS_8; michael@0: else if (os == NS_LITERAL_STRING("WINNT 6.3")) michael@0: return DRIVER_OS_WINDOWS_8_1; michael@0: else if (os == NS_LITERAL_STRING("Linux")) michael@0: return DRIVER_OS_LINUX; michael@0: else if (os == NS_LITERAL_STRING("Darwin 9")) michael@0: return DRIVER_OS_OS_X_10_5; michael@0: else if (os == NS_LITERAL_STRING("Darwin 10")) michael@0: return DRIVER_OS_OS_X_10_6; michael@0: else if (os == NS_LITERAL_STRING("Darwin 11")) michael@0: return DRIVER_OS_OS_X_10_7; michael@0: else if (os == NS_LITERAL_STRING("Darwin 12")) michael@0: return DRIVER_OS_OS_X_10_8; michael@0: else if (os == NS_LITERAL_STRING("Android")) michael@0: return DRIVER_OS_ANDROID; michael@0: else if (os == NS_LITERAL_STRING("All")) michael@0: return DRIVER_OS_ALL; michael@0: michael@0: return DRIVER_OS_UNKNOWN; michael@0: } michael@0: michael@0: static GfxDeviceFamily* michael@0: BlacklistDevicesToDeviceFamily(nsIDOMHTMLCollection* aDevices) michael@0: { michael@0: uint32_t length; michael@0: if (NS_FAILED(aDevices->GetLength(&length))) michael@0: return nullptr; michael@0: michael@0: // For each , get its device ID, and return a freshly-allocated michael@0: // GfxDeviceFamily with the contents of that array. michael@0: GfxDeviceFamily* deviceIds = new GfxDeviceFamily; michael@0: michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: nsCOMPtr node; michael@0: if (NS_FAILED(aDevices->Item(i, getter_AddRefs(node))) || !node) michael@0: continue; michael@0: michael@0: nsAutoString deviceValue; michael@0: if (!BlacklistNodeToTextValue(node, deviceValue)) michael@0: continue; michael@0: michael@0: deviceIds->AppendElement(deviceValue); michael@0: } michael@0: michael@0: return deviceIds; michael@0: } michael@0: michael@0: static int32_t michael@0: BlacklistFeatureToGfxFeature(const nsAString& aFeature) michael@0: { michael@0: if (aFeature == NS_LITERAL_STRING("DIRECT2D")) michael@0: return nsIGfxInfo::FEATURE_DIRECT2D; michael@0: else if (aFeature == NS_LITERAL_STRING("DIRECT3D_9_LAYERS")) michael@0: return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS; michael@0: else if (aFeature == NS_LITERAL_STRING("DIRECT3D_10_LAYERS")) michael@0: return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS; michael@0: else if (aFeature == NS_LITERAL_STRING("DIRECT3D_10_1_LAYERS")) michael@0: return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS; michael@0: else if (aFeature == NS_LITERAL_STRING("OPENGL_LAYERS")) michael@0: return nsIGfxInfo::FEATURE_OPENGL_LAYERS; michael@0: else if (aFeature == NS_LITERAL_STRING("WEBGL_OPENGL")) michael@0: return nsIGfxInfo::FEATURE_WEBGL_OPENGL; michael@0: else if (aFeature == NS_LITERAL_STRING("WEBGL_ANGLE")) michael@0: return nsIGfxInfo::FEATURE_WEBGL_ANGLE; michael@0: else if (aFeature == NS_LITERAL_STRING("WEBGL_MSAA")) michael@0: return nsIGfxInfo::FEATURE_WEBGL_MSAA; michael@0: else if (aFeature == NS_LITERAL_STRING("STAGEFRIGHT")) michael@0: return nsIGfxInfo::FEATURE_STAGEFRIGHT; michael@0: return 0; michael@0: } michael@0: michael@0: static int32_t michael@0: BlacklistFeatureStatusToGfxFeatureStatus(const nsAString& aStatus) michael@0: { michael@0: if (aStatus == NS_LITERAL_STRING("NO_INFO")) michael@0: return nsIGfxInfo::FEATURE_NO_INFO; michael@0: else if (aStatus == NS_LITERAL_STRING("BLOCKED_DRIVER_VERSION")) michael@0: return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION; michael@0: else if (aStatus == NS_LITERAL_STRING("BLOCKED_DEVICE")) michael@0: return nsIGfxInfo::FEATURE_BLOCKED_DEVICE; michael@0: else if (aStatus == NS_LITERAL_STRING("DISCOURAGED")) michael@0: return nsIGfxInfo::FEATURE_DISCOURAGED; michael@0: else if (aStatus == NS_LITERAL_STRING("BLOCKED_OS_VERSION")) michael@0: return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION; michael@0: michael@0: // Do not allow it to set STATUS_UNKNOWN. michael@0: michael@0: return nsIGfxInfo::FEATURE_NO_INFO; michael@0: } michael@0: michael@0: static VersionComparisonOp michael@0: BlacklistComparatorToComparisonOp(const nsAString& op) michael@0: { michael@0: if (op == NS_LITERAL_STRING("LESS_THAN")) michael@0: return DRIVER_LESS_THAN; michael@0: else if (op == NS_LITERAL_STRING("LESS_THAN_OR_EQUAL")) michael@0: return DRIVER_LESS_THAN_OR_EQUAL; michael@0: else if (op == NS_LITERAL_STRING("GREATER_THAN")) michael@0: return DRIVER_GREATER_THAN; michael@0: else if (op == NS_LITERAL_STRING("GREATER_THAN_OR_EQUAL")) michael@0: return DRIVER_GREATER_THAN_OR_EQUAL; michael@0: else if (op == NS_LITERAL_STRING("EQUAL")) michael@0: return DRIVER_EQUAL; michael@0: else if (op == NS_LITERAL_STRING("NOT_EQUAL")) michael@0: return DRIVER_NOT_EQUAL; michael@0: else if (op == NS_LITERAL_STRING("BETWEEN_EXCLUSIVE")) michael@0: return DRIVER_BETWEEN_EXCLUSIVE; michael@0: else if (op == NS_LITERAL_STRING("BETWEEN_INCLUSIVE")) michael@0: return DRIVER_BETWEEN_INCLUSIVE; michael@0: else if (op == NS_LITERAL_STRING("BETWEEN_INCLUSIVE_START")) michael@0: return DRIVER_BETWEEN_INCLUSIVE_START; michael@0: michael@0: return DRIVER_COMPARISON_IGNORED; michael@0: } michael@0: michael@0: // Arbitrarily returns the first |tagname| child of |element|. michael@0: static bool michael@0: BlacklistNodeGetChildByName(nsIDOMElement *element, michael@0: const nsAString& tagname, michael@0: nsIDOMNode** firstchild) michael@0: { michael@0: nsCOMPtr nodelist; michael@0: if (NS_FAILED(element->GetElementsByTagName(tagname, michael@0: getter_AddRefs(nodelist))) || michael@0: !nodelist) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr node; michael@0: if (NS_FAILED(nodelist->Item(0, getter_AddRefs(node))) || !node) michael@0: return false; michael@0: michael@0: node.forget(firstchild); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: michael@0: michael@0: WINNT 6.0 michael@0: 0x8086 michael@0: michael@0: 0x2582 michael@0: 0x2782 michael@0: michael@0: DIRECT3D_10_LAYERS michael@0: BLOCKED_DRIVER_VERSION michael@0: 8.52.322.2202 michael@0: LESS_THAN_OR_EQUAL michael@0: michael@0: michael@0: */ michael@0: static bool michael@0: BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry, michael@0: GfxDriverInfo& aDriverInfo) michael@0: { michael@0: nsAutoString nodename; michael@0: if (NS_FAILED(aBlacklistEntry->GetNodeName(nodename)) || michael@0: nodename != NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME)) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr element = do_QueryInterface(aBlacklistEntry); michael@0: if (!element) michael@0: return false; michael@0: michael@0: nsCOMPtr dataNode; michael@0: nsAutoString dataValue; michael@0: michael@0: // WINNT 6.0 michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("os"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue); michael@0: } michael@0: michael@0: // 14 currently only used for Android michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("osversion"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mOperatingSystemVersion = strtoul(NS_LossyConvertUTF16toASCII(dataValue).get(), michael@0: nullptr, 10); michael@0: } michael@0: michael@0: // 0x8086 michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("vendor"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mAdapterVendor = dataValue; michael@0: } michael@0: michael@0: // michael@0: // 0x2582 michael@0: // 0x2782 michael@0: // michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("devices"), michael@0: getter_AddRefs(dataNode))) { michael@0: nsCOMPtr devicesElement = do_QueryInterface(dataNode); michael@0: if (devicesElement) { michael@0: michael@0: // Get only the nodes, because BlacklistDevicesToDeviceFamily michael@0: // assumes it is passed no other nodes. michael@0: nsCOMPtr devices; michael@0: if (NS_SUCCEEDED(devicesElement->GetElementsByTagName(NS_LITERAL_STRING("device"), michael@0: getter_AddRefs(devices)))) { michael@0: GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices); michael@0: if (deviceIds) { michael@0: // Get GfxDriverInfo to adopt the devices array we created. michael@0: aDriverInfo.mDeleteDevices = true; michael@0: aDriverInfo.mDevices = deviceIds; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // DIRECT3D_10_LAYERS michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("feature"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue); michael@0: } michael@0: michael@0: // BLOCKED_DRIVER_VERSION michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("featureStatus"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue); michael@0: } michael@0: michael@0: // 8.52.322.2202 michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersion"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: uint64_t version; michael@0: if (ParseDriverVersion(dataValue, &version)) michael@0: aDriverInfo.mDriverVersion = version; michael@0: } michael@0: michael@0: // LESS_THAN_OR_EQUAL michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersionComparator"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue); michael@0: } michael@0: michael@0: // foo michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("model"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mModel = dataValue; michael@0: } michael@0: // foo michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("product"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mProduct = dataValue; michael@0: } michael@0: // foo michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("manufacturer"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mManufacturer = dataValue; michael@0: } michael@0: // foo michael@0: if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("hardware"), michael@0: getter_AddRefs(dataNode))) { michael@0: BlacklistNodeToTextValue(dataNode, dataValue); michael@0: aDriverInfo.mHardware = dataValue; michael@0: } michael@0: michael@0: // We explicitly ignore unknown elements. michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: BlacklistEntriesToDriverInfo(nsIDOMHTMLCollection* aBlacklistEntries, michael@0: nsTArray& aDriverInfo) michael@0: { michael@0: uint32_t length; michael@0: if (NS_FAILED(aBlacklistEntries->GetLength(&length))) michael@0: return; michael@0: michael@0: aDriverInfo.Clear(); michael@0: aDriverInfo.SetLength(length); michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: nsCOMPtr blacklistEntry; michael@0: if (NS_SUCCEEDED(aBlacklistEntries->Item(i, michael@0: getter_AddRefs(blacklistEntry))) && michael@0: blacklistEntry) { michael@0: GfxDriverInfo di; michael@0: if (BlacklistEntryToDriverInfo(blacklistEntry, di)) { michael@0: aDriverInfo[i] = di; michael@0: } michael@0: // Prevent di falling out of scope from destroying the devices. michael@0: di.mDeleteDevices = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) { michael@0: nsCOMPtr gfxItems = do_QueryInterface(aSubject); michael@0: if (gfxItems) { michael@0: nsCOMPtr blacklistEntries; michael@0: if (NS_SUCCEEDED(gfxItems-> michael@0: GetElementsByTagName(NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME), michael@0: getter_AddRefs(blacklistEntries))) && michael@0: blacklistEntries) michael@0: { michael@0: nsTArray driverInfo; michael@0: BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo); michael@0: EvaluateDownloadedBlacklist(driverInfo); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: GfxInfoBase::GfxInfoBase() michael@0: : mFailureCount(0) michael@0: , mMutex("GfxInfoBase") michael@0: { michael@0: } michael@0: michael@0: GfxInfoBase::~GfxInfoBase() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: GfxInfoBase::Init() michael@0: { michael@0: InitGfxDriverInfoShutdownObserver(); michael@0: michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->AddObserver(this, "blocklist-data-gfxItems", true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GfxInfoBase::GetFeatureStatus(int32_t aFeature, int32_t* aStatus) michael@0: { michael@0: if (GetPrefValueForFeature(aFeature, *aStatus)) michael@0: return NS_OK; michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: // Delegate to the parent process. michael@0: mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); michael@0: bool success; michael@0: cc->SendGetGraphicsFeatureStatus(aFeature, aStatus, &success); michael@0: return success ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsString version; michael@0: nsTArray driverInfo; michael@0: return GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo); michael@0: } michael@0: michael@0: int32_t michael@0: GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray& info, michael@0: nsAString& aSuggestedVersion, michael@0: int32_t aFeature, michael@0: OperatingSystem os) michael@0: { michael@0: int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; michael@0: michael@0: nsAutoString adapterVendorID; michael@0: nsAutoString adapterDeviceID; michael@0: nsAutoString adapterDriverVersionString; michael@0: if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) || michael@0: NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) || michael@0: NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: #if defined(XP_WIN) || defined(ANDROID) michael@0: uint64_t driverVersion; michael@0: ParseDriverVersion(adapterDriverVersionString, &driverVersion); michael@0: #endif michael@0: michael@0: uint32_t i = 0; michael@0: for (; i < info.Length(); i++) { michael@0: if (info[i].mOperatingSystem != DRIVER_OS_ALL && michael@0: info[i].mOperatingSystem != os) michael@0: { michael@0: continue; michael@0: } michael@0: michael@0: if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) { michael@0: continue; michael@0: } michael@0: michael@0: if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) && michael@0: !info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) { michael@0: continue; michael@0: } michael@0: michael@0: if (info[i].mDevices != GfxDriverInfo::allDevices && info[i].mDevices->Length()) { michael@0: bool deviceMatches = false; michael@0: for (uint32_t j = 0; j < info[i].mDevices->Length(); j++) { michael@0: if ((*info[i].mDevices)[j].Equals(adapterDeviceID, nsCaseInsensitiveStringComparator())) { michael@0: deviceMatches = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!deviceMatches) { michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: bool match = false; michael@0: michael@0: if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) { michael@0: continue; michael@0: } michael@0: if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) { michael@0: continue; michael@0: } michael@0: if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) { michael@0: continue; michael@0: } michael@0: if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) { michael@0: continue; michael@0: } michael@0: michael@0: #if defined(XP_WIN) || defined(ANDROID) michael@0: switch (info[i].mComparisonOp) { michael@0: case DRIVER_LESS_THAN: michael@0: match = driverVersion < info[i].mDriverVersion; michael@0: break; michael@0: case DRIVER_LESS_THAN_OR_EQUAL: michael@0: match = driverVersion <= info[i].mDriverVersion; michael@0: break; michael@0: case DRIVER_GREATER_THAN: michael@0: match = driverVersion > info[i].mDriverVersion; michael@0: break; michael@0: case DRIVER_GREATER_THAN_OR_EQUAL: michael@0: match = driverVersion >= info[i].mDriverVersion; michael@0: break; michael@0: case DRIVER_EQUAL: michael@0: match = driverVersion == info[i].mDriverVersion; michael@0: break; michael@0: case DRIVER_NOT_EQUAL: michael@0: match = driverVersion != info[i].mDriverVersion; michael@0: break; michael@0: case DRIVER_BETWEEN_EXCLUSIVE: michael@0: match = driverVersion > info[i].mDriverVersion && driverVersion < info[i].mDriverVersionMax; michael@0: break; michael@0: case DRIVER_BETWEEN_INCLUSIVE: michael@0: match = driverVersion >= info[i].mDriverVersion && driverVersion <= info[i].mDriverVersionMax; michael@0: break; michael@0: case DRIVER_BETWEEN_INCLUSIVE_START: michael@0: match = driverVersion >= info[i].mDriverVersion && driverVersion < info[i].mDriverVersionMax; michael@0: break; michael@0: case DRIVER_COMPARISON_IGNORED: michael@0: // We don't have a comparison op, so we match everything. michael@0: match = true; michael@0: break; michael@0: default: michael@0: NS_WARNING("Bogus op in GfxDriverInfo"); michael@0: break; michael@0: } michael@0: #else michael@0: // We don't care what driver version it was. We only check OS version and if michael@0: // the device matches. michael@0: match = true; michael@0: #endif michael@0: michael@0: if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) { michael@0: if (info[i].mFeature == GfxDriverInfo::allFeatures || michael@0: info[i].mFeature == aFeature) michael@0: { michael@0: status = info[i].mFeatureStatus; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object michael@0: // back to the Windows handler, so we must handle this here. michael@0: #if defined(XP_WIN) michael@0: if (status == FEATURE_BLOCKED_DRIVER_VERSION) { michael@0: if (info[i].mSuggestedVersion) { michael@0: aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion); michael@0: } else if (info[i].mComparisonOp == DRIVER_LESS_THAN && michael@0: info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) michael@0: { michael@0: aSuggestedVersion.AppendPrintf("%lld.%lld.%lld.%lld", michael@0: (info[i].mDriverVersion & 0xffff000000000000) >> 48, michael@0: (info[i].mDriverVersion & 0x0000ffff00000000) >> 32, michael@0: (info[i].mDriverVersion & 0x00000000ffff0000) >> 16, michael@0: (info[i].mDriverVersion & 0x000000000000ffff)); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return status; michael@0: } michael@0: michael@0: nsresult michael@0: GfxInfoBase::GetFeatureStatusImpl(int32_t aFeature, michael@0: int32_t* aStatus, michael@0: nsAString& aSuggestedVersion, michael@0: const nsTArray& aDriverInfo, michael@0: OperatingSystem* aOS /* = nullptr */) michael@0: { michael@0: if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) { michael@0: // Terminate now with the status determined by the derived type (OS-specific michael@0: // code). michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If an operating system was provided by the derived GetFeatureStatusImpl, michael@0: // grab it here. Otherwise, the OS is unknown. michael@0: OperatingSystem os = DRIVER_OS_UNKNOWN; michael@0: if (aOS) michael@0: os = *aOS; michael@0: michael@0: nsAutoString adapterVendorID; michael@0: nsAutoString adapterDeviceID; michael@0: nsAutoString adapterDriverVersionString; michael@0: if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) || michael@0: NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) || michael@0: NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Check if the device is blocked from the downloaded blocklist. If not, check michael@0: // the static list after that. This order is used so that we can later escape michael@0: // out of static blocks (i.e. if we were wrong or something was patched, we michael@0: // can back out our static block without doing a release). michael@0: int32_t status; michael@0: if (aDriverInfo.Length()) { michael@0: status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature, os); michael@0: } else { michael@0: if (!mDriverInfo) { michael@0: mDriverInfo = new nsTArray(); michael@0: } michael@0: status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion, aFeature, os); michael@0: } michael@0: michael@0: // It's now done being processed. It's safe to set the status to NO_INFO. michael@0: if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) { michael@0: *aStatus = nsIGfxInfo::FEATURE_NO_INFO; michael@0: } else { michael@0: *aStatus = status; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature, michael@0: nsAString& aVersion) michael@0: { michael@0: nsCString version; michael@0: if (GetPrefValueForDriverVersion(version)) { michael@0: aVersion = NS_ConvertASCIItoUTF16(version); michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t status; michael@0: nsTArray driverInfo; michael@0: return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: GfxInfoBase::GetWebGLParameter(const nsAString& aParam, michael@0: nsAString& aResult) michael@0: { michael@0: return GfxInfoWebGL::GetWebGLParameter(aParam, aResult); michael@0: } michael@0: michael@0: void michael@0: GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray& aDriverInfo) michael@0: { michael@0: int32_t features[] = { michael@0: nsIGfxInfo::FEATURE_DIRECT2D, michael@0: nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, michael@0: nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS, michael@0: nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS, michael@0: nsIGfxInfo::FEATURE_OPENGL_LAYERS, michael@0: nsIGfxInfo::FEATURE_WEBGL_OPENGL, michael@0: nsIGfxInfo::FEATURE_WEBGL_ANGLE, michael@0: nsIGfxInfo::FEATURE_WEBGL_MSAA, michael@0: nsIGfxInfo::FEATURE_STAGEFRIGHT, michael@0: 0 michael@0: }; michael@0: michael@0: // For every feature we know about, we evaluate whether this blacklist has a michael@0: // non-NO_INFO status. If it does, we set the pref we evaluate in michael@0: // GetFeatureStatus above, so we don't need to hold on to this blacklist michael@0: // anywhere permanent. michael@0: int i = 0; michael@0: while (features[i]) { michael@0: int32_t status; michael@0: nsAutoString suggestedVersion; michael@0: if (NS_SUCCEEDED(GetFeatureStatusImpl(features[i], &status, michael@0: suggestedVersion, michael@0: aDriverInfo))) { michael@0: switch (status) { michael@0: default: michael@0: case nsIGfxInfo::FEATURE_NO_INFO: michael@0: RemovePrefForFeature(features[i]); michael@0: break; michael@0: michael@0: case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION: michael@0: if (!suggestedVersion.IsEmpty()) { michael@0: SetPrefValueForDriverVersion(suggestedVersion); michael@0: } else { michael@0: RemovePrefForDriverVersion(); michael@0: } michael@0: // FALLTHROUGH michael@0: michael@0: case nsIGfxInfo::FEATURE_BLOCKED_DEVICE: michael@0: case nsIGfxInfo::FEATURE_DISCOURAGED: michael@0: case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION: michael@0: SetPrefValueForFeature(features[i], status); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: ++i; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: GfxInfoBase::LogFailure(const nsACString &failure) michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: /* We only keep the first 9 failures */ michael@0: if (mFailureCount < ArrayLength(mFailures)) { michael@0: mFailures[mFailureCount++] = failure; michael@0: michael@0: /* record it in the crash notes too */ michael@0: #if defined(MOZ_CRASHREPORTER) michael@0: CrashReporter::AppendAppNotesToCrashReport(failure); michael@0: #endif michael@0: } michael@0: michael@0: } michael@0: michael@0: /* void getFailures ([optional] out unsigned long failureCount, [array, size_is (failureCount), retval] out string failures); */ michael@0: /* XPConnect method of returning arrays is very ugly. Would not recommend. Fallable nsMemory::Alloc makes things worse */ michael@0: NS_IMETHODIMP GfxInfoBase::GetFailures(uint32_t *failureCount, char ***failures) michael@0: { michael@0: michael@0: NS_ENSURE_ARG_POINTER(failureCount); michael@0: NS_ENSURE_ARG_POINTER(failures); michael@0: michael@0: *failures = nullptr; michael@0: *failureCount = mFailureCount; michael@0: michael@0: if (*failureCount != 0) { michael@0: *failures = (char**)nsMemory::Alloc(*failureCount * sizeof(char*)); michael@0: if (!failures) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: /* copy over the failure messages into the array we just allocated */ michael@0: for (uint32_t i = 0; i < *failureCount; i++) { michael@0: nsCString& flattenedFailureMessage(mFailures[i]); michael@0: (*failures)[i] = (char*)nsMemory::Clone(flattenedFailureMessage.get(), flattenedFailureMessage.Length() + 1); michael@0: michael@0: if (!(*failures)[i]) { michael@0: /* I'm too afraid to use an inline function... */ michael@0: NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, (*failures)); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsTArray *sCollectors; michael@0: michael@0: static void michael@0: InitCollectors() michael@0: { michael@0: if (!sCollectors) michael@0: sCollectors = new nsTArray; michael@0: } michael@0: michael@0: nsresult GfxInfoBase::GetInfo(JSContext* aCx, JS::MutableHandle aResult) michael@0: { michael@0: InitCollectors(); michael@0: InfoObject obj(aCx); michael@0: michael@0: for (uint32_t i = 0; i < sCollectors->Length(); i++) { michael@0: (*sCollectors)[i]->GetInfo(obj); michael@0: } michael@0: michael@0: // Some example property definitions michael@0: // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count()); michael@0: // obj.DefineProperty("renderer", mRendererIDsString); michael@0: // obj.DefineProperty("five", 5); michael@0: michael@0: if (!obj.mOk) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aResult.setObject(*obj.mObj); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) michael@0: { michael@0: InitCollectors(); michael@0: sCollectors->AppendElement(collector); michael@0: } michael@0: michael@0: void michael@0: GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) michael@0: { michael@0: InitCollectors(); michael@0: for (uint32_t i = 0; i < sCollectors->Length(); i++) { michael@0: if ((*sCollectors)[i] == collector) { michael@0: sCollectors->RemoveElementAt(i); michael@0: break; michael@0: } michael@0: } michael@0: if (sCollectors->IsEmpty()) { michael@0: delete sCollectors; michael@0: sCollectors = nullptr; michael@0: } michael@0: } michael@0: michael@0: GfxInfoCollectorBase::GfxInfoCollectorBase() michael@0: { michael@0: GfxInfoBase::AddCollector(this); michael@0: } michael@0: michael@0: GfxInfoCollectorBase::~GfxInfoCollectorBase() michael@0: { michael@0: GfxInfoBase::RemoveCollector(this); michael@0: }