mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
a01c2107a2
Bug 1742985 added the test helper_zoom_after_gpu_process_restart.html, but it doesn't actually get run on any platform with the GPU process enabled. (Due to bug 1495580 on windows, and because the GPU process isn't yet enabled on android.) The test kills the GPU process, then tries to wait for it to be restarted before proceeding. However, the function ensureGPUProcessReadyForTests doesn't always work as intended, as the GPUProcessManager may not have yet noticed that the process has been killed, and therefore may return immediately from EnsureGPUReady. This patch removes the buggy ensureGPUProcessReadyForTests function, and instead makes the test wait for the "compositor-reinitialized" topic to be observed. Differential Revision: https://phabricator.services.mozilla.com/D138125
1920 lines
63 KiB
C++
1920 lines
63 KiB
C++
/* vim: se cin sw=2 ts=2 et : */
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
|
|
#include "GfxInfoBase.h"
|
|
|
|
#include <mutex> // std::call_once
|
|
|
|
#include "GfxDriverInfo.h"
|
|
#include "js/Array.h" // JS::GetArrayLength, JS::NewArrayObject
|
|
#include "js/PropertyAndElement.h" // JS_SetElement, JS_SetProperty
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCOMArray.h"
|
|
#include "nsString.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsVersionComparator.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/Observer.h"
|
|
#include "nsIObserver.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIScreenManager.h"
|
|
#include "nsTArray.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "nsIXULAppInfo.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/StaticPrefs_gfx.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/gfx/GPUProcessManager.h"
|
|
#include "mozilla/gfx/Logging.h"
|
|
#include "mozilla/gfx/gfxVars.h"
|
|
|
|
#include "gfxPlatform.h"
|
|
#include "gfxConfig.h"
|
|
#include "DriverCrashGuard.h"
|
|
|
|
using namespace mozilla::widget;
|
|
using namespace mozilla;
|
|
using mozilla::MutexAutoLock;
|
|
|
|
nsTArray<GfxDriverInfo>* GfxInfoBase::sDriverInfo;
|
|
StaticAutoPtr<nsTArray<gfx::GfxInfoFeatureStatus>> GfxInfoBase::sFeatureStatus;
|
|
bool GfxInfoBase::sDriverInfoObserverInitialized;
|
|
bool GfxInfoBase::sShutdownOccurred;
|
|
|
|
// Call this when setting sFeatureStatus to a non-null pointer to
|
|
// ensure destruction even if the GfxInfo component is never instantiated.
|
|
static void InitFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>* aPtr) {
|
|
static std::once_flag sOnce;
|
|
std::call_once(sOnce, [] { ClearOnShutdown(&GfxInfoBase::sFeatureStatus); });
|
|
GfxInfoBase::sFeatureStatus = aPtr;
|
|
}
|
|
|
|
// Observes for shutdown so that the child GfxDriverInfo list is freed.
|
|
class ShutdownObserver : public nsIObserver {
|
|
virtual ~ShutdownObserver() = default;
|
|
|
|
public:
|
|
ShutdownObserver() = default;
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_IMETHOD Observe(nsISupports* subject, const char* aTopic,
|
|
const char16_t* aData) override {
|
|
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
|
|
|
|
delete GfxInfoBase::sDriverInfo;
|
|
GfxInfoBase::sDriverInfo = nullptr;
|
|
|
|
for (auto& deviceFamily : GfxDriverInfo::sDeviceFamilies) {
|
|
delete deviceFamily;
|
|
deviceFamily = nullptr;
|
|
}
|
|
|
|
for (auto& desktop : GfxDriverInfo::sDesktopEnvironment) {
|
|
delete desktop;
|
|
desktop = nullptr;
|
|
}
|
|
|
|
for (auto& windowProtocol : GfxDriverInfo::sWindowProtocol) {
|
|
delete windowProtocol;
|
|
windowProtocol = nullptr;
|
|
}
|
|
|
|
for (auto& deviceVendor : GfxDriverInfo::sDeviceVendors) {
|
|
delete deviceVendor;
|
|
deviceVendor = nullptr;
|
|
}
|
|
|
|
for (auto& driverVendor : GfxDriverInfo::sDriverVendors) {
|
|
delete driverVendor;
|
|
driverVendor = nullptr;
|
|
}
|
|
|
|
GfxInfoBase::sShutdownOccurred = true;
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
|
|
|
|
static void InitGfxDriverInfoShutdownObserver() {
|
|
if (GfxInfoBase::sDriverInfoObserverInitialized) return;
|
|
|
|
GfxInfoBase::sDriverInfoObserverInitialized = true;
|
|
|
|
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
if (!observerService) {
|
|
NS_WARNING("Could not get observer service!");
|
|
return;
|
|
}
|
|
|
|
ShutdownObserver* obs = new ShutdownObserver();
|
|
observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
}
|
|
|
|
using namespace mozilla::widget;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla;
|
|
|
|
NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver,
|
|
nsISupportsWeakReference)
|
|
|
|
#define BLOCKLIST_PREF_BRANCH "gfx.blacklist."
|
|
#define SUGGESTED_VERSION_PREF BLOCKLIST_PREF_BRANCH "suggested-driver-version"
|
|
|
|
static const char* GetPrefNameForFeature(int32_t aFeature) {
|
|
const char* name = nullptr;
|
|
switch (aFeature) {
|
|
case nsIGfxInfo::FEATURE_DIRECT2D:
|
|
name = BLOCKLIST_PREF_BRANCH "direct2d";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
|
|
name = BLOCKLIST_PREF_BRANCH "layers.direct3d9";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
|
|
name = BLOCKLIST_PREF_BRANCH "layers.direct3d10";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
|
|
name = BLOCKLIST_PREF_BRANCH "layers.direct3d10-1";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
|
|
name = BLOCKLIST_PREF_BRANCH "layers.direct3d11";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
|
|
name = BLOCKLIST_PREF_BRANCH "direct3d11angle";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
|
|
name = BLOCKLIST_PREF_BRANCH "hardwarevideodecoding";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
|
|
name = BLOCKLIST_PREF_BRANCH "layers.opengl";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
|
|
name = BLOCKLIST_PREF_BRANCH "webgl.opengl";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
|
|
name = BLOCKLIST_PREF_BRANCH "webgl.angle";
|
|
break;
|
|
case nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA:
|
|
name = BLOCKLIST_PREF_BRANCH "webgl.msaa";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_STAGEFRIGHT:
|
|
name = BLOCKLIST_PREF_BRANCH "stagefright";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264:
|
|
name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.h264";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
|
|
name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
|
|
name = BLOCKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
|
|
name = BLOCKLIST_PREF_BRANCH "canvas2d.acceleration";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DX_INTEROP2:
|
|
name = BLOCKLIST_PREF_BRANCH "dx.interop2";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_GPU_PROCESS:
|
|
name = BLOCKLIST_PREF_BRANCH "gpu.process";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBGL2:
|
|
name = BLOCKLIST_PREF_BRANCH "webgl2";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX:
|
|
name = BLOCKLIST_PREF_BRANCH "d3d11.keyed.mutex";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRENDER:
|
|
name = BLOCKLIST_PREF_BRANCH "webrender";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
|
|
name = BLOCKLIST_PREF_BRANCH "webrender.compositor";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DX_NV12:
|
|
name = BLOCKLIST_PREF_BRANCH "dx.nv12";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DX_P010:
|
|
name = BLOCKLIST_PREF_BRANCH "dx.p010";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DX_P016:
|
|
name = BLOCKLIST_PREF_BRANCH "dx.p016";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
|
|
case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
|
|
// We don't provide prefs for these features as these are
|
|
// not handling downloadable blocklist.
|
|
break;
|
|
case nsIGfxInfo::FEATURE_GL_SWIZZLE:
|
|
name = BLOCKLIST_PREF_BRANCH "gl.swizzle";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS:
|
|
name = BLOCKLIST_PREF_BRANCH "webrender.scissored_cache_clears";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS:
|
|
name = BLOCKLIST_PREF_BRANCH "webgl.allow-oop";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_THREADSAFE_GL:
|
|
name = BLOCKLIST_PREF_BRANCH "gl.threadsafe";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS:
|
|
name = BLOCKLIST_PREF_BRANCH "webrender.optimized-shaders";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_X11_EGL:
|
|
name = BLOCKLIST_PREF_BRANCH "x11.egl";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_DMABUF:
|
|
name = BLOCKLIST_PREF_BRANCH "dmabuf";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE:
|
|
name = BLOCKLIST_PREF_BRANCH "webrender.program-binary-disk";
|
|
break;
|
|
case nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT:
|
|
name = BLOCKLIST_PREF_BRANCH "webrender.partial-present";
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
|
|
break;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
// Returns the value of the pref for the relevant feature in aValue.
|
|
// If the pref doesn't exist, aValue is not touched, and returns false.
|
|
static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
|
|
nsACString& aFailureId) {
|
|
const char* prefname = GetPrefNameForFeature(aFeature);
|
|
if (!prefname) return false;
|
|
|
|
aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
|
|
if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
|
|
return false;
|
|
}
|
|
|
|
nsCString failureprefname(prefname);
|
|
failureprefname += ".failureid";
|
|
nsAutoCString failureValue;
|
|
nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aFailureId = failureValue.get();
|
|
} else {
|
|
aFailureId = "FEATURE_FAILURE_BLOCKLIST_PREF";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
|
|
const nsACString& aFailureId) {
|
|
const char* prefname = GetPrefNameForFeature(aFeature);
|
|
if (!prefname) return;
|
|
if (XRE_IsParentProcess()) {
|
|
GfxInfoBase::sFeatureStatus = nullptr;
|
|
}
|
|
|
|
Preferences::SetInt(prefname, aValue);
|
|
if (!aFailureId.IsEmpty()) {
|
|
nsCString failureprefname(prefname);
|
|
failureprefname += ".failureid";
|
|
Preferences::SetCString(failureprefname.get(), aFailureId);
|
|
}
|
|
}
|
|
|
|
static void RemovePrefForFeature(int32_t aFeature) {
|
|
const char* prefname = GetPrefNameForFeature(aFeature);
|
|
if (!prefname) return;
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
GfxInfoBase::sFeatureStatus = nullptr;
|
|
}
|
|
Preferences::ClearUser(prefname);
|
|
}
|
|
|
|
static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
|
|
return NS_SUCCEEDED(
|
|
Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
|
|
}
|
|
|
|
static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
|
|
Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
|
|
}
|
|
|
|
static void RemovePrefForDriverVersion() {
|
|
Preferences::ClearUser(SUGGESTED_VERSION_PREF);
|
|
}
|
|
|
|
static OperatingSystem BlocklistOSToOperatingSystem(const nsAString& os) {
|
|
if (os.EqualsLiteral("WINNT 6.1")) {
|
|
return OperatingSystem::Windows7;
|
|
}
|
|
if (os.EqualsLiteral("WINNT 6.2")) {
|
|
return OperatingSystem::Windows8;
|
|
}
|
|
if (os.EqualsLiteral("WINNT 6.3")) {
|
|
return OperatingSystem::Windows8_1;
|
|
}
|
|
if (os.EqualsLiteral("WINNT 10.0")) {
|
|
return OperatingSystem::Windows10;
|
|
}
|
|
if (os.EqualsLiteral("Linux")) {
|
|
return OperatingSystem::Linux;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 9")) {
|
|
return OperatingSystem::OSX10_5;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 10")) {
|
|
return OperatingSystem::OSX10_6;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 11")) {
|
|
return OperatingSystem::OSX10_7;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 12")) {
|
|
return OperatingSystem::OSX10_8;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 13")) {
|
|
return OperatingSystem::OSX10_9;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 14")) {
|
|
return OperatingSystem::OSX10_10;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 15")) {
|
|
return OperatingSystem::OSX10_11;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 16")) {
|
|
return OperatingSystem::OSX10_12;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 17")) {
|
|
return OperatingSystem::OSX10_13;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 18")) {
|
|
return OperatingSystem::OSX10_14;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 19")) {
|
|
return OperatingSystem::OSX10_15;
|
|
}
|
|
if (os.EqualsLiteral("Darwin 20")) {
|
|
return OperatingSystem::OSX11_0;
|
|
}
|
|
if (os.EqualsLiteral("Android")) {
|
|
return OperatingSystem::Android;
|
|
// For historical reasons, "All" in blocklist means "All Windows"
|
|
}
|
|
if (os.EqualsLiteral("All")) {
|
|
return OperatingSystem::Windows;
|
|
}
|
|
if (os.EqualsLiteral("Darwin")) {
|
|
return OperatingSystem::OSX;
|
|
}
|
|
|
|
return OperatingSystem::Unknown;
|
|
}
|
|
|
|
static GfxDeviceFamily* BlocklistDevicesToDeviceFamily(
|
|
nsTArray<nsCString>& devices) {
|
|
if (devices.Length() == 0) return nullptr;
|
|
|
|
// For each device, get its device ID, and return a freshly-allocated
|
|
// GfxDeviceFamily with the contents of that array.
|
|
GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
|
|
|
|
for (uint32_t i = 0; i < devices.Length(); ++i) {
|
|
// We make sure we don't add any "empty" device entries to the array, so
|
|
// we don't need to check if devices[i] is empty.
|
|
deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
|
|
}
|
|
|
|
return deviceIds;
|
|
}
|
|
|
|
static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) {
|
|
MOZ_ASSERT(!aFeature.IsEmpty());
|
|
if (aFeature.EqualsLiteral("DIRECT2D")) {
|
|
return nsIGfxInfo::FEATURE_DIRECT2D;
|
|
}
|
|
if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS")) {
|
|
return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
|
|
}
|
|
if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS")) {
|
|
return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
|
|
}
|
|
if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS")) {
|
|
return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
|
|
}
|
|
if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS")) {
|
|
return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
|
|
}
|
|
if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE")) {
|
|
return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
|
|
}
|
|
if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING")) {
|
|
return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
|
|
}
|
|
if (aFeature.EqualsLiteral("OPENGL_LAYERS")) {
|
|
return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBGL_OPENGL")) {
|
|
return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBGL_ANGLE")) {
|
|
return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBGL_MSAA")) {
|
|
return nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA;
|
|
}
|
|
if (aFeature.EqualsLiteral("STAGEFRIGHT")) {
|
|
return nsIGfxInfo::FEATURE_STAGEFRIGHT;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE")) {
|
|
return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE")) {
|
|
return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_H264")) {
|
|
return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264;
|
|
}
|
|
if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION")) {
|
|
return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
|
|
}
|
|
if (aFeature.EqualsLiteral("DX_INTEROP2")) {
|
|
return nsIGfxInfo::FEATURE_DX_INTEROP2;
|
|
}
|
|
if (aFeature.EqualsLiteral("GPU_PROCESS")) {
|
|
return nsIGfxInfo::FEATURE_GPU_PROCESS;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBGL2")) {
|
|
return nsIGfxInfo::FEATURE_WEBGL2;
|
|
}
|
|
if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX")) {
|
|
return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRENDER")) {
|
|
return nsIGfxInfo::FEATURE_WEBRENDER;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRENDER_COMPOSITOR")) {
|
|
return nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR;
|
|
}
|
|
if (aFeature.EqualsLiteral("DX_NV12")) {
|
|
return nsIGfxInfo::FEATURE_DX_NV12;
|
|
}
|
|
// We do not support FEATURE_VP8_HW_DECODE and FEATURE_VP9_HW_DECODE
|
|
// in downloadable blocklist.
|
|
if (aFeature.EqualsLiteral("GL_SWIZZLE")) {
|
|
return nsIGfxInfo::FEATURE_GL_SWIZZLE;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRENDER_SCISSORED_CACHE_CLEARS")) {
|
|
return nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS;
|
|
}
|
|
if (aFeature.EqualsLiteral("ALLOW_WEBGL_OUT_OF_PROCESS")) {
|
|
return nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS;
|
|
}
|
|
if (aFeature.EqualsLiteral("THREADSAFE_GL")) {
|
|
return nsIGfxInfo::FEATURE_THREADSAFE_GL;
|
|
}
|
|
if (aFeature.EqualsLiteral("X11_EGL")) {
|
|
return nsIGfxInfo::FEATURE_X11_EGL;
|
|
}
|
|
if (aFeature.EqualsLiteral("DMABUF")) {
|
|
return nsIGfxInfo::FEATURE_DMABUF;
|
|
}
|
|
if (aFeature.EqualsLiteral("WEBRENDER_PARTIAL_PRESENT")) {
|
|
return nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT;
|
|
}
|
|
|
|
// If we don't recognize the feature, it may be new, and something
|
|
// this version doesn't understand. So, nothing to do. This is
|
|
// different from feature not being specified at all, in which case
|
|
// this method should not get called and we should continue with the
|
|
// "all features" blocklisting.
|
|
return -1;
|
|
}
|
|
|
|
static int32_t BlocklistFeatureStatusToGfxFeatureStatus(
|
|
const nsAString& aStatus) {
|
|
if (aStatus.EqualsLiteral("STATUS_OK")) {
|
|
return nsIGfxInfo::FEATURE_STATUS_OK;
|
|
}
|
|
if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION")) {
|
|
return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
|
|
}
|
|
if (aStatus.EqualsLiteral("BLOCKED_DEVICE")) {
|
|
return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
|
|
}
|
|
if (aStatus.EqualsLiteral("DISCOURAGED")) {
|
|
return nsIGfxInfo::FEATURE_DISCOURAGED;
|
|
}
|
|
if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION")) {
|
|
return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
|
|
}
|
|
if (aStatus.EqualsLiteral("DENIED")) {
|
|
return nsIGfxInfo::FEATURE_DENIED;
|
|
}
|
|
if (aStatus.EqualsLiteral("ALLOW_QUALIFIED")) {
|
|
return nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
|
|
}
|
|
if (aStatus.EqualsLiteral("ALLOW_ALWAYS")) {
|
|
return nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
|
|
}
|
|
|
|
// Do not allow it to set STATUS_UNKNOWN. Also, we are not
|
|
// expecting the "mismatch" status showing up here.
|
|
|
|
return nsIGfxInfo::FEATURE_STATUS_OK;
|
|
}
|
|
|
|
static VersionComparisonOp BlocklistComparatorToComparisonOp(
|
|
const nsAString& op) {
|
|
if (op.EqualsLiteral("LESS_THAN")) {
|
|
return DRIVER_LESS_THAN;
|
|
}
|
|
if (op.EqualsLiteral("BUILD_ID_LESS_THAN")) {
|
|
return DRIVER_BUILD_ID_LESS_THAN;
|
|
}
|
|
if (op.EqualsLiteral("LESS_THAN_OR_EQUAL")) {
|
|
return DRIVER_LESS_THAN_OR_EQUAL;
|
|
}
|
|
if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL")) {
|
|
return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
|
|
}
|
|
if (op.EqualsLiteral("GREATER_THAN")) {
|
|
return DRIVER_GREATER_THAN;
|
|
}
|
|
if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL")) {
|
|
return DRIVER_GREATER_THAN_OR_EQUAL;
|
|
}
|
|
if (op.EqualsLiteral("EQUAL")) {
|
|
return DRIVER_EQUAL;
|
|
}
|
|
if (op.EqualsLiteral("NOT_EQUAL")) {
|
|
return DRIVER_NOT_EQUAL;
|
|
}
|
|
if (op.EqualsLiteral("BETWEEN_EXCLUSIVE")) {
|
|
return DRIVER_BETWEEN_EXCLUSIVE;
|
|
}
|
|
if (op.EqualsLiteral("BETWEEN_INCLUSIVE")) {
|
|
return DRIVER_BETWEEN_INCLUSIVE;
|
|
}
|
|
if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START")) {
|
|
return DRIVER_BETWEEN_INCLUSIVE_START;
|
|
}
|
|
|
|
return DRIVER_COMPARISON_IGNORED;
|
|
}
|
|
|
|
/*
|
|
Deserialize Blocklist entries from string.
|
|
e.g:
|
|
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
|
|
*/
|
|
static bool BlocklistEntryToDriverInfo(const nsACString& aBlocklistEntry,
|
|
GfxDriverInfo& aDriverInfo) {
|
|
// If we get an application version to be zero, something is not working
|
|
// and we are not going to bother checking the blocklist versions.
|
|
// See TestGfxWidgets.cpp for how version comparison works.
|
|
// <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
|
|
static mozilla::Version zeroV("0");
|
|
static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
|
|
if (appV <= zeroV) {
|
|
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
|
|
<< "Invalid application version "
|
|
<< GfxInfoBase::GetApplicationVersion().get();
|
|
}
|
|
|
|
aDriverInfo.mRuleId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID"_ns;
|
|
|
|
for (const auto& keyValue : aBlocklistEntry.Split('\t')) {
|
|
nsTArray<nsCString> splitted;
|
|
ParseString(keyValue, ':', splitted);
|
|
if (splitted.Length() != 2) {
|
|
// If we don't recognize the input data, we do not want to proceed.
|
|
gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
|
|
<< "Unrecognized data " << nsCString(keyValue).get();
|
|
return false;
|
|
}
|
|
const nsCString& key = splitted[0];
|
|
const nsCString& value = splitted[1];
|
|
NS_ConvertUTF8toUTF16 dataValue(value);
|
|
|
|
if (value.Length() == 0) {
|
|
// Safety check for empty values.
|
|
gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
|
|
<< "Empty value for " << key.get();
|
|
return false;
|
|
}
|
|
|
|
if (key.EqualsLiteral("blockID")) {
|
|
nsCString blockIdStr = "FEATURE_FAILURE_DL_BLOCKLIST_"_ns + value;
|
|
aDriverInfo.mRuleId = blockIdStr.get();
|
|
} else if (key.EqualsLiteral("os")) {
|
|
aDriverInfo.mOperatingSystem = BlocklistOSToOperatingSystem(dataValue);
|
|
} else if (key.EqualsLiteral("osversion")) {
|
|
aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
|
|
} else if (key.EqualsLiteral("desktopEnvironment")) {
|
|
aDriverInfo.mDesktopEnvironment = dataValue;
|
|
} else if (key.EqualsLiteral("windowProtocol")) {
|
|
aDriverInfo.mWindowProtocol = dataValue;
|
|
} else if (key.EqualsLiteral("vendor")) {
|
|
aDriverInfo.mAdapterVendor = dataValue;
|
|
} else if (key.EqualsLiteral("driverVendor")) {
|
|
aDriverInfo.mDriverVendor = dataValue;
|
|
} else if (key.EqualsLiteral("feature")) {
|
|
aDriverInfo.mFeature = BlocklistFeatureToGfxFeature(dataValue);
|
|
if (aDriverInfo.mFeature < 0) {
|
|
// If we don't recognize the feature, we do not want to proceed.
|
|
gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
|
|
<< "Unrecognized feature " << value.get();
|
|
return false;
|
|
}
|
|
} else if (key.EqualsLiteral("featureStatus")) {
|
|
aDriverInfo.mFeatureStatus =
|
|
BlocklistFeatureStatusToGfxFeatureStatus(dataValue);
|
|
} else if (key.EqualsLiteral("driverVersion")) {
|
|
uint64_t version;
|
|
if (ParseDriverVersion(dataValue, &version))
|
|
aDriverInfo.mDriverVersion = version;
|
|
} else if (key.EqualsLiteral("driverVersionMax")) {
|
|
uint64_t version;
|
|
if (ParseDriverVersion(dataValue, &version))
|
|
aDriverInfo.mDriverVersionMax = version;
|
|
} else if (key.EqualsLiteral("driverVersionComparator")) {
|
|
aDriverInfo.mComparisonOp = BlocklistComparatorToComparisonOp(dataValue);
|
|
} else if (key.EqualsLiteral("model")) {
|
|
aDriverInfo.mModel = dataValue;
|
|
} else if (key.EqualsLiteral("product")) {
|
|
aDriverInfo.mProduct = dataValue;
|
|
} else if (key.EqualsLiteral("manufacturer")) {
|
|
aDriverInfo.mManufacturer = dataValue;
|
|
} else if (key.EqualsLiteral("hardware")) {
|
|
aDriverInfo.mHardware = dataValue;
|
|
} else if (key.EqualsLiteral("versionRange")) {
|
|
nsTArray<nsCString> versionRange;
|
|
ParseString(value, ',', versionRange);
|
|
if (versionRange.Length() != 2) {
|
|
gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
|
|
<< "Unrecognized versionRange " << value.get();
|
|
return false;
|
|
}
|
|
const nsCString& minValue = versionRange[0];
|
|
const nsCString& maxValue = versionRange[1];
|
|
|
|
mozilla::Version minV(minValue.get());
|
|
mozilla::Version maxV(maxValue.get());
|
|
|
|
if (minV > zeroV && !(appV >= minV)) {
|
|
// The version of the application is less than the minimal version
|
|
// this blocklist entry applies to, so we can just ignore it by
|
|
// returning false and letting the caller deal with it.
|
|
return false;
|
|
}
|
|
if (maxV > zeroV && !(appV <= maxV)) {
|
|
// The version of the application is more than the maximal version
|
|
// this blocklist entry applies to, so we can just ignore it by
|
|
// returning false and letting the caller deal with it.
|
|
return false;
|
|
}
|
|
} else if (key.EqualsLiteral("devices")) {
|
|
nsTArray<nsCString> devices;
|
|
ParseString(value, ',', devices);
|
|
GfxDeviceFamily* deviceIds = BlocklistDevicesToDeviceFamily(devices);
|
|
if (deviceIds) {
|
|
// Get GfxDriverInfo to adopt the devices array we created.
|
|
aDriverInfo.mDeleteDevices = true;
|
|
aDriverInfo.mDevices = deviceIds;
|
|
}
|
|
}
|
|
// We explicitly ignore unknown elements.
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData) {
|
|
if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
|
|
nsTArray<GfxDriverInfo> driverInfo;
|
|
NS_ConvertUTF16toUTF8 utf8Data(aData);
|
|
|
|
for (const auto& blocklistEntry : utf8Data.Split('\n')) {
|
|
GfxDriverInfo di;
|
|
if (BlocklistEntryToDriverInfo(blocklistEntry, di)) {
|
|
// XXX Changing this to driverInfo.AppendElement(di) causes leaks.
|
|
// Probably some non-standard semantics of the copy/move operations?
|
|
*driverInfo.AppendElement() = di;
|
|
// Prevent di falling out of scope from destroying the devices.
|
|
di.mDeleteDevices = false;
|
|
} else {
|
|
driverInfo.AppendElement();
|
|
}
|
|
}
|
|
|
|
EvaluateDownloadedBlocklist(driverInfo);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
|
|
|
|
GfxInfoBase::~GfxInfoBase() = default;
|
|
|
|
nsresult GfxInfoBase::Init() {
|
|
InitGfxDriverInfoShutdownObserver();
|
|
|
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
|
if (os) {
|
|
os->AddObserver(this, "blocklist-data-gfxItems", true);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void GfxInfoBase::GetData() {
|
|
if (mScreenPixels != INT64_MAX) {
|
|
// Already initialized.
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIScreenManager> manager =
|
|
do_GetService("@mozilla.org/gfx/screenmanager;1");
|
|
if (!manager) {
|
|
MOZ_ASSERT_UNREACHABLE("failed to get nsIScreenManager");
|
|
return;
|
|
}
|
|
|
|
manager->GetTotalScreenPixels(&mScreenPixels);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
|
|
int32_t* aStatus) {
|
|
// Ignore the gfx.blocklist.all pref on release and beta.
|
|
#if defined(RELEASE_OR_BETA)
|
|
int32_t blocklistAll = 0;
|
|
#else
|
|
int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
|
|
#endif
|
|
if (blocklistAll > 0) {
|
|
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
|
|
<< "Forcing blocklisting all features";
|
|
*aStatus = FEATURE_BLOCKED_DEVICE;
|
|
aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
|
|
return NS_OK;
|
|
}
|
|
|
|
if (blocklistAll < 0) {
|
|
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
|
|
<< "Ignoring any feature blocklisting.";
|
|
*aStatus = FEATURE_STATUS_OK;
|
|
return NS_OK;
|
|
}
|
|
|
|
if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (XRE_IsContentProcess() || XRE_IsGPUProcess()) {
|
|
// Use the cached data received from the parent process.
|
|
MOZ_ASSERT(sFeatureStatus);
|
|
bool success = false;
|
|
for (const auto& fs : *sFeatureStatus) {
|
|
if (fs.feature() == aFeature) {
|
|
aFailureId = fs.failureId();
|
|
*aStatus = fs.status();
|
|
success = true;
|
|
break;
|
|
}
|
|
}
|
|
return success ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsString version;
|
|
nsTArray<GfxDriverInfo> driverInfo;
|
|
nsresult rv =
|
|
GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
|
|
return rv;
|
|
}
|
|
|
|
nsTArray<gfx::GfxInfoFeatureStatus> GfxInfoBase::GetAllFeatures() {
|
|
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
|
|
if (!sFeatureStatus) {
|
|
InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>());
|
|
for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
|
|
int32_t status = 0;
|
|
nsAutoCString failureId;
|
|
GetFeatureStatus(i, failureId, &status);
|
|
gfx::GfxInfoFeatureStatus gfxFeatureStatus;
|
|
gfxFeatureStatus.feature() = i;
|
|
gfxFeatureStatus.status() = status;
|
|
gfxFeatureStatus.failureId() = failureId;
|
|
sFeatureStatus->AppendElement(gfxFeatureStatus);
|
|
}
|
|
}
|
|
|
|
nsTArray<gfx::GfxInfoFeatureStatus> features;
|
|
for (const auto& status : *sFeatureStatus) {
|
|
gfx::GfxInfoFeatureStatus copy = status;
|
|
features.AppendElement(copy);
|
|
}
|
|
return features;
|
|
}
|
|
|
|
inline bool MatchingAllowStatus(int32_t aStatus) {
|
|
switch (aStatus) {
|
|
case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
|
|
case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Matching OS go somewhat beyond the simple equality check because of the
|
|
// "All Windows" and "All OS X" variations.
|
|
//
|
|
// aBlockedOS is describing the system(s) we are trying to block.
|
|
// aSystemOS is describing the system we are running on.
|
|
//
|
|
// aSystemOS should not be "Windows" or "OSX" - it should be set to
|
|
// a particular version instead.
|
|
// However, it is valid for aBlockedOS to be one of those generic values,
|
|
// as we could be blocking all of the versions.
|
|
inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
|
|
OperatingSystem aSystemOS,
|
|
uint32_t aSystemOSBuild) {
|
|
MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
|
|
aSystemOS != OperatingSystem::OSX);
|
|
|
|
// If the block entry OS is unknown, it doesn't match
|
|
if (aBlockedOS == OperatingSystem::Unknown) {
|
|
return false;
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
if (aBlockedOS == OperatingSystem::Windows) {
|
|
// We do want even "unknown" aSystemOS to fall under "all windows"
|
|
return true;
|
|
}
|
|
|
|
constexpr uint32_t kMinWin10BuildNumber = 18362;
|
|
if (aBlockedOS == OperatingSystem::RecentWindows10 &&
|
|
aSystemOS == OperatingSystem::Windows10) {
|
|
// For allowlist purposes, we sometimes want to restrict to only recent
|
|
// versions of Windows 10. This is a bit of a kludge but easier than adding
|
|
// complicated blocklist infrastructure for build ID comparisons like driver
|
|
// versions.
|
|
return aSystemOSBuild >= kMinWin10BuildNumber;
|
|
}
|
|
|
|
if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
|
|
if (aSystemOS == OperatingSystem::Windows10) {
|
|
return aSystemOSBuild < kMinWin10BuildNumber;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(XP_MACOSX)
|
|
if (aBlockedOS == OperatingSystem::OSX) {
|
|
// We do want even "unknown" aSystemOS to fall under "all OS X"
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return aSystemOS == aBlockedOS;
|
|
}
|
|
|
|
inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
|
|
switch (aBatteryStatus) {
|
|
case BatteryStatus::All:
|
|
return true;
|
|
case BatteryStatus::None:
|
|
return !aHasBattery;
|
|
case BatteryStatus::Present:
|
|
return aHasBattery;
|
|
}
|
|
|
|
MOZ_ASSERT_UNREACHABLE("bad battery status");
|
|
return false;
|
|
}
|
|
|
|
inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
|
|
int64_t aScreenPixels) {
|
|
constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
|
|
constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
|
|
|
|
switch (aScreenStatus) {
|
|
case ScreenSizeStatus::All:
|
|
return true;
|
|
case ScreenSizeStatus::Small:
|
|
return aScreenPixels <= kMaxSmallPixels;
|
|
case ScreenSizeStatus::SmallAndMedium:
|
|
return aScreenPixels <= kMaxMediumPixels;
|
|
case ScreenSizeStatus::Medium:
|
|
return aScreenPixels > kMaxSmallPixels &&
|
|
aScreenPixels <= kMaxMediumPixels;
|
|
case ScreenSizeStatus::MediumAndLarge:
|
|
return aScreenPixels > kMaxSmallPixels;
|
|
case ScreenSizeStatus::Large:
|
|
return aScreenPixels > kMaxMediumPixels;
|
|
}
|
|
|
|
MOZ_ASSERT_UNREACHABLE("bad screen status");
|
|
return false;
|
|
}
|
|
|
|
int32_t GfxInfoBase::FindBlocklistedDeviceInList(
|
|
const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
|
|
int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
|
|
bool aForAllowing) {
|
|
int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
|
|
|
|
// Some properties are not available on all platforms.
|
|
nsAutoString desktopEnvironment;
|
|
nsresult rv = GetDesktopEnvironment(desktopEnvironment);
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
|
|
return 0;
|
|
}
|
|
|
|
nsAutoString windowProtocol;
|
|
rv = GetWindowProtocol(windowProtocol);
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
|
|
return 0;
|
|
}
|
|
|
|
bool hasBattery = false;
|
|
rv = GetHasBattery(&hasBattery);
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t osBuild = OperatingSystemBuild();
|
|
|
|
// Get the adapters once then reuse below
|
|
nsAutoString adapterVendorID[2];
|
|
nsAutoString adapterDeviceID[2];
|
|
nsAutoString adapterDriverVendor[2];
|
|
nsAutoString adapterDriverVersionString[2];
|
|
bool adapterInfoFailed[2];
|
|
|
|
adapterInfoFailed[0] =
|
|
(NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
|
|
NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
|
|
NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
|
|
NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
|
|
adapterInfoFailed[1] =
|
|
(NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
|
|
NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
|
|
NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
|
|
NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
|
|
// No point in going on if we don't have adapter info
|
|
if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
|
|
return 0;
|
|
}
|
|
|
|
#if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
|
|
uint64_t driverVersion[2] = {0, 0};
|
|
if (!adapterInfoFailed[0]) {
|
|
ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
|
|
}
|
|
if (!adapterInfoFailed[1]) {
|
|
ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
|
|
}
|
|
#endif
|
|
|
|
uint32_t i = 0;
|
|
for (; i < info.Length(); i++) {
|
|
// If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
|
|
// blocklisting. Only consider entries for our search mode.
|
|
if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
|
|
continue;
|
|
}
|
|
|
|
// If we don't have the info for this GPU, no need to check further.
|
|
// It is unclear that we would ever have a mixture of 1st and 2nd
|
|
// GPU, but leaving the code in for that possibility for now.
|
|
// (Actually, currently mGpu2 will never be true, so this can
|
|
// be optimized out.)
|
|
uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
|
|
if (adapterInfoFailed[infoIndex]) {
|
|
continue;
|
|
}
|
|
|
|
// Do the operating system check first, no point in getting the driver
|
|
// info if we won't need to use it.
|
|
if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
|
|
continue;
|
|
}
|
|
|
|
if (info[i].mOperatingSystemVersion &&
|
|
info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
|
|
continue;
|
|
}
|
|
|
|
if (!MatchingBattery(info[i].mBattery, hasBattery)) {
|
|
continue;
|
|
}
|
|
|
|
if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
|
|
continue;
|
|
}
|
|
|
|
if (!DoesDesktopEnvironmentMatch(info[i].mDesktopEnvironment,
|
|
desktopEnvironment)) {
|
|
continue;
|
|
}
|
|
|
|
if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
|
|
continue;
|
|
}
|
|
|
|
if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
|
|
continue;
|
|
}
|
|
|
|
if (!DoesDriverVendorMatch(info[i].mDriverVendor,
|
|
adapterDriverVendor[infoIndex])) {
|
|
continue;
|
|
}
|
|
|
|
if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
|
|
nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
|
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
// Not found
|
|
continue;
|
|
}
|
|
if (rv != NS_OK) {
|
|
// Failed to search, allowlist should not match, blocklist should match
|
|
// for safety reasons
|
|
if (aForAllowing) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool match = false;
|
|
|
|
if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
|
|
continue;
|
|
}
|
|
if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
|
|
continue;
|
|
}
|
|
if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
|
|
continue;
|
|
}
|
|
if (!info[i].mManufacturer.IsEmpty() &&
|
|
!info[i].mManufacturer.Equals(Manufacturer())) {
|
|
continue;
|
|
}
|
|
|
|
#if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_WIDGET_GTK)
|
|
switch (info[i].mComparisonOp) {
|
|
case DRIVER_LESS_THAN:
|
|
match = driverVersion[infoIndex] < info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_BUILD_ID_LESS_THAN:
|
|
match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_LESS_THAN_OR_EQUAL:
|
|
match = driverVersion[infoIndex] <= info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
|
|
match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_GREATER_THAN:
|
|
match = driverVersion[infoIndex] > info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_GREATER_THAN_OR_EQUAL:
|
|
match = driverVersion[infoIndex] >= info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_EQUAL:
|
|
match = driverVersion[infoIndex] == info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_NOT_EQUAL:
|
|
match = driverVersion[infoIndex] != info[i].mDriverVersion;
|
|
break;
|
|
case DRIVER_BETWEEN_EXCLUSIVE:
|
|
match = driverVersion[infoIndex] > info[i].mDriverVersion &&
|
|
driverVersion[infoIndex] < info[i].mDriverVersionMax;
|
|
break;
|
|
case DRIVER_BETWEEN_INCLUSIVE:
|
|
match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
|
|
driverVersion[infoIndex] <= info[i].mDriverVersionMax;
|
|
break;
|
|
case DRIVER_BETWEEN_INCLUSIVE_START:
|
|
match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
|
|
driverVersion[infoIndex] < info[i].mDriverVersionMax;
|
|
break;
|
|
case DRIVER_COMPARISON_IGNORED:
|
|
// We don't have a comparison op, so we match everything.
|
|
match = true;
|
|
break;
|
|
default:
|
|
NS_WARNING("Bogus op in GfxDriverInfo");
|
|
break;
|
|
}
|
|
#else
|
|
// We don't care what driver version it was. We only check OS version and if
|
|
// the device matches.
|
|
match = true;
|
|
#endif
|
|
|
|
if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
|
|
if (info[i].mFeature == GfxDriverInfo::allFeatures ||
|
|
info[i].mFeature == aFeature) {
|
|
status = info[i].mFeatureStatus;
|
|
if (!info[i].mRuleId.IsEmpty()) {
|
|
aFailureId = info[i].mRuleId.get();
|
|
} else {
|
|
aFailureId = "FEATURE_FAILURE_DL_BLOCKLIST_NO_ID";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
// As a very special case, we block D2D on machines with an NVidia 310M GPU
|
|
// as either the primary or secondary adapter. D2D is also blocked when the
|
|
// NV 310M is the primary adapter (using the standard blocklisting mechanism).
|
|
// If the primary GPU already matched something in the blocklist then we
|
|
// ignore this special rule. See bug 1008759.
|
|
if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
|
|
(aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
|
|
if (!adapterInfoFailed[1]) {
|
|
nsAString& nvVendorID =
|
|
(nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
|
|
const nsString nv310mDeviceId = u"0x0A70"_ns;
|
|
if (nvVendorID.Equals(adapterVendorID[1],
|
|
nsCaseInsensitiveStringComparator) &&
|
|
nv310mDeviceId.Equals(adapterDeviceID[1],
|
|
nsCaseInsensitiveStringComparator)) {
|
|
status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
|
|
aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
|
|
// back to the Windows handler, so we must handle this here.
|
|
if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
|
|
if (info[i].mSuggestedVersion) {
|
|
aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
|
|
} else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
|
|
info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
|
|
aSuggestedVersion.AppendPrintf(
|
|
"%lld.%lld.%lld.%lld",
|
|
(info[i].mDriverVersion & 0xffff000000000000) >> 48,
|
|
(info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
|
|
(info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
|
|
(info[i].mDriverVersion & 0x000000000000ffff));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
void GfxInfoBase::SetFeatureStatus(nsTArray<gfx::GfxInfoFeatureStatus>&& aFS) {
|
|
MOZ_ASSERT(!sFeatureStatus);
|
|
InitFeatureStatus(new nsTArray<gfx::GfxInfoFeatureStatus>(std::move(aFS)));
|
|
}
|
|
|
|
bool GfxInfoBase::DoesDesktopEnvironmentMatch(
|
|
const nsAString& aBlocklistDesktop, const nsAString& aDesktopEnv) {
|
|
return aBlocklistDesktop.Equals(aDesktopEnv,
|
|
nsCaseInsensitiveStringComparator) ||
|
|
aBlocklistDesktop.Equals(
|
|
GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::All),
|
|
nsCaseInsensitiveStringComparator);
|
|
}
|
|
|
|
bool GfxInfoBase::DoesWindowProtocolMatch(
|
|
const nsAString& aBlocklistWindowProtocol,
|
|
const nsAString& aWindowProtocol) {
|
|
return aBlocklistWindowProtocol.Equals(aWindowProtocol,
|
|
nsCaseInsensitiveStringComparator) ||
|
|
aBlocklistWindowProtocol.Equals(
|
|
GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
|
|
nsCaseInsensitiveStringComparator);
|
|
}
|
|
|
|
bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
|
|
const nsAString& aAdapterVendor) {
|
|
return aBlocklistVendor.Equals(aAdapterVendor,
|
|
nsCaseInsensitiveStringComparator) ||
|
|
aBlocklistVendor.Equals(
|
|
GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
|
|
nsCaseInsensitiveStringComparator);
|
|
}
|
|
|
|
bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
|
|
const nsAString& aDriverVendor) {
|
|
return aBlocklistVendor.Equals(aDriverVendor,
|
|
nsCaseInsensitiveStringComparator) ||
|
|
aBlocklistVendor.Equals(
|
|
GfxDriverInfo::GetDriverVendor(DriverVendor::All),
|
|
nsCaseInsensitiveStringComparator);
|
|
}
|
|
|
|
bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
|
|
return aFeature == nsIGfxInfo::FEATURE_WEBRENDER;
|
|
}
|
|
|
|
nsresult GfxInfoBase::GetFeatureStatusImpl(
|
|
int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
|
|
const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
|
|
OperatingSystem* aOS /* = nullptr */) {
|
|
if (aFeature <= 0) {
|
|
gfxWarning() << "Invalid feature <= 0";
|
|
return NS_OK;
|
|
}
|
|
|
|
if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
|
|
// Terminate now with the status determined by the derived type (OS-specific
|
|
// code).
|
|
return NS_OK;
|
|
}
|
|
|
|
if (sShutdownOccurred) {
|
|
// This is futile; we've already commenced shutdown and our blocklists have
|
|
// been deleted. We may want to look into resurrecting the blocklist instead
|
|
// but for now, just don't even go there.
|
|
return NS_OK;
|
|
}
|
|
|
|
// Ensure any additional initialization required is complete.
|
|
GetData();
|
|
|
|
// If an operating system was provided by the derived GetFeatureStatusImpl,
|
|
// grab it here. Otherwise, the OS is unknown.
|
|
OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
|
|
|
|
nsAutoString adapterVendorID;
|
|
nsAutoString adapterDeviceID;
|
|
nsAutoString adapterDriverVersionString;
|
|
if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
|
|
NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
|
|
NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
|
|
aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
|
|
*aStatus = FEATURE_BLOCKED_DEVICE;
|
|
return NS_OK;
|
|
}
|
|
|
|
// Check if the device is blocked from the downloaded blocklist. If not, check
|
|
// the static list after that. This order is used so that we can later escape
|
|
// out of static blocks (i.e. if we were wrong or something was patched, we
|
|
// can back out our static block without doing a release).
|
|
int32_t status;
|
|
if (aDriverInfo.Length()) {
|
|
status =
|
|
FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
|
|
aFailureId, os, /* aForAllowing */ false);
|
|
} else {
|
|
if (!sDriverInfo) {
|
|
sDriverInfo = new nsTArray<GfxDriverInfo>();
|
|
}
|
|
status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
|
|
aFeature, aFailureId, os,
|
|
/* aForAllowing */ false);
|
|
}
|
|
|
|
if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
|
|
if (IsFeatureAllowlisted(aFeature)) {
|
|
// This feature is actually using the allowlist; that means after we pass
|
|
// the blocklist to prevent us explicitly from getting the feature, we now
|
|
// need to check the allowlist to ensure we are allowed to get it in the
|
|
// first place.
|
|
if (aDriverInfo.Length()) {
|
|
status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
|
|
aFeature, aFailureId, os,
|
|
/* aForAllowing */ true);
|
|
} else {
|
|
status = FindBlocklistedDeviceInList(
|
|
GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
|
|
/* aForAllowing */ true);
|
|
}
|
|
|
|
if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
|
|
status = nsIGfxInfo::FEATURE_DENIED;
|
|
}
|
|
} else {
|
|
// It's now done being processed. It's safe to set the status to
|
|
// STATUS_OK.
|
|
status = nsIGfxInfo::FEATURE_STATUS_OK;
|
|
}
|
|
}
|
|
|
|
*aStatus = status;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
|
|
nsAString& aVersion) {
|
|
nsCString version;
|
|
if (GetPrefValueForDriverVersion(version)) {
|
|
aVersion = NS_ConvertASCIItoUTF16(version);
|
|
return NS_OK;
|
|
}
|
|
|
|
int32_t status;
|
|
nsCString discardFailureId;
|
|
nsTArray<GfxDriverInfo> driverInfo;
|
|
return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
|
|
discardFailureId);
|
|
}
|
|
|
|
void GfxInfoBase::EvaluateDownloadedBlocklist(
|
|
nsTArray<GfxDriverInfo>& aDriverInfo) {
|
|
int32_t features[] = {nsIGfxInfo::FEATURE_DIRECT2D,
|
|
nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
|
|
nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
|
|
nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
|
|
nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
|
|
nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
|
|
nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
|
|
nsIGfxInfo::FEATURE_OPENGL_LAYERS,
|
|
nsIGfxInfo::FEATURE_WEBGL_OPENGL,
|
|
nsIGfxInfo::FEATURE_WEBGL_ANGLE,
|
|
nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
|
|
nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
|
|
nsIGfxInfo::UNUSED_FEATURE_WEBGL_MSAA,
|
|
nsIGfxInfo::FEATURE_STAGEFRIGHT,
|
|
nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264,
|
|
nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
|
|
nsIGfxInfo::FEATURE_VP8_HW_DECODE,
|
|
nsIGfxInfo::FEATURE_VP9_HW_DECODE,
|
|
nsIGfxInfo::FEATURE_DX_INTEROP2,
|
|
nsIGfxInfo::FEATURE_GPU_PROCESS,
|
|
nsIGfxInfo::FEATURE_WEBGL2,
|
|
nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX,
|
|
nsIGfxInfo::FEATURE_WEBRENDER,
|
|
nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
|
|
nsIGfxInfo::FEATURE_DX_NV12,
|
|
nsIGfxInfo::FEATURE_DX_P010,
|
|
nsIGfxInfo::FEATURE_DX_P016,
|
|
nsIGfxInfo::FEATURE_GL_SWIZZLE,
|
|
nsIGfxInfo::FEATURE_ALLOW_WEBGL_OUT_OF_PROCESS,
|
|
nsIGfxInfo::FEATURE_X11_EGL,
|
|
nsIGfxInfo::FEATURE_DMABUF,
|
|
nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
|
|
0};
|
|
|
|
// For every feature we know about, we evaluate whether this blocklist has a
|
|
// non-STATUS_OK status. If it does, we set the pref we evaluate in
|
|
// GetFeatureStatus above, so we don't need to hold on to this blocklist
|
|
// anywhere permanent.
|
|
int i = 0;
|
|
while (features[i]) {
|
|
int32_t status;
|
|
nsCString failureId;
|
|
nsAutoString suggestedVersion;
|
|
if (NS_SUCCEEDED(GetFeatureStatusImpl(
|
|
features[i], &status, suggestedVersion, aDriverInfo, failureId))) {
|
|
switch (status) {
|
|
default:
|
|
case nsIGfxInfo::FEATURE_STATUS_OK:
|
|
RemovePrefForFeature(features[i]);
|
|
break;
|
|
|
|
case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
|
|
if (!suggestedVersion.IsEmpty()) {
|
|
SetPrefValueForDriverVersion(suggestedVersion);
|
|
} else {
|
|
RemovePrefForDriverVersion();
|
|
}
|
|
[[fallthrough]];
|
|
|
|
case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
|
|
case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
|
|
case nsIGfxInfo::FEATURE_DISCOURAGED:
|
|
case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
|
|
SetPrefValueForFeature(features[i], status, failureId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP_(void)
|
|
GfxInfoBase::LogFailure(const nsACString& failure) {
|
|
// gfxCriticalError has a mutex lock of its own, so we may not actually
|
|
// need this lock. ::GetFailures() accesses the data but the LogForwarder
|
|
// will not return the copy of the logs unless it can get the same lock
|
|
// that gfxCriticalError uses. Still, that is so much of an implementation
|
|
// detail that it's nicer to just add an extra lock here and in
|
|
// ::GetFailures()
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// By default, gfxCriticalError asserts; make it not assert in this case.
|
|
gfxCriticalError(CriticalLog::DefaultOptions(false))
|
|
<< "(LF) " << failure.BeginReading();
|
|
}
|
|
|
|
NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
|
|
nsTArray<nsCString>& failures) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
LogForwarder* logForwarder = Factory::GetLogForwarder();
|
|
if (!logForwarder) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// There are two string copies in this method, starting with this one. We are
|
|
// assuming this is not a big deal, as the size of the array should be small
|
|
// and the strings in it should be small as well (the error messages in the
|
|
// code.) The second copy happens with the AppendElement() calls.
|
|
// Technically, we don't need the mutex lock after the StringVectorCopy()
|
|
// call.
|
|
LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
|
|
LoggingRecord::const_iterator it;
|
|
for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
|
|
failures.AppendElement(
|
|
nsDependentCSubstring(Get<1>(*it).c_str(), Get<1>(*it).size()));
|
|
indices.AppendElement(Get<0>(*it));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsTArray<GfxInfoCollectorBase*>* sCollectors;
|
|
|
|
static void InitCollectors() {
|
|
if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
|
|
}
|
|
|
|
nsresult GfxInfoBase::GetInfo(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aResult) {
|
|
InitCollectors();
|
|
InfoObject obj(aCx);
|
|
|
|
for (uint32_t i = 0; i < sCollectors->Length(); i++) {
|
|
(*sCollectors)[i]->GetInfo(obj);
|
|
}
|
|
|
|
// Some example property definitions
|
|
// obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
|
|
// obj.DefineProperty("renderer", mRendererIDsString);
|
|
// obj.DefineProperty("five", 5);
|
|
|
|
if (!obj.mOk) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
aResult.setObject(*obj.mObj);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString gBaseAppVersion;
|
|
|
|
const nsCString& GfxInfoBase::GetApplicationVersion() {
|
|
static bool versionInitialized = false;
|
|
if (!versionInitialized) {
|
|
// If we fail to get the version, we will not try again.
|
|
versionInitialized = true;
|
|
|
|
// Get the version from xpcom/system/nsIXULAppInfo.idl
|
|
nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
|
|
if (app) {
|
|
app->GetVersion(gBaseAppVersion);
|
|
}
|
|
}
|
|
return gBaseAppVersion;
|
|
}
|
|
|
|
void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
|
|
InitCollectors();
|
|
sCollectors->AppendElement(collector);
|
|
}
|
|
|
|
void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
|
|
InitCollectors();
|
|
for (uint32_t i = 0; i < sCollectors->Length(); i++) {
|
|
if ((*sCollectors)[i] == collector) {
|
|
sCollectors->RemoveElementAt(i);
|
|
break;
|
|
}
|
|
}
|
|
if (sCollectors->IsEmpty()) {
|
|
delete sCollectors;
|
|
sCollectors = nullptr;
|
|
}
|
|
}
|
|
|
|
nsresult GfxInfoBase::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) {
|
|
// If we have no platform specific implementation for detecting monitors, we
|
|
// can just get the screen size from gfxPlatform as the best guess.
|
|
if (!gfxPlatform::Initialized()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// If the screen size is empty, we are probably in xpcshell.
|
|
gfx::IntSize screenSize = gfxPlatform::GetPlatform()->GetScreenSize();
|
|
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
|
|
JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
|
|
JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
|
|
|
|
JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
|
|
JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
|
|
|
|
JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
|
|
JS_SetElement(aCx, aOutArray, 0, element);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult) {
|
|
JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
|
|
|
|
nsresult rv = FindMonitors(aCx, array);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
aResult.setObject(*array);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::RefreshMonitors() { return NS_ERROR_NOT_IMPLEMENTED; }
|
|
|
|
static inline bool SetJSPropertyString(JSContext* aCx,
|
|
JS::Handle<JSObject*> aObj,
|
|
const char* aProp, const char* aString) {
|
|
JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
|
|
return JS_SetProperty(aCx, aObj, aProp, val);
|
|
}
|
|
|
|
template <typename T>
|
|
static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
|
const T& aValue) {
|
|
uint32_t index;
|
|
if (!JS::GetArrayLength(aCx, aObj, &index)) {
|
|
return false;
|
|
}
|
|
return JS_SetElement(aCx, aObj, index, aValue);
|
|
}
|
|
|
|
nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aOut) {
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
aOut.setObject(*obj);
|
|
|
|
layers::LayersBackend backend =
|
|
gfxPlatform::Initialized()
|
|
? gfxPlatform::GetPlatform()->GetCompositorBackend()
|
|
: layers::LayersBackend::LAYERS_NONE;
|
|
const char* backendName = layers::GetLayersBackendName(backend);
|
|
SetJSPropertyString(aCx, obj, "compositor", backendName);
|
|
|
|
// If graphics isn't initialized yet, just stop now.
|
|
if (!gfxPlatform::Initialized()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
DescribeFeatures(aCx, obj);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aOut) {
|
|
JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
|
|
if (!containerObj) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
aOut.setObject(*containerObj);
|
|
|
|
JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
|
|
if (!featureArray) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Collect features.
|
|
gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
|
|
FeatureState& aFeature) -> void {
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return;
|
|
}
|
|
if (!SetJSPropertyString(aCx, obj, "name", aName) ||
|
|
!SetJSPropertyString(aCx, obj, "description", aDescription) ||
|
|
!SetJSPropertyString(aCx, obj, "status",
|
|
FeatureStatusToString(aFeature.GetValue()))) {
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> log(aCx);
|
|
if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
|
|
return;
|
|
}
|
|
if (!JS_SetProperty(aCx, obj, "log", log)) {
|
|
return;
|
|
}
|
|
|
|
if (!AppendJSElement(aCx, featureArray, obj)) {
|
|
return;
|
|
}
|
|
});
|
|
|
|
JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
|
|
if (!fallbackArray) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Collect fallbacks.
|
|
gfxConfig::ForEachFallback(
|
|
[&](const char* aName, const char* aMessage) -> void {
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return;
|
|
}
|
|
|
|
if (!SetJSPropertyString(aCx, obj, "name", aName) ||
|
|
!SetJSPropertyString(aCx, obj, "message", aMessage)) {
|
|
return;
|
|
}
|
|
|
|
if (!AppendJSElement(aCx, fallbackArray, obj)) {
|
|
return;
|
|
}
|
|
});
|
|
|
|
JS::Rooted<JS::Value> val(aCx);
|
|
|
|
val = JS::ObjectValue(*featureArray);
|
|
JS_SetProperty(aCx, containerObj, "features", val);
|
|
|
|
val = JS::ObjectValue(*fallbackArray);
|
|
JS_SetProperty(aCx, containerObj, "fallbacks", val);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
|
|
const FeatureState& aFeature,
|
|
JS::MutableHandle<JS::Value> aOut) {
|
|
JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
|
|
if (!log) {
|
|
return false;
|
|
}
|
|
aOut.setObject(*log);
|
|
|
|
aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
|
|
const char* aMessage,
|
|
const nsCString& aFailureId) -> void {
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return;
|
|
}
|
|
|
|
if (!SetJSPropertyString(aCx, obj, "type", aType) ||
|
|
!SetJSPropertyString(aCx, obj, "status",
|
|
FeatureStatusToString(aStatus)) ||
|
|
(aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
|
|
return;
|
|
}
|
|
|
|
if (!AppendJSElement(aCx, log, obj)) {
|
|
return;
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
|
|
JS::Rooted<JSObject*> obj(aCx);
|
|
|
|
gfx::FeatureState& hwCompositing =
|
|
gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
|
|
InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
|
|
|
|
gfx::FeatureState& gpuProcess =
|
|
gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
|
|
InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
|
|
|
|
gfx::FeatureState& wrQualified =
|
|
gfxConfig::GetFeature(gfx::Feature::WEBRENDER_QUALIFIED);
|
|
InitFeatureObject(aCx, aObj, "wrQualified", wrQualified, &obj);
|
|
|
|
gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
|
|
InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
|
|
|
|
gfx::FeatureState& wrCompositor =
|
|
gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
|
|
InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
|
|
|
|
gfx::FeatureState& wrSoftware =
|
|
gfxConfig::GetFeature(gfx::Feature::WEBRENDER_SOFTWARE);
|
|
InitFeatureObject(aCx, aObj, "wrSoftware", wrSoftware, &obj);
|
|
|
|
gfx::FeatureState& openglCompositing =
|
|
gfxConfig::GetFeature(gfx::Feature::OPENGL_COMPOSITING);
|
|
InitFeatureObject(aCx, aObj, "openglCompositing", openglCompositing, &obj);
|
|
|
|
gfx::FeatureState& omtp = gfxConfig::GetFeature(gfx::Feature::OMTP);
|
|
InitFeatureObject(aCx, aObj, "omtp", omtp, &obj);
|
|
}
|
|
|
|
bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aContainer,
|
|
const char* aName,
|
|
mozilla::gfx::FeatureState& aFeatureState,
|
|
JS::MutableHandle<JSObject*> aOutObj) {
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return false;
|
|
}
|
|
|
|
nsCString status = aFeatureState.GetStatusAndFailureIdString();
|
|
|
|
JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
|
|
JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
|
|
JS_SetProperty(aCx, obj, "status", val);
|
|
|
|
// Add the feature object to the container.
|
|
{
|
|
JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
|
|
JS_SetProperty(aCx, aContainer, aName, val);
|
|
}
|
|
|
|
aOutObj.set(obj);
|
|
return true;
|
|
}
|
|
|
|
nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aOut) {
|
|
JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
|
|
if (!array) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
aOut.setObject(*array);
|
|
|
|
DriverCrashGuard::ForEachActiveCrashGuard(
|
|
[&](const char* aName, const char* aPrefName) -> void {
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return;
|
|
}
|
|
if (!SetJSPropertyString(aCx, obj, "type", aName)) {
|
|
return;
|
|
}
|
|
if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
|
|
return;
|
|
}
|
|
if (!AppendJSElement(aCx, array, obj)) {
|
|
return;
|
|
}
|
|
});
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled) {
|
|
*aWebRenderEnabled = gfxVars::UseWebRender();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
|
|
*aTargetFrameRate = gfxPlatform::TargetFrameRate();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
|
|
*aIsHeadless = gfxPlatform::IsHeadless();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
|
|
BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
|
|
nsString outStr;
|
|
|
|
switch (backend) {
|
|
case BackendType::DIRECT2D1_1: {
|
|
outStr.AppendPrintf("Direct2D 1.1");
|
|
break;
|
|
}
|
|
case BackendType::SKIA: {
|
|
outStr.AppendPrintf("Skia");
|
|
break;
|
|
}
|
|
case BackendType::CAIRO: {
|
|
outStr.AppendPrintf("Cairo");
|
|
break;
|
|
}
|
|
default:
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
aContentBackend.Assign(outStr);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetAzureCanvasBackend(nsAString& aBackend) {
|
|
CopyASCIItoUTF16(mozilla::MakeStringSpan(
|
|
gfxPlatform::GetPlatform()->GetAzureCanvasBackend()),
|
|
aBackend);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetAzureContentBackend(nsAString& aBackend) {
|
|
CopyASCIItoUTF16(mozilla::MakeStringSpan(
|
|
gfxPlatform::GetPlatform()->GetAzureContentBackend()),
|
|
aBackend);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
|
|
GPUProcessManager* gpu = GPUProcessManager::Get();
|
|
if (!gpu) {
|
|
// Not supported in content processes.
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*aOutValue = !!gpu->GetGPUChild();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
|
|
gfxPlatform::GetPlatform();
|
|
|
|
GPUProcessManager* gpm = GPUProcessManager::Get();
|
|
if (aEnable) {
|
|
if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
|
|
gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
|
|
}
|
|
gpm->LaunchGPUProcess();
|
|
gpm->EnsureGPUReady();
|
|
} else {
|
|
gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
|
|
gpm->KillProcess();
|
|
}
|
|
|
|
*_retval = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP GfxInfoBase::KillGPUProcessForTests() {
|
|
GPUProcessManager* gpm = GPUProcessManager::Get();
|
|
if (!gpm) {
|
|
// gfxPlatform has not been initialized.
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
gpm->KillProcess();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP GfxInfoBase::CrashGPUProcessForTests() {
|
|
GPUProcessManager* gpm = GPUProcessManager::Get();
|
|
if (!gpm) {
|
|
// gfxPlatform has not been initialized.
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
gpm->CrashProcess();
|
|
return NS_OK;
|
|
}
|
|
|
|
GfxInfoCollectorBase::GfxInfoCollectorBase() {
|
|
GfxInfoBase::AddCollector(this);
|
|
}
|
|
|
|
GfxInfoCollectorBase::~GfxInfoCollectorBase() {
|
|
GfxInfoBase::RemoveCollector(this);
|
|
}
|